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