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