Home | History | Annotate | Download | only in usage
      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.NOTIFICATION_SEEN;
     20 import static android.app.usage.UsageEvents.Event.SLICE_PINNED;
     21 import static android.app.usage.UsageEvents.Event.SLICE_PINNED_PRIV;
     22 import static android.app.usage.UsageEvents.Event.SYSTEM_INTERACTION;
     23 import static android.app.usage.UsageEvents.Event.USER_INTERACTION;
     24 import static android.app.usage.UsageStatsManager.REASON_MAIN_DEFAULT;
     25 import static android.app.usage.UsageStatsManager.REASON_MAIN_FORCED;
     26 import static android.app.usage.UsageStatsManager.REASON_MAIN_PREDICTED;
     27 import static android.app.usage.UsageStatsManager.REASON_MAIN_TIMEOUT;
     28 import static android.app.usage.UsageStatsManager.REASON_MAIN_USAGE;
     29 import static android.app.usage.UsageStatsManager.STANDBY_BUCKET_ACTIVE;
     30 import static android.app.usage.UsageStatsManager.STANDBY_BUCKET_EXEMPTED;
     31 import static android.app.usage.UsageStatsManager.STANDBY_BUCKET_FREQUENT;
     32 import static android.app.usage.UsageStatsManager.STANDBY_BUCKET_NEVER;
     33 import static android.app.usage.UsageStatsManager.STANDBY_BUCKET_RARE;
     34 import static android.app.usage.UsageStatsManager.STANDBY_BUCKET_WORKING_SET;
     35 
     36 import static org.junit.Assert.assertEquals;
     37 import static org.junit.Assert.assertFalse;
     38 import static org.junit.Assert.assertNotEquals;
     39 import static org.junit.Assert.assertTrue;
     40 
     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.Matchers.anyString;
     45 import static org.mockito.Mockito.doReturn;
     46 import static org.mockito.Mockito.mock;
     47 
     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.support.test.filters.SmallTest;
     62 import android.support.test.InstrumentationRegistry;
     63 import android.support.test.runner.AndroidJUnit4;
     64 import android.util.ArraySet;
     65 import android.view.Display;
     66 
     67 import com.android.server.SystemService;
     68 
     69 import org.junit.Before;
     70 import org.junit.Test;
     71 import org.junit.runner.RunWith;
     72 
     73 import java.io.File;
     74 import java.util.ArrayList;
     75 import java.util.Arrays;
     76 import java.util.List;
     77 import java.util.Set;
     78 import java.util.concurrent.CountDownLatch;
     79 import java.util.concurrent.TimeUnit;
     80 
     81 /**
     82  * Unit test for AppStandbyController.
     83  */
     84 @RunWith(AndroidJUnit4.class)
     85 @Presubmit
     86 @SmallTest
     87 public class AppStandbyControllerTests {
     88 
     89     private static final String PACKAGE_1 = "com.example.foo";
     90     private static final int UID_1 = 10000;
     91     private static final String PACKAGE_EXEMPTED_1 = "com.android.exempted";
     92     private static final int UID_EXEMPTED_1 = 10001;
     93     private static final int USER_ID = 0;
     94     private static final int USER_ID2 = 10;
     95 
     96     private static final String ADMIN_PKG = "com.android.admin";
     97     private static final String ADMIN_PKG2 = "com.android.admin2";
     98     private static final String ADMIN_PKG3 = "com.android.admin3";
     99 
    100     private static final long MINUTE_MS = 60 * 1000;
    101     private static final long HOUR_MS = 60 * MINUTE_MS;
    102     private static final long DAY_MS = 24 * HOUR_MS;
    103 
    104     private static final long WORKING_SET_THRESHOLD = 12 * HOUR_MS;
    105     private static final long FREQUENT_THRESHOLD = 24 * HOUR_MS;
    106     private static final long RARE_THRESHOLD = 48 * HOUR_MS;
    107     // Short STABLE_CHARGING_THRESHOLD for testing purposes
    108     private static final long STABLE_CHARGING_THRESHOLD = 2000;
    109 
    110     private MyInjector mInjector;
    111     private AppStandbyController mController;
    112 
    113     static class MyContextWrapper extends ContextWrapper {
    114         PackageManager mockPm = mock(PackageManager.class);
    115 
    116         public MyContextWrapper(Context base) {
    117             super(base);
    118         }
    119 
    120         public PackageManager getPackageManager() {
    121             return mockPm;
    122         }
    123     }
    124 
    125     static class MyInjector extends AppStandbyController.Injector {
    126         long mElapsedRealtime;
    127         boolean mIsCharging;
    128         List<String> mPowerSaveWhitelistExceptIdle = new ArrayList<>();
    129         boolean mDisplayOn;
    130         DisplayManager.DisplayListener mDisplayListener;
    131         String mBoundWidgetPackage = PACKAGE_EXEMPTED_1;
    132 
    133         MyInjector(Context context, Looper looper) {
    134             super(context, looper);
    135         }
    136 
    137         @Override
    138         void onBootPhase(int phase) {
    139         }
    140 
    141         @Override
    142         int getBootPhase() {
    143             return SystemService.PHASE_BOOT_COMPLETED;
    144         }
    145 
    146         @Override
    147         long elapsedRealtime() {
    148             return mElapsedRealtime;
    149         }
    150 
    151         @Override
    152         long currentTimeMillis() {
    153             return mElapsedRealtime;
    154         }
    155 
    156         @Override
    157         boolean isAppIdleEnabled() {
    158             return true;
    159         }
    160 
    161         @Override
    162         boolean isCharging() {
    163             return mIsCharging;
    164         }
    165 
    166         @Override
    167         boolean isPowerSaveWhitelistExceptIdleApp(String packageName) throws RemoteException {
    168             return mPowerSaveWhitelistExceptIdle.contains(packageName);
    169         }
    170 
    171         @Override
    172         File getDataSystemDirectory() {
    173             return new File(getContext().getFilesDir(), Long.toString(Math.randomLongInternal()));
    174         }
    175 
    176         @Override
    177         void noteEvent(int event, String packageName, int uid) throws RemoteException {
    178         }
    179 
    180         @Override
    181         boolean isPackageEphemeral(int userId, String packageName) {
    182             // TODO: update when testing ephemeral apps scenario
    183             return false;
    184         }
    185 
    186         @Override
    187         int[] getRunningUserIds() {
    188             return new int[] {USER_ID};
    189         }
    190 
    191         @Override
    192         boolean isDefaultDisplayOn() {
    193             return mDisplayOn;
    194         }
    195 
    196         @Override
    197         void registerDisplayListener(DisplayManager.DisplayListener listener, Handler handler) {
    198             mDisplayListener = listener;
    199         }
    200 
    201         @Override
    202         String getActiveNetworkScorer() {
    203             return null;
    204         }
    205 
    206         @Override
    207         public boolean isBoundWidgetPackage(AppWidgetManager appWidgetManager, String packageName,
    208                 int userId) {
    209             return packageName != null && packageName.equals(mBoundWidgetPackage);
    210         }
    211 
    212         @Override
    213         String getAppIdleSettings() {
    214             return "screen_thresholds=0/0/0/" + HOUR_MS + ",elapsed_thresholds=0/"
    215                     + WORKING_SET_THRESHOLD + "/"
    216                     + FREQUENT_THRESHOLD + "/"
    217                     + RARE_THRESHOLD + ","
    218                     + "stable_charging_threshold=" + STABLE_CHARGING_THRESHOLD;
    219         }
    220 
    221         @Override
    222         public boolean isDeviceIdleMode() {
    223             return false;
    224         }
    225 
    226         // Internal methods
    227 
    228         void setDisplayOn(boolean on) {
    229             mDisplayOn = on;
    230             if (mDisplayListener != null) {
    231                 mDisplayListener.onDisplayChanged(Display.DEFAULT_DISPLAY);
    232             }
    233         }
    234     }
    235 
    236     private void setupPm(PackageManager mockPm) throws PackageManager.NameNotFoundException {
    237         List<PackageInfo> packages = new ArrayList<>();
    238         PackageInfo pi = new PackageInfo();
    239         pi.applicationInfo = new ApplicationInfo();
    240         pi.applicationInfo.uid = UID_1;
    241         pi.packageName = PACKAGE_1;
    242         packages.add(pi);
    243 
    244         PackageInfo pie = new PackageInfo();
    245         pie.applicationInfo = new ApplicationInfo();
    246         pie.applicationInfo.uid = UID_EXEMPTED_1;
    247         pie.packageName = PACKAGE_EXEMPTED_1;
    248         packages.add(pie);
    249 
    250         doReturn(packages).when(mockPm).getInstalledPackagesAsUser(anyInt(), anyInt());
    251         try {
    252             doReturn(UID_1).when(mockPm).getPackageUidAsUser(eq(PACKAGE_1), anyInt(), anyInt());
    253             doReturn(UID_EXEMPTED_1).when(mockPm).getPackageUidAsUser(eq(PACKAGE_EXEMPTED_1),
    254                     anyInt(), anyInt());
    255             doReturn(pi.applicationInfo).when(mockPm).getApplicationInfo(eq(pi.packageName),
    256                     anyInt());
    257             doReturn(pie.applicationInfo).when(mockPm).getApplicationInfo(eq(pie.packageName),
    258                     anyInt());
    259         } catch (PackageManager.NameNotFoundException nnfe) {}
    260     }
    261 
    262     private void setChargingState(AppStandbyController controller, boolean charging) {
    263         mInjector.mIsCharging = charging;
    264         if (controller != null) {
    265             controller.setChargingState(charging);
    266         }
    267     }
    268 
    269     private AppStandbyController setupController() throws Exception {
    270         mInjector.mElapsedRealtime = 0;
    271         setupPm(mInjector.getContext().getPackageManager());
    272         AppStandbyController controller = new AppStandbyController(mInjector);
    273         controller.initializeDefaultsForSystemApps(USER_ID);
    274         controller.onBootPhase(SystemService.PHASE_SYSTEM_SERVICES_READY);
    275         controller.onBootPhase(SystemService.PHASE_BOOT_COMPLETED);
    276         mInjector.setDisplayOn(false);
    277         mInjector.setDisplayOn(true);
    278         setChargingState(controller, false);
    279         controller.checkIdleStates(USER_ID);
    280         assertEquals(STANDBY_BUCKET_EXEMPTED,
    281                 controller.getAppStandbyBucket(PACKAGE_EXEMPTED_1, USER_ID,
    282                         mInjector.mElapsedRealtime, false));
    283         assertNotEquals(STANDBY_BUCKET_EXEMPTED,
    284                 controller.getAppStandbyBucket(PACKAGE_1, USER_ID,
    285                         mInjector.mElapsedRealtime, false));
    286 
    287         return controller;
    288     }
    289 
    290     private long getCurrentTime() {
    291         return TimeUnit.NANOSECONDS.toMillis(System.nanoTime());
    292     }
    293 
    294     @Before
    295     public void setUp() throws Exception {
    296         MyContextWrapper myContext = new MyContextWrapper(InstrumentationRegistry.getContext());
    297         mInjector = new MyInjector(myContext, Looper.getMainLooper());
    298         mController = setupController();
    299         setChargingState(mController, false);
    300     }
    301 
    302     private class TestParoleListener extends UsageStatsManagerInternal.AppIdleStateChangeListener {
    303         private boolean mOnParole = false;
    304         private CountDownLatch mLatch;
    305         private long mLastParoleChangeTime;
    306 
    307         public boolean getParoleState() {
    308             synchronized (this) {
    309                 return mOnParole;
    310             }
    311         }
    312 
    313         public void rearmLatch() {
    314             synchronized (this) {
    315                 mLatch = new CountDownLatch(1);
    316             }
    317         }
    318 
    319         public void awaitOnLatch(long time) throws Exception {
    320             mLatch.await(time, TimeUnit.MILLISECONDS);
    321         }
    322 
    323         public long getLastParoleChangeTime() {
    324             synchronized (this) {
    325                 return mLastParoleChangeTime;
    326             }
    327         }
    328 
    329         @Override
    330         public void onAppIdleStateChanged(String packageName, int userId, boolean idle,
    331                 int bucket, int reason) {
    332         }
    333 
    334         @Override
    335         public void onParoleStateChanged(boolean isParoleOn) {
    336             synchronized (this) {
    337                 // Only record information if it is being looked for
    338                 if (mLatch.getCount() > 0) {
    339                     mOnParole = isParoleOn;
    340                     mLastParoleChangeTime = getCurrentTime();
    341                     mLatch.countDown();
    342                 }
    343             }
    344         }
    345     }
    346 
    347     @Test
    348     public void testCharging() throws Exception {
    349         long startTime;
    350         TestParoleListener paroleListener = new TestParoleListener();
    351         long marginOfError = 200;
    352 
    353         // Charging
    354         paroleListener.rearmLatch();
    355         mController.addListener(paroleListener);
    356         startTime = getCurrentTime();
    357         setChargingState(mController, true);
    358         paroleListener.awaitOnLatch(STABLE_CHARGING_THRESHOLD * 3 / 2);
    359         assertTrue(paroleListener.mOnParole);
    360         // Parole will only be granted after device has been charging for a sufficient amount of
    361         // time.
    362         assertEquals(STABLE_CHARGING_THRESHOLD,
    363                 paroleListener.getLastParoleChangeTime() - startTime,
    364                 marginOfError);
    365 
    366         // Discharging
    367         paroleListener.rearmLatch();
    368         startTime = getCurrentTime();
    369         setChargingState(mController, false);
    370         mController.checkIdleStates(USER_ID);
    371         paroleListener.awaitOnLatch(STABLE_CHARGING_THRESHOLD * 3 / 2);
    372         assertFalse(paroleListener.getParoleState());
    373         // Parole should be revoked immediately
    374         assertEquals(0,
    375                 paroleListener.getLastParoleChangeTime() - startTime,
    376                 marginOfError);
    377 
    378         // Brief Charging
    379         paroleListener.rearmLatch();
    380         setChargingState(mController, true);
    381         setChargingState(mController, false);
    382         // Device stopped charging before the stable charging threshold.
    383         // Parole should not be granted at the end of the threshold
    384         paroleListener.awaitOnLatch(STABLE_CHARGING_THRESHOLD * 3 / 2);
    385         assertFalse(paroleListener.getParoleState());
    386 
    387         // Charging Again
    388         paroleListener.rearmLatch();
    389         startTime = getCurrentTime();
    390         setChargingState(mController, true);
    391         paroleListener.awaitOnLatch(STABLE_CHARGING_THRESHOLD * 3 / 2);
    392         assertTrue(paroleListener.getParoleState());
    393         assertTrue(paroleListener.mOnParole);
    394         assertEquals(STABLE_CHARGING_THRESHOLD,
    395                 paroleListener.getLastParoleChangeTime() - startTime,
    396                 marginOfError);
    397     }
    398 
    399     private void assertTimeout(AppStandbyController controller, long elapsedTime, int bucket) {
    400         mInjector.mElapsedRealtime = elapsedTime;
    401         controller.checkIdleStates(USER_ID);
    402         assertEquals(bucket,
    403                 controller.getAppStandbyBucket(PACKAGE_1, USER_ID, mInjector.mElapsedRealtime,
    404                         false));
    405     }
    406 
    407     private void reportEvent(AppStandbyController controller, int eventType,
    408             long elapsedTime) {
    409         // Back to ACTIVE on event
    410         mInjector.mElapsedRealtime = elapsedTime;
    411         UsageEvents.Event ev = new UsageEvents.Event();
    412         ev.mPackage = PACKAGE_1;
    413         ev.mEventType = eventType;
    414         controller.reportEvent(ev, elapsedTime, USER_ID);
    415     }
    416 
    417     private int getStandbyBucket(AppStandbyController controller) {
    418         return controller.getAppStandbyBucket(PACKAGE_1, USER_ID, mInjector.mElapsedRealtime,
    419                 true);
    420     }
    421 
    422     private void assertBucket(int bucket) {
    423         assertEquals(bucket, getStandbyBucket(mController));
    424     }
    425 
    426     @Test
    427     public void testBuckets() throws Exception {
    428         assertTimeout(mController, 0, STANDBY_BUCKET_NEVER);
    429 
    430         reportEvent(mController, USER_INTERACTION, 0);
    431 
    432         // ACTIVE bucket
    433         assertTimeout(mController, WORKING_SET_THRESHOLD - 1, STANDBY_BUCKET_ACTIVE);
    434 
    435         // WORKING_SET bucket
    436         assertTimeout(mController, WORKING_SET_THRESHOLD + 1, STANDBY_BUCKET_WORKING_SET);
    437 
    438         // WORKING_SET bucket
    439         assertTimeout(mController, FREQUENT_THRESHOLD - 1, STANDBY_BUCKET_WORKING_SET);
    440 
    441         // FREQUENT bucket
    442         assertTimeout(mController, FREQUENT_THRESHOLD + 1, STANDBY_BUCKET_FREQUENT);
    443 
    444         // RARE bucket
    445         assertTimeout(mController, RARE_THRESHOLD + 1, STANDBY_BUCKET_RARE);
    446 
    447         reportEvent(mController, USER_INTERACTION, RARE_THRESHOLD + 1);
    448 
    449         assertTimeout(mController, RARE_THRESHOLD + 1, STANDBY_BUCKET_ACTIVE);
    450 
    451         // RARE bucket
    452         assertTimeout(mController, RARE_THRESHOLD * 2 + 2, STANDBY_BUCKET_RARE);
    453     }
    454 
    455     @Test
    456     public void testScreenTimeAndBuckets() throws Exception {
    457         mInjector.setDisplayOn(false);
    458 
    459         assertTimeout(mController, 0, STANDBY_BUCKET_NEVER);
    460 
    461         reportEvent(mController, USER_INTERACTION, 0);
    462 
    463         // ACTIVE bucket
    464         assertTimeout(mController, WORKING_SET_THRESHOLD - 1, STANDBY_BUCKET_ACTIVE);
    465 
    466         // WORKING_SET bucket
    467         assertTimeout(mController, WORKING_SET_THRESHOLD + 1, STANDBY_BUCKET_WORKING_SET);
    468 
    469         // RARE bucket, should fail because the screen wasn't ON.
    470         mInjector.mElapsedRealtime = RARE_THRESHOLD + 1;
    471         mController.checkIdleStates(USER_ID);
    472         assertNotEquals(STANDBY_BUCKET_RARE, getStandbyBucket(mController));
    473 
    474         mInjector.setDisplayOn(true);
    475         assertTimeout(mController, RARE_THRESHOLD * 2 + 2, STANDBY_BUCKET_RARE);
    476     }
    477 
    478     @Test
    479     public void testForcedIdle() throws Exception {
    480         mController.forceIdleState(PACKAGE_1, USER_ID, true);
    481         assertEquals(STANDBY_BUCKET_RARE, getStandbyBucket(mController));
    482         assertTrue(mController.isAppIdleFiltered(PACKAGE_1, UID_1, USER_ID, 0));
    483 
    484         mController.forceIdleState(PACKAGE_1, USER_ID, false);
    485         assertEquals(STANDBY_BUCKET_ACTIVE, mController.getAppStandbyBucket(PACKAGE_1, USER_ID, 0,
    486                 true));
    487         assertFalse(mController.isAppIdleFiltered(PACKAGE_1, UID_1, USER_ID, 0));
    488     }
    489 
    490     @Test
    491     public void testNotificationEvent() throws Exception {
    492         reportEvent(mController, USER_INTERACTION, 0);
    493         assertEquals(STANDBY_BUCKET_ACTIVE, getStandbyBucket(mController));
    494         mInjector.mElapsedRealtime = 1;
    495         reportEvent(mController, NOTIFICATION_SEEN, mInjector.mElapsedRealtime);
    496         assertEquals(STANDBY_BUCKET_ACTIVE, getStandbyBucket(mController));
    497 
    498         mController.forceIdleState(PACKAGE_1, USER_ID, true);
    499         reportEvent(mController, NOTIFICATION_SEEN, mInjector.mElapsedRealtime);
    500         assertEquals(STANDBY_BUCKET_WORKING_SET, getStandbyBucket(mController));
    501     }
    502 
    503     @Test
    504     public void testSlicePinnedEvent() throws Exception {
    505         reportEvent(mController, USER_INTERACTION, 0);
    506         assertEquals(STANDBY_BUCKET_ACTIVE, getStandbyBucket(mController));
    507         mInjector.mElapsedRealtime = 1;
    508         reportEvent(mController, SLICE_PINNED, mInjector.mElapsedRealtime);
    509         assertEquals(STANDBY_BUCKET_ACTIVE, getStandbyBucket(mController));
    510 
    511         mController.forceIdleState(PACKAGE_1, USER_ID, true);
    512         reportEvent(mController, SLICE_PINNED, mInjector.mElapsedRealtime);
    513         assertEquals(STANDBY_BUCKET_WORKING_SET, getStandbyBucket(mController));
    514     }
    515 
    516     @Test
    517     public void testSlicePinnedPrivEvent() throws Exception {
    518         mController.forceIdleState(PACKAGE_1, USER_ID, true);
    519         reportEvent(mController, SLICE_PINNED_PRIV, mInjector.mElapsedRealtime);
    520         assertEquals(STANDBY_BUCKET_ACTIVE, getStandbyBucket(mController));
    521     }
    522 
    523     @Test
    524     public void testPredictionTimedout() throws Exception {
    525         // Set it to timeout or usage, so that prediction can override it
    526         mController.setAppStandbyBucket(PACKAGE_1, USER_ID, STANDBY_BUCKET_RARE,
    527                 REASON_MAIN_TIMEOUT, HOUR_MS);
    528         assertEquals(STANDBY_BUCKET_RARE, getStandbyBucket(mController));
    529 
    530         mController.setAppStandbyBucket(PACKAGE_1, USER_ID, STANDBY_BUCKET_ACTIVE,
    531                 REASON_MAIN_PREDICTED, HOUR_MS);
    532         assertEquals(STANDBY_BUCKET_ACTIVE, getStandbyBucket(mController));
    533 
    534         // Fast forward 12 hours
    535         mInjector.mElapsedRealtime += WORKING_SET_THRESHOLD;
    536         mController.checkIdleStates(USER_ID);
    537         // Should still be in predicted bucket, since prediction timeout is 1 day since prediction
    538         assertEquals(STANDBY_BUCKET_ACTIVE, getStandbyBucket(mController));
    539         // Fast forward two more hours
    540         mInjector.mElapsedRealtime += 2 * HOUR_MS;
    541         mController.checkIdleStates(USER_ID);
    542         // Should have now applied prediction timeout
    543         assertEquals(STANDBY_BUCKET_WORKING_SET, getStandbyBucket(mController));
    544 
    545         // Fast forward RARE bucket
    546         mInjector.mElapsedRealtime += RARE_THRESHOLD;
    547         mController.checkIdleStates(USER_ID);
    548         // Should continue to apply prediction timeout
    549         assertEquals(STANDBY_BUCKET_RARE, getStandbyBucket(mController));
    550     }
    551 
    552     @Test
    553     public void testOverrides() throws Exception {
    554         // Can force to NEVER
    555         mController.setAppStandbyBucket(PACKAGE_1, USER_ID, STANDBY_BUCKET_NEVER,
    556                 REASON_MAIN_FORCED, 1 * HOUR_MS);
    557         assertEquals(STANDBY_BUCKET_NEVER, getStandbyBucket(mController));
    558 
    559         // Prediction can't override FORCED reason
    560         mController.setAppStandbyBucket(PACKAGE_1, USER_ID, STANDBY_BUCKET_FREQUENT,
    561                 REASON_MAIN_FORCED, 1 * HOUR_MS);
    562         mController.setAppStandbyBucket(PACKAGE_1, USER_ID, STANDBY_BUCKET_WORKING_SET,
    563                 REASON_MAIN_PREDICTED, 1 * HOUR_MS);
    564         assertEquals(STANDBY_BUCKET_FREQUENT, getStandbyBucket(mController));
    565 
    566         // Prediction can't override NEVER
    567         mController.setAppStandbyBucket(PACKAGE_1, USER_ID, STANDBY_BUCKET_NEVER,
    568                 REASON_MAIN_DEFAULT, 2 * HOUR_MS);
    569         mController.setAppStandbyBucket(PACKAGE_1, USER_ID, STANDBY_BUCKET_ACTIVE,
    570                 REASON_MAIN_PREDICTED, 2 * HOUR_MS);
    571         assertEquals(STANDBY_BUCKET_NEVER, getStandbyBucket(mController));
    572 
    573         // Prediction can't set to NEVER
    574         mController.setAppStandbyBucket(PACKAGE_1, USER_ID, STANDBY_BUCKET_ACTIVE,
    575                 REASON_MAIN_USAGE, 2 * HOUR_MS);
    576         mController.setAppStandbyBucket(PACKAGE_1, USER_ID, STANDBY_BUCKET_NEVER,
    577                 REASON_MAIN_PREDICTED, 2 * HOUR_MS);
    578         assertEquals(STANDBY_BUCKET_ACTIVE, getStandbyBucket(mController));
    579     }
    580 
    581     @Test
    582     public void testTimeout() throws Exception {
    583         reportEvent(mController, USER_INTERACTION, 0);
    584         assertBucket(STANDBY_BUCKET_ACTIVE);
    585 
    586         mInjector.mElapsedRealtime = 2000;
    587         mController.setAppStandbyBucket(PACKAGE_1, USER_ID, STANDBY_BUCKET_FREQUENT,
    588                 REASON_MAIN_PREDICTED, mInjector.mElapsedRealtime);
    589         assertBucket(STANDBY_BUCKET_ACTIVE);
    590 
    591         // bucketing works after timeout
    592         mInjector.mElapsedRealtime = mController.mPredictionTimeoutMillis - 100;
    593         mController.checkIdleStates(USER_ID);
    594         // Use recent prediction
    595         assertBucket(STANDBY_BUCKET_FREQUENT);
    596 
    597         // Way past prediction timeout, use system thresholds
    598         mInjector.mElapsedRealtime = RARE_THRESHOLD * 4;
    599         mController.checkIdleStates(USER_ID);
    600         assertBucket(STANDBY_BUCKET_RARE);
    601     }
    602 
    603     @Test
    604     public void testCascadingTimeouts() throws Exception {
    605         reportEvent(mController, USER_INTERACTION, 0);
    606         assertBucket(STANDBY_BUCKET_ACTIVE);
    607 
    608         reportEvent(mController, NOTIFICATION_SEEN, 1000);
    609         assertBucket(STANDBY_BUCKET_ACTIVE);
    610 
    611         mController.setAppStandbyBucket(PACKAGE_1, USER_ID, STANDBY_BUCKET_WORKING_SET,
    612                 REASON_MAIN_PREDICTED, 1000);
    613         assertBucket(STANDBY_BUCKET_ACTIVE);
    614 
    615         mController.setAppStandbyBucket(PACKAGE_1, USER_ID, STANDBY_BUCKET_FREQUENT,
    616                 REASON_MAIN_PREDICTED, 2000 + mController.mStrongUsageTimeoutMillis);
    617         assertBucket(STANDBY_BUCKET_WORKING_SET);
    618 
    619         mController.setAppStandbyBucket(PACKAGE_1, USER_ID, STANDBY_BUCKET_FREQUENT,
    620                 REASON_MAIN_PREDICTED, 2000 + mController.mNotificationSeenTimeoutMillis);
    621         assertBucket(STANDBY_BUCKET_FREQUENT);
    622     }
    623 
    624     @Test
    625     public void testOverlappingTimeouts() throws Exception {
    626         reportEvent(mController, USER_INTERACTION, 0);
    627         assertBucket(STANDBY_BUCKET_ACTIVE);
    628 
    629         reportEvent(mController, NOTIFICATION_SEEN, 1000);
    630         assertBucket(STANDBY_BUCKET_ACTIVE);
    631 
    632         // Overlapping USER_INTERACTION before previous one times out
    633         reportEvent(mController, USER_INTERACTION, mController.mStrongUsageTimeoutMillis - 1000);
    634         assertBucket(STANDBY_BUCKET_ACTIVE);
    635 
    636         // Still in ACTIVE after first USER_INTERACTION times out
    637         mInjector.mElapsedRealtime = mController.mStrongUsageTimeoutMillis + 1000;
    638         mController.setAppStandbyBucket(PACKAGE_1, USER_ID, STANDBY_BUCKET_FREQUENT,
    639                 REASON_MAIN_PREDICTED, mInjector.mElapsedRealtime);
    640         assertBucket(STANDBY_BUCKET_ACTIVE);
    641 
    642         // Both timed out, so NOTIFICATION_SEEN timeout should be effective
    643         mInjector.mElapsedRealtime = mController.mStrongUsageTimeoutMillis * 2 + 2000;
    644         mController.setAppStandbyBucket(PACKAGE_1, USER_ID, STANDBY_BUCKET_FREQUENT,
    645                 REASON_MAIN_PREDICTED, mInjector.mElapsedRealtime);
    646         assertBucket(STANDBY_BUCKET_WORKING_SET);
    647 
    648         mInjector.mElapsedRealtime = mController.mNotificationSeenTimeoutMillis + 2000;
    649         mController.setAppStandbyBucket(PACKAGE_1, USER_ID, STANDBY_BUCKET_RARE,
    650                 REASON_MAIN_PREDICTED, mInjector.mElapsedRealtime);
    651         assertBucket(STANDBY_BUCKET_RARE);
    652     }
    653 
    654     @Test
    655     public void testSystemInteractionTimeout() throws Exception {
    656         setChargingState(mController, false);
    657 
    658         reportEvent(mController, USER_INTERACTION, 0);
    659         // Fast forward to RARE
    660         mInjector.mElapsedRealtime = RARE_THRESHOLD + 100;
    661         mController.checkIdleStates(USER_ID);
    662         assertBucket(STANDBY_BUCKET_RARE);
    663 
    664         // Trigger a SYSTEM_INTERACTION and verify bucket
    665         reportEvent(mController, SYSTEM_INTERACTION, mInjector.mElapsedRealtime);
    666         assertBucket(STANDBY_BUCKET_ACTIVE);
    667 
    668         // Verify it's still in ACTIVE close to end of timeout
    669         mInjector.mElapsedRealtime += mController.mSystemInteractionTimeoutMillis - 100;
    670         mController.checkIdleStates(USER_ID);
    671         assertBucket(STANDBY_BUCKET_ACTIVE);
    672 
    673         // Verify bucket moves to RARE after timeout
    674         mInjector.mElapsedRealtime += 200;
    675         mController.checkIdleStates(USER_ID);
    676         assertBucket(STANDBY_BUCKET_RARE);
    677     }
    678 
    679     @Test
    680     public void testPredictionNotOverridden() throws Exception {
    681         reportEvent(mController, USER_INTERACTION, 0);
    682         assertBucket(STANDBY_BUCKET_ACTIVE);
    683 
    684         mInjector.mElapsedRealtime = WORKING_SET_THRESHOLD - 1000;
    685         reportEvent(mController, NOTIFICATION_SEEN, mInjector.mElapsedRealtime);
    686         assertBucket(STANDBY_BUCKET_ACTIVE);
    687 
    688         // Falls back to WORKING_SET
    689         mInjector.mElapsedRealtime += 5000;
    690         mController.checkIdleStates(USER_ID);
    691         assertBucket(STANDBY_BUCKET_WORKING_SET);
    692 
    693         // Predict to ACTIVE
    694         mInjector.mElapsedRealtime += 1000;
    695         mController.setAppStandbyBucket(PACKAGE_1, USER_ID, STANDBY_BUCKET_ACTIVE,
    696                 REASON_MAIN_PREDICTED, mInjector.mElapsedRealtime);
    697         assertBucket(STANDBY_BUCKET_ACTIVE);
    698 
    699         // CheckIdleStates should not change the prediction
    700         mInjector.mElapsedRealtime += 1000;
    701         mController.checkIdleStates(USER_ID);
    702         assertBucket(STANDBY_BUCKET_ACTIVE);
    703     }
    704 
    705     @Test
    706     public void testPredictionStrikesBack() throws Exception {
    707         reportEvent(mController, USER_INTERACTION, 0);
    708         assertBucket(STANDBY_BUCKET_ACTIVE);
    709 
    710         // Predict to FREQUENT
    711         mInjector.mElapsedRealtime = RARE_THRESHOLD;
    712         mController.setAppStandbyBucket(PACKAGE_1, USER_ID, STANDBY_BUCKET_FREQUENT,
    713                 REASON_MAIN_PREDICTED, mInjector.mElapsedRealtime);
    714         assertBucket(STANDBY_BUCKET_FREQUENT);
    715 
    716         // Add a short timeout event
    717         mInjector.mElapsedRealtime += 1000;
    718         reportEvent(mController, SYSTEM_INTERACTION, mInjector.mElapsedRealtime);
    719         assertBucket(STANDBY_BUCKET_ACTIVE);
    720         mInjector.mElapsedRealtime += 1000;
    721         mController.checkIdleStates(USER_ID);
    722         assertBucket(STANDBY_BUCKET_ACTIVE);
    723 
    724         // Verify it reverted to predicted
    725         mInjector.mElapsedRealtime += WORKING_SET_THRESHOLD / 2;
    726         mController.checkIdleStates(USER_ID);
    727         assertBucket(STANDBY_BUCKET_FREQUENT);
    728     }
    729 
    730     @Test
    731     public void testAddActiveDeviceAdmin() {
    732         assertActiveAdmins(USER_ID, (String[]) null);
    733         assertActiveAdmins(USER_ID2, (String[]) null);
    734 
    735         mController.addActiveDeviceAdmin(ADMIN_PKG, USER_ID);
    736         assertActiveAdmins(USER_ID, ADMIN_PKG);
    737         assertActiveAdmins(USER_ID2, (String[]) null);
    738 
    739         mController.addActiveDeviceAdmin(ADMIN_PKG, USER_ID);
    740         assertActiveAdmins(USER_ID, ADMIN_PKG);
    741         assertActiveAdmins(USER_ID2, (String[]) null);
    742 
    743         mController.addActiveDeviceAdmin(ADMIN_PKG2, USER_ID2);
    744         assertActiveAdmins(USER_ID, ADMIN_PKG);
    745         assertActiveAdmins(USER_ID2, ADMIN_PKG2);
    746     }
    747 
    748     @Test
    749     public void testSetActiveAdminApps() {
    750         assertActiveAdmins(USER_ID, (String[]) null);
    751         assertActiveAdmins(USER_ID2, (String[]) null);
    752 
    753         setActiveAdmins(USER_ID, ADMIN_PKG, ADMIN_PKG2);
    754         assertActiveAdmins(USER_ID, ADMIN_PKG, ADMIN_PKG2);
    755         assertActiveAdmins(USER_ID2, (String[]) null);
    756 
    757         mController.addActiveDeviceAdmin(ADMIN_PKG2, USER_ID2);
    758         setActiveAdmins(USER_ID2, ADMIN_PKG);
    759         assertActiveAdmins(USER_ID, ADMIN_PKG, ADMIN_PKG2);
    760         assertActiveAdmins(USER_ID2, ADMIN_PKG);
    761 
    762         mController.setActiveAdminApps(null, USER_ID);
    763         assertActiveAdmins(USER_ID, (String[]) null);
    764     }
    765 
    766     @Test
    767     public void isActiveDeviceAdmin() {
    768         assertActiveAdmins(USER_ID, (String[]) null);
    769         assertActiveAdmins(USER_ID2, (String[]) null);
    770 
    771         mController.addActiveDeviceAdmin(ADMIN_PKG, USER_ID);
    772         assertIsActiveAdmin(ADMIN_PKG, USER_ID);
    773         assertIsNotActiveAdmin(ADMIN_PKG, USER_ID2);
    774 
    775         mController.addActiveDeviceAdmin(ADMIN_PKG2, USER_ID2);
    776         mController.addActiveDeviceAdmin(ADMIN_PKG, USER_ID2);
    777         assertIsActiveAdmin(ADMIN_PKG, USER_ID);
    778         assertIsNotActiveAdmin(ADMIN_PKG2, USER_ID);
    779         assertIsActiveAdmin(ADMIN_PKG, USER_ID2);
    780         assertIsActiveAdmin(ADMIN_PKG2, USER_ID2);
    781 
    782         setActiveAdmins(USER_ID2, ADMIN_PKG2);
    783         assertIsActiveAdmin(ADMIN_PKG2, USER_ID2);
    784         assertIsNotActiveAdmin(ADMIN_PKG, USER_ID2);
    785         assertIsActiveAdmin(ADMIN_PKG, USER_ID);
    786         assertIsNotActiveAdmin(ADMIN_PKG2, USER_ID);
    787     }
    788 
    789     private String getAdminAppsStr(int userId) {
    790         return getAdminAppsStr(userId, mController.getActiveAdminAppsForTest(userId));
    791     }
    792 
    793     private String getAdminAppsStr(int userId, Set<String> adminApps) {
    794         return "admin apps for u" + userId + ": "
    795                 + (adminApps == null ? "null" : Arrays.toString(adminApps.toArray()));
    796     }
    797 
    798     private void assertIsActiveAdmin(String adminApp, int userId) {
    799         assertTrue(adminApp + " should be an active admin; " + getAdminAppsStr(userId),
    800                 mController.isActiveDeviceAdmin(adminApp, userId));
    801     }
    802 
    803     private void assertIsNotActiveAdmin(String adminApp, int userId) {
    804         assertFalse(adminApp + " shouldn't be an active admin; " + getAdminAppsStr(userId),
    805                 mController.isActiveDeviceAdmin(adminApp, userId));
    806     }
    807 
    808     private void assertActiveAdmins(int userId, String... admins) {
    809         final Set<String> actualAdminApps = mController.getActiveAdminAppsForTest(userId);
    810         if (admins == null) {
    811             if (actualAdminApps != null && !actualAdminApps.isEmpty()) {
    812                 fail("Admin apps should be null; " + getAdminAppsStr(userId, actualAdminApps));
    813             }
    814             return;
    815         }
    816         assertEquals("No. of admin apps not equal; " + getAdminAppsStr(userId, actualAdminApps)
    817                 + "; expected=" + Arrays.toString(admins), admins.length, actualAdminApps.size());
    818         final Set<String> adminAppsCopy = new ArraySet<>(actualAdminApps);
    819         for (String admin : admins) {
    820             adminAppsCopy.remove(admin);
    821         }
    822         assertTrue("Unexpected admin apps; " + getAdminAppsStr(userId, actualAdminApps)
    823                 + "; expected=" + Arrays.toString(admins), adminAppsCopy.isEmpty());
    824     }
    825 
    826     private void setActiveAdmins(int userId, String... admins) {
    827         mController.setActiveAdminApps(new ArraySet<>(Arrays.asList(admins)), userId);
    828     }
    829 }
    830