1 /* 2 * Copyright (C) 2018 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy 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, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License 15 */ 16 17 package android.app.usage.cts; 18 19 import static android.server.wm.SplitScreenActivityUtils.supportsSplitScreenMultiWindow; 20 21 import static org.junit.Assert.fail; 22 import static org.junit.Assume.assumeFalse; 23 import static org.junit.Assume.assumeTrue; 24 25 import static java.util.Objects.requireNonNull; 26 27 import android.app.Activity; 28 import android.app.Instrumentation; 29 import android.app.usage.UsageStatsManager; 30 import android.content.ComponentName; 31 import android.content.Context; 32 import android.content.pm.PackageManager; 33 import android.os.UserManager; 34 import android.platform.test.annotations.AppModeFull; 35 import android.server.wm.LaunchActivityBuilder; 36 import android.server.wm.LockScreenSession; 37 import android.server.wm.NestedShellPermission; 38 import android.server.wm.SplitScreenActivityUtils; 39 import android.server.wm.TestTaskOrganizer; 40 import android.server.wm.UiDeviceUtils; 41 import android.server.wm.WindowManagerStateHelper; 42 43 import androidx.annotation.NonNull; 44 import androidx.test.platform.app.InstrumentationRegistry; 45 import androidx.test.uiautomator.UiDevice; 46 47 import com.android.bedstead.harrier.DeviceState; 48 import com.android.bedstead.multiuser.annotations.RequireNotVisibleBackgroundUsers; 49 import com.android.compatibility.common.util.TestUtils; 50 51 import org.junit.After; 52 import org.junit.Before; 53 import org.junit.ClassRule; 54 import org.junit.Rule; 55 import org.junit.Test; 56 57 /** 58 * Test the UsageStats API around usage reporting against tokens 59 * Run test: atest CtsUsageStatsTestCases:UsageReportingTest 60 */ 61 @AppModeFull 62 public class UsageReportingTest { 63 64 private static final String TARGET_PACKAGE = Activities.class.getPackageName(); 65 private static final String TOKEN_0 = "SuperSecretToken"; 66 private static final String TOKEN_1 = "AnotherSecretToken"; 67 private static final String FULL_TOKEN_0 = TARGET_PACKAGE + "/" + TOKEN_0; 68 private static final String FULL_TOKEN_1 = TARGET_PACKAGE + "/" + TOKEN_1; 69 70 private static final ComponentName ACTIVITY_ONE_COMPONENT = 71 new ComponentName(TARGET_PACKAGE, Activities.ActivityOne.class.getName()); 72 private static final ComponentName ACTIVITY_TWO_COMPONENT = 73 new ComponentName(TARGET_PACKAGE, Activities.ActivityTwo.class.getName()); 74 75 private static final String DEVICE_SLEEP_COMMAND = "input keyevent KEYCODE_SLEEP"; 76 77 private static final int ASSERT_TIMEOUT_SECONDS = 5; // 5 seconds 78 79 @NonNull 80 private final WindowManagerStateHelper mWmState = new WindowManagerStateHelper(); 81 @NonNull 82 private Instrumentation mInstrumentation; 83 @NonNull 84 private UiDevice mUiDevice; 85 @NonNull 86 private Context mContext; 87 @NonNull 88 private UsageStatsManager mUsageStatsManager; 89 @NonNull 90 private TestTaskOrganizer mTaskOrganizer; 91 @NonNull 92 private SplitScreenActivityUtils mSplitScreenActivityUtils; 93 94 @ClassRule 95 @Rule 96 public static final DeviceState sDeviceState = new DeviceState(); 97 98 @Before setUp()99 public void setUp() { 100 mInstrumentation = InstrumentationRegistry.getInstrumentation(); 101 mUiDevice = UiDevice.getInstance(mInstrumentation); 102 mContext = mInstrumentation.getContext(); 103 mUsageStatsManager = requireNonNull(mContext.getSystemService(UsageStatsManager.class)); 104 UiDeviceUtils.wakeUpAndUnlock(mContext); 105 NestedShellPermission.run(() -> { 106 // TaskOrganizer ctor requires MANAGE_ACTIVITY_TASKS permission 107 mTaskOrganizer = new TestTaskOrganizer(); 108 }); 109 mSplitScreenActivityUtils = new SplitScreenActivityUtils(mWmState, mTaskOrganizer); 110 } 111 112 @After tearDown()113 public void tearDown() { 114 if (mTaskOrganizer != null) { 115 mTaskOrganizer.unregisterOrganizerIfNeeded(); 116 } 117 Activities.sStartedActivities.clear(); 118 } 119 120 @Test testUsageStartAndStopReporting()121 public void testUsageStartAndStopReporting() throws Exception { 122 launchActivity(ACTIVITY_ONE_COMPONENT); 123 124 final Activity activity; 125 synchronized (Activities.sStartedActivities) { 126 activity = Activities.sStartedActivities.valueAt(0); 127 } 128 129 mUsageStatsManager.reportUsageStart(activity, TOKEN_0); 130 assertAppOrTokenUsed(FULL_TOKEN_0, true); 131 132 mUsageStatsManager.reportUsageStop(activity, TOKEN_0); 133 assertAppOrTokenUsed(FULL_TOKEN_0, false); 134 135 136 mUsageStatsManager.reportUsageStart(activity, TOKEN_0); 137 assertAppOrTokenUsed(FULL_TOKEN_0, true); 138 139 mUsageStatsManager.reportUsageStop(activity, TOKEN_0); 140 assertAppOrTokenUsed(FULL_TOKEN_0, false); 141 } 142 143 @Test testUsagePastReporting()144 public void testUsagePastReporting() throws Exception { 145 launchActivity(ACTIVITY_ONE_COMPONENT); 146 147 final Activity activity; 148 synchronized (Activities.sStartedActivities) { 149 activity = Activities.sStartedActivities.valueAt(0); 150 } 151 152 mUsageStatsManager.reportUsageStart(activity, TOKEN_0, 100); 153 assertAppOrTokenUsed(FULL_TOKEN_0, true); 154 155 mUsageStatsManager.reportUsageStop(activity, TOKEN_0); 156 assertAppOrTokenUsed(FULL_TOKEN_0, false); 157 } 158 159 @Test 160 @RequireNotVisibleBackgroundUsers(reason = "KEYCODE_SLEEP doesn't support visible background" 161 + " user") testUsageReportingMissingStop()162 public void testUsageReportingMissingStop() throws Exception { 163 // TODO(b/330610015): This test should be re-enabled once PowerManager#isInteractive 164 // is fixed on form factors with visible background user. 165 assumeFalse(isAutomotiveWithVisibleBackgroundUser()); 166 167 launchActivity(ACTIVITY_ONE_COMPONENT); 168 169 final Activity activity; 170 synchronized (Activities.sStartedActivities) { 171 activity = Activities.sStartedActivities.valueAt(0); 172 } 173 174 mUsageStatsManager.reportUsageStart(activity, TOKEN_0); 175 assertAppOrTokenUsed(FULL_TOKEN_0, true); 176 177 // Send the device to sleep to get onStop called for the token reporting activities. 178 mUiDevice.executeShellCommand(DEVICE_SLEEP_COMMAND); 179 Thread.sleep(1000); 180 181 assertAppOrTokenUsed(FULL_TOKEN_0, false); 182 } 183 184 @Test testExceptionOnRepeatReport()185 public void testExceptionOnRepeatReport() throws Exception { 186 launchActivity(ACTIVITY_ONE_COMPONENT); 187 188 final Activity activity; 189 synchronized (Activities.sStartedActivities) { 190 activity = Activities.sStartedActivities.valueAt(0); 191 } 192 193 mUsageStatsManager.reportUsageStart(activity, TOKEN_0); 194 assertAppOrTokenUsed(FULL_TOKEN_0, true); 195 196 try { 197 mUsageStatsManager.reportUsageStart(activity, TOKEN_0); 198 fail("Should have thrown an IllegalArgumentException for double reporting start"); 199 } catch (IllegalArgumentException iae) { 200 //Expected exception 201 } 202 assertAppOrTokenUsed(FULL_TOKEN_0, true); 203 204 mUsageStatsManager.reportUsageStop(activity, TOKEN_0); 205 assertAppOrTokenUsed(FULL_TOKEN_0, false); 206 207 208 try { 209 mUsageStatsManager.reportUsageStop(activity, TOKEN_0); 210 fail("Should have thrown an IllegalArgumentException for double reporting stop"); 211 } catch (IllegalArgumentException iae) { 212 //Expected exception 213 } 214 215 // One more cycle of reporting just to make sure there was no underflow 216 mUsageStatsManager.reportUsageStart(activity, TOKEN_0); 217 assertAppOrTokenUsed(FULL_TOKEN_0, true); 218 219 mUsageStatsManager.reportUsageStop(activity, TOKEN_0); 220 assertAppOrTokenUsed(FULL_TOKEN_0, false); 221 } 222 223 @Test testMultipleTokenUsageReporting()224 public void testMultipleTokenUsageReporting() throws Exception { 225 launchActivity(ACTIVITY_ONE_COMPONENT); 226 227 final Activity activity; 228 synchronized (Activities.sStartedActivities) { 229 activity = Activities.sStartedActivities.valueAt(0); 230 } 231 232 mUsageStatsManager.reportUsageStart(activity, TOKEN_0); 233 mUsageStatsManager.reportUsageStart(activity, TOKEN_1); 234 assertAppOrTokenUsed(FULL_TOKEN_0, true); 235 assertAppOrTokenUsed(FULL_TOKEN_1, true); 236 237 mUsageStatsManager.reportUsageStop(activity, TOKEN_0); 238 assertAppOrTokenUsed(FULL_TOKEN_0, false); 239 assertAppOrTokenUsed(FULL_TOKEN_1, true); 240 241 mUsageStatsManager.reportUsageStop(activity, TOKEN_1); 242 assertAppOrTokenUsed(FULL_TOKEN_0, false); 243 assertAppOrTokenUsed(FULL_TOKEN_1, false); 244 } 245 246 @Test 247 @RequireNotVisibleBackgroundUsers(reason = "KEYCODE_SLEEP doesn't support visible background" 248 + " user") testMultipleTokenMissingStop()249 public void testMultipleTokenMissingStop() throws Exception { 250 // TODO(b/330610015): This test should be re-enabled once PowerManager#isInteractive 251 // is fixed on form factors with visible background user. 252 assumeFalse(isAutomotiveWithVisibleBackgroundUser()); 253 254 launchActivity(ACTIVITY_ONE_COMPONENT); 255 256 final Activity activity; 257 synchronized (Activities.sStartedActivities) { 258 activity = Activities.sStartedActivities.valueAt(0); 259 } 260 261 mUsageStatsManager.reportUsageStart(activity, TOKEN_0); 262 mUsageStatsManager.reportUsageStart(activity, TOKEN_1); 263 assertAppOrTokenUsed(FULL_TOKEN_0, true); 264 assertAppOrTokenUsed(FULL_TOKEN_1, true); 265 266 // Send the device to sleep to get onStop called for the token reporting activities. 267 mUiDevice.executeShellCommand(DEVICE_SLEEP_COMMAND); 268 Thread.sleep(1000); 269 270 assertAppOrTokenUsed(FULL_TOKEN_0, false); 271 assertAppOrTokenUsed(FULL_TOKEN_1, false); 272 } 273 274 @Test testSplitscreenUsageReporting()275 public void testSplitscreenUsageReporting() throws Exception { 276 assumeTrue("Skipping test: no multi-window support", 277 supportsSplitScreenMultiWindow(mContext)); 278 279 mSplitScreenActivityUtils.launchActivitiesInSplitScreen( 280 getLaunchActivityBuilder().setTargetActivity(ACTIVITY_ONE_COMPONENT), 281 getLaunchActivityBuilder().setTargetActivity(ACTIVITY_TWO_COMPONENT)); 282 Thread.sleep(500); 283 284 final Activity activity0; 285 final Activity activity1; 286 synchronized (Activities.sStartedActivities) { 287 activity0 = Activities.sStartedActivities.valueAt(0); 288 activity1 = Activities.sStartedActivities.valueAt(1); 289 } 290 291 mUsageStatsManager.reportUsageStart(activity0, TOKEN_0); 292 mUsageStatsManager.reportUsageStart(activity1, TOKEN_1); 293 assertAppOrTokenUsed(FULL_TOKEN_0, true); 294 assertAppOrTokenUsed(FULL_TOKEN_1, true); 295 296 mUsageStatsManager.reportUsageStop(activity0, TOKEN_0); 297 assertAppOrTokenUsed(FULL_TOKEN_0, false); 298 assertAppOrTokenUsed(FULL_TOKEN_1, true); 299 300 mUsageStatsManager.reportUsageStop(activity1, TOKEN_1); 301 assertAppOrTokenUsed(FULL_TOKEN_0, false); 302 assertAppOrTokenUsed(FULL_TOKEN_1, false); 303 } 304 305 @Test testSplitscreenSameToken()306 public void testSplitscreenSameToken() throws Exception { 307 assumeTrue("Skipping test: no multi-window support", 308 supportsSplitScreenMultiWindow(mContext)); 309 310 mSplitScreenActivityUtils.launchActivitiesInSplitScreen( 311 getLaunchActivityBuilder().setTargetActivity(ACTIVITY_ONE_COMPONENT), 312 getLaunchActivityBuilder().setTargetActivity(ACTIVITY_TWO_COMPONENT)); 313 Thread.sleep(500); 314 315 final Activity activity0; 316 final Activity activity1; 317 synchronized (Activities.sStartedActivities) { 318 activity0 = Activities.sStartedActivities.valueAt(0); 319 activity1 = Activities.sStartedActivities.valueAt(1); 320 } 321 322 mUsageStatsManager.reportUsageStart(activity0, TOKEN_0); 323 mUsageStatsManager.reportUsageStart(activity1, TOKEN_0); 324 assertAppOrTokenUsed(FULL_TOKEN_0, true); 325 326 mUsageStatsManager.reportUsageStop(activity0, TOKEN_0); 327 assertAppOrTokenUsed(FULL_TOKEN_0, true); 328 329 mUsageStatsManager.reportUsageStop(activity1, TOKEN_0); 330 assertAppOrTokenUsed(FULL_TOKEN_0, false); 331 } 332 333 @Test testSplitscreenSameTokenOneMissedStop()334 public void testSplitscreenSameTokenOneMissedStop() throws Exception { 335 assumeTrue("Skipping test: no multi-window support", 336 supportsSplitScreenMultiWindow(mContext)); 337 338 mSplitScreenActivityUtils.launchActivitiesInSplitScreen( 339 getLaunchActivityBuilder().setTargetActivity(ACTIVITY_ONE_COMPONENT), 340 getLaunchActivityBuilder().setTargetActivity(ACTIVITY_TWO_COMPONENT)); 341 Thread.sleep(500); 342 343 final Activity activity0; 344 final Activity activity1; 345 synchronized (Activities.sStartedActivities) { 346 activity0 = Activities.sStartedActivities.valueAt(0); 347 activity1 = Activities.sStartedActivities.valueAt(1); 348 } 349 350 mUsageStatsManager.reportUsageStart(activity0, TOKEN_0); 351 mUsageStatsManager.reportUsageStart(activity1, TOKEN_0); 352 assertAppOrTokenUsed(FULL_TOKEN_0, true); 353 354 mUsageStatsManager.reportUsageStop(activity0, TOKEN_0); 355 assertAppOrTokenUsed(FULL_TOKEN_0, true); 356 357 // Send the device to keyguard to get onStop called for the token reporting activities. 358 try (LockScreenSession lockScreenSession = 359 new LockScreenSession(mInstrumentation, mWmState)) { 360 lockScreenSession.gotoKeyguard(); 361 Thread.sleep(1000); 362 assertAppOrTokenUsed(FULL_TOKEN_0, false); 363 } 364 } 365 366 @Test testSplitscreenSameTokenTwoMissedStop()367 public void testSplitscreenSameTokenTwoMissedStop() throws Exception { 368 assumeTrue("Skipping test: no multi-window support", 369 supportsSplitScreenMultiWindow(mContext)); 370 371 mSplitScreenActivityUtils.launchActivitiesInSplitScreen( 372 getLaunchActivityBuilder().setTargetActivity(ACTIVITY_ONE_COMPONENT), 373 getLaunchActivityBuilder().setTargetActivity(ACTIVITY_TWO_COMPONENT)); 374 Thread.sleep(500); 375 376 final Activity activity0; 377 final Activity activity1; 378 synchronized (Activities.sStartedActivities) { 379 activity0 = Activities.sStartedActivities.valueAt(0); 380 activity1 = Activities.sStartedActivities.valueAt(1); 381 } 382 383 mUsageStatsManager.reportUsageStart(activity0, TOKEN_0); 384 mUsageStatsManager.reportUsageStart(activity1, TOKEN_0); 385 assertAppOrTokenUsed(FULL_TOKEN_0, true); 386 387 // Send the device to keyguard to get onStop called for the token reporting activities. 388 try (LockScreenSession lockScreenSession = 389 new LockScreenSession(mInstrumentation, mWmState)) { 390 lockScreenSession.gotoKeyguard(); 391 Thread.sleep(1000); 392 assertAppOrTokenUsed(FULL_TOKEN_0, false); 393 } 394 } 395 assertAppOrTokenUsed(String entity, boolean expected)396 private void assertAppOrTokenUsed(String entity, boolean expected) throws Exception { 397 final String failMessage; 398 if (expected) { 399 failMessage = entity + " not found in list of active activities and tokens"; 400 } else { 401 failMessage = entity + " found in list of active activities and tokens"; 402 } 403 404 TestUtils.waitUntil(failMessage, ASSERT_TIMEOUT_SECONDS, () -> { 405 final String activeUsages = 406 mUiDevice.executeShellCommand("dumpsys usagestats apptimelimit actives"); 407 final String[] actives = activeUsages.split("\n"); 408 boolean found = false; 409 410 for (String active : actives) { 411 if (active.equals(entity)) { 412 found = true; 413 break; 414 } 415 } 416 return found == expected; 417 }); 418 } 419 420 @NonNull getLaunchActivityBuilder()421 private LaunchActivityBuilder getLaunchActivityBuilder() { 422 return new LaunchActivityBuilder(mWmState); 423 } 424 launchActivity(@onNull ComponentName componentName)425 private void launchActivity(@NonNull ComponentName componentName) { 426 getLaunchActivityBuilder() 427 .setLaunchingActivity(componentName) 428 .setWaitForLaunched(true) 429 .execute(); 430 } 431 isAutomotiveWithVisibleBackgroundUser()432 private boolean isAutomotiveWithVisibleBackgroundUser() { 433 PackageManager packageManager = mContext.getPackageManager(); 434 UserManager userManager = mContext.getSystemService(UserManager.class); 435 return packageManager.hasSystemFeature(PackageManager.FEATURE_AUTOMOTIVE) 436 && userManager.isVisibleBackgroundUsersSupported(); 437 } 438 439 } 440