1 /*
2  * Copyright (C) 2015 The Android Open Source Project
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 
17 package com.android.internal.util;
18 
19 import android.app.AlarmManager;
20 import android.content.Context;
21 import android.os.Handler;
22 import android.os.Message;
23 
24 import com.android.internal.annotations.VisibleForTesting;
25 
26  /**
27  * An AlarmListener that sends the specified message to a Handler and keeps the system awake until
28  * the message is processed.
29  *
30  * This is useful when using the AlarmManager direct callback interface to wake up the system and
31  * request that an object whose API consists of messages (such as a StateMachine) perform some
32  * action.
33  *
34  * In this situation, using AlarmManager.onAlarmListener by itself will wake up the system to send
35  * the message, but does not guarantee that the system will be awake until the target object has
36  * processed it. This is because as soon as the onAlarmListener sends the message and returns, the
37  * AlarmManager releases its wakelock and the system is free to go to sleep again.
38  */
39 public class WakeupMessage implements AlarmManager.OnAlarmListener {
40     private final AlarmManager mAlarmManager;
41 
42     @VisibleForTesting
43     protected final Handler mHandler;
44     @VisibleForTesting
45     protected final String mCmdName;
46     @VisibleForTesting
47     protected final int mCmd, mArg1, mArg2;
48     @VisibleForTesting
49     protected final Object mObj;
50     private final Runnable mRunnable;
51     private boolean mScheduled;
52 
WakeupMessage(Context context, Handler handler, String cmdName, int cmd, int arg1, int arg2, Object obj)53     public WakeupMessage(Context context, Handler handler,
54             String cmdName, int cmd, int arg1, int arg2, Object obj) {
55         mAlarmManager = getAlarmManager(context);
56         mHandler = handler;
57         mCmdName = cmdName;
58         mCmd = cmd;
59         mArg1 = arg1;
60         mArg2 = arg2;
61         mObj = obj;
62         mRunnable = null;
63     }
64 
WakeupMessage(Context context, Handler handler, String cmdName, int cmd, int arg1)65     public WakeupMessage(Context context, Handler handler, String cmdName, int cmd, int arg1) {
66         this(context, handler, cmdName, cmd, arg1, 0, null);
67     }
68 
WakeupMessage(Context context, Handler handler, String cmdName, int cmd, int arg1, int arg2)69     public WakeupMessage(Context context, Handler handler,
70             String cmdName, int cmd, int arg1, int arg2) {
71         this(context, handler, cmdName, cmd, arg1, arg2, null);
72     }
73 
WakeupMessage(Context context, Handler handler, String cmdName, int cmd)74     public WakeupMessage(Context context, Handler handler, String cmdName, int cmd) {
75         this(context, handler, cmdName, cmd, 0, 0, null);
76     }
77 
WakeupMessage(Context context, Handler handler, String cmdName, Runnable runnable)78     public WakeupMessage(Context context, Handler handler, String cmdName, Runnable runnable) {
79         mAlarmManager = getAlarmManager(context);
80         mHandler = handler;
81         mCmdName = cmdName;
82         mCmd = 0;
83         mArg1 = 0;
84         mArg2 = 0;
85         mObj = null;
86         mRunnable = runnable;
87     }
88 
getAlarmManager(Context context)89     private static AlarmManager getAlarmManager(Context context) {
90         return (AlarmManager) context.getSystemService(Context.ALARM_SERVICE);
91     }
92 
93     /**
94      * Schedule the message to be delivered at the time in milliseconds of the
95      * {@link android.os.SystemClock#elapsedRealtime SystemClock.elapsedRealtime()} clock and wakeup
96      * the device when it goes off. If schedule is called multiple times without the message being
97      * dispatched then the alarm is rescheduled to the new time.
98      */
schedule(long when)99     public synchronized void schedule(long when) {
100         mAlarmManager.setExact(
101                 AlarmManager.ELAPSED_REALTIME_WAKEUP, when, mCmdName, this, mHandler);
102         mScheduled = true;
103     }
104 
105     /**
106      * Cancel all pending messages. This includes alarms that may have been fired, but have not been
107      * run on the handler yet.
108      */
cancel()109     public synchronized void cancel() {
110         if (mScheduled) {
111             mAlarmManager.cancel(this);
112             mScheduled = false;
113         }
114     }
115 
116     @Override
onAlarm()117     public void onAlarm() {
118         // Once this method is called the alarm has already been fired and removed from
119         // AlarmManager (it is still partially tracked, but only for statistics). The alarm can now
120         // be marked as unscheduled so that it can be rescheduled in the message handler.
121         final boolean stillScheduled;
122         synchronized (this) {
123             stillScheduled = mScheduled;
124             mScheduled = false;
125         }
126         if (stillScheduled) {
127             Message msg;
128             if (mRunnable == null) {
129                 msg = mHandler.obtainMessage(mCmd, mArg1, mArg2, mObj);
130             } else {
131                 msg = Message.obtain(mHandler, mRunnable);
132             }
133             mHandler.dispatchMessage(msg);
134             msg.recycle();
135         }
136     }
137 }
138