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.assertNotNull; 22 import static org.junit.Assert.assertTrue; 23 import static org.junit.Assert.fail; 24 import static org.junit.Assume.assumeFalse; 25 import static org.junit.Assume.assumeTrue; 26 27 import android.app.*; 28 import android.app.usage.EventStats; 29 import android.app.usage.UsageEvents; 30 import android.app.usage.UsageEvents.Event; 31 import android.app.usage.UsageStats; 32 import android.app.usage.UsageStatsManager; 33 import android.content.Context; 34 import android.content.Intent; 35 import android.content.pm.PackageManager; 36 import android.os.Parcel; 37 import android.os.SystemClock; 38 import android.platform.test.annotations.AppModeFull; 39 import android.platform.test.annotations.AppModeInstant; 40 import android.provider.Settings; 41 import android.support.test.uiautomator.By; 42 import android.support.test.uiautomator.UiDevice; 43 import android.support.test.uiautomator.Until; 44 import android.text.format.DateUtils; 45 import android.util.Log; 46 import android.util.SparseArray; 47 import android.util.SparseLongArray; 48 import android.view.KeyEvent; 49 50 import androidx.test.InstrumentationRegistry; 51 import androidx.test.runner.AndroidJUnit4; 52 53 import com.android.compatibility.common.util.AppStandbyUtils; 54 55 import com.android.compatibility.common.util.SystemUtil; 56 import org.junit.After; 57 import org.junit.Before; 58 import org.junit.Ignore; 59 import org.junit.Test; 60 import org.junit.runner.RunWith; 61 62 import java.io.IOException; 63 import java.text.MessageFormat; 64 import java.time.Duration; 65 import java.util.ArrayList; 66 import java.util.Arrays; 67 import java.util.List; 68 import java.util.Map; 69 import java.util.concurrent.TimeUnit; 70 71 /** 72 * Test the UsageStats API. It is difficult to test the entire surface area 73 * of the API, as a lot of the testing depends on what data is already present 74 * on the device and for how long that data has been aggregating. 75 * 76 * These tests perform simple checks that each interval is of the correct duration, 77 * and that events do appear in the event log. 78 * 79 * Tests to add that are difficult to add now: 80 * - Invoking a device configuration change and then watching for it in the event log. 81 * - Changing the system time and verifying that all data has been correctly shifted 82 * along with the new time. 83 * - Proper eviction of old data. 84 */ 85 @RunWith(AndroidJUnit4.class) 86 public class UsageStatsTest { 87 private static final boolean DEBUG = false; 88 private static final String TAG = "UsageStatsTest"; 89 90 private static final String APPOPS_SET_SHELL_COMMAND = "appops set {0} " + 91 AppOpsManager.OPSTR_GET_USAGE_STATS + " {1}"; 92 93 private static final String USAGE_SOURCE_GET_SHELL_COMMAND = "settings get global " + 94 Settings.Global.APP_TIME_LIMIT_USAGE_SOURCE; 95 96 private static final String USAGE_SOURCE_SET_SHELL_COMMAND = "settings put global " + 97 Settings.Global.APP_TIME_LIMIT_USAGE_SOURCE + " {0}"; 98 99 private static final String USAGE_SOURCE_DELETE_SHELL_COMMAND = "settings delete global " + 100 Settings.Global.APP_TIME_LIMIT_USAGE_SOURCE; 101 102 private static final String TEST_APP_PKG = "android.app.usage.cts.test1"; 103 private static final String TEST_APP_CLASS = "android.app.usage.cts.test1.SomeActivity"; 104 105 private static final long TIMEOUT = TimeUnit.SECONDS.toMillis(5); 106 private static final long MINUTE = TimeUnit.MINUTES.toMillis(1); 107 private static final long DAY = TimeUnit.DAYS.toMillis(1); 108 private static final long WEEK = 7 * DAY; 109 private static final long MONTH = 30 * DAY; 110 private static final long YEAR = 365 * DAY; 111 private static final long TIME_DIFF_THRESHOLD = 200; 112 private static final String CHANNEL_ID = "my_channel"; 113 114 115 private UiDevice mUiDevice; 116 private UsageStatsManager mUsageStatsManager; 117 private String mTargetPackage; 118 private String mCachedUsageSourceSetting; 119 120 @Before 121 public void setUp() throws Exception { 122 mUiDevice = UiDevice.getInstance(InstrumentationRegistry.getInstrumentation()); 123 mUsageStatsManager = (UsageStatsManager) InstrumentationRegistry.getInstrumentation() 124 .getContext().getSystemService(Context.USAGE_STATS_SERVICE); 125 mTargetPackage = InstrumentationRegistry.getContext().getPackageName(); 126 assumeTrue("App Standby not enabled on device", AppStandbyUtils.isAppStandbyEnabled()); 127 setAppOpsMode("allow"); 128 mCachedUsageSourceSetting = getUsageSourceSetting(); 129 } 130 131 132 @After 133 public void cleanUp() throws Exception { 134 if (mCachedUsageSourceSetting != null && 135 !mCachedUsageSourceSetting.equals(getUsageSourceSetting())) { 136 setUsageSourceSetting(mCachedUsageSourceSetting); 137 mUsageStatsManager.forceUsageSourceSettingRead(); 138 } 139 } 140 141 private static void assertLessThan(long left, long right) { 142 assertTrue("Expected " + left + " to be less than " + right, left < right); 143 } 144 145 private static void assertLessThanOrEqual(long left, long right) { 146 assertTrue("Expected " + left + " to be less than " + right, left <= right); 147 } 148 149 private void setAppOpsMode(String mode) throws Exception { 150 final String command = MessageFormat.format(APPOPS_SET_SHELL_COMMAND, 151 InstrumentationRegistry.getContext().getPackageName(), mode); 152 mUiDevice.executeShellCommand(command); 153 } 154 155 156 private String getUsageSourceSetting() throws Exception { 157 return mUiDevice.executeShellCommand(USAGE_SOURCE_GET_SHELL_COMMAND); 158 } 159 160 private void setUsageSourceSetting(String usageSource) throws Exception { 161 if (usageSource.equals("null")) { 162 mUiDevice.executeShellCommand(USAGE_SOURCE_DELETE_SHELL_COMMAND); 163 } else { 164 final String command = MessageFormat.format(USAGE_SOURCE_SET_SHELL_COMMAND, 165 usageSource); 166 mUiDevice.executeShellCommand(command); 167 } 168 mUsageStatsManager.forceUsageSourceSettingRead(); 169 } 170 171 private void launchSubActivity(Class<? extends Activity> clazz) { 172 final Context context = InstrumentationRegistry.getInstrumentation().getContext(); 173 final Intent intent = new Intent(Intent.ACTION_MAIN); 174 intent.setClassName(mTargetPackage, clazz.getName()); 175 intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK); 176 context.startActivity(intent); 177 mUiDevice.wait(Until.hasObject(By.clazz(clazz)), TIMEOUT); 178 } 179 180 private void launchSubActivities(Class<? extends Activity>[] activityClasses) { 181 for (Class<? extends Activity> clazz : activityClasses) { 182 launchSubActivity(clazz); 183 } 184 } 185 186 @AppModeFull(reason = "No usage events access in instant apps") 187 @Test 188 public void testOrderedActivityLaunchSequenceInEventLog() throws Exception { 189 @SuppressWarnings("unchecked") 190 Class<? extends Activity>[] activitySequence = new Class[] { 191 Activities.ActivityOne.class, 192 Activities.ActivityTwo.class, 193 Activities.ActivityThree.class, 194 }; 195 mUiDevice.wakeUp(); 196 197 final long startTime = System.currentTimeMillis(); 198 // Launch the series of Activities. 199 launchSubActivities(activitySequence); 200 final long endTime = System.currentTimeMillis(); 201 UsageEvents events = mUsageStatsManager.queryEvents(startTime, endTime); 202 203 // Only look at events belongs to mTargetPackage. 204 ArrayList<UsageEvents.Event> eventList = new ArrayList<>(); 205 while (events.hasNextEvent()) { 206 UsageEvents.Event event = new UsageEvents.Event(); 207 assertTrue(events.getNextEvent(event)); 208 if (mTargetPackage.equals(event.getPackageName())) { 209 eventList.add(event); 210 } 211 } 212 213 final int activityCount = activitySequence.length; 214 for (int i = 0; i < activityCount; i++) { 215 String className = activitySequence[i].getName(); 216 ArrayList<UsageEvents.Event> activityEvents = new ArrayList<>(); 217 final int size = eventList.size(); 218 for (int j = 0; j < size; j++) { 219 Event evt = eventList.get(j); 220 if (className.equals(evt.getClassName())) { 221 activityEvents.add(evt); 222 } 223 } 224 // We expect 3 events per Activity launched (ACTIVITY_RESUMED + ACTIVITY_PAUSED 225 // + ACTIVITY_STOPPED) except for the last Activity, which only has 226 // ACTIVITY_RESUMED event. 227 if (i < activityCount - 1) { 228 assertEquals(3, activityEvents.size()); 229 assertEquals(Event.ACTIVITY_RESUMED, activityEvents.get(0).getEventType()); 230 assertEquals(Event.ACTIVITY_PAUSED, activityEvents.get(1).getEventType()); 231 assertEquals(Event.ACTIVITY_STOPPED, activityEvents.get(2).getEventType()); 232 } else { 233 // The last activity 234 assertEquals(1, activityEvents.size()); 235 assertEquals(Event.ACTIVITY_RESUMED, activityEvents.get(0).getEventType()); 236 } 237 } 238 } 239 240 @AppModeFull(reason = "No usage events access in instant apps") 241 @Test 242 public void testActivityOnBackButton() throws Exception { 243 testActivityOnButton(mUiDevice::pressBack); 244 } 245 246 @AppModeFull(reason = "No usage events access in instant apps") 247 @Test 248 public void testActivityOnHomeButton() throws Exception { 249 testActivityOnButton(mUiDevice::pressHome); 250 } 251 252 private void testActivityOnButton(Runnable pressButton) throws Exception { 253 mUiDevice.wakeUp(); 254 final long startTime = System.currentTimeMillis(); 255 final Class clazz = Activities.ActivityOne.class; 256 launchSubActivity(clazz); 257 pressButton.run(); 258 Thread.sleep(1000); 259 final long endTime = System.currentTimeMillis(); 260 UsageEvents events = mUsageStatsManager.queryEvents(startTime, endTime); 261 262 ArrayList<UsageEvents.Event> eventList = new ArrayList<>(); 263 while (events.hasNextEvent()) { 264 UsageEvents.Event event = new UsageEvents.Event(); 265 assertTrue(events.getNextEvent(event)); 266 if (mTargetPackage.equals(event.getPackageName()) 267 && clazz.getName().equals(event.getClassName())) { 268 eventList.add(event); 269 } 270 } 271 assertEquals(3, eventList.size()); 272 assertEquals(Event.ACTIVITY_RESUMED, eventList.get(0).getEventType()); 273 assertEquals(Event.ACTIVITY_PAUSED, eventList.get(1).getEventType()); 274 assertEquals(Event.ACTIVITY_STOPPED, eventList.get(2).getEventType()); 275 } 276 277 @AppModeFull(reason = "No usage events access in instant apps") 278 @Test 279 public void testAppLaunchCount() throws Exception { 280 long endTime = System.currentTimeMillis(); 281 long startTime = endTime - DateUtils.DAY_IN_MILLIS; 282 Map<String,UsageStats> events = mUsageStatsManager.queryAndAggregateUsageStats( 283 startTime, endTime); 284 UsageStats stats = events.get(mTargetPackage); 285 int startingCount = stats.getAppLaunchCount(); 286 launchSubActivity(Activities.ActivityOne.class); 287 launchSubActivity(Activities.ActivityTwo.class); 288 endTime = System.currentTimeMillis(); 289 events = mUsageStatsManager.queryAndAggregateUsageStats( 290 startTime, endTime); 291 stats = events.get(mTargetPackage); 292 assertEquals(startingCount + 1, stats.getAppLaunchCount()); 293 mUiDevice.pressHome(); 294 launchSubActivity(Activities.ActivityOne.class); 295 launchSubActivity(Activities.ActivityTwo.class); 296 launchSubActivity(Activities.ActivityThree.class); 297 endTime = System.currentTimeMillis(); 298 events = mUsageStatsManager.queryAndAggregateUsageStats( 299 startTime, endTime); 300 stats = events.get(mTargetPackage); 301 assertEquals(startingCount + 2, stats.getAppLaunchCount()); 302 } 303 304 @AppModeFull(reason = "No usage events access in instant apps") 305 @Test 306 public void testStandbyBucketChangeLog() throws Exception { 307 final long startTime = System.currentTimeMillis(); 308 mUiDevice.executeShellCommand("am set-standby-bucket " + mTargetPackage + " rare"); 309 310 final long endTime = System.currentTimeMillis(); 311 UsageEvents events = mUsageStatsManager.queryEvents(startTime - 1_000, endTime + 1_000); 312 313 boolean found = false; 314 // Check all the events. 315 while (events.hasNextEvent()) { 316 UsageEvents.Event event = new UsageEvents.Event(); 317 assertTrue(events.getNextEvent(event)); 318 if (event.getEventType() == UsageEvents.Event.STANDBY_BUCKET_CHANGED) { 319 found |= event.getAppStandbyBucket() == UsageStatsManager.STANDBY_BUCKET_RARE; 320 } 321 } 322 323 assertTrue(found); 324 } 325 326 @Test 327 public void testGetAppStandbyBuckets() throws Exception { 328 final boolean origValue = AppStandbyUtils.isAppStandbyEnabledAtRuntime(); 329 AppStandbyUtils.setAppStandbyEnabledAtRuntime(true); 330 try { 331 assumeTrue("Skip GetAppStandby test: app standby is disabled.", 332 AppStandbyUtils.isAppStandbyEnabled()); 333 334 mUiDevice.executeShellCommand("am set-standby-bucket " + mTargetPackage + " rare"); 335 Map<String, Integer> bucketMap = mUsageStatsManager.getAppStandbyBuckets(); 336 assertTrue("No bucket data returned", bucketMap.size() > 0); 337 final int bucket = bucketMap.getOrDefault(mTargetPackage, -1); 338 assertEquals("Incorrect bucket returned for " + mTargetPackage, bucket, 339 UsageStatsManager.STANDBY_BUCKET_RARE); 340 } finally { 341 AppStandbyUtils.setAppStandbyEnabledAtRuntime(origValue); 342 } 343 } 344 345 @Test 346 public void testGetAppStandbyBucket() throws Exception { 347 // App should be at least active, since it's running instrumentation tests 348 assertLessThanOrEqual(UsageStatsManager.STANDBY_BUCKET_ACTIVE, 349 mUsageStatsManager.getAppStandbyBucket()); 350 } 351 352 @Test 353 public void testQueryEventsForSelf() throws Exception { 354 setAppOpsMode("ignore"); // To ensure permission is not required 355 // Time drifts of 2s are expected inside usage stats 356 final long start = System.currentTimeMillis() - 2_000; 357 mUiDevice.executeShellCommand("am set-standby-bucket " + mTargetPackage + " rare"); 358 Thread.sleep(100); 359 mUiDevice.executeShellCommand("am set-standby-bucket " + mTargetPackage + " working_set"); 360 Thread.sleep(100); 361 final long end = System.currentTimeMillis() + 2_000; 362 final UsageEvents events = mUsageStatsManager.queryEventsForSelf(start, end); 363 long rareTimeStamp = end + 1; // Initializing as rareTimeStamp > workingTimeStamp 364 long workingTimeStamp = start - 1; 365 int numEvents = 0; 366 while (events.hasNextEvent()) { 367 UsageEvents.Event event = new UsageEvents.Event(); 368 assertTrue(events.getNextEvent(event)); 369 numEvents++; 370 assertEquals("Event for a different package", mTargetPackage, event.getPackageName()); 371 if (event.getEventType() == Event.STANDBY_BUCKET_CHANGED) { 372 if (event.getAppStandbyBucket() == UsageStatsManager.STANDBY_BUCKET_RARE) { 373 rareTimeStamp = event.getTimeStamp(); 374 } 375 else if (event.getAppStandbyBucket() == UsageStatsManager 376 .STANDBY_BUCKET_WORKING_SET) { 377 workingTimeStamp = event.getTimeStamp(); 378 } 379 } 380 } 381 assertTrue("Only " + numEvents + " events returned", numEvents >= 2); 382 assertLessThan(rareTimeStamp, workingTimeStamp); 383 } 384 385 /** 386 * We can't run this test because we are unable to change the system time. 387 * It would be nice to add a shell command or other to allow the shell user 388 * to set the time, thereby allowing this test to set the time using the UIAutomator. 389 */ 390 @Ignore 391 @Test 392 public void ignore_testStatsAreShiftedInTimeWhenSystemTimeChanges() throws Exception { 393 launchSubActivity(Activities.ActivityOne.class); 394 launchSubActivity(Activities.ActivityThree.class); 395 396 long endTime = System.currentTimeMillis(); 397 long startTime = endTime - MINUTE; 398 Map<String, UsageStats> statsMap = mUsageStatsManager.queryAndAggregateUsageStats(startTime, 399 endTime); 400 assertFalse(statsMap.isEmpty()); 401 assertTrue(statsMap.containsKey(mTargetPackage)); 402 final UsageStats before = statsMap.get(mTargetPackage); 403 404 SystemClock.setCurrentTimeMillis(System.currentTimeMillis() - (DAY / 2)); 405 try { 406 endTime = System.currentTimeMillis(); 407 startTime = endTime - MINUTE; 408 statsMap = mUsageStatsManager.queryAndAggregateUsageStats(startTime, endTime); 409 assertFalse(statsMap.isEmpty()); 410 assertTrue(statsMap.containsKey(mTargetPackage)); 411 final UsageStats after = statsMap.get(mTargetPackage); 412 assertEquals(before.getPackageName(), after.getPackageName()); 413 414 long diff = before.getFirstTimeStamp() - after.getFirstTimeStamp(); 415 assertLessThan(Math.abs(diff - (DAY / 2)), TIME_DIFF_THRESHOLD); 416 417 assertEquals(before.getLastTimeStamp() - before.getFirstTimeStamp(), 418 after.getLastTimeStamp() - after.getFirstTimeStamp()); 419 assertEquals(before.getLastTimeUsed() - before.getFirstTimeStamp(), 420 after.getLastTimeUsed() - after.getFirstTimeStamp()); 421 assertEquals(before.getTotalTimeInForeground(), after.getTotalTimeInForeground()); 422 } finally { 423 SystemClock.setCurrentTimeMillis(System.currentTimeMillis() + (DAY / 2)); 424 } 425 } 426 427 @Test 428 public void testUsageEventsParceling() throws Exception { 429 final long startTime = System.currentTimeMillis() - MINUTE; 430 431 // Ensure some data is in the UsageStats log. 432 @SuppressWarnings("unchecked") 433 Class<? extends Activity>[] activityClasses = new Class[] { 434 Activities.ActivityTwo.class, 435 Activities.ActivityOne.class, 436 Activities.ActivityThree.class, 437 }; 438 launchSubActivities(activityClasses); 439 440 final long endTime = System.currentTimeMillis(); 441 UsageEvents events = mUsageStatsManager.queryEvents(startTime, endTime); 442 assertTrue(events.getNextEvent(new UsageEvents.Event())); 443 444 Parcel p = Parcel.obtain(); 445 p.setDataPosition(0); 446 events.writeToParcel(p, 0); 447 p.setDataPosition(0); 448 449 UsageEvents reparceledEvents = UsageEvents.CREATOR.createFromParcel(p); 450 451 UsageEvents.Event e1 = new UsageEvents.Event(); 452 UsageEvents.Event e2 = new UsageEvents.Event(); 453 while (events.hasNextEvent() && reparceledEvents.hasNextEvent()) { 454 events.getNextEvent(e1); 455 reparceledEvents.getNextEvent(e2); 456 assertEquals(e1.getPackageName(), e2.getPackageName()); 457 assertEquals(e1.getClassName(), e2.getClassName()); 458 assertEquals(e1.getConfiguration(), e2.getConfiguration()); 459 assertEquals(e1.getEventType(), e2.getEventType()); 460 assertEquals(e1.getTimeStamp(), e2.getTimeStamp()); 461 } 462 463 assertEquals(events.hasNextEvent(), reparceledEvents.hasNextEvent()); 464 } 465 466 @AppModeFull(reason = "No usage events access in instant apps") 467 @Test 468 public void testPackageUsageStatsIntervals() throws Exception { 469 final long beforeTime = System.currentTimeMillis(); 470 471 // Launch an Activity. 472 launchSubActivity(Activities.ActivityFour.class); 473 launchSubActivity(Activities.ActivityThree.class); 474 475 final long endTime = System.currentTimeMillis(); 476 477 final SparseLongArray intervalLengths = new SparseLongArray(); 478 intervalLengths.put(UsageStatsManager.INTERVAL_DAILY, DAY); 479 intervalLengths.put(UsageStatsManager.INTERVAL_WEEKLY, WEEK); 480 intervalLengths.put(UsageStatsManager.INTERVAL_MONTHLY, MONTH); 481 intervalLengths.put(UsageStatsManager.INTERVAL_YEARLY, YEAR); 482 483 final int intervalCount = intervalLengths.size(); 484 for (int i = 0; i < intervalCount; i++) { 485 final int intervalType = intervalLengths.keyAt(i); 486 final long intervalDuration = intervalLengths.valueAt(i); 487 final long startTime = endTime - (2 * intervalDuration); 488 final List<UsageStats> statsList = mUsageStatsManager.queryUsageStats(intervalType, 489 startTime, endTime); 490 assertFalse(statsList.isEmpty()); 491 492 boolean foundPackage = false; 493 for (UsageStats stats : statsList) { 494 // Verify that each period is a day long. 495 assertLessThanOrEqual(stats.getLastTimeStamp() - stats.getFirstTimeStamp(), 496 intervalDuration); 497 if (stats.getPackageName().equals(mTargetPackage) && 498 stats.getLastTimeUsed() >= beforeTime - TIME_DIFF_THRESHOLD) { 499 foundPackage = true; 500 } 501 } 502 503 assertTrue("Did not find package " + mTargetPackage + " in interval " + intervalType, 504 foundPackage); 505 } 506 } 507 508 @Test 509 public void testNoAccessSilentlyFails() throws Exception { 510 final long startTime = System.currentTimeMillis() - MINUTE; 511 512 launchSubActivity(android.app.usage.cts.Activities.ActivityOne.class); 513 launchSubActivity(android.app.usage.cts.Activities.ActivityThree.class); 514 515 final long endTime = System.currentTimeMillis(); 516 List<UsageStats> stats = mUsageStatsManager.queryUsageStats(UsageStatsManager.INTERVAL_BEST, 517 startTime, endTime); 518 assertFalse(stats.isEmpty()); 519 520 // We set the mode to ignore because our package has the PACKAGE_USAGE_STATS permission, 521 // and default would allow in this case. 522 setAppOpsMode("ignore"); 523 524 stats = mUsageStatsManager.queryUsageStats(UsageStatsManager.INTERVAL_BEST, 525 startTime, endTime); 526 assertTrue(stats.isEmpty()); 527 } 528 529 @AppModeFull(reason = "No usage events access in instant apps") 530 @Test 531 public void testNotificationSeen() throws Exception { 532 final long startTime = System.currentTimeMillis(); 533 Context context = InstrumentationRegistry.getContext(); 534 535 // Skip the test for wearable devices and televisions; neither has a notification shade. 536 assumeFalse("Test cannot run on a watch- notification shade is not shown", 537 context.getPackageManager().hasSystemFeature(PackageManager.FEATURE_WATCH)); 538 assumeFalse("Test cannot run on a television- notifications are not shown", 539 context.getPackageManager().hasSystemFeature(PackageManager.FEATURE_LEANBACK_ONLY)); 540 541 NotificationManager mNotificationManager = 542 (NotificationManager) context.getSystemService(Context.NOTIFICATION_SERVICE); 543 int importance = NotificationManager.IMPORTANCE_DEFAULT; 544 NotificationChannel mChannel = new NotificationChannel(CHANNEL_ID, "Channel", 545 importance); 546 // Configure the notification channel. 547 mChannel.setDescription("Test channel"); 548 mNotificationManager.createNotificationChannel(mChannel); 549 Notification.Builder mBuilder = 550 new Notification.Builder(context, CHANNEL_ID) 551 .setSmallIcon(R.drawable.ic_notification) 552 .setContentTitle("My notification") 553 .setContentText("Hello World!"); 554 PendingIntent pi = PendingIntent.getActivity(context, 1, 555 new Intent(Settings.ACTION_SETTINGS), 0); 556 mBuilder.setContentIntent(pi); 557 mNotificationManager.notify(1, mBuilder.build()); 558 Thread.sleep(500); 559 long endTime = System.currentTimeMillis(); 560 UsageEvents events = mUsageStatsManager.queryEvents(startTime, endTime); 561 boolean found = false; 562 Event event = new Event(); 563 while (events.hasNextEvent()) { 564 events.getNextEvent(event); 565 if (event.getEventType() == Event.NOTIFICATION_SEEN) { 566 found = true; 567 } 568 } 569 assertFalse(found); 570 // Pull down shade 571 mUiDevice.openNotification(); 572 outer: 573 for (int i = 0; i < 5; i++) { 574 Thread.sleep(500); 575 endTime = System.currentTimeMillis(); 576 events = mUsageStatsManager.queryEvents(startTime, endTime); 577 found = false; 578 while (events.hasNextEvent()) { 579 events.getNextEvent(event); 580 if (event.getEventType() == Event.NOTIFICATION_SEEN) { 581 found = true; 582 break outer; 583 } 584 } 585 } 586 assertTrue(found); 587 mUiDevice.pressBack(); 588 } 589 590 static final int[] INTERACTIVE_EVENTS = new int[] { 591 Event.SCREEN_INTERACTIVE, 592 Event.SCREEN_NON_INTERACTIVE 593 }; 594 595 static final int[] KEYGUARD_EVENTS = new int[] { 596 Event.KEYGUARD_SHOWN, 597 Event.KEYGUARD_HIDDEN 598 }; 599 600 static final int[] ALL_EVENTS = new int[] { 601 Event.SCREEN_INTERACTIVE, 602 Event.SCREEN_NON_INTERACTIVE, 603 Event.KEYGUARD_SHOWN, 604 Event.KEYGUARD_HIDDEN 605 }; 606 607 private long getEvents(int[] whichEvents, long startTime, List<Event> out) { 608 final long endTime = System.currentTimeMillis(); 609 if (DEBUG) Log.i(TAG, "Looking for events " + Arrays.toString(whichEvents) 610 + " between " + startTime + " and " + endTime); 611 UsageEvents events = mUsageStatsManager.queryEvents(startTime, endTime); 612 613 long latestTime = 0; 614 615 // Find events. 616 while (events.hasNextEvent()) { 617 UsageEvents.Event event = new UsageEvents.Event(); 618 assertTrue(events.getNextEvent(event)); 619 final int ev = event.getEventType(); 620 for (int which : whichEvents) { 621 if (ev == which) { 622 if (out != null) { 623 out.add(event); 624 } 625 if (DEBUG) Log.i(TAG, "Next event type " + event.getEventType() 626 + " time=" + event.getTimeStamp()); 627 if (latestTime < event.getTimeStamp()) { 628 latestTime = event.getTimeStamp(); 629 } 630 break; 631 } 632 } 633 } 634 635 return latestTime; 636 } 637 638 private ArrayList<Event> waitForEventCount(int[] whichEvents, long startTime, int count) { 639 final ArrayList<Event> events = new ArrayList<>(); 640 final long endTime = SystemClock.uptimeMillis() + 2000; 641 do { 642 events.clear(); 643 getEvents(whichEvents, startTime, events); 644 if (events.size() == count) { 645 return events; 646 } 647 if (events.size() > count) { 648 fail("Found too many events: got " + events.size() + ", expected " + count); 649 return events; 650 } 651 SystemClock.sleep(10); 652 } while (SystemClock.uptimeMillis() < endTime); 653 654 fail("Timed out waiting for " + count + " events, only reached " + events.size()); 655 return events; 656 } 657 658 static class AggrEventData { 659 final String label; 660 int count; 661 long duration; 662 long lastEventTime; 663 664 AggrEventData(String label) { 665 this.label = label; 666 } 667 } 668 669 static class AggrAllEventsData { 670 final AggrEventData interactive = new AggrEventData("Interactive"); 671 final AggrEventData nonInteractive = new AggrEventData("Non-interactive"); 672 final AggrEventData keyguardShown = new AggrEventData("Keyguard shown"); 673 final AggrEventData keyguardHidden = new AggrEventData("Keyguard hidden"); 674 int interactiveCount; 675 long interactiveDuration; 676 long interactiveLastEventTime; 677 int nonInteractiveCount; 678 long nonInteractiveDuration; 679 long nonInteractiveLastEventTime; 680 int keyguardShownCount; 681 long keyguardShownDuration; 682 int keyguardHiddenCount; 683 long keyguardHiddenDuration; 684 } 685 686 private SparseArray<AggrAllEventsData> getAggrEventData(long beforeTime) { 687 final long endTime = System.currentTimeMillis(); 688 689 final SparseLongArray intervalLengths = new SparseLongArray(); 690 intervalLengths.put(UsageStatsManager.INTERVAL_DAILY, DAY); 691 intervalLengths.put(UsageStatsManager.INTERVAL_WEEKLY, WEEK); 692 intervalLengths.put(UsageStatsManager.INTERVAL_MONTHLY, MONTH); 693 intervalLengths.put(UsageStatsManager.INTERVAL_YEARLY, YEAR); 694 695 final SparseArray<AggrAllEventsData> allAggr = new SparseArray<>(); 696 697 final int intervalCount = intervalLengths.size(); 698 for (int i = 0; i < intervalCount; i++) { 699 final int intervalType = intervalLengths.keyAt(i); 700 final long intervalDuration = intervalLengths.valueAt(i); 701 final long startTime = endTime - (2 * intervalDuration); 702 List<EventStats> statsList = mUsageStatsManager.queryEventStats(intervalType, 703 startTime, endTime); 704 assertFalse(statsList.isEmpty()); 705 706 final AggrAllEventsData aggr = new AggrAllEventsData(); 707 allAggr.put(intervalType, aggr); 708 709 boolean foundEvent = false; 710 for (EventStats stats : statsList) { 711 // Verify that each period is a day long. 712 //assertLessThanOrEqual(stats.getLastTimeStamp() - stats.getFirstTimeStamp(), 713 // intervalDuration); 714 AggrEventData data = null; 715 switch (stats.getEventType()) { 716 case Event.SCREEN_INTERACTIVE: 717 data = aggr.interactive; 718 break; 719 case Event.SCREEN_NON_INTERACTIVE: 720 data = aggr.nonInteractive; 721 break; 722 case Event.KEYGUARD_HIDDEN: 723 data = aggr.keyguardHidden; 724 break; 725 case Event.KEYGUARD_SHOWN: 726 data = aggr.keyguardShown; 727 break; 728 } 729 if (data != null) { 730 foundEvent = true; 731 data.count += stats.getCount(); 732 data.duration += stats.getTotalTime(); 733 if (data.lastEventTime < stats.getLastEventTime()) { 734 data.lastEventTime = stats.getLastEventTime(); 735 } 736 } 737 } 738 739 assertTrue("Did not find event data in interval " + intervalType, 740 foundEvent); 741 } 742 743 return allAggr; 744 } 745 746 private void verifyCount(int oldCount, int newCount, boolean larger, String label, 747 int interval) { 748 if (larger) { 749 if (newCount <= oldCount) { 750 fail(label + " count newer " + newCount 751 + " expected to be larger than older " + oldCount 752 + " @ interval " + interval); 753 } 754 } else { 755 if (newCount != oldCount) { 756 fail(label + " count newer " + newCount 757 + " expected to be same as older " + oldCount 758 + " @ interval " + interval); 759 } 760 } 761 } 762 763 private void verifyDuration(long oldDur, long newDur, boolean larger, String label, 764 int interval) { 765 if (larger) { 766 if (newDur <= oldDur) { 767 fail(label + " duration newer " + newDur 768 + " expected to be larger than older " + oldDur 769 + " @ interval " + interval); 770 } 771 } else { 772 if (newDur != oldDur) { 773 fail(label + " duration newer " + newDur 774 + " expected to be same as older " + oldDur 775 + " @ interval " + interval); 776 } 777 } 778 } 779 780 private void verifyAggrEventData(AggrEventData older, AggrEventData newer, 781 boolean countLarger, boolean durationLarger, int interval) { 782 verifyCount(older.count, newer.count, countLarger, older.label, interval); 783 verifyDuration(older.duration, newer.duration, durationLarger, older.label, interval); 784 } 785 786 private void verifyAggrInteractiveEventData(SparseArray<AggrAllEventsData> older, 787 SparseArray<AggrAllEventsData> newer, boolean interactiveLarger, 788 boolean nonInteractiveLarger) { 789 for (int i = 0; i < older.size(); i++) { 790 AggrAllEventsData o = older.valueAt(i); 791 AggrAllEventsData n = newer.valueAt(i); 792 // When we are told something is larger, that means we have transitioned 793 // *out* of that state -- so the duration of that state is expected to 794 // increase, but the count should stay the same (and the count of the state 795 // we transition to is increased). 796 final int interval = older.keyAt(i); 797 verifyAggrEventData(o.interactive, n.interactive, nonInteractiveLarger, 798 interactiveLarger, interval); 799 verifyAggrEventData(o.nonInteractive, n.nonInteractive, interactiveLarger, 800 nonInteractiveLarger, interval); 801 } 802 } 803 804 private void verifyAggrKeyguardEventData(SparseArray<AggrAllEventsData> older, 805 SparseArray<AggrAllEventsData> newer, boolean hiddenLarger, 806 boolean shownLarger) { 807 for (int i = 0; i < older.size(); i++) { 808 AggrAllEventsData o = older.valueAt(i); 809 AggrAllEventsData n = newer.valueAt(i); 810 // When we are told something is larger, that means we have transitioned 811 // *out* of that state -- so the duration of that state is expected to 812 // increase, but the count should stay the same (and the count of the state 813 // we transition to is increased). 814 final int interval = older.keyAt(i); 815 verifyAggrEventData(o.keyguardHidden, n.keyguardHidden, shownLarger, 816 hiddenLarger, interval); 817 verifyAggrEventData(o.keyguardShown, n.keyguardShown, hiddenLarger, 818 shownLarger, interval); 819 } 820 } 821 822 @AppModeFull(reason = "No usage events access in instant apps") 823 @Test 824 public void testInteractiveEvents() throws Exception { 825 final KeyguardManager kmgr = InstrumentationRegistry.getInstrumentation() 826 .getContext().getSystemService(KeyguardManager.class); 827 828 // We need to start out with the screen on. 829 if (!mUiDevice.isScreenOn()) { 830 pressWakeUp(); 831 SystemClock.sleep(1000); 832 } 833 834 // Also want to start out with the keyguard dismissed. 835 if (kmgr.isKeyguardLocked()) { 836 final long startTime = getEvents(KEYGUARD_EVENTS, 0, null) + 1; 837 mUiDevice.executeShellCommand("wm dismiss-keyguard"); 838 ArrayList<Event> events = waitForEventCount(KEYGUARD_EVENTS, startTime, 1); 839 assertEquals(Event.KEYGUARD_HIDDEN, events.get(0).getEventType()); 840 SystemClock.sleep(500); 841 } 842 843 try { 844 ArrayList<Event> events; 845 846 // Determine time to start looking for events. 847 final long startTime = getEvents(ALL_EVENTS, 0, null) + 1; 848 SparseArray<AggrAllEventsData> baseAggr = getAggrEventData(0); 849 850 // First test -- put device to sleep and make sure we see this event. 851 pressSleep(); 852 853 // Do we have one event, going in to non-interactive mode? 854 events = waitForEventCount(INTERACTIVE_EVENTS, startTime, 1); 855 assertEquals(Event.SCREEN_NON_INTERACTIVE, events.get(0).getEventType()); 856 SparseArray<AggrAllEventsData> offAggr = getAggrEventData(startTime); 857 verifyAggrInteractiveEventData(baseAggr, offAggr, true, false); 858 859 // Next test -- turn screen on and make sure we have a second event. 860 // XXX need to wait a bit so we don't accidentally trigger double-power 861 // to launch camera. (SHOULD FIX HOW WE WAKEUP / SLEEP TO NOT USE POWER KEY) 862 SystemClock.sleep(500); 863 pressWakeUp(); 864 events = waitForEventCount(INTERACTIVE_EVENTS, startTime, 2); 865 assertEquals(Event.SCREEN_NON_INTERACTIVE, events.get(0).getEventType()); 866 assertEquals(Event.SCREEN_INTERACTIVE, events.get(1).getEventType()); 867 SparseArray<AggrAllEventsData> onAggr = getAggrEventData(startTime); 868 verifyAggrInteractiveEventData(offAggr, onAggr, false, true); 869 870 // If the device is doing a lock screen, verify that we are also seeing the 871 // appropriate keyguard behavior. We don't know the timing from when the screen 872 // will go off until the keyguard is shown, so we will do this all after turning 873 // the screen back on (at which point it must be shown). 874 // XXX CTS seems to be preventing the keyguard from showing, so this path is 875 // never being tested. 876 if (kmgr.isKeyguardLocked()) { 877 events = waitForEventCount(KEYGUARD_EVENTS, startTime, 1); 878 assertEquals(Event.KEYGUARD_SHOWN, events.get(0).getEventType()); 879 SparseArray<AggrAllEventsData> shownAggr = getAggrEventData(startTime); 880 verifyAggrKeyguardEventData(offAggr, shownAggr, true, false); 881 882 // Now dismiss the keyguard and verify the resulting events. 883 mUiDevice.executeShellCommand("wm dismiss-keyguard"); 884 events = waitForEventCount(KEYGUARD_EVENTS, startTime, 2); 885 assertEquals(Event.KEYGUARD_SHOWN, events.get(0).getEventType()); 886 assertEquals(Event.KEYGUARD_HIDDEN, events.get(1).getEventType()); 887 SparseArray<AggrAllEventsData> hiddenAggr = getAggrEventData(startTime); 888 verifyAggrKeyguardEventData(shownAggr, hiddenAggr, false, true); 889 } 890 891 } finally { 892 // Dismiss keyguard to get device back in its normal state. 893 pressWakeUp(); 894 mUiDevice.executeShellCommand("wm dismiss-keyguard"); 895 } 896 } 897 898 @Test 899 public void testIgnoreNonexistentPackage() throws Exception { 900 final String fakePackageName = "android.fake.package.name"; 901 final int defaultValue = -1; 902 903 mUiDevice.executeShellCommand("am set-standby-bucket " + fakePackageName + " rare"); 904 // Verify the above does not add a new entry to the App Standby bucket map 905 Map<String, Integer> bucketMap = mUsageStatsManager.getAppStandbyBuckets(); 906 int bucket = bucketMap.getOrDefault(fakePackageName, defaultValue); 907 assertFalse("Meaningful bucket value " + bucket + " returned for " + fakePackageName 908 + " after set-standby-bucket", bucket > 0); 909 910 mUiDevice.executeShellCommand("am get-standby-bucket " + fakePackageName); 911 // Verify the above does not add a new entry to the App Standby bucket map 912 bucketMap = mUsageStatsManager.getAppStandbyBuckets(); 913 bucket = bucketMap.getOrDefault(fakePackageName, defaultValue); 914 assertFalse("Meaningful bucket value " + bucket + " returned for " + fakePackageName 915 + " after get-standby-bucket", bucket > 0); 916 } 917 918 @Test 919 public void testObserveUsagePermissionForRegisterObserver() { 920 final int observerId = 0; 921 final String[] packages = new String[] {"com.android.settings"}; 922 923 try { 924 mUsageStatsManager.registerAppUsageObserver(observerId, packages, 925 1, java.util.concurrent.TimeUnit.HOURS, null); 926 fail("Expected SecurityException for an app not holding OBSERVE_APP_USAGE permission."); 927 } catch (SecurityException e) { 928 // Exception expected 929 } 930 931 try { 932 mUsageStatsManager.registerUsageSessionObserver(observerId, packages, 933 Duration.ofHours(1), Duration.ofSeconds(10), null, null); 934 fail("Expected SecurityException for an app not holding OBSERVE_APP_USAGE permission."); 935 } catch (SecurityException e) { 936 // Exception expected 937 } 938 939 try { 940 mUsageStatsManager.registerAppUsageLimitObserver(observerId, packages, 941 Duration.ofHours(1), Duration.ofHours(0), null); 942 fail("Expected SecurityException for an app not holding OBSERVE_APP_USAGE permission."); 943 } catch (SecurityException e) { 944 // Exception expected 945 } 946 } 947 948 @Test 949 public void testObserveUsagePermissionForUnregisterObserver() { 950 final int observerId = 0; 951 952 try { 953 mUsageStatsManager.unregisterAppUsageObserver(observerId); 954 fail("Expected SecurityException for an app not holding OBSERVE_APP_USAGE permission."); 955 } catch (SecurityException e) { 956 // Exception expected 957 } 958 959 try { 960 mUsageStatsManager.unregisterUsageSessionObserver(observerId); 961 fail("Expected SecurityException for an app not holding OBSERVE_APP_USAGE permission."); 962 } catch (SecurityException e) { 963 // Exception expected 964 } 965 966 try { 967 mUsageStatsManager.unregisterAppUsageLimitObserver(observerId); 968 fail("Expected SecurityException for an app not holding OBSERVE_APP_USAGE permission."); 969 } catch (SecurityException e) { 970 // Exception expected 971 } 972 } 973 974 @AppModeFull(reason = "No usage events access in instant apps") 975 @Test 976 public void testForegroundService() throws Exception { 977 // This test start a foreground service then stop it. The event list should have one 978 // FOREGROUND_SERVICE_START and one FOREGROUND_SERVICE_STOP event. 979 final long startTime = System.currentTimeMillis(); 980 final Context context = InstrumentationRegistry.getInstrumentation().getContext(); 981 context.startService(new Intent(context, TestService.class)); 982 mUiDevice.wait(Until.hasObject(By.clazz(TestService.class)), TIMEOUT); 983 final long sleepTime = 500; 984 SystemClock.sleep(sleepTime); 985 context.stopService(new Intent(context, TestService.class)); 986 mUiDevice.wait(Until.gone(By.clazz(TestService.class)), TIMEOUT); 987 final long endTime = System.currentTimeMillis(); 988 UsageEvents events = mUsageStatsManager.queryEvents(startTime, endTime); 989 990 int numStarts = 0; 991 int numStops = 0; 992 int startIdx = -1; 993 int stopIdx = -1; 994 int i = 0; 995 while (events.hasNextEvent()) { 996 UsageEvents.Event event = new UsageEvents.Event(); 997 assertTrue(events.getNextEvent(event)); 998 if (mTargetPackage.equals(event.getPackageName()) 999 || TestService.class.getName().equals(event.getClassName())) { 1000 if (event.getEventType() == Event.FOREGROUND_SERVICE_START) { 1001 numStarts++; 1002 startIdx = i; 1003 } else if (event.getEventType() == Event.FOREGROUND_SERVICE_STOP) { 1004 numStops++; 1005 stopIdx = i; 1006 } 1007 i++; 1008 } 1009 } 1010 // One FOREGROUND_SERVICE_START event followed by one FOREGROUND_SERVICE_STOP event. 1011 assertEquals(numStarts, 1); 1012 assertEquals(numStops, 1); 1013 assertLessThan(startIdx, stopIdx); 1014 1015 final Map<String, UsageStats> map = mUsageStatsManager.queryAndAggregateUsageStats( 1016 startTime, endTime); 1017 final UsageStats stats = map.get(mTargetPackage); 1018 assertNotNull(stats); 1019 final long lastTimeUsed = stats.getLastTimeForegroundServiceUsed(); 1020 // lastTimeUsed should be falling between startTime and endTime. 1021 assertLessThan(startTime, lastTimeUsed); 1022 assertLessThan(lastTimeUsed, endTime); 1023 final long totalTimeUsed = stats.getTotalTimeForegroundServiceUsed(); 1024 // because we slept for 500 milliseconds earlier, we know the totalTimeUsed must be more 1025 // more than 500 milliseconds. 1026 assertLessThan(sleepTime, totalTimeUsed); 1027 } 1028 1029 @AppModeFull(reason = "No usage events access in instant apps") 1030 @Test 1031 public void testTaskRootEventField() throws Exception { 1032 final KeyguardManager kmgr = InstrumentationRegistry.getInstrumentation() 1033 .getContext().getSystemService(KeyguardManager.class); 1034 mUiDevice.wakeUp(); 1035 // Also want to start out with the keyguard dismissed. 1036 if (kmgr.isKeyguardLocked()) { 1037 final long startTime = getEvents(KEYGUARD_EVENTS, 0, null) + 1; 1038 mUiDevice.executeShellCommand("wm dismiss-keyguard"); 1039 ArrayList<Event> events = waitForEventCount(KEYGUARD_EVENTS, startTime, 1); 1040 assertEquals(Event.KEYGUARD_HIDDEN, events.get(0).getEventType()); 1041 SystemClock.sleep(500); 1042 } 1043 1044 final long startTime = System.currentTimeMillis(); 1045 launchSubActivity(TaskRootActivity.class); 1046 final long endTime = System.currentTimeMillis(); 1047 UsageEvents events = mUsageStatsManager.queryEvents(startTime, endTime); 1048 1049 while (events.hasNextEvent()) { 1050 UsageEvents.Event event = new UsageEvents.Event(); 1051 assertTrue(events.getNextEvent(event)); 1052 if (TaskRootActivity.TEST_APP_PKG.equals(event.getPackageName()) 1053 && TaskRootActivity.TEST_APP_CLASS.equals(event.getClassName())) { 1054 assertEquals(mTargetPackage, event.getTaskRootPackageName()); 1055 assertEquals(TaskRootActivity.class.getCanonicalName(), 1056 event.getTaskRootClassName()); 1057 return; 1058 } 1059 } 1060 fail("Did not find nested activity name in usage events"); 1061 } 1062 1063 @AppModeFull(reason = "No usage events access in instant apps") 1064 @Test 1065 public void testUsageSourceAttribution() throws Exception { 1066 final KeyguardManager kmgr = InstrumentationRegistry.getInstrumentation() 1067 .getContext().getSystemService(KeyguardManager.class); 1068 mUiDevice.wakeUp(); 1069 // Also want to start out with the keyguard dismissed. 1070 if (kmgr.isKeyguardLocked()) { 1071 final long startTime = getEvents(KEYGUARD_EVENTS, 0, null) + 1; 1072 mUiDevice.executeShellCommand("wm dismiss-keyguard"); 1073 ArrayList<Event> events = waitForEventCount(KEYGUARD_EVENTS, startTime, 1); 1074 assertEquals(Event.KEYGUARD_HIDDEN, events.get(0).getEventType()); 1075 SystemClock.sleep(500); 1076 } 1077 1078 mUiDevice.pressHome(); 1079 1080 setUsageSourceSetting(Integer.toString(mUsageStatsManager.USAGE_SOURCE_CURRENT_ACTIVITY)); 1081 launchSubActivity(TaskRootActivity.class); 1082 // Usage should be attributed to the test app package 1083 assertAppOrTokenUsed(TaskRootActivity.TEST_APP_PKG, true); 1084 1085 mUiDevice.pressHome(); 1086 1087 setUsageSourceSetting(Integer.toString( 1088 mUsageStatsManager.USAGE_SOURCE_TASK_ROOT_ACTIVITY)); 1089 launchSubActivity(TaskRootActivity.class); 1090 // Usage should be attributed to this package 1091 assertAppOrTokenUsed(mTargetPackage, true); 1092 } 1093 1094 @AppModeInstant 1095 @Test 1096 public void testInstantAppUsageEventsObfuscated() throws Exception { 1097 @SuppressWarnings("unchecked") 1098 final Class<? extends Activity>[] activitySequence = new Class[] { 1099 Activities.ActivityOne.class, 1100 Activities.ActivityTwo.class, 1101 Activities.ActivityThree.class, 1102 }; 1103 mUiDevice.wakeUp(); 1104 mUiDevice.pressHome(); 1105 1106 final long startTime = System.currentTimeMillis(); 1107 // Launch the series of Activities. 1108 launchSubActivities(activitySequence); 1109 SystemClock.sleep(250); 1110 1111 final long endTime = System.currentTimeMillis(); 1112 final UsageEvents events = mUsageStatsManager.queryEvents(startTime, endTime); 1113 1114 int resumes = 0; 1115 int pauses = 0; 1116 int stops = 0; 1117 1118 // Only look at events belongs to mTargetPackage. 1119 ArrayList<UsageEvents.Event> eventList = new ArrayList<>(); 1120 while (events.hasNextEvent()) { 1121 final UsageEvents.Event event = new UsageEvents.Event(); 1122 assertTrue(events.getNextEvent(event)); 1123 // There should be no events with this packages name 1124 assertFalse("Instant app package name found in usage event list", 1125 mTargetPackage.equals(event.getPackageName())); 1126 1127 // Look for the obfuscated instant app string instead 1128 if(UsageEvents.INSTANT_APP_PACKAGE_NAME.equals(event.getPackageName())) { 1129 switch (event.mEventType) { 1130 case Event.ACTIVITY_RESUMED: 1131 resumes++; 1132 break; 1133 case Event.ACTIVITY_PAUSED: 1134 pauses++; 1135 break; 1136 case Event.ACTIVITY_STOPPED: 1137 stops++; 1138 break; 1139 } 1140 } 1141 } 1142 assertEquals("Unexpected number of activity resumes", 3, resumes); 1143 assertEquals("Unexpected number of activity pauses", 2, pauses); 1144 assertEquals("Unexpected number of activity stops", 2, stops); 1145 } 1146 1147 1148 1149 @AppModeFull(reason = "No usage events access in instant apps") 1150 @Test 1151 public void testSuddenDestroy() throws Exception { 1152 final Context context = InstrumentationRegistry.getInstrumentation().getContext(); 1153 final KeyguardManager kmgr = InstrumentationRegistry.getInstrumentation() 1154 .getContext().getSystemService(KeyguardManager.class); 1155 mUiDevice.wakeUp(); 1156 // Also want to start out with the keyguard dismissed. 1157 if (kmgr.isKeyguardLocked()) { 1158 final long startTime = getEvents(KEYGUARD_EVENTS, 0, null) + 1; 1159 mUiDevice.executeShellCommand("wm dismiss-keyguard"); 1160 ArrayList<Event> events = waitForEventCount(KEYGUARD_EVENTS, startTime, 1); 1161 assertEquals(Event.KEYGUARD_HIDDEN, events.get(0).getEventType()); 1162 SystemClock.sleep(500); 1163 } 1164 1165 mUiDevice.pressHome(); 1166 1167 final long startTime = System.currentTimeMillis(); 1168 final ActivityManager mAm = context.getSystemService(ActivityManager.class); 1169 1170 Intent intent = new Intent(); 1171 intent.setClassName(TEST_APP_PKG, TEST_APP_CLASS); 1172 intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK); 1173 context.startActivity(intent); 1174 mUiDevice.wait(Until.hasObject(By.clazz(TEST_APP_PKG, TEST_APP_CLASS)), TIMEOUT); 1175 SystemClock.sleep(500); 1176 1177 // Destroy the activity 1178 SystemUtil.runWithShellPermissionIdentity(() -> mAm.forceStopPackage(TEST_APP_PKG)); 1179 mUiDevice.wait(Until.gone(By.clazz(TEST_APP_PKG, TEST_APP_CLASS)), TIMEOUT); 1180 SystemClock.sleep(500); 1181 1182 final long endTime = System.currentTimeMillis(); 1183 final UsageEvents events = mUsageStatsManager.queryEvents(startTime, endTime); 1184 1185 int resumes = 0; 1186 int stops = 0; 1187 1188 while (events.hasNextEvent()) { 1189 final UsageEvents.Event event = new UsageEvents.Event(); 1190 assertTrue(events.getNextEvent(event)); 1191 1192 if(TEST_APP_PKG.equals(event.getPackageName())) { 1193 switch (event.mEventType) { 1194 case Event.ACTIVITY_RESUMED: 1195 assertNotNull("ACTIVITY_RESUMED event Task Root should not be null", 1196 event.getTaskRootPackageName()); 1197 resumes++; 1198 break; 1199 case Event.ACTIVITY_STOPPED: 1200 assertNotNull("ACTIVITY_STOPPED event Task Root should not be null", 1201 event.getTaskRootPackageName()); 1202 stops++; 1203 break; 1204 } 1205 } 1206 } 1207 assertEquals("Unexpected number of activity resumes", 1, resumes); 1208 assertEquals("Unexpected number of activity stops", 1, stops); 1209 } 1210 1211 private void pressWakeUp() { 1212 mUiDevice.pressKeyCode(KeyEvent.KEYCODE_WAKEUP); 1213 } 1214 1215 private void pressSleep() { 1216 mUiDevice.pressKeyCode(KeyEvent.KEYCODE_SLEEP); 1217 } 1218 1219 /** 1220 * Assert on an app or token's usage state. 1221 * @param entity name of the app or token 1222 * @param expected expected usage state, true for in use, false for not in use 1223 */ 1224 private void assertAppOrTokenUsed(String entity, boolean expected) throws IOException { 1225 final String activeUsages = 1226 mUiDevice.executeShellCommand("dumpsys usagestats apptimelimit actives"); 1227 final String[] actives = activeUsages.split("\n"); 1228 boolean found = false; 1229 1230 for (String active : actives) { 1231 if (active.equals(entity)) { 1232 found = true; 1233 break; 1234 } 1235 } 1236 if (expected) { 1237 assertTrue(entity + " not found in list of active activities and tokens\n" 1238 + activeUsages, found); 1239 } else { 1240 assertFalse(entity + " found in list of active activities and tokens\n" 1241 + activeUsages, found); 1242 } 1243 } 1244 } 1245