xref: /aosp_15_r20/cts/tests/tests/app.usage/src/android/app/usage/cts/UsageReportingTest.java (revision b7c941bb3fa97aba169d73cee0bed2de8ac964bf)
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