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