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