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 android.app.test; 18 19 import static org.mockito.Mockito.any; 20 import static org.mockito.Mockito.anyInt; 21 import static org.mockito.Mockito.anyLong; 22 import static org.mockito.Mockito.anyString; 23 import static org.mockito.Mockito.doAnswer; 24 import static org.mockito.Mockito.mock; 25 26 import android.app.AlarmManager; 27 import android.app.test.MockAnswerUtil.AnswerWithArguments; 28 import android.os.Handler; 29 30 import java.util.ArrayList; 31 import java.util.Iterator; 32 import java.util.List; 33 import java.util.Objects; 34 35 /** 36 * Creates an AlarmManager whose alarm dispatch can be controlled 37 * Currently only supports alarm listeners 38 * 39 * Alarm listeners will be dispatched to the handler provided or will 40 * be dispatched immediately if they would have been sent to the main 41 * looper (handler was null). 42 */ 43 public class TestAlarmManager { 44 private final AlarmManager mAlarmManager; 45 private final List<PendingAlarm> mPendingAlarms; 46 TestAlarmManager()47 public TestAlarmManager() throws Exception { 48 mPendingAlarms = new ArrayList<>(); 49 50 mAlarmManager = mock(AlarmManager.class); 51 doAnswer(new SetListenerAnswer()).when(mAlarmManager).set(anyInt(), anyLong(), anyString(), 52 any(AlarmManager.OnAlarmListener.class), any(Handler.class)); 53 doAnswer(new SetListenerAnswer()).when(mAlarmManager).setExact(anyInt(), anyLong(), 54 anyString(), any(AlarmManager.OnAlarmListener.class), any(Handler.class)); 55 doAnswer(new CancelListenerAnswer()) 56 .when(mAlarmManager).cancel(any(AlarmManager.OnAlarmListener.class)); 57 } 58 getAlarmManager()59 public AlarmManager getAlarmManager() { 60 return mAlarmManager; 61 } 62 63 /** 64 * Dispatch a pending alarm with the given tag 65 * @return if any alarm was dispatched 66 */ dispatch(String tag)67 public boolean dispatch(String tag) { 68 for (int i = 0; i < mPendingAlarms.size(); ++i) { 69 PendingAlarm alarm = mPendingAlarms.get(i); 70 if (Objects.equals(tag, alarm.getTag())) { 71 mPendingAlarms.remove(i); 72 alarm.dispatch(); 73 return true; 74 } 75 } 76 return false; 77 } 78 79 /** 80 * @return if an alarm with the given tag is pending 81 */ isPending(String tag)82 public boolean isPending(String tag) { 83 for (int i = 0; i < mPendingAlarms.size(); ++i) { 84 PendingAlarm alarm = mPendingAlarms.get(i); 85 if (Objects.equals(tag, alarm.getTag())) { 86 return true; 87 } 88 } 89 return false; 90 } 91 92 /** 93 * @return trigger time of an pending alarm with the given tag 94 * -1 if no pending alarm with the given tag 95 */ getTriggerTimeMillis(String tag)96 public long getTriggerTimeMillis(String tag) { 97 for (int i = 0; i < mPendingAlarms.size(); ++i) { 98 PendingAlarm alarm = mPendingAlarms.get(i); 99 if (Objects.equals(tag, alarm.getTag())) { 100 return alarm.getTriggerTimeMillis(); 101 } 102 } 103 return -1; 104 } 105 106 private static class PendingAlarm { 107 private final int mType; 108 private final long mTriggerAtMillis; 109 private final String mTag; 110 private final Runnable mCallback; 111 PendingAlarm(int type, long triggerAtMillis, String tag, Runnable callback)112 public PendingAlarm(int type, long triggerAtMillis, String tag, Runnable callback) { 113 mType = type; 114 mTriggerAtMillis = triggerAtMillis; 115 mTag = tag; 116 mCallback = callback; 117 } 118 dispatch()119 public void dispatch() { 120 if (mCallback != null) { 121 mCallback.run(); 122 } 123 } 124 getCallback()125 public Runnable getCallback() { 126 return mCallback; 127 } 128 getTag()129 public String getTag() { 130 return mTag; 131 } 132 getTriggerTimeMillis()133 public long getTriggerTimeMillis() { 134 return mTriggerAtMillis; 135 } 136 } 137 138 private class SetListenerAnswer extends AnswerWithArguments { answer(int type, long triggerAtMillis, String tag, AlarmManager.OnAlarmListener listener, Handler handler)139 public void answer(int type, long triggerAtMillis, String tag, 140 AlarmManager.OnAlarmListener listener, Handler handler) { 141 mPendingAlarms.add(new PendingAlarm(type, triggerAtMillis, tag, 142 new AlarmListenerRunnable(listener, handler))); 143 } 144 } 145 146 private class CancelListenerAnswer extends AnswerWithArguments { answer(AlarmManager.OnAlarmListener listener)147 public void answer(AlarmManager.OnAlarmListener listener) { 148 Iterator<PendingAlarm> alarmItr = mPendingAlarms.iterator(); 149 while (alarmItr.hasNext()) { 150 PendingAlarm alarm = alarmItr.next(); 151 if (alarm.getCallback() instanceof AlarmListenerRunnable) { 152 AlarmListenerRunnable alarmCallback = 153 (AlarmListenerRunnable) alarm.getCallback(); 154 if (alarmCallback.getListener() == listener) { 155 alarmItr.remove(); 156 } 157 } 158 } 159 } 160 } 161 162 private static class AlarmListenerRunnable implements Runnable { 163 private final AlarmManager.OnAlarmListener mListener; 164 private final Handler mHandler; AlarmListenerRunnable(AlarmManager.OnAlarmListener listener, Handler handler)165 public AlarmListenerRunnable(AlarmManager.OnAlarmListener listener, Handler handler) { 166 mListener = listener; 167 mHandler = handler; 168 } 169 getListener()170 public AlarmManager.OnAlarmListener getListener() { 171 return mListener; 172 } 173 174 @Override run()175 public void run() { 176 if (mHandler != null) { 177 mHandler.post(new Runnable() { 178 @Override 179 public void run() { 180 mListener.onAlarm(); 181 } 182 }); 183 } else { // normally gets dispatched in main looper 184 mListener.onAlarm(); 185 } 186 } 187 } 188 } 189