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