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