OSDN Git Service

Add a new WakeupMessage class and use it in two places.
authorLorenzo Colitti <lorenzo@google.com>
Wed, 2 Dec 2015 08:51:28 +0000 (17:51 +0900)
committerLorenzo Colitti <lorenzo@google.com>
Tue, 8 Dec 2015 14:50:23 +0000 (23:50 +0900)
This is useful when using the new AlarmManager direct callback
interface to wake up the system and request that an object whose
API consists of messages (such as a StateMachine) perform some
action.

In this situation, using AlarmManager.onAlarmListener by itself
will wake up the system to send the message, but does not
guarantee that the system will be awake until the target object
has processed it. This is because as soon as the onAlarmListener
sends the message and returns, the system is free to go to sleep
again.

Bug: 20157436
Bug: 25823676
Change-Id: Idff20029d287f26347441a2523b7fb20eda6a8b0

core/java/com/android/internal/util/WakeupMessage.java [new file with mode: 0644]
services/core/java/com/android/server/connectivity/NetworkMonitor.java
services/net/java/android/net/dhcp/DhcpClient.java

diff --git a/core/java/com/android/internal/util/WakeupMessage.java b/core/java/com/android/internal/util/WakeupMessage.java
new file mode 100644 (file)
index 0000000..77859b8
--- /dev/null
@@ -0,0 +1,79 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.internal.util;
+
+import android.app.AlarmManager;
+import android.content.Context;
+import android.os.Handler;
+import android.os.Message;
+
+ /**
+ * An AlarmListener that sends the specified message to a Handler and keeps the system awake until
+ * the message is processed.
+ *
+ * This is useful when using the AlarmManager direct callback interface to wake up the system and
+ * request that an object whose API consists of messages (such as a StateMachine) perform some
+ * action.
+ *
+ * In this situation, using AlarmManager.onAlarmListener by itself will wake up the system to send
+ * the message, but does not guarantee that the system will be awake until the target object has
+ * processed it. This is because as soon as the onAlarmListener sends the message and returns, the
+ * AlarmManager releases its wakelock and the system is free to go to sleep again.
+ *
+ */
+public class WakeupMessage implements AlarmManager.OnAlarmListener {
+    private static AlarmManager sAlarmManager;
+    private final Handler mHandler;
+    private final String mCmdName;
+    private final int mCmd, mArg1, mArg2;
+
+    public WakeupMessage(Context context, Handler handler,
+            String cmdName, int cmd, int arg1, int arg2) {
+        if (sAlarmManager == null) {
+            sAlarmManager = context.getSystemService(AlarmManager.class);
+        }
+        mHandler = handler;
+        mCmdName = cmdName;
+        mCmd = cmd;
+        mArg1 = arg1;
+        mArg2 = arg2;
+    }
+
+    public WakeupMessage(Context context, Handler handler, String cmdName, int cmd, int arg1) {
+        this(context, handler, cmdName, cmd, arg1, 0);
+    }
+
+    public WakeupMessage(Context context, Handler handler, String cmdName, int cmd) {
+        this(context, handler, cmdName, cmd, 0, 0);
+    }
+
+    public void schedule(long when) {
+        sAlarmManager.setExact(
+                AlarmManager.ELAPSED_REALTIME_WAKEUP, when, mCmdName, this, mHandler);
+    }
+
+    public void cancel() {
+        sAlarmManager.cancel(this);
+    }
+
+    @Override
+    public void onAlarm() {
+        Message msg = mHandler.obtainMessage(mCmd, mArg1, mArg2);
+        mHandler.handleMessage(msg);
+        msg.recycle();
+    }
+}
index 5108564..3a10dbe 100644 (file)
@@ -62,6 +62,7 @@ import com.android.internal.annotations.VisibleForTesting;
 import com.android.internal.util.Protocol;
 import com.android.internal.util.State;
 import com.android.internal.util.StateMachine;
+import com.android.internal.util.WakeupMessage;
 import com.android.server.connectivity.NetworkAgentInfo;
 
 import java.io.IOException;
@@ -565,19 +566,14 @@ public class NetworkMonitor extends StateMachine {
     private class LingeringState extends State {
         private static final String ACTION_LINGER_EXPIRED = "android.net.netmon.lingerExpired";
 
-        private CustomIntentReceiver mBroadcastReceiver;
-        private PendingIntent mIntent;
+        private WakeupMessage mWakeupMessage;
 
         @Override
         public void enter() {
-            mLingerToken = new Random().nextInt();
-            mBroadcastReceiver = new CustomIntentReceiver(ACTION_LINGER_EXPIRED, mLingerToken,
-                    CMD_LINGER_EXPIRED);
-            mIntent = mBroadcastReceiver.getPendingIntent();
+            final String cmdName = ACTION_LINGER_EXPIRED + "." + mNetworkAgentInfo.network.netId;
+            mWakeupMessage = new WakeupMessage(mContext, getHandler(), cmdName, CMD_LINGER_EXPIRED);
             long wakeupTime = SystemClock.elapsedRealtime() + mLingerDelayMs;
-            mAlarmManager.setWindow(AlarmManager.ELAPSED_REALTIME_WAKEUP, wakeupTime,
-                    // Give a specific window so we aren't subject to unknown inexactitude.
-                    mLingerDelayMs / 6, mIntent);
+            mWakeupMessage.schedule(wakeupTime);
         }
 
         @Override
@@ -592,8 +588,6 @@ public class NetworkMonitor extends StateMachine {
                     }
                     return NOT_HANDLED;
                 case CMD_LINGER_EXPIRED:
-                    if (message.arg1 != mLingerToken)
-                        return HANDLED;
                     mConnectivityServiceHandler.sendMessage(
                             obtainMessage(EVENT_NETWORK_LINGER_COMPLETE, mNetworkAgentInfo));
                     return HANDLED;
@@ -624,8 +618,7 @@ public class NetworkMonitor extends StateMachine {
 
         @Override
         public void exit() {
-            mAlarmManager.cancel(mIntent);
-            mContext.unregisterReceiver(mBroadcastReceiver);
+            mWakeupMessage.cancel();
         }
     }
 
index e7e99c4..812d9b6 100644 (file)
@@ -20,8 +20,8 @@ import com.android.internal.util.HexDump;
 import com.android.internal.util.Protocol;
 import com.android.internal.util.State;
 import com.android.internal.util.StateMachine;
+import com.android.internal.util.WakeupMessage;
 
-import android.app.AlarmManager;
 import android.content.Context;
 import android.content.Intent;
 import android.content.IntentFilter;
@@ -128,7 +128,6 @@ public class DhcpClient extends BaseDhcpStateMachine {
 
     // System services / libraries we use.
     private final Context mContext;
-    private final AlarmManager mAlarmManager;
     private final Random mRandom;
     private final INetworkManagementService mNMService;
 
@@ -143,10 +142,10 @@ public class DhcpClient extends BaseDhcpStateMachine {
 
     // State variables.
     private final StateMachine mController;
-    private final AlarmListener mKickAlarm;
-    private final AlarmListener mTimeoutAlarm;
-    private final AlarmListener mRenewAlarm;
-    private final AlarmListener mOneshotTimeoutAlarm;
+    private final WakeupMessage mKickAlarm;
+    private final WakeupMessage mTimeoutAlarm;
+    private final WakeupMessage mRenewAlarm;
+    private final WakeupMessage mOneshotTimeoutAlarm;
     private final String mIfaceName;
 
     private boolean mRegisteredForPreDhcpNotification;
@@ -174,6 +173,11 @@ public class DhcpClient extends BaseDhcpStateMachine {
     private State mWaitBeforeStartState = new WaitBeforeStartState(mDhcpInitState);
     private State mWaitBeforeRenewalState = new WaitBeforeRenewalState(mDhcpRenewingState);
 
+    private WakeupMessage makeWakeupMessage(String cmdName, int cmd) {
+        cmdName = DhcpClient.class.getSimpleName() + "." + mIfaceName + "." + cmdName;
+        return new WakeupMessage(mContext, getHandler(), cmdName, cmd);
+    }
+
     private DhcpClient(Context context, StateMachine controller, String iface) {
         super(TAG);
 
@@ -197,22 +201,21 @@ public class DhcpClient extends BaseDhcpStateMachine {
 
         setInitialState(mStoppedState);
 
-        mAlarmManager = (AlarmManager) mContext.getSystemService(Context.ALARM_SERVICE);
         IBinder b = ServiceManager.getService(Context.NETWORKMANAGEMENT_SERVICE);
         mNMService = INetworkManagementService.Stub.asInterface(b);
 
         mRandom = new Random();
 
         // Used to schedule packet retransmissions.
-        mKickAlarm = new AlarmListener("KICK", CMD_KICK);
+        mKickAlarm = makeWakeupMessage("KICK", CMD_KICK);
         // Used to time out PacketRetransmittingStates.
-        mTimeoutAlarm = new AlarmListener("TIMEOUT", CMD_TIMEOUT);
+        mTimeoutAlarm = makeWakeupMessage("TIMEOUT", CMD_TIMEOUT);
         // Used to schedule DHCP renews.
-        mRenewAlarm = new AlarmListener("RENEW", DhcpStateMachine.CMD_RENEW_DHCP);
+        mRenewAlarm = makeWakeupMessage("RENEW", DhcpStateMachine.CMD_RENEW_DHCP);
         // Used to tell the caller when its request (CMD_START_DHCP or CMD_RENEW_DHCP) timed out.
         // TODO: when the legacy DHCP client is gone, make the client fully asynchronous and
         // remove this.
-        mOneshotTimeoutAlarm = new AlarmListener("ONESHOT_TIMEOUT", CMD_ONESHOT_TIMEOUT);
+        mOneshotTimeoutAlarm = makeWakeupMessage("ONESHOT_TIMEOUT", CMD_ONESHOT_TIMEOUT);
     }
 
     @Override
@@ -227,32 +230,6 @@ public class DhcpClient extends BaseDhcpStateMachine {
         return client;
     }
 
-    /**
-     * An AlarmListener that sends the specified command to the state machine.
-     */
-    private class AlarmListener implements AlarmManager.OnAlarmListener {
-        private final int cmd;
-        private final String name;
-
-        public AlarmListener(final String cmdName, final int cmd) {
-            this.cmd = cmd;
-            this.name = DhcpClient.class.getSimpleName() + "." + mIfaceName + "." + cmdName;
-        }
-
-        public void set(long alarmTime) {
-            mAlarmManager.setExact(
-                    AlarmManager.ELAPSED_REALTIME_WAKEUP, alarmTime, name, this, getHandler());
-        }
-
-        public void cancel() {
-            mAlarmManager.cancel(this);
-        }
-
-        public void onAlarm() {
-            sendMessage(cmd);
-        }
-    }
-
     private boolean initInterface() {
         try {
             mIface = NetworkInterface.getByName(mIfaceName);
@@ -412,11 +389,11 @@ public class DhcpClient extends BaseDhcpStateMachine {
     }
 
     private void scheduleRenew() {
-        mAlarmManager.cancel(mRenewAlarm);
+        mRenewAlarm.cancel();
         if (mDhcpLeaseExpiry != 0) {
             long now = SystemClock.elapsedRealtime();
             long alarmTime = (now + mDhcpLeaseExpiry) / 2;
-            mRenewAlarm.set(alarmTime);
+            mRenewAlarm.schedule(alarmTime);
             Log.d(TAG, "Scheduling renewal in " + ((alarmTime - now) / 1000) + "s");
         } else {
             Log.d(TAG, "Infinite lease, no renewal needed");
@@ -548,7 +525,7 @@ public class DhcpClient extends BaseDhcpStateMachine {
     // one state, so we can just use the state timeout.
     private void scheduleOneshotTimeout() {
         final long alarmTime = SystemClock.elapsedRealtime() + DHCP_TIMEOUT_MS;
-        mOneshotTimeoutAlarm.set(alarmTime);
+        mOneshotTimeoutAlarm.schedule(alarmTime);
     }
 
     class StoppedState extends LoggingState {
@@ -713,8 +690,7 @@ public class DhcpClient extends BaseDhcpStateMachine {
             long now = SystemClock.elapsedRealtime();
             long timeout = jitterTimer(mTimer);
             long alarmTime = now + timeout;
-            mKickAlarm.cancel();
-            mKickAlarm.set(alarmTime);
+            mKickAlarm.schedule(alarmTime);
             mTimer *= 2;
             if (mTimer > MAX_TIMEOUT_MS) {
                 mTimer = MAX_TIMEOUT_MS;
@@ -724,7 +700,7 @@ public class DhcpClient extends BaseDhcpStateMachine {
         protected void maybeInitTimeout() {
             if (mTimeout > 0) {
                 long alarmTime = SystemClock.elapsedRealtime() + mTimeout;
-                mTimeoutAlarm.set(alarmTime);
+                mTimeoutAlarm.schedule(alarmTime);
             }
         }
     }