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