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