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 org.junit.Assert.assertEquals; 20 import static org.junit.Assert.assertFalse; 21 import static org.junit.Assert.assertNotEquals; 22 import static org.junit.Assert.assertNotNull; 23 import static org.junit.Assert.assertTrue; 24 import static org.junit.Assert.fail; 25 import static org.junit.Assume.assumeFalse; 26 import static org.junit.Assume.assumeTrue; 27 28 import android.app.Activity; 29 import android.app.ActivityManager; 30 import android.app.AppOpsManager; 31 import android.app.KeyguardManager; 32 import android.app.Notification; 33 import android.app.NotificationChannel; 34 import android.app.NotificationManager; 35 import android.app.PendingIntent; 36 import android.app.usage.EventStats; 37 import android.app.usage.UsageEvents; 38 import android.app.usage.UsageEvents.Event; 39 import android.app.usage.UsageStats; 40 import android.app.usage.UsageStatsManager; 41 import android.content.ComponentName; 42 import android.content.Context; 43 import android.content.Intent; 44 import android.content.ServiceConnection; 45 import android.content.pm.PackageManager; 46 import android.os.IBinder; 47 import android.os.Parcel; 48 import android.os.SystemClock; 49 import android.os.UserHandle; 50 import android.os.UserManager; 51 import android.platform.test.annotations.AppModeFull; 52 import android.platform.test.annotations.AppModeInstant; 53 import android.provider.Settings; 54 import android.server.wm.WindowManagerState; 55 import android.server.wm.WindowManagerStateHelper; 56 import android.support.test.uiautomator.By; 57 import android.support.test.uiautomator.UiDevice; 58 import android.support.test.uiautomator.Until; 59 import android.text.format.DateUtils; 60 import android.util.Log; 61 import android.util.SparseArray; 62 import android.util.SparseLongArray; 63 import android.view.KeyEvent; 64 65 import androidx.test.InstrumentationRegistry; 66 import androidx.test.runner.AndroidJUnit4; 67 68 import com.android.compatibility.common.util.AppStandbyUtils; 69 import com.android.compatibility.common.util.BatteryUtils; 70 import com.android.compatibility.common.util.SystemUtil; 71 72 import org.junit.After; 73 import org.junit.Before; 74 import org.junit.Ignore; 75 import org.junit.Test; 76 import org.junit.runner.RunWith; 77 78 import java.io.IOException; 79 import java.text.MessageFormat; 80 import java.time.Duration; 81 import java.util.ArrayList; 82 import java.util.Arrays; 83 import java.util.List; 84 import java.util.Map; 85 import java.util.concurrent.BlockingQueue; 86 import java.util.concurrent.LinkedBlockingQueue; 87 import java.util.concurrent.TimeUnit; 88 import java.util.function.BooleanSupplier; 89 90 /** 91 * Test the UsageStats API. It is difficult to test the entire surface area 92 * of the API, as a lot of the testing depends on what data is already present 93 * on the device and for how long that data has been aggregating. 94 * 95 * These tests perform simple checks that each interval is of the correct duration, 96 * and that events do appear in the event log. 97 * 98 * Tests to add that are difficult to add now: 99 * - Invoking a device configuration change and then watching for it in the event log. 100 * - Changing the system time and verifying that all data has been correctly shifted 101 * along with the new time. 102 * - Proper eviction of old data. 103 */ 104 @RunWith(AndroidJUnit4.class) 105 public class UsageStatsTest { 106 private static final boolean DEBUG = false; 107 private static final String TAG = "UsageStatsTest"; 108 109 private static final String APPOPS_SET_SHELL_COMMAND = "appops set {0} " + 110 AppOpsManager.OPSTR_GET_USAGE_STATS + " {1}"; 111 112 private static final String GET_SHELL_COMMAND = "settings get global "; 113 114 private static final String SET_SHELL_COMMAND = "settings put global "; 115 116 private static final String DELETE_SHELL_COMMAND = "settings delete global "; 117 118 private static final String JOBSCHEDULER_RUN_SHELL_COMMAND = "cmd jobscheduler run"; 119 120 private static final String TEST_APP_PKG = "android.app.usage.cts.test1"; 121 private static final String TEST_APP_CLASS = "android.app.usage.cts.test1.SomeActivity"; 122 private static final String TEST_APP_CLASS_LOCUS 123 = "android.app.usage.cts.test1.SomeActivityWithLocus"; 124 private static final String TEST_APP_CLASS_SERVICE 125 = "android.app.usage.cts.test1.TestService"; 126 private static final String TEST_APP2_PKG = "android.app.usage.cts.test2"; 127 private static final String TEST_APP2_CLASS_FINISHING_TASK_ROOT = 128 "android.app.usage.cts.test2.FinishingTaskRootActivity"; 129 private static final String TEST_APP2_CLASS_PIP = 130 "android.app.usage.cts.test2.PipActivity"; 131 private static final ComponentName TEST_APP2_PIP_COMPONENT = new ComponentName(TEST_APP2_PKG, 132 TEST_APP2_CLASS_PIP); 133 134 private static final long TIMEOUT = TimeUnit.SECONDS.toMillis(5); 135 private static final long MINUTE = TimeUnit.MINUTES.toMillis(1); 136 private static final long DAY = TimeUnit.DAYS.toMillis(1); 137 private static final long WEEK = 7 * DAY; 138 private static final long MONTH = 30 * DAY; 139 private static final long YEAR = 365 * DAY; 140 private static final long TIME_DIFF_THRESHOLD = 200; 141 private static final String CHANNEL_ID = "my_channel"; 142 143 private static final long TIMEOUT_BINDER_SERVICE_SEC = 2; 144 145 private Context mContext; 146 private UiDevice mUiDevice; 147 private ActivityManager mAm; 148 private UsageStatsManager mUsageStatsManager; 149 private KeyguardManager mKeyguardManager; 150 private String mTargetPackage; 151 private String mCachedUsageSourceSetting; 152 private String mCachedEnableRestrictedBucketSetting; 153 private int mOtherUser; 154 private Context mOtherUserContext; 155 private UsageStatsManager mOtherUsageStats; 156 private WindowManagerStateHelper mWMStateHelper; 157 158 @Before setUp()159 public void setUp() throws Exception { 160 mContext = InstrumentationRegistry.getInstrumentation().getContext(); 161 mUiDevice = UiDevice.getInstance(InstrumentationRegistry.getInstrumentation()); 162 mAm = mContext.getSystemService(ActivityManager.class); 163 mUsageStatsManager = (UsageStatsManager) mContext.getSystemService( 164 Context.USAGE_STATS_SERVICE); 165 mKeyguardManager = mContext.getSystemService(KeyguardManager.class); 166 mTargetPackage = mContext.getPackageName(); 167 168 mWMStateHelper = new WindowManagerStateHelper(); 169 170 assumeTrue("App Standby not enabled on device", AppStandbyUtils.isAppStandbyEnabled()); 171 setAppOpsMode("allow"); 172 mCachedUsageSourceSetting = getSetting(Settings.Global.APP_TIME_LIMIT_USAGE_SOURCE); 173 mCachedEnableRestrictedBucketSetting = getSetting(Settings.Global.ENABLE_RESTRICTED_BUCKET); 174 } 175 176 @After cleanUp()177 public void cleanUp() throws Exception { 178 if (mCachedUsageSourceSetting != null && 179 !mCachedUsageSourceSetting.equals( 180 getSetting(Settings.Global.APP_TIME_LIMIT_USAGE_SOURCE))) { 181 setUsageSourceSetting(mCachedUsageSourceSetting); 182 } 183 setSetting(Settings.Global.ENABLE_RESTRICTED_BUCKET, mCachedEnableRestrictedBucketSetting); 184 // Force stop test package to avoid any running test code from carrying over to the next run 185 SystemUtil.runWithShellPermissionIdentity(() -> mAm.forceStopPackage(TEST_APP_PKG)); 186 SystemUtil.runWithShellPermissionIdentity(() -> mAm.forceStopPackage(TEST_APP2_PKG)); 187 mUiDevice.pressHome(); 188 // Destroy the other user if created 189 if (mOtherUser != 0) { 190 stopUser(mOtherUser, true, true); 191 removeUser(mOtherUser); 192 mOtherUser = 0; 193 } 194 } 195 assertLessThan(long left, long right)196 private static void assertLessThan(long left, long right) { 197 assertTrue("Expected " + left + " to be less than " + right, left < right); 198 } 199 assertLessThanOrEqual(long left, long right)200 private static void assertLessThanOrEqual(long left, long right) { 201 assertTrue("Expected " + left + " to be less than " + right, left <= right); 202 } 203 setAppOpsMode(String mode)204 private void setAppOpsMode(String mode) throws Exception { 205 executeShellCmd(MessageFormat.format(APPOPS_SET_SHELL_COMMAND, mTargetPackage, mode)); 206 } 207 getSetting(String name)208 private String getSetting(String name) throws Exception { 209 return executeShellCmd(GET_SHELL_COMMAND + name); 210 } 211 setSetting(String name, String setting)212 private void setSetting(String name, String setting) throws Exception { 213 if (setting == null || setting.equals("null")) { 214 executeShellCmd(DELETE_SHELL_COMMAND + name); 215 } else { 216 executeShellCmd(SET_SHELL_COMMAND + name + " " + setting); 217 } 218 } 219 setUsageSourceSetting(String value)220 private void setUsageSourceSetting(String value) throws Exception { 221 setSetting(Settings.Global.APP_TIME_LIMIT_USAGE_SOURCE, value); 222 mUsageStatsManager.forceUsageSourceSettingRead(); 223 } 224 launchSubActivity(Class<? extends Activity> clazz)225 private void launchSubActivity(Class<? extends Activity> clazz) { 226 final Intent intent = new Intent(Intent.ACTION_MAIN); 227 intent.setClassName(mTargetPackage, clazz.getName()); 228 intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TASK | Intent.FLAG_ACTIVITY_NEW_TASK); 229 mContext.startActivity(intent); 230 mUiDevice.wait(Until.hasObject(By.clazz(clazz)), TIMEOUT); 231 } 232 createTestActivityIntent(String pkgName, String className)233 private Intent createTestActivityIntent(String pkgName, String className) { 234 final Intent intent = new Intent(); 235 intent.setClassName(pkgName, className); 236 intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK); 237 return intent; 238 } 239 launchTestActivity(String pkgName, String className)240 private void launchTestActivity(String pkgName, String className) { 241 mContext.startActivity(createTestActivityIntent(pkgName, className)); 242 mUiDevice.wait(Until.hasObject(By.clazz(pkgName, className)), TIMEOUT); 243 } 244 launchSubActivities(Class<? extends Activity>[] activityClasses)245 private void launchSubActivities(Class<? extends Activity>[] activityClasses) { 246 for (Class<? extends Activity> clazz : activityClasses) { 247 launchSubActivity(clazz); 248 } 249 } 250 251 @AppModeFull(reason = "No usage events access in instant apps") 252 @Test testLastTimeAnyComponentUsed_launchActivityShouldBeDetected()253 public void testLastTimeAnyComponentUsed_launchActivityShouldBeDetected() throws Exception { 254 mUiDevice.wakeUp(); 255 dismissKeyguard(); // also want to start out with the keyguard dismissed. 256 257 final long startTime = System.currentTimeMillis(); 258 launchSubActivity(Activities.ActivityOne.class); 259 final long endTime = System.currentTimeMillis(); 260 261 verifyLastTimeAnyComponentUsedWithinRange(startTime, endTime, mTargetPackage); 262 } 263 264 @AppModeFull(reason = "No usage events access in instant apps") 265 @Test testLastTimeAnyComponentUsed_bindServiceShouldBeDetected()266 public void testLastTimeAnyComponentUsed_bindServiceShouldBeDetected() throws Exception { 267 mUiDevice.wakeUp(); 268 dismissKeyguard(); // also want to start out with the keyguard dismissed. 269 270 final long startTime = System.currentTimeMillis(); 271 bindToTestService(); 272 final long endTime = System.currentTimeMillis(); 273 274 verifyLastTimeAnyComponentUsedWithinRange(startTime, endTime, TEST_APP_PKG); 275 } 276 verifyLastTimeAnyComponentUsedWithinRange( long startTime, long endTime, String targetPackage)277 private void verifyLastTimeAnyComponentUsedWithinRange( 278 long startTime, long endTime, String targetPackage) { 279 final Map<String, UsageStats> map = mUsageStatsManager.queryAndAggregateUsageStats( 280 startTime, endTime); 281 final UsageStats stats = map.get(targetPackage); 282 assertNotNull(stats); 283 final long lastTimeAnyComponentUsed = stats.getLastTimeAnyComponentUsed(); 284 assertLessThan(startTime, lastTimeAnyComponentUsed); 285 assertLessThan(lastTimeAnyComponentUsed, endTime); 286 287 SystemUtil.runWithShellPermissionIdentity(()-> { 288 final long lastDayAnyComponentUsedGlobal = 289 mUsageStatsManager.getLastTimeAnyComponentUsed(targetPackage) / DAY; 290 assertLessThanOrEqual(startTime / DAY, lastDayAnyComponentUsedGlobal); 291 assertLessThanOrEqual(lastDayAnyComponentUsedGlobal, endTime / DAY); 292 }); 293 } 294 295 @AppModeFull(reason = "No usage events access in instant apps") 296 @Test testLastTimeAnyComponentUsed_JobServiceShouldBeIgnored()297 public void testLastTimeAnyComponentUsed_JobServiceShouldBeIgnored() throws Exception { 298 mUiDevice.wakeUp(); 299 dismissKeyguard(); // also want to start out with the keyguard dismissed. 300 301 final long startTime = System.currentTimeMillis(); 302 runJobImmediately(); 303 waitUntil(TestJob.hasJobStarted, /* expected */ true); 304 305 final Map<String, UsageStats> map = mUsageStatsManager.queryAndAggregateUsageStats( 306 startTime, System.currentTimeMillis()); 307 final UsageStats stats = map.get(mTargetPackage); 308 if (stats != null) { 309 final long lastTimeAnyComponentUsed = stats.getLastTimeAnyComponentUsed(); 310 // Check that the usage is NOT detected. 311 assertLessThanOrEqual(lastTimeAnyComponentUsed, startTime); 312 } 313 314 SystemUtil.runWithShellPermissionIdentity(()-> { 315 final long lastDayAnyComponentUsedGlobal = 316 mUsageStatsManager.getLastTimeAnyComponentUsed(mTargetPackage) / DAY; 317 // Check that the usage is NOT detected. 318 assertLessThanOrEqual(lastDayAnyComponentUsedGlobal, startTime / DAY); 319 }); 320 } 321 322 @AppModeFull(reason = "No usage events access in instant apps") 323 @Test testLastTimeAnyComponentUsedGlobal_withoutPermission()324 public void testLastTimeAnyComponentUsedGlobal_withoutPermission() throws Exception { 325 try{ 326 mUsageStatsManager.getLastTimeAnyComponentUsed(mTargetPackage); 327 fail("Query across users should require INTERACT_ACROSS_USERS permission"); 328 } catch (SecurityException se) { 329 // Expected 330 } 331 } 332 333 @AppModeFull(reason = "No usage events access in instant apps") 334 @Test testOrderedActivityLaunchSequenceInEventLog()335 public void testOrderedActivityLaunchSequenceInEventLog() throws Exception { 336 @SuppressWarnings("unchecked") 337 Class<? extends Activity>[] activitySequence = new Class[] { 338 Activities.ActivityOne.class, 339 Activities.ActivityTwo.class, 340 Activities.ActivityThree.class, 341 }; 342 mUiDevice.wakeUp(); 343 dismissKeyguard(); // also want to start out with the keyguard dismissed. 344 345 final long startTime = System.currentTimeMillis(); 346 // Launch the series of Activities. 347 launchSubActivities(activitySequence); 348 final long endTime = System.currentTimeMillis(); 349 UsageEvents events = mUsageStatsManager.queryEvents(startTime, endTime); 350 351 // Only look at events belongs to mTargetPackage. 352 ArrayList<UsageEvents.Event> eventList = new ArrayList<>(); 353 while (events.hasNextEvent()) { 354 UsageEvents.Event event = new UsageEvents.Event(); 355 assertTrue(events.getNextEvent(event)); 356 if (mTargetPackage.equals(event.getPackageName())) { 357 eventList.add(event); 358 } 359 } 360 361 final int activityCount = activitySequence.length; 362 for (int i = 0; i < activityCount; i++) { 363 String className = activitySequence[i].getName(); 364 ArrayList<UsageEvents.Event> activityEvents = new ArrayList<>(); 365 final int size = eventList.size(); 366 for (int j = 0; j < size; j++) { 367 Event evt = eventList.get(j); 368 if (className.equals(evt.getClassName())) { 369 activityEvents.add(evt); 370 } 371 } 372 // We expect 3 events per Activity launched (ACTIVITY_RESUMED + ACTIVITY_PAUSED 373 // + ACTIVITY_STOPPED) except for the last Activity, which only has 374 // ACTIVITY_RESUMED event. 375 if (i < activityCount - 1) { 376 assertEquals(3, activityEvents.size()); 377 assertEquals(Event.ACTIVITY_RESUMED, activityEvents.get(0).getEventType()); 378 assertEquals(Event.ACTIVITY_PAUSED, activityEvents.get(1).getEventType()); 379 assertEquals(Event.ACTIVITY_STOPPED, activityEvents.get(2).getEventType()); 380 } else { 381 // The last activity 382 assertEquals(1, activityEvents.size()); 383 assertEquals(Event.ACTIVITY_RESUMED, activityEvents.get(0).getEventType()); 384 } 385 } 386 } 387 388 @AppModeFull(reason = "No usage events access in instant apps") 389 @Test testActivityOnBackButton()390 public void testActivityOnBackButton() throws Exception { 391 testActivityOnButton(mUiDevice::pressBack); 392 } 393 394 @AppModeFull(reason = "No usage events access in instant apps") 395 @Test testActivityOnHomeButton()396 public void testActivityOnHomeButton() throws Exception { 397 testActivityOnButton(mUiDevice::pressHome); 398 } 399 testActivityOnButton(Runnable pressButton)400 private void testActivityOnButton(Runnable pressButton) throws Exception { 401 mUiDevice.wakeUp(); 402 final long startTime = System.currentTimeMillis(); 403 final Class clazz = Activities.ActivityOne.class; 404 launchSubActivity(clazz); 405 pressButton.run(); 406 Thread.sleep(1000); 407 final long endTime = System.currentTimeMillis(); 408 UsageEvents events = mUsageStatsManager.queryEvents(startTime, endTime); 409 410 ArrayList<UsageEvents.Event> eventList = new ArrayList<>(); 411 while (events.hasNextEvent()) { 412 UsageEvents.Event event = new UsageEvents.Event(); 413 assertTrue(events.getNextEvent(event)); 414 if (mTargetPackage.equals(event.getPackageName()) 415 && clazz.getName().equals(event.getClassName())) { 416 eventList.add(event); 417 } 418 } 419 assertEquals(3, eventList.size()); 420 assertEquals(Event.ACTIVITY_RESUMED, eventList.get(0).getEventType()); 421 assertEquals(Event.ACTIVITY_PAUSED, eventList.get(1).getEventType()); 422 assertEquals(Event.ACTIVITY_STOPPED, eventList.get(2).getEventType()); 423 } 424 425 @AppModeFull(reason = "No usage events access in instant apps") 426 @Test testAppLaunchCount()427 public void testAppLaunchCount() throws Exception { 428 long endTime = System.currentTimeMillis(); 429 long startTime = endTime - DateUtils.DAY_IN_MILLIS; 430 Map<String,UsageStats> events = mUsageStatsManager.queryAndAggregateUsageStats( 431 startTime, endTime); 432 UsageStats stats = events.get(mTargetPackage); 433 int startingCount = stats.getAppLaunchCount(); 434 launchSubActivity(Activities.ActivityOne.class); 435 launchSubActivity(Activities.ActivityTwo.class); 436 endTime = System.currentTimeMillis(); 437 events = mUsageStatsManager.queryAndAggregateUsageStats( 438 startTime, endTime); 439 stats = events.get(mTargetPackage); 440 assertEquals(startingCount + 1, stats.getAppLaunchCount()); 441 mUiDevice.pressHome(); 442 launchSubActivity(Activities.ActivityOne.class); 443 launchSubActivity(Activities.ActivityTwo.class); 444 launchSubActivity(Activities.ActivityThree.class); 445 endTime = System.currentTimeMillis(); 446 events = mUsageStatsManager.queryAndAggregateUsageStats( 447 startTime, endTime); 448 stats = events.get(mTargetPackage); 449 assertEquals(startingCount + 2, stats.getAppLaunchCount()); 450 } 451 452 @AppModeFull(reason = "No usage events access in instant apps") 453 @Test testStandbyBucketChangeLog()454 public void testStandbyBucketChangeLog() throws Exception { 455 final long startTime = System.currentTimeMillis(); 456 setStandByBucket(mTargetPackage, "rare"); 457 458 final long endTime = System.currentTimeMillis(); 459 UsageEvents events = mUsageStatsManager.queryEvents(startTime - 1_000, endTime + 1_000); 460 461 boolean found = false; 462 // Check all the events. 463 while (events.hasNextEvent()) { 464 UsageEvents.Event event = new UsageEvents.Event(); 465 assertTrue(events.getNextEvent(event)); 466 if (event.getEventType() == UsageEvents.Event.STANDBY_BUCKET_CHANGED) { 467 found |= event.getAppStandbyBucket() == UsageStatsManager.STANDBY_BUCKET_RARE; 468 } 469 } 470 471 assertTrue(found); 472 } 473 474 @Test testGetAppStandbyBuckets()475 public void testGetAppStandbyBuckets() throws Exception { 476 final boolean origValue = AppStandbyUtils.isAppStandbyEnabledAtRuntime(); 477 AppStandbyUtils.setAppStandbyEnabledAtRuntime(true); 478 try { 479 assumeTrue("Skip GetAppStandby test: app standby is disabled.", 480 AppStandbyUtils.isAppStandbyEnabled()); 481 482 setStandByBucket(mTargetPackage, "rare"); 483 Map<String, Integer> bucketMap = mUsageStatsManager.getAppStandbyBuckets(); 484 assertTrue("No bucket data returned", bucketMap.size() > 0); 485 final int bucket = bucketMap.getOrDefault(mTargetPackage, -1); 486 assertEquals("Incorrect bucket returned for " + mTargetPackage, bucket, 487 UsageStatsManager.STANDBY_BUCKET_RARE); 488 } finally { 489 AppStandbyUtils.setAppStandbyEnabledAtRuntime(origValue); 490 } 491 } 492 493 @Test testGetAppStandbyBucket()494 public void testGetAppStandbyBucket() throws Exception { 495 // App should be at least active, since it's running instrumentation tests 496 assertLessThanOrEqual(UsageStatsManager.STANDBY_BUCKET_ACTIVE, 497 mUsageStatsManager.getAppStandbyBucket()); 498 } 499 500 @Test testQueryEventsForSelf()501 public void testQueryEventsForSelf() throws Exception { 502 setAppOpsMode("ignore"); // To ensure permission is not required 503 // Time drifts of 2s are expected inside usage stats 504 final long start = System.currentTimeMillis() - 2_000; 505 setStandByBucket(mTargetPackage, "rare"); 506 Thread.sleep(100); 507 setStandByBucket(mTargetPackage, "working_set"); 508 Thread.sleep(100); 509 final long end = System.currentTimeMillis() + 2_000; 510 final UsageEvents events = mUsageStatsManager.queryEventsForSelf(start, end); 511 long rareTimeStamp = end + 1; // Initializing as rareTimeStamp > workingTimeStamp 512 long workingTimeStamp = start - 1; 513 int numEvents = 0; 514 while (events.hasNextEvent()) { 515 UsageEvents.Event event = new UsageEvents.Event(); 516 assertTrue(events.getNextEvent(event)); 517 numEvents++; 518 assertEquals("Event for a different package", mTargetPackage, event.getPackageName()); 519 if (event.getEventType() == Event.STANDBY_BUCKET_CHANGED) { 520 if (event.getAppStandbyBucket() == UsageStatsManager.STANDBY_BUCKET_RARE) { 521 rareTimeStamp = event.getTimeStamp(); 522 } 523 else if (event.getAppStandbyBucket() == UsageStatsManager 524 .STANDBY_BUCKET_WORKING_SET) { 525 workingTimeStamp = event.getTimeStamp(); 526 } 527 } 528 } 529 assertTrue("Only " + numEvents + " events returned", numEvents >= 2); 530 assertLessThan(rareTimeStamp, workingTimeStamp); 531 } 532 533 /** 534 * We can't run this test because we are unable to change the system time. 535 * It would be nice to add a shell command or other to allow the shell user 536 * to set the time, thereby allowing this test to set the time using the UIAutomator. 537 */ 538 @Ignore 539 @Test ignore_testStatsAreShiftedInTimeWhenSystemTimeChanges()540 public void ignore_testStatsAreShiftedInTimeWhenSystemTimeChanges() throws Exception { 541 launchSubActivity(Activities.ActivityOne.class); 542 launchSubActivity(Activities.ActivityThree.class); 543 544 long endTime = System.currentTimeMillis(); 545 long startTime = endTime - MINUTE; 546 Map<String, UsageStats> statsMap = mUsageStatsManager.queryAndAggregateUsageStats(startTime, 547 endTime); 548 assertFalse(statsMap.isEmpty()); 549 assertTrue(statsMap.containsKey(mTargetPackage)); 550 final UsageStats before = statsMap.get(mTargetPackage); 551 552 SystemClock.setCurrentTimeMillis(System.currentTimeMillis() - (DAY / 2)); 553 try { 554 endTime = System.currentTimeMillis(); 555 startTime = endTime - MINUTE; 556 statsMap = mUsageStatsManager.queryAndAggregateUsageStats(startTime, endTime); 557 assertFalse(statsMap.isEmpty()); 558 assertTrue(statsMap.containsKey(mTargetPackage)); 559 final UsageStats after = statsMap.get(mTargetPackage); 560 assertEquals(before.getPackageName(), after.getPackageName()); 561 562 long diff = before.getFirstTimeStamp() - after.getFirstTimeStamp(); 563 assertLessThan(Math.abs(diff - (DAY / 2)), TIME_DIFF_THRESHOLD); 564 565 assertEquals(before.getLastTimeStamp() - before.getFirstTimeStamp(), 566 after.getLastTimeStamp() - after.getFirstTimeStamp()); 567 assertEquals(before.getLastTimeUsed() - before.getFirstTimeStamp(), 568 after.getLastTimeUsed() - after.getFirstTimeStamp()); 569 assertEquals(before.getTotalTimeInForeground(), after.getTotalTimeInForeground()); 570 } finally { 571 SystemClock.setCurrentTimeMillis(System.currentTimeMillis() + (DAY / 2)); 572 } 573 } 574 575 @Test testUsageEventsParceling()576 public void testUsageEventsParceling() throws Exception { 577 final long startTime = System.currentTimeMillis() - MINUTE; 578 579 // Ensure some data is in the UsageStats log. 580 @SuppressWarnings("unchecked") 581 Class<? extends Activity>[] activityClasses = new Class[] { 582 Activities.ActivityTwo.class, 583 Activities.ActivityOne.class, 584 Activities.ActivityThree.class, 585 }; 586 launchSubActivities(activityClasses); 587 588 final long endTime = System.currentTimeMillis(); 589 UsageEvents events = mUsageStatsManager.queryEvents(startTime, endTime); 590 assertTrue(events.getNextEvent(new UsageEvents.Event())); 591 592 Parcel p = Parcel.obtain(); 593 p.setDataPosition(0); 594 events.writeToParcel(p, 0); 595 p.setDataPosition(0); 596 597 UsageEvents reparceledEvents = UsageEvents.CREATOR.createFromParcel(p); 598 599 UsageEvents.Event e1 = new UsageEvents.Event(); 600 UsageEvents.Event e2 = new UsageEvents.Event(); 601 while (events.hasNextEvent() && reparceledEvents.hasNextEvent()) { 602 events.getNextEvent(e1); 603 reparceledEvents.getNextEvent(e2); 604 assertEquals(e1.getPackageName(), e2.getPackageName()); 605 assertEquals(e1.getClassName(), e2.getClassName()); 606 assertEquals(e1.getConfiguration(), e2.getConfiguration()); 607 assertEquals(e1.getEventType(), e2.getEventType()); 608 assertEquals(e1.getTimeStamp(), e2.getTimeStamp()); 609 } 610 611 assertEquals(events.hasNextEvent(), reparceledEvents.hasNextEvent()); 612 } 613 614 @AppModeFull(reason = "No usage events access in instant apps") 615 @Test testPackageUsageStatsIntervals()616 public void testPackageUsageStatsIntervals() throws Exception { 617 final long beforeTime = System.currentTimeMillis(); 618 619 // Launch an Activity. 620 launchSubActivity(Activities.ActivityFour.class); 621 launchSubActivity(Activities.ActivityThree.class); 622 623 final long endTime = System.currentTimeMillis(); 624 625 final SparseLongArray intervalLengths = new SparseLongArray(); 626 intervalLengths.put(UsageStatsManager.INTERVAL_DAILY, DAY); 627 intervalLengths.put(UsageStatsManager.INTERVAL_WEEKLY, WEEK); 628 intervalLengths.put(UsageStatsManager.INTERVAL_MONTHLY, MONTH); 629 intervalLengths.put(UsageStatsManager.INTERVAL_YEARLY, YEAR); 630 631 final int intervalCount = intervalLengths.size(); 632 for (int i = 0; i < intervalCount; i++) { 633 final int intervalType = intervalLengths.keyAt(i); 634 final long intervalDuration = intervalLengths.valueAt(i); 635 final long startTime = endTime - (2 * intervalDuration); 636 final List<UsageStats> statsList = mUsageStatsManager.queryUsageStats(intervalType, 637 startTime, endTime); 638 assertFalse(statsList.isEmpty()); 639 640 boolean foundPackage = false; 641 for (UsageStats stats : statsList) { 642 // Verify that each period is a day long. 643 assertLessThanOrEqual(stats.getLastTimeStamp() - stats.getFirstTimeStamp(), 644 intervalDuration); 645 if (stats.getPackageName().equals(mTargetPackage) && 646 stats.getLastTimeUsed() >= beforeTime - TIME_DIFF_THRESHOLD) { 647 foundPackage = true; 648 } 649 } 650 651 assertTrue("Did not find package " + mTargetPackage + " in interval " + intervalType, 652 foundPackage); 653 } 654 } 655 656 @Test testNoAccessSilentlyFails()657 public void testNoAccessSilentlyFails() throws Exception { 658 final long startTime = System.currentTimeMillis() - MINUTE; 659 660 launchSubActivity(android.app.usage.cts.Activities.ActivityOne.class); 661 launchSubActivity(android.app.usage.cts.Activities.ActivityThree.class); 662 663 final long endTime = System.currentTimeMillis(); 664 List<UsageStats> stats = mUsageStatsManager.queryUsageStats(UsageStatsManager.INTERVAL_BEST, 665 startTime, endTime); 666 assertFalse(stats.isEmpty()); 667 668 // We set the mode to ignore because our package has the PACKAGE_USAGE_STATS permission, 669 // and default would allow in this case. 670 setAppOpsMode("ignore"); 671 672 stats = mUsageStatsManager.queryUsageStats(UsageStatsManager.INTERVAL_BEST, 673 startTime, endTime); 674 assertTrue(stats.isEmpty()); 675 } 676 generateAndSendNotification()677 private void generateAndSendNotification() throws Exception { 678 final NotificationManager mNotificationManager = 679 (NotificationManager) mContext.getSystemService(Context.NOTIFICATION_SERVICE); 680 final NotificationChannel mChannel = new NotificationChannel(CHANNEL_ID, "Channel", 681 NotificationManager.IMPORTANCE_DEFAULT); 682 // Configure the notification channel. 683 mChannel.setDescription("Test channel"); 684 mNotificationManager.createNotificationChannel(mChannel); 685 final Notification.Builder mBuilder = 686 new Notification.Builder(mContext, CHANNEL_ID) 687 .setSmallIcon(R.drawable.ic_notification) 688 .setContentTitle("My notification") 689 .setContentText("Hello World!"); 690 final PendingIntent pi = PendingIntent.getActivity(mContext, 1, 691 new Intent(Settings.ACTION_SETTINGS), PendingIntent.FLAG_IMMUTABLE); 692 mBuilder.setContentIntent(pi); 693 mNotificationManager.notify(1, mBuilder.build()); 694 Thread.sleep(500); 695 } 696 697 @AppModeFull(reason = "No usage events access in instant apps") 698 @Test testNotificationSeen()699 public void testNotificationSeen() throws Exception { 700 final long startTime = System.currentTimeMillis(); 701 702 // Skip the test for wearable devices, televisions and automotives; neither has 703 // a notification shade, as notifications are shown via a different path than phones 704 assumeFalse("Test cannot run on a watch- notification shade is not shown", 705 mContext.getPackageManager().hasSystemFeature(PackageManager.FEATURE_WATCH)); 706 assumeFalse("Test cannot run on a television- notifications are not shown", 707 mContext.getPackageManager().hasSystemFeature( 708 PackageManager.FEATURE_LEANBACK_ONLY)); 709 assumeFalse("Test cannot run on an automotive - notification shade is not shown", 710 mContext.getPackageManager().hasSystemFeature(PackageManager.FEATURE_AUTOMOTIVE)); 711 712 generateAndSendNotification(); 713 714 long endTime = System.currentTimeMillis(); 715 UsageEvents events = queryEventsAsShell(startTime, endTime); 716 boolean found = false; 717 Event event = new Event(); 718 while (events.hasNextEvent()) { 719 events.getNextEvent(event); 720 if (event.getEventType() == Event.NOTIFICATION_SEEN) { 721 found = true; 722 } 723 } 724 assertFalse(found); 725 // Pull down shade 726 mUiDevice.openNotification(); 727 outer: 728 for (int i = 0; i < 5; i++) { 729 Thread.sleep(500); 730 endTime = System.currentTimeMillis(); 731 events = queryEventsAsShell(startTime, endTime); 732 found = false; 733 while (events.hasNextEvent()) { 734 events.getNextEvent(event); 735 if (event.getEventType() == Event.NOTIFICATION_SEEN) { 736 found = true; 737 break outer; 738 } 739 } 740 } 741 assertTrue(found); 742 mUiDevice.pressBack(); 743 } 744 745 @AppModeFull(reason = "No usage events access in instant apps") 746 @Test testNotificationInterruptionEventsObfuscation()747 public void testNotificationInterruptionEventsObfuscation() throws Exception { 748 final long startTime = System.currentTimeMillis(); 749 750 // Skip the test for wearable devices and televisions; neither has a notification shade. 751 assumeFalse("Test cannot run on a watch- notification shade is not shown", 752 mContext.getPackageManager().hasSystemFeature(PackageManager.FEATURE_WATCH)); 753 assumeFalse("Test cannot run on a television- notifications are not shown", 754 mContext.getPackageManager().hasSystemFeature( 755 PackageManager.FEATURE_LEANBACK_ONLY)); 756 757 generateAndSendNotification(); 758 final long endTime = System.currentTimeMillis(); 759 760 final UsageEvents obfuscatedEvents = mUsageStatsManager.queryEvents(startTime, endTime); 761 final UsageEvents unobfuscatedEvents = queryEventsAsShell(startTime, endTime); 762 verifyNotificationInterruptionEvent(obfuscatedEvents, true); 763 verifyNotificationInterruptionEvent(unobfuscatedEvents, false); 764 } 765 verifyNotificationInterruptionEvent(UsageEvents events, boolean obfuscated)766 private void verifyNotificationInterruptionEvent(UsageEvents events, boolean obfuscated) { 767 boolean found = false; 768 Event event = new Event(); 769 while (events.hasNextEvent()) { 770 events.getNextEvent(event); 771 if (event.getEventType() == Event.NOTIFICATION_INTERRUPTION) { 772 found = true; 773 break; 774 } 775 } 776 assertTrue(found); 777 if (obfuscated) { 778 assertEquals("Notification channel id was not obfuscated.", 779 UsageEvents.OBFUSCATED_NOTIFICATION_CHANNEL_ID, event.mNotificationChannelId); 780 } else { 781 assertEquals("Failed to verify notification channel id.", 782 CHANNEL_ID, event.mNotificationChannelId); 783 } 784 } 785 786 @AppModeFull(reason = "No usage events access in instant apps") 787 @Test testUserUnlockedEventExists()788 public void testUserUnlockedEventExists() throws Exception { 789 final UsageEvents events = mUsageStatsManager.queryEvents(0, System.currentTimeMillis()); 790 while (events.hasNextEvent()) { 791 final Event event = new Event(); 792 events.getNextEvent(event); 793 if (event.mEventType == Event.USER_UNLOCKED) { 794 return; 795 } 796 } 797 fail("Couldn't find a user unlocked event."); 798 } 799 800 @AppModeFull(reason = "No usage stats access in instant apps") 801 @Test testCrossUserQuery_withPermission()802 public void testCrossUserQuery_withPermission() throws Exception { 803 assumeTrue(UserManager.supportsMultipleUsers()); 804 final long startTime = System.currentTimeMillis(); 805 // Create user 806 final int userId = createUser("Test User"); 807 startUser(userId, true); 808 installExistingPackageAsUser(mContext.getPackageName(), userId); 809 810 // Query as Shell 811 SystemUtil.runWithShellPermissionIdentity(() -> { 812 final UserHandle otherUser = UserHandle.of(userId); 813 final Context userContext = mContext.createContextAsUser(otherUser, 0); 814 815 final UsageStatsManager usmOther = userContext.getSystemService( 816 UsageStatsManager.class); 817 818 waitUntil(() -> { 819 final List<UsageStats> stats = usmOther.queryUsageStats( 820 UsageStatsManager.INTERVAL_DAILY, startTime, System.currentTimeMillis()); 821 return stats.isEmpty(); 822 }, false); 823 }); 824 // user cleanup done in @After 825 } 826 827 @AppModeFull(reason = "No usage stats access in instant apps") 828 @Test testCrossUserQuery_withoutPermission()829 public void testCrossUserQuery_withoutPermission() throws Exception { 830 assumeTrue(UserManager.supportsMultipleUsers()); 831 final long startTime = System.currentTimeMillis(); 832 // Create user 833 final int userId = createUser("Test User"); 834 startUser(userId, true); 835 installExistingPackageAsUser(mContext.getPackageName(), userId); 836 837 SystemUtil.runWithShellPermissionIdentity(() -> { 838 mOtherUserContext = mContext.createContextAsUser(UserHandle.of(userId), 0); 839 mOtherUsageStats = mOtherUserContext.getSystemService(UsageStatsManager.class); 840 }); 841 842 try { 843 mOtherUsageStats.queryUsageStats(UsageStatsManager.INTERVAL_DAILY, startTime, 844 System.currentTimeMillis()); 845 fail("Query across users should require INTERACT_ACROSS_USERS permission"); 846 } catch (SecurityException se) { 847 // Expected 848 } 849 850 // user cleanup done in @After 851 } 852 853 // TODO(148887416): get this test to work for instant apps 854 @AppModeFull(reason = "Test APK Activity not found when installed as an instant app") 855 @Test testUserForceIntoRestricted()856 public void testUserForceIntoRestricted() throws Exception { 857 setSetting(Settings.Global.ENABLE_RESTRICTED_BUCKET, "1"); 858 859 launchSubActivity(TaskRootActivity.class); 860 assertEquals("Activity launch didn't bring app up to ACTIVE bucket", 861 UsageStatsManager.STANDBY_BUCKET_ACTIVE, 862 mUsageStatsManager.getAppStandbyBucket(mTargetPackage)); 863 864 // User force shouldn't have to deal with the timeout. 865 setStandByBucket(mTargetPackage, "restricted"); 866 assertEquals("User was unable to force an ACTIVE app down into RESTRICTED bucket", 867 UsageStatsManager.STANDBY_BUCKET_RESTRICTED, 868 mUsageStatsManager.getAppStandbyBucket(mTargetPackage)); 869 870 } 871 872 // TODO(148887416): get this test to work for instant apps 873 @AppModeFull(reason = "Test APK Activity not found when installed as an instant app") 874 @Test testUserForceIntoRestricted_BucketDisabled()875 public void testUserForceIntoRestricted_BucketDisabled() throws Exception { 876 setSetting(Settings.Global.ENABLE_RESTRICTED_BUCKET, "0"); 877 878 launchSubActivity(TaskRootActivity.class); 879 assertEquals("Activity launch didn't bring app up to ACTIVE bucket", 880 UsageStatsManager.STANDBY_BUCKET_ACTIVE, 881 mUsageStatsManager.getAppStandbyBucket(mTargetPackage)); 882 883 // User force shouldn't have to deal with the timeout. 884 setStandByBucket(mTargetPackage, "restricted"); 885 assertNotEquals("User was able to force into RESTRICTED bucket when bucket disabled", 886 UsageStatsManager.STANDBY_BUCKET_RESTRICTED, 887 mUsageStatsManager.getAppStandbyBucket(mTargetPackage)); 888 889 } 890 891 // TODO(148887416): get this test to work for instant apps 892 @AppModeFull(reason = "Test APK Activity not found when installed as an instant app") 893 @Test testUserLaunchRemovesFromRestricted()894 public void testUserLaunchRemovesFromRestricted() throws Exception { 895 setSetting(Settings.Global.ENABLE_RESTRICTED_BUCKET, "1"); 896 897 setStandByBucket(mTargetPackage, "restricted"); 898 assertEquals("User was unable to force an app into RESTRICTED bucket", 899 UsageStatsManager.STANDBY_BUCKET_RESTRICTED, 900 mUsageStatsManager.getAppStandbyBucket(mTargetPackage)); 901 902 launchSubActivity(TaskRootActivity.class); 903 assertEquals("Activity launch didn't bring RESTRICTED app into ACTIVE bucket", 904 UsageStatsManager.STANDBY_BUCKET_ACTIVE, 905 mUsageStatsManager.getAppStandbyBucket(mTargetPackage)); 906 } 907 908 // TODO(148887416): get this test to work for instant apps 909 @AppModeFull(reason = "Test APK Activity not found when installed as an instant app") 910 @Test testIsAppInactive()911 public void testIsAppInactive() throws Exception { 912 setStandByBucket(mTargetPackage, "rare"); 913 914 try { 915 BatteryUtils.runDumpsysBatteryUnplug(); 916 917 waitUntil(() -> mUsageStatsManager.isAppInactive(mTargetPackage), true); 918 assertFalse( 919 "App without PACKAGE_USAGE_STATS permission should always receive false for " 920 + "isAppInactive", 921 isAppInactiveAsPermissionlessApp(mTargetPackage)); 922 923 launchSubActivity(Activities.ActivityOne.class); 924 925 waitUntil(() -> mUsageStatsManager.isAppInactive(mTargetPackage), false); 926 assertFalse( 927 "App without PACKAGE_USAGE_STATS permission should always receive false for " 928 + "isAppInactive", 929 isAppInactiveAsPermissionlessApp(mTargetPackage)); 930 931 mUiDevice.pressHome(); 932 setStandByBucket(TEST_APP_PKG, "rare"); 933 // Querying for self does not require the PACKAGE_USAGE_STATS 934 waitUntil(() -> mUsageStatsManager.isAppInactive(TEST_APP_PKG), true); 935 assertTrue( 936 "App without PACKAGE_USAGE_STATS permission should be able to call " 937 + "isAppInactive for itself", 938 isAppInactiveAsPermissionlessApp(TEST_APP_PKG)); 939 940 launchTestActivity(TEST_APP_PKG, TEST_APP_CLASS); 941 942 waitUntil(() -> mUsageStatsManager.isAppInactive(TEST_APP_PKG), false); 943 assertFalse( 944 "App without PACKAGE_USAGE_STATS permission should be able to call " 945 + "isAppInactive for itself", 946 isAppInactiveAsPermissionlessApp(TEST_APP_PKG)); 947 948 } finally { 949 BatteryUtils.runDumpsysBatteryReset(); 950 } 951 } 952 953 // TODO(148887416): get this test to work for instant apps 954 @AppModeFull(reason = "Test APK Activity not found when installed as an instant app") 955 @Test testIsAppInactive_Charging()956 public void testIsAppInactive_Charging() throws Exception { 957 setStandByBucket(TEST_APP_PKG, "rare"); 958 959 try { 960 BatteryUtils.runDumpsysBatteryUnplug(); 961 // Plug/unplug change takes a while to propagate inside the system. 962 waitUntil(() -> mUsageStatsManager.isAppInactive(TEST_APP_PKG), true); 963 964 BatteryUtils.runDumpsysBatterySetPluggedIn(true); 965 BatteryUtils.runDumpsysBatterySetLevel(100); 966 // Plug/unplug change takes a while to propagate inside the system. 967 waitUntil(() -> mUsageStatsManager.isAppInactive(TEST_APP_PKG), false); 968 } finally { 969 BatteryUtils.runDumpsysBatteryReset(); 970 } 971 } 972 973 private static final int[] INTERACTIVE_EVENTS = new int[] { 974 Event.SCREEN_INTERACTIVE, 975 Event.SCREEN_NON_INTERACTIVE 976 }; 977 978 private static final int[] KEYGUARD_EVENTS = new int[] { 979 Event.KEYGUARD_SHOWN, 980 Event.KEYGUARD_HIDDEN 981 }; 982 983 private static final int[] ALL_EVENTS = new int[] { 984 Event.SCREEN_INTERACTIVE, 985 Event.SCREEN_NON_INTERACTIVE, 986 Event.KEYGUARD_SHOWN, 987 Event.KEYGUARD_HIDDEN 988 }; 989 990 private static final int[] PAUSED_EVENT = new int[] { 991 Event.ACTIVITY_PAUSED 992 }; 993 994 private static final int[] STOPPED_EVENT = new int[] { 995 Event.ACTIVITY_STOPPED 996 }; 997 getEvents(int[] whichEvents, long startTime, List<Event> out, String packageName)998 private long getEvents(int[] whichEvents, long startTime, List<Event> out, String packageName) { 999 final long endTime = System.currentTimeMillis(); 1000 if (DEBUG) { 1001 Log.i(TAG, "Looking for events " + Arrays.toString(whichEvents) 1002 + " between " + startTime + " and " + endTime); 1003 } 1004 UsageEvents events = mUsageStatsManager.queryEvents(startTime, endTime); 1005 1006 long latestTime = 0; 1007 1008 // Find events. 1009 while (events.hasNextEvent()) { 1010 UsageEvents.Event event = new UsageEvents.Event(); 1011 assertTrue(events.getNextEvent(event)); 1012 final int ev = event.getEventType(); 1013 for (int which : whichEvents) { 1014 if (ev == which) { 1015 if (packageName != null && !packageName.equals(event.getPackageName())) { 1016 break; 1017 } 1018 1019 if (out != null) { 1020 out.add(event); 1021 } 1022 if (DEBUG) Log.i(TAG, "Next event type " + event.getEventType() 1023 + " time=" + event.getTimeStamp()); 1024 if (latestTime < event.getTimeStamp()) { 1025 latestTime = event.getTimeStamp(); 1026 } 1027 break; 1028 } 1029 } 1030 } 1031 1032 return latestTime; 1033 } 1034 1035 waitForEventCount(int[] whichEvents, long startTime, int count)1036 private ArrayList<Event> waitForEventCount(int[] whichEvents, long startTime, int count) { 1037 return waitForEventCount(whichEvents, startTime, count, null); 1038 } 1039 waitForEventCount(int[] whichEvents, long startTime, int count, String packageName)1040 private ArrayList<Event> waitForEventCount(int[] whichEvents, long startTime, int count, 1041 String packageName) { 1042 final ArrayList<Event> events = new ArrayList<>(); 1043 final long endTime = SystemClock.uptimeMillis() + TIMEOUT; 1044 do { 1045 events.clear(); 1046 getEvents(whichEvents, startTime, events, packageName); 1047 if (events.size() == count) { 1048 return events; 1049 } 1050 if (events.size() > count) { 1051 fail("Found too many events: got " + events.size() + ", expected " + count); 1052 return events; 1053 } 1054 SystemClock.sleep(10); 1055 } while (SystemClock.uptimeMillis() < endTime); 1056 1057 fail("Timed out waiting for " + count + " events, only reached " + events.size()); 1058 return events; 1059 } 1060 waitUntil(BooleanSupplier condition, boolean expected)1061 private void waitUntil(BooleanSupplier condition, boolean expected) throws Exception { 1062 final long sleepTimeMs = 500; 1063 final int count = 10; 1064 for (int i = 0; i < count; ++i) { 1065 if (condition.getAsBoolean() == expected) { 1066 return; 1067 } 1068 Thread.sleep(sleepTimeMs); 1069 } 1070 fail("Condition wasn't satisfied after " + (sleepTimeMs * count) + "ms"); 1071 } 1072 1073 static class AggrEventData { 1074 final String label; 1075 int count; 1076 long duration; 1077 long lastEventTime; 1078 AggrEventData(String label)1079 AggrEventData(String label) { 1080 this.label = label; 1081 } 1082 } 1083 1084 static class AggrAllEventsData { 1085 final AggrEventData interactive = new AggrEventData("Interactive"); 1086 final AggrEventData nonInteractive = new AggrEventData("Non-interactive"); 1087 final AggrEventData keyguardShown = new AggrEventData("Keyguard shown"); 1088 final AggrEventData keyguardHidden = new AggrEventData("Keyguard hidden"); 1089 } 1090 getAggrEventData()1091 private SparseArray<AggrAllEventsData> getAggrEventData() { 1092 final long endTime = System.currentTimeMillis(); 1093 1094 final SparseLongArray intervalLengths = new SparseLongArray(); 1095 intervalLengths.put(UsageStatsManager.INTERVAL_DAILY, DAY); 1096 intervalLengths.put(UsageStatsManager.INTERVAL_WEEKLY, WEEK); 1097 intervalLengths.put(UsageStatsManager.INTERVAL_MONTHLY, MONTH); 1098 intervalLengths.put(UsageStatsManager.INTERVAL_YEARLY, YEAR); 1099 1100 final SparseArray<AggrAllEventsData> allAggr = new SparseArray<>(); 1101 1102 final int intervalCount = intervalLengths.size(); 1103 for (int i = 0; i < intervalCount; i++) { 1104 final int intervalType = intervalLengths.keyAt(i); 1105 final long intervalDuration = intervalLengths.valueAt(i); 1106 final long startTime = endTime - (2 * intervalDuration); 1107 List<EventStats> statsList = mUsageStatsManager.queryEventStats(intervalType, 1108 startTime, endTime); 1109 assertFalse(statsList.isEmpty()); 1110 1111 final AggrAllEventsData aggr = new AggrAllEventsData(); 1112 allAggr.put(intervalType, aggr); 1113 1114 boolean foundEvent = false; 1115 for (EventStats stats : statsList) { 1116 // Verify that each period is a day long. 1117 //assertLessThanOrEqual(stats.getLastTimeStamp() - stats.getFirstTimeStamp(), 1118 // intervalDuration); 1119 AggrEventData data = null; 1120 switch (stats.getEventType()) { 1121 case Event.SCREEN_INTERACTIVE: 1122 data = aggr.interactive; 1123 break; 1124 case Event.SCREEN_NON_INTERACTIVE: 1125 data = aggr.nonInteractive; 1126 break; 1127 case Event.KEYGUARD_HIDDEN: 1128 data = aggr.keyguardHidden; 1129 break; 1130 case Event.KEYGUARD_SHOWN: 1131 data = aggr.keyguardShown; 1132 break; 1133 } 1134 if (data != null) { 1135 foundEvent = true; 1136 data.count += stats.getCount(); 1137 data.duration += stats.getTotalTime(); 1138 if (data.lastEventTime < stats.getLastEventTime()) { 1139 data.lastEventTime = stats.getLastEventTime(); 1140 } 1141 } 1142 } 1143 1144 assertTrue("Did not find event data in interval " + intervalType, 1145 foundEvent); 1146 } 1147 1148 return allAggr; 1149 } 1150 verifyCount(int oldCount, int newCount, boolean larger, String label, int interval)1151 private void verifyCount(int oldCount, int newCount, boolean larger, String label, 1152 int interval) { 1153 if (larger) { 1154 if (newCount <= oldCount) { 1155 fail(label + " count newer " + newCount 1156 + " expected to be larger than older " + oldCount 1157 + " @ interval " + interval); 1158 } 1159 } else { 1160 if (newCount != oldCount) { 1161 fail(label + " count newer " + newCount 1162 + " expected to be same as older " + oldCount 1163 + " @ interval " + interval); 1164 } 1165 } 1166 } 1167 verifyDuration(long oldDur, long newDur, boolean larger, String label, int interval)1168 private void verifyDuration(long oldDur, long newDur, boolean larger, String label, 1169 int interval) { 1170 if (larger) { 1171 if (newDur <= oldDur) { 1172 fail(label + " duration newer " + newDur 1173 + " expected to be larger than older " + oldDur 1174 + " @ interval " + interval); 1175 } 1176 } else { 1177 if (newDur != oldDur) { 1178 fail(label + " duration newer " + newDur 1179 + " expected to be same as older " + oldDur 1180 + " @ interval " + interval); 1181 } 1182 } 1183 } 1184 verifyAggrEventData(AggrEventData older, AggrEventData newer, boolean countLarger, boolean durationLarger, int interval)1185 private void verifyAggrEventData(AggrEventData older, AggrEventData newer, 1186 boolean countLarger, boolean durationLarger, int interval) { 1187 verifyCount(older.count, newer.count, countLarger, older.label, interval); 1188 verifyDuration(older.duration, newer.duration, durationLarger, older.label, interval); 1189 } 1190 verifyAggrInteractiveEventData(SparseArray<AggrAllEventsData> older, SparseArray<AggrAllEventsData> newer, boolean interactiveLarger, boolean nonInteractiveLarger)1191 private void verifyAggrInteractiveEventData(SparseArray<AggrAllEventsData> older, 1192 SparseArray<AggrAllEventsData> newer, boolean interactiveLarger, 1193 boolean nonInteractiveLarger) { 1194 for (int i = 0; i < older.size(); i++) { 1195 AggrAllEventsData o = older.valueAt(i); 1196 AggrAllEventsData n = newer.valueAt(i); 1197 // When we are told something is larger, that means we have transitioned 1198 // *out* of that state -- so the duration of that state is expected to 1199 // increase, but the count should stay the same (and the count of the state 1200 // we transition to is increased). 1201 final int interval = older.keyAt(i); 1202 verifyAggrEventData(o.interactive, n.interactive, nonInteractiveLarger, 1203 interactiveLarger, interval); 1204 verifyAggrEventData(o.nonInteractive, n.nonInteractive, interactiveLarger, 1205 nonInteractiveLarger, interval); 1206 } 1207 } 1208 verifyAggrKeyguardEventData(SparseArray<AggrAllEventsData> older, SparseArray<AggrAllEventsData> newer, boolean hiddenLarger, boolean shownLarger)1209 private void verifyAggrKeyguardEventData(SparseArray<AggrAllEventsData> older, 1210 SparseArray<AggrAllEventsData> newer, boolean hiddenLarger, 1211 boolean shownLarger) { 1212 for (int i = 0; i < older.size(); i++) { 1213 AggrAllEventsData o = older.valueAt(i); 1214 AggrAllEventsData n = newer.valueAt(i); 1215 // When we are told something is larger, that means we have transitioned 1216 // *out* of that state -- so the duration of that state is expected to 1217 // increase, but the count should stay the same (and the count of the state 1218 // we transition to is increased). 1219 final int interval = older.keyAt(i); 1220 verifyAggrEventData(o.keyguardHidden, n.keyguardHidden, shownLarger, 1221 hiddenLarger, interval); 1222 verifyAggrEventData(o.keyguardShown, n.keyguardShown, hiddenLarger, 1223 shownLarger, interval); 1224 } 1225 } 1226 1227 @AppModeFull(reason = "No usage events access in instant apps") 1228 @Test testInteractiveEvents()1229 public void testInteractiveEvents() throws Exception { 1230 // We need to start out with the screen on. 1231 mUiDevice.wakeUp(); 1232 dismissKeyguard(); // also want to start out with the keyguard dismissed. 1233 1234 try { 1235 ArrayList<Event> events; 1236 1237 // Determine time to start looking for events. 1238 final long startTime = getEvents(ALL_EVENTS, 0, null, null) + 1; 1239 SparseArray<AggrAllEventsData> baseAggr = getAggrEventData(); 1240 1241 // First test -- put device to sleep and make sure we see this event. 1242 sleepDevice(); 1243 1244 // Do we have one event, going in to non-interactive mode? 1245 events = waitForEventCount(INTERACTIVE_EVENTS, startTime, 1); 1246 assertEquals(Event.SCREEN_NON_INTERACTIVE, events.get(0).getEventType()); 1247 SparseArray<AggrAllEventsData> offAggr = getAggrEventData(); 1248 verifyAggrInteractiveEventData(baseAggr, offAggr, true, false); 1249 1250 // Next test -- turn screen on and make sure we have a second event. 1251 // XXX need to wait a bit so we don't accidentally trigger double-power 1252 // to launch camera. (SHOULD FIX HOW WE WAKEUP / SLEEP TO NOT USE POWER KEY) 1253 SystemClock.sleep(500); 1254 mUiDevice.wakeUp(); 1255 events = waitForEventCount(INTERACTIVE_EVENTS, startTime, 2); 1256 assertEquals(Event.SCREEN_NON_INTERACTIVE, events.get(0).getEventType()); 1257 assertEquals(Event.SCREEN_INTERACTIVE, events.get(1).getEventType()); 1258 SparseArray<AggrAllEventsData> onAggr = getAggrEventData(); 1259 verifyAggrInteractiveEventData(offAggr, onAggr, false, true); 1260 1261 // If the device is doing a lock screen, verify that we are also seeing the 1262 // appropriate keyguard behavior. We don't know the timing from when the screen 1263 // will go off until the keyguard is shown, so we will do this all after turning 1264 // the screen back on (at which point it must be shown). 1265 // XXX CTS seems to be preventing the keyguard from showing, so this path is 1266 // never being tested. 1267 if (mKeyguardManager.isKeyguardLocked()) { 1268 events = waitForEventCount(KEYGUARD_EVENTS, startTime, 1); 1269 assertEquals(Event.KEYGUARD_SHOWN, events.get(0).getEventType()); 1270 SparseArray<AggrAllEventsData> shownAggr = getAggrEventData(); 1271 verifyAggrKeyguardEventData(offAggr, shownAggr, true, false); 1272 1273 // Now dismiss the keyguard and verify the resulting events. 1274 executeShellCmd("wm dismiss-keyguard"); 1275 events = waitForEventCount(KEYGUARD_EVENTS, startTime, 2); 1276 assertEquals(Event.KEYGUARD_SHOWN, events.get(0).getEventType()); 1277 assertEquals(Event.KEYGUARD_HIDDEN, events.get(1).getEventType()); 1278 SparseArray<AggrAllEventsData> hiddenAggr = getAggrEventData(); 1279 verifyAggrKeyguardEventData(shownAggr, hiddenAggr, false, true); 1280 } 1281 1282 } finally { 1283 // Dismiss keyguard to get device back in its normal state. 1284 mUiDevice.wakeUp(); 1285 executeShellCmd("wm dismiss-keyguard"); 1286 } 1287 } 1288 1289 @Test testIgnoreNonexistentPackage()1290 public void testIgnoreNonexistentPackage() throws Exception { 1291 final String fakePackageName = "android.fake.package.name"; 1292 final int defaultValue = -1; 1293 1294 setStandByBucket(fakePackageName, "rare"); 1295 // Verify the above does not add a new entry to the App Standby bucket map 1296 Map<String, Integer> bucketMap = mUsageStatsManager.getAppStandbyBuckets(); 1297 int bucket = bucketMap.getOrDefault(fakePackageName, defaultValue); 1298 assertFalse("Meaningful bucket value " + bucket + " returned for " + fakePackageName 1299 + " after set-standby-bucket", bucket > 0); 1300 1301 executeShellCmd("am get-standby-bucket " + fakePackageName); 1302 // Verify the above does not add a new entry to the App Standby bucket map 1303 bucketMap = mUsageStatsManager.getAppStandbyBuckets(); 1304 bucket = bucketMap.getOrDefault(fakePackageName, defaultValue); 1305 assertFalse("Meaningful bucket value " + bucket + " returned for " + fakePackageName 1306 + " after get-standby-bucket", bucket > 0); 1307 } 1308 1309 @Test testObserveUsagePermissionForRegisterObserver()1310 public void testObserveUsagePermissionForRegisterObserver() { 1311 final int observerId = 0; 1312 final String[] packages = new String[] {"com.android.settings"}; 1313 1314 try { 1315 mUsageStatsManager.registerAppUsageObserver(observerId, packages, 1316 1, java.util.concurrent.TimeUnit.HOURS, null); 1317 fail("Expected SecurityException for an app not holding OBSERVE_APP_USAGE permission."); 1318 } catch (SecurityException e) { 1319 // Exception expected 1320 } 1321 1322 try { 1323 mUsageStatsManager.registerUsageSessionObserver(observerId, packages, 1324 Duration.ofHours(1), Duration.ofSeconds(10), null, null); 1325 fail("Expected SecurityException for an app not holding OBSERVE_APP_USAGE permission."); 1326 } catch (SecurityException e) { 1327 // Exception expected 1328 } 1329 1330 try { 1331 mUsageStatsManager.registerAppUsageLimitObserver(observerId, packages, 1332 Duration.ofHours(1), Duration.ofHours(0), null); 1333 fail("Expected SecurityException for an app not holding OBSERVE_APP_USAGE permission."); 1334 } catch (SecurityException e) { 1335 // Exception expected 1336 } 1337 } 1338 1339 @Test testObserveUsagePermissionForUnregisterObserver()1340 public void testObserveUsagePermissionForUnregisterObserver() { 1341 final int observerId = 0; 1342 1343 try { 1344 mUsageStatsManager.unregisterAppUsageObserver(observerId); 1345 fail("Expected SecurityException for an app not holding OBSERVE_APP_USAGE permission."); 1346 } catch (SecurityException e) { 1347 // Exception expected 1348 } 1349 1350 try { 1351 mUsageStatsManager.unregisterUsageSessionObserver(observerId); 1352 fail("Expected SecurityException for an app not holding OBSERVE_APP_USAGE permission."); 1353 } catch (SecurityException e) { 1354 // Exception expected 1355 } 1356 1357 try { 1358 mUsageStatsManager.unregisterAppUsageLimitObserver(observerId); 1359 fail("Expected SecurityException for an app not holding OBSERVE_APP_USAGE permission."); 1360 } catch (SecurityException e) { 1361 // Exception expected 1362 } 1363 } 1364 1365 @AppModeFull(reason = "No usage events access in instant apps") 1366 @Test testForegroundService()1367 public void testForegroundService() throws Exception { 1368 // This test start a foreground service then stop it. The event list should have one 1369 // FOREGROUND_SERVICE_START and one FOREGROUND_SERVICE_STOP event. 1370 final long startTime = System.currentTimeMillis(); 1371 mContext.startService(new Intent(mContext, TestService.class)); 1372 mUiDevice.wait(Until.hasObject(By.clazz(TestService.class)), TIMEOUT); 1373 final long sleepTime = 500; 1374 SystemClock.sleep(sleepTime); 1375 mContext.stopService(new Intent(mContext, TestService.class)); 1376 mUiDevice.wait(Until.gone(By.clazz(TestService.class)), TIMEOUT); 1377 final long endTime = System.currentTimeMillis(); 1378 UsageEvents events = mUsageStatsManager.queryEvents(startTime, endTime); 1379 1380 int numStarts = 0; 1381 int numStops = 0; 1382 int startIdx = -1; 1383 int stopIdx = -1; 1384 int i = 0; 1385 while (events.hasNextEvent()) { 1386 UsageEvents.Event event = new UsageEvents.Event(); 1387 assertTrue(events.getNextEvent(event)); 1388 if (mTargetPackage.equals(event.getPackageName()) 1389 || TestService.class.getName().equals(event.getClassName())) { 1390 if (event.getEventType() == Event.FOREGROUND_SERVICE_START) { 1391 numStarts++; 1392 startIdx = i; 1393 } else if (event.getEventType() == Event.FOREGROUND_SERVICE_STOP) { 1394 numStops++; 1395 stopIdx = i; 1396 } 1397 i++; 1398 } 1399 } 1400 // One FOREGROUND_SERVICE_START event followed by one FOREGROUND_SERVICE_STOP event. 1401 assertEquals(numStarts, 1); 1402 assertEquals(numStops, 1); 1403 assertLessThan(startIdx, stopIdx); 1404 1405 final Map<String, UsageStats> map = mUsageStatsManager.queryAndAggregateUsageStats( 1406 startTime, endTime); 1407 final UsageStats stats = map.get(mTargetPackage); 1408 assertNotNull(stats); 1409 final long lastTimeUsed = stats.getLastTimeForegroundServiceUsed(); 1410 // lastTimeUsed should be falling between startTime and endTime. 1411 assertLessThan(startTime, lastTimeUsed); 1412 assertLessThan(lastTimeUsed, endTime); 1413 final long totalTimeUsed = stats.getTotalTimeForegroundServiceUsed(); 1414 // because we slept for 500 milliseconds earlier, we know the totalTimeUsed must be more 1415 // more than 500 milliseconds. 1416 assertLessThan(sleepTime, totalTimeUsed); 1417 } 1418 1419 @AppModeFull(reason = "No usage events access in instant apps") 1420 @Test testTaskRootEventField()1421 public void testTaskRootEventField() throws Exception { 1422 mUiDevice.wakeUp(); 1423 dismissKeyguard(); // also want to start out with the keyguard dismissed. 1424 1425 final long startTime = System.currentTimeMillis(); 1426 launchSubActivity(TaskRootActivity.class); 1427 final long endTime = System.currentTimeMillis(); 1428 UsageEvents events = mUsageStatsManager.queryEvents(startTime, endTime); 1429 1430 while (events.hasNextEvent()) { 1431 UsageEvents.Event event = new UsageEvents.Event(); 1432 assertTrue(events.getNextEvent(event)); 1433 if (TaskRootActivity.TEST_APP_PKG.equals(event.getPackageName()) 1434 && TaskRootActivity.TEST_APP_CLASS.equals(event.getClassName())) { 1435 assertEquals(mTargetPackage, event.getTaskRootPackageName()); 1436 assertEquals(TaskRootActivity.class.getCanonicalName(), 1437 event.getTaskRootClassName()); 1438 return; 1439 } 1440 } 1441 fail("Did not find nested activity name in usage events"); 1442 } 1443 1444 @AppModeFull(reason = "No usage events access in instant apps") 1445 @Test testUsageSourceAttribution()1446 public void testUsageSourceAttribution() throws Exception { 1447 mUiDevice.wakeUp(); 1448 dismissKeyguard(); // also want to start out with the keyguard dismissed. 1449 mUiDevice.pressHome(); 1450 1451 setUsageSourceSetting(Integer.toString(UsageStatsManager.USAGE_SOURCE_CURRENT_ACTIVITY)); 1452 launchSubActivity(TaskRootActivity.class); 1453 // Usage should be attributed to the test app package 1454 assertAppOrTokenUsed(TaskRootActivity.TEST_APP_PKG, true, TIMEOUT); 1455 1456 SystemUtil.runWithShellPermissionIdentity(() -> mAm.forceStopPackage(TEST_APP_PKG)); 1457 1458 setUsageSourceSetting(Integer.toString(UsageStatsManager.USAGE_SOURCE_TASK_ROOT_ACTIVITY)); 1459 launchSubActivity(TaskRootActivity.class); 1460 // Usage should be attributed to this package 1461 assertAppOrTokenUsed(mTargetPackage, true, TIMEOUT); 1462 } 1463 1464 @AppModeFull(reason = "No usage events access in instant apps") 1465 @Test testTaskRootAttribution_finishingTaskRoot()1466 public void testTaskRootAttribution_finishingTaskRoot() throws Exception { 1467 setUsageSourceSetting(Integer.toString(UsageStatsManager.USAGE_SOURCE_TASK_ROOT_ACTIVITY)); 1468 mUiDevice.wakeUp(); 1469 dismissKeyguard(); // also want to start out with the keyguard dismissed. 1470 1471 launchTestActivity(TEST_APP2_PKG, TEST_APP2_CLASS_FINISHING_TASK_ROOT); 1472 // Wait until the nested activity gets started 1473 mUiDevice.wait(Until.hasObject(By.clazz(TEST_APP_PKG, TEST_APP_CLASS)), TIMEOUT); 1474 1475 // Usage should be attributed to the task root app package 1476 assertAppOrTokenUsed(TEST_APP_PKG, false, TIMEOUT); 1477 assertAppOrTokenUsed(TEST_APP2_PKG, true, TIMEOUT); 1478 SystemUtil.runWithShellPermissionIdentity(() -> mAm.forceStopPackage(TEST_APP_PKG)); 1479 mUiDevice.wait(Until.gone(By.clazz(TEST_APP_PKG, TEST_APP_CLASS)), TIMEOUT); 1480 1481 // Usage should no longer be tracked 1482 assertAppOrTokenUsed(TEST_APP_PKG, false, TIMEOUT); 1483 assertAppOrTokenUsed(TEST_APP2_PKG, false, TIMEOUT); 1484 } 1485 1486 @AppModeInstant 1487 @Test testInstantAppUsageEventsObfuscated()1488 public void testInstantAppUsageEventsObfuscated() throws Exception { 1489 @SuppressWarnings("unchecked") 1490 final Class<? extends Activity>[] activitySequence = new Class[] { 1491 Activities.ActivityOne.class, 1492 Activities.ActivityTwo.class, 1493 Activities.ActivityThree.class, 1494 }; 1495 mUiDevice.wakeUp(); 1496 mUiDevice.pressHome(); 1497 1498 final long startTime = System.currentTimeMillis(); 1499 // Launch the series of Activities. 1500 launchSubActivities(activitySequence); 1501 SystemClock.sleep(250); 1502 1503 final long endTime = System.currentTimeMillis(); 1504 final UsageEvents events = mUsageStatsManager.queryEvents(startTime, endTime); 1505 1506 int resumes = 0; 1507 int pauses = 0; 1508 int stops = 0; 1509 1510 // Only look at events belongs to mTargetPackage. 1511 while (events.hasNextEvent()) { 1512 final UsageEvents.Event event = new UsageEvents.Event(); 1513 assertTrue(events.getNextEvent(event)); 1514 // There should be no events with this packages name 1515 assertNotEquals("Instant app package name found in usage event list", 1516 mTargetPackage, event.getPackageName()); 1517 1518 // Look for the obfuscated instant app string instead 1519 if(UsageEvents.INSTANT_APP_PACKAGE_NAME.equals(event.getPackageName())) { 1520 switch (event.mEventType) { 1521 case Event.ACTIVITY_RESUMED: 1522 resumes++; 1523 break; 1524 case Event.ACTIVITY_PAUSED: 1525 pauses++; 1526 break; 1527 case Event.ACTIVITY_STOPPED: 1528 stops++; 1529 break; 1530 } 1531 } 1532 } 1533 assertEquals("Unexpected number of activity resumes", 3, resumes); 1534 assertEquals("Unexpected number of activity pauses", 2, pauses); 1535 assertEquals("Unexpected number of activity stops", 2, stops); 1536 } 1537 1538 @AppModeFull(reason = "No usage events access in instant apps") 1539 @Test testSuddenDestroy()1540 public void testSuddenDestroy() throws Exception { 1541 mUiDevice.wakeUp(); 1542 dismissKeyguard(); // also want to start out with the keyguard dismissed. 1543 mUiDevice.pressHome(); 1544 1545 final long startTime = System.currentTimeMillis(); 1546 1547 launchTestActivity(TEST_APP_PKG, TEST_APP_CLASS); 1548 SystemClock.sleep(500); 1549 1550 // Destroy the activity 1551 SystemUtil.runWithShellPermissionIdentity(() -> mAm.forceStopPackage(TEST_APP_PKG)); 1552 mUiDevice.wait(Until.gone(By.clazz(TEST_APP_PKG, TEST_APP_CLASS)), TIMEOUT); 1553 SystemClock.sleep(500); 1554 1555 final long endTime = System.currentTimeMillis(); 1556 final UsageEvents events = mUsageStatsManager.queryEvents(startTime, endTime); 1557 1558 int resumes = 0; 1559 int stops = 0; 1560 1561 while (events.hasNextEvent()) { 1562 final UsageEvents.Event event = new UsageEvents.Event(); 1563 assertTrue(events.getNextEvent(event)); 1564 1565 if(TEST_APP_PKG.equals(event.getPackageName())) { 1566 switch (event.mEventType) { 1567 case Event.ACTIVITY_RESUMED: 1568 assertNotNull("ACTIVITY_RESUMED event Task Root should not be null", 1569 event.getTaskRootPackageName()); 1570 resumes++; 1571 break; 1572 case Event.ACTIVITY_STOPPED: 1573 assertNotNull("ACTIVITY_STOPPED event Task Root should not be null", 1574 event.getTaskRootPackageName()); 1575 stops++; 1576 break; 1577 } 1578 } 1579 } 1580 assertEquals("Unexpected number of activity resumes", 1, resumes); 1581 assertEquals("Unexpected number of activity stops", 1, stops); 1582 } 1583 1584 @AppModeFull(reason = "No usage events access in instant apps") 1585 @Test testPipActivity()1586 public void testPipActivity() throws Exception { 1587 assumeTrue("Test cannot run without Picture in Picture support", 1588 mContext.getPackageManager().hasSystemFeature( 1589 PackageManager.FEATURE_PICTURE_IN_PICTURE)); 1590 mUiDevice.wakeUp(); 1591 dismissKeyguard(); // also want to start out with the keyguard dismissed. 1592 mUiDevice.pressHome(); 1593 1594 final long startTime = System.currentTimeMillis(); 1595 1596 launchTestActivity(TEST_APP2_PKG, TEST_APP2_CLASS_PIP); 1597 SystemClock.sleep(500); 1598 1599 // TEST_APP_PKG should take focus, pausing the TEST_APP2_CLASS_PIP activity. 1600 launchTestActivity(TEST_APP_PKG, TEST_APP_CLASS); 1601 SystemClock.sleep(500); 1602 1603 mWMStateHelper.waitForActivityState(TEST_APP2_PIP_COMPONENT, 1604 WindowManagerState.STATE_PAUSED); 1605 1606 mWMStateHelper.assertActivityDisplayed(TEST_APP2_PIP_COMPONENT); 1607 mWMStateHelper.assertNotFocusedActivity("Pip activity should not be in focus", 1608 TEST_APP2_PIP_COMPONENT); 1609 1610 final long endTime = System.currentTimeMillis(); 1611 final UsageEvents events = mUsageStatsManager.queryEvents(startTime, endTime); 1612 1613 int resumes = 0; 1614 int pauses = 0; 1615 int stops = 0; 1616 1617 while (events.hasNextEvent()) { 1618 final UsageEvents.Event event = new UsageEvents.Event(); 1619 assertTrue(events.getNextEvent(event)); 1620 1621 if(TEST_APP2_PKG.equals(event.getPackageName())) { 1622 switch (event.mEventType) { 1623 case Event.ACTIVITY_RESUMED: 1624 assertNotNull("ACTIVITY_RESUMED event Task Root should not be null", 1625 event.getTaskRootPackageName()); 1626 resumes++; 1627 break; 1628 case Event.ACTIVITY_PAUSED: 1629 assertNotNull("ACTIVITY_PAUSED event Task Root should not be null", 1630 event.getTaskRootPackageName()); 1631 pauses++; 1632 break; 1633 case Event.ACTIVITY_STOPPED: 1634 assertNotNull("ACTIVITY_STOPPED event Task Root should not be null", 1635 event.getTaskRootPackageName()); 1636 stops++; 1637 break; 1638 } 1639 } 1640 } 1641 assertEquals("Unexpected number of activity resumes", 1, resumes); 1642 assertEquals("Unexpected number of activity pauses", 1, pauses); 1643 assertEquals("Unexpected number of activity stops", 0, stops); 1644 } 1645 1646 @AppModeFull(reason = "No usage events access in instant apps") 1647 @Test testPipActivity_StopToPause()1648 public void testPipActivity_StopToPause() throws Exception { 1649 assumeTrue("Test cannot run without Picture in Picture support", 1650 mContext.getPackageManager().hasSystemFeature( 1651 PackageManager.FEATURE_PICTURE_IN_PICTURE)); 1652 mUiDevice.wakeUp(); 1653 dismissKeyguard(); // also want to start out with the keyguard dismissed. 1654 mUiDevice.pressHome(); 1655 1656 launchTestActivity(TEST_APP2_PKG, TEST_APP2_CLASS_PIP); 1657 SystemClock.sleep(500); 1658 1659 // TEST_APP_PKG should take focus, pausing the TEST_APP2_CLASS_PIP activity. 1660 launchTestActivity(TEST_APP_PKG, TEST_APP_CLASS); 1661 SystemClock.sleep(500); 1662 1663 mWMStateHelper.assertActivityDisplayed(TEST_APP2_PIP_COMPONENT); 1664 mWMStateHelper.assertNotFocusedActivity("Pip activity should not be in focus", 1665 TEST_APP2_PIP_COMPONENT); 1666 1667 // Sleeping the device should cause the Pip activity to stop. 1668 final long sleepTime = System.currentTimeMillis(); 1669 sleepDevice(); 1670 mWMStateHelper.waitForActivityState(TEST_APP2_PIP_COMPONENT, 1671 WindowManagerState.STATE_STOPPED); 1672 1673 // Pip activity stop should show up in UsageStats. 1674 final ArrayList<Event> stoppedEvent = waitForEventCount(STOPPED_EVENT, sleepTime, 1, 1675 TEST_APP2_PKG); 1676 assertEquals(Event.ACTIVITY_STOPPED, stoppedEvent.get(0).getEventType()); 1677 1678 // Waking the device should cause the stopped Pip to return to the paused state. 1679 final long wakeTime = System.currentTimeMillis(); 1680 mUiDevice.wakeUp(); 1681 dismissKeyguard(); 1682 mWMStateHelper.waitForActivityState(TEST_APP2_PIP_COMPONENT, 1683 WindowManagerState.STATE_PAUSED); 1684 1685 mWMStateHelper.assertActivityDisplayed(TEST_APP2_PIP_COMPONENT); 1686 mWMStateHelper.assertNotFocusedActivity("Pip activity should not be in focus", 1687 TEST_APP2_PIP_COMPONENT); 1688 1689 // Sleeping the device should cause the Pip activity to stop again. 1690 final long secondSleepTime = System.currentTimeMillis(); 1691 sleepDevice(); 1692 mWMStateHelper.waitForActivityState(TEST_APP2_PIP_COMPONENT, 1693 WindowManagerState.STATE_STOPPED); 1694 1695 // Pip activity stop should show up in UsageStats again. 1696 final ArrayList<Event> secondStoppedEvent = waitForEventCount(STOPPED_EVENT, 1697 secondSleepTime, 1, 1698 TEST_APP2_PKG); 1699 assertEquals(Event.ACTIVITY_STOPPED, secondStoppedEvent.get(0).getEventType()); 1700 } 1701 1702 @AppModeFull(reason = "No usage events access in instant apps") 1703 @Test testLocusIdEventsVisibility()1704 public void testLocusIdEventsVisibility() throws Exception { 1705 final long startTime = System.currentTimeMillis(); 1706 startAndDestroyActivityWithLocus(); 1707 final long endTime = System.currentTimeMillis(); 1708 1709 final UsageEvents restrictedEvents = mUsageStatsManager.queryEvents(startTime, endTime); 1710 final UsageEvents allEvents = queryEventsAsShell(startTime, endTime); 1711 verifyLocusIdEventVisibility(restrictedEvents, false); 1712 verifyLocusIdEventVisibility(allEvents, true); 1713 } 1714 startAndDestroyActivityWithLocus()1715 private void startAndDestroyActivityWithLocus() { 1716 launchTestActivity(TEST_APP_PKG, TEST_APP_CLASS_LOCUS); 1717 SystemClock.sleep(500); 1718 1719 // Destroy the activity 1720 SystemUtil.runWithShellPermissionIdentity(() -> mAm.forceStopPackage(TEST_APP_PKG)); 1721 mUiDevice.wait(Until.gone(By.clazz(TEST_APP_PKG, TEST_APP_CLASS_LOCUS)), TIMEOUT); 1722 SystemClock.sleep(500); 1723 } 1724 verifyLocusIdEventVisibility(UsageEvents events, boolean hasPermission)1725 private void verifyLocusIdEventVisibility(UsageEvents events, boolean hasPermission) { 1726 int locuses = 0; 1727 while (events.hasNextEvent()) { 1728 final Event event = new UsageEvents.Event(); 1729 assertTrue(events.getNextEvent(event)); 1730 1731 if (TEST_APP_PKG.equals(event.getPackageName()) 1732 && event.mEventType == Event.LOCUS_ID_SET) { 1733 locuses++; 1734 } 1735 } 1736 1737 if (hasPermission) { 1738 assertEquals("LOCUS_ID_SET events were not visible.", 2, locuses); 1739 } else { 1740 assertEquals("LOCUS_ID_SET events were visible.", 0, locuses); 1741 } 1742 } 1743 1744 /** 1745 * Assert on an app or token's usage state. 1746 * 1747 * @param entity name of the app or token 1748 * @param expected expected usage state, true for in use, false for not in use 1749 */ assertAppOrTokenUsed(String entity, boolean expected, long timeout)1750 private void assertAppOrTokenUsed(String entity, boolean expected, long timeout) 1751 throws IOException { 1752 final long realtimeTimeout = SystemClock.elapsedRealtime() + timeout; 1753 String activeUsages; 1754 boolean found; 1755 do { 1756 activeUsages = executeShellCmd("dumpsys usagestats apptimelimit actives"); 1757 final String[] actives = activeUsages.split("\n"); 1758 found = Arrays.asList(actives).contains(entity); 1759 } while (found != expected && SystemClock.elapsedRealtime() <= realtimeTimeout); 1760 1761 if (expected) { 1762 assertTrue(entity + " not found in list of active activities and tokens\n" 1763 + activeUsages, found); 1764 } else { 1765 assertFalse(entity + " found in list of active activities and tokens\n" 1766 + activeUsages, found); 1767 } 1768 } 1769 dismissKeyguard()1770 private void dismissKeyguard() throws Exception { 1771 if (mKeyguardManager.isKeyguardLocked()) { 1772 final long startTime = getEvents(KEYGUARD_EVENTS, 0, null, null) + 1; 1773 executeShellCmd("wm dismiss-keyguard"); 1774 final ArrayList<Event> events = waitForEventCount(KEYGUARD_EVENTS, startTime, 1); 1775 assertEquals(Event.KEYGUARD_HIDDEN, events.get(0).getEventType()); 1776 SystemClock.sleep(500); 1777 } 1778 } 1779 setStandByBucket(String packageName, String bucket)1780 private void setStandByBucket(String packageName, String bucket) throws IOException { 1781 executeShellCmd("am set-standby-bucket " + packageName + " " + bucket); 1782 } 1783 executeShellCmd(String command)1784 private String executeShellCmd(String command) throws IOException { 1785 return mUiDevice.executeShellCommand(command); 1786 } 1787 queryEventsAsShell(long start, long end)1788 private UsageEvents queryEventsAsShell(long start, long end) { 1789 return SystemUtil.runWithShellPermissionIdentity(() -> 1790 mUsageStatsManager.queryEvents(start, end)); 1791 } 1792 bindToTestService()1793 private ITestReceiver bindToTestService() throws Exception { 1794 final TestServiceConnection connection = new TestServiceConnection(); 1795 final Intent intent = new Intent().setComponent( 1796 new ComponentName(TEST_APP_PKG, TEST_APP_CLASS_SERVICE)); 1797 mContext.bindService(intent, connection, Context.BIND_AUTO_CREATE); 1798 return ITestReceiver.Stub.asInterface(connection.getService()); 1799 } 1800 1801 private class TestServiceConnection implements ServiceConnection { 1802 private BlockingQueue<IBinder> mBlockingQueue = new LinkedBlockingQueue<>(); 1803 onServiceConnected(ComponentName componentName, IBinder service)1804 public void onServiceConnected(ComponentName componentName, IBinder service) { 1805 mBlockingQueue.offer(service); 1806 } 1807 onServiceDisconnected(ComponentName componentName)1808 public void onServiceDisconnected(ComponentName componentName) { 1809 } 1810 getService()1811 public IBinder getService() throws Exception { 1812 final IBinder service = mBlockingQueue.poll(TIMEOUT_BINDER_SERVICE_SEC, 1813 TimeUnit.SECONDS); 1814 return service; 1815 } 1816 } 1817 runJobImmediately()1818 private void runJobImmediately() throws Exception { 1819 TestJob.schedule(mContext); 1820 executeShellCmd(JOBSCHEDULER_RUN_SHELL_COMMAND 1821 + " " + mContext.getPackageName() 1822 + " " + TestJob.TEST_JOB_ID); 1823 } 1824 isAppInactiveAsPermissionlessApp(String pkg)1825 private boolean isAppInactiveAsPermissionlessApp(String pkg) throws Exception { 1826 final ITestReceiver testService = bindToTestService(); 1827 return testService.isAppInactive(pkg); 1828 } 1829 createUser(String name)1830 private int createUser(String name) throws Exception { 1831 final String output = executeShellCmd( 1832 "pm create-user " + name); 1833 if (output.startsWith("Success")) { 1834 return mOtherUser = Integer.parseInt(output.substring(output.lastIndexOf(" ")).trim()); 1835 } 1836 throw new IllegalStateException(String.format("Failed to create user: %s", output)); 1837 } 1838 removeUser(final int userId)1839 private boolean removeUser(final int userId) throws Exception { 1840 final String output = executeShellCmd(String.format("pm remove-user %s", userId)); 1841 if (output.startsWith("Error")) { 1842 return false; 1843 } 1844 return true; 1845 } 1846 startUser(int userId, boolean waitFlag)1847 private boolean startUser(int userId, boolean waitFlag) throws Exception { 1848 String cmd = "am start-user " + (waitFlag ? "-w " : "") + userId; 1849 1850 final String output = executeShellCmd(cmd); 1851 if (output.startsWith("Error")) { 1852 return false; 1853 } 1854 if (waitFlag) { 1855 String state = executeShellCmd("am get-started-user-state " + userId); 1856 if (!state.contains("RUNNING_UNLOCKED")) { 1857 return false; 1858 } 1859 } 1860 return true; 1861 } 1862 stopUser(int userId, boolean waitFlag, boolean forceFlag)1863 private boolean stopUser(int userId, boolean waitFlag, boolean forceFlag) 1864 throws Exception { 1865 StringBuilder cmd = new StringBuilder("am stop-user "); 1866 if (waitFlag) { 1867 cmd.append("-w "); 1868 } 1869 if (forceFlag) { 1870 cmd.append("-f "); 1871 } 1872 cmd.append(userId); 1873 1874 final String output = executeShellCmd(cmd.toString()); 1875 if (output.contains("Error: Can't stop system user")) { 1876 return false; 1877 } 1878 return true; 1879 } 1880 installExistingPackageAsUser(String packageName, int userId)1881 private void installExistingPackageAsUser(String packageName, int userId) 1882 throws Exception { 1883 executeShellCmd( 1884 String.format("pm install-existing --user %d --wait %s", userId, packageName)); 1885 } 1886 sleepDevice()1887 private void sleepDevice() throws Exception { 1888 if (mContext.getPackageManager().hasSystemFeature(PackageManager.FEATURE_WATCH)) { 1889 mUiDevice.pressKeyCode(KeyEvent.KEYCODE_SLEEP); 1890 } else { 1891 mUiDevice.sleep(); 1892 } 1893 1894 waitUntil(() -> { 1895 try { 1896 return mUiDevice.isScreenOn(); 1897 } catch(Exception e) { 1898 return true; 1899 } 1900 }, false); 1901 } 1902 } 1903