1 /* 2 * Copyright (C) 2019 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 android.app.AlarmManager.ELAPSED_REALTIME_WAKEUP; 20 import static android.app.AlarmManager.RTC_WAKEUP; 21 22 import static org.junit.Assert.assertFalse; 23 import static org.junit.Assert.assertTrue; 24 25 import android.alarmmanager.util.AlarmManagerDeviceConfigHelper; 26 import android.app.AlarmManager; 27 import android.app.PendingIntent; 28 import android.content.BroadcastReceiver; 29 import android.content.Context; 30 import android.content.Intent; 31 import android.content.IntentFilter; 32 import android.os.SystemClock; 33 import android.platform.test.annotations.AppModeFull; 34 import android.util.Log; 35 36 import androidx.test.InstrumentationRegistry; 37 import androidx.test.filters.LargeTest; 38 import androidx.test.runner.AndroidJUnit4; 39 40 import com.android.compatibility.common.util.BatteryUtils; 41 import com.android.compatibility.common.util.SystemUtil; 42 43 import org.junit.After; 44 import org.junit.Before; 45 import org.junit.Test; 46 import org.junit.runner.RunWith; 47 48 import java.util.concurrent.CountDownLatch; 49 import java.util.concurrent.TimeUnit; 50 51 /** 52 * Tests that system time changes are handled appropriately for alarms 53 */ 54 @AppModeFull 55 @LargeTest 56 @RunWith(AndroidJUnit4.class) 57 public class TimeChangeTests { 58 private static final String TAG = TimeChangeTests.class.getSimpleName(); 59 private static final String ACTION_ALARM = "android.alarmmanager.cts.ACTION_ALARM"; 60 private static final long DEFAULT_WAIT_MILLIS = 5_000; 61 private static final long MILLIS_IN_MINUTE = 60_000; 62 63 private final Context mContext = InstrumentationRegistry.getTargetContext(); 64 private final AlarmManager mAlarmManager = mContext.getSystemService(AlarmManager.class); 65 private AlarmManagerDeviceConfigHelper mConfigHelper = new AlarmManagerDeviceConfigHelper(); 66 private PendingIntent mAlarmPi; 67 private long mTestStartRtc; 68 private long mTestStartElapsed; 69 private boolean mTimeChanged; 70 private boolean mAutoTimeEnabled; 71 72 final CountDownLatch mAlarmLatch = new CountDownLatch(1); 73 74 private BroadcastReceiver mAlarmReceiver = new BroadcastReceiver() { 75 @Override 76 public void onReceive(Context context, Intent intent) { 77 switch (intent.getAction()) { 78 case ACTION_ALARM: 79 if (mAlarmLatch != null) { 80 mAlarmLatch.countDown(); 81 } 82 break; 83 default: 84 Log.e(TAG, "Received unexpected action " + intent.getAction()); 85 } 86 } 87 }; 88 setTime(long rtcToSet)89 private void setTime(long rtcToSet) { 90 Log.i(TAG, "Changing system time to " + rtcToSet); 91 SystemUtil.runWithShellPermissionIdentity(() -> mAlarmManager.setTime(rtcToSet)); 92 mTimeChanged = true; 93 } 94 isAutoTimeEnabled()95 private boolean isAutoTimeEnabled() { 96 String auto_time = SystemUtil.runShellCommand("settings get global auto_time"); 97 return auto_time.trim().equals("1"); 98 } 99 100 @Before setUp()101 public void setUp() throws Exception { 102 final Intent alarmIntent = new Intent(ACTION_ALARM) 103 .setPackage(mContext.getPackageName()) 104 .addFlags(Intent.FLAG_RECEIVER_FOREGROUND); 105 mAlarmPi = PendingIntent.getBroadcast(mContext, 0, alarmIntent, PendingIntent.FLAG_MUTABLE); 106 final IntentFilter alarmFilter = new IntentFilter(ACTION_ALARM); 107 mContext.registerReceiver(mAlarmReceiver, alarmFilter, 108 Context.RECEIVER_EXPORTED_UNAUDITED); 109 mConfigHelper.with("min_futurity", 0L).commitAndAwaitPropagation(); 110 BatteryUtils.runDumpsysBatteryUnplug(); 111 mTestStartRtc = System.currentTimeMillis(); 112 mTestStartElapsed = SystemClock.elapsedRealtime(); 113 mAutoTimeEnabled = isAutoTimeEnabled(); 114 // Disable auto time as tests might fail if the system restores time while they are running 115 SystemUtil.runShellCommand("settings put global auto_time 0"); 116 } 117 118 @Test elapsedAlarmsUnaffected()119 public void elapsedAlarmsUnaffected() throws Exception { 120 final long delayElapsed = 5_000; 121 final long expectedTriggerElapsed = mTestStartElapsed + delayElapsed; 122 mAlarmManager.setExact(ELAPSED_REALTIME_WAKEUP, expectedTriggerElapsed, mAlarmPi); 123 final long newRtc = mTestStartRtc - 32 * MILLIS_IN_MINUTE; // arbitrary, shouldn't matter 124 setTime(newRtc); 125 Thread.sleep(delayElapsed); 126 assertTrue("Elapsed alarm did not fire as expected after time change", 127 mAlarmLatch.await(DEFAULT_WAIT_MILLIS, TimeUnit.MILLISECONDS)); 128 } 129 130 @Test rtcAlarmsRescheduled()131 public void rtcAlarmsRescheduled() throws Exception { 132 final long newRtc = mTestStartRtc + 14 * MILLIS_IN_MINUTE; // arbitrary, but in the future 133 final long delayRtc = 4_231; 134 final long expectedTriggerRtc = newRtc + delayRtc; 135 mAlarmManager.setExact(RTC_WAKEUP, expectedTriggerRtc, mAlarmPi); 136 Thread.sleep(delayRtc); 137 assertFalse("Alarm fired before time was changed", 138 mAlarmLatch.await(DEFAULT_WAIT_MILLIS, TimeUnit.MILLISECONDS)); 139 setTime(newRtc); 140 Thread.sleep(delayRtc); 141 assertTrue("Alarm did not fire as expected after time was changed", 142 mAlarmLatch.await(DEFAULT_WAIT_MILLIS, TimeUnit.MILLISECONDS)); 143 } 144 145 @After tearDown()146 public void tearDown() { 147 mConfigHelper.restoreAll(); 148 BatteryUtils.runDumpsysBatteryReset(); 149 if (mTimeChanged) { 150 // Make an attempt at resetting the clock to normal 151 final long testDuration = SystemClock.elapsedRealtime() - mTestStartElapsed; 152 final long expectedCorrectRtc = mTestStartRtc + testDuration; 153 setTime(expectedCorrectRtc); 154 } 155 if (mAutoTimeEnabled) { 156 // Restore auto time 157 SystemUtil.runShellCommand("settings put global auto_time 1"); 158 } 159 } 160 } 161