Loading core/java/com/android/internal/util/WakeupMessage.java +35 −13 Original line number Diff line number Diff line Loading @@ -33,19 +33,17 @@ import android.os.Message; * 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 AlarmManager mAlarmManager; private final Handler mHandler; private final String mCmdName; private final int mCmd, mArg1, mArg2; private boolean mScheduled; public WakeupMessage(Context context, Handler handler, String cmdName, int cmd, int arg1, int arg2) { if (sAlarmManager == null) { sAlarmManager = context.getSystemService(AlarmManager.class); } mAlarmManager = (AlarmManager) context.getSystemService(Context.ALARM_SERVICE); mHandler = handler; mCmdName = cmdName; mCmd = cmd; Loading @@ -61,19 +59,43 @@ public class WakeupMessage implements AlarmManager.OnAlarmListener { this(context, handler, cmdName, cmd, 0, 0); } public void schedule(long when) { sAlarmManager.setExact( /** * Schedule the message to be delivered at the time in milliseconds of the * {@link android.os.SystemClock#elapsedRealtime SystemClock.elapsedRealtime()} clock and wakeup * the device when it goes off. If schedule is called multiple times without the message being * dispatched then the alarm is rescheduled to the new time. */ public synchronized void schedule(long when) { mAlarmManager.setExact( AlarmManager.ELAPSED_REALTIME_WAKEUP, when, mCmdName, this, mHandler); mScheduled = true; } public void cancel() { sAlarmManager.cancel(this); /** * Cancel all pending messages. This includes alarms that may have been fired, but have not been * run on the handler yet. */ public synchronized void cancel() { if (mScheduled) { mAlarmManager.cancel(this); mScheduled = false; } } @Override public void onAlarm() { // Once this method is called the alarm has already been fired and removed from // AlarmManager (it is still partially tracked, but only for statistics). The alarm can now // be marked as unscheduled so that it can be rescheduled in the message handler. final boolean stillScheduled; synchronized (this) { stillScheduled = mScheduled; mScheduled = false; } if (stillScheduled) { Message msg = mHandler.obtainMessage(mCmd, mArg1, mArg2); mHandler.handleMessage(msg); msg.recycle(); } } } core/tests/utiltests/Android.mk +2 −1 Original line number Diff line number Diff line Loading @@ -12,7 +12,8 @@ LOCAL_MODULE_TAGS := tests LOCAL_SRC_FILES := $(call all-java-files-under, src) LOCAL_STATIC_JAVA_LIBRARIES := \ android-support-test android-support-test \ mockito-target LOCAL_JAVA_LIBRARIES := android.test.runner Loading core/tests/utiltests/src/com/android/internal/util/WakeupMessageTest.java 0 → 100644 +169 −0 Original line number Diff line number Diff line /* * Copyright (C) 2016 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 static org.junit.Assert.*; import static org.mockito.Mockito.*; import android.app.AlarmManager; import android.content.Context; import android.os.Handler; import android.os.Looper; import android.os.Message; import android.test.suitebuilder.annotation.SmallTest; import org.junit.After; import org.junit.Before; import org.junit.Test; import org.mockito.ArgumentCaptor; import org.mockito.Mock; import org.mockito.MockitoAnnotations; import org.mockito.Spy; import org.mockito.invocation.InvocationOnMock; import org.mockito.stubbing.Answer; /** * Unit tests for {@link com.android.internal.util.WakeupMessage}. */ @SmallTest public class WakeupMessageTest { private static final String TEST_CMD_NAME = "TEST cmd Name"; private static final int TEST_CMD = 18; private static final int TEST_ARG1 = 33; private static final int TEST_ARG2 = 182; @Mock AlarmManager mAlarmManager; WakeupMessage mMessage; // Make a spy so that we can verify calls to it @Spy MessageCapturingHandler mHandler = new MessageCapturingHandler(); ArgumentCaptor<AlarmManager.OnAlarmListener> mListenerCaptor = ArgumentCaptor.forClass(AlarmManager.OnAlarmListener.class); /** * A Handler that will capture the most recent message sent to it. * * This handler is setup on the main Looper */ public static class MessageCapturingHandler extends Handler { private Message mLastMessage; public MessageCapturingHandler() { super(Looper.getMainLooper(), /* Nothing is actually dispatched on this Looper */ null, false); } @Override public void handleMessage(Message m) { // need to copy since it will be recycled after this method returns mLastMessage = Message.obtain(m); } public Message getLastMessage() { return mLastMessage; } } /** * Sets up the test. */ @Before public void setUp() { MockitoAnnotations.initMocks(this); Context context = mock(Context.class); when(context.getSystemService(Context.ALARM_SERVICE)).thenReturn(mAlarmManager); // capture the listener for each AlarmManager.setExact call doNothing().when(mAlarmManager).setExact(anyInt(), anyLong(), any(String.class), mListenerCaptor.capture(), any(Handler.class)); mMessage = new WakeupMessage(context, mHandler, TEST_CMD_NAME, TEST_CMD, TEST_ARG1, TEST_ARG2); } /** * Ensure the test is cleaned up and ready for the next test. */ @After public void cleanup() { validateMockitoUsage(); } private void scheduleAndVerifyAlarm(long when) { mMessage.schedule(when); verify(mAlarmManager).setExact(eq(AlarmManager.ELAPSED_REALTIME_WAKEUP), eq(when), eq(TEST_CMD_NAME), any(AlarmManager.OnAlarmListener.class), eq(mHandler)); } private void verifyMessageDispatchedOnce() { verify(mHandler, times(1)).handleMessage(any(Message.class)); assertEquals("what", TEST_CMD, mHandler.getLastMessage().what); assertEquals("arg1", TEST_ARG1, mHandler.getLastMessage().arg1); assertEquals("arg2", TEST_ARG2, mHandler.getLastMessage().arg2); } /** * Schedule and deliver a single message */ @Test public void scheduleAndDeliverMessage() { final long when = 1001; scheduleAndVerifyAlarm(when); verify(mHandler, never()).handleMessage(any(Message.class)); mListenerCaptor.getValue().onAlarm(); verifyMessageDispatchedOnce(); } /** * Check that the message is not delivered if cancel is called it after its alarm fires but * before onAlarm is called. * * This ensures that if cancel is called on the handler thread, any previously-scheduled message * is guaranteed not to be delivered. */ @Test public void scheduleAndCancelMessage() { final long when = 1010; scheduleAndVerifyAlarm(when); mMessage.cancel(); mListenerCaptor.getValue().onAlarm(); verify(mHandler, never()).handleMessage(any(Message.class)); } /** * Verify nothing happens when cancel is called without a schedule */ @Test public void cancelWithoutSchedule() { mMessage.cancel(); } /** * Verify that the message is silently rescheduled if schedule is called twice without the * message being dispatched first. */ @Test public void scheduleTwiceWithoutMessageDispatched() { final long when1 = 1011; final long when2 = 1012; scheduleAndVerifyAlarm(when1); scheduleAndVerifyAlarm(when2); mListenerCaptor.getValue().onAlarm(); verifyMessageDispatchedOnce(); } } Loading
core/java/com/android/internal/util/WakeupMessage.java +35 −13 Original line number Diff line number Diff line Loading @@ -33,19 +33,17 @@ import android.os.Message; * 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 AlarmManager mAlarmManager; private final Handler mHandler; private final String mCmdName; private final int mCmd, mArg1, mArg2; private boolean mScheduled; public WakeupMessage(Context context, Handler handler, String cmdName, int cmd, int arg1, int arg2) { if (sAlarmManager == null) { sAlarmManager = context.getSystemService(AlarmManager.class); } mAlarmManager = (AlarmManager) context.getSystemService(Context.ALARM_SERVICE); mHandler = handler; mCmdName = cmdName; mCmd = cmd; Loading @@ -61,19 +59,43 @@ public class WakeupMessage implements AlarmManager.OnAlarmListener { this(context, handler, cmdName, cmd, 0, 0); } public void schedule(long when) { sAlarmManager.setExact( /** * Schedule the message to be delivered at the time in milliseconds of the * {@link android.os.SystemClock#elapsedRealtime SystemClock.elapsedRealtime()} clock and wakeup * the device when it goes off. If schedule is called multiple times without the message being * dispatched then the alarm is rescheduled to the new time. */ public synchronized void schedule(long when) { mAlarmManager.setExact( AlarmManager.ELAPSED_REALTIME_WAKEUP, when, mCmdName, this, mHandler); mScheduled = true; } public void cancel() { sAlarmManager.cancel(this); /** * Cancel all pending messages. This includes alarms that may have been fired, but have not been * run on the handler yet. */ public synchronized void cancel() { if (mScheduled) { mAlarmManager.cancel(this); mScheduled = false; } } @Override public void onAlarm() { // Once this method is called the alarm has already been fired and removed from // AlarmManager (it is still partially tracked, but only for statistics). The alarm can now // be marked as unscheduled so that it can be rescheduled in the message handler. final boolean stillScheduled; synchronized (this) { stillScheduled = mScheduled; mScheduled = false; } if (stillScheduled) { Message msg = mHandler.obtainMessage(mCmd, mArg1, mArg2); mHandler.handleMessage(msg); msg.recycle(); } } }
core/tests/utiltests/Android.mk +2 −1 Original line number Diff line number Diff line Loading @@ -12,7 +12,8 @@ LOCAL_MODULE_TAGS := tests LOCAL_SRC_FILES := $(call all-java-files-under, src) LOCAL_STATIC_JAVA_LIBRARIES := \ android-support-test android-support-test \ mockito-target LOCAL_JAVA_LIBRARIES := android.test.runner Loading
core/tests/utiltests/src/com/android/internal/util/WakeupMessageTest.java 0 → 100644 +169 −0 Original line number Diff line number Diff line /* * Copyright (C) 2016 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 static org.junit.Assert.*; import static org.mockito.Mockito.*; import android.app.AlarmManager; import android.content.Context; import android.os.Handler; import android.os.Looper; import android.os.Message; import android.test.suitebuilder.annotation.SmallTest; import org.junit.After; import org.junit.Before; import org.junit.Test; import org.mockito.ArgumentCaptor; import org.mockito.Mock; import org.mockito.MockitoAnnotations; import org.mockito.Spy; import org.mockito.invocation.InvocationOnMock; import org.mockito.stubbing.Answer; /** * Unit tests for {@link com.android.internal.util.WakeupMessage}. */ @SmallTest public class WakeupMessageTest { private static final String TEST_CMD_NAME = "TEST cmd Name"; private static final int TEST_CMD = 18; private static final int TEST_ARG1 = 33; private static final int TEST_ARG2 = 182; @Mock AlarmManager mAlarmManager; WakeupMessage mMessage; // Make a spy so that we can verify calls to it @Spy MessageCapturingHandler mHandler = new MessageCapturingHandler(); ArgumentCaptor<AlarmManager.OnAlarmListener> mListenerCaptor = ArgumentCaptor.forClass(AlarmManager.OnAlarmListener.class); /** * A Handler that will capture the most recent message sent to it. * * This handler is setup on the main Looper */ public static class MessageCapturingHandler extends Handler { private Message mLastMessage; public MessageCapturingHandler() { super(Looper.getMainLooper(), /* Nothing is actually dispatched on this Looper */ null, false); } @Override public void handleMessage(Message m) { // need to copy since it will be recycled after this method returns mLastMessage = Message.obtain(m); } public Message getLastMessage() { return mLastMessage; } } /** * Sets up the test. */ @Before public void setUp() { MockitoAnnotations.initMocks(this); Context context = mock(Context.class); when(context.getSystemService(Context.ALARM_SERVICE)).thenReturn(mAlarmManager); // capture the listener for each AlarmManager.setExact call doNothing().when(mAlarmManager).setExact(anyInt(), anyLong(), any(String.class), mListenerCaptor.capture(), any(Handler.class)); mMessage = new WakeupMessage(context, mHandler, TEST_CMD_NAME, TEST_CMD, TEST_ARG1, TEST_ARG2); } /** * Ensure the test is cleaned up and ready for the next test. */ @After public void cleanup() { validateMockitoUsage(); } private void scheduleAndVerifyAlarm(long when) { mMessage.schedule(when); verify(mAlarmManager).setExact(eq(AlarmManager.ELAPSED_REALTIME_WAKEUP), eq(when), eq(TEST_CMD_NAME), any(AlarmManager.OnAlarmListener.class), eq(mHandler)); } private void verifyMessageDispatchedOnce() { verify(mHandler, times(1)).handleMessage(any(Message.class)); assertEquals("what", TEST_CMD, mHandler.getLastMessage().what); assertEquals("arg1", TEST_ARG1, mHandler.getLastMessage().arg1); assertEquals("arg2", TEST_ARG2, mHandler.getLastMessage().arg2); } /** * Schedule and deliver a single message */ @Test public void scheduleAndDeliverMessage() { final long when = 1001; scheduleAndVerifyAlarm(when); verify(mHandler, never()).handleMessage(any(Message.class)); mListenerCaptor.getValue().onAlarm(); verifyMessageDispatchedOnce(); } /** * Check that the message is not delivered if cancel is called it after its alarm fires but * before onAlarm is called. * * This ensures that if cancel is called on the handler thread, any previously-scheduled message * is guaranteed not to be delivered. */ @Test public void scheduleAndCancelMessage() { final long when = 1010; scheduleAndVerifyAlarm(when); mMessage.cancel(); mListenerCaptor.getValue().onAlarm(); verify(mHandler, never()).handleMessage(any(Message.class)); } /** * Verify nothing happens when cancel is called without a schedule */ @Test public void cancelWithoutSchedule() { mMessage.cancel(); } /** * Verify that the message is silently rescheduled if schedule is called twice without the * message being dispatched first. */ @Test public void scheduleTwiceWithoutMessageDispatched() { final long when1 = 1011; final long when2 = 1012; scheduleAndVerifyAlarm(when1); scheduleAndVerifyAlarm(when2); mListenerCaptor.getValue().onAlarm(); verifyMessageDispatchedOnce(); } }