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