1 /*
2  * Copyright (C) 2017 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 com.android.server.usage;
18 
19 import static android.app.usage.UsageEvents.Event.FOREGROUND_SERVICE_START;
20 import static android.app.usage.UsageEvents.Event.NOTIFICATION_SEEN;
21 import static android.app.usage.UsageEvents.Event.SLICE_PINNED;
22 import static android.app.usage.UsageEvents.Event.SLICE_PINNED_PRIV;
23 import static android.app.usage.UsageEvents.Event.SYSTEM_INTERACTION;
24 import static android.app.usage.UsageEvents.Event.USER_INTERACTION;
25 import static android.app.usage.UsageStatsManager.REASON_MAIN_DEFAULT;
26 import static android.app.usage.UsageStatsManager.REASON_MAIN_FORCED;
27 import static android.app.usage.UsageStatsManager.REASON_MAIN_PREDICTED;
28 import static android.app.usage.UsageStatsManager.REASON_MAIN_TIMEOUT;
29 import static android.app.usage.UsageStatsManager.REASON_MAIN_USAGE;
30 import static android.app.usage.UsageStatsManager.STANDBY_BUCKET_ACTIVE;
31 import static android.app.usage.UsageStatsManager.STANDBY_BUCKET_EXEMPTED;
32 import static android.app.usage.UsageStatsManager.STANDBY_BUCKET_FREQUENT;
33 import static android.app.usage.UsageStatsManager.STANDBY_BUCKET_NEVER;
34 import static android.app.usage.UsageStatsManager.STANDBY_BUCKET_RARE;
35 import static android.app.usage.UsageStatsManager.STANDBY_BUCKET_WORKING_SET;
36 
37 import static org.junit.Assert.assertEquals;
38 import static org.junit.Assert.assertFalse;
39 import static org.junit.Assert.assertNotEquals;
40 import static org.junit.Assert.assertTrue;
41 import static org.junit.Assert.fail;
42 import static org.mockito.ArgumentMatchers.eq;
43 import static org.mockito.Matchers.anyInt;
44 import static org.mockito.Mockito.doReturn;
45 import static org.mockito.Mockito.mock;
46 
47 import android.app.usage.AppStandbyInfo;
48 import android.app.usage.UsageEvents;
49 import android.app.usage.UsageStatsManagerInternal;
50 import android.appwidget.AppWidgetManager;
51 import android.content.Context;
52 import android.content.ContextWrapper;
53 import android.content.pm.ApplicationInfo;
54 import android.content.pm.PackageInfo;
55 import android.content.pm.PackageManager;
56 import android.hardware.display.DisplayManager;
57 import android.os.Handler;
58 import android.os.Looper;
59 import android.os.RemoteException;
60 import android.platform.test.annotations.Presubmit;
61 import android.util.ArraySet;
62 import android.view.Display;
63 
64 import androidx.test.InstrumentationRegistry;
65 import androidx.test.filters.SmallTest;
66 import androidx.test.runner.AndroidJUnit4;
67 
68 import com.android.server.SystemService;
69 
70 import org.junit.Before;
71 import org.junit.Test;
72 import org.junit.runner.RunWith;
73 
74 import java.io.File;
75 import java.util.ArrayList;
76 import java.util.Arrays;
77 import java.util.List;
78 import java.util.Set;
79 import java.util.concurrent.CountDownLatch;
80 import java.util.concurrent.TimeUnit;
81 
82 /**
83  * Unit test for AppStandbyController.
84  */
85 @RunWith(AndroidJUnit4.class)
86 @Presubmit
87 @SmallTest
88 public class AppStandbyControllerTests {
89 
90     private static final String PACKAGE_1 = "com.example.foo";
91     private static final int UID_1 = 10000;
92     private static final String PACKAGE_EXEMPTED_1 = "com.android.exempted";
93     private static final int UID_EXEMPTED_1 = 10001;
94     private static final int USER_ID = 0;
95     private static final int USER_ID2 = 10;
96 
97     private static final String PACKAGE_UNKNOWN = "com.example.unknown";
98 
99     private static final String ADMIN_PKG = "com.android.admin";
100     private static final String ADMIN_PKG2 = "com.android.admin2";
101     private static final String ADMIN_PKG3 = "com.android.admin3";
102 
103     private static final long MINUTE_MS = 60 * 1000;
104     private static final long HOUR_MS = 60 * MINUTE_MS;
105     private static final long DAY_MS = 24 * HOUR_MS;
106 
107     private static final long WORKING_SET_THRESHOLD = 12 * HOUR_MS;
108     private static final long FREQUENT_THRESHOLD = 24 * HOUR_MS;
109     private static final long RARE_THRESHOLD = 48 * HOUR_MS;
110     // Short STABLE_CHARGING_THRESHOLD for testing purposes
111     private static final long STABLE_CHARGING_THRESHOLD = 2000;
112 
113     /** Mock variable used in {@link MyInjector#isPackageInstalled(String, int, int)} */
114     private static boolean isPackageInstalled = true;
115 
116     private MyInjector mInjector;
117     private AppStandbyController mController;
118 
119     static class MyContextWrapper extends ContextWrapper {
120         PackageManager mockPm = mock(PackageManager.class);
121 
MyContextWrapper(Context base)122         public MyContextWrapper(Context base) {
123             super(base);
124         }
125 
getPackageManager()126         public PackageManager getPackageManager() {
127             return mockPm;
128         }
129     }
130 
131     static class MyInjector extends AppStandbyController.Injector {
132         long mElapsedRealtime;
133         boolean mIsAppIdleEnabled = true;
134         boolean mIsCharging;
135         List<String> mPowerSaveWhitelistExceptIdle = new ArrayList<>();
136         boolean mDisplayOn;
137         DisplayManager.DisplayListener mDisplayListener;
138         String mBoundWidgetPackage = PACKAGE_EXEMPTED_1;
139 
MyInjector(Context context, Looper looper)140         MyInjector(Context context, Looper looper) {
141             super(context, looper);
142         }
143 
144         @Override
onBootPhase(int phase)145         void onBootPhase(int phase) {
146         }
147 
148         @Override
getBootPhase()149         int getBootPhase() {
150             return SystemService.PHASE_BOOT_COMPLETED;
151         }
152 
153         @Override
elapsedRealtime()154         long elapsedRealtime() {
155             return mElapsedRealtime;
156         }
157 
158         @Override
currentTimeMillis()159         long currentTimeMillis() {
160             return mElapsedRealtime;
161         }
162 
163         @Override
isAppIdleEnabled()164         boolean isAppIdleEnabled() {
165             return mIsAppIdleEnabled;
166         }
167 
168         @Override
isCharging()169         boolean isCharging() {
170             return mIsCharging;
171         }
172 
173         @Override
isPowerSaveWhitelistExceptIdleApp(String packageName)174         boolean isPowerSaveWhitelistExceptIdleApp(String packageName) throws RemoteException {
175             return mPowerSaveWhitelistExceptIdle.contains(packageName);
176         }
177 
178         @Override
getDataSystemDirectory()179         File getDataSystemDirectory() {
180             return new File(getContext().getFilesDir(), Long.toString(Math.randomLongInternal()));
181         }
182 
183         @Override
noteEvent(int event, String packageName, int uid)184         void noteEvent(int event, String packageName, int uid) throws RemoteException {
185         }
186 
187         @Override
isPackageEphemeral(int userId, String packageName)188         boolean isPackageEphemeral(int userId, String packageName) {
189             // TODO: update when testing ephemeral apps scenario
190             return false;
191         }
192 
193         @Override
isPackageInstalled(String packageName, int flags, int userId)194         boolean isPackageInstalled(String packageName, int flags, int userId) {
195             // Should always return true (default value) unless testing for an uninstalled app
196             return isPackageInstalled;
197         }
198 
199         @Override
getRunningUserIds()200         int[] getRunningUserIds() {
201             return new int[] {USER_ID};
202         }
203 
204         @Override
isDefaultDisplayOn()205         boolean isDefaultDisplayOn() {
206             return mDisplayOn;
207         }
208 
209         @Override
registerDisplayListener(DisplayManager.DisplayListener listener, Handler handler)210         void registerDisplayListener(DisplayManager.DisplayListener listener, Handler handler) {
211             mDisplayListener = listener;
212         }
213 
214         @Override
getActiveNetworkScorer()215         String getActiveNetworkScorer() {
216             return null;
217         }
218 
219         @Override
isBoundWidgetPackage(AppWidgetManager appWidgetManager, String packageName, int userId)220         public boolean isBoundWidgetPackage(AppWidgetManager appWidgetManager, String packageName,
221                 int userId) {
222             return packageName != null && packageName.equals(mBoundWidgetPackage);
223         }
224 
225         @Override
getAppIdleSettings()226         String getAppIdleSettings() {
227             return "screen_thresholds=0/0/0/" + HOUR_MS + ",elapsed_thresholds=0/"
228                     + WORKING_SET_THRESHOLD + "/"
229                     + FREQUENT_THRESHOLD + "/"
230                     + RARE_THRESHOLD + ","
231                     + "stable_charging_threshold=" + STABLE_CHARGING_THRESHOLD;
232         }
233 
234         @Override
isDeviceIdleMode()235         public boolean isDeviceIdleMode() {
236             return false;
237         }
238 
239         // Internal methods
240 
setDisplayOn(boolean on)241         void setDisplayOn(boolean on) {
242             mDisplayOn = on;
243             if (mDisplayListener != null) {
244                 mDisplayListener.onDisplayChanged(Display.DEFAULT_DISPLAY);
245             }
246         }
247     }
248 
setupPm(PackageManager mockPm)249     private void setupPm(PackageManager mockPm) throws PackageManager.NameNotFoundException {
250         List<PackageInfo> packages = new ArrayList<>();
251         PackageInfo pi = new PackageInfo();
252         pi.applicationInfo = new ApplicationInfo();
253         pi.applicationInfo.uid = UID_1;
254         pi.packageName = PACKAGE_1;
255         packages.add(pi);
256 
257         PackageInfo pie = new PackageInfo();
258         pie.applicationInfo = new ApplicationInfo();
259         pie.applicationInfo.uid = UID_EXEMPTED_1;
260         pie.packageName = PACKAGE_EXEMPTED_1;
261         packages.add(pie);
262 
263         doReturn(packages).when(mockPm).getInstalledPackagesAsUser(anyInt(), anyInt());
264         try {
265             doReturn(UID_1).when(mockPm).getPackageUidAsUser(eq(PACKAGE_1), anyInt(), anyInt());
266             doReturn(UID_EXEMPTED_1).when(mockPm).getPackageUidAsUser(eq(PACKAGE_EXEMPTED_1),
267                     anyInt(), anyInt());
268             doReturn(pi.applicationInfo).when(mockPm).getApplicationInfo(eq(pi.packageName),
269                     anyInt());
270             doReturn(pie.applicationInfo).when(mockPm).getApplicationInfo(eq(pie.packageName),
271                     anyInt());
272         } catch (PackageManager.NameNotFoundException nnfe) {}
273     }
274 
setChargingState(AppStandbyController controller, boolean charging)275     private void setChargingState(AppStandbyController controller, boolean charging) {
276         mInjector.mIsCharging = charging;
277         if (controller != null) {
278             controller.setChargingState(charging);
279         }
280     }
281 
setAppIdleEnabled(AppStandbyController controller, boolean enabled)282     private void setAppIdleEnabled(AppStandbyController controller, boolean enabled) {
283         mInjector.mIsAppIdleEnabled = enabled;
284         if (controller != null) {
285             controller.setAppIdleEnabled(enabled);
286         }
287     }
288 
setupController()289     private AppStandbyController setupController() throws Exception {
290         mInjector.mElapsedRealtime = 0;
291         setupPm(mInjector.getContext().getPackageManager());
292         AppStandbyController controller = new AppStandbyController(mInjector);
293         controller.initializeDefaultsForSystemApps(USER_ID);
294         controller.onBootPhase(SystemService.PHASE_SYSTEM_SERVICES_READY);
295         controller.onBootPhase(SystemService.PHASE_BOOT_COMPLETED);
296         mInjector.setDisplayOn(false);
297         mInjector.setDisplayOn(true);
298         setChargingState(controller, false);
299         controller.checkIdleStates(USER_ID);
300         assertEquals(STANDBY_BUCKET_EXEMPTED,
301                 controller.getAppStandbyBucket(PACKAGE_EXEMPTED_1, USER_ID,
302                         mInjector.mElapsedRealtime, false));
303         assertNotEquals(STANDBY_BUCKET_EXEMPTED,
304                 controller.getAppStandbyBucket(PACKAGE_1, USER_ID,
305                         mInjector.mElapsedRealtime, false));
306 
307         return controller;
308     }
309 
getCurrentTime()310     private long getCurrentTime() {
311         return TimeUnit.NANOSECONDS.toMillis(System.nanoTime());
312     }
313 
314     @Before
setUp()315     public void setUp() throws Exception {
316         MyContextWrapper myContext = new MyContextWrapper(InstrumentationRegistry.getContext());
317         mInjector = new MyInjector(myContext, Looper.getMainLooper());
318         mController = setupController();
319         setChargingState(mController, false);
320     }
321 
322     private class TestParoleListener extends UsageStatsManagerInternal.AppIdleStateChangeListener {
323         private boolean mOnParole = false;
324         private CountDownLatch mLatch;
325         private long mLastParoleChangeTime;
326         private boolean mIsExpecting = false;
327         private boolean mExpectedParoleState;
328 
getParoleState()329         public boolean getParoleState() {
330             synchronized (this) {
331                 return mOnParole;
332             }
333         }
334 
rearmLatch()335         public void rearmLatch() {
336             synchronized (this) {
337                 mLatch = new CountDownLatch(1);
338                 mIsExpecting = false;
339             }
340         }
341 
rearmLatch(boolean expectedParoleState)342         public void rearmLatch(boolean expectedParoleState) {
343             synchronized (this) {
344                 mLatch = new CountDownLatch(1);
345                 mIsExpecting = true;
346                 mExpectedParoleState = expectedParoleState;
347             }
348         }
349 
awaitOnLatch(long time)350         public void awaitOnLatch(long time) throws Exception {
351             mLatch.await(time, TimeUnit.MILLISECONDS);
352         }
353 
getLastParoleChangeTime()354         public long getLastParoleChangeTime() {
355             synchronized (this) {
356                 return mLastParoleChangeTime;
357             }
358         }
359 
360         @Override
onAppIdleStateChanged(String packageName, int userId, boolean idle, int bucket, int reason)361         public void onAppIdleStateChanged(String packageName, int userId, boolean idle,
362                 int bucket, int reason) {
363         }
364 
365         @Override
onParoleStateChanged(boolean isParoleOn)366         public void onParoleStateChanged(boolean isParoleOn) {
367             synchronized (this) {
368                 // Only record information if it is being looked for
369                 if (mLatch != null && mLatch.getCount() > 0) {
370                     mOnParole = isParoleOn;
371                     mLastParoleChangeTime = getCurrentTime();
372                     if (!mIsExpecting || isParoleOn == mExpectedParoleState) {
373                         mLatch.countDown();
374                     }
375                 }
376             }
377         }
378     }
379 
380     @Test
testCharging()381     public void testCharging() throws Exception {
382         long startTime;
383         TestParoleListener paroleListener = new TestParoleListener();
384         long marginOfError = 200;
385 
386         // Charging
387         paroleListener.rearmLatch();
388         mController.addListener(paroleListener);
389         startTime = getCurrentTime();
390         setChargingState(mController, true);
391         paroleListener.awaitOnLatch(STABLE_CHARGING_THRESHOLD * 3 / 2);
392         assertTrue(paroleListener.mOnParole);
393         // Parole will only be granted after device has been charging for a sufficient amount of
394         // time.
395         assertEquals(STABLE_CHARGING_THRESHOLD,
396                 paroleListener.getLastParoleChangeTime() - startTime,
397                 marginOfError);
398 
399         // Discharging
400         paroleListener.rearmLatch();
401         startTime = getCurrentTime();
402         setChargingState(mController, false);
403         mController.checkIdleStates(USER_ID);
404         paroleListener.awaitOnLatch(STABLE_CHARGING_THRESHOLD * 3 / 2);
405         assertFalse(paroleListener.getParoleState());
406         // Parole should be revoked immediately
407         assertEquals(0,
408                 paroleListener.getLastParoleChangeTime() - startTime,
409                 marginOfError);
410 
411         // Brief Charging
412         paroleListener.rearmLatch();
413         setChargingState(mController, true);
414         setChargingState(mController, false);
415         // Device stopped charging before the stable charging threshold.
416         // Parole should not be granted at the end of the threshold
417         paroleListener.awaitOnLatch(STABLE_CHARGING_THRESHOLD * 3 / 2);
418         assertFalse(paroleListener.getParoleState());
419 
420         // Charging Again
421         paroleListener.rearmLatch();
422         startTime = getCurrentTime();
423         setChargingState(mController, true);
424         paroleListener.awaitOnLatch(STABLE_CHARGING_THRESHOLD * 3 / 2);
425         assertTrue(paroleListener.getParoleState());
426         assertTrue(paroleListener.mOnParole);
427         assertEquals(STABLE_CHARGING_THRESHOLD,
428                 paroleListener.getLastParoleChangeTime() - startTime,
429                 marginOfError);
430     }
431 
432     @Test
testEnabledState()433     public void testEnabledState() throws Exception {
434         TestParoleListener paroleListener = new TestParoleListener();
435         paroleListener.rearmLatch(true);
436         mController.addListener(paroleListener);
437         long lastUpdateTime;
438 
439         // Test that listeners are notified if enabled changes when the device is not in parole.
440         setChargingState(mController, false);
441 
442         // Start off not enabled. Device is effectively in permanent parole.
443         setAppIdleEnabled(mController, false);
444         // Since AppStandbyController uses a handler to notify listeners of a state change, there is
445         // some inherent latency between changing the state and getting the notification. We need to
446         // wait until the paroleListener has been notified that parole is on before continuing with
447         // the test.
448         paroleListener.awaitOnLatch(STABLE_CHARGING_THRESHOLD * 3 / 2);
449         assertTrue(paroleListener.mOnParole);
450 
451         // Enable controller
452         paroleListener.rearmLatch();
453         setAppIdleEnabled(mController, true);
454         paroleListener.awaitOnLatch(STABLE_CHARGING_THRESHOLD * 3 / 2);
455         assertFalse(paroleListener.mOnParole);
456         lastUpdateTime = paroleListener.getLastParoleChangeTime();
457 
458         paroleListener.rearmLatch();
459         setAppIdleEnabled(mController, true);
460         paroleListener.awaitOnLatch(STABLE_CHARGING_THRESHOLD * 3 / 2);
461         assertFalse(paroleListener.mOnParole);
462         // Make sure AppStandbyController doesn't notify listeners when there's no change.
463         assertEquals(lastUpdateTime, paroleListener.getLastParoleChangeTime());
464 
465         // Disable controller
466         paroleListener.rearmLatch();
467         setAppIdleEnabled(mController, false);
468         paroleListener.awaitOnLatch(STABLE_CHARGING_THRESHOLD * 3 / 2);
469         assertTrue(paroleListener.mOnParole);
470         lastUpdateTime = paroleListener.getLastParoleChangeTime();
471 
472         paroleListener.rearmLatch();
473         setAppIdleEnabled(mController, false);
474         paroleListener.awaitOnLatch(STABLE_CHARGING_THRESHOLD * 3 / 2);
475         assertTrue(paroleListener.mOnParole);
476         // Make sure AppStandbyController doesn't notify listeners when there's no change.
477         assertEquals(lastUpdateTime, paroleListener.getLastParoleChangeTime());
478 
479 
480         // Test that listeners aren't notified if enabled status changes when the device is already
481         // in parole.
482 
483         // A device is in parole whenever it's charging.
484         setChargingState(mController, true);
485 
486         // Start off not enabled.
487         paroleListener.rearmLatch();
488         setAppIdleEnabled(mController, false);
489         paroleListener.awaitOnLatch(STABLE_CHARGING_THRESHOLD * 3 / 2);
490         assertTrue(paroleListener.mOnParole);
491         lastUpdateTime = paroleListener.getLastParoleChangeTime();
492 
493         // Test that toggling doesn't notify the listener.
494         paroleListener.rearmLatch();
495         setAppIdleEnabled(mController, true);
496         paroleListener.awaitOnLatch(STABLE_CHARGING_THRESHOLD * 3 / 2);
497         assertTrue(paroleListener.mOnParole);
498         assertEquals(lastUpdateTime, paroleListener.getLastParoleChangeTime());
499 
500         paroleListener.rearmLatch();
501         setAppIdleEnabled(mController, false);
502         paroleListener.awaitOnLatch(STABLE_CHARGING_THRESHOLD * 3 / 2);
503         assertTrue(paroleListener.mOnParole);
504         assertEquals(lastUpdateTime, paroleListener.getLastParoleChangeTime());
505     }
506 
assertTimeout(AppStandbyController controller, long elapsedTime, int bucket)507     private void assertTimeout(AppStandbyController controller, long elapsedTime, int bucket) {
508         mInjector.mElapsedRealtime = elapsedTime;
509         controller.checkIdleStates(USER_ID);
510         assertEquals(bucket,
511                 controller.getAppStandbyBucket(PACKAGE_1, USER_ID, mInjector.mElapsedRealtime,
512                         false));
513     }
514 
reportEvent(AppStandbyController controller, int eventType, long elapsedTime, String packageName)515     private void reportEvent(AppStandbyController controller, int eventType, long elapsedTime,
516             String packageName) {
517         // Back to ACTIVE on event
518         mInjector.mElapsedRealtime = elapsedTime;
519         UsageEvents.Event ev = new UsageEvents.Event();
520         ev.mPackage = packageName;
521         ev.mEventType = eventType;
522         controller.reportEvent(ev, elapsedTime, USER_ID);
523     }
524 
getStandbyBucket(AppStandbyController controller, String packageName)525     private int getStandbyBucket(AppStandbyController controller, String packageName) {
526         return controller.getAppStandbyBucket(packageName, USER_ID, mInjector.mElapsedRealtime,
527                 true);
528     }
529 
assertBucket(int bucket)530     private void assertBucket(int bucket) {
531         assertEquals(bucket, getStandbyBucket(mController, PACKAGE_1));
532     }
533 
534     @Test
testBuckets()535     public void testBuckets() throws Exception {
536         assertTimeout(mController, 0, STANDBY_BUCKET_NEVER);
537 
538         reportEvent(mController, USER_INTERACTION, 0, PACKAGE_1);
539 
540         // ACTIVE bucket
541         assertTimeout(mController, WORKING_SET_THRESHOLD - 1, STANDBY_BUCKET_ACTIVE);
542 
543         // WORKING_SET bucket
544         assertTimeout(mController, WORKING_SET_THRESHOLD + 1, STANDBY_BUCKET_WORKING_SET);
545 
546         // WORKING_SET bucket
547         assertTimeout(mController, FREQUENT_THRESHOLD - 1, STANDBY_BUCKET_WORKING_SET);
548 
549         // FREQUENT bucket
550         assertTimeout(mController, FREQUENT_THRESHOLD + 1, STANDBY_BUCKET_FREQUENT);
551 
552         // RARE bucket
553         assertTimeout(mController, RARE_THRESHOLD + 1, STANDBY_BUCKET_RARE);
554 
555         reportEvent(mController, USER_INTERACTION, RARE_THRESHOLD + 1, PACKAGE_1);
556 
557         assertTimeout(mController, RARE_THRESHOLD + 1, STANDBY_BUCKET_ACTIVE);
558 
559         // RARE bucket
560         assertTimeout(mController, RARE_THRESHOLD * 2 + 2, STANDBY_BUCKET_RARE);
561     }
562 
563     @Test
testSetAppStandbyBucket()564     public void testSetAppStandbyBucket() throws Exception {
565         // For a known package, standby bucket should be set properly
566         reportEvent(mController, USER_INTERACTION, 0, PACKAGE_1);
567         mController.setAppStandbyBucket(PACKAGE_1, USER_ID, STANDBY_BUCKET_ACTIVE,
568                 REASON_MAIN_TIMEOUT, HOUR_MS);
569         assertEquals(STANDBY_BUCKET_ACTIVE, getStandbyBucket(mController, PACKAGE_1));
570 
571         // For an unknown package, standby bucket should not be set, hence NEVER is returned
572         // Ensure the unknown package is not already in history by removing it
573         mController.clearAppIdleForPackage(PACKAGE_UNKNOWN, USER_ID);
574         isPackageInstalled = false; // Mock package is not installed
575         mController.setAppStandbyBucket(PACKAGE_UNKNOWN, USER_ID, STANDBY_BUCKET_ACTIVE,
576                 REASON_MAIN_TIMEOUT, HOUR_MS);
577         isPackageInstalled = true; // Reset mocked variable for other tests
578         assertEquals(STANDBY_BUCKET_NEVER, getStandbyBucket(mController, PACKAGE_UNKNOWN));
579     }
580 
581     @Test
testAppStandbyBucketOnInstallAndUninstall()582     public void testAppStandbyBucketOnInstallAndUninstall() throws Exception {
583         // On package install, standby bucket should be ACTIVE
584         reportEvent(mController, USER_INTERACTION, 0, PACKAGE_UNKNOWN);
585         assertEquals(STANDBY_BUCKET_ACTIVE, getStandbyBucket(mController, PACKAGE_UNKNOWN));
586 
587         // On uninstall, package should not exist in history and should return a NEVER bucket
588         mController.clearAppIdleForPackage(PACKAGE_UNKNOWN, USER_ID);
589         assertEquals(STANDBY_BUCKET_NEVER, getStandbyBucket(mController, PACKAGE_UNKNOWN));
590         // Ensure uninstalled app is not in history
591         List<AppStandbyInfo> buckets = mController.getAppStandbyBuckets(USER_ID);
592         for(AppStandbyInfo bucket : buckets) {
593             if (bucket.mPackageName.equals(PACKAGE_UNKNOWN)) {
594                 fail("packageName found in app idle history after uninstall.");
595             }
596         }
597     }
598 
599     @Test
testScreenTimeAndBuckets()600     public void testScreenTimeAndBuckets() throws Exception {
601         mInjector.setDisplayOn(false);
602 
603         assertTimeout(mController, 0, STANDBY_BUCKET_NEVER);
604 
605         reportEvent(mController, USER_INTERACTION, 0, PACKAGE_1);
606 
607         // ACTIVE bucket
608         assertTimeout(mController, WORKING_SET_THRESHOLD - 1, STANDBY_BUCKET_ACTIVE);
609 
610         // WORKING_SET bucket
611         assertTimeout(mController, WORKING_SET_THRESHOLD + 1, STANDBY_BUCKET_WORKING_SET);
612 
613         // RARE bucket, should fail because the screen wasn't ON.
614         mInjector.mElapsedRealtime = RARE_THRESHOLD + 1;
615         mController.checkIdleStates(USER_ID);
616         assertNotEquals(STANDBY_BUCKET_RARE, getStandbyBucket(mController, PACKAGE_1));
617 
618         mInjector.setDisplayOn(true);
619         assertTimeout(mController, RARE_THRESHOLD * 2 + 2, STANDBY_BUCKET_RARE);
620     }
621 
622     @Test
testForcedIdle()623     public void testForcedIdle() throws Exception {
624         mController.forceIdleState(PACKAGE_1, USER_ID, true);
625         assertEquals(STANDBY_BUCKET_RARE, getStandbyBucket(mController, PACKAGE_1));
626         assertTrue(mController.isAppIdleFiltered(PACKAGE_1, UID_1, USER_ID, 0));
627 
628         mController.forceIdleState(PACKAGE_1, USER_ID, false);
629         assertEquals(STANDBY_BUCKET_ACTIVE, mController.getAppStandbyBucket(PACKAGE_1, USER_ID, 0,
630                 true));
631         assertFalse(mController.isAppIdleFiltered(PACKAGE_1, UID_1, USER_ID, 0));
632     }
633 
634     @Test
testNotificationEvent()635     public void testNotificationEvent() throws Exception {
636         reportEvent(mController, USER_INTERACTION, 0, PACKAGE_1);
637         assertEquals(STANDBY_BUCKET_ACTIVE, getStandbyBucket(mController, PACKAGE_1));
638         mInjector.mElapsedRealtime = 1;
639         reportEvent(mController, NOTIFICATION_SEEN, mInjector.mElapsedRealtime, PACKAGE_1);
640         assertEquals(STANDBY_BUCKET_ACTIVE, getStandbyBucket(mController, PACKAGE_1));
641 
642         mController.forceIdleState(PACKAGE_1, USER_ID, true);
643         reportEvent(mController, NOTIFICATION_SEEN, mInjector.mElapsedRealtime, PACKAGE_1);
644         assertEquals(STANDBY_BUCKET_WORKING_SET, getStandbyBucket(mController, PACKAGE_1));
645     }
646 
647     @Test
testSlicePinnedEvent()648     public void testSlicePinnedEvent() throws Exception {
649         reportEvent(mController, USER_INTERACTION, 0, PACKAGE_1);
650         assertEquals(STANDBY_BUCKET_ACTIVE, getStandbyBucket(mController, PACKAGE_1));
651         mInjector.mElapsedRealtime = 1;
652         reportEvent(mController, SLICE_PINNED, mInjector.mElapsedRealtime, PACKAGE_1);
653         assertEquals(STANDBY_BUCKET_ACTIVE, getStandbyBucket(mController, PACKAGE_1));
654 
655         mController.forceIdleState(PACKAGE_1, USER_ID, true);
656         reportEvent(mController, SLICE_PINNED, mInjector.mElapsedRealtime, PACKAGE_1);
657         assertEquals(STANDBY_BUCKET_WORKING_SET, getStandbyBucket(mController, PACKAGE_1));
658     }
659 
660     @Test
testSlicePinnedPrivEvent()661     public void testSlicePinnedPrivEvent() throws Exception {
662         mController.forceIdleState(PACKAGE_1, USER_ID, true);
663         reportEvent(mController, SLICE_PINNED_PRIV, mInjector.mElapsedRealtime, PACKAGE_1);
664         assertEquals(STANDBY_BUCKET_ACTIVE, getStandbyBucket(mController, PACKAGE_1));
665     }
666 
667     @Test
testPredictionTimedout()668     public void testPredictionTimedout() throws Exception {
669         // Set it to timeout or usage, so that prediction can override it
670         mController.setAppStandbyBucket(PACKAGE_1, USER_ID, STANDBY_BUCKET_RARE,
671                 REASON_MAIN_TIMEOUT, HOUR_MS);
672         assertEquals(STANDBY_BUCKET_RARE, getStandbyBucket(mController, PACKAGE_1));
673 
674         mController.setAppStandbyBucket(PACKAGE_1, USER_ID, STANDBY_BUCKET_ACTIVE,
675                 REASON_MAIN_PREDICTED, HOUR_MS);
676         assertEquals(STANDBY_BUCKET_ACTIVE, getStandbyBucket(mController, PACKAGE_1));
677 
678         // Fast forward 12 hours
679         mInjector.mElapsedRealtime += WORKING_SET_THRESHOLD;
680         mController.checkIdleStates(USER_ID);
681         // Should still be in predicted bucket, since prediction timeout is 1 day since prediction
682         assertEquals(STANDBY_BUCKET_ACTIVE, getStandbyBucket(mController, PACKAGE_1));
683         // Fast forward two more hours
684         mInjector.mElapsedRealtime += 2 * HOUR_MS;
685         mController.checkIdleStates(USER_ID);
686         // Should have now applied prediction timeout
687         assertEquals(STANDBY_BUCKET_WORKING_SET, getStandbyBucket(mController, PACKAGE_1));
688 
689         // Fast forward RARE bucket
690         mInjector.mElapsedRealtime += RARE_THRESHOLD;
691         mController.checkIdleStates(USER_ID);
692         // Should continue to apply prediction timeout
693         assertEquals(STANDBY_BUCKET_RARE, getStandbyBucket(mController, PACKAGE_1));
694     }
695 
696     @Test
testOverrides()697     public void testOverrides() throws Exception {
698         // Can force to NEVER
699         mController.setAppStandbyBucket(PACKAGE_1, USER_ID, STANDBY_BUCKET_NEVER,
700                 REASON_MAIN_FORCED, 1 * HOUR_MS);
701         assertEquals(STANDBY_BUCKET_NEVER, getStandbyBucket(mController, PACKAGE_1));
702 
703         // Prediction can't override FORCED reason
704         mController.setAppStandbyBucket(PACKAGE_1, USER_ID, STANDBY_BUCKET_FREQUENT,
705                 REASON_MAIN_FORCED, 1 * HOUR_MS);
706         mController.setAppStandbyBucket(PACKAGE_1, USER_ID, STANDBY_BUCKET_WORKING_SET,
707                 REASON_MAIN_PREDICTED, 1 * HOUR_MS);
708         assertEquals(STANDBY_BUCKET_FREQUENT, getStandbyBucket(mController, PACKAGE_1));
709 
710         // Prediction can't override NEVER
711         mController.setAppStandbyBucket(PACKAGE_1, USER_ID, STANDBY_BUCKET_NEVER,
712                 REASON_MAIN_DEFAULT, 2 * HOUR_MS);
713         mController.setAppStandbyBucket(PACKAGE_1, USER_ID, STANDBY_BUCKET_ACTIVE,
714                 REASON_MAIN_PREDICTED, 2 * HOUR_MS);
715         assertEquals(STANDBY_BUCKET_NEVER, getStandbyBucket(mController, PACKAGE_1));
716 
717         // Prediction can't set to NEVER
718         mController.setAppStandbyBucket(PACKAGE_1, USER_ID, STANDBY_BUCKET_ACTIVE,
719                 REASON_MAIN_USAGE, 2 * HOUR_MS);
720         mController.setAppStandbyBucket(PACKAGE_1, USER_ID, STANDBY_BUCKET_NEVER,
721                 REASON_MAIN_PREDICTED, 2 * HOUR_MS);
722         assertEquals(STANDBY_BUCKET_ACTIVE, getStandbyBucket(mController, PACKAGE_1));
723     }
724 
725     @Test
testTimeout()726     public void testTimeout() throws Exception {
727         reportEvent(mController, USER_INTERACTION, 0, PACKAGE_1);
728         assertBucket(STANDBY_BUCKET_ACTIVE);
729 
730         mInjector.mElapsedRealtime = 2000;
731         mController.setAppStandbyBucket(PACKAGE_1, USER_ID, STANDBY_BUCKET_FREQUENT,
732                 REASON_MAIN_PREDICTED, mInjector.mElapsedRealtime);
733         assertBucket(STANDBY_BUCKET_ACTIVE);
734 
735         // bucketing works after timeout
736         mInjector.mElapsedRealtime = mController.mPredictionTimeoutMillis - 100;
737         mController.checkIdleStates(USER_ID);
738         // Use recent prediction
739         assertBucket(STANDBY_BUCKET_FREQUENT);
740 
741         // Way past prediction timeout, use system thresholds
742         mInjector.mElapsedRealtime = RARE_THRESHOLD * 4;
743         mController.checkIdleStates(USER_ID);
744         assertBucket(STANDBY_BUCKET_RARE);
745     }
746 
747     @Test
testCascadingTimeouts()748     public void testCascadingTimeouts() throws Exception {
749         reportEvent(mController, USER_INTERACTION, 0, PACKAGE_1);
750         assertBucket(STANDBY_BUCKET_ACTIVE);
751 
752         reportEvent(mController, NOTIFICATION_SEEN, 1000, PACKAGE_1);
753         assertBucket(STANDBY_BUCKET_ACTIVE);
754 
755         mController.setAppStandbyBucket(PACKAGE_1, USER_ID, STANDBY_BUCKET_WORKING_SET,
756                 REASON_MAIN_PREDICTED, 1000);
757         assertBucket(STANDBY_BUCKET_ACTIVE);
758 
759         mController.setAppStandbyBucket(PACKAGE_1, USER_ID, STANDBY_BUCKET_FREQUENT,
760                 REASON_MAIN_PREDICTED, 2000 + mController.mStrongUsageTimeoutMillis);
761         assertBucket(STANDBY_BUCKET_WORKING_SET);
762 
763         mController.setAppStandbyBucket(PACKAGE_1, USER_ID, STANDBY_BUCKET_FREQUENT,
764                 REASON_MAIN_PREDICTED, 2000 + mController.mNotificationSeenTimeoutMillis);
765         assertBucket(STANDBY_BUCKET_FREQUENT);
766     }
767 
768     @Test
testOverlappingTimeouts()769     public void testOverlappingTimeouts() throws Exception {
770         reportEvent(mController, USER_INTERACTION, 0, PACKAGE_1);
771         assertBucket(STANDBY_BUCKET_ACTIVE);
772 
773         reportEvent(mController, NOTIFICATION_SEEN, 1000, PACKAGE_1);
774         assertBucket(STANDBY_BUCKET_ACTIVE);
775 
776         // Overlapping USER_INTERACTION before previous one times out
777         reportEvent(mController, USER_INTERACTION, mController.mStrongUsageTimeoutMillis - 1000,
778                 PACKAGE_1);
779         assertBucket(STANDBY_BUCKET_ACTIVE);
780 
781         // Still in ACTIVE after first USER_INTERACTION times out
782         mInjector.mElapsedRealtime = mController.mStrongUsageTimeoutMillis + 1000;
783         mController.setAppStandbyBucket(PACKAGE_1, USER_ID, STANDBY_BUCKET_FREQUENT,
784                 REASON_MAIN_PREDICTED, mInjector.mElapsedRealtime);
785         assertBucket(STANDBY_BUCKET_ACTIVE);
786 
787         // Both timed out, so NOTIFICATION_SEEN timeout should be effective
788         mInjector.mElapsedRealtime = mController.mStrongUsageTimeoutMillis * 2 + 2000;
789         mController.setAppStandbyBucket(PACKAGE_1, USER_ID, STANDBY_BUCKET_FREQUENT,
790                 REASON_MAIN_PREDICTED, mInjector.mElapsedRealtime);
791         assertBucket(STANDBY_BUCKET_WORKING_SET);
792 
793         mInjector.mElapsedRealtime = mController.mNotificationSeenTimeoutMillis + 2000;
794         mController.setAppStandbyBucket(PACKAGE_1, USER_ID, STANDBY_BUCKET_RARE,
795                 REASON_MAIN_PREDICTED, mInjector.mElapsedRealtime);
796         assertBucket(STANDBY_BUCKET_RARE);
797     }
798 
799     @Test
testSystemInteractionTimeout()800     public void testSystemInteractionTimeout() throws Exception {
801         setChargingState(mController, false);
802 
803         reportEvent(mController, USER_INTERACTION, 0, PACKAGE_1);
804         // Fast forward to RARE
805         mInjector.mElapsedRealtime = RARE_THRESHOLD + 100;
806         mController.checkIdleStates(USER_ID);
807         assertBucket(STANDBY_BUCKET_RARE);
808 
809         // Trigger a SYSTEM_INTERACTION and verify bucket
810         reportEvent(mController, SYSTEM_INTERACTION, mInjector.mElapsedRealtime, PACKAGE_1);
811         assertBucket(STANDBY_BUCKET_ACTIVE);
812 
813         // Verify it's still in ACTIVE close to end of timeout
814         mInjector.mElapsedRealtime += mController.mSystemInteractionTimeoutMillis - 100;
815         mController.checkIdleStates(USER_ID);
816         assertBucket(STANDBY_BUCKET_ACTIVE);
817 
818         // Verify bucket moves to RARE after timeout
819         mInjector.mElapsedRealtime += 200;
820         mController.checkIdleStates(USER_ID);
821         assertBucket(STANDBY_BUCKET_RARE);
822     }
823 
824     @Test
testInitialForegroundServiceTimeout()825     public void testInitialForegroundServiceTimeout() throws Exception {
826         setChargingState(mController, false);
827 
828         mInjector.mElapsedRealtime = 1 * RARE_THRESHOLD + 100;
829         // Make sure app is in NEVER bucket
830         mController.setAppStandbyBucket(PACKAGE_1, USER_ID, STANDBY_BUCKET_NEVER,
831                 REASON_MAIN_FORCED, mInjector.mElapsedRealtime);
832         mController.checkIdleStates(USER_ID);
833         assertBucket(STANDBY_BUCKET_NEVER);
834 
835         mInjector.mElapsedRealtime += 100;
836 
837         // Trigger a FOREGROUND_SERVICE_START and verify bucket
838         reportEvent(mController, FOREGROUND_SERVICE_START, mInjector.mElapsedRealtime, PACKAGE_1);
839         mController.checkIdleStates(USER_ID);
840         assertBucket(STANDBY_BUCKET_ACTIVE);
841 
842         // Verify it's still in ACTIVE close to end of timeout
843         mInjector.mElapsedRealtime += mController.mInitialForegroundServiceStartTimeoutMillis - 100;
844         mController.checkIdleStates(USER_ID);
845         assertBucket(STANDBY_BUCKET_ACTIVE);
846 
847         // Verify bucket moves to RARE after timeout
848         mInjector.mElapsedRealtime += 200;
849         mController.checkIdleStates(USER_ID);
850         assertBucket(STANDBY_BUCKET_RARE);
851 
852         // Trigger a FOREGROUND_SERVICE_START again
853         reportEvent(mController, FOREGROUND_SERVICE_START, mInjector.mElapsedRealtime, PACKAGE_1);
854         mController.checkIdleStates(USER_ID);
855         // Bucket should not be immediately elevated on subsequent service starts
856         assertBucket(STANDBY_BUCKET_RARE);
857     }
858 
859     @Test
testPredictionNotOverridden()860     public void testPredictionNotOverridden() throws Exception {
861         reportEvent(mController, USER_INTERACTION, 0, PACKAGE_1);
862         assertBucket(STANDBY_BUCKET_ACTIVE);
863 
864         mInjector.mElapsedRealtime = WORKING_SET_THRESHOLD - 1000;
865         reportEvent(mController, NOTIFICATION_SEEN, mInjector.mElapsedRealtime, PACKAGE_1);
866         assertBucket(STANDBY_BUCKET_ACTIVE);
867 
868         // Falls back to WORKING_SET
869         mInjector.mElapsedRealtime += 5000;
870         mController.checkIdleStates(USER_ID);
871         assertBucket(STANDBY_BUCKET_WORKING_SET);
872 
873         // Predict to ACTIVE
874         mInjector.mElapsedRealtime += 1000;
875         mController.setAppStandbyBucket(PACKAGE_1, USER_ID, STANDBY_BUCKET_ACTIVE,
876                 REASON_MAIN_PREDICTED, mInjector.mElapsedRealtime);
877         assertBucket(STANDBY_BUCKET_ACTIVE);
878 
879         // CheckIdleStates should not change the prediction
880         mInjector.mElapsedRealtime += 1000;
881         mController.checkIdleStates(USER_ID);
882         assertBucket(STANDBY_BUCKET_ACTIVE);
883     }
884 
885     @Test
testPredictionStrikesBack()886     public void testPredictionStrikesBack() throws Exception {
887         reportEvent(mController, USER_INTERACTION, 0, PACKAGE_1);
888         assertBucket(STANDBY_BUCKET_ACTIVE);
889 
890         // Predict to FREQUENT
891         mInjector.mElapsedRealtime = RARE_THRESHOLD;
892         mController.setAppStandbyBucket(PACKAGE_1, USER_ID, STANDBY_BUCKET_FREQUENT,
893                 REASON_MAIN_PREDICTED, mInjector.mElapsedRealtime);
894         assertBucket(STANDBY_BUCKET_FREQUENT);
895 
896         // Add a short timeout event
897         mInjector.mElapsedRealtime += 1000;
898         reportEvent(mController, SYSTEM_INTERACTION, mInjector.mElapsedRealtime, PACKAGE_1);
899         assertBucket(STANDBY_BUCKET_ACTIVE);
900         mInjector.mElapsedRealtime += 1000;
901         mController.checkIdleStates(USER_ID);
902         assertBucket(STANDBY_BUCKET_ACTIVE);
903 
904         // Verify it reverted to predicted
905         mInjector.mElapsedRealtime += WORKING_SET_THRESHOLD / 2;
906         mController.checkIdleStates(USER_ID);
907         assertBucket(STANDBY_BUCKET_FREQUENT);
908     }
909 
910     @Test
testAddActiveDeviceAdmin()911     public void testAddActiveDeviceAdmin() {
912         assertActiveAdmins(USER_ID, (String[]) null);
913         assertActiveAdmins(USER_ID2, (String[]) null);
914 
915         mController.addActiveDeviceAdmin(ADMIN_PKG, USER_ID);
916         assertActiveAdmins(USER_ID, ADMIN_PKG);
917         assertActiveAdmins(USER_ID2, (String[]) null);
918 
919         mController.addActiveDeviceAdmin(ADMIN_PKG, USER_ID);
920         assertActiveAdmins(USER_ID, ADMIN_PKG);
921         assertActiveAdmins(USER_ID2, (String[]) null);
922 
923         mController.addActiveDeviceAdmin(ADMIN_PKG2, USER_ID2);
924         assertActiveAdmins(USER_ID, ADMIN_PKG);
925         assertActiveAdmins(USER_ID2, ADMIN_PKG2);
926     }
927 
928     @Test
testSetActiveAdminApps()929     public void testSetActiveAdminApps() {
930         assertActiveAdmins(USER_ID, (String[]) null);
931         assertActiveAdmins(USER_ID2, (String[]) null);
932 
933         setActiveAdmins(USER_ID, ADMIN_PKG, ADMIN_PKG2);
934         assertActiveAdmins(USER_ID, ADMIN_PKG, ADMIN_PKG2);
935         assertActiveAdmins(USER_ID2, (String[]) null);
936 
937         mController.addActiveDeviceAdmin(ADMIN_PKG2, USER_ID2);
938         setActiveAdmins(USER_ID2, ADMIN_PKG);
939         assertActiveAdmins(USER_ID, ADMIN_PKG, ADMIN_PKG2);
940         assertActiveAdmins(USER_ID2, ADMIN_PKG);
941 
942         mController.setActiveAdminApps(null, USER_ID);
943         assertActiveAdmins(USER_ID, (String[]) null);
944     }
945 
946     @Test
isActiveDeviceAdmin()947     public void isActiveDeviceAdmin() {
948         assertActiveAdmins(USER_ID, (String[]) null);
949         assertActiveAdmins(USER_ID2, (String[]) null);
950 
951         mController.addActiveDeviceAdmin(ADMIN_PKG, USER_ID);
952         assertIsActiveAdmin(ADMIN_PKG, USER_ID);
953         assertIsNotActiveAdmin(ADMIN_PKG, USER_ID2);
954 
955         mController.addActiveDeviceAdmin(ADMIN_PKG2, USER_ID2);
956         mController.addActiveDeviceAdmin(ADMIN_PKG, USER_ID2);
957         assertIsActiveAdmin(ADMIN_PKG, USER_ID);
958         assertIsNotActiveAdmin(ADMIN_PKG2, USER_ID);
959         assertIsActiveAdmin(ADMIN_PKG, USER_ID2);
960         assertIsActiveAdmin(ADMIN_PKG2, USER_ID2);
961 
962         setActiveAdmins(USER_ID2, ADMIN_PKG2);
963         assertIsActiveAdmin(ADMIN_PKG2, USER_ID2);
964         assertIsNotActiveAdmin(ADMIN_PKG, USER_ID2);
965         assertIsActiveAdmin(ADMIN_PKG, USER_ID);
966         assertIsNotActiveAdmin(ADMIN_PKG2, USER_ID);
967     }
968 
getAdminAppsStr(int userId)969     private String getAdminAppsStr(int userId) {
970         return getAdminAppsStr(userId, mController.getActiveAdminAppsForTest(userId));
971     }
972 
getAdminAppsStr(int userId, Set<String> adminApps)973     private String getAdminAppsStr(int userId, Set<String> adminApps) {
974         return "admin apps for u" + userId + ": "
975                 + (adminApps == null ? "null" : Arrays.toString(adminApps.toArray()));
976     }
977 
assertIsActiveAdmin(String adminApp, int userId)978     private void assertIsActiveAdmin(String adminApp, int userId) {
979         assertTrue(adminApp + " should be an active admin; " + getAdminAppsStr(userId),
980                 mController.isActiveDeviceAdmin(adminApp, userId));
981     }
982 
assertIsNotActiveAdmin(String adminApp, int userId)983     private void assertIsNotActiveAdmin(String adminApp, int userId) {
984         assertFalse(adminApp + " shouldn't be an active admin; " + getAdminAppsStr(userId),
985                 mController.isActiveDeviceAdmin(adminApp, userId));
986     }
987 
assertActiveAdmins(int userId, String... admins)988     private void assertActiveAdmins(int userId, String... admins) {
989         final Set<String> actualAdminApps = mController.getActiveAdminAppsForTest(userId);
990         if (admins == null) {
991             if (actualAdminApps != null && !actualAdminApps.isEmpty()) {
992                 fail("Admin apps should be null; " + getAdminAppsStr(userId, actualAdminApps));
993             }
994             return;
995         }
996         assertEquals("No. of admin apps not equal; " + getAdminAppsStr(userId, actualAdminApps)
997                 + "; expected=" + Arrays.toString(admins), admins.length, actualAdminApps.size());
998         final Set<String> adminAppsCopy = new ArraySet<>(actualAdminApps);
999         for (String admin : admins) {
1000             adminAppsCopy.remove(admin);
1001         }
1002         assertTrue("Unexpected admin apps; " + getAdminAppsStr(userId, actualAdminApps)
1003                 + "; expected=" + Arrays.toString(admins), adminAppsCopy.isEmpty());
1004     }
1005 
setActiveAdmins(int userId, String... admins)1006     private void setActiveAdmins(int userId, String... admins) {
1007         mController.setActiveAdminApps(new ArraySet<>(Arrays.asList(admins)), userId);
1008     }
1009 }
1010