1 /*
2  * Copyright (C) 2021 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.alarmmanager.cts;
18 
19 import static org.junit.Assert.assertEquals;
20 import static org.junit.Assert.assertFalse;
21 import static org.junit.Assert.assertNotEquals;
22 import static org.junit.Assert.assertNull;
23 import static org.junit.Assert.assertTrue;
24 import static org.junit.Assert.fail;
25 import static org.junit.Assume.assumeTrue;
26 
27 import android.Manifest;
28 import android.alarmmanager.util.AlarmManagerDeviceConfigHelper;
29 import android.alarmmanager.util.Utils;
30 import android.app.AlarmManager;
31 import android.app.AlarmManager.AlarmClockInfo;
32 import android.app.PendingIntent;
33 import android.content.BroadcastReceiver;
34 import android.content.Context;
35 import android.content.Intent;
36 import android.content.IntentFilter;
37 import android.os.Build;
38 import android.os.PowerManager;
39 import android.os.SystemClock;
40 import android.os.WorkSource;
41 import android.platform.test.annotations.AppModeFull;
42 import android.util.Log;
43 
44 import androidx.annotation.GuardedBy;
45 import androidx.test.InstrumentationRegistry;
46 import androidx.test.filters.LargeTest;
47 import androidx.test.runner.AndroidJUnit4;
48 
49 import com.android.compatibility.common.util.ApiLevelUtil;
50 import com.android.compatibility.common.util.PollingCheck;
51 import com.android.compatibility.common.util.SystemUtil;
52 
53 import org.junit.After;
54 import org.junit.Before;
55 import org.junit.Rule;
56 import org.junit.Test;
57 import org.junit.runner.RunWith;
58 
59 /**
60  * General API tests earlier present at CtsAppTestCases:AlarmManagerTest
61  */
62 @LargeTest
63 @AppModeFull
64 @RunWith(AndroidJUnit4.class)
65 public class BasicApiTests {
66     private static final String TAG = BasicApiTests.class.getSimpleName();
67     public static final String MOCKACTION = "android.app.AlarmManagerTest.TEST_ALARMRECEIVER";
68     public static final String MOCKACTION2 = "android.app.AlarmManagerTest.TEST_ALARMRECEIVER2";
69 
70     private AlarmManager mAm;
71     private Intent mIntent;
72     private PendingIntent mSender;
73     private Intent mIntent2;
74     private PendingIntent mSender2;
75 
76     /*
77      *  The default snooze delay: 5 seconds
78      */
79     private static final long SNOOZE_DELAY = 5_000L;
80     private long mWakeupTime;
81     private MockAlarmReceiver mMockAlarmReceiver;
82     private MockAlarmReceiver mMockAlarmReceiver2;
83 
84     private static final int TIME_DELTA = 1000;
85     private static final int TIME_DELAY = 10_000;
86 
87     private static final long TEST_ALARM_FUTURITY = 2_000L;
88     private static final long FAIL_DELTA = 50;
89     private static final long PRIORITY_ALARM_DELAY = 6_000;
90     private static final long REPEAT_PERIOD = 30_000;
91     private Context mContext = InstrumentationRegistry.getTargetContext();
92     private final AlarmManagerDeviceConfigHelper mDeviceConfigHelper =
93             new AlarmManagerDeviceConfigHelper();
94     private PowerManager mPowerManager = mContext.getSystemService(PowerManager.class);
95 
96     @Rule
97     public DumpLoggerRule mFailLoggerRule = new DumpLoggerRule(TAG);
98 
99     @Before
setUp()100     public void setUp() throws Exception {
101         mAm = (AlarmManager) mContext.getSystemService(Context.ALARM_SERVICE);
102 
103         mIntent = new Intent(MOCKACTION)
104                 .addFlags(Intent.FLAG_RECEIVER_FOREGROUND | Intent.FLAG_RECEIVER_REGISTERED_ONLY);
105         mSender = PendingIntent.getBroadcast(mContext, 0, mIntent, PendingIntent.FLAG_IMMUTABLE);
106         mMockAlarmReceiver = new MockAlarmReceiver(mIntent.getAction());
107 
108         mIntent2 = new Intent(MOCKACTION2)
109                 .addFlags(Intent.FLAG_RECEIVER_FOREGROUND | Intent.FLAG_RECEIVER_REGISTERED_ONLY);
110         mSender2 = PendingIntent.getBroadcast(mContext, 0, mIntent2, PendingIntent.FLAG_IMMUTABLE);
111         mMockAlarmReceiver2 = new MockAlarmReceiver(mIntent2.getAction());
112 
113         IntentFilter filter = new IntentFilter(mIntent.getAction());
114         mContext.registerReceiver(mMockAlarmReceiver, filter,
115                 Context.RECEIVER_EXPORTED_UNAUDITED);
116 
117         IntentFilter filter2 = new IntentFilter(mIntent2.getAction());
118         mContext.registerReceiver(mMockAlarmReceiver2, filter2,
119                 Context.RECEIVER_EXPORTED_UNAUDITED);
120 
121         mDeviceConfigHelper.with("min_futurity", 0L)
122                 .with("min_interval", REPEAT_PERIOD)
123                 .with("min_window", 0L)
124                 .with("priority_alarm_delay", PRIORITY_ALARM_DELAY)
125                 .commitAndAwaitPropagation();
126         Utils.enableChangeForSelf(AlarmManager.ENABLE_USE_EXACT_ALARM);
127     }
128 
129     @After
tearDown()130     public void tearDown() throws Exception {
131         mAm.cancel(mMockAlarmReceiver);
132         mAm.cancel(mMockAlarmReceiver2);
133 
134         mDeviceConfigHelper.restoreAll();
135         mContext.unregisterReceiver(mMockAlarmReceiver);
136         mContext.unregisterReceiver(mMockAlarmReceiver2);
137         toggleIdleMode(false);
138         Utils.resetChange(AlarmManager.ENABLE_USE_EXACT_ALARM, mContext.getOpPackageName());
139     }
140 
141     @Test
testSetTypes()142     public void testSetTypes() {
143         // We cannot test non wakeup alarms reliably because they are held up until the
144         // device becomes interactive
145 
146         // test parameter type is RTC_WAKEUP
147         mMockAlarmReceiver.reset();
148         mWakeupTime = System.currentTimeMillis() + SNOOZE_DELAY;
149         mAm.setExact(AlarmManager.RTC_WAKEUP, mWakeupTime, mSender);
150 
151         new PollingCheck(SNOOZE_DELAY + TIME_DELAY) {
152             @Override
153             protected boolean check() {
154                 return mMockAlarmReceiver.isAlarmed();
155             }
156         }.run();
157         assertEquals(mMockAlarmReceiver.getRtcTime(), mWakeupTime, TIME_DELTA);
158 
159         // test parameter type is ELAPSED_REALTIME_WAKEUP
160         mMockAlarmReceiver.reset();
161         mWakeupTime = SystemClock.elapsedRealtime() + SNOOZE_DELAY;
162         mAm.setExact(AlarmManager.ELAPSED_REALTIME_WAKEUP, mWakeupTime, mSender);
163         new PollingCheck(SNOOZE_DELAY + TIME_DELAY) {
164             @Override
165             protected boolean check() {
166                 return mMockAlarmReceiver.isAlarmed();
167             }
168         }.run();
169         assertEquals(mMockAlarmReceiver.getElapsedTime(), mWakeupTime, TIME_DELTA);
170     }
171 
172     @Test
testAlarmTriggersImmediatelyIfSetTimeIsNegative()173     public void testAlarmTriggersImmediatelyIfSetTimeIsNegative() {
174         // An alarm with a negative wakeup time should be triggered immediately.
175         // This exercises a workaround for a limitation of the /dev/alarm driver
176         // that would instead cause such alarms to never be triggered.
177         mMockAlarmReceiver.reset();
178         mWakeupTime = -1000;
179         mAm.set(AlarmManager.RTC_WAKEUP, mWakeupTime, mSender);
180         new PollingCheck(TIME_DELAY) {
181             @Override
182             protected boolean check() {
183                 return mMockAlarmReceiver.isAlarmed();
184             }
185         }.run();
186     }
187 
188     @Test
noBinderOverflowWithListenerSpam()189     public void noBinderOverflowWithListenerSpam() {
190         final long now = SystemClock.elapsedRealtime();
191         for (int i = 0; i < 51_500; i++) {
192             // Need it to be larger than 51200, which is the global reference table size.
193             mAm.setExact(AlarmManager.ELAPSED_REALTIME, now + 10_000 + i * 1000, "spam-test",
194                     mMockAlarmReceiver, null);
195         }
196         // Binder overflow should crash the system, so if we're out of the loop, the test passed.
197     }
198 
199     /**
200      * We run a few trials of an exact alarm that is placed within an inexact alarm's window of
201      * opportunity, and mandate that the average observed delivery skew between the two be
202      * statistically significant -- i.e. that the two alarms are not being coalesced.
203      */
204     @Test
testExactAlarmBatching()205     public void testExactAlarmBatching() throws InterruptedException {
206         final long windowLength = 6_000;
207         int deliveriesTogether = 0;
208         for (int i = 0; i < 5; i++) {
209             mMockAlarmReceiver.reset();
210             mMockAlarmReceiver2.reset();
211 
212             final long now = SystemClock.elapsedRealtime();
213             final long windowStart = now + 1000;
214             final long exactStart = windowStart + windowLength / 2;
215 
216             mAm.setWindow(AlarmManager.ELAPSED_REALTIME_WAKEUP, windowStart, windowLength, mSender);
217             mAm.setExact(AlarmManager.ELAPSED_REALTIME_WAKEUP, exactStart, mSender2);
218 
219             // Wait until the end of the window.
220             Thread.sleep(windowStart - now + windowLength);
221             PollingCheck.waitFor(1000, mMockAlarmReceiver::isAlarmed,
222                     "Inexact alarm did not fire by the end of the window");
223 
224             // If needed, wait until the time of the exact alarm.
225             final long timeToExact = Math.max(exactStart - SystemClock.elapsedRealtime(), 0);
226             Thread.sleep(timeToExact);
227             PollingCheck.waitFor(1000, mMockAlarmReceiver2::isAlarmed,
228                     "Exact alarm did not fire as expected");
229 
230             final long delta = Math.abs(
231                     mMockAlarmReceiver2.getElapsedTime() - mMockAlarmReceiver.getElapsedTime());
232             Log.i(TAG, "testExactAlarmBatching: [" + i + "]  delta = " + delta);
233             if (delta < FAIL_DELTA) {
234                 deliveriesTogether++;
235                 if (deliveriesTogether > 1) {
236                     fail("More than 1 deliveries with exact alarms close to inexact alarms");
237                 }
238             }
239         }
240     }
241 
242     @Test
testSetExactWithWorkSource()243     public void testSetExactWithWorkSource() throws Exception {
244         final int myUid = mContext.getPackageManager().getPackageUid(mContext.getOpPackageName(),
245                 0);
246         final long futurityMs = 1000;
247         mMockAlarmReceiver.reset();
248 
249         SystemUtil.runWithShellPermissionIdentity(
250                 () -> mAm.setExact(AlarmManager.ELAPSED_REALTIME_WAKEUP,
251                         SystemClock.elapsedRealtime() + futurityMs, "test-tag", r -> r.run(),
252                         new WorkSource(myUid), mMockAlarmReceiver),
253                 Manifest.permission.UPDATE_DEVICE_STATS);
254 
255         Thread.sleep(futurityMs);
256         PollingCheck.waitFor(2000, mMockAlarmReceiver::isAlarmed,
257                 "Exact alarm with work source did not fire as expected");
258     }
259 
260     @Test
testSetExact()261     public void testSetExact() throws Exception {
262         final long futurityMs = 1000;
263         mMockAlarmReceiver.reset();
264         mAm.setExact(AlarmManager.ELAPSED_REALTIME_WAKEUP,
265                 SystemClock.elapsedRealtime() + futurityMs, "test-tag", mMockAlarmReceiver, null);
266 
267         Thread.sleep(futurityMs);
268         PollingCheck.waitFor(2000, mMockAlarmReceiver::isAlarmed,
269                 "Exact alarm with work source did not fire as expected");
270     }
271 
272     @Test
testSetWindow()273     public void testSetWindow() throws Exception {
274         final long futurityMs = 1000;
275         final long windowLength = 2000;
276         mMockAlarmReceiver.reset();
277         mAm.setWindow(AlarmManager.ELAPSED_REALTIME_WAKEUP,
278                 SystemClock.elapsedRealtime() + futurityMs, windowLength, "test-tag", Runnable::run,
279                 null, mMockAlarmReceiver);
280 
281         Thread.sleep(futurityMs + windowLength);
282         PollingCheck.waitFor(4000, mMockAlarmReceiver::isAlarmed,
283                 "Window alarm did not fire as expected");
284     }
285 
286     @Test
testSetRepeating()287     public void testSetRepeating() {
288         mMockAlarmReceiver.reset();
289         mWakeupTime = System.currentTimeMillis() + TEST_ALARM_FUTURITY;
290         mAm.setRepeating(AlarmManager.RTC_WAKEUP, mWakeupTime, REPEAT_PERIOD, mSender);
291 
292         // wait beyond the initial alarm's possible delivery window to verify that it fires the
293         // first time
294         new PollingCheck(TEST_ALARM_FUTURITY + REPEAT_PERIOD) {
295             @Override
296             protected boolean check() {
297                 return mMockAlarmReceiver.isAlarmed();
298             }
299         }.run();
300 
301         // Now reset the receiver and wait for the intended repeat alarm to fire as expected
302         mMockAlarmReceiver.reset();
303         new PollingCheck(REPEAT_PERIOD * 2) {
304             @Override
305             protected boolean check() {
306                 return mMockAlarmReceiver.isAlarmed();
307             }
308         }.run();
309 
310         mAm.cancel(mSender);
311     }
312 
isDeviceIdleEnabled()313     private static boolean isDeviceIdleEnabled() {
314         final String output = SystemUtil.runShellCommand("cmd deviceidle enabled deep").trim();
315         return Integer.parseInt(output) != 0;
316     }
317 
toggleIdleMode(boolean on)318     private void toggleIdleMode(boolean on) {
319         SystemUtil.runShellCommand("cmd deviceidle " + (on ? "force-idle deep" : "unforce"));
320         if (!on) {
321             // Make sure the device doesn't stay idle, even after unforcing.
322             SystemUtil.runShellCommand("cmd deviceidle motion");
323         }
324         PollingCheck.waitFor(10_000, () -> (on == mPowerManager.isDeviceIdleMode()),
325                 "Could not set doze state to " + on);
326     }
327 
328     @Test(expected = SecurityException.class)
testSetPrioritizedWithoutPermission()329     public void testSetPrioritizedWithoutPermission() {
330         mAm.setPrioritized(AlarmManager.ELAPSED_REALTIME_WAKEUP, 20, 10,
331                 "testSetPrioritizedWithoutPermission", r -> r.run(), mMockAlarmReceiver);
332     }
333 
334     @Test
testSetPrioritized()335     public void testSetPrioritized() throws InterruptedException {
336         assumeTrue("Doze not supported on this device", isDeviceIdleEnabled());
337 
338         mMockAlarmReceiver.reset();
339         mMockAlarmReceiver2.reset();
340 
341         final long trigger1 = SystemClock.elapsedRealtime() + 1000;
342         final long trigger2 = SystemClock.elapsedRealtime() + 2000;
343         SystemUtil.runWithShellPermissionIdentity(
344                 () -> mAm.setPrioritized(AlarmManager.ELAPSED_REALTIME_WAKEUP, trigger1, 10,
345                         "test-1", r -> r.run(), mMockAlarmReceiver));
346         SystemUtil.runWithShellPermissionIdentity(
347                 () -> mAm.setPrioritized(AlarmManager.ELAPSED_REALTIME_WAKEUP, trigger2, 10,
348                         "test-2", r -> r.run(), mMockAlarmReceiver2));
349 
350         Thread.sleep(2010);
351         PollingCheck.waitFor(1000,
352                 () -> (mMockAlarmReceiver.isAlarmed() && mMockAlarmReceiver2.isAlarmed()));
353 
354         toggleIdleMode(true);
355         // Ensure no previous alarm in doze throttles the next one.
356         Thread.sleep(PRIORITY_ALARM_DELAY);
357         mMockAlarmReceiver.reset();
358         mMockAlarmReceiver2.reset();
359 
360         final long trigger3 = SystemClock.elapsedRealtime() + 1000;
361         final long trigger4 = SystemClock.elapsedRealtime() + 2000;
362         SystemUtil.runWithShellPermissionIdentity(
363                 () -> mAm.setPrioritized(AlarmManager.ELAPSED_REALTIME_WAKEUP, trigger3, 10,
364                         "test-3", r -> r.run(), mMockAlarmReceiver));
365         SystemUtil.runWithShellPermissionIdentity(
366                 () -> mAm.setPrioritized(AlarmManager.ELAPSED_REALTIME_WAKEUP, trigger4, 10,
367                         "test-4", r -> r.run(), mMockAlarmReceiver2));
368         Thread.sleep(1010);
369         PollingCheck.waitFor(1000, mMockAlarmReceiver::isAlarmed,
370                 "First alarm not received as expected in doze");
371 
372         Thread.sleep(1000);
373         assertFalse("Second alarm fired prematurely while in doze",
374                 mMockAlarmReceiver2.isAlarmed());
375 
376         final long timeToNextAlarm = mMockAlarmReceiver.getElapsedTime() + PRIORITY_ALARM_DELAY
377                 - SystemClock.elapsedRealtime();
378         Thread.sleep(Math.max(0, timeToNextAlarm));
379         PollingCheck.waitFor(1000, mMockAlarmReceiver2::isAlarmed,
380                 "Second alarm not received as expected in doze");
381 
382 
383         final long firstAlarmTime = mMockAlarmReceiver.getElapsedTime();
384         final long secondAlarmTime = mMockAlarmReceiver2.getElapsedTime();
385         assertTrue("First alarm: " + firstAlarmTime + " and second alarm: " + secondAlarmTime
386                         + " not separated enough",
387                 (secondAlarmTime - firstAlarmTime) > (PRIORITY_ALARM_DELAY - FAIL_DELTA));
388     }
389 
390     @Test
testCancel()391     public void testCancel() {
392         mMockAlarmReceiver.reset();
393         mMockAlarmReceiver2.reset();
394 
395         // set two alarms
396         final long now = System.currentTimeMillis();
397         final long when1 = now + TEST_ALARM_FUTURITY;
398         mAm.setExact(AlarmManager.RTC_WAKEUP, when1, mSender);
399         final long when2 = when1 + TIME_DELTA; // will fire after when1's target time
400         mAm.setExact(AlarmManager.RTC_WAKEUP, when2, mSender2);
401 
402         // cancel the earlier one
403         mAm.cancel(mSender);
404 
405         // and verify that only the later one fired
406         new PollingCheck(when2 - now + TIME_DELAY) {
407             @Override
408             protected boolean check() {
409                 return mMockAlarmReceiver2.isAlarmed();
410             }
411         }.run();
412 
413         assertFalse(mMockAlarmReceiver.isAlarmed());
414     }
415 
416     @Test
testCancelAll()417     public void testCancelAll() throws InterruptedException {
418         mMockAlarmReceiver.reset();
419         mMockAlarmReceiver2.reset();
420 
421         final long when1Rtc = System.currentTimeMillis() + TEST_ALARM_FUTURITY;
422         mAm.setExact(AlarmManager.RTC_WAKEUP, when1Rtc, mSender);
423 
424         final long nowElapsed = SystemClock.elapsedRealtime();
425         // setting the second one to fire after the first one
426         final long when2Elapsed = nowElapsed + TEST_ALARM_FUTURITY + TIME_DELTA;
427         mAm.setExact(AlarmManager.ELAPSED_REALTIME_WAKEUP, when2Elapsed, "test-tag",
428                 mMockAlarmReceiver2, null);
429 
430         mAm.cancelAll();
431 
432         // wait till some time past the scheduled expiration of the second one
433         Thread.sleep(when2Elapsed - nowElapsed + TIME_DELAY);
434 
435         assertFalse(mMockAlarmReceiver.isAlarmed() || mMockAlarmReceiver2.isAlarmed());
436     }
437 
438     @Test
testSetAlarmClock()439     public void testSetAlarmClock() {
440         assumeTrue(ApiLevelUtil.isAtLeast(Build.VERSION_CODES.LOLLIPOP));
441 
442         mMockAlarmReceiver.reset();
443         mMockAlarmReceiver2.reset();
444 
445         // Set first alarm clock.
446         final long wakeupTimeFirst = System.currentTimeMillis()
447                 + 2 * TEST_ALARM_FUTURITY;
448         mAm.setAlarmClock(new AlarmClockInfo(wakeupTimeFirst, null), mSender);
449 
450         // Verify getNextAlarmClock returns first alarm clock.
451         AlarmClockInfo nextAlarmClock = mAm.getNextAlarmClock();
452         assertEquals(wakeupTimeFirst, nextAlarmClock.getTriggerTime());
453         assertNull(nextAlarmClock.getShowIntent());
454 
455         // Set second alarm clock, earlier than first.
456         final long wakeupTimeSecond = System.currentTimeMillis()
457                 + TEST_ALARM_FUTURITY;
458         PendingIntent showIntentSecond = PendingIntent.getBroadcast(mContext, 0,
459                 new Intent(mContext, BasicApiTests.class).setAction("SHOW_INTENT"),
460                 PendingIntent.FLAG_IMMUTABLE);
461         mAm.setAlarmClock(new AlarmClockInfo(wakeupTimeSecond, showIntentSecond),
462                 mSender2);
463 
464         // Verify getNextAlarmClock returns second alarm clock now.
465         nextAlarmClock = mAm.getNextAlarmClock();
466         assertEquals(wakeupTimeSecond, nextAlarmClock.getTriggerTime());
467         assertEquals(showIntentSecond, nextAlarmClock.getShowIntent());
468 
469         // Cancel second alarm.
470         mAm.cancel(mSender2);
471 
472         // Verify getNextAlarmClock returns first alarm clock again.
473         nextAlarmClock = mAm.getNextAlarmClock();
474         assertEquals(wakeupTimeFirst, nextAlarmClock.getTriggerTime());
475         assertNull(nextAlarmClock.getShowIntent());
476 
477         // Wait for first alarm to trigger.
478         assertFalse(mMockAlarmReceiver.isAlarmed());
479         new PollingCheck(2 * TEST_ALARM_FUTURITY + TIME_DELAY) {
480             @Override
481             protected boolean check() {
482                 return mMockAlarmReceiver.isAlarmed();
483             }
484         }.run();
485 
486         // Verify first alarm fired at the right time.
487         assertEquals(mMockAlarmReceiver.getRtcTime(), wakeupTimeFirst, TIME_DELTA);
488 
489         // Verify second alarm didn't fire.
490         assertFalse(mMockAlarmReceiver2.isAlarmed());
491 
492         // Verify the next alarm is not returning neither the first nor the second alarm.
493         nextAlarmClock = mAm.getNextAlarmClock();
494         assertNotEquals(wakeupTimeFirst,
495                 nextAlarmClock != null ? nextAlarmClock.getTriggerTime() : 0);
496         assertNotEquals(wakeupTimeSecond,
497                 nextAlarmClock != null ? nextAlarmClock.getTriggerTime() : 0);
498     }
499 
500     /**
501      * this class receives alarm from AlarmManagerTest
502      */
503     public static class MockAlarmReceiver extends BroadcastReceiver
504             implements AlarmManager.OnAlarmListener {
505         private final Object mSync = new Object();
506         public final String mTargetAction;
507 
508         @GuardedBy("mSync")
509         private boolean mAlarmed = false;
510         @GuardedBy("mSync")
511         private long mElapsedTime = 0;
512         @GuardedBy("mSync")
513         private long mRtcTime = 0;
514 
MockAlarmReceiver(String targetAction)515         public MockAlarmReceiver(String targetAction) {
516             mTargetAction = targetAction;
517         }
518 
519         @Override
onReceive(Context context, Intent intent)520         public void onReceive(Context context, Intent intent) {
521             final String action = intent.getAction();
522             if (action.equals(mTargetAction)) {
523                 synchronized (mSync) {
524                     mAlarmed = true;
525                     mElapsedTime = SystemClock.elapsedRealtime();
526                     mRtcTime = System.currentTimeMillis();
527                 }
528             }
529         }
530 
getElapsedTime()531         public long getElapsedTime() {
532             synchronized (mSync) {
533                 return mElapsedTime;
534             }
535         }
536 
getRtcTime()537         public long getRtcTime() {
538             synchronized (mSync) {
539                 return mRtcTime;
540             }
541         }
542 
reset()543         public void reset() {
544             synchronized (mSync) {
545                 mAlarmed = false;
546                 mRtcTime = mElapsedTime = 0;
547             }
548         }
549 
isAlarmed()550         public boolean isAlarmed() {
551             synchronized (mSync) {
552                 return mAlarmed;
553             }
554         }
555 
556         @Override
onAlarm()557         public void onAlarm() {
558             onReceive(null, new Intent(mTargetAction));
559         }
560     }
561 }
562