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