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