• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 android.alarmmanager.cts.AlarmReceiver.getAlarmSender;
20 
21 import static org.junit.Assert.assertEquals;
22 import static org.junit.Assert.assertFalse;
23 import static org.junit.Assert.assertNotNull;
24 import static org.junit.Assert.assertTrue;
25 import static org.junit.Assume.assumeFalse;
26 import static org.junit.Assume.assumeTrue;
27 
28 import android.alarmmanager.alarmtestapp.cts.TestAlarmReceiver;
29 import android.alarmmanager.alarmtestapp.cts.TestAlarmScheduler;
30 import android.alarmmanager.alarmtestapp.cts.common.FgsTester;
31 import android.alarmmanager.alarmtestapp.cts.common.PermissionStateChangedReceiver;
32 import android.alarmmanager.alarmtestapp.cts.common.RequestReceiver;
33 import android.alarmmanager.util.AlarmManagerDeviceConfigHelper;
34 import android.alarmmanager.util.Utils;
35 import android.app.Activity;
36 import android.app.AlarmManager;
37 import android.app.AppOpsManager;
38 import android.content.BroadcastReceiver;
39 import android.content.ComponentName;
40 import android.content.Context;
41 import android.content.Intent;
42 import android.content.IntentFilter;
43 import android.content.pm.PackageManager;
44 import android.net.Uri;
45 import android.os.PowerWhitelistManager;
46 import android.os.Process;
47 import android.os.SystemClock;
48 import android.os.UserHandle;
49 import android.platform.test.annotations.AppModeFull;
50 import android.provider.Settings;
51 import android.util.Log;
52 
53 import androidx.test.InstrumentationRegistry;
54 import androidx.test.runner.AndroidJUnit4;
55 
56 import com.android.compatibility.common.util.AppOpsUtils;
57 import com.android.compatibility.common.util.FeatureUtil;
58 import com.android.compatibility.common.util.ShellUtils;
59 import com.android.compatibility.common.util.SystemUtil;
60 import com.android.compatibility.common.util.TestUtils;
61 
62 import org.junit.After;
63 import org.junit.Before;
64 import org.junit.Rule;
65 import org.junit.Test;
66 import org.junit.runner.Description;
67 import org.junit.runner.RunWith;
68 
69 import java.io.IOException;
70 import java.util.Random;
71 import java.util.concurrent.CountDownLatch;
72 import java.util.concurrent.TimeUnit;
73 import java.util.concurrent.atomic.AtomicInteger;
74 import java.util.concurrent.atomic.AtomicReference;
75 
76 @AppModeFull
77 @RunWith(AndroidJUnit4.class)
78 public class ExactAlarmsTest {
79     /**
80      * TODO (b/182835530): Add more tests for the following:
81      *
82      * Pre-S apps can:
83      * - use setAlarmClock freely -- no temp-allowlist
84      * - use setExactAndAWI with 7 / hr quota with standby and temp-allowlist
85      * - use setInexactAndAWI with 7 / hr quota with standby-bucket "ACTIVE" and temp-allowlist
86      *
87      * S+ apps with permission can:
88      * - use setInexactAWI with low quota + standby and *no* temp-allowlist.
89      */
90     private static final String TAG = ExactAlarmsTest.class.getSimpleName();
91 
92     private static final String TEST_APP_30 = "android.alarmmanager.alarmtestapp.cts.sdk30";
93     private static final String TEST_APP_WITH_SCHEDULE_EXACT_ALARM_32 =
94             "android.alarmmanager.alarmtestapp.cts.user_permission_32";
95     private static final String TEST_APP_WITH_USE_EXACT_ALARM_32 =
96             "android.alarmmanager.alarmtestapp.cts.policy_permission_32";
97 
98     private static final int ALLOW_WHILE_IDLE_QUOTA = 5;
99     private static final long ALLOW_WHILE_IDLE_WINDOW = 10_000;
100     private static final int ALLOW_WHILE_IDLE_COMPAT_QUOTA = 3;
101     private static final long ALLOW_WHILE_IDLE_COMPAT_WINDOW = 10_000;
102 
103     /**
104      * Waiting generously long for success because the system can sometimes be slow to
105      * provide expected behavior.
106      * A different and shorter duration should be used while waiting for no-failure, because
107      * even if the system is slow to fail in some cases, it would still cause some
108      * flakiness and get flagged for investigation.
109      */
110     private static final long DEFAULT_WAIT_FOR_SUCCESS = 30_000;
111 
112     private static final String TEST_APP_PACKAGE = "android.alarmmanager.alarmtestapp.cts";
113 
114     private static final Context sContext = InstrumentationRegistry.getTargetContext();
115     private final AlarmManager mAlarmManager = sContext.getSystemService(AlarmManager.class);
116     private final PowerWhitelistManager mWhitelistManager = sContext.getSystemService(
117             PowerWhitelistManager.class);
118     private final PackageManager mPackageManager = sContext.getPackageManager();
119     private final ComponentName mPermissionChangeReceiver = new ComponentName(TEST_APP_PACKAGE,
120             PermissionStateChangedReceiver.class.getName());
121     private final ComponentName mPermissionChangeReceiver32 = new ComponentName(
122             TEST_APP_WITH_SCHEDULE_EXACT_ALARM_32,
123             PermissionStateChangedReceiver.class.getName());
124 
125     private final AlarmManagerDeviceConfigHelper mDeviceConfigHelper =
126             new AlarmManagerDeviceConfigHelper();
127     private final Random mIdGenerator = new Random(6789);
128 
129     @Rule
130     public DumpLoggerRule mFailLoggerRule = new DumpLoggerRule(TAG) {
131         @Override
132         protected void failed(Throwable e, Description description) {
133             super.failed(e, description);
134             AlarmReceiver.dumpState();
135         }
136     };
137 
138     @Before
updateAlarmManagerConstants()139     public void updateAlarmManagerConstants() {
140         mDeviceConfigHelper.with("min_futurity", 0L)
141                 .with("allow_while_idle_quota", ALLOW_WHILE_IDLE_QUOTA)
142                 .with("allow_while_idle_compat_quota", ALLOW_WHILE_IDLE_COMPAT_QUOTA)
143                 .with("allow_while_idle_window", ALLOW_WHILE_IDLE_WINDOW)
144                 .with("allow_while_idle_compat_window", ALLOW_WHILE_IDLE_COMPAT_WINDOW)
145                 .commitAndAwaitPropagation();
146     }
147 
148     @Before
enableChanges()149     public void enableChanges() {
150         Utils.enableChangeForSelf(AlarmManager.REQUIRE_EXACT_ALARM_PERMISSION);
151         Utils.enableChangeForSelf(AlarmManager.ENABLE_USE_EXACT_ALARM);
152         Utils.enableChange(AlarmManager.SCHEDULE_EXACT_ALARM_DENIED_BY_DEFAULT, TEST_APP_PACKAGE,
153                 sContext.getUserId());
154     }
155 
156     @After
resetChanges()157     public void resetChanges() {
158         Utils.resetChange(AlarmManager.REQUIRE_EXACT_ALARM_PERMISSION, sContext.getOpPackageName());
159         Utils.resetChange(AlarmManager.ENABLE_USE_EXACT_ALARM, sContext.getOpPackageName());
160         Utils.resetChange(AlarmManager.SCHEDULE_EXACT_ALARM_DENIED_BY_DEFAULT, TEST_APP_PACKAGE);
161     }
162 
163     @After
removeFromWhitelists()164     public void removeFromWhitelists() {
165         removeFromWhitelists(sContext.getOpPackageName());
166         removeFromWhitelists(TEST_APP_PACKAGE);
167     }
168 
removeFromWhitelists(String packageName)169     private void removeFromWhitelists(String packageName) {
170         SystemUtil.runWithShellPermissionIdentity(
171                 () -> mWhitelistManager.removeFromWhitelist(packageName));
172         SystemUtil.runShellCommand("cmd deviceidle tempwhitelist -r " + packageName);
173     }
174 
175     @After
restoreBatteryState()176     public void restoreBatteryState() {
177         SystemUtil.runShellCommand("cmd deviceidle unforce");
178         SystemUtil.runShellCommandForNoOutput("dumpsys battery reset");
179     }
180 
181     @After
restorePermissionReceiverState()182     public void restorePermissionReceiverState() {
183         SystemUtil.runWithShellPermissionIdentity(
184                 () -> mPackageManager.setComponentEnabledSetting(mPermissionChangeReceiver,
185                         PackageManager.COMPONENT_ENABLED_STATE_DEFAULT,
186                         PackageManager.DONT_KILL_APP));
187     }
188 
189     @After
resetAppOps()190     public void resetAppOps() throws IOException {
191         AppOpsUtils.reset(TEST_APP_PACKAGE);
192         AppOpsUtils.reset(TEST_APP_30);
193     }
194 
195     @After
restoreAlarmManagerConstants()196     public void restoreAlarmManagerConstants() throws IOException {
197         mDeviceConfigHelper.restoreAll();
198     }
199 
revokeAppOp(String packageName)200     private void revokeAppOp(String packageName) {
201         setAppOp(packageName, AppOpsManager.MODE_IGNORED);
202     }
203 
setAppOp(String packageName, int mode)204     static void setAppOp(String packageName, int mode) {
205         final int uid = Utils.getPackageUid(packageName);
206         AppOpsUtils.setUidMode(uid, AppOpsManager.OPSTR_SCHEDULE_EXACT_ALARM, mode);
207     }
208 
getCanScheduleExactAlarmFromTestApp(String testAppName)209     private boolean getCanScheduleExactAlarmFromTestApp(String testAppName) throws Exception {
210         final String apiResult = assertResultFromTestApp(
211                 RequestReceiver.ACTION_GET_CAN_SCHEDULE_EXACT_ALARM, testAppName,
212                 Activity.RESULT_OK);
213         return Boolean.parseBoolean(apiResult);
214     }
215 
216     @Test
noPermissionByDefault()217     public void noPermissionByDefault() throws Exception {
218         setAppOp(TEST_APP_PACKAGE, AppOpsManager.MODE_DEFAULT);
219         assertFalse(getCanScheduleExactAlarmFromTestApp(TEST_APP_PACKAGE));
220     }
221 
222     @Test
noPermissionWhenIgnored()223     public void noPermissionWhenIgnored() throws Exception {
224         revokeAppOp(TEST_APP_PACKAGE);
225         assertFalse(getCanScheduleExactAlarmFromTestApp(TEST_APP_PACKAGE));
226     }
227 
228     @Test
hasPermissionWhenAllowed()229     public void hasPermissionWhenAllowed() throws Exception {
230         setAppOp(TEST_APP_PACKAGE, AppOpsManager.MODE_ALLOWED);
231         assertTrue(getCanScheduleExactAlarmFromTestApp(TEST_APP_PACKAGE));
232     }
233 
234     @Test
canScheduleExactAlarmWithPolicyPermission()235     public void canScheduleExactAlarmWithPolicyPermission() {
236         assertTrue(mAlarmManager.canScheduleExactAlarms());
237     }
238 
239     @Test
canScheduleExactAlarmWithPolicyPermissionSdk32()240     public void canScheduleExactAlarmWithPolicyPermissionSdk32() throws Exception {
241         // Policy permission is not enabled at SDK 32.
242         assertFalse(getCanScheduleExactAlarmFromTestApp(TEST_APP_WITH_USE_EXACT_ALARM_32));
243     }
244 
245     @Test
canScheduleExactAlarmWithUserPermissionSdk32()246     public void canScheduleExactAlarmWithUserPermissionSdk32() throws Exception {
247         // Should be allowed by default.
248         assertTrue(getCanScheduleExactAlarmFromTestApp(TEST_APP_WITH_SCHEDULE_EXACT_ALARM_32));
249     }
250 
251     @Test
canScheduleExactAlarmSdk30()252     public void canScheduleExactAlarmSdk30() throws Exception {
253         revokeAppOp(TEST_APP_30);
254         assertTrue(getCanScheduleExactAlarmFromTestApp(TEST_APP_30));
255     }
256 
assertResultFromTestApp(String requestAction, String testAppName, int expectedResult)257     private static String assertResultFromTestApp(String requestAction, String testAppName,
258             int expectedResult) throws InterruptedException {
259         final CountDownLatch resultLatch = new CountDownLatch(1);
260         final AtomicInteger result = new AtomicInteger(-1);
261         final AtomicReference<String> resultData = new AtomicReference<>(null);
262 
263         final Intent requestToTestApp = new Intent(requestAction)
264                 .setClassName(testAppName, RequestReceiver.class.getName())
265                 .addFlags(Intent.FLAG_RECEIVER_FOREGROUND);
266         sContext.sendOrderedBroadcast(requestToTestApp, null, new BroadcastReceiver() {
267             @Override
268             public void onReceive(Context context, Intent intent) {
269                 result.set(getResultCode());
270                 resultData.set(getResultData());
271                 resultLatch.countDown();
272             }
273         }, null, Activity.RESULT_CANCELED, null, null);
274 
275         assertTrue("Timed out waiting for response from helper app " + testAppName,
276                 resultLatch.await(10, TimeUnit.SECONDS));
277         assertEquals(expectedResult, result.get());
278         return resultData.get();
279     }
280 
whitelistTestApp()281     private void whitelistTestApp() {
282         SystemUtil.runWithShellPermissionIdentity(
283                 () -> mWhitelistManager.addToWhitelist(sContext.getOpPackageName()));
284     }
285 
setAlarmClockForFgs(long triggerRTC, String testAppName)286     private void setAlarmClockForFgs(long triggerRTC, String testAppName) throws Exception {
287         final CountDownLatch resultLatch = new CountDownLatch(1);
288         final AtomicInteger result = new AtomicInteger(-1);
289 
290         AlarmManager.AlarmClockInfo alarmInfo = new AlarmManager.AlarmClockInfo(triggerRTC, null);
291 
292         final Intent requestToTestApp = new Intent(TestAlarmScheduler.ACTION_SET_ALARM_CLOCK)
293                 .setClassName(testAppName, TestAlarmScheduler.class.getName())
294                 .putExtra(TestAlarmScheduler.EXTRA_ALARM_CLOCK_INFO, alarmInfo)
295                 .putExtra(TestAlarmScheduler.EXTRA_TEST_FGS, true)
296                 .addFlags(Intent.FLAG_RECEIVER_FOREGROUND);
297 
298         sContext.sendOrderedBroadcast(requestToTestApp, null, new BroadcastReceiver() {
299             @Override
300             public void onReceive(Context context, Intent intent) {
301                 result.set(getResultCode());
302                 resultLatch.countDown();
303             }
304         }, null, Activity.RESULT_CANCELED, null, null);
305 
306         assertTrue("Timed out waiting for response from helper app " + testAppName,
307                 resultLatch.await(10, TimeUnit.SECONDS));
308         assertEquals(Activity.RESULT_OK, result.get());
309     }
310 
311     @Test
alarmClockAllowsFGS()312     public void alarmClockAllowsFGS() throws Exception {
313         setAppOp(TEST_APP_PACKAGE, AppOpsManager.MODE_ALLOWED);
314 
315         final long triggerRtc = System.currentTimeMillis() + 5_000;
316         setAlarmClockForFgs(triggerRtc, TEST_APP_PACKAGE);
317 
318         final AtomicReference<String> resultHolder = new AtomicReference<>();
319         final CountDownLatch alarmLatch = new CountDownLatch(1);
320 
321         final IntentFilter filter = new IntentFilter(TestAlarmReceiver.ACTION_REPORT_ALARM_EXPIRED);
322         final BroadcastReceiver receiver = new BroadcastReceiver() {
323             @Override
324             public void onReceive(Context context, Intent intent) {
325                 Log.d(TAG, "Received response intent: " + intent);
326                 resultHolder.set(intent.getStringExtra(FgsTester.EXTRA_FGS_START_RESULT));
327                 alarmLatch.countDown();
328             }
329         };
330         sContext.registerReceiver(receiver, filter, Context.RECEIVER_EXPORTED);
331         try {
332             Thread.sleep(5_000);
333             assertTrue("AlarmClock expiration not reported",
334                     alarmLatch.await(30, TimeUnit.SECONDS));
335             assertEquals("FGS result should be empty", "", resultHolder.get());
336         } finally {
337             sContext.unregisterReceiver(receiver);
338         }
339     }
340 
341     @Test
setAlarmClockWithPermission()342     public void setAlarmClockWithPermission() throws Exception {
343         final long now = System.currentTimeMillis();
344         final int numAlarms = 100;   // Number much higher than any quota.
345         for (int i = 0; i < numAlarms; i++) {
346             final int id = mIdGenerator.nextInt();
347             final AlarmManager.AlarmClockInfo alarmClock = new AlarmManager.AlarmClockInfo(now,
348                     null);
349             mAlarmManager.setAlarmClock(alarmClock, getAlarmSender(id, false));
350             assertTrue("Alarm " + id + " not received",
351                     AlarmReceiver.waitForAlarm(id, DEFAULT_WAIT_FOR_SUCCESS));
352         }
353     }
354 
355     @Test
setAlarmClockWithoutPermissionOrWhitelist()356     public void setAlarmClockWithoutPermissionOrWhitelist() throws Exception {
357         revokeAppOp(TEST_APP_PACKAGE);
358         assertResultFromTestApp(RequestReceiver.ACTION_SET_ALARM_CLOCK, TEST_APP_PACKAGE,
359                 RequestReceiver.RESULT_SECURITY_EXCEPTION);
360     }
361 
362     @Test
setExactAwiWithoutPermissionOrWhitelist()363     public void setExactAwiWithoutPermissionOrWhitelist() throws Exception {
364         revokeAppOp(TEST_APP_PACKAGE);
365         assertResultFromTestApp(RequestReceiver.ACTION_SET_EXACT_AND_AWI, TEST_APP_PACKAGE,
366                 RequestReceiver.RESULT_SECURITY_EXCEPTION);
367     }
368 
369     @Test
setExactPiWithoutPermissionOrWhitelist()370     public void setExactPiWithoutPermissionOrWhitelist() throws Exception {
371         revokeAppOp(TEST_APP_PACKAGE);
372         assertResultFromTestApp(RequestReceiver.ACTION_SET_EXACT_PI, TEST_APP_PACKAGE,
373                 RequestReceiver.RESULT_SECURITY_EXCEPTION);
374     }
375 
376     @Test
setExactCallbackWithoutPermissionOrWhitelist()377     public void setExactCallbackWithoutPermissionOrWhitelist() throws Exception {
378         revokeAppOp(TEST_APP_PACKAGE);
379         assertResultFromTestApp(RequestReceiver.ACTION_SET_EXACT_CALLBACK, TEST_APP_PACKAGE,
380                 Activity.RESULT_OK);
381     }
382 
isDeviceIdleEnabled()383     private static boolean isDeviceIdleEnabled() {
384         final String output = SystemUtil.runShellCommand("cmd deviceidle enabled deep").trim();
385         return Integer.parseInt(output) != 0;
386     }
387 
putDeviceToIdle()388     private void putDeviceToIdle() {
389         SystemUtil.runShellCommandForNoOutput("dumpsys battery unplug");
390         SystemUtil.runShellCommand("cmd deviceidle force-idle deep");
391     }
392 
393     @Test
setExactAwiCallbackQuota()394     public void setExactAwiCallbackQuota() throws Exception {
395         assumeTrue(isDeviceIdleEnabled());
396         putDeviceToIdle();
397 
398         sleepUninterruptiblyUntil(getNextEligibleAwiCompatTime(ALLOW_WHILE_IDLE_COMPAT_QUOTA));
399 
400         int alarmId;
401         for (int i = 0; i < ALLOW_WHILE_IDLE_COMPAT_QUOTA; i++) {
402             final long trigger = SystemClock.elapsedRealtime() + 500;
403             mAlarmManager.setExactAndAllowWhileIdle(AlarmManager.ELAPSED_REALTIME_WAKEUP, trigger,
404                     "test-tag", Runnable::run, null,
405                     AlarmReceiver.createListener(alarmId = mIdGenerator.nextInt(), true));
406             Thread.sleep(500);
407             assertTrue("Alarm " + alarmId + " not received",
408                     AlarmReceiver.waitForAlarm(alarmId, DEFAULT_WAIT_FOR_SUCCESS));
409         }
410 
411         final long nextSet = SystemClock.elapsedRealtime();
412         final long nextTrigger = getNextEligibleAwiCompatTime(1);
413         assertTrue("Not enough margin to test reliably", nextTrigger > nextSet + 5000);
414 
415         mAlarmManager.setExactAndAllowWhileIdle(AlarmManager.ELAPSED_REALTIME_WAKEUP, nextSet,
416                 "test-tag", Runnable::run, null,
417                 AlarmReceiver.createListener(alarmId = mIdGenerator.nextInt(), true));
418         assertFalse("Alarm received when no quota", AlarmReceiver.waitForAlarm(alarmId, 5000));
419 
420         sleepUninterruptiblyUntil(nextTrigger);
421         assertTrue("Alarm " + alarmId + " not received when back in quota",
422                 AlarmReceiver.waitForAlarm(alarmId, DEFAULT_WAIT_FOR_SUCCESS));
423     }
424 
425     @Test
setExactAwiWithPermissionAndWhitelist()426     public void setExactAwiWithPermissionAndWhitelist() throws Exception {
427         whitelistTestApp();
428         final long now = SystemClock.elapsedRealtime();
429         // The user whitelist takes precedence, so the app should get unrestricted alarms.
430         final int numAlarms = 100;   // Number much higher than any quota.
431         for (int i = 0; i < numAlarms; i++) {
432             final int id = mIdGenerator.nextInt();
433             mAlarmManager.setExactAndAllowWhileIdle(AlarmManager.ELAPSED_REALTIME_WAKEUP, now,
434                     getAlarmSender(id, false));
435             assertTrue("Alarm " + id + " not received",
436                     AlarmReceiver.waitForAlarm(id, DEFAULT_WAIT_FOR_SUCCESS));
437         }
438     }
439 
440     @Test
setExactAwiExecutorWithPermissionAndWhitelist()441     public void setExactAwiExecutorWithPermissionAndWhitelist() throws Exception {
442         whitelistTestApp();
443         final long now = SystemClock.elapsedRealtime();
444         // The user whitelist takes precedence, so the app should get unrestricted alarms.
445         final int numAlarms = 100;   // Number much higher than any quota.
446         for (int i = 0; i < numAlarms; i++) {
447             final int id = mIdGenerator.nextInt();
448             mAlarmManager.setExactAndAllowWhileIdle(AlarmManager.ELAPSED_REALTIME_WAKEUP, now,
449                     "test-tag", Runnable::run, null, AlarmReceiver.createListener(id, false));
450             assertTrue("Alarm " + id + " not received",
451                     AlarmReceiver.waitForAlarm(id, DEFAULT_WAIT_FOR_SUCCESS));
452         }
453     }
454 
getNextEligibleAwiTime(int alarmsNeeded)455     private static long getNextEligibleAwiTime(int alarmsNeeded) {
456         assertTrue("Alarms needed exceed max quota", alarmsNeeded <= ALLOW_WHILE_IDLE_QUOTA);
457         final long t = AlarmReceiver.getNthLastAlarmTime(ALLOW_WHILE_IDLE_QUOTA - alarmsNeeded + 1);
458         return t + ALLOW_WHILE_IDLE_WINDOW;
459     }
460 
getNextEligibleAwiCompatTime(int alarmsNeeded)461     private static long getNextEligibleAwiCompatTime(int alarmsNeeded) {
462         assertTrue("Alarms needed exceed max quota", alarmsNeeded <= ALLOW_WHILE_IDLE_COMPAT_QUOTA);
463         final long t = AlarmReceiver.getNthLastCompatAlarmTime(
464                 ALLOW_WHILE_IDLE_COMPAT_QUOTA - alarmsNeeded + 1);
465         return t + ALLOW_WHILE_IDLE_COMPAT_WINDOW;
466     }
467 
sleepUninterruptiblyUntil(long untilElapsed)468     private static void sleepUninterruptiblyUntil(long untilElapsed) {
469         long now;
470         while ((now = SystemClock.elapsedRealtime()) < untilElapsed) {
471             try {
472                 Thread.sleep(untilElapsed - now);
473             } catch (InterruptedException e) {
474                 Log.e(TAG, "Thread interrupted while reclaiming quota!", e);
475             }
476         }
477     }
478 
479     @Test
setExactAwiWithPermissionWithoutWhitelist()480     public void setExactAwiWithPermissionWithoutWhitelist() throws Exception {
481         assumeTrue(isDeviceIdleEnabled());
482         putDeviceToIdle();
483 
484         sleepUninterruptiblyUntil(getNextEligibleAwiTime(ALLOW_WHILE_IDLE_QUOTA));
485 
486         int alarmId;
487         for (int i = 0; i < ALLOW_WHILE_IDLE_QUOTA; i++) {
488             final long trigger = SystemClock.elapsedRealtime() + 500;
489             mAlarmManager.setExactAndAllowWhileIdle(AlarmManager.ELAPSED_REALTIME_WAKEUP, trigger,
490                     getAlarmSender(alarmId = mIdGenerator.nextInt(), true));
491             Thread.sleep(500);
492             assertTrue("Alarm " + alarmId + " not received",
493                     AlarmReceiver.waitForAlarm(alarmId, DEFAULT_WAIT_FOR_SUCCESS));
494         }
495         long nextSet = SystemClock.elapsedRealtime();
496         final long nextTrigger = getNextEligibleAwiTime(1);
497         assertTrue("Not enough margin to test reliably", nextTrigger > nextSet + 5000);
498 
499         mAlarmManager.setExactAndAllowWhileIdle(AlarmManager.ELAPSED_REALTIME_WAKEUP, nextSet,
500                 getAlarmSender(alarmId = mIdGenerator.nextInt(), true));
501         assertFalse("Alarm received when no quota", AlarmReceiver.waitForAlarm(alarmId, 5000));
502 
503         sleepUninterruptiblyUntil(nextTrigger);
504         assertTrue("Alarm " + alarmId + " not received when back in quota",
505                 AlarmReceiver.waitForAlarm(alarmId, DEFAULT_WAIT_FOR_SUCCESS));
506     }
507 
assertTempWhitelistState(boolean whitelisted)508     private static void assertTempWhitelistState(boolean whitelisted) {
509         final String selfAppId = String.valueOf(UserHandle.getAppId(Process.myUid()));
510         SystemUtil.runShellCommand("cmd deviceidle tempwhitelist",
511                 output -> (output.contains(selfAppId) == whitelisted));
512     }
513 
514     @Test
alarmClockGrantsWhitelist()515     public void alarmClockGrantsWhitelist() throws Exception {
516         // no device idle in auto
517         assumeFalse(FeatureUtil.isAutomotive());
518 
519         final int id = mIdGenerator.nextInt();
520         final AlarmManager.AlarmClockInfo alarmClock = new AlarmManager.AlarmClockInfo(
521                 System.currentTimeMillis() + 100, null);
522         mAlarmManager.setAlarmClock(alarmClock, getAlarmSender(id, false));
523         Thread.sleep(100);
524         assertTrue("Alarm " + id + " not received", AlarmReceiver.waitForAlarm(id,
525                 DEFAULT_WAIT_FOR_SUCCESS));
526         assertTempWhitelistState(true);
527     }
528 
529     @Test
exactAwiGrantsWhitelist()530     public void exactAwiGrantsWhitelist() throws Exception {
531         // no device idle in auto
532         assumeFalse(FeatureUtil.isAutomotive());
533 
534         sleepUninterruptiblyUntil(getNextEligibleAwiTime(1));
535 
536         final int id = mIdGenerator.nextInt();
537         mAlarmManager.setExactAndAllowWhileIdle(AlarmManager.ELAPSED_REALTIME_WAKEUP,
538                 SystemClock.elapsedRealtime() + 100, getAlarmSender(id, false));
539         Thread.sleep(100);
540         assertTrue("Alarm " + id + " not received", AlarmReceiver.waitForAlarm(id,
541                 DEFAULT_WAIT_FOR_SUCCESS));
542         assertTempWhitelistState(true);
543     }
544 
545     @Test
activityToRequestPermissionExists()546     public void activityToRequestPermissionExists() {
547         final Intent request = new Intent(Settings.ACTION_REQUEST_SCHEDULE_EXACT_ALARM);
548         final PackageManager pm = sContext.getPackageManager();
549 
550         assertNotNull("No activity found for " + Settings.ACTION_REQUEST_SCHEDULE_EXACT_ALARM,
551                 pm.resolveActivity(request, 0));
552 
553         request.setData(Uri.fromParts("package", sContext.getOpPackageName(), null));
554 
555         assertNotNull("No app specific activity found for "
556                 + Settings.ACTION_REQUEST_SCHEDULE_EXACT_ALARM, pm.resolveActivity(request, 0));
557     }
558 
559     /**
560      * Check if a given UID is in the "can start FGS" allowlist.
561      */
checkThisAppTempAllowListed(int uid)562     private boolean checkThisAppTempAllowListed(int uid) {
563         // The allowlist used internally is ActivityManagerService.mFgsStartTempAllowList. We
564         // don't use the device-idle allowlist directly.
565 
566         // Run "dumpsys activity processes", and remove everything until "mFgsStartTempAllowList:".
567         String output = ShellUtils.runShellCommand("dumpsys activity processes");
568         output = output.replaceFirst("^.*? mFgsStartTempAllowList:$", "");
569 
570         final String uidStr = UserHandle.formatUid(uid);
571         final String expected = "^\\s*" + uidStr + ":";
572         for (String line : output.split("\n")) {
573             if (line.matches(expected)) {
574                 return true;
575             }
576         }
577         return false;
578     }
579 
prepareTestAppForBroadcast(ComponentName receiver)580     private void prepareTestAppForBroadcast(ComponentName receiver) {
581         // Just send an explicit foreground broadcast to the test app to make sure
582         // the app is out of force-stop.
583         SystemUtil.runWithShellPermissionIdentity(
584                 () -> mPackageManager.setComponentEnabledSetting(receiver,
585                         PackageManager.COMPONENT_ENABLED_STATE_ENABLED,
586                         PackageManager.DONT_KILL_APP));
587         Log.d(TAG, "Un-force-stoppping the test app");
588         Intent i = new Intent("android.app.action.cts.ACTION_PING");
589         i.setComponent(receiver);
590         i.setFlags(Intent.FLAG_RECEIVER_FOREGROUND);
591         sContext.sendBroadcast(i);
592     }
593 
594     @Test
scheduleExactAlarmPermissionStateChangedSentAppOp()595     public void scheduleExactAlarmPermissionStateChangedSentAppOp() throws Exception {
596         // Revoke the permission, and remove it from the temp-allowlist.
597         prepareTestAppForBroadcast(mPermissionChangeReceiver);
598         Log.d(TAG, "Revoking the appop");
599         revokeAppOp(TEST_APP_PACKAGE);
600         removeFromWhitelists(TEST_APP_PACKAGE);
601 
602         final int uid = Utils.getPackageUid(TEST_APP_PACKAGE);
603         TestUtils.waitUntil("Package still allowlisted",
604                 () -> !checkThisAppTempAllowListed(uid));
605 
606         Thread.sleep(1000); // Give the system a little time to settle down.
607 
608         final IntentFilter filter = new IntentFilter(
609                 PermissionStateChangedReceiver.ACTION_FGS_START_RESULT);
610         final AtomicReference<String> resultHolder = new AtomicReference<>();
611         final CountDownLatch latch = new CountDownLatch(1);
612         final BroadcastReceiver receiver = new BroadcastReceiver() {
613             @Override
614             public void onReceive(Context context, Intent intent) {
615                 Log.d(TAG, "Received response intent: " + intent);
616                 resultHolder.set(intent.getStringExtra(
617                         FgsTester.EXTRA_FGS_START_RESULT));
618                 latch.countDown();
619             }
620         };
621         sContext.registerReceiver(receiver, filter, Context.RECEIVER_EXPORTED);
622         try {
623             Log.d(TAG, "Granting the appop");
624             setAppOp(TEST_APP_PACKAGE, AppOpsManager.MODE_ALLOWED);
625 
626             assertTrue("Didn't receive response",
627                     latch.await(30, TimeUnit.SECONDS));
628             assertEquals("Failure message should be empty", "", resultHolder.get());
629         } finally {
630             sContext.unregisterReceiver(receiver);
631         }
632     }
633 }
634