1 /** 2 * Copyright (C) 2014 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 6 * 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, WITHOUT 12 * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the 13 * License for the specific language governing permissions and limitations 14 * under the License. 15 */ 16 17 package android.app.usage.cts; 18 19 import static android.Manifest.permission.POST_NOTIFICATIONS; 20 import static android.Manifest.permission.REVOKE_POST_NOTIFICATIONS_WITHOUT_KILL; 21 import static android.Manifest.permission.REVOKE_RUNTIME_PERMISSIONS; 22 import static android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN; 23 import static android.app.WindowConfiguration.WINDOWING_MODE_UNDEFINED; 24 import static android.app.usage.UsageStatsManager.STANDBY_BUCKET_FREQUENT; 25 import static android.app.usage.UsageStatsManager.STANDBY_BUCKET_NEVER; 26 import static android.app.usage.UsageStatsManager.STANDBY_BUCKET_RARE; 27 import static android.app.usage.UsageStatsManager.STANDBY_BUCKET_WORKING_SET; 28 import static android.provider.DeviceConfig.NAMESPACE_APP_STANDBY; 29 30 import static org.junit.Assert.assertEquals; 31 import static org.junit.Assert.assertFalse; 32 import static org.junit.Assert.assertNotEquals; 33 import static org.junit.Assert.assertNotNull; 34 import static org.junit.Assert.assertTrue; 35 import static org.junit.Assert.fail; 36 import static org.junit.Assume.assumeFalse; 37 import static org.junit.Assume.assumeTrue; 38 39 import android.Manifest; 40 import android.app.Activity; 41 import android.app.ActivityManager; 42 import android.app.ActivityOptions; 43 import android.app.AppOpsManager; 44 import android.app.KeyguardManager; 45 import android.app.Notification; 46 import android.app.NotificationChannel; 47 import android.app.NotificationManager; 48 import android.app.PendingIntent; 49 import android.app.usage.EventStats; 50 import android.app.usage.UsageEvents; 51 import android.app.usage.UsageEvents.Event; 52 import android.app.usage.UsageStats; 53 import android.app.usage.UsageStatsManager; 54 import android.content.BroadcastReceiver; 55 import android.content.ComponentName; 56 import android.content.ContentProviderClient; 57 import android.content.Context; 58 import android.content.Intent; 59 import android.content.ServiceConnection; 60 import android.content.pm.PackageManager; 61 import android.database.Cursor; 62 import android.net.Uri; 63 import android.os.IBinder; 64 import android.os.Parcel; 65 import android.os.Process; 66 import android.os.SystemClock; 67 import android.os.UserHandle; 68 import android.os.UserManager; 69 import android.permission.PermissionManager; 70 import android.permission.cts.PermissionUtils; 71 import android.platform.test.annotations.AppModeFull; 72 import android.platform.test.annotations.AppModeInstant; 73 import android.platform.test.annotations.AsbSecurityTest; 74 import android.provider.Settings; 75 import android.server.wm.WindowManagerState; 76 import android.server.wm.WindowManagerStateHelper; 77 import android.text.format.DateUtils; 78 import android.util.ArrayMap; 79 import android.util.Log; 80 import android.util.SparseArray; 81 import android.util.SparseLongArray; 82 import android.view.KeyEvent; 83 84 import androidx.test.InstrumentationRegistry; 85 import androidx.test.filters.MediumTest; 86 import androidx.test.uiautomator.By; 87 import androidx.test.uiautomator.UiDevice; 88 import androidx.test.uiautomator.Until; 89 90 import com.android.compatibility.common.util.AppStandbyUtils; 91 import com.android.compatibility.common.util.BatteryUtils; 92 import com.android.compatibility.common.util.DeviceConfigStateHelper; 93 import com.android.compatibility.common.util.PollingCheck; 94 import com.android.compatibility.common.util.SystemUtil; 95 import com.android.sts.common.util.StsExtraBusinessLogicTestCase; 96 97 import org.junit.After; 98 import org.junit.Before; 99 import org.junit.Ignore; 100 import org.junit.Test; 101 import org.junit.runner.RunWith; 102 103 import java.io.IOException; 104 import java.text.MessageFormat; 105 import java.time.Duration; 106 import java.util.ArrayList; 107 import java.util.Arrays; 108 import java.util.List; 109 import java.util.Map; 110 import java.util.Objects; 111 import java.util.concurrent.BlockingQueue; 112 import java.util.concurrent.CountDownLatch; 113 import java.util.concurrent.LinkedBlockingQueue; 114 import java.util.concurrent.TimeUnit; 115 import java.util.function.Function; 116 import java.util.function.Supplier; 117 118 /** 119 * Test the UsageStats API. It is difficult to test the entire surface area 120 * of the API, as a lot of the testing depends on what data is already present 121 * on the device and for how long that data has been aggregating. 122 * 123 * These tests perform simple checks that each interval is of the correct duration, 124 * and that events do appear in the event log. 125 * 126 * Tests to add that are difficult to add now: 127 * - Invoking a device configuration change and then watching for it in the event log. 128 * - Changing the system time and verifying that all data has been correctly shifted 129 * along with the new time. 130 * - Proper eviction of old data. 131 */ 132 @RunWith(UsageStatsTestRunner.class) 133 public class UsageStatsTest extends StsExtraBusinessLogicTestCase { 134 private static final boolean DEBUG = false; 135 static final String TAG = "UsageStatsTest"; 136 137 private static final String APPOPS_SET_SHELL_COMMAND = "appops set {0} " + 138 AppOpsManager.OPSTR_GET_USAGE_STATS + " {1}"; 139 private static final String APPOPS_RESET_SHELL_COMMAND = "appops reset {0}"; 140 141 private static final String PRUNE_PACKAGE_DATA_SHELL_COMMAND = 142 "cmd usagestats delete-package-data {0} -u {1}"; 143 144 private static final String GET_SHELL_COMMAND = "settings get global "; 145 146 private static final String SET_SHELL_COMMAND = "settings put global "; 147 148 private static final String DELETE_SHELL_COMMAND = "settings delete global "; 149 150 private static final String JOBSCHEDULER_RUN_SHELL_COMMAND = "cmd jobscheduler run"; 151 152 static final String TEST_APP_PKG = "android.app.usage.cts.test1"; 153 154 static final String TEST_APP_CLASS = "android.app.usage.cts.test1.SomeActivity"; 155 private static final String TEST_APP_CLASS_LOCUS 156 = "android.app.usage.cts.test1.SomeActivityWithLocus"; 157 static final String TEST_APP_CLASS_SERVICE 158 = "android.app.usage.cts.test1.TestService"; 159 static final String TEST_APP_CLASS_BROADCAST_RECEIVER 160 = "android.app.usage.cts.test1.TestBroadcastReceiver"; 161 private static final String TEST_APP_CLASS_FINISH_SELF_ON_RESUME = 162 "android.app.usage.cts.test1.FinishOnResumeActivity"; 163 private static final String TEST_AUTHORITY = "android.app.usage.cts.test1.provider"; 164 private static final String TEST_APP_CONTENT_URI_STRING = "content://" + TEST_AUTHORITY; 165 private static final String TEST_APP2_PKG = "android.app.usage.cts.test2"; 166 private static final String TEST_APP2_CLASS_FINISHING_TASK_ROOT = 167 "android.app.usage.cts.test2.FinishingTaskRootActivity"; 168 private static final String TEST_APP2_CLASS_PIP = 169 "android.app.usage.cts.test2.PipActivity"; 170 private static final ComponentName TEST_APP2_PIP_COMPONENT = new ComponentName(TEST_APP2_PKG, 171 TEST_APP2_CLASS_PIP); 172 173 private static final String TEST_APP_API_32_PKG = "android.app.usage.cts.testapi32"; 174 175 // TODO(206518483): Define these constants in UsageStatsManager to avoid hardcoding here. 176 private static final String KEY_NOTIFICATION_SEEN_HOLD_DURATION = 177 "notification_seen_duration"; 178 private static final String KEY_NOTIFICATION_SEEN_PROMOTED_BUCKET = 179 "notification_seen_promoted_bucket"; 180 private static final String KEY_RETAIN_NOTIFICATION_SEEN_IMPACT_FOR_PRE_T_APPS = 181 "retain_notification_seen_impact_for_pre_t_apps"; 182 183 private static final int DEFAULT_TIMEOUT_MS = 10_000; 184 185 private static final long TIMEOUT = TimeUnit.SECONDS.toMillis(5); 186 private static final long MINUTE = TimeUnit.MINUTES.toMillis(1); 187 private static final long DAY = TimeUnit.DAYS.toMillis(1); 188 private static final long WEEK = 7 * DAY; 189 private static final long MONTH = 30 * DAY; 190 private static final long YEAR = 365 * DAY; 191 private static final long TIME_DIFF_THRESHOLD = 200; 192 private static final String CHANNEL_ID = "my_channel"; 193 194 private static final long TIMEOUT_BINDER_SERVICE_SEC = 2; 195 196 private static final String TEST_NOTIFICATION_CHANNEL_ID = "test-channel-id"; 197 private static final String TEST_NOTIFICATION_CHANNEL_NAME = "test-channel-name"; 198 private static final String TEST_NOTIFICATION_CHANNEL_DESC = "test-channel-description"; 199 200 private static final int TEST_NOTIFICATION_ID_1 = 10; 201 private static final int TEST_NOTIFICATION_ID_2 = 20; 202 private static final String TEST_NOTIFICATION_TITLE_FMT = "Test title; id=%s"; 203 private static final String TEST_NOTIFICATION_TEXT_1 = "Test content 1"; 204 private static final String TEST_NOTIFICATION_TEXT_2 = "Test content 2"; 205 206 private Context mContext; 207 private UiDevice mUiDevice; 208 private ActivityManager mAm; 209 private UsageStatsManager mUsageStatsManager; 210 private KeyguardManager mKeyguardManager; 211 private String mTargetPackage; 212 private String mCachedUsageSourceSetting; 213 private int mOtherUser; 214 private Context mOtherUserContext; 215 private UsageStatsManager mOtherUsageStats; 216 private WindowManagerStateHelper mWMStateHelper; 217 218 @Before setUp()219 public void setUp() throws Exception { 220 mContext = InstrumentationRegistry.getInstrumentation().getContext(); 221 mUiDevice = UiDevice.getInstance(InstrumentationRegistry.getInstrumentation()); 222 mAm = mContext.getSystemService(ActivityManager.class); 223 mUsageStatsManager = (UsageStatsManager) mContext.getSystemService( 224 Context.USAGE_STATS_SERVICE); 225 mKeyguardManager = mContext.getSystemService(KeyguardManager.class); 226 mTargetPackage = mContext.getPackageName(); 227 PermissionUtils.grantPermission(mTargetPackage, POST_NOTIFICATIONS); 228 229 mWMStateHelper = new WindowManagerStateHelper(); 230 231 assumeTrue("App Standby not enabled on device", AppStandbyUtils.isAppStandbyEnabled()); 232 setAppOpsMode("allow"); 233 mCachedUsageSourceSetting = getSetting(Settings.Global.APP_TIME_LIMIT_USAGE_SOURCE); 234 } 235 236 @After cleanUp()237 public void cleanUp() throws Exception { 238 if (mCachedUsageSourceSetting != null && 239 !mCachedUsageSourceSetting.equals( 240 getSetting(Settings.Global.APP_TIME_LIMIT_USAGE_SOURCE))) { 241 setUsageSourceSetting(mCachedUsageSourceSetting); 242 } 243 // Force stop test package to avoid any running test code from carrying over to the next run 244 if (mAm != null) { 245 SystemUtil.runWithShellPermissionIdentity(() -> mAm.forceStopPackage(TEST_APP_PKG)); 246 SystemUtil.runWithShellPermissionIdentity(() -> mAm.forceStopPackage(TEST_APP2_PKG)); 247 } 248 249 if (mUiDevice != null) { 250 mUiDevice.pressHome(); 251 } 252 253 // delete any usagestats data that was created for the test packages 254 clearTestPackagesData(mContext.getUserId()); 255 256 // Destroy the other user if created 257 if (mOtherUser != 0) { 258 clearTestPackagesData(mOtherUser); 259 stopUser(mOtherUser, true, true); 260 removeUser(mOtherUser); 261 mOtherUser = 0; 262 } 263 // Use test API to prevent PermissionManager from killing the test process when revoking 264 // permission. 265 if (mContext != null && mTargetPackage != null) { 266 SystemUtil.runWithShellPermissionIdentity( 267 () -> mContext.getSystemService(PermissionManager.class) 268 .revokePostNotificationPermissionWithoutKillForTest( 269 mTargetPackage, 270 Process.myUserHandle().getIdentifier()), 271 REVOKE_POST_NOTIFICATIONS_WITHOUT_KILL, 272 REVOKE_RUNTIME_PERMISSIONS); 273 } 274 } 275 assertLessThan(long left, long right)276 private static void assertLessThan(long left, long right) { 277 assertTrue("Expected " + left + " to be less than " + right, left < right); 278 } 279 assertLessThanOrEqual(long left, long right)280 private static void assertLessThanOrEqual(long left, long right) { 281 assertTrue("Expected " + left + " to be less than " + right, left <= right); 282 } 283 setAppOpsMode(String mode)284 private void setAppOpsMode(String mode) throws Exception { 285 executeShellCmd(MessageFormat.format(APPOPS_SET_SHELL_COMMAND, mTargetPackage, mode)); 286 } 287 resetAppOpsMode()288 private void resetAppOpsMode() throws Exception { 289 executeShellCmd(MessageFormat.format(APPOPS_RESET_SHELL_COMMAND, mTargetPackage)); 290 } 291 clearTestPackagesData(int userId)292 private void clearTestPackagesData(int userId) throws Exception { 293 executeShellCmd(MessageFormat.format(PRUNE_PACKAGE_DATA_SHELL_COMMAND, mTargetPackage, 294 userId)); 295 executeShellCmd(MessageFormat.format(PRUNE_PACKAGE_DATA_SHELL_COMMAND, TEST_APP_PKG, 296 userId)); 297 executeShellCmd(MessageFormat.format(PRUNE_PACKAGE_DATA_SHELL_COMMAND, TEST_APP2_PKG, 298 userId)); 299 executeShellCmd(MessageFormat.format(PRUNE_PACKAGE_DATA_SHELL_COMMAND, TEST_APP_API_32_PKG, 300 userId)); 301 } 302 getSetting(String name)303 private String getSetting(String name) throws Exception { 304 return executeShellCmd(GET_SHELL_COMMAND + name); 305 } 306 setSetting(String name, String setting)307 private void setSetting(String name, String setting) throws Exception { 308 if (setting == null || setting.equals("null")) { 309 executeShellCmd(DELETE_SHELL_COMMAND + name); 310 } else { 311 executeShellCmd(SET_SHELL_COMMAND + name + " " + setting); 312 } 313 } 314 setUsageSourceSetting(String value)315 private void setUsageSourceSetting(String value) throws Exception { 316 setSetting(Settings.Global.APP_TIME_LIMIT_USAGE_SOURCE, value); 317 mUsageStatsManager.forceUsageSourceSettingRead(); 318 } 319 launchSubActivity(Class<? extends Activity> clazz)320 private void launchSubActivity(Class<? extends Activity> clazz) { 321 launchSubActivity(clazz, WINDOWING_MODE_UNDEFINED); 322 } 323 launchSubActivity(Class<? extends Activity> clazz, int windowingMode)324 private void launchSubActivity(Class<? extends Activity> clazz, int windowingMode) { 325 final Intent intent = new Intent(Intent.ACTION_MAIN); 326 intent.setClassName(mTargetPackage, clazz.getName()); 327 intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TASK | Intent.FLAG_ACTIVITY_NEW_TASK); 328 final ActivityOptions options = ActivityOptions.makeBasic(); 329 options.setLaunchWindowingMode(windowingMode); 330 mContext.startActivity(intent, options.toBundle()); 331 mUiDevice.wait(Until.hasObject(By.clazz(clazz)), TIMEOUT); 332 } 333 createTestActivityIntent(String pkgName, String className)334 private Intent createTestActivityIntent(String pkgName, String className) { 335 final Intent intent = new Intent(); 336 intent.setClassName(pkgName, className); 337 intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK); 338 return intent; 339 } 340 launchTestActivity(String pkgName, String className)341 private void launchTestActivity(String pkgName, String className) { 342 launchTestActivity(pkgName, className, WINDOWING_MODE_UNDEFINED); 343 } 344 launchTestActivity(String pkgName, String className, int windowingMode)345 private void launchTestActivity(String pkgName, String className, int windowingMode) { 346 final ActivityOptions options = ActivityOptions.makeBasic(); 347 options.setLaunchWindowingMode(windowingMode); 348 mContext.startActivity(createTestActivityIntent(pkgName, className), options.toBundle()); 349 mUiDevice.wait(Until.hasObject(By.clazz(pkgName, className)), TIMEOUT); 350 } 351 launchSubActivities(Class<? extends Activity>[] activityClasses)352 private void launchSubActivities(Class<? extends Activity>[] activityClasses) { 353 for (Class<? extends Activity> clazz : activityClasses) { 354 launchSubActivity(clazz); 355 } 356 } 357 358 @Test testTogglingViaSettings()359 public void testTogglingViaSettings() throws Exception { 360 final String initialAppStandbyEnabled = getSetting(Settings.Global.APP_STANDBY_ENABLED); 361 final String initialAdaptiveBatteryManagementEnabled = 362 getSetting(Settings.Global.ADAPTIVE_BATTERY_MANAGEMENT_ENABLED); 363 try { 364 // Right now, this test only runs when we've already confirmed that app standby is 365 // enabled via the command-line. 366 assertTrue(mUsageStatsManager.isAppStandbyEnabled()); 367 368 setSetting(Settings.Global.APP_STANDBY_ENABLED, "0"); 369 // Need to wait a bit for the setting change to propagate 370 waitUntil(() -> mUsageStatsManager.isAppStandbyEnabled(), false); 371 assertFalse(AppStandbyUtils.isAppStandbyEnabled()); 372 373 setSetting(Settings.Global.APP_STANDBY_ENABLED, "1"); 374 setSetting(Settings.Global.ADAPTIVE_BATTERY_MANAGEMENT_ENABLED, "0"); 375 waitUntil(() -> mUsageStatsManager.isAppStandbyEnabled(), false); 376 assertFalse(AppStandbyUtils.isAppStandbyEnabled()); 377 378 setSetting(Settings.Global.ADAPTIVE_BATTERY_MANAGEMENT_ENABLED, "1"); 379 waitUntil(() -> mUsageStatsManager.isAppStandbyEnabled(), true); 380 assertTrue(AppStandbyUtils.isAppStandbyEnabled()); 381 } finally { 382 setSetting(Settings.Global.APP_STANDBY_ENABLED, initialAppStandbyEnabled); 383 setSetting(Settings.Global.ADAPTIVE_BATTERY_MANAGEMENT_ENABLED, 384 initialAdaptiveBatteryManagementEnabled); 385 } 386 } 387 388 @AppModeFull(reason = "No usage events access in instant apps") 389 @Test testLastTimeVisible_launchActivityShouldBeDetected()390 public void testLastTimeVisible_launchActivityShouldBeDetected() throws Exception { 391 wakeDevice(); 392 dismissKeyguard(); // also want to start out with the keyguard dismissed. 393 394 final long startTime = System.currentTimeMillis(); 395 launchSubActivity(Activities.ActivityOne.class); 396 final long endTime = System.currentTimeMillis(); 397 398 verifyLastTimeVisibleWithinRange(startTime, endTime, mTargetPackage); 399 } 400 401 @AppModeFull(reason = "No usage events access in instant apps") 402 @Test testLastTimeAnyComponentUsed_launchActivityShouldBeDetected()403 public void testLastTimeAnyComponentUsed_launchActivityShouldBeDetected() throws Exception { 404 wakeDevice(); 405 dismissKeyguard(); // also want to start out with the keyguard dismissed. 406 407 final long startTime = System.currentTimeMillis(); 408 launchSubActivity(Activities.ActivityOne.class); 409 final long endTime = System.currentTimeMillis(); 410 411 verifyLastTimeAnyComponentUsedWithinRange(startTime, endTime, mTargetPackage); 412 } 413 414 @AppModeFull(reason = "No usage events access in instant apps") 415 @Test testLastTimeAnyComponentUsed_bindServiceShouldBeDetected()416 public void testLastTimeAnyComponentUsed_bindServiceShouldBeDetected() throws Exception { 417 wakeDevice(); 418 dismissKeyguard(); // also want to start out with the keyguard dismissed. 419 420 final long startTime = System.currentTimeMillis(); 421 bindToTestService(); 422 final long endTime = System.currentTimeMillis(); 423 424 verifyLastTimeAnyComponentUsedWithinRange(startTime, endTime, TEST_APP_PKG); 425 } 426 427 @AppModeFull(reason = "No usage events access in instant apps") 428 @Test testLastTimeAnyComponentUsed_bindExplicitBroadcastReceiverShouldBeDetected()429 public void testLastTimeAnyComponentUsed_bindExplicitBroadcastReceiverShouldBeDetected() 430 throws Exception { 431 wakeDevice(); 432 dismissKeyguard(); // also want to start out with the keyguard dismissed. 433 434 final long startTime = System.currentTimeMillis(); 435 bindToTestBroadcastReceiver(); 436 final long endTime = System.currentTimeMillis(); 437 438 verifyLastTimeAnyComponentUsedWithinRange(startTime, endTime, TEST_APP_PKG); 439 } 440 441 @AppModeFull(reason = "No usage events access in instant apps") 442 @Test testLastTimeAnyComponentUsed_bindContentProviderShouldBeDetected()443 public void testLastTimeAnyComponentUsed_bindContentProviderShouldBeDetected() 444 throws Exception { 445 wakeDevice(); 446 dismissKeyguard(); // also want to start out with the keyguard dismissed. 447 448 final long startTime = System.currentTimeMillis(); 449 bindToTestContentProvider(); 450 final long endTime = System.currentTimeMillis(); 451 452 verifyLastTimeAnyComponentUsedWithinRange(startTime, endTime, TEST_APP_PKG); 453 } 454 verifyLastTimeVisibleWithinRange( long startTime, long endTime, String targetPackage)455 private void verifyLastTimeVisibleWithinRange( 456 long startTime, long endTime, String targetPackage) { 457 UsageStats stats = getAggregateUsageStats(startTime, endTime, targetPackage); 458 assertNotNull(stats); 459 long lastTimeVisible = stats.getLastTimeVisible(); 460 if (lastTimeVisible < startTime) { 461 // There is a slight possibility that the returned stats do not include the latest data, 462 // so query usage stats again after a 1s wait for the most recent data 463 SystemClock.sleep(1000); 464 stats = getAggregateUsageStats(startTime, endTime, targetPackage); 465 assertNotNull(stats); 466 lastTimeVisible = stats.getLastTimeVisible(); 467 } 468 assertLessThanOrEqual(startTime, lastTimeVisible); 469 assertLessThanOrEqual(lastTimeVisible, endTime); 470 } 471 verifyLastTimeAnyComponentUsedWithinRange( long startTime, long endTime, String targetPackage)472 private void verifyLastTimeAnyComponentUsedWithinRange( 473 long startTime, long endTime, String targetPackage) { 474 475 UsageStats stats = getAggregateUsageStats(startTime, endTime, targetPackage); 476 assertNotNull(stats); 477 long lastTimeAnyComponentUsed = stats.getLastTimeAnyComponentUsed(); 478 if (lastTimeAnyComponentUsed < startTime) { 479 // There is a slight possibility that the returned stats do not include the latest data, 480 // so query usage stats again after a 1s wait for the most recent data 481 SystemClock.sleep(1000); 482 stats = getAggregateUsageStats(startTime, endTime, targetPackage); 483 assertNotNull(stats); 484 lastTimeAnyComponentUsed = stats.getLastTimeAnyComponentUsed(); 485 } 486 assertLessThanOrEqual(startTime, lastTimeAnyComponentUsed); 487 assertLessThanOrEqual(lastTimeAnyComponentUsed, endTime); 488 489 SystemUtil.runWithShellPermissionIdentity(()-> { 490 final long lastDayAnyComponentUsedGlobal = 491 mUsageStatsManager.getLastTimeAnyComponentUsed(targetPackage) / DAY; 492 assertLessThanOrEqual(startTime / DAY, lastDayAnyComponentUsedGlobal); 493 assertLessThanOrEqual(lastDayAnyComponentUsedGlobal, endTime / DAY); 494 }); 495 } 496 getAggregateUsageStats(long startTime, long endTime, String targetPackage)497 private UsageStats getAggregateUsageStats(long startTime, long endTime, String targetPackage) { 498 UsageStats stats; 499 // Query for up to 5 seconds in case the handler is busy. 500 for (int i = 0; i < 10; i++) { 501 final Map<String, UsageStats> map = mUsageStatsManager.queryAndAggregateUsageStats( 502 startTime, endTime + 1000); 503 stats = map.get(targetPackage); 504 if (stats != null) { 505 return stats; 506 } 507 SystemClock.sleep(500); 508 } 509 return null; 510 } 511 512 @AppModeFull(reason = "No usage events access in instant apps") 513 @Test testLastTimeAnyComponentUsed_JobServiceShouldBeIgnored()514 public void testLastTimeAnyComponentUsed_JobServiceShouldBeIgnored() throws Exception { 515 wakeDevice(); 516 dismissKeyguard(); // also want to start out with the keyguard dismissed. 517 518 final long startTime = System.currentTimeMillis(); 519 runJobImmediately(); 520 waitUntil(TestJob.hasJobStarted, /* expected */ true); 521 522 final Map<String, UsageStats> map = mUsageStatsManager.queryAndAggregateUsageStats( 523 startTime, System.currentTimeMillis()); 524 final UsageStats stats = map.get(mTargetPackage); 525 if (stats != null) { 526 final long lastTimeAnyComponentUsed = stats.getLastTimeAnyComponentUsed(); 527 // Check that the usage is NOT detected. 528 assertLessThanOrEqual(lastTimeAnyComponentUsed, startTime); 529 } 530 531 SystemUtil.runWithShellPermissionIdentity(()-> { 532 final long lastDayAnyComponentUsedGlobal = 533 mUsageStatsManager.getLastTimeAnyComponentUsed(mTargetPackage) / DAY; 534 // Check that the usage is NOT detected. 535 assertLessThanOrEqual(lastDayAnyComponentUsedGlobal, startTime / DAY); 536 }); 537 } 538 539 @AppModeFull(reason = "No usage events access in instant apps") 540 @Test testLastTimeAnyComponentUsedGlobal_withoutPermission()541 public void testLastTimeAnyComponentUsedGlobal_withoutPermission() throws Exception { 542 try{ 543 mUsageStatsManager.getLastTimeAnyComponentUsed(mTargetPackage); 544 fail("Query across users should require INTERACT_ACROSS_USERS permission"); 545 } catch (SecurityException se) { 546 // Expected 547 } 548 } 549 550 @AppModeFull(reason = "No usage events access in instant apps") 551 @Test testOrderedActivityLaunchSequenceInEventLog()552 public void testOrderedActivityLaunchSequenceInEventLog() throws Exception { 553 @SuppressWarnings("unchecked") 554 Class<? extends Activity>[] activitySequence = new Class[] { 555 Activities.ActivityOne.class, 556 Activities.ActivityTwo.class, 557 Activities.ActivityThree.class, 558 }; 559 wakeDevice(); 560 dismissKeyguard(); // also want to start out with the keyguard dismissed. 561 562 final long startTime = System.currentTimeMillis(); 563 // Launch the series of Activities. 564 launchSubActivities(activitySequence); 565 final long endTime = System.currentTimeMillis(); 566 UsageEvents events = mUsageStatsManager.queryEvents(startTime, endTime); 567 568 // Only look at events belongs to mTargetPackage. 569 ArrayList<UsageEvents.Event> eventList = new ArrayList<>(); 570 while (events.hasNextEvent()) { 571 UsageEvents.Event event = new UsageEvents.Event(); 572 assertTrue(events.getNextEvent(event)); 573 if (mTargetPackage.equals(event.getPackageName())) { 574 eventList.add(event); 575 } 576 } 577 578 final int activityCount = activitySequence.length; 579 for (int i = 0; i < activityCount; i++) { 580 String className = activitySequence[i].getName(); 581 ArrayList<UsageEvents.Event> activityEvents = new ArrayList<>(); 582 final int size = eventList.size(); 583 for (int j = 0; j < size; j++) { 584 Event evt = eventList.get(j); 585 if (className.equals(evt.getClassName())) { 586 activityEvents.add(evt); 587 } 588 } 589 // We expect 3 events per Activity launched (ACTIVITY_RESUMED + ACTIVITY_PAUSED 590 // + ACTIVITY_STOPPED) except for the last Activity, which only has 591 // ACTIVITY_RESUMED event. 592 if (i < activityCount - 1) { 593 assertEquals(3, activityEvents.size()); 594 assertEquals(Event.ACTIVITY_RESUMED, activityEvents.get(0).getEventType()); 595 assertEquals(Event.ACTIVITY_PAUSED, activityEvents.get(1).getEventType()); 596 assertEquals(Event.ACTIVITY_STOPPED, activityEvents.get(2).getEventType()); 597 } else { 598 // The last activity 599 assertEquals(1, activityEvents.size()); 600 assertEquals(Event.ACTIVITY_RESUMED, activityEvents.get(0).getEventType()); 601 } 602 } 603 } 604 605 @AppModeFull(reason = "No usage events access in instant apps") 606 @Test testActivityOnBackButton()607 public void testActivityOnBackButton() throws Exception { 608 testActivityOnButton(mUiDevice::pressBack); 609 } 610 611 @AppModeFull(reason = "No usage events access in instant apps") 612 @Test testActivityOnHomeButton()613 public void testActivityOnHomeButton() throws Exception { 614 testActivityOnButton(mUiDevice::pressHome); 615 } 616 testActivityOnButton(Runnable pressButton)617 private void testActivityOnButton(Runnable pressButton) throws Exception { 618 wakeDevice(); 619 final long startTime = System.currentTimeMillis(); 620 final Class clazz = Activities.ActivityOne.class; 621 launchSubActivity(clazz); 622 pressButton.run(); 623 Thread.sleep(1000); 624 final long endTime = System.currentTimeMillis(); 625 UsageEvents events = mUsageStatsManager.queryEvents(startTime, endTime); 626 627 ArrayList<UsageEvents.Event> eventList = new ArrayList<>(); 628 while (events.hasNextEvent()) { 629 UsageEvents.Event event = new UsageEvents.Event(); 630 assertTrue(events.getNextEvent(event)); 631 if (mTargetPackage.equals(event.getPackageName()) 632 && clazz.getName().equals(event.getClassName())) { 633 eventList.add(event); 634 } 635 } 636 assertEquals(3, eventList.size()); 637 assertEquals(Event.ACTIVITY_RESUMED, eventList.get(0).getEventType()); 638 assertEquals(Event.ACTIVITY_PAUSED, eventList.get(1).getEventType()); 639 assertEquals(Event.ACTIVITY_STOPPED, eventList.get(2).getEventType()); 640 } 641 642 @AppModeFull(reason = "No usage events access in instant apps") 643 @Test testAppLaunchCount()644 public void testAppLaunchCount() throws Exception { 645 long endTime = System.currentTimeMillis(); 646 long startTime = endTime - DateUtils.DAY_IN_MILLIS; 647 Map<String,UsageStats> events = mUsageStatsManager.queryAndAggregateUsageStats( 648 startTime, endTime); 649 UsageStats stats = events.get(mTargetPackage); 650 if (stats == null) { 651 fail("Querying UsageStats for " + mTargetPackage + " returned empty; list of packages " 652 + "with events: " + Arrays.toString(events.keySet().toArray(new String[0]))); 653 } 654 int startingCount = stats.getAppLaunchCount(); 655 // Launch count is updated by UsageStatsService depending on last background package. 656 // When running this test on single screen device (where tasks are launched in the same 657 // TaskDisplayArea), the last background package is updated when the HOME activity is 658 // paused. In a hierarchy with multiple TaskDisplayArea there is no guarantee the Home 659 // Activity will be paused as the activities we launch might be placed on a different 660 // TaskDisplayArea. Starting an activity and finishing it immediately will update the last 661 // background package of the UsageStatsService regardless of the HOME Activity state. 662 // To ensure that the test is not affected by the display windowing mode, all activities are 663 // forced to launch in fullscreen mode in this test. 664 launchTestActivity(TEST_APP_PKG, TEST_APP_CLASS_FINISH_SELF_ON_RESUME, 665 WINDOWING_MODE_FULLSCREEN); 666 launchSubActivity(Activities.ActivityOne.class, WINDOWING_MODE_FULLSCREEN); 667 launchSubActivity(Activities.ActivityTwo.class, WINDOWING_MODE_FULLSCREEN); 668 endTime = System.currentTimeMillis(); 669 events = mUsageStatsManager.queryAndAggregateUsageStats( 670 startTime, endTime); 671 stats = events.get(mTargetPackage); 672 assertEquals(startingCount + 1, stats.getAppLaunchCount()); 673 674 launchTestActivity(TEST_APP_PKG, TEST_APP_CLASS_FINISH_SELF_ON_RESUME, 675 WINDOWING_MODE_FULLSCREEN); 676 launchSubActivity(Activities.ActivityOne.class, WINDOWING_MODE_FULLSCREEN); 677 launchSubActivity(Activities.ActivityTwo.class, WINDOWING_MODE_FULLSCREEN); 678 launchSubActivity(Activities.ActivityThree.class, WINDOWING_MODE_FULLSCREEN); 679 endTime = System.currentTimeMillis(); 680 events = mUsageStatsManager.queryAndAggregateUsageStats( 681 startTime, endTime); 682 stats = events.get(mTargetPackage); 683 684 assertEquals(startingCount + 2, stats.getAppLaunchCount()); 685 } 686 687 @AppModeFull(reason = "No usage events access in instant apps") 688 @Test testStandbyBucketChangeLog()689 public void testStandbyBucketChangeLog() throws Exception { 690 final long startTime = System.currentTimeMillis(); 691 setStandByBucket(mTargetPackage, "rare"); 692 693 final long endTime = System.currentTimeMillis(); 694 UsageEvents events = mUsageStatsManager.queryEvents(startTime - 1_000, endTime + 1_000); 695 696 boolean found = false; 697 // Check all the events. 698 while (events.hasNextEvent()) { 699 UsageEvents.Event event = new UsageEvents.Event(); 700 assertTrue(events.getNextEvent(event)); 701 if (event.getEventType() == UsageEvents.Event.STANDBY_BUCKET_CHANGED) { 702 found |= event.getAppStandbyBucket() == STANDBY_BUCKET_RARE; 703 } 704 } 705 706 assertTrue(found); 707 } 708 709 @Test testGetAppStandbyBuckets()710 public void testGetAppStandbyBuckets() throws Exception { 711 final boolean origValue = AppStandbyUtils.isAppStandbyEnabledAtRuntime(); 712 AppStandbyUtils.setAppStandbyEnabledAtRuntime(true); 713 try { 714 assumeTrue("Skip GetAppStandby test: app standby is disabled.", 715 AppStandbyUtils.isAppStandbyEnabled()); 716 717 setStandByBucket(mTargetPackage, "rare"); 718 Map<String, Integer> bucketMap = mUsageStatsManager.getAppStandbyBuckets(); 719 assertTrue("No bucket data returned", bucketMap.size() > 0); 720 final int bucket = bucketMap.getOrDefault(mTargetPackage, -1); 721 assertEquals("Incorrect bucket returned for " + mTargetPackage, bucket, 722 STANDBY_BUCKET_RARE); 723 } finally { 724 AppStandbyUtils.setAppStandbyEnabledAtRuntime(origValue); 725 } 726 } 727 728 @Test testGetAppStandbyBucket()729 public void testGetAppStandbyBucket() throws Exception { 730 // App should be at least active, since it's running instrumentation tests 731 assertLessThanOrEqual(UsageStatsManager.STANDBY_BUCKET_ACTIVE, 732 mUsageStatsManager.getAppStandbyBucket()); 733 } 734 735 @Test testQueryEventsForSelf()736 public void testQueryEventsForSelf() throws Exception { 737 setAppOpsMode("ignore"); // To ensure permission is not required 738 // Time drifts of 2s are expected inside usage stats 739 final long start = System.currentTimeMillis() - 2_000; 740 setStandByBucket(mTargetPackage, "rare"); 741 Thread.sleep(100); 742 setStandByBucket(mTargetPackage, "working_set"); 743 Thread.sleep(100); 744 final long end = System.currentTimeMillis() + 2_000; 745 final UsageEvents events = mUsageStatsManager.queryEventsForSelf(start, end); 746 long rareTimeStamp = end + 1; // Initializing as rareTimeStamp > workingTimeStamp 747 long workingTimeStamp = start - 1; 748 int numEvents = 0; 749 while (events.hasNextEvent()) { 750 UsageEvents.Event event = new UsageEvents.Event(); 751 assertTrue(events.getNextEvent(event)); 752 numEvents++; 753 assertEquals("Event for a different package", mTargetPackage, event.getPackageName()); 754 if (event.getEventType() == Event.STANDBY_BUCKET_CHANGED) { 755 if (event.getAppStandbyBucket() == STANDBY_BUCKET_RARE) { 756 rareTimeStamp = event.getTimeStamp(); 757 } 758 else if (event.getAppStandbyBucket() == UsageStatsManager 759 .STANDBY_BUCKET_WORKING_SET) { 760 workingTimeStamp = event.getTimeStamp(); 761 } 762 } 763 } 764 assertTrue("Only " + numEvents + " events returned", numEvents >= 2); 765 assertLessThan(rareTimeStamp, workingTimeStamp); 766 } 767 768 /** 769 * We can't run this test because we are unable to change the system time. 770 * It would be nice to add a shell command or other to allow the shell user 771 * to set the time, thereby allowing this test to set the time using the UIAutomator. 772 */ 773 @Ignore 774 @Test ignore_testStatsAreShiftedInTimeWhenSystemTimeChanges()775 public void ignore_testStatsAreShiftedInTimeWhenSystemTimeChanges() throws Exception { 776 launchSubActivity(Activities.ActivityOne.class); 777 launchSubActivity(Activities.ActivityThree.class); 778 779 long endTime = System.currentTimeMillis(); 780 long startTime = endTime - MINUTE; 781 Map<String, UsageStats> statsMap = mUsageStatsManager.queryAndAggregateUsageStats(startTime, 782 endTime); 783 assertFalse(statsMap.isEmpty()); 784 assertTrue(statsMap.containsKey(mTargetPackage)); 785 final UsageStats before = statsMap.get(mTargetPackage); 786 787 SystemClock.setCurrentTimeMillis(System.currentTimeMillis() - (DAY / 2)); 788 try { 789 endTime = System.currentTimeMillis(); 790 startTime = endTime - MINUTE; 791 statsMap = mUsageStatsManager.queryAndAggregateUsageStats(startTime, endTime); 792 assertFalse(statsMap.isEmpty()); 793 assertTrue(statsMap.containsKey(mTargetPackage)); 794 final UsageStats after = statsMap.get(mTargetPackage); 795 assertEquals(before.getPackageName(), after.getPackageName()); 796 797 long diff = before.getFirstTimeStamp() - after.getFirstTimeStamp(); 798 assertLessThan(Math.abs(diff - (DAY / 2)), TIME_DIFF_THRESHOLD); 799 800 assertEquals(before.getLastTimeStamp() - before.getFirstTimeStamp(), 801 after.getLastTimeStamp() - after.getFirstTimeStamp()); 802 assertEquals(before.getLastTimeUsed() - before.getFirstTimeStamp(), 803 after.getLastTimeUsed() - after.getFirstTimeStamp()); 804 assertEquals(before.getTotalTimeInForeground(), after.getTotalTimeInForeground()); 805 } finally { 806 SystemClock.setCurrentTimeMillis(System.currentTimeMillis() + (DAY / 2)); 807 } 808 } 809 810 @Test testUsageEventsParceling()811 public void testUsageEventsParceling() throws Exception { 812 final long startTime = System.currentTimeMillis() - MINUTE; 813 814 // Ensure some data is in the UsageStats log. 815 @SuppressWarnings("unchecked") 816 Class<? extends Activity>[] activityClasses = new Class[] { 817 Activities.ActivityTwo.class, 818 Activities.ActivityOne.class, 819 Activities.ActivityThree.class, 820 }; 821 launchSubActivities(activityClasses); 822 823 final long endTime = System.currentTimeMillis(); 824 UsageEvents events = mUsageStatsManager.queryEvents(startTime, endTime); 825 assertTrue(events.getNextEvent(new UsageEvents.Event())); 826 827 Parcel p = Parcel.obtain(); 828 p.setDataPosition(0); 829 events.writeToParcel(p, 0); 830 p.setDataPosition(0); 831 832 UsageEvents reparceledEvents = UsageEvents.CREATOR.createFromParcel(p); 833 834 UsageEvents.Event e1 = new UsageEvents.Event(); 835 UsageEvents.Event e2 = new UsageEvents.Event(); 836 while (events.hasNextEvent() && reparceledEvents.hasNextEvent()) { 837 events.getNextEvent(e1); 838 reparceledEvents.getNextEvent(e2); 839 assertEquals(e1.getPackageName(), e2.getPackageName()); 840 assertEquals(e1.getClassName(), e2.getClassName()); 841 assertEquals(e1.getConfiguration(), e2.getConfiguration()); 842 assertEquals(e1.getEventType(), e2.getEventType()); 843 assertEquals(e1.getTimeStamp(), e2.getTimeStamp()); 844 } 845 846 assertEquals(events.hasNextEvent(), reparceledEvents.hasNextEvent()); 847 } 848 849 @AppModeFull(reason = "No usage events access in instant apps") 850 @Test testPackageUsageStatsIntervals()851 public void testPackageUsageStatsIntervals() throws Exception { 852 final long beforeTime = System.currentTimeMillis(); 853 854 // Launch an Activity. 855 launchSubActivity(Activities.ActivityFour.class); 856 launchSubActivity(Activities.ActivityThree.class); 857 858 final long endTime = System.currentTimeMillis(); 859 860 final SparseLongArray intervalLengths = new SparseLongArray(); 861 intervalLengths.put(UsageStatsManager.INTERVAL_DAILY, DAY); 862 intervalLengths.put(UsageStatsManager.INTERVAL_WEEKLY, WEEK); 863 intervalLengths.put(UsageStatsManager.INTERVAL_MONTHLY, MONTH); 864 intervalLengths.put(UsageStatsManager.INTERVAL_YEARLY, YEAR); 865 866 final int intervalCount = intervalLengths.size(); 867 for (int i = 0; i < intervalCount; i++) { 868 final int intervalType = intervalLengths.keyAt(i); 869 final long intervalDuration = intervalLengths.valueAt(i); 870 final long startTime = endTime - (2 * intervalDuration); 871 final List<UsageStats> statsList = mUsageStatsManager.queryUsageStats(intervalType, 872 startTime, endTime); 873 assertFalse(statsList.isEmpty()); 874 875 boolean foundPackage = false; 876 for (UsageStats stats : statsList) { 877 // Verify that each period is a day long. 878 assertLessThanOrEqual(stats.getLastTimeStamp() - stats.getFirstTimeStamp(), 879 intervalDuration); 880 if (stats.getPackageName().equals(mTargetPackage) && 881 stats.getLastTimeUsed() >= beforeTime - TIME_DIFF_THRESHOLD) { 882 foundPackage = true; 883 } 884 } 885 886 assertTrue("Did not find package " + mTargetPackage + " in interval " + intervalType, 887 foundPackage); 888 } 889 } 890 891 @Test testNoAccessSilentlyFails()892 public void testNoAccessSilentlyFails() throws Exception { 893 final long startTime = System.currentTimeMillis() - MINUTE; 894 895 launchSubActivity(android.app.usage.cts.Activities.ActivityOne.class); 896 launchSubActivity(android.app.usage.cts.Activities.ActivityThree.class); 897 898 final long endTime = System.currentTimeMillis(); 899 List<UsageStats> stats = mUsageStatsManager.queryUsageStats(UsageStatsManager.INTERVAL_BEST, 900 startTime, endTime); 901 assertFalse(stats.isEmpty()); 902 903 // We set the mode to ignore because our package has the PACKAGE_USAGE_STATS permission, 904 // and default would allow in this case. 905 setAppOpsMode("ignore"); 906 907 stats = mUsageStatsManager.queryUsageStats(UsageStatsManager.INTERVAL_BEST, 908 startTime, endTime); 909 assertTrue(stats.isEmpty()); 910 } 911 generateAndSendNotification()912 private void generateAndSendNotification() throws Exception { 913 final NotificationManager mNotificationManager = 914 (NotificationManager) mContext.getSystemService(Context.NOTIFICATION_SERVICE); 915 final NotificationChannel mChannel = new NotificationChannel(CHANNEL_ID, "Channel", 916 NotificationManager.IMPORTANCE_DEFAULT); 917 // Configure the notification channel. 918 mChannel.setDescription("Test channel"); 919 mNotificationManager.createNotificationChannel(mChannel); 920 final Notification.Builder mBuilder = 921 new Notification.Builder(mContext, CHANNEL_ID) 922 .setSmallIcon(R.drawable.ic_notification) 923 .setContentTitle("My notification") 924 .setContentText("Hello World!"); 925 final PendingIntent pi = PendingIntent.getActivity(mContext, 1, 926 new Intent(Settings.ACTION_SETTINGS), PendingIntent.FLAG_IMMUTABLE); 927 mBuilder.setContentIntent(pi); 928 mNotificationManager.notify(1, mBuilder.build()); 929 Thread.sleep(500); 930 } 931 932 @AppModeFull(reason = "No usage events access in instant apps") 933 @Test testNotificationSeen()934 public void testNotificationSeen() throws Exception { 935 final long startTime = System.currentTimeMillis(); 936 937 // Skip the test for wearable devices, televisions and automotives; none of them have 938 // a notification shade, as notifications are shown via a different path than phones 939 assumeFalse("Test cannot run on a watch- notification shade is not shown", 940 mContext.getPackageManager().hasSystemFeature(PackageManager.FEATURE_WATCH)); 941 assumeFalse("Test cannot run on a television- notifications are not shown", 942 mContext.getPackageManager().hasSystemFeature( 943 PackageManager.FEATURE_LEANBACK_ONLY)); 944 assumeFalse("Test cannot run on an automotive - notification shade is not shown", 945 mContext.getPackageManager().hasSystemFeature(PackageManager.FEATURE_AUTOMOTIVE)); 946 947 generateAndSendNotification(); 948 949 long endTime = System.currentTimeMillis(); 950 UsageEvents events = queryEventsAsShell(startTime, endTime); 951 boolean found = false; 952 Event event = new Event(); 953 while (events.hasNextEvent()) { 954 events.getNextEvent(event); 955 if (event.getEventType() == Event.NOTIFICATION_SEEN) { 956 found = true; 957 } 958 } 959 assertFalse(found); 960 // Pull down shade 961 mUiDevice.openNotification(); 962 outer: 963 for (int i = 0; i < 5; i++) { 964 Thread.sleep(500); 965 endTime = System.currentTimeMillis(); 966 events = queryEventsAsShell(startTime, endTime); 967 found = false; 968 while (events.hasNextEvent()) { 969 events.getNextEvent(event); 970 if (event.getEventType() == Event.NOTIFICATION_SEEN) { 971 found = true; 972 break outer; 973 } 974 } 975 } 976 assertTrue(found); 977 mUiDevice.pressBack(); 978 } 979 980 @AppModeFull(reason = "No usage events access in instant apps") 981 @MediumTest 982 @Test testNotificationSeen_verifyBucket()983 public void testNotificationSeen_verifyBucket() throws Exception { 984 // Skip the test for wearable devices, televisions and automotives; none of them have 985 // a notification shade, as notifications are shown via a different path than phones 986 assumeFalse("Test cannot run on a watch- notification shade is not shown", 987 mContext.getPackageManager().hasSystemFeature(PackageManager.FEATURE_WATCH)); 988 assumeFalse("Test cannot run on a television- notifications are not shown", 989 mContext.getPackageManager().hasSystemFeature( 990 PackageManager.FEATURE_LEANBACK_ONLY)); 991 assumeFalse("Test cannot run on an automotive - notification shade is not shown", 992 mContext.getPackageManager().hasSystemFeature(PackageManager.FEATURE_AUTOMOTIVE)); 993 994 final long promotedBucketHoldDurationMs = TimeUnit.MINUTES.toMillis(2); 995 try (DeviceConfigStateHelper deviceConfigStateHelper = 996 new DeviceConfigStateHelper(NAMESPACE_APP_STANDBY)) { 997 deviceConfigStateHelper.set(KEY_NOTIFICATION_SEEN_PROMOTED_BUCKET, 998 String.valueOf(STANDBY_BUCKET_FREQUENT)); 999 deviceConfigStateHelper.set(KEY_NOTIFICATION_SEEN_HOLD_DURATION, 1000 String.valueOf(promotedBucketHoldDurationMs)); 1001 1002 wakeDevice(); 1003 dismissKeyguard(); 1004 final TestServiceConnection connection = bindToTestServiceAndGetConnection(); 1005 final TestServiceConnection connection2 = bindToTestServiceAndGetConnection( 1006 TEST_APP_API_32_PKG); 1007 try { 1008 ITestReceiver testReceiver = connection.getITestReceiver(); 1009 ITestReceiver testReceiver2 = connection2.getITestReceiver(); 1010 for (ITestReceiver receiver : new ITestReceiver[] { 1011 testReceiver, 1012 testReceiver2 1013 }) { 1014 receiver.cancelAll(); 1015 receiver.createNotificationChannel(TEST_NOTIFICATION_CHANNEL_ID, 1016 TEST_NOTIFICATION_CHANNEL_NAME, 1017 TEST_NOTIFICATION_CHANNEL_DESC); 1018 receiver.postNotification(TEST_NOTIFICATION_ID_1, 1019 buildNotification(TEST_NOTIFICATION_CHANNEL_ID, TEST_NOTIFICATION_ID_1, 1020 TEST_NOTIFICATION_TEXT_1)); 1021 } 1022 } finally { 1023 connection.unbind(); 1024 connection2.unbind(); 1025 } 1026 for (String pkg : new String[] {TEST_APP_PKG, TEST_APP_API_32_PKG}) { 1027 setStandByBucket(pkg, "rare"); 1028 executeShellCmd("cmd usagestats clear-last-used-timestamps " + pkg); 1029 waitUntil(() -> mUsageStatsManager.getAppStandbyBucket(pkg), 1030 STANDBY_BUCKET_RARE); 1031 } 1032 mUiDevice.openNotification(); 1033 waitUntil(() -> mUsageStatsManager.getAppStandbyBucket(TEST_APP_PKG), 1034 STANDBY_BUCKET_FREQUENT); 1035 waitUntil(() -> mUsageStatsManager.getAppStandbyBucket(TEST_APP_API_32_PKG), 1036 STANDBY_BUCKET_FREQUENT); 1037 SystemClock.sleep(promotedBucketHoldDurationMs); 1038 // Verify that after the promoted duration expires, the app drops into a 1039 // lower standby bucket. 1040 // Note: "set-standby-bucket" command only updates the bucket of the app and not 1041 // it's last used timestamps. So, it is possible when the standby bucket is calculated 1042 // the app is not going to be back in RARE bucket we set earlier. So, just verify 1043 // the app gets demoted to some lower bucket. 1044 waitUntil(() -> mUsageStatsManager.getAppStandbyBucket(TEST_APP_PKG), 1045 result -> result > STANDBY_BUCKET_FREQUENT, 1046 "bucket should be > FREQUENT"); 1047 waitUntil(() -> mUsageStatsManager.getAppStandbyBucket(TEST_APP_API_32_PKG), 1048 result -> result > STANDBY_BUCKET_FREQUENT, 1049 "bucket should be > FREQUENT"); 1050 mUiDevice.pressHome(); 1051 } 1052 } 1053 1054 @AppModeFull(reason = "No usage events access in instant apps") 1055 @MediumTest 1056 @Test testNotificationSeen_verifyBucket_retainPreTImpact()1057 public void testNotificationSeen_verifyBucket_retainPreTImpact() throws Exception { 1058 // Skip the test for wearable devices, televisions and automotives; none of them have 1059 // a notification shade, as notifications are shown via a different path than phones 1060 assumeFalse("Test cannot run on a watch- notification shade is not shown", 1061 mContext.getPackageManager().hasSystemFeature(PackageManager.FEATURE_WATCH)); 1062 assumeFalse("Test cannot run on a television- notifications are not shown", 1063 mContext.getPackageManager().hasSystemFeature( 1064 PackageManager.FEATURE_LEANBACK_ONLY)); 1065 assumeFalse("Test cannot run on an automotive - notification shade is not shown", 1066 mContext.getPackageManager().hasSystemFeature(PackageManager.FEATURE_AUTOMOTIVE)); 1067 1068 final long promotedBucketHoldDurationMs = TimeUnit.SECONDS.toMillis(10); 1069 try (DeviceConfigStateHelper deviceConfigStateHelper = 1070 new DeviceConfigStateHelper(NAMESPACE_APP_STANDBY)) { 1071 deviceConfigStateHelper.set(KEY_NOTIFICATION_SEEN_PROMOTED_BUCKET, 1072 String.valueOf(STANDBY_BUCKET_FREQUENT)); 1073 deviceConfigStateHelper.set(KEY_NOTIFICATION_SEEN_HOLD_DURATION, 1074 String.valueOf(promotedBucketHoldDurationMs)); 1075 deviceConfigStateHelper.set(KEY_RETAIN_NOTIFICATION_SEEN_IMPACT_FOR_PRE_T_APPS, 1076 String.valueOf(true)); 1077 1078 wakeDevice(); 1079 dismissKeyguard(); 1080 final TestServiceConnection connection = bindToTestServiceAndGetConnection(); 1081 final TestServiceConnection connection2 = bindToTestServiceAndGetConnection( 1082 TEST_APP_API_32_PKG); 1083 try { 1084 ITestReceiver testReceiver = connection.getITestReceiver(); 1085 ITestReceiver testReceiver2 = connection2.getITestReceiver(); 1086 for (ITestReceiver receiver : new ITestReceiver[] { 1087 testReceiver, 1088 testReceiver2 1089 }) { 1090 receiver.cancelAll(); 1091 receiver.createNotificationChannel(TEST_NOTIFICATION_CHANNEL_ID, 1092 TEST_NOTIFICATION_CHANNEL_NAME, 1093 TEST_NOTIFICATION_CHANNEL_DESC); 1094 receiver.postNotification(TEST_NOTIFICATION_ID_1, 1095 buildNotification(TEST_NOTIFICATION_CHANNEL_ID, TEST_NOTIFICATION_ID_1, 1096 TEST_NOTIFICATION_TEXT_1)); 1097 } 1098 } finally { 1099 connection.unbind(); 1100 connection2.unbind(); 1101 } 1102 for (String pkg : new String[] {TEST_APP_PKG, TEST_APP_API_32_PKG}) { 1103 setStandByBucket(pkg, "rare"); 1104 executeShellCmd("cmd usagestats clear-last-used-timestamps " + pkg); 1105 waitUntil(() -> mUsageStatsManager.getAppStandbyBucket(pkg), 1106 STANDBY_BUCKET_RARE); 1107 } 1108 mUiDevice.openNotification(); 1109 waitUntil(() -> mUsageStatsManager.getAppStandbyBucket(TEST_APP_PKG), 1110 STANDBY_BUCKET_FREQUENT); 1111 waitUntil(() -> mUsageStatsManager.getAppStandbyBucket(TEST_APP_API_32_PKG), 1112 STANDBY_BUCKET_WORKING_SET); 1113 SystemClock.sleep(promotedBucketHoldDurationMs); 1114 // Verify that after the promoted duration expires, the app drops into a 1115 // lower standby bucket. 1116 // Note: "set-standby-bucket" command only updates the bucket of the app and not 1117 // it's last used timestamps. So, it is possible when the standby bucket is calculated 1118 // the app is not going to be back in RARE bucket we set earlier. So, just verify 1119 // the app gets demoted to some lower bucket. 1120 waitUntil(() -> mUsageStatsManager.getAppStandbyBucket(TEST_APP_PKG), 1121 result -> result > STANDBY_BUCKET_FREQUENT, 1122 "bucket should be > FREQUENT"); 1123 // App targeting api level 32 should still be in the working set bucket after a few 1124 // minutes. 1125 waitUntil(() -> mUsageStatsManager.getAppStandbyBucket(TEST_APP_API_32_PKG), 1126 STANDBY_BUCKET_WORKING_SET); 1127 mUiDevice.pressHome(); 1128 } 1129 } 1130 1131 @AppModeFull(reason = "No usage events access in instant apps") 1132 @MediumTest 1133 @Test testNotificationSeen_noImpact()1134 public void testNotificationSeen_noImpact() throws Exception { 1135 // Skip the test for wearable devices, televisions and automotives; none of them have 1136 // a notification shade, as notifications are shown via a different path than phones 1137 assumeFalse("Test cannot run on a watch- notification shade is not shown", 1138 mContext.getPackageManager().hasSystemFeature(PackageManager.FEATURE_WATCH)); 1139 assumeFalse("Test cannot run on a television- notifications are not shown", 1140 mContext.getPackageManager().hasSystemFeature( 1141 PackageManager.FEATURE_LEANBACK_ONLY)); 1142 assumeFalse("Test cannot run on an automotive - notification shade is not shown", 1143 mContext.getPackageManager().hasSystemFeature(PackageManager.FEATURE_AUTOMOTIVE)); 1144 1145 final long promotedBucketHoldDurationMs = TimeUnit.SECONDS.toMillis(10); 1146 try (DeviceConfigStateHelper deviceConfigStateHelper = 1147 new DeviceConfigStateHelper(NAMESPACE_APP_STANDBY)) { 1148 deviceConfigStateHelper.set(KEY_NOTIFICATION_SEEN_PROMOTED_BUCKET, 1149 String.valueOf(STANDBY_BUCKET_NEVER)); 1150 deviceConfigStateHelper.set(KEY_NOTIFICATION_SEEN_HOLD_DURATION, 1151 String.valueOf(promotedBucketHoldDurationMs)); 1152 1153 wakeDevice(); 1154 dismissKeyguard(); 1155 final TestServiceConnection connection = bindToTestServiceAndGetConnection(); 1156 try { 1157 ITestReceiver testReceiver = connection.getITestReceiver(); 1158 testReceiver.cancelAll(); 1159 testReceiver.createNotificationChannel(TEST_NOTIFICATION_CHANNEL_ID, 1160 TEST_NOTIFICATION_CHANNEL_NAME, 1161 TEST_NOTIFICATION_CHANNEL_DESC); 1162 testReceiver.postNotification(TEST_NOTIFICATION_ID_1, 1163 buildNotification(TEST_NOTIFICATION_CHANNEL_ID, TEST_NOTIFICATION_ID_1, 1164 TEST_NOTIFICATION_TEXT_1)); 1165 } finally { 1166 connection.unbind(); 1167 } 1168 setStandByBucket(TEST_APP_PKG, "rare"); 1169 executeShellCmd("cmd usagestats clear-last-used-timestamps " + TEST_APP_PKG); 1170 waitUntil(() -> mUsageStatsManager.getAppStandbyBucket(TEST_APP_PKG), 1171 STANDBY_BUCKET_RARE); 1172 mUiDevice.openNotification(); 1173 // Verify there is no change in the standby bucket 1174 waitUntil(() -> mUsageStatsManager.getAppStandbyBucket(TEST_APP_PKG), 1175 STANDBY_BUCKET_RARE); 1176 SystemClock.sleep(promotedBucketHoldDurationMs); 1177 // Verify there is no change in the standby bucket even after the hold duration 1178 // is elapsed. 1179 waitUntil(() -> mUsageStatsManager.getAppStandbyBucket(TEST_APP_PKG), 1180 STANDBY_BUCKET_RARE); 1181 mUiDevice.pressHome(); 1182 } 1183 } 1184 buildNotification(String channelId, int notificationId, String notificationText)1185 private Notification buildNotification(String channelId, int notificationId, 1186 String notificationText) { 1187 return new Notification.Builder(mContext, channelId) 1188 .setSmallIcon(android.R.drawable.ic_info) 1189 .setContentTitle(String.format(TEST_NOTIFICATION_TITLE_FMT, notificationId)) 1190 .setContentText(notificationText) 1191 .build(); 1192 } 1193 1194 @AppModeFull(reason = "No usage events access in instant apps") 1195 @Test testNotificationInterruptionEventsObfuscation()1196 public void testNotificationInterruptionEventsObfuscation() throws Exception { 1197 final long startTime = System.currentTimeMillis(); 1198 1199 // Skip the test for wearable devices and televisions; none of them have a 1200 // notification shade. 1201 assumeFalse("Test cannot run on a watch- notification shade is not shown", 1202 mContext.getPackageManager().hasSystemFeature(PackageManager.FEATURE_WATCH)); 1203 assumeFalse("Test cannot run on a television- notifications are not shown", 1204 mContext.getPackageManager().hasSystemFeature( 1205 PackageManager.FEATURE_LEANBACK_ONLY)); 1206 1207 generateAndSendNotification(); 1208 final long endTime = System.currentTimeMillis(); 1209 1210 final UsageEvents obfuscatedEvents = mUsageStatsManager.queryEvents(startTime, endTime); 1211 final UsageEvents unobfuscatedEvents = queryEventsAsShell(startTime, endTime); 1212 verifyNotificationInterruptionEvent(obfuscatedEvents, true); 1213 verifyNotificationInterruptionEvent(unobfuscatedEvents, false); 1214 } 1215 verifyNotificationInterruptionEvent(UsageEvents events, boolean obfuscated)1216 private void verifyNotificationInterruptionEvent(UsageEvents events, boolean obfuscated) { 1217 boolean found = false; 1218 Event event = new Event(); 1219 while (events.hasNextEvent()) { 1220 events.getNextEvent(event); 1221 if (event.getEventType() == Event.NOTIFICATION_INTERRUPTION) { 1222 found = true; 1223 break; 1224 } 1225 } 1226 assertTrue(found); 1227 if (obfuscated) { 1228 assertEquals("Notification channel id was not obfuscated.", 1229 UsageEvents.OBFUSCATED_NOTIFICATION_CHANNEL_ID, event.mNotificationChannelId); 1230 } else { 1231 assertEquals("Failed to verify notification channel id.", 1232 CHANNEL_ID, event.mNotificationChannelId); 1233 } 1234 } 1235 1236 @AppModeFull(reason = "No usage events access in instant apps") 1237 @Test testUserUnlockedEventExists()1238 public void testUserUnlockedEventExists() throws Exception { 1239 final UsageEvents events = mUsageStatsManager.queryEvents(0, System.currentTimeMillis()); 1240 while (events.hasNextEvent()) { 1241 final Event event = new Event(); 1242 events.getNextEvent(event); 1243 if (event.mEventType == Event.USER_UNLOCKED) { 1244 return; 1245 } 1246 } 1247 fail("Couldn't find a user unlocked event."); 1248 } 1249 1250 @AppModeFull(reason = "No usage stats access in instant apps") 1251 @Test testCrossUserQuery_withPermission()1252 public void testCrossUserQuery_withPermission() throws Exception { 1253 assumeTrue(UserManager.supportsMultipleUsers()); 1254 final long startTime = System.currentTimeMillis(); 1255 // Create user 1256 final int userId = createUser("Test User"); 1257 startUser(userId, true); 1258 installExistingPackageAsUser(mContext.getPackageName(), userId); 1259 1260 // Query as Shell 1261 SystemUtil.runWithShellPermissionIdentity(() -> { 1262 final UserHandle otherUser = UserHandle.of(userId); 1263 final Context userContext = mContext.createContextAsUser(otherUser, 0); 1264 1265 final UsageStatsManager usmOther = userContext.getSystemService( 1266 UsageStatsManager.class); 1267 1268 waitUntil(() -> { 1269 final List<UsageStats> stats = usmOther.queryUsageStats( 1270 UsageStatsManager.INTERVAL_DAILY, startTime, System.currentTimeMillis()); 1271 return stats.isEmpty(); 1272 }, false); 1273 }); 1274 // user cleanup done in @After 1275 } 1276 1277 @AppModeFull(reason = "No usage stats access in instant apps") 1278 @Test testCrossUserQuery_withoutPermission()1279 public void testCrossUserQuery_withoutPermission() throws Exception { 1280 assumeTrue(UserManager.supportsMultipleUsers()); 1281 final long startTime = System.currentTimeMillis(); 1282 // Create user 1283 final int userId = createUser("Test User"); 1284 startUser(userId, true); 1285 installExistingPackageAsUser(mContext.getPackageName(), userId); 1286 1287 SystemUtil.runWithShellPermissionIdentity(() -> { 1288 mOtherUserContext = mContext.createContextAsUser(UserHandle.of(userId), 0); 1289 mOtherUsageStats = mOtherUserContext.getSystemService(UsageStatsManager.class); 1290 }); 1291 1292 try { 1293 mOtherUsageStats.queryUsageStats(UsageStatsManager.INTERVAL_DAILY, startTime, 1294 System.currentTimeMillis()); 1295 fail("Query across users should require INTERACT_ACROSS_USERS permission"); 1296 } catch (SecurityException se) { 1297 // Expected 1298 } 1299 1300 // user cleanup done in @After 1301 } 1302 1303 // TODO(148887416): get this test to work for instant apps 1304 @AppModeFull(reason = "Test APK Activity not found when installed as an instant app") 1305 @Test testUserForceIntoRestricted()1306 public void testUserForceIntoRestricted() throws Exception { 1307 launchSubActivity(TaskRootActivity.class); 1308 assertEquals("Activity launch didn't bring app up to ACTIVE bucket", 1309 UsageStatsManager.STANDBY_BUCKET_ACTIVE, 1310 mUsageStatsManager.getAppStandbyBucket(mTargetPackage)); 1311 1312 // User force shouldn't have to deal with the timeout. 1313 setStandByBucket(mTargetPackage, "restricted"); 1314 assertEquals("User was unable to force an ACTIVE app down into RESTRICTED bucket", 1315 UsageStatsManager.STANDBY_BUCKET_RESTRICTED, 1316 mUsageStatsManager.getAppStandbyBucket(mTargetPackage)); 1317 1318 } 1319 1320 // TODO(148887416): get this test to work for instant apps 1321 @AppModeFull(reason = "Test APK Activity not found when installed as an instant app") 1322 @Test testUserLaunchRemovesFromRestricted()1323 public void testUserLaunchRemovesFromRestricted() throws Exception { 1324 setStandByBucket(mTargetPackage, "restricted"); 1325 assertEquals("User was unable to force an app into RESTRICTED bucket", 1326 UsageStatsManager.STANDBY_BUCKET_RESTRICTED, 1327 mUsageStatsManager.getAppStandbyBucket(mTargetPackage)); 1328 1329 launchSubActivity(TaskRootActivity.class); 1330 assertEquals("Activity launch didn't bring RESTRICTED app into ACTIVE bucket", 1331 UsageStatsManager.STANDBY_BUCKET_ACTIVE, 1332 mUsageStatsManager.getAppStandbyBucket(mTargetPackage)); 1333 } 1334 1335 // TODO(148887416): get this test to work for instant apps 1336 @AppModeFull(reason = "Test APK Activity not found when installed as an instant app") 1337 @Test testIsAppInactive()1338 public void testIsAppInactive() throws Exception { 1339 assumeTrue("Test only works on devices with a battery", BatteryUtils.hasBattery()); 1340 1341 setStandByBucket(mTargetPackage, "rare"); 1342 1343 try { 1344 BatteryUtils.runDumpsysBatteryUnplug(); 1345 1346 waitUntil(() -> mUsageStatsManager.isAppInactive(mTargetPackage), true); 1347 assertFalse( 1348 "App without PACKAGE_USAGE_STATS permission should always receive false for " 1349 + "isAppInactive", 1350 isAppInactiveAsPermissionlessApp(mTargetPackage)); 1351 1352 launchSubActivity(Activities.ActivityOne.class); 1353 1354 waitUntil(() -> mUsageStatsManager.isAppInactive(mTargetPackage), false); 1355 assertFalse( 1356 "App without PACKAGE_USAGE_STATS permission should always receive false for " 1357 + "isAppInactive", 1358 isAppInactiveAsPermissionlessApp(mTargetPackage)); 1359 1360 mUiDevice.pressHome(); 1361 setStandByBucket(TEST_APP_PKG, "rare"); 1362 // Querying for self does not require the PACKAGE_USAGE_STATS 1363 waitUntil(() -> mUsageStatsManager.isAppInactive(TEST_APP_PKG), true); 1364 assertTrue( 1365 "App without PACKAGE_USAGE_STATS permission should be able to call " 1366 + "isAppInactive for itself", 1367 isAppInactiveAsPermissionlessApp(TEST_APP_PKG)); 1368 1369 launchTestActivity(TEST_APP_PKG, TEST_APP_CLASS); 1370 1371 waitUntil(() -> mUsageStatsManager.isAppInactive(TEST_APP_PKG), false); 1372 assertFalse( 1373 "App without PACKAGE_USAGE_STATS permission should be able to call " 1374 + "isAppInactive for itself", 1375 isAppInactiveAsPermissionlessApp(TEST_APP_PKG)); 1376 1377 } finally { 1378 BatteryUtils.runDumpsysBatteryReset(); 1379 } 1380 } 1381 1382 // TODO(148887416): get this test to work for instant apps 1383 @AppModeFull(reason = "Test APK Activity not found when installed as an instant app") 1384 @Test testIsAppInactive_Charging()1385 public void testIsAppInactive_Charging() throws Exception { 1386 assumeTrue("Test only works on devices with a battery", BatteryUtils.hasBattery()); 1387 1388 setStandByBucket(TEST_APP_PKG, "rare"); 1389 1390 try { 1391 BatteryUtils.runDumpsysBatteryUnplug(); 1392 // Plug/unplug change takes a while to propagate inside the system. 1393 waitUntil(() -> mUsageStatsManager.isAppInactive(TEST_APP_PKG), true); 1394 1395 BatteryUtils.runDumpsysBatterySetPluggedIn(true); 1396 BatteryUtils.runDumpsysBatterySetLevel(100); 1397 // Plug/unplug change takes a while to propagate inside the system. 1398 waitUntil(() -> mUsageStatsManager.isAppInactive(TEST_APP_PKG), false); 1399 } finally { 1400 BatteryUtils.runDumpsysBatteryReset(); 1401 } 1402 } 1403 1404 @Test testSetEstimatedLaunchTime_NotUsableByShell()1405 public void testSetEstimatedLaunchTime_NotUsableByShell() { 1406 SystemUtil.runWithShellPermissionIdentity(() -> { 1407 try { 1408 mUsageStatsManager.setEstimatedLaunchTimeMillis(TEST_APP_PKG, 1409 System.currentTimeMillis() + 1000); 1410 fail("Shell was able to set an app's estimated launch time"); 1411 } catch (SecurityException expected) { 1412 // Success 1413 } 1414 1415 try { 1416 Map<String, Long> estimatedLaunchTime = new ArrayMap<>(); 1417 estimatedLaunchTime.put(TEST_APP_PKG, System.currentTimeMillis() + 10_000); 1418 mUsageStatsManager.setEstimatedLaunchTimesMillis(estimatedLaunchTime); 1419 fail("Shell was able to set an app's estimated launch time"); 1420 } catch (SecurityException expected) { 1421 // Success 1422 } 1423 }, Manifest.permission.CHANGE_APP_LAUNCH_TIME_ESTIMATE); 1424 } 1425 1426 private static final int[] INTERACTIVE_EVENTS = new int[] { 1427 Event.SCREEN_INTERACTIVE, 1428 Event.SCREEN_NON_INTERACTIVE 1429 }; 1430 1431 private static final int[] KEYGUARD_EVENTS = new int[] { 1432 Event.KEYGUARD_SHOWN, 1433 Event.KEYGUARD_HIDDEN 1434 }; 1435 1436 private static final int[] ALL_EVENTS = new int[] { 1437 Event.SCREEN_INTERACTIVE, 1438 Event.SCREEN_NON_INTERACTIVE, 1439 Event.KEYGUARD_SHOWN, 1440 Event.KEYGUARD_HIDDEN 1441 }; 1442 1443 private static final int[] PAUSED_EVENT = new int[] { 1444 Event.ACTIVITY_PAUSED 1445 }; 1446 1447 private static final int[] STOPPED_EVENT = new int[] { 1448 Event.ACTIVITY_STOPPED 1449 }; 1450 getEvents(int[] whichEvents, long startTime, List<Event> out, String packageName)1451 private long getEvents(int[] whichEvents, long startTime, List<Event> out, String packageName) { 1452 final long endTime = System.currentTimeMillis(); 1453 if (DEBUG) { 1454 Log.i(TAG, "Looking for events " + Arrays.toString(whichEvents) 1455 + " between " + startTime + " and " + endTime); 1456 } 1457 UsageEvents events = mUsageStatsManager.queryEvents(startTime, endTime); 1458 1459 long latestTime = 0; 1460 1461 // Find events. 1462 while (events.hasNextEvent()) { 1463 UsageEvents.Event event = new UsageEvents.Event(); 1464 assertTrue(events.getNextEvent(event)); 1465 final int ev = event.getEventType(); 1466 for (int which : whichEvents) { 1467 if (ev == which) { 1468 if (packageName != null && !packageName.equals(event.getPackageName())) { 1469 break; 1470 } 1471 1472 if (out != null) { 1473 out.add(event); 1474 } 1475 if (DEBUG) Log.i(TAG, "Next event type " + event.getEventType() 1476 + " time=" + event.getTimeStamp()); 1477 if (latestTime < event.getTimeStamp()) { 1478 latestTime = event.getTimeStamp(); 1479 } 1480 break; 1481 } 1482 } 1483 } 1484 1485 return latestTime; 1486 } 1487 1488 waitForEventCount(int[] whichEvents, long startTime, int count)1489 private ArrayList<Event> waitForEventCount(int[] whichEvents, long startTime, int count) { 1490 return waitForEventCount(whichEvents, startTime, count, null); 1491 } 1492 waitForEventCount(int[] whichEvents, long startTime, int count, String packageName)1493 private ArrayList<Event> waitForEventCount(int[] whichEvents, long startTime, int count, 1494 String packageName) { 1495 final ArrayList<Event> events = new ArrayList<>(); 1496 final long endTime = SystemClock.uptimeMillis() + TIMEOUT; 1497 do { 1498 events.clear(); 1499 getEvents(whichEvents, startTime, events, packageName); 1500 if (events.size() == count) { 1501 return events; 1502 } 1503 if (events.size() > count) { 1504 fail("Found too many events: got " + events.size() + ", expected " + count); 1505 return events; 1506 } 1507 SystemClock.sleep(10); 1508 } while (SystemClock.uptimeMillis() < endTime); 1509 1510 fail("Timed out waiting for " + count + " events, only reached " + events.size()); 1511 return events; 1512 } 1513 waitUntil(Supplier<T> resultSupplier, T expectedResult)1514 private <T> void waitUntil(Supplier<T> resultSupplier, T expectedResult) { 1515 final T actualResult = PollingCheck.waitFor(DEFAULT_TIMEOUT_MS, resultSupplier, 1516 result -> Objects.equals(expectedResult, result)); 1517 assertEquals(expectedResult, actualResult); 1518 } 1519 waitUntil(Supplier<T> resultSupplier, Function<T, Boolean> condition, String conditionDesc)1520 private <T> void waitUntil(Supplier<T> resultSupplier, Function<T, Boolean> condition, 1521 String conditionDesc) { 1522 final T actualResult = PollingCheck.waitFor(DEFAULT_TIMEOUT_MS, resultSupplier, 1523 condition); 1524 Log.d(TAG, "Expecting '" + conditionDesc + "'; actual result=" + actualResult); 1525 assertTrue("Timed out waiting for '" + conditionDesc + "', actual=" + actualResult, 1526 condition.apply(actualResult)); 1527 } 1528 1529 static class AggrEventData { 1530 final String label; 1531 int count; 1532 long duration; 1533 long lastEventTime; 1534 AggrEventData(String label)1535 AggrEventData(String label) { 1536 this.label = label; 1537 } 1538 } 1539 1540 static class AggrAllEventsData { 1541 final AggrEventData interactive = new AggrEventData("Interactive"); 1542 final AggrEventData nonInteractive = new AggrEventData("Non-interactive"); 1543 final AggrEventData keyguardShown = new AggrEventData("Keyguard shown"); 1544 final AggrEventData keyguardHidden = new AggrEventData("Keyguard hidden"); 1545 } 1546 getAggrEventData()1547 private SparseArray<AggrAllEventsData> getAggrEventData() { 1548 final long endTime = System.currentTimeMillis(); 1549 1550 final SparseLongArray intervalLengths = new SparseLongArray(); 1551 intervalLengths.put(UsageStatsManager.INTERVAL_DAILY, DAY); 1552 intervalLengths.put(UsageStatsManager.INTERVAL_WEEKLY, WEEK); 1553 intervalLengths.put(UsageStatsManager.INTERVAL_MONTHLY, MONTH); 1554 intervalLengths.put(UsageStatsManager.INTERVAL_YEARLY, YEAR); 1555 1556 final SparseArray<AggrAllEventsData> allAggr = new SparseArray<>(); 1557 1558 final int intervalCount = intervalLengths.size(); 1559 for (int i = 0; i < intervalCount; i++) { 1560 final int intervalType = intervalLengths.keyAt(i); 1561 final long intervalDuration = intervalLengths.valueAt(i); 1562 final long startTime = endTime - (2 * intervalDuration); 1563 List<EventStats> statsList = mUsageStatsManager.queryEventStats(intervalType, 1564 startTime, endTime); 1565 assertFalse(statsList.isEmpty()); 1566 1567 final AggrAllEventsData aggr = new AggrAllEventsData(); 1568 allAggr.put(intervalType, aggr); 1569 1570 boolean foundEvent = false; 1571 for (EventStats stats : statsList) { 1572 // Verify that each period is a day long. 1573 //assertLessThanOrEqual(stats.getLastTimeStamp() - stats.getFirstTimeStamp(), 1574 // intervalDuration); 1575 AggrEventData data = null; 1576 switch (stats.getEventType()) { 1577 case Event.SCREEN_INTERACTIVE: 1578 data = aggr.interactive; 1579 break; 1580 case Event.SCREEN_NON_INTERACTIVE: 1581 data = aggr.nonInteractive; 1582 break; 1583 case Event.KEYGUARD_HIDDEN: 1584 data = aggr.keyguardHidden; 1585 break; 1586 case Event.KEYGUARD_SHOWN: 1587 data = aggr.keyguardShown; 1588 break; 1589 } 1590 if (data != null) { 1591 foundEvent = true; 1592 data.count += stats.getCount(); 1593 data.duration += stats.getTotalTime(); 1594 if (data.lastEventTime < stats.getLastEventTime()) { 1595 data.lastEventTime = stats.getLastEventTime(); 1596 } 1597 } 1598 } 1599 1600 assertTrue("Did not find event data in interval " + intervalType, 1601 foundEvent); 1602 } 1603 1604 return allAggr; 1605 } 1606 verifyCount(int oldCount, int newCount, boolean larger, String label, int interval)1607 private void verifyCount(int oldCount, int newCount, boolean larger, String label, 1608 int interval) { 1609 if (larger) { 1610 if (newCount <= oldCount) { 1611 fail(label + " count newer " + newCount 1612 + " expected to be larger than older " + oldCount 1613 + " @ interval " + interval); 1614 } 1615 } else { 1616 if (newCount != oldCount) { 1617 fail(label + " count newer " + newCount 1618 + " expected to be same as older " + oldCount 1619 + " @ interval " + interval); 1620 } 1621 } 1622 } 1623 verifyDuration(long oldDur, long newDur, boolean larger, String label, int interval)1624 private void verifyDuration(long oldDur, long newDur, boolean larger, String label, 1625 int interval) { 1626 if (larger) { 1627 if (newDur <= oldDur) { 1628 fail(label + " duration newer " + newDur 1629 + " expected to be larger than older " + oldDur 1630 + " @ interval " + interval); 1631 } 1632 } else { 1633 if (newDur != oldDur) { 1634 fail(label + " duration newer " + newDur 1635 + " expected to be same as older " + oldDur 1636 + " @ interval " + interval); 1637 } 1638 } 1639 } 1640 verifyAggrEventData(AggrEventData older, AggrEventData newer, boolean countLarger, boolean durationLarger, int interval)1641 private void verifyAggrEventData(AggrEventData older, AggrEventData newer, 1642 boolean countLarger, boolean durationLarger, int interval) { 1643 verifyCount(older.count, newer.count, countLarger, older.label, interval); 1644 verifyDuration(older.duration, newer.duration, durationLarger, older.label, interval); 1645 } 1646 verifyAggrInteractiveEventData(SparseArray<AggrAllEventsData> older, SparseArray<AggrAllEventsData> newer, boolean interactiveLarger, boolean nonInteractiveLarger)1647 private void verifyAggrInteractiveEventData(SparseArray<AggrAllEventsData> older, 1648 SparseArray<AggrAllEventsData> newer, boolean interactiveLarger, 1649 boolean nonInteractiveLarger) { 1650 for (int i = 0; i < older.size(); i++) { 1651 AggrAllEventsData o = older.valueAt(i); 1652 AggrAllEventsData n = newer.valueAt(i); 1653 // When we are told something is larger, that means we have transitioned 1654 // *out* of that state -- so the duration of that state is expected to 1655 // increase, but the count should stay the same (and the count of the state 1656 // we transition to is increased). 1657 final int interval = older.keyAt(i); 1658 verifyAggrEventData(o.interactive, n.interactive, nonInteractiveLarger, 1659 interactiveLarger, interval); 1660 verifyAggrEventData(o.nonInteractive, n.nonInteractive, interactiveLarger, 1661 nonInteractiveLarger, interval); 1662 } 1663 } 1664 verifyAggrKeyguardEventData(SparseArray<AggrAllEventsData> older, SparseArray<AggrAllEventsData> newer, boolean hiddenLarger, boolean shownLarger)1665 private void verifyAggrKeyguardEventData(SparseArray<AggrAllEventsData> older, 1666 SparseArray<AggrAllEventsData> newer, boolean hiddenLarger, 1667 boolean shownLarger) { 1668 for (int i = 0; i < older.size(); i++) { 1669 AggrAllEventsData o = older.valueAt(i); 1670 AggrAllEventsData n = newer.valueAt(i); 1671 // When we are told something is larger, that means we have transitioned 1672 // *out* of that state -- so the duration of that state is expected to 1673 // increase, but the count should stay the same (and the count of the state 1674 // we transition to is increased). 1675 final int interval = older.keyAt(i); 1676 verifyAggrEventData(o.keyguardHidden, n.keyguardHidden, shownLarger, 1677 hiddenLarger, interval); 1678 verifyAggrEventData(o.keyguardShown, n.keyguardShown, hiddenLarger, 1679 shownLarger, interval); 1680 } 1681 } 1682 1683 @AppModeFull(reason = "No usage events access in instant apps") 1684 @Test testInteractiveEvents()1685 public void testInteractiveEvents() throws Exception { 1686 // We need to start out with the screen on. 1687 wakeDevice(); 1688 dismissKeyguard(); // also want to start out with the keyguard dismissed. 1689 SystemClock.sleep(500); 1690 1691 1692 try { 1693 ArrayList<Event> events; 1694 1695 // Determine time to start looking for events. 1696 final long startTime = getEvents(ALL_EVENTS, 0, null, null) + 1; 1697 SparseArray<AggrAllEventsData> baseAggr = getAggrEventData(); 1698 SystemClock.sleep(500); 1699 1700 // First test -- put device to sleep and make sure we see this event. 1701 sleepDevice(); 1702 SystemClock.sleep(500); 1703 1704 // Do we have one event, going in to non-interactive mode? 1705 events = waitForEventCount(INTERACTIVE_EVENTS, startTime, 1); 1706 assertEquals(Event.SCREEN_NON_INTERACTIVE, events.get(0).getEventType()); 1707 SparseArray<AggrAllEventsData> offAggr = getAggrEventData(); 1708 verifyAggrInteractiveEventData(baseAggr, offAggr, true, false); 1709 1710 // Next test -- turn screen on and make sure we have a second event. 1711 // XXX need to wait a bit so we don't accidentally trigger double-power 1712 // to launch camera. (SHOULD FIX HOW WE WAKEUP / SLEEP TO NOT USE POWER KEY) 1713 SystemClock.sleep(500); 1714 wakeDevice(); 1715 events = waitForEventCount(INTERACTIVE_EVENTS, startTime, 2); 1716 assertEquals(Event.SCREEN_NON_INTERACTIVE, events.get(0).getEventType()); 1717 assertEquals(Event.SCREEN_INTERACTIVE, events.get(1).getEventType()); 1718 SparseArray<AggrAllEventsData> onAggr = getAggrEventData(); 1719 verifyAggrInteractiveEventData(offAggr, onAggr, false, true); 1720 1721 // If the device is doing a lock screen, verify that we are also seeing the 1722 // appropriate keyguard behavior. We don't know the timing from when the screen 1723 // will go off until the keyguard is shown, so we will do this all after turning 1724 // the screen back on (at which point it must be shown). 1725 // XXX CTS seems to be preventing the keyguard from showing, so this path is 1726 // never being tested. 1727 if (mKeyguardManager.isKeyguardLocked()) { 1728 events = waitForEventCount(KEYGUARD_EVENTS, startTime, 1); 1729 assertEquals(Event.KEYGUARD_SHOWN, events.get(0).getEventType()); 1730 SparseArray<AggrAllEventsData> shownAggr = getAggrEventData(); 1731 verifyAggrKeyguardEventData(offAggr, shownAggr, true, false); 1732 1733 // Now dismiss the keyguard and verify the resulting events. 1734 executeShellCmd("wm dismiss-keyguard"); 1735 events = waitForEventCount(KEYGUARD_EVENTS, startTime, 2); 1736 assertEquals(Event.KEYGUARD_SHOWN, events.get(0).getEventType()); 1737 assertEquals(Event.KEYGUARD_HIDDEN, events.get(1).getEventType()); 1738 SparseArray<AggrAllEventsData> hiddenAggr = getAggrEventData(); 1739 verifyAggrKeyguardEventData(shownAggr, hiddenAggr, false, true); 1740 } 1741 1742 } finally { 1743 // Dismiss keyguard to get device back in its normal state. 1744 wakeDevice(); 1745 executeShellCmd("wm dismiss-keyguard"); 1746 } 1747 } 1748 1749 @Test testIgnoreNonexistentPackage()1750 public void testIgnoreNonexistentPackage() throws Exception { 1751 final String fakePackageName = "android.fake.package.name"; 1752 final int defaultValue = -1; 1753 1754 setStandByBucket(fakePackageName, "rare"); 1755 // Verify the above does not add a new entry to the App Standby bucket map 1756 Map<String, Integer> bucketMap = mUsageStatsManager.getAppStandbyBuckets(); 1757 int bucket = bucketMap.getOrDefault(fakePackageName, defaultValue); 1758 assertFalse("Meaningful bucket value " + bucket + " returned for " + fakePackageName 1759 + " after set-standby-bucket", bucket > 0); 1760 1761 executeShellCmd("am get-standby-bucket " + fakePackageName); 1762 // Verify the above does not add a new entry to the App Standby bucket map 1763 bucketMap = mUsageStatsManager.getAppStandbyBuckets(); 1764 bucket = bucketMap.getOrDefault(fakePackageName, defaultValue); 1765 assertFalse("Meaningful bucket value " + bucket + " returned for " + fakePackageName 1766 + " after get-standby-bucket", bucket > 0); 1767 } 1768 1769 @Test testObserveUsagePermissionForRegisterObserver()1770 public void testObserveUsagePermissionForRegisterObserver() { 1771 final int observerId = 0; 1772 final String[] packages = new String[] {"com.android.settings"}; 1773 1774 try { 1775 mUsageStatsManager.registerAppUsageObserver(observerId, packages, 1776 1, java.util.concurrent.TimeUnit.HOURS, null); 1777 fail("Expected SecurityException for an app not holding OBSERVE_APP_USAGE permission."); 1778 } catch (SecurityException e) { 1779 // Exception expected 1780 } 1781 1782 try { 1783 mUsageStatsManager.registerUsageSessionObserver(observerId, packages, 1784 Duration.ofHours(1), Duration.ofSeconds(10), null, null); 1785 fail("Expected SecurityException for an app not holding OBSERVE_APP_USAGE permission."); 1786 } catch (SecurityException e) { 1787 // Exception expected 1788 } 1789 1790 try { 1791 mUsageStatsManager.registerAppUsageLimitObserver(observerId, packages, 1792 Duration.ofHours(1), Duration.ofHours(0), null); 1793 fail("Expected SecurityException for an app not holding OBSERVE_APP_USAGE permission."); 1794 } catch (SecurityException e) { 1795 // Exception expected 1796 } 1797 } 1798 1799 @Test testObserveUsagePermissionForUnregisterObserver()1800 public void testObserveUsagePermissionForUnregisterObserver() { 1801 final int observerId = 0; 1802 1803 try { 1804 mUsageStatsManager.unregisterAppUsageObserver(observerId); 1805 fail("Expected SecurityException for an app not holding OBSERVE_APP_USAGE permission."); 1806 } catch (SecurityException e) { 1807 // Exception expected 1808 } 1809 1810 try { 1811 mUsageStatsManager.unregisterUsageSessionObserver(observerId); 1812 fail("Expected SecurityException for an app not holding OBSERVE_APP_USAGE permission."); 1813 } catch (SecurityException e) { 1814 // Exception expected 1815 } 1816 1817 try { 1818 mUsageStatsManager.unregisterAppUsageLimitObserver(observerId); 1819 fail("Expected SecurityException for an app not holding OBSERVE_APP_USAGE permission."); 1820 } catch (SecurityException e) { 1821 // Exception expected 1822 } 1823 } 1824 1825 @AppModeFull(reason = "No usage events access in instant apps") 1826 @Test testForegroundService()1827 public void testForegroundService() throws Exception { 1828 // This test start a foreground service then stop it. The event list should have one 1829 // FOREGROUND_SERVICE_START and one FOREGROUND_SERVICE_STOP event. 1830 final long startTime = System.currentTimeMillis(); 1831 mContext.startService(new Intent(mContext, TestService.class)); 1832 mUiDevice.wait(Until.hasObject(By.clazz(TestService.class)), TIMEOUT); 1833 final long sleepTime = 500; 1834 SystemClock.sleep(sleepTime); 1835 mContext.stopService(new Intent(mContext, TestService.class)); 1836 mUiDevice.wait(Until.gone(By.clazz(TestService.class)), TIMEOUT); 1837 final long endTime = System.currentTimeMillis(); 1838 UsageEvents events = mUsageStatsManager.queryEvents(startTime, endTime); 1839 1840 int numStarts = 0; 1841 int numStops = 0; 1842 int startIdx = -1; 1843 int stopIdx = -1; 1844 int i = 0; 1845 while (events.hasNextEvent()) { 1846 UsageEvents.Event event = new UsageEvents.Event(); 1847 assertTrue(events.getNextEvent(event)); 1848 if (mTargetPackage.equals(event.getPackageName()) 1849 || TestService.class.getName().equals(event.getClassName())) { 1850 if (event.getEventType() == Event.FOREGROUND_SERVICE_START) { 1851 numStarts++; 1852 startIdx = i; 1853 } else if (event.getEventType() == Event.FOREGROUND_SERVICE_STOP) { 1854 numStops++; 1855 stopIdx = i; 1856 } 1857 i++; 1858 } 1859 } 1860 // One FOREGROUND_SERVICE_START event followed by one FOREGROUND_SERVICE_STOP event. 1861 assertEquals(numStarts, 1); 1862 assertEquals(numStops, 1); 1863 assertLessThan(startIdx, stopIdx); 1864 1865 final Map<String, UsageStats> map = mUsageStatsManager.queryAndAggregateUsageStats( 1866 startTime, endTime); 1867 final UsageStats stats = map.get(mTargetPackage); 1868 assertNotNull(stats); 1869 final long lastTimeUsed = stats.getLastTimeForegroundServiceUsed(); 1870 // lastTimeUsed should be falling between startTime and endTime. 1871 assertLessThan(startTime, lastTimeUsed); 1872 assertLessThan(lastTimeUsed, endTime); 1873 final long totalTimeUsed = stats.getTotalTimeForegroundServiceUsed(); 1874 // because we slept for 500 milliseconds earlier, we know the totalTimeUsed must be more 1875 // more than 500 milliseconds. 1876 assertLessThan(sleepTime, totalTimeUsed); 1877 } 1878 1879 @AppModeFull(reason = "No usage events access in instant apps") 1880 @Test testTaskRootEventField()1881 public void testTaskRootEventField() throws Exception { 1882 wakeDevice(); 1883 dismissKeyguard(); // also want to start out with the keyguard dismissed. 1884 1885 final long startTime = System.currentTimeMillis(); 1886 launchSubActivity(TaskRootActivity.class); 1887 final long endTime = System.currentTimeMillis(); 1888 UsageEvents events = mUsageStatsManager.queryEvents(startTime, endTime); 1889 1890 while (events.hasNextEvent()) { 1891 UsageEvents.Event event = new UsageEvents.Event(); 1892 assertTrue(events.getNextEvent(event)); 1893 if (TaskRootActivity.TEST_APP_PKG.equals(event.getPackageName()) 1894 && TaskRootActivity.TEST_APP_CLASS.equals(event.getClassName())) { 1895 assertEquals(mTargetPackage, event.getTaskRootPackageName()); 1896 assertEquals(TaskRootActivity.class.getCanonicalName(), 1897 event.getTaskRootClassName()); 1898 return; 1899 } 1900 } 1901 fail("Did not find nested activity name in usage events"); 1902 } 1903 1904 @AppModeFull(reason = "No usage events access in instant apps") 1905 @Test testUsageSourceAttribution()1906 public void testUsageSourceAttribution() throws Exception { 1907 wakeDevice(); 1908 dismissKeyguard(); // also want to start out with the keyguard dismissed. 1909 mUiDevice.pressHome(); 1910 1911 setUsageSourceSetting(Integer.toString(UsageStatsManager.USAGE_SOURCE_CURRENT_ACTIVITY)); 1912 launchSubActivity(TaskRootActivity.class); 1913 // Usage should be attributed to the test app package 1914 assertAppOrTokenUsed(TaskRootActivity.TEST_APP_PKG, true, TIMEOUT); 1915 1916 SystemUtil.runWithShellPermissionIdentity(() -> mAm.forceStopPackage(TEST_APP_PKG)); 1917 1918 setUsageSourceSetting(Integer.toString(UsageStatsManager.USAGE_SOURCE_TASK_ROOT_ACTIVITY)); 1919 launchSubActivity(TaskRootActivity.class); 1920 // Usage should be attributed to this package 1921 assertAppOrTokenUsed(mTargetPackage, true, TIMEOUT); 1922 } 1923 1924 @AppModeFull(reason = "No usage events access in instant apps") 1925 @Test testTaskRootAttribution_finishingTaskRoot()1926 public void testTaskRootAttribution_finishingTaskRoot() throws Exception { 1927 setUsageSourceSetting(Integer.toString(UsageStatsManager.USAGE_SOURCE_TASK_ROOT_ACTIVITY)); 1928 wakeDevice(); 1929 dismissKeyguard(); // also want to start out with the keyguard dismissed. 1930 1931 launchTestActivity(TEST_APP2_PKG, TEST_APP2_CLASS_FINISHING_TASK_ROOT); 1932 // Wait until the nested activity gets started 1933 mUiDevice.wait(Until.hasObject(By.clazz(TEST_APP_PKG, TEST_APP_CLASS)), TIMEOUT); 1934 1935 // Usage should be attributed to the task root app package 1936 assertAppOrTokenUsed(TEST_APP_PKG, false, TIMEOUT); 1937 assertAppOrTokenUsed(TEST_APP2_PKG, true, TIMEOUT); 1938 SystemUtil.runWithShellPermissionIdentity(() -> mAm.forceStopPackage(TEST_APP_PKG)); 1939 mUiDevice.wait(Until.gone(By.clazz(TEST_APP_PKG, TEST_APP_CLASS)), TIMEOUT); 1940 1941 // Usage should no longer be tracked 1942 assertAppOrTokenUsed(TEST_APP_PKG, false, TIMEOUT); 1943 assertAppOrTokenUsed(TEST_APP2_PKG, false, TIMEOUT); 1944 } 1945 1946 @AppModeInstant 1947 @Test testInstantAppUsageEventsObfuscated()1948 public void testInstantAppUsageEventsObfuscated() throws Exception { 1949 @SuppressWarnings("unchecked") 1950 final Class<? extends Activity>[] activitySequence = new Class[] { 1951 Activities.ActivityOne.class, 1952 Activities.ActivityTwo.class, 1953 Activities.ActivityThree.class, 1954 }; 1955 wakeDevice(); 1956 mUiDevice.pressHome(); 1957 1958 final long startTime = System.currentTimeMillis(); 1959 // Launch the series of Activities. 1960 launchSubActivities(activitySequence); 1961 SystemClock.sleep(250); 1962 1963 final long endTime = System.currentTimeMillis(); 1964 final UsageEvents events = mUsageStatsManager.queryEvents(startTime, endTime); 1965 1966 int resumes = 0; 1967 int pauses = 0; 1968 int stops = 0; 1969 1970 // Only look at events belongs to mTargetPackage. 1971 while (events.hasNextEvent()) { 1972 final UsageEvents.Event event = new UsageEvents.Event(); 1973 assertTrue(events.getNextEvent(event)); 1974 // There should be no events with this packages name 1975 assertNotEquals("Instant app package name found in usage event list", 1976 mTargetPackage, event.getPackageName()); 1977 1978 // Look for the obfuscated instant app string instead 1979 if(UsageEvents.INSTANT_APP_PACKAGE_NAME.equals(event.getPackageName())) { 1980 switch (event.mEventType) { 1981 case Event.ACTIVITY_RESUMED: 1982 resumes++; 1983 break; 1984 case Event.ACTIVITY_PAUSED: 1985 pauses++; 1986 break; 1987 case Event.ACTIVITY_STOPPED: 1988 stops++; 1989 break; 1990 } 1991 } 1992 } 1993 assertEquals("Unexpected number of activity resumes", 3, resumes); 1994 assertEquals("Unexpected number of activity pauses", 2, pauses); 1995 assertEquals("Unexpected number of activity stops", 2, stops); 1996 } 1997 1998 @AppModeFull(reason = "No usage events access in instant apps") 1999 @Test testSuddenDestroy()2000 public void testSuddenDestroy() throws Exception { 2001 wakeDevice(); 2002 dismissKeyguard(); // also want to start out with the keyguard dismissed. 2003 mUiDevice.pressHome(); 2004 2005 final long startTime = System.currentTimeMillis(); 2006 2007 launchTestActivity(TEST_APP_PKG, TEST_APP_CLASS); 2008 SystemClock.sleep(500); 2009 2010 // Destroy the activity 2011 SystemUtil.runWithShellPermissionIdentity(() -> mAm.forceStopPackage(TEST_APP_PKG)); 2012 mUiDevice.wait(Until.gone(By.clazz(TEST_APP_PKG, TEST_APP_CLASS)), TIMEOUT); 2013 SystemClock.sleep(500); 2014 2015 final long endTime = System.currentTimeMillis(); 2016 final UsageEvents events = mUsageStatsManager.queryEvents(startTime, endTime); 2017 2018 int resumes = 0; 2019 int stops = 0; 2020 2021 while (events.hasNextEvent()) { 2022 final UsageEvents.Event event = new UsageEvents.Event(); 2023 assertTrue(events.getNextEvent(event)); 2024 2025 if(TEST_APP_PKG.equals(event.getPackageName())) { 2026 switch (event.mEventType) { 2027 case Event.ACTIVITY_RESUMED: 2028 assertNotNull("ACTIVITY_RESUMED event Task Root should not be null", 2029 event.getTaskRootPackageName()); 2030 resumes++; 2031 break; 2032 case Event.ACTIVITY_STOPPED: 2033 assertNotNull("ACTIVITY_STOPPED event Task Root should not be null", 2034 event.getTaskRootPackageName()); 2035 stops++; 2036 break; 2037 } 2038 } 2039 } 2040 assertEquals("Unexpected number of activity resumes", 1, resumes); 2041 assertEquals("Unexpected number of activity stops", 1, stops); 2042 } 2043 2044 @AppModeFull(reason = "No usage events access in instant apps") 2045 @Test testPipActivity()2046 public void testPipActivity() throws Exception { 2047 assumeTrue("Test cannot run without Picture in Picture support", 2048 mContext.getPackageManager().hasSystemFeature( 2049 PackageManager.FEATURE_PICTURE_IN_PICTURE)); 2050 wakeDevice(); 2051 dismissKeyguard(); // also want to start out with the keyguard dismissed. 2052 mUiDevice.pressHome(); 2053 2054 final long startTime = System.currentTimeMillis(); 2055 2056 launchTestActivity(TEST_APP2_PKG, TEST_APP2_CLASS_PIP); 2057 SystemClock.sleep(500); 2058 2059 // TEST_APP_PKG should take focus, pausing the TEST_APP2_CLASS_PIP activity. 2060 launchTestActivity(TEST_APP_PKG, TEST_APP_CLASS); 2061 SystemClock.sleep(500); 2062 2063 mWMStateHelper.waitForActivityState(TEST_APP2_PIP_COMPONENT, 2064 WindowManagerState.STATE_PAUSED); 2065 2066 mWMStateHelper.assertActivityDisplayed(TEST_APP2_PIP_COMPONENT); 2067 mWMStateHelper.assertNotFocusedActivity("Pip activity should not be in focus", 2068 TEST_APP2_PIP_COMPONENT); 2069 2070 final long endTime = System.currentTimeMillis(); 2071 final UsageEvents events = mUsageStatsManager.queryEvents(startTime, endTime); 2072 2073 int resumes = 0; 2074 int pauses = 0; 2075 int stops = 0; 2076 2077 while (events.hasNextEvent()) { 2078 final UsageEvents.Event event = new UsageEvents.Event(); 2079 assertTrue(events.getNextEvent(event)); 2080 2081 if(TEST_APP2_PKG.equals(event.getPackageName())) { 2082 switch (event.mEventType) { 2083 case Event.ACTIVITY_RESUMED: 2084 assertNotNull("ACTIVITY_RESUMED event Task Root should not be null", 2085 event.getTaskRootPackageName()); 2086 resumes++; 2087 break; 2088 case Event.ACTIVITY_PAUSED: 2089 assertNotNull("ACTIVITY_PAUSED event Task Root should not be null", 2090 event.getTaskRootPackageName()); 2091 pauses++; 2092 break; 2093 case Event.ACTIVITY_STOPPED: 2094 assertNotNull("ACTIVITY_STOPPED event Task Root should not be null", 2095 event.getTaskRootPackageName()); 2096 stops++; 2097 break; 2098 } 2099 } 2100 } 2101 assertEquals("Unexpected number of activity resumes", 1, resumes); 2102 assertEquals("Unexpected number of activity pauses", 1, pauses); 2103 assertEquals("Unexpected number of activity stops", 0, stops); 2104 2105 final Map<String, UsageStats> map = mUsageStatsManager.queryAndAggregateUsageStats( 2106 startTime, endTime); 2107 final UsageStats stats = map.get(TEST_APP2_PKG); 2108 assertNotNull(stats); 2109 final long totalTimeVisible = stats.getTotalTimeVisible(); 2110 assertLessThan(0, totalTimeVisible); 2111 } 2112 2113 @AppModeFull(reason = "No usage events access in instant apps") 2114 @Test testPipActivity_StopToPause()2115 public void testPipActivity_StopToPause() throws Exception { 2116 assumeTrue("Test cannot run without Picture in Picture support", 2117 mContext.getPackageManager().hasSystemFeature( 2118 PackageManager.FEATURE_PICTURE_IN_PICTURE)); 2119 wakeDevice(); 2120 dismissKeyguard(); // also want to start out with the keyguard dismissed. 2121 mUiDevice.pressHome(); 2122 2123 launchTestActivity(TEST_APP2_PKG, TEST_APP2_CLASS_PIP); 2124 SystemClock.sleep(500); 2125 2126 // TEST_APP_PKG should take focus, pausing the TEST_APP2_CLASS_PIP activity. 2127 launchTestActivity(TEST_APP_PKG, TEST_APP_CLASS); 2128 SystemClock.sleep(500); 2129 2130 mWMStateHelper.assertActivityDisplayed(TEST_APP2_PIP_COMPONENT); 2131 mWMStateHelper.assertNotFocusedActivity("Pip activity should not be in focus", 2132 TEST_APP2_PIP_COMPONENT); 2133 2134 // Sleeping the device should cause the Pip activity to stop. 2135 final long sleepTime = System.currentTimeMillis(); 2136 sleepDevice(); 2137 mWMStateHelper.waitForActivityState(TEST_APP2_PIP_COMPONENT, 2138 WindowManagerState.STATE_STOPPED); 2139 2140 // Pip activity stop should show up in UsageStats. 2141 final ArrayList<Event> stoppedEvent = waitForEventCount(STOPPED_EVENT, sleepTime, 1, 2142 TEST_APP2_PKG); 2143 assertEquals(Event.ACTIVITY_STOPPED, stoppedEvent.get(0).getEventType()); 2144 2145 // Waking the device should cause the stopped Pip to return to the paused state. 2146 final long wakeTime = System.currentTimeMillis(); 2147 wakeDevice(); 2148 dismissKeyguard(); 2149 mWMStateHelper.waitForActivityState(TEST_APP2_PIP_COMPONENT, 2150 WindowManagerState.STATE_PAUSED); 2151 2152 mWMStateHelper.assertActivityDisplayed(TEST_APP2_PIP_COMPONENT); 2153 mWMStateHelper.assertNotFocusedActivity("Pip activity should not be in focus", 2154 TEST_APP2_PIP_COMPONENT); 2155 2156 // Sleeping the device should cause the Pip activity to stop again. 2157 final long secondSleepTime = System.currentTimeMillis(); 2158 sleepDevice(); 2159 mWMStateHelper.waitForActivityState(TEST_APP2_PIP_COMPONENT, 2160 WindowManagerState.STATE_STOPPED); 2161 2162 // Pip activity stop should show up in UsageStats again. 2163 final ArrayList<Event> secondStoppedEvent = waitForEventCount(STOPPED_EVENT, 2164 secondSleepTime, 1, 2165 TEST_APP2_PKG); 2166 assertEquals(Event.ACTIVITY_STOPPED, secondStoppedEvent.get(0).getEventType()); 2167 } 2168 2169 @AppModeFull(reason = "No usage events access in instant apps") 2170 @Test 2171 @AsbSecurityTest(cveBugId = 229633537) testReportChooserSelection()2172 public void testReportChooserSelection() throws Exception { 2173 // attempt to report an event with a null package, should fail. 2174 try { 2175 mUsageStatsManager.reportChooserSelection(null, 0, 2176 "text/plain", null, "android.intent.action.SEND"); 2177 fail("Able to report a chooser selection with a null package"); 2178 } catch (IllegalArgumentException expected) { } 2179 2180 // attempt to report an event with a non-existent package, should fail. 2181 long startTime = System.currentTimeMillis(); 2182 mUsageStatsManager.reportChooserSelection("android.app.usage.cts.nonexistent.pkg", 0, 2183 "text/plain", null, "android.intent.action.SEND"); 2184 UsageEvents events = mUsageStatsManager.queryEvents( 2185 startTime - 1000, System.currentTimeMillis() + 1000); 2186 while (events.hasNextEvent()) { 2187 final Event event = new Event(); 2188 events.getNextEvent(event); 2189 if (event.mEventType == Event.CHOOSER_ACTION) { 2190 fail("Able to report a chooser action event with a non-existent package."); 2191 } 2192 } 2193 2194 // attempt to report an event with a null/empty contentType, should fail. 2195 startTime = System.currentTimeMillis(); 2196 mUsageStatsManager.reportChooserSelection(TEST_APP_PKG, 0, 2197 null, null, "android.intent.action.SEND"); 2198 mUsageStatsManager.reportChooserSelection(TEST_APP_PKG, 0, 2199 " ", null, "android.intent.action.SEND"); 2200 events = mUsageStatsManager.queryEvents( 2201 startTime - 1000, System.currentTimeMillis() + 1000); 2202 while (events.hasNextEvent()) { 2203 final Event event = new Event(); 2204 events.getNextEvent(event); 2205 if (event.mEventType == Event.CHOOSER_ACTION) { 2206 fail("Able to report a chooser action event with a null/empty contentType."); 2207 } 2208 } 2209 2210 // attempt to report an event with a null/empty action, should fail. 2211 startTime = System.currentTimeMillis(); 2212 mUsageStatsManager.reportChooserSelection(TEST_APP_PKG, 0, 2213 "text/plain", null, null); 2214 mUsageStatsManager.reportChooserSelection(TEST_APP_PKG, 0, 2215 "text/plain", null, " "); 2216 events = mUsageStatsManager.queryEvents( 2217 startTime - 1000, System.currentTimeMillis() + 1000); 2218 while (events.hasNextEvent()) { 2219 final Event event = new Event(); 2220 events.getNextEvent(event); 2221 if (event.mEventType == Event.CHOOSER_ACTION) { 2222 fail("Able to report a chooser action event with a null/empty action."); 2223 } 2224 } 2225 2226 // report an event with valid args - event should be found. 2227 startTime = System.currentTimeMillis(); 2228 mUsageStatsManager.reportChooserSelection(TEST_APP_PKG, 0, 2229 "text/plain", null, "android.intent.action.SEND"); 2230 Thread.sleep(500); // wait a little for the event to report via the handler. 2231 events = mUsageStatsManager.queryEvents( 2232 startTime - 1000, System.currentTimeMillis() + 1000); 2233 boolean foundEvent = false; 2234 while (events.hasNextEvent()) { 2235 final Event event = new Event(); 2236 events.getNextEvent(event); 2237 if (event.mEventType == Event.CHOOSER_ACTION) { 2238 foundEvent = true; 2239 break; 2240 } 2241 } 2242 assertTrue("Couldn't find the reported chooser action event.", foundEvent); 2243 } 2244 2245 @AppModeFull(reason = "No usage events access in instant apps") 2246 @Test testLocusIdEventsVisibility()2247 public void testLocusIdEventsVisibility() throws Exception { 2248 final long startTime = System.currentTimeMillis(); 2249 startAndDestroyActivityWithLocus(); 2250 final long endTime = System.currentTimeMillis(); 2251 2252 final UsageEvents restrictedEvents = mUsageStatsManager.queryEvents(startTime, endTime); 2253 final UsageEvents allEvents = queryEventsAsShell(startTime, endTime); 2254 verifyLocusIdEventVisibility(restrictedEvents, false); 2255 verifyLocusIdEventVisibility(allEvents, true); 2256 } 2257 startAndDestroyActivityWithLocus()2258 private void startAndDestroyActivityWithLocus() { 2259 launchTestActivity(TEST_APP_PKG, TEST_APP_CLASS_LOCUS); 2260 SystemClock.sleep(500); 2261 2262 // Destroy the activity 2263 SystemUtil.runWithShellPermissionIdentity(() -> mAm.forceStopPackage(TEST_APP_PKG)); 2264 mUiDevice.wait(Until.gone(By.clazz(TEST_APP_PKG, TEST_APP_CLASS_LOCUS)), TIMEOUT); 2265 SystemClock.sleep(500); 2266 } 2267 verifyLocusIdEventVisibility(UsageEvents events, boolean hasPermission)2268 private void verifyLocusIdEventVisibility(UsageEvents events, boolean hasPermission) { 2269 int locuses = 0; 2270 while (events.hasNextEvent()) { 2271 final Event event = new UsageEvents.Event(); 2272 assertTrue(events.getNextEvent(event)); 2273 2274 if (TEST_APP_PKG.equals(event.getPackageName()) 2275 && event.mEventType == Event.LOCUS_ID_SET) { 2276 locuses++; 2277 } 2278 } 2279 2280 if (hasPermission) { 2281 assertEquals("LOCUS_ID_SET events were not visible.", 2, locuses); 2282 } else { 2283 assertEquals("LOCUS_ID_SET events were visible.", 0, locuses); 2284 } 2285 } 2286 2287 /** 2288 * Assert on an app or token's usage state. 2289 * 2290 * @param entity name of the app or token 2291 * @param expected expected usage state, true for in use, false for not in use 2292 */ assertAppOrTokenUsed(String entity, boolean expected, long timeout)2293 private void assertAppOrTokenUsed(String entity, boolean expected, long timeout) 2294 throws IOException { 2295 final long realtimeTimeout = SystemClock.elapsedRealtime() + timeout; 2296 String activeUsages; 2297 boolean found; 2298 do { 2299 activeUsages = executeShellCmd("dumpsys usagestats apptimelimit actives"); 2300 final String[] actives = activeUsages.split("\n"); 2301 found = Arrays.asList(actives).contains(entity); 2302 } while (found != expected && SystemClock.elapsedRealtime() <= realtimeTimeout); 2303 2304 if (expected) { 2305 assertTrue(entity + " not found in list of active activities and tokens\n" 2306 + activeUsages, found); 2307 } else { 2308 assertFalse(entity + " found in list of active activities and tokens\n" 2309 + activeUsages, found); 2310 } 2311 } 2312 dismissKeyguard()2313 private void dismissKeyguard() throws Exception { 2314 if (mKeyguardManager.isKeyguardLocked()) { 2315 final long startTime = getEvents(KEYGUARD_EVENTS, 0, null, null) + 1; 2316 executeShellCmd("wm dismiss-keyguard"); 2317 final ArrayList<Event> events = waitForEventCount(KEYGUARD_EVENTS, startTime, 1); 2318 assertEquals(Event.KEYGUARD_HIDDEN, events.get(0).getEventType()); 2319 SystemClock.sleep(500); 2320 } 2321 } 2322 setStandByBucket(String packageName, String bucket)2323 private void setStandByBucket(String packageName, String bucket) throws IOException { 2324 executeShellCmd("am set-standby-bucket " + packageName + " " + bucket); 2325 } 2326 executeShellCmd(String command)2327 private String executeShellCmd(String command) throws IOException { 2328 return mUiDevice.executeShellCommand(command); 2329 } 2330 queryEventsAsShell(long start, long end)2331 private UsageEvents queryEventsAsShell(long start, long end) { 2332 return SystemUtil.runWithShellPermissionIdentity(() -> 2333 mUsageStatsManager.queryEvents(start, end)); 2334 } 2335 bindToTestService()2336 private ITestReceiver bindToTestService() throws Exception { 2337 final TestServiceConnection connection = bindToTestServiceAndGetConnection(); 2338 return connection.getITestReceiver(); 2339 } 2340 bindToTestServiceAndGetConnection(String packageName)2341 private TestServiceConnection bindToTestServiceAndGetConnection(String packageName) 2342 throws Exception { 2343 final TestServiceConnection connection = new TestServiceConnection(mContext); 2344 final Intent intent = new Intent().setComponent( 2345 new ComponentName(packageName, TEST_APP_CLASS_SERVICE)); 2346 mContext.bindService(intent, connection, Context.BIND_AUTO_CREATE); 2347 return connection; 2348 } 2349 bindToTestServiceAndGetConnection()2350 private TestServiceConnection bindToTestServiceAndGetConnection() throws Exception { 2351 return bindToTestServiceAndGetConnection(TEST_APP_PKG); 2352 } 2353 2354 /** 2355 * Send broadcast to test app's receiver and wait for it to be received. 2356 */ bindToTestBroadcastReceiver()2357 private void bindToTestBroadcastReceiver() { 2358 final Intent intent = new Intent().setComponent( 2359 new ComponentName(TEST_APP_PKG, TEST_APP_CLASS_BROADCAST_RECEIVER)); 2360 CountDownLatch latch = new CountDownLatch(1); 2361 mContext.sendOrderedBroadcast( 2362 intent, 2363 null /* receiverPermission */, 2364 new BroadcastReceiver() { 2365 @Override public void onReceive(Context context, Intent intent) { 2366 latch.countDown(); 2367 } 2368 }, 2369 null /* scheduler */, 2370 Activity.RESULT_OK, 2371 null /* initialData */, 2372 null /* initialExtras */); 2373 try { 2374 assertTrue("Timed out waiting for test broadcast to be received", 2375 latch.await(TIMEOUT, TimeUnit.MILLISECONDS)); 2376 } catch (InterruptedException e) { 2377 throw new IllegalStateException("Interrupted", e); 2378 } 2379 } 2380 2381 /** 2382 * Bind to the test app's content provider. 2383 */ bindToTestContentProvider()2384 private void bindToTestContentProvider() throws Exception { 2385 // Acquire unstable content provider so that test process isn't killed when content 2386 // provider app is killed. 2387 final Uri testUri = Uri.parse(TEST_APP_CONTENT_URI_STRING); 2388 ContentProviderClient client = 2389 mContext.getContentResolver().acquireUnstableContentProviderClient(testUri); 2390 try (Cursor cursor = client.query( 2391 testUri, 2392 null /* projection */, 2393 null /* selection */, 2394 null /* selectionArgs */, 2395 null /* sortOrder */)) { 2396 assertNotNull(cursor); 2397 } 2398 } 2399 2400 static class TestServiceConnection implements ServiceConnection { 2401 private BlockingQueue<IBinder> mBlockingQueue = new LinkedBlockingQueue<>(); 2402 private Context mContext; 2403 TestServiceConnection(Context context)2404 TestServiceConnection(Context context) { 2405 mContext = context; 2406 } 2407 onServiceConnected(ComponentName componentName, IBinder service)2408 public void onServiceConnected(ComponentName componentName, IBinder service) { 2409 mBlockingQueue.offer(service); 2410 } 2411 onServiceDisconnected(ComponentName componentName)2412 public void onServiceDisconnected(ComponentName componentName) { 2413 } 2414 getService()2415 public IBinder getService() throws Exception { 2416 final IBinder service = mBlockingQueue.poll(TIMEOUT_BINDER_SERVICE_SEC, 2417 TimeUnit.SECONDS); 2418 return service; 2419 } 2420 getITestReceiver()2421 public ITestReceiver getITestReceiver() throws Exception { 2422 return ITestReceiver.Stub.asInterface(getService()); 2423 } 2424 unbind()2425 public void unbind() { 2426 mContext.unbindService(this); 2427 } 2428 } 2429 runJobImmediately()2430 private void runJobImmediately() throws Exception { 2431 TestJob.schedule(mContext); 2432 executeShellCmd(JOBSCHEDULER_RUN_SHELL_COMMAND 2433 + " " + mContext.getPackageName() 2434 + " " + TestJob.TEST_JOB_ID); 2435 } 2436 isAppInactiveAsPermissionlessApp(String pkg)2437 private boolean isAppInactiveAsPermissionlessApp(String pkg) throws Exception { 2438 final ITestReceiver testService = bindToTestService(); 2439 return testService.isAppInactive(pkg); 2440 } 2441 createUser(String name)2442 private int createUser(String name) throws Exception { 2443 final String output = executeShellCmd( 2444 "pm create-user " + name); 2445 if (output.startsWith("Success")) { 2446 return mOtherUser = Integer.parseInt(output.substring(output.lastIndexOf(" ")).trim()); 2447 } 2448 throw new IllegalStateException(String.format("Failed to create user: %s", output)); 2449 } 2450 removeUser(final int userId)2451 private boolean removeUser(final int userId) throws Exception { 2452 final String output = executeShellCmd(String.format("pm remove-user %s", userId)); 2453 if (output.startsWith("Error")) { 2454 return false; 2455 } 2456 return true; 2457 } 2458 startUser(int userId, boolean waitFlag)2459 private boolean startUser(int userId, boolean waitFlag) throws Exception { 2460 String cmd = "am start-user " + (waitFlag ? "-w " : "") + userId; 2461 2462 final String output = executeShellCmd(cmd); 2463 if (output.startsWith("Error")) { 2464 return false; 2465 } 2466 if (waitFlag) { 2467 String state = executeShellCmd("am get-started-user-state " + userId); 2468 if (!state.contains("RUNNING_UNLOCKED")) { 2469 return false; 2470 } 2471 } 2472 return true; 2473 } 2474 stopUser(int userId, boolean waitFlag, boolean forceFlag)2475 private boolean stopUser(int userId, boolean waitFlag, boolean forceFlag) 2476 throws Exception { 2477 StringBuilder cmd = new StringBuilder("am stop-user "); 2478 if (waitFlag) { 2479 cmd.append("-w "); 2480 } 2481 if (forceFlag) { 2482 cmd.append("-f "); 2483 } 2484 cmd.append(userId); 2485 2486 final String output = executeShellCmd(cmd.toString()); 2487 if (output.contains("Error: Can't stop system user")) { 2488 return false; 2489 } 2490 return true; 2491 } 2492 installExistingPackageAsUser(String packageName, int userId)2493 private void installExistingPackageAsUser(String packageName, int userId) 2494 throws Exception { 2495 executeShellCmd( 2496 String.format("pm install-existing --user %d --wait %s", userId, packageName)); 2497 } 2498 sleepDevice()2499 private void sleepDevice() throws Exception { 2500 if (mContext.getPackageManager().hasSystemFeature(PackageManager.FEATURE_WATCH)) { 2501 mUiDevice.pressKeyCode(KeyEvent.KEYCODE_SLEEP); 2502 } else { 2503 mUiDevice.sleep(); 2504 } 2505 2506 waitUntil(() -> { 2507 try { 2508 return mUiDevice.isScreenOn(); 2509 } catch (Exception e) { 2510 return true; 2511 } 2512 }, false); 2513 } 2514 wakeDevice()2515 private void wakeDevice() throws Exception { 2516 mUiDevice.wakeUp(); 2517 2518 waitUntil(() -> { 2519 try { 2520 return mUiDevice.isScreenOn(); 2521 } catch (Exception e) { 2522 return false; 2523 } 2524 }, true); 2525 } 2526 } 2527