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