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 17package com.android.storagemanager.deletionhelper; 18 19import android.app.usage.UsageStats; 20import android.app.usage.UsageStatsManager; 21import android.content.Context; 22import android.content.pm.ApplicationInfo; 23import android.content.pm.PackageInfo; 24import android.os.Looper; 25import com.android.settingslib.applications.ApplicationsState; 26import com.android.storagemanager.testing.TestingConstants; 27import com.android.storagemanager.deletionhelper.AppStateUsageStatsBridge.UsageStatsState; 28import org.junit.Before; 29import org.junit.Test; 30import org.junit.runner.RunWith; 31import org.mockito.ArgumentCaptor; 32import org.mockito.Mock; 33import org.mockito.MockitoAnnotations; 34import org.robolectric.RobolectricTestRunner; 35import org.robolectric.RuntimeEnvironment; 36import org.robolectric.Shadows; 37import org.robolectric.annotation.Config; 38import org.robolectric.res.builder.RobolectricPackageManager; 39import org.robolectric.shadows.ShadowApplication; 40 41import java.util.ArrayList; 42import java.util.HashMap; 43import java.util.concurrent.TimeUnit; 44 45import static com.google.common.truth.Truth.assertThat; 46import static org.mockito.Matchers.any; 47import static org.mockito.Matchers.anyLong; 48import static org.mockito.Mockito.atLeastOnce; 49import static org.mockito.Mockito.mock; 50import static org.mockito.Mockito.verify; 51import static org.mockito.Mockito.when; 52 53@RunWith(RobolectricTestRunner.class) 54@Config(manifest=TestingConstants.MANIFEST, sdk=23) 55public 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