Merge "Modify WakeupMessage to only send the message if not canceled" into mm-wireless-dev
This commit is contained in:
committed by
Android Partner Code Review
commit
9505e0ac3f
@@ -21,7 +21,7 @@ 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.
|
||||
*
|
||||
@@ -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;
|
||||
@@ -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() {
|
||||
Message msg = mHandler.obtainMessage(mCmd, mArg1, mArg2);
|
||||
mHandler.handleMessage(msg);
|
||||
msg.recycle();
|
||||
// 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();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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
|
||||
|
||||
|
||||
@@ -0,0 +1,169 @@
|
||||
/*
|
||||
* 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();
|
||||
}
|
||||
|
||||
}
|
||||
Reference in New Issue
Block a user