Home | History | Annotate | Download | only in deletionhelper
      1 /*
      2  * Copyright (C) 2016 The Android Open Source Project
      3  *
      4  * Licensed under the Apache License, Version 2.0 (the "License"); you may not
      5  * use this file except in compliance with the License. You may obtain a copy of
      6  * 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, WITHOUT
     12  * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
     13  * License for the specific language governing permissions and limitations under
     14  * the License.
     15  */
     16 
     17 package com.android.storagemanager.deletionhelper;
     18 
     19 import android.app.usage.UsageStats;
     20 import android.app.usage.UsageStatsManager;
     21 import android.content.Context;
     22 import android.content.pm.ApplicationInfo;
     23 import android.content.pm.PackageInfo;
     24 import android.os.Looper;
     25 import com.android.settingslib.applications.ApplicationsState;
     26 import com.android.storagemanager.testing.TestingConstants;
     27 import com.android.storagemanager.deletionhelper.AppStateUsageStatsBridge.UsageStatsState;
     28 import org.junit.Before;
     29 import org.junit.Test;
     30 import org.junit.runner.RunWith;
     31 import org.mockito.ArgumentCaptor;
     32 import org.mockito.Mock;
     33 import org.mockito.MockitoAnnotations;
     34 import org.robolectric.RobolectricTestRunner;
     35 import org.robolectric.RuntimeEnvironment;
     36 import org.robolectric.Shadows;
     37 import org.robolectric.annotation.Config;
     38 import org.robolectric.res.builder.RobolectricPackageManager;
     39 import org.robolectric.shadows.ShadowApplication;
     40 
     41 import java.util.ArrayList;
     42 import java.util.HashMap;
     43 import java.util.concurrent.TimeUnit;
     44 
     45 import static com.google.common.truth.Truth.assertThat;
     46 import static org.mockito.Matchers.any;
     47 import static org.mockito.Matchers.anyLong;
     48 import static org.mockito.Mockito.atLeastOnce;
     49 import static org.mockito.Mockito.mock;
     50 import static org.mockito.Mockito.verify;
     51 import static org.mockito.Mockito.when;
     52 
     53 @RunWith(RobolectricTestRunner.class)
     54 @Config(manifest=TestingConstants.MANIFEST, sdk=23)
     55 public class AppStateUsageStatsBridgeTest {
     56 
     57     public static final String PACKAGE_SYSTEM = "package.system";
     58     private static final long STARTING_TIME = TimeUnit.DAYS.toMillis(1000);
     59     private static final String PACKAGE_NAME = "package.mcpackageface";
     60     public static final String PACKAGE_CLEARABLE = "package.clearable";
     61     public static final String PACKAGE_TOO_NEW_TO_DELETE = "package.tooNewToDelete";
     62 
     63     @Mock private ApplicationsState mState;
     64     @Mock private ApplicationsState.Session mSession;
     65     @Mock private UsageStatsManager mUsageStatsManager;
     66     @Mock private AppStateUsageStatsBridge.Clock mClock;
     67     private AppStateUsageStatsBridge mBridge;
     68     private ArrayList<ApplicationsState.AppEntry> mApps;
     69     private HashMap<String, UsageStats> mUsageStats;
     70     private RobolectricPackageManager mPm;
     71 
     72     @Before
     73     public void setUp() {
     74         MockitoAnnotations.initMocks(this);
     75 
     76         // Set up the application state.
     77         when(mState.newSession(any(ApplicationsState.Callbacks.class))).thenReturn(mSession);
     78         when(mState.getBackgroundLooper()).thenReturn(Looper.getMainLooper());
     79 
     80         // Set up the ApplicationState's session to return our fake list of apps.
     81         mApps = new ArrayList<>();
     82         when(mSession.getAllApps()).thenReturn(mApps);
     83 
     84         // Set up our mock usage stats service.
     85         ShadowApplication app = Shadows.shadowOf(RuntimeEnvironment.application);
     86         mPm = RuntimeEnvironment.getRobolectricPackageManager();
     87         app.setSystemService(Context.USAGE_STATS_SERVICE, mUsageStatsManager);
     88 
     89         // Set up the AppStateUsageStatsBridge with a fake clock for us to manipulate the time.
     90         when(mClock.getCurrentTime()).thenReturn(STARTING_TIME);
     91         mBridge = new AppStateUsageStatsBridge(RuntimeEnvironment.application, mState, null);
     92         mBridge.mClock = mClock;
     93         AppStateUsageStatsBridge.FILTER_USAGE_STATS.init();
     94 
     95         // Set up our fake usage stats.
     96         mUsageStats = new HashMap<>();
     97         when(mUsageStatsManager.queryAndAggregateUsageStats(anyLong(),
     98                 anyLong())).thenReturn(mUsageStats);
     99     }
    100 
    101     @Test
    102     public void test_appInstalledSameDayNeverUsed_isInvalid() {
    103         ApplicationsState.AppEntry app =
    104                 addPackageToPackageManager(PACKAGE_NAME, TimeUnit.DAYS.toMillis(1000));
    105 
    106         mBridge.updateExtraInfo(app, PACKAGE_NAME, 0);
    107         UsageStatsState stats = (UsageStatsState) app.extraInfo;
    108 
    109         assertThat(app.extraInfo).isNotNull();
    110         assertThat(stats.daysSinceFirstInstall).isEqualTo(0);
    111         assertThat(stats.daysSinceLastUse).isEqualTo(AppStateUsageStatsBridge.NEVER_USED);
    112         assertThat(AppStateUsageStatsBridge.FILTER_USAGE_STATS.filterApp(app)).isFalse();
    113     }
    114 
    115     @Test
    116     public void test_noThresholdFilter_appInstalledSameDayNeverUsed_isValid() {
    117         ApplicationsState.AppEntry app =
    118                 addPackageToPackageManager(PACKAGE_NAME, TimeUnit.DAYS.toMillis(1000));
    119 
    120         mBridge.updateExtraInfo(app, PACKAGE_NAME, 0);
    121         UsageStatsState stats = (UsageStatsState) app.extraInfo;
    122 
    123         assertThat(app.extraInfo).isNotNull();
    124         assertThat(stats.daysSinceFirstInstall).isEqualTo(0);
    125         assertThat(stats.daysSinceLastUse).isEqualTo(AppStateUsageStatsBridge.NEVER_USED);
    126         assertThat(AppStateUsageStatsBridge.FILTER_NO_THRESHOLD.filterApp(app)).isTrue();
    127     }
    128 
    129     @Test
    130     public void test_unusedApp_isValid() {
    131         ApplicationsState.AppEntry app =
    132                 addPackageToPackageManager(PACKAGE_NAME, TimeUnit.DAYS.toMillis(910));
    133 
    134         mBridge.updateExtraInfo(app, PACKAGE_NAME, 0);
    135         UsageStatsState stats = (UsageStatsState) app.extraInfo;
    136 
    137         assertThat(app.extraInfo).isNotNull();
    138         assertThat(stats.daysSinceFirstInstall).isEqualTo(90);
    139         assertThat(stats.daysSinceLastUse).isEqualTo(AppStateUsageStatsBridge.NEVER_USED);
    140         assertThat(AppStateUsageStatsBridge.FILTER_USAGE_STATS.filterApp(app)).isTrue();
    141     }
    142 
    143     @Test
    144     public void test_noThresholdFilter_unusedApp_isValid() {
    145         ApplicationsState.AppEntry app =
    146                 addPackageToPackageManager(PACKAGE_NAME, TimeUnit.DAYS.toMillis(910));
    147 
    148         mBridge.updateExtraInfo(app, PACKAGE_NAME, 0);
    149         UsageStatsState stats = (UsageStatsState) app.extraInfo;
    150 
    151         assertThat(app.extraInfo).isNotNull();
    152         assertThat(stats.daysSinceFirstInstall).isEqualTo(90);
    153         assertThat(stats.daysSinceLastUse).isEqualTo(AppStateUsageStatsBridge.NEVER_USED);
    154         assertThat(AppStateUsageStatsBridge.FILTER_NO_THRESHOLD.filterApp(app)).isTrue();
    155     }
    156 
    157     @Test
    158     public void test_unknownLastUse_isFilteredOut() {
    159         ApplicationsState.AppEntry app =
    160                 addPackageToPackageManager(PACKAGE_NAME, TimeUnit.DAYS.toMillis(910));
    161         registerLastUse(PACKAGE_NAME, -1);
    162 
    163         mBridge.updateExtraInfo(app, PACKAGE_NAME, 0);
    164         UsageStatsState stats = (UsageStatsState) app.extraInfo;
    165 
    166         assertThat(app.extraInfo).isNotNull();
    167         assertThat(stats.daysSinceFirstInstall).isEqualTo(90);
    168         assertThat(stats.daysSinceLastUse).isEqualTo(AppStateUsageStatsBridge.UNKNOWN_LAST_USE);
    169         assertThat(AppStateUsageStatsBridge.FILTER_USAGE_STATS.filterApp(app)).isFalse();
    170     }
    171 
    172     @Test
    173     public void test_noThresholdFilter_unknownLastUse_isFilteredOut() {
    174         ApplicationsState.AppEntry app =
    175                 addPackageToPackageManager(PACKAGE_NAME, TimeUnit.DAYS.toMillis(910));
    176         registerLastUse(PACKAGE_NAME, -1);
    177 
    178         mBridge.updateExtraInfo(app, PACKAGE_NAME, 0);
    179         UsageStatsState stats = (UsageStatsState) app.extraInfo;
    180 
    181         assertThat(app.extraInfo).isNotNull();
    182         assertThat(stats.daysSinceFirstInstall).isEqualTo(90);
    183         assertThat(stats.daysSinceLastUse).isEqualTo(AppStateUsageStatsBridge.UNKNOWN_LAST_USE);
    184         assertThat(AppStateUsageStatsBridge.FILTER_NO_THRESHOLD.filterApp(app)).isFalse();
    185     }
    186 
    187     @Test
    188     public void test_oldAppRecentlyUsed_isNotValid() {
    189         ApplicationsState.AppEntry app =
    190                 addPackageToPackageManager(PACKAGE_NAME, TimeUnit.DAYS.toMillis(800));
    191         registerLastUse(PACKAGE_NAME, TimeUnit.DAYS.toMillis(999));
    192 
    193         mBridge.updateExtraInfo(app, PACKAGE_NAME, 0);
    194         UsageStatsState stats = (UsageStatsState) app.extraInfo;
    195 
    196         assertThat(app.extraInfo).isNotNull();
    197         assertThat(stats.daysSinceFirstInstall).isEqualTo(200);
    198         assertThat(stats.daysSinceLastUse).isEqualTo(1);
    199         assertThat(AppStateUsageStatsBridge.FILTER_USAGE_STATS.filterApp(app)).isFalse();
    200     }
    201 
    202     @Test
    203     public void test_noThresholdFilter_oldAppRecentlyUsed_isValid() {
    204         ApplicationsState.AppEntry app =
    205                 addPackageToPackageManager(PACKAGE_NAME, TimeUnit.DAYS.toMillis(800));
    206         registerLastUse(PACKAGE_NAME, TimeUnit.DAYS.toMillis(999));
    207 
    208         mBridge.updateExtraInfo(app, PACKAGE_NAME, 0);
    209         UsageStatsState stats = (UsageStatsState) app.extraInfo;
    210 
    211         assertThat(app.extraInfo).isNotNull();
    212         assertThat(stats.daysSinceFirstInstall).isEqualTo(200);
    213         assertThat(stats.daysSinceLastUse).isEqualTo(1);
    214         assertThat(AppStateUsageStatsBridge.FILTER_NO_THRESHOLD.filterApp(app)).isTrue();
    215     }
    216 
    217     @Test
    218     public void test_oldUnusedApp_isValid() {
    219         ApplicationsState.AppEntry app =
    220                 addPackageToPackageManager(PACKAGE_NAME, TimeUnit.DAYS.toMillis(800));
    221         registerLastUse(PACKAGE_NAME, TimeUnit.DAYS.toMillis(801));
    222 
    223         mBridge.updateExtraInfo(app, PACKAGE_NAME, 0);
    224         UsageStatsState stats = (UsageStatsState) app.extraInfo;
    225 
    226         assertThat(app.extraInfo).isNotNull();
    227         assertThat(stats.daysSinceFirstInstall).isEqualTo(200);
    228         assertThat(stats.daysSinceLastUse).isEqualTo(199);
    229         assertThat(AppStateUsageStatsBridge.FILTER_USAGE_STATS.filterApp(app)).isTrue();
    230     }
    231 
    232     @Test
    233     public void test_noThresholdFilter_oldUnusedApp_isValid() {
    234         ApplicationsState.AppEntry app =
    235                 addPackageToPackageManager(PACKAGE_NAME, TimeUnit.DAYS.toMillis(800));
    236         registerLastUse(PACKAGE_NAME, TimeUnit.DAYS.toMillis(801));
    237 
    238         mBridge.updateExtraInfo(app, PACKAGE_NAME, 0);
    239         UsageStatsState stats = (UsageStatsState) app.extraInfo;
    240 
    241         assertThat(app.extraInfo).isNotNull();
    242         assertThat(stats.daysSinceFirstInstall).isEqualTo(200);
    243         assertThat(stats.daysSinceLastUse).isEqualTo(199);
    244         assertThat(AppStateUsageStatsBridge.FILTER_NO_THRESHOLD.filterApp(app)).isTrue();
    245     }
    246 
    247     @Test
    248     public void test_systemApps_areInvalid() {
    249         ApplicationsState.AppEntry app =
    250                 addPackageToPackageManager(PACKAGE_NAME, TimeUnit.DAYS.toMillis(800));
    251         registerLastUse(PACKAGE_NAME, TimeUnit.DAYS.toMillis(800));
    252         app.info.flags = ApplicationInfo.FLAG_SYSTEM;
    253 
    254         mBridge.updateExtraInfo(app, PACKAGE_NAME, 0);
    255         UsageStatsState stats = (UsageStatsState) app.extraInfo;
    256 
    257         assertThat(app.extraInfo).isNotNull();
    258         assertThat(stats.daysSinceFirstInstall).isEqualTo(200);
    259         assertThat(stats.daysSinceLastUse).isEqualTo(200);
    260         assertThat(AppStateUsageStatsBridge.FILTER_USAGE_STATS.filterApp(app)).isFalse();
    261     }
    262 
    263     @Test
    264     public void test_noThresholdFilter_systemApps_areInvalid() {
    265         ApplicationsState.AppEntry app =
    266                 addPackageToPackageManager(PACKAGE_NAME, TimeUnit.DAYS.toMillis(800));
    267         registerLastUse(PACKAGE_NAME, TimeUnit.DAYS.toMillis(800));
    268         app.info.flags = ApplicationInfo.FLAG_SYSTEM;
    269 
    270         mBridge.updateExtraInfo(app, PACKAGE_NAME, 0);
    271         UsageStatsState stats = (UsageStatsState) app.extraInfo;
    272 
    273         assertThat(app.extraInfo).isNotNull();
    274         assertThat(stats.daysSinceFirstInstall).isEqualTo(200);
    275         assertThat(stats.daysSinceLastUse).isEqualTo(200);
    276         assertThat(AppStateUsageStatsBridge.FILTER_NO_THRESHOLD.filterApp(app)).isFalse();
    277     }
    278 
    279     @Test
    280     public void test_persistentProcessApps_areInvalid() {
    281         ApplicationsState.AppEntry app =
    282                 addPackageToPackageManager(PACKAGE_NAME, TimeUnit.DAYS.toMillis(800));
    283         registerLastUse(PACKAGE_NAME, TimeUnit.DAYS.toMillis(800));
    284         app.info.flags = ApplicationInfo.FLAG_PERSISTENT;
    285 
    286         mBridge.updateExtraInfo(app, PACKAGE_NAME, 0);
    287         UsageStatsState stats = (UsageStatsState) app.extraInfo;
    288 
    289         assertThat(app.extraInfo).isNotNull();
    290         assertThat(stats.daysSinceFirstInstall).isEqualTo(200);
    291         assertThat(stats.daysSinceLastUse).isEqualTo(200);
    292         assertThat(AppStateUsageStatsBridge.FILTER_USAGE_STATS.filterApp(app)).isFalse();
    293     }
    294 
    295     @Test
    296     public void test_noThresholdFilter_persistentProcessApps_areInvalid() {
    297         ApplicationsState.AppEntry app =
    298                 addPackageToPackageManager(PACKAGE_NAME, TimeUnit.DAYS.toMillis(800));
    299         registerLastUse(PACKAGE_NAME, TimeUnit.DAYS.toMillis(800));
    300         app.info.flags = ApplicationInfo.FLAG_PERSISTENT;
    301 
    302         mBridge.updateExtraInfo(app, PACKAGE_NAME, 0);
    303         UsageStatsState stats = (UsageStatsState) app.extraInfo;
    304 
    305         assertThat(app.extraInfo).isNotNull();
    306         assertThat(stats.daysSinceFirstInstall).isEqualTo(200);
    307         assertThat(stats.daysSinceLastUse).isEqualTo(200);
    308         assertThat(AppStateUsageStatsBridge.FILTER_NO_THRESHOLD.filterApp(app)).isFalse();
    309     }
    310 
    311     @Test
    312     public void test_multipleApps_processCorrectly() {
    313         ApplicationsState.AppEntry clearable =
    314                 addPackageToPackageManager(PACKAGE_CLEARABLE, TimeUnit.DAYS.toMillis(800));
    315         registerLastUse(PACKAGE_CLEARABLE, TimeUnit.DAYS.toMillis(800));
    316         mApps.add(clearable);
    317         ApplicationsState.AppEntry tooNewtoDelete =
    318                 addPackageToPackageManager(PACKAGE_TOO_NEW_TO_DELETE, TimeUnit.DAYS.toMillis(1000));
    319         registerLastUse(PACKAGE_TOO_NEW_TO_DELETE, TimeUnit.DAYS.toMillis(1000));
    320         mApps.add(tooNewtoDelete);
    321         ApplicationsState.AppEntry systemApp =
    322                 addPackageToPackageManager(PACKAGE_SYSTEM, TimeUnit.DAYS.toMillis(800));
    323         registerLastUse(PACKAGE_SYSTEM, TimeUnit.DAYS.toMillis(800));
    324         systemApp.info.flags = ApplicationInfo.FLAG_SYSTEM;
    325         mApps.add(systemApp);
    326         ApplicationsState.AppEntry persistentApp =
    327                 addPackageToPackageManager(PACKAGE_NAME, TimeUnit.DAYS.toMillis(800));
    328         registerLastUse(PACKAGE_NAME, TimeUnit.DAYS.toMillis(800));
    329         persistentApp.info.flags = ApplicationInfo.FLAG_PERSISTENT;
    330         mApps.add(persistentApp);
    331 
    332         mBridge.loadAllExtraInfo();
    333 
    334         assertThat(AppStateUsageStatsBridge.FILTER_USAGE_STATS.filterApp(clearable)).isTrue();
    335         assertThat(AppStateUsageStatsBridge.FILTER_USAGE_STATS.filterApp(tooNewtoDelete)).isFalse();
    336         assertThat(AppStateUsageStatsBridge.FILTER_USAGE_STATS.filterApp(systemApp)).isFalse();
    337         assertThat(AppStateUsageStatsBridge.FILTER_USAGE_STATS.filterApp(persistentApp)).isFalse();
    338     }
    339 
    340     @Test
    341     public void test_noThresholdFilter_ignoresUsageForFiltering() {
    342         ApplicationsState.AppEntry clearable =
    343                 addPackageToPackageManager(PACKAGE_CLEARABLE, TimeUnit.DAYS.toMillis(800));
    344         registerLastUse(PACKAGE_CLEARABLE, TimeUnit.DAYS.toMillis(800));
    345         mApps.add(clearable);
    346         ApplicationsState.AppEntry tooNewtoDelete =
    347                 addPackageToPackageManager(PACKAGE_TOO_NEW_TO_DELETE, TimeUnit.DAYS.toMillis(1000));
    348         registerLastUse(PACKAGE_TOO_NEW_TO_DELETE, TimeUnit.DAYS.toMillis(1000));
    349         mApps.add(tooNewtoDelete);
    350         ApplicationsState.AppEntry systemApp =
    351                 addPackageToPackageManager(PACKAGE_SYSTEM, TimeUnit.DAYS.toMillis(800));
    352         registerLastUse(PACKAGE_SYSTEM, TimeUnit.DAYS.toMillis(800));
    353         systemApp.info.flags = ApplicationInfo.FLAG_SYSTEM;
    354         mApps.add(systemApp);
    355         ApplicationsState.AppEntry persistentApp =
    356                 addPackageToPackageManager(PACKAGE_NAME, TimeUnit.DAYS.toMillis(800));
    357         registerLastUse(PACKAGE_NAME, TimeUnit.DAYS.toMillis(800));
    358         persistentApp.info.flags = ApplicationInfo.FLAG_PERSISTENT;
    359         mApps.add(persistentApp);
    360 
    361         mBridge.loadAllExtraInfo();
    362 
    363         assertThat(AppStateUsageStatsBridge.FILTER_NO_THRESHOLD.filterApp(clearable)).isTrue();
    364         assertThat(AppStateUsageStatsBridge.FILTER_NO_THRESHOLD.filterApp(tooNewtoDelete)).isTrue();
    365         assertThat(AppStateUsageStatsBridge.FILTER_NO_THRESHOLD.filterApp(systemApp)).isFalse();
    366         assertThat(AppStateUsageStatsBridge.FILTER_NO_THRESHOLD.filterApp(persistentApp)).isFalse();
    367     }
    368 
    369     @Test
    370     public void testAppUsedOverOneYearAgoIsValid() {
    371         ApplicationsState.AppEntry app =
    372                 addPackageToPackageManager(PACKAGE_NAME, TimeUnit.DAYS.toMillis(600));
    373         registerLastUse(PACKAGE_NAME, TimeUnit.DAYS.toMillis(1000 - 366));
    374 
    375         mBridge.updateExtraInfo(app, PACKAGE_NAME, 0);
    376         UsageStatsState stats = (UsageStatsState) app.extraInfo;
    377 
    378         assertThat(app.extraInfo).isNotNull();
    379         assertThat(stats.daysSinceFirstInstall).isEqualTo(400);
    380         assertThat(stats.daysSinceLastUse).isEqualTo(AppStateUsageStatsBridge.NEVER_USED);
    381         assertThat(AppStateUsageStatsBridge.FILTER_USAGE_STATS.filterApp(app)).isTrue();
    382     }
    383 
    384     @Test
    385     public void testStartEndIsBeforeEndTimeInQuery() {
    386         ApplicationsState.AppEntry app =
    387                 addPackageToPackageManager(PACKAGE_NAME, TimeUnit.DAYS.toMillis(600));
    388         registerLastUse(PACKAGE_NAME, TimeUnit.DAYS.toMillis(1000 - 366));
    389         ArgumentCaptor<Long> startTimeCaptor = ArgumentCaptor.forClass(Long.class);
    390         ArgumentCaptor<Long> endTimeCaptor = ArgumentCaptor.forClass(Long.class);
    391 
    392         mBridge.updateExtraInfo(app, PACKAGE_NAME, 0);
    393 
    394         verify(mUsageStatsManager, atLeastOnce())
    395                 .queryAndAggregateUsageStats(startTimeCaptor.capture(), endTimeCaptor.capture());
    396         assertThat(startTimeCaptor.getValue()).isLessThan(endTimeCaptor.getValue());
    397     }
    398 
    399     private ApplicationsState.AppEntry addPackageToPackageManager(String packageName,
    400             long installTime) {
    401         PackageInfo testPackage = new PackageInfo();
    402         testPackage.packageName = packageName;
    403         testPackage.firstInstallTime = installTime;
    404         mPm.addPackage(testPackage);
    405         ApplicationsState.AppEntry app = mock(ApplicationsState.AppEntry.class);
    406         ApplicationInfo info = mock(ApplicationInfo.class);
    407         info.packageName = packageName;
    408         app.info = info;
    409         return app;
    410     }
    411 
    412     private void registerLastUse(String packageName, long time) {
    413         UsageStats usageStats = mock(UsageStats.class);
    414         when(usageStats.getPackageName()).thenReturn(packageName);
    415         when(usageStats.getLastTimeUsed()).thenReturn(time);
    416         mUsageStats.put(packageName, usageStats);
    417     }
    418 
    419     private class FakeClock extends AppStateUsageStatsBridge.Clock {
    420         public long time;
    421 
    422         @Override
    423         public long getCurrentTime() {
    424             return time;
    425         }
    426     }
    427 }
    428