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