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