Home | History | Annotate | Download | only in cts
      1 /**
      2  * Copyright (C) 2014 The Android Open Source Project
      3  *
      4  * Licensed under the Apache License, Version 2.0 (the "License"); you may not
      5  * use this file except in compliance with the License. You may obtain a copy
      6  * of the License at
      7  *
      8  * http://www.apache.org/licenses/LICENSE-2.0
      9  *
     10  * Unless required by applicable law or agreed to in writing, software
     11  * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
     12  * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
     13  * License for the specific language governing permissions and limitations
     14  * under the License.
     15  */
     16 
     17 package android.app.usage.cts;
     18 
     19 import static org.junit.Assert.assertEquals;
     20 import static org.junit.Assert.assertFalse;
     21 import static org.junit.Assert.assertNotNull;
     22 import static org.junit.Assert.assertTrue;
     23 import static org.junit.Assert.fail;
     24 import static org.junit.Assume.assumeFalse;
     25 import static org.junit.Assume.assumeTrue;
     26 
     27 import android.app.*;
     28 import android.app.usage.EventStats;
     29 import android.app.usage.UsageEvents;
     30 import android.app.usage.UsageEvents.Event;
     31 import android.app.usage.UsageStats;
     32 import android.app.usage.UsageStatsManager;
     33 import android.content.Context;
     34 import android.content.Intent;
     35 import android.content.pm.PackageManager;
     36 import android.os.Parcel;
     37 import android.os.SystemClock;
     38 import android.platform.test.annotations.AppModeFull;
     39 import android.platform.test.annotations.AppModeInstant;
     40 import android.provider.Settings;
     41 import android.support.test.uiautomator.By;
     42 import android.support.test.uiautomator.UiDevice;
     43 import android.support.test.uiautomator.Until;
     44 import android.text.format.DateUtils;
     45 import android.util.Log;
     46 import android.util.SparseArray;
     47 import android.util.SparseLongArray;
     48 import android.view.KeyEvent;
     49 
     50 import androidx.test.InstrumentationRegistry;
     51 import androidx.test.runner.AndroidJUnit4;
     52 
     53 import com.android.compatibility.common.util.AppStandbyUtils;
     54 
     55 import com.android.compatibility.common.util.SystemUtil;
     56 import org.junit.After;
     57 import org.junit.Before;
     58 import org.junit.Ignore;
     59 import org.junit.Test;
     60 import org.junit.runner.RunWith;
     61 
     62 import java.io.IOException;
     63 import java.text.MessageFormat;
     64 import java.time.Duration;
     65 import java.util.ArrayList;
     66 import java.util.Arrays;
     67 import java.util.List;
     68 import java.util.Map;
     69 import java.util.concurrent.TimeUnit;
     70 
     71 /**
     72  * Test the UsageStats API. It is difficult to test the entire surface area
     73  * of the API, as a lot of the testing depends on what data is already present
     74  * on the device and for how long that data has been aggregating.
     75  *
     76  * These tests perform simple checks that each interval is of the correct duration,
     77  * and that events do appear in the event log.
     78  *
     79  * Tests to add that are difficult to add now:
     80  * - Invoking a device configuration change and then watching for it in the event log.
     81  * - Changing the system time and verifying that all data has been correctly shifted
     82  *   along with the new time.
     83  * - Proper eviction of old data.
     84  */
     85 @RunWith(AndroidJUnit4.class)
     86 public class UsageStatsTest {
     87     private static final boolean DEBUG = false;
     88     private static final String TAG = "UsageStatsTest";
     89 
     90     private static final String APPOPS_SET_SHELL_COMMAND = "appops set {0} " +
     91             AppOpsManager.OPSTR_GET_USAGE_STATS + " {1}";
     92 
     93     private static final String USAGE_SOURCE_GET_SHELL_COMMAND = "settings get global " +
     94             Settings.Global.APP_TIME_LIMIT_USAGE_SOURCE;
     95 
     96     private static final String USAGE_SOURCE_SET_SHELL_COMMAND = "settings put global " +
     97             Settings.Global.APP_TIME_LIMIT_USAGE_SOURCE + " {0}";
     98 
     99     private static final String USAGE_SOURCE_DELETE_SHELL_COMMAND = "settings delete global " +
    100             Settings.Global.APP_TIME_LIMIT_USAGE_SOURCE;
    101 
    102     private static final String TEST_APP_PKG = "android.app.usage.cts.test1";
    103     private static final String TEST_APP_CLASS = "android.app.usage.cts.test1.SomeActivity";
    104 
    105     private static final long TIMEOUT = TimeUnit.SECONDS.toMillis(5);
    106     private static final long MINUTE = TimeUnit.MINUTES.toMillis(1);
    107     private static final long DAY = TimeUnit.DAYS.toMillis(1);
    108     private static final long WEEK = 7 * DAY;
    109     private static final long MONTH = 30 * DAY;
    110     private static final long YEAR = 365 * DAY;
    111     private static final long TIME_DIFF_THRESHOLD = 200;
    112     private static final String CHANNEL_ID = "my_channel";
    113 
    114 
    115     private UiDevice mUiDevice;
    116     private UsageStatsManager mUsageStatsManager;
    117     private String mTargetPackage;
    118     private String mCachedUsageSourceSetting;
    119 
    120     @Before
    121     public void setUp() throws Exception {
    122         mUiDevice = UiDevice.getInstance(InstrumentationRegistry.getInstrumentation());
    123         mUsageStatsManager = (UsageStatsManager) InstrumentationRegistry.getInstrumentation()
    124                 .getContext().getSystemService(Context.USAGE_STATS_SERVICE);
    125         mTargetPackage = InstrumentationRegistry.getContext().getPackageName();
    126         assumeTrue("App Standby not enabled on device", AppStandbyUtils.isAppStandbyEnabled());
    127         setAppOpsMode("allow");
    128         mCachedUsageSourceSetting = getUsageSourceSetting();
    129     }
    130 
    131 
    132     @After
    133     public void cleanUp() throws Exception {
    134         if (mCachedUsageSourceSetting != null &&
    135             !mCachedUsageSourceSetting.equals(getUsageSourceSetting())) {
    136             setUsageSourceSetting(mCachedUsageSourceSetting);
    137             mUsageStatsManager.forceUsageSourceSettingRead();
    138         }
    139     }
    140 
    141     private static void assertLessThan(long left, long right) {
    142         assertTrue("Expected " + left + " to be less than " + right, left < right);
    143     }
    144 
    145     private static void assertLessThanOrEqual(long left, long right) {
    146         assertTrue("Expected " + left + " to be less than " + right, left <= right);
    147     }
    148 
    149     private void setAppOpsMode(String mode) throws Exception {
    150         final String command = MessageFormat.format(APPOPS_SET_SHELL_COMMAND,
    151                 InstrumentationRegistry.getContext().getPackageName(), mode);
    152         mUiDevice.executeShellCommand(command);
    153     }
    154 
    155 
    156     private String getUsageSourceSetting() throws Exception {
    157         return mUiDevice.executeShellCommand(USAGE_SOURCE_GET_SHELL_COMMAND);
    158     }
    159 
    160     private void setUsageSourceSetting(String usageSource) throws Exception {
    161         if (usageSource.equals("null")) {
    162             mUiDevice.executeShellCommand(USAGE_SOURCE_DELETE_SHELL_COMMAND);
    163         } else {
    164             final String command = MessageFormat.format(USAGE_SOURCE_SET_SHELL_COMMAND,
    165                                                         usageSource);
    166             mUiDevice.executeShellCommand(command);
    167         }
    168         mUsageStatsManager.forceUsageSourceSettingRead();
    169     }
    170 
    171     private void launchSubActivity(Class<? extends Activity> clazz) {
    172         final Context context = InstrumentationRegistry.getInstrumentation().getContext();
    173         final Intent intent = new Intent(Intent.ACTION_MAIN);
    174         intent.setClassName(mTargetPackage, clazz.getName());
    175         intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
    176         context.startActivity(intent);
    177         mUiDevice.wait(Until.hasObject(By.clazz(clazz)), TIMEOUT);
    178     }
    179 
    180     private void launchSubActivities(Class<? extends Activity>[] activityClasses) {
    181         for (Class<? extends Activity> clazz : activityClasses) {
    182             launchSubActivity(clazz);
    183         }
    184     }
    185 
    186     @AppModeFull(reason = "No usage events access in instant apps")
    187     @Test
    188     public void testOrderedActivityLaunchSequenceInEventLog() throws Exception {
    189         @SuppressWarnings("unchecked")
    190         Class<? extends Activity>[] activitySequence = new Class[] {
    191                 Activities.ActivityOne.class,
    192                 Activities.ActivityTwo.class,
    193                 Activities.ActivityThree.class,
    194         };
    195         mUiDevice.wakeUp();
    196 
    197         final long startTime = System.currentTimeMillis();
    198         // Launch the series of Activities.
    199         launchSubActivities(activitySequence);
    200         final long endTime = System.currentTimeMillis();
    201         UsageEvents events = mUsageStatsManager.queryEvents(startTime, endTime);
    202 
    203         // Only look at events belongs to mTargetPackage.
    204         ArrayList<UsageEvents.Event> eventList = new ArrayList<>();
    205         while (events.hasNextEvent()) {
    206             UsageEvents.Event event = new UsageEvents.Event();
    207             assertTrue(events.getNextEvent(event));
    208             if (mTargetPackage.equals(event.getPackageName())) {
    209                 eventList.add(event);
    210             }
    211         }
    212 
    213         final int activityCount = activitySequence.length;
    214         for (int i = 0; i < activityCount; i++) {
    215             String className = activitySequence[i].getName();
    216             ArrayList<UsageEvents.Event> activityEvents = new ArrayList<>();
    217             final int size = eventList.size();
    218             for (int j = 0; j < size; j++) {
    219                 Event evt = eventList.get(j);
    220                 if (className.equals(evt.getClassName())) {
    221                     activityEvents.add(evt);
    222                 }
    223             }
    224             // We expect 3 events per Activity launched (ACTIVITY_RESUMED + ACTIVITY_PAUSED
    225             // + ACTIVITY_STOPPED) except for the last Activity, which only has
    226             // ACTIVITY_RESUMED event.
    227             if (i < activityCount - 1) {
    228                 assertEquals(3, activityEvents.size());
    229                 assertEquals(Event.ACTIVITY_RESUMED, activityEvents.get(0).getEventType());
    230                 assertEquals(Event.ACTIVITY_PAUSED, activityEvents.get(1).getEventType());
    231                 assertEquals(Event.ACTIVITY_STOPPED, activityEvents.get(2).getEventType());
    232             } else {
    233                 // The last activity
    234                 assertEquals(1, activityEvents.size());
    235                 assertEquals(Event.ACTIVITY_RESUMED, activityEvents.get(0).getEventType());
    236             }
    237         }
    238     }
    239 
    240     @AppModeFull(reason = "No usage events access in instant apps")
    241     @Test
    242     public void testActivityOnBackButton() throws Exception {
    243         testActivityOnButton(mUiDevice::pressBack);
    244     }
    245 
    246     @AppModeFull(reason = "No usage events access in instant apps")
    247     @Test
    248     public void testActivityOnHomeButton() throws Exception {
    249         testActivityOnButton(mUiDevice::pressHome);
    250     }
    251 
    252     private void testActivityOnButton(Runnable pressButton) throws Exception {
    253         mUiDevice.wakeUp();
    254         final long startTime = System.currentTimeMillis();
    255         final Class clazz = Activities.ActivityOne.class;
    256         launchSubActivity(clazz);
    257         pressButton.run();
    258         Thread.sleep(1000);
    259         final long endTime = System.currentTimeMillis();
    260         UsageEvents events = mUsageStatsManager.queryEvents(startTime, endTime);
    261 
    262         ArrayList<UsageEvents.Event> eventList = new ArrayList<>();
    263         while (events.hasNextEvent()) {
    264             UsageEvents.Event event = new UsageEvents.Event();
    265             assertTrue(events.getNextEvent(event));
    266             if (mTargetPackage.equals(event.getPackageName())
    267                 && clazz.getName().equals(event.getClassName())) {
    268                 eventList.add(event);
    269             }
    270         }
    271         assertEquals(3, eventList.size());
    272         assertEquals(Event.ACTIVITY_RESUMED, eventList.get(0).getEventType());
    273         assertEquals(Event.ACTIVITY_PAUSED, eventList.get(1).getEventType());
    274         assertEquals(Event.ACTIVITY_STOPPED, eventList.get(2).getEventType());
    275     }
    276 
    277     @AppModeFull(reason = "No usage events access in instant apps")
    278     @Test
    279     public void testAppLaunchCount() throws Exception {
    280         long endTime = System.currentTimeMillis();
    281         long startTime = endTime - DateUtils.DAY_IN_MILLIS;
    282         Map<String,UsageStats> events = mUsageStatsManager.queryAndAggregateUsageStats(
    283                 startTime, endTime);
    284         UsageStats stats = events.get(mTargetPackage);
    285         int startingCount = stats.getAppLaunchCount();
    286         launchSubActivity(Activities.ActivityOne.class);
    287         launchSubActivity(Activities.ActivityTwo.class);
    288         endTime = System.currentTimeMillis();
    289         events = mUsageStatsManager.queryAndAggregateUsageStats(
    290                 startTime, endTime);
    291         stats = events.get(mTargetPackage);
    292         assertEquals(startingCount + 1, stats.getAppLaunchCount());
    293         mUiDevice.pressHome();
    294         launchSubActivity(Activities.ActivityOne.class);
    295         launchSubActivity(Activities.ActivityTwo.class);
    296         launchSubActivity(Activities.ActivityThree.class);
    297         endTime = System.currentTimeMillis();
    298         events = mUsageStatsManager.queryAndAggregateUsageStats(
    299                 startTime, endTime);
    300         stats = events.get(mTargetPackage);
    301         assertEquals(startingCount + 2, stats.getAppLaunchCount());
    302     }
    303 
    304     @AppModeFull(reason = "No usage events access in instant apps")
    305     @Test
    306     public void testStandbyBucketChangeLog() throws Exception {
    307         final long startTime = System.currentTimeMillis();
    308         mUiDevice.executeShellCommand("am set-standby-bucket " + mTargetPackage + " rare");
    309 
    310         final long endTime = System.currentTimeMillis();
    311         UsageEvents events = mUsageStatsManager.queryEvents(startTime - 1_000, endTime + 1_000);
    312 
    313         boolean found = false;
    314         // Check all the events.
    315         while (events.hasNextEvent()) {
    316             UsageEvents.Event event = new UsageEvents.Event();
    317             assertTrue(events.getNextEvent(event));
    318             if (event.getEventType() == UsageEvents.Event.STANDBY_BUCKET_CHANGED) {
    319                 found |= event.getAppStandbyBucket() == UsageStatsManager.STANDBY_BUCKET_RARE;
    320             }
    321         }
    322 
    323         assertTrue(found);
    324     }
    325 
    326     @Test
    327     public void testGetAppStandbyBuckets() throws Exception {
    328         final boolean origValue = AppStandbyUtils.isAppStandbyEnabledAtRuntime();
    329         AppStandbyUtils.setAppStandbyEnabledAtRuntime(true);
    330         try {
    331             assumeTrue("Skip GetAppStandby test: app standby is disabled.",
    332                     AppStandbyUtils.isAppStandbyEnabled());
    333 
    334             mUiDevice.executeShellCommand("am set-standby-bucket " + mTargetPackage + " rare");
    335             Map<String, Integer> bucketMap = mUsageStatsManager.getAppStandbyBuckets();
    336             assertTrue("No bucket data returned", bucketMap.size() > 0);
    337             final int bucket = bucketMap.getOrDefault(mTargetPackage, -1);
    338             assertEquals("Incorrect bucket returned for " + mTargetPackage, bucket,
    339                     UsageStatsManager.STANDBY_BUCKET_RARE);
    340         } finally {
    341             AppStandbyUtils.setAppStandbyEnabledAtRuntime(origValue);
    342         }
    343     }
    344 
    345     @Test
    346     public void testGetAppStandbyBucket() throws Exception {
    347         // App should be at least active, since it's running instrumentation tests
    348         assertLessThanOrEqual(UsageStatsManager.STANDBY_BUCKET_ACTIVE,
    349                 mUsageStatsManager.getAppStandbyBucket());
    350     }
    351 
    352     @Test
    353     public void testQueryEventsForSelf() throws Exception {
    354         setAppOpsMode("ignore"); // To ensure permission is not required
    355         // Time drifts of 2s are expected inside usage stats
    356         final long start = System.currentTimeMillis() - 2_000;
    357         mUiDevice.executeShellCommand("am set-standby-bucket " + mTargetPackage + " rare");
    358         Thread.sleep(100);
    359         mUiDevice.executeShellCommand("am set-standby-bucket " + mTargetPackage + " working_set");
    360         Thread.sleep(100);
    361         final long end = System.currentTimeMillis() + 2_000;
    362         final UsageEvents events = mUsageStatsManager.queryEventsForSelf(start, end);
    363         long rareTimeStamp = end + 1; // Initializing as rareTimeStamp > workingTimeStamp
    364         long workingTimeStamp = start - 1;
    365         int numEvents = 0;
    366         while (events.hasNextEvent()) {
    367             UsageEvents.Event event = new UsageEvents.Event();
    368             assertTrue(events.getNextEvent(event));
    369             numEvents++;
    370             assertEquals("Event for a different package", mTargetPackage, event.getPackageName());
    371             if (event.getEventType() == Event.STANDBY_BUCKET_CHANGED) {
    372                 if (event.getAppStandbyBucket() == UsageStatsManager.STANDBY_BUCKET_RARE) {
    373                     rareTimeStamp = event.getTimeStamp();
    374                 }
    375                 else if (event.getAppStandbyBucket() == UsageStatsManager
    376                         .STANDBY_BUCKET_WORKING_SET) {
    377                     workingTimeStamp = event.getTimeStamp();
    378                 }
    379             }
    380         }
    381         assertTrue("Only " + numEvents + " events returned", numEvents >= 2);
    382         assertLessThan(rareTimeStamp, workingTimeStamp);
    383     }
    384 
    385     /**
    386      * We can't run this test because we are unable to change the system time.
    387      * It would be nice to add a shell command or other to allow the shell user
    388      * to set the time, thereby allowing this test to set the time using the UIAutomator.
    389      */
    390     @Ignore
    391     @Test
    392     public void ignore_testStatsAreShiftedInTimeWhenSystemTimeChanges() throws Exception {
    393         launchSubActivity(Activities.ActivityOne.class);
    394         launchSubActivity(Activities.ActivityThree.class);
    395 
    396         long endTime = System.currentTimeMillis();
    397         long startTime = endTime - MINUTE;
    398         Map<String, UsageStats> statsMap = mUsageStatsManager.queryAndAggregateUsageStats(startTime,
    399                 endTime);
    400         assertFalse(statsMap.isEmpty());
    401         assertTrue(statsMap.containsKey(mTargetPackage));
    402         final UsageStats before = statsMap.get(mTargetPackage);
    403 
    404         SystemClock.setCurrentTimeMillis(System.currentTimeMillis() - (DAY / 2));
    405         try {
    406             endTime = System.currentTimeMillis();
    407             startTime = endTime - MINUTE;
    408             statsMap = mUsageStatsManager.queryAndAggregateUsageStats(startTime, endTime);
    409             assertFalse(statsMap.isEmpty());
    410             assertTrue(statsMap.containsKey(mTargetPackage));
    411             final UsageStats after = statsMap.get(mTargetPackage);
    412             assertEquals(before.getPackageName(), after.getPackageName());
    413 
    414             long diff = before.getFirstTimeStamp() - after.getFirstTimeStamp();
    415             assertLessThan(Math.abs(diff - (DAY / 2)), TIME_DIFF_THRESHOLD);
    416 
    417             assertEquals(before.getLastTimeStamp() - before.getFirstTimeStamp(),
    418                     after.getLastTimeStamp() - after.getFirstTimeStamp());
    419             assertEquals(before.getLastTimeUsed() - before.getFirstTimeStamp(),
    420                     after.getLastTimeUsed() - after.getFirstTimeStamp());
    421             assertEquals(before.getTotalTimeInForeground(), after.getTotalTimeInForeground());
    422         } finally {
    423             SystemClock.setCurrentTimeMillis(System.currentTimeMillis() + (DAY / 2));
    424         }
    425     }
    426 
    427     @Test
    428     public void testUsageEventsParceling() throws Exception {
    429         final long startTime = System.currentTimeMillis() - MINUTE;
    430 
    431         // Ensure some data is in the UsageStats log.
    432         @SuppressWarnings("unchecked")
    433         Class<? extends Activity>[] activityClasses = new Class[] {
    434                 Activities.ActivityTwo.class,
    435                 Activities.ActivityOne.class,
    436                 Activities.ActivityThree.class,
    437         };
    438         launchSubActivities(activityClasses);
    439 
    440         final long endTime = System.currentTimeMillis();
    441         UsageEvents events = mUsageStatsManager.queryEvents(startTime, endTime);
    442         assertTrue(events.getNextEvent(new UsageEvents.Event()));
    443 
    444         Parcel p = Parcel.obtain();
    445         p.setDataPosition(0);
    446         events.writeToParcel(p, 0);
    447         p.setDataPosition(0);
    448 
    449         UsageEvents reparceledEvents = UsageEvents.CREATOR.createFromParcel(p);
    450 
    451         UsageEvents.Event e1 = new UsageEvents.Event();
    452         UsageEvents.Event e2 = new UsageEvents.Event();
    453         while (events.hasNextEvent() && reparceledEvents.hasNextEvent()) {
    454             events.getNextEvent(e1);
    455             reparceledEvents.getNextEvent(e2);
    456             assertEquals(e1.getPackageName(), e2.getPackageName());
    457             assertEquals(e1.getClassName(), e2.getClassName());
    458             assertEquals(e1.getConfiguration(), e2.getConfiguration());
    459             assertEquals(e1.getEventType(), e2.getEventType());
    460             assertEquals(e1.getTimeStamp(), e2.getTimeStamp());
    461         }
    462 
    463         assertEquals(events.hasNextEvent(), reparceledEvents.hasNextEvent());
    464     }
    465 
    466     @AppModeFull(reason = "No usage events access in instant apps")
    467     @Test
    468     public void testPackageUsageStatsIntervals() throws Exception {
    469         final long beforeTime = System.currentTimeMillis();
    470 
    471         // Launch an Activity.
    472         launchSubActivity(Activities.ActivityFour.class);
    473         launchSubActivity(Activities.ActivityThree.class);
    474 
    475         final long endTime = System.currentTimeMillis();
    476 
    477         final SparseLongArray intervalLengths = new SparseLongArray();
    478         intervalLengths.put(UsageStatsManager.INTERVAL_DAILY, DAY);
    479         intervalLengths.put(UsageStatsManager.INTERVAL_WEEKLY, WEEK);
    480         intervalLengths.put(UsageStatsManager.INTERVAL_MONTHLY, MONTH);
    481         intervalLengths.put(UsageStatsManager.INTERVAL_YEARLY, YEAR);
    482 
    483         final int intervalCount = intervalLengths.size();
    484         for (int i = 0; i < intervalCount; i++) {
    485             final int intervalType = intervalLengths.keyAt(i);
    486             final long intervalDuration = intervalLengths.valueAt(i);
    487             final long startTime = endTime - (2 * intervalDuration);
    488             final List<UsageStats> statsList = mUsageStatsManager.queryUsageStats(intervalType,
    489                     startTime, endTime);
    490             assertFalse(statsList.isEmpty());
    491 
    492             boolean foundPackage = false;
    493             for (UsageStats stats : statsList) {
    494                 // Verify that each period is a day long.
    495                 assertLessThanOrEqual(stats.getLastTimeStamp() - stats.getFirstTimeStamp(),
    496                         intervalDuration);
    497                 if (stats.getPackageName().equals(mTargetPackage) &&
    498                         stats.getLastTimeUsed() >= beforeTime - TIME_DIFF_THRESHOLD) {
    499                     foundPackage = true;
    500                 }
    501             }
    502 
    503             assertTrue("Did not find package " + mTargetPackage + " in interval " + intervalType,
    504                     foundPackage);
    505         }
    506     }
    507 
    508     @Test
    509     public void testNoAccessSilentlyFails() throws Exception {
    510         final long startTime = System.currentTimeMillis() - MINUTE;
    511 
    512         launchSubActivity(android.app.usage.cts.Activities.ActivityOne.class);
    513         launchSubActivity(android.app.usage.cts.Activities.ActivityThree.class);
    514 
    515         final long endTime = System.currentTimeMillis();
    516         List<UsageStats> stats = mUsageStatsManager.queryUsageStats(UsageStatsManager.INTERVAL_BEST,
    517                 startTime, endTime);
    518         assertFalse(stats.isEmpty());
    519 
    520         // We set the mode to ignore because our package has the PACKAGE_USAGE_STATS permission,
    521         // and default would allow in this case.
    522         setAppOpsMode("ignore");
    523 
    524         stats = mUsageStatsManager.queryUsageStats(UsageStatsManager.INTERVAL_BEST,
    525                 startTime, endTime);
    526         assertTrue(stats.isEmpty());
    527     }
    528 
    529     @AppModeFull(reason = "No usage events access in instant apps")
    530     @Test
    531     public void testNotificationSeen() throws Exception {
    532         final long startTime = System.currentTimeMillis();
    533         Context context = InstrumentationRegistry.getContext();
    534 
    535         // Skip the test for wearable devices and televisions; neither has a notification shade.
    536         assumeFalse("Test cannot run on a watch- notification shade is not shown",
    537                 context.getPackageManager().hasSystemFeature(PackageManager.FEATURE_WATCH));
    538         assumeFalse("Test cannot run on a television- notifications are not shown",
    539                 context.getPackageManager().hasSystemFeature(PackageManager.FEATURE_LEANBACK_ONLY));
    540 
    541         NotificationManager mNotificationManager =
    542             (NotificationManager) context.getSystemService(Context.NOTIFICATION_SERVICE);
    543         int importance = NotificationManager.IMPORTANCE_DEFAULT;
    544         NotificationChannel mChannel = new NotificationChannel(CHANNEL_ID, "Channel",
    545             importance);
    546         // Configure the notification channel.
    547         mChannel.setDescription("Test channel");
    548         mNotificationManager.createNotificationChannel(mChannel);
    549         Notification.Builder mBuilder =
    550                 new Notification.Builder(context, CHANNEL_ID)
    551                     .setSmallIcon(R.drawable.ic_notification)
    552                     .setContentTitle("My notification")
    553                     .setContentText("Hello World!");
    554         PendingIntent pi = PendingIntent.getActivity(context, 1,
    555                 new Intent(Settings.ACTION_SETTINGS), 0);
    556         mBuilder.setContentIntent(pi);
    557         mNotificationManager.notify(1, mBuilder.build());
    558         Thread.sleep(500);
    559         long endTime = System.currentTimeMillis();
    560         UsageEvents events = mUsageStatsManager.queryEvents(startTime, endTime);
    561         boolean found = false;
    562         Event event = new Event();
    563         while (events.hasNextEvent()) {
    564             events.getNextEvent(event);
    565             if (event.getEventType() == Event.NOTIFICATION_SEEN) {
    566                 found = true;
    567             }
    568         }
    569         assertFalse(found);
    570         // Pull down shade
    571         mUiDevice.openNotification();
    572         outer:
    573         for (int i = 0; i < 5; i++) {
    574             Thread.sleep(500);
    575             endTime = System.currentTimeMillis();
    576             events = mUsageStatsManager.queryEvents(startTime, endTime);
    577             found = false;
    578             while (events.hasNextEvent()) {
    579                 events.getNextEvent(event);
    580                 if (event.getEventType() == Event.NOTIFICATION_SEEN) {
    581                     found = true;
    582                     break outer;
    583                 }
    584             }
    585         }
    586         assertTrue(found);
    587         mUiDevice.pressBack();
    588     }
    589 
    590     static final int[] INTERACTIVE_EVENTS = new int[] {
    591             Event.SCREEN_INTERACTIVE,
    592             Event.SCREEN_NON_INTERACTIVE
    593     };
    594 
    595     static final int[] KEYGUARD_EVENTS = new int[] {
    596             Event.KEYGUARD_SHOWN,
    597             Event.KEYGUARD_HIDDEN
    598     };
    599 
    600     static final int[] ALL_EVENTS = new int[] {
    601             Event.SCREEN_INTERACTIVE,
    602             Event.SCREEN_NON_INTERACTIVE,
    603             Event.KEYGUARD_SHOWN,
    604             Event.KEYGUARD_HIDDEN
    605     };
    606 
    607     private long getEvents(int[] whichEvents, long startTime, List<Event> out) {
    608         final long endTime = System.currentTimeMillis();
    609         if (DEBUG) Log.i(TAG, "Looking for events " + Arrays.toString(whichEvents)
    610                 + " between " + startTime + " and " + endTime);
    611         UsageEvents events = mUsageStatsManager.queryEvents(startTime, endTime);
    612 
    613         long latestTime = 0;
    614 
    615         // Find events.
    616         while (events.hasNextEvent()) {
    617             UsageEvents.Event event = new UsageEvents.Event();
    618             assertTrue(events.getNextEvent(event));
    619             final int ev = event.getEventType();
    620             for (int which : whichEvents) {
    621                 if (ev == which) {
    622                     if (out != null) {
    623                         out.add(event);
    624                     }
    625                     if (DEBUG) Log.i(TAG, "Next event type " + event.getEventType()
    626                             + " time=" + event.getTimeStamp());
    627                     if (latestTime < event.getTimeStamp()) {
    628                         latestTime = event.getTimeStamp();
    629                     }
    630                     break;
    631                 }
    632             }
    633         }
    634 
    635         return latestTime;
    636     }
    637 
    638     private ArrayList<Event> waitForEventCount(int[] whichEvents, long startTime, int count) {
    639         final ArrayList<Event> events = new ArrayList<>();
    640         final long endTime = SystemClock.uptimeMillis() + 2000;
    641         do {
    642             events.clear();
    643             getEvents(whichEvents, startTime, events);
    644             if (events.size() == count) {
    645                 return events;
    646             }
    647             if (events.size() > count) {
    648                 fail("Found too many events: got " + events.size() + ", expected " + count);
    649                 return events;
    650             }
    651             SystemClock.sleep(10);
    652         } while (SystemClock.uptimeMillis() < endTime);
    653 
    654         fail("Timed out waiting for " + count + " events, only reached " + events.size());
    655         return events;
    656     }
    657 
    658     static class AggrEventData {
    659         final String label;
    660         int count;
    661         long duration;
    662         long lastEventTime;
    663 
    664         AggrEventData(String label) {
    665             this.label = label;
    666         }
    667     }
    668 
    669     static class AggrAllEventsData {
    670         final AggrEventData interactive = new AggrEventData("Interactive");
    671         final AggrEventData nonInteractive = new AggrEventData("Non-interactive");
    672         final AggrEventData keyguardShown = new AggrEventData("Keyguard shown");
    673         final AggrEventData keyguardHidden = new AggrEventData("Keyguard hidden");
    674         int interactiveCount;
    675         long interactiveDuration;
    676         long interactiveLastEventTime;
    677         int nonInteractiveCount;
    678         long nonInteractiveDuration;
    679         long nonInteractiveLastEventTime;
    680         int keyguardShownCount;
    681         long keyguardShownDuration;
    682         int keyguardHiddenCount;
    683         long keyguardHiddenDuration;
    684     }
    685 
    686     private SparseArray<AggrAllEventsData> getAggrEventData(long beforeTime) {
    687         final long endTime = System.currentTimeMillis();
    688 
    689         final SparseLongArray intervalLengths = new SparseLongArray();
    690         intervalLengths.put(UsageStatsManager.INTERVAL_DAILY, DAY);
    691         intervalLengths.put(UsageStatsManager.INTERVAL_WEEKLY, WEEK);
    692         intervalLengths.put(UsageStatsManager.INTERVAL_MONTHLY, MONTH);
    693         intervalLengths.put(UsageStatsManager.INTERVAL_YEARLY, YEAR);
    694 
    695         final SparseArray<AggrAllEventsData> allAggr = new SparseArray<>();
    696 
    697         final int intervalCount = intervalLengths.size();
    698         for (int i = 0; i < intervalCount; i++) {
    699             final int intervalType = intervalLengths.keyAt(i);
    700             final long intervalDuration = intervalLengths.valueAt(i);
    701             final long startTime = endTime - (2 * intervalDuration);
    702             List<EventStats> statsList = mUsageStatsManager.queryEventStats(intervalType,
    703                     startTime, endTime);
    704             assertFalse(statsList.isEmpty());
    705 
    706             final AggrAllEventsData aggr = new AggrAllEventsData();
    707             allAggr.put(intervalType, aggr);
    708 
    709             boolean foundEvent = false;
    710             for (EventStats stats : statsList) {
    711                 // Verify that each period is a day long.
    712                 //assertLessThanOrEqual(stats.getLastTimeStamp() - stats.getFirstTimeStamp(),
    713                 //        intervalDuration);
    714                 AggrEventData data = null;
    715                 switch (stats.getEventType()) {
    716                     case Event.SCREEN_INTERACTIVE:
    717                         data = aggr.interactive;
    718                         break;
    719                     case Event.SCREEN_NON_INTERACTIVE:
    720                         data = aggr.nonInteractive;
    721                         break;
    722                     case Event.KEYGUARD_HIDDEN:
    723                         data = aggr.keyguardHidden;
    724                         break;
    725                     case Event.KEYGUARD_SHOWN:
    726                         data = aggr.keyguardShown;
    727                         break;
    728                 }
    729                 if (data != null) {
    730                     foundEvent = true;
    731                     data.count += stats.getCount();
    732                     data.duration += stats.getTotalTime();
    733                     if (data.lastEventTime < stats.getLastEventTime()) {
    734                         data.lastEventTime = stats.getLastEventTime();
    735                     }
    736                 }
    737             }
    738 
    739             assertTrue("Did not find event data in interval " + intervalType,
    740                     foundEvent);
    741         }
    742 
    743         return allAggr;
    744     }
    745 
    746     private void verifyCount(int oldCount, int newCount, boolean larger, String label,
    747             int interval) {
    748         if (larger) {
    749             if (newCount <= oldCount) {
    750                 fail(label + " count newer " + newCount
    751                         + " expected to be larger than older " + oldCount
    752                         + " @ interval " + interval);
    753             }
    754         } else {
    755             if (newCount != oldCount) {
    756                 fail(label + " count newer " + newCount
    757                         + " expected to be same as older " + oldCount
    758                         + " @ interval " + interval);
    759             }
    760         }
    761     }
    762 
    763     private void verifyDuration(long oldDur, long newDur, boolean larger, String label,
    764             int interval) {
    765         if (larger) {
    766             if (newDur <= oldDur) {
    767                 fail(label + " duration newer " + newDur
    768                         + " expected to be larger than older " + oldDur
    769                         + " @ interval " + interval);
    770             }
    771         } else {
    772             if (newDur != oldDur) {
    773                 fail(label + " duration newer " + newDur
    774                         + " expected to be same as older " + oldDur
    775                         + " @ interval " + interval);
    776             }
    777         }
    778     }
    779 
    780     private void verifyAggrEventData(AggrEventData older, AggrEventData newer,
    781             boolean countLarger, boolean durationLarger, int interval) {
    782         verifyCount(older.count, newer.count, countLarger, older.label, interval);
    783         verifyDuration(older.duration, newer.duration, durationLarger, older.label, interval);
    784     }
    785 
    786     private void verifyAggrInteractiveEventData(SparseArray<AggrAllEventsData> older,
    787             SparseArray<AggrAllEventsData> newer, boolean interactiveLarger,
    788             boolean nonInteractiveLarger) {
    789         for (int i = 0; i < older.size(); i++) {
    790             AggrAllEventsData o = older.valueAt(i);
    791             AggrAllEventsData n = newer.valueAt(i);
    792             // When we are told something is larger, that means we have transitioned
    793             // *out* of that state -- so the duration of that state is expected to
    794             // increase, but the count should stay the same (and the count of the state
    795             // we transition to is increased).
    796             final int interval = older.keyAt(i);
    797             verifyAggrEventData(o.interactive, n.interactive, nonInteractiveLarger,
    798                     interactiveLarger, interval);
    799             verifyAggrEventData(o.nonInteractive, n.nonInteractive, interactiveLarger,
    800                     nonInteractiveLarger, interval);
    801         }
    802     }
    803 
    804     private void verifyAggrKeyguardEventData(SparseArray<AggrAllEventsData> older,
    805             SparseArray<AggrAllEventsData> newer, boolean hiddenLarger,
    806             boolean shownLarger) {
    807         for (int i = 0; i < older.size(); i++) {
    808             AggrAllEventsData o = older.valueAt(i);
    809             AggrAllEventsData n = newer.valueAt(i);
    810             // When we are told something is larger, that means we have transitioned
    811             // *out* of that state -- so the duration of that state is expected to
    812             // increase, but the count should stay the same (and the count of the state
    813             // we transition to is increased).
    814             final int interval = older.keyAt(i);
    815             verifyAggrEventData(o.keyguardHidden, n.keyguardHidden, shownLarger,
    816                     hiddenLarger, interval);
    817             verifyAggrEventData(o.keyguardShown, n.keyguardShown, hiddenLarger,
    818                     shownLarger, interval);
    819         }
    820     }
    821 
    822     @AppModeFull(reason = "No usage events access in instant apps")
    823     @Test
    824     public void testInteractiveEvents() throws Exception {
    825         final KeyguardManager kmgr = InstrumentationRegistry.getInstrumentation()
    826                 .getContext().getSystemService(KeyguardManager.class);
    827 
    828         // We need to start out with the screen on.
    829         if (!mUiDevice.isScreenOn()) {
    830             pressWakeUp();
    831             SystemClock.sleep(1000);
    832         }
    833 
    834         // Also want to start out with the keyguard dismissed.
    835         if (kmgr.isKeyguardLocked()) {
    836             final long startTime = getEvents(KEYGUARD_EVENTS, 0, null) + 1;
    837             mUiDevice.executeShellCommand("wm dismiss-keyguard");
    838             ArrayList<Event> events = waitForEventCount(KEYGUARD_EVENTS, startTime, 1);
    839             assertEquals(Event.KEYGUARD_HIDDEN, events.get(0).getEventType());
    840             SystemClock.sleep(500);
    841         }
    842 
    843         try {
    844             ArrayList<Event> events;
    845 
    846             // Determine time to start looking for events.
    847             final long startTime = getEvents(ALL_EVENTS, 0, null) + 1;
    848             SparseArray<AggrAllEventsData> baseAggr = getAggrEventData(0);
    849 
    850             // First test -- put device to sleep and make sure we see this event.
    851             pressSleep();
    852 
    853             // Do we have one event, going in to non-interactive mode?
    854             events = waitForEventCount(INTERACTIVE_EVENTS, startTime, 1);
    855             assertEquals(Event.SCREEN_NON_INTERACTIVE, events.get(0).getEventType());
    856             SparseArray<AggrAllEventsData> offAggr = getAggrEventData(startTime);
    857             verifyAggrInteractiveEventData(baseAggr, offAggr, true, false);
    858 
    859             // Next test -- turn screen on and make sure we have a second event.
    860             // XXX need to wait a bit so we don't accidentally trigger double-power
    861             // to launch camera.  (SHOULD FIX HOW WE WAKEUP / SLEEP TO NOT USE POWER KEY)
    862             SystemClock.sleep(500);
    863             pressWakeUp();
    864             events = waitForEventCount(INTERACTIVE_EVENTS, startTime, 2);
    865             assertEquals(Event.SCREEN_NON_INTERACTIVE, events.get(0).getEventType());
    866             assertEquals(Event.SCREEN_INTERACTIVE, events.get(1).getEventType());
    867             SparseArray<AggrAllEventsData> onAggr = getAggrEventData(startTime);
    868             verifyAggrInteractiveEventData(offAggr, onAggr, false, true);
    869 
    870             // If the device is doing a lock screen, verify that we are also seeing the
    871             // appropriate keyguard behavior.  We don't know the timing from when the screen
    872             // will go off until the keyguard is shown, so we will do this all after turning
    873             // the screen back on (at which point it must be shown).
    874             // XXX CTS seems to be preventing the keyguard from showing, so this path is
    875             // never being tested.
    876             if (kmgr.isKeyguardLocked()) {
    877                 events = waitForEventCount(KEYGUARD_EVENTS, startTime, 1);
    878                 assertEquals(Event.KEYGUARD_SHOWN, events.get(0).getEventType());
    879                 SparseArray<AggrAllEventsData> shownAggr = getAggrEventData(startTime);
    880                 verifyAggrKeyguardEventData(offAggr, shownAggr, true, false);
    881 
    882                 // Now dismiss the keyguard and verify the resulting events.
    883                 mUiDevice.executeShellCommand("wm dismiss-keyguard");
    884                 events = waitForEventCount(KEYGUARD_EVENTS, startTime, 2);
    885                 assertEquals(Event.KEYGUARD_SHOWN, events.get(0).getEventType());
    886                 assertEquals(Event.KEYGUARD_HIDDEN, events.get(1).getEventType());
    887                 SparseArray<AggrAllEventsData> hiddenAggr = getAggrEventData(startTime);
    888                 verifyAggrKeyguardEventData(shownAggr, hiddenAggr, false, true);
    889             }
    890 
    891         } finally {
    892             // Dismiss keyguard to get device back in its normal state.
    893             pressWakeUp();
    894             mUiDevice.executeShellCommand("wm dismiss-keyguard");
    895         }
    896     }
    897 
    898     @Test
    899     public void testIgnoreNonexistentPackage() throws Exception {
    900         final String fakePackageName = "android.fake.package.name";
    901         final int defaultValue = -1;
    902 
    903         mUiDevice.executeShellCommand("am set-standby-bucket " + fakePackageName + " rare");
    904         // Verify the above does not add a new entry to the App Standby bucket map
    905         Map<String, Integer> bucketMap = mUsageStatsManager.getAppStandbyBuckets();
    906         int bucket = bucketMap.getOrDefault(fakePackageName, defaultValue);
    907         assertFalse("Meaningful bucket value " + bucket + " returned for " + fakePackageName
    908                 + " after set-standby-bucket", bucket > 0);
    909 
    910         mUiDevice.executeShellCommand("am get-standby-bucket " + fakePackageName);
    911         // Verify the above does not add a new entry to the App Standby bucket map
    912         bucketMap = mUsageStatsManager.getAppStandbyBuckets();
    913         bucket = bucketMap.getOrDefault(fakePackageName, defaultValue);
    914         assertFalse("Meaningful bucket value " + bucket + " returned for " + fakePackageName
    915                 + " after get-standby-bucket", bucket > 0);
    916     }
    917 
    918     @Test
    919     public void testObserveUsagePermissionForRegisterObserver() {
    920         final int observerId = 0;
    921         final String[] packages = new String[] {"com.android.settings"};
    922 
    923         try {
    924             mUsageStatsManager.registerAppUsageObserver(observerId, packages,
    925                     1, java.util.concurrent.TimeUnit.HOURS, null);
    926             fail("Expected SecurityException for an app not holding OBSERVE_APP_USAGE permission.");
    927         } catch (SecurityException e) {
    928             // Exception expected
    929         }
    930 
    931         try {
    932             mUsageStatsManager.registerUsageSessionObserver(observerId, packages,
    933                     Duration.ofHours(1), Duration.ofSeconds(10), null, null);
    934             fail("Expected SecurityException for an app not holding OBSERVE_APP_USAGE permission.");
    935         } catch (SecurityException e) {
    936             // Exception expected
    937         }
    938 
    939         try {
    940             mUsageStatsManager.registerAppUsageLimitObserver(observerId, packages,
    941                     Duration.ofHours(1), Duration.ofHours(0), null);
    942             fail("Expected SecurityException for an app not holding OBSERVE_APP_USAGE permission.");
    943         } catch (SecurityException e) {
    944             // Exception expected
    945         }
    946     }
    947 
    948     @Test
    949     public void testObserveUsagePermissionForUnregisterObserver() {
    950         final int observerId = 0;
    951 
    952         try {
    953             mUsageStatsManager.unregisterAppUsageObserver(observerId);
    954             fail("Expected SecurityException for an app not holding OBSERVE_APP_USAGE permission.");
    955         } catch (SecurityException e) {
    956             // Exception expected
    957         }
    958 
    959         try {
    960             mUsageStatsManager.unregisterUsageSessionObserver(observerId);
    961             fail("Expected SecurityException for an app not holding OBSERVE_APP_USAGE permission.");
    962         } catch (SecurityException e) {
    963             // Exception expected
    964         }
    965 
    966         try {
    967             mUsageStatsManager.unregisterAppUsageLimitObserver(observerId);
    968             fail("Expected SecurityException for an app not holding OBSERVE_APP_USAGE permission.");
    969         } catch (SecurityException e) {
    970             // Exception expected
    971         }
    972     }
    973 
    974     @AppModeFull(reason = "No usage events access in instant apps")
    975     @Test
    976     public void testForegroundService() throws Exception {
    977         // This test start a foreground service then stop it. The event list should have one
    978         // FOREGROUND_SERVICE_START and one FOREGROUND_SERVICE_STOP event.
    979         final long startTime = System.currentTimeMillis();
    980         final Context context = InstrumentationRegistry.getInstrumentation().getContext();
    981         context.startService(new Intent(context, TestService.class));
    982         mUiDevice.wait(Until.hasObject(By.clazz(TestService.class)), TIMEOUT);
    983         final long sleepTime = 500;
    984         SystemClock.sleep(sleepTime);
    985         context.stopService(new Intent(context, TestService.class));
    986         mUiDevice.wait(Until.gone(By.clazz(TestService.class)), TIMEOUT);
    987         final long endTime = System.currentTimeMillis();
    988         UsageEvents events = mUsageStatsManager.queryEvents(startTime, endTime);
    989 
    990         int numStarts = 0;
    991         int numStops = 0;
    992         int startIdx = -1;
    993         int stopIdx = -1;
    994         int i = 0;
    995         while (events.hasNextEvent()) {
    996             UsageEvents.Event event = new UsageEvents.Event();
    997             assertTrue(events.getNextEvent(event));
    998             if (mTargetPackage.equals(event.getPackageName())
    999                     || TestService.class.getName().equals(event.getClassName())) {
   1000                 if (event.getEventType() == Event.FOREGROUND_SERVICE_START) {
   1001                     numStarts++;
   1002                     startIdx = i;
   1003                 } else if (event.getEventType() == Event.FOREGROUND_SERVICE_STOP) {
   1004                     numStops++;
   1005                     stopIdx = i;
   1006                 }
   1007                 i++;
   1008             }
   1009         }
   1010         // One FOREGROUND_SERVICE_START event followed by one FOREGROUND_SERVICE_STOP event.
   1011         assertEquals(numStarts, 1);
   1012         assertEquals(numStops, 1);
   1013         assertLessThan(startIdx, stopIdx);
   1014 
   1015         final Map<String, UsageStats> map = mUsageStatsManager.queryAndAggregateUsageStats(
   1016             startTime, endTime);
   1017         final UsageStats stats = map.get(mTargetPackage);
   1018         assertNotNull(stats);
   1019         final long lastTimeUsed = stats.getLastTimeForegroundServiceUsed();
   1020         // lastTimeUsed should be falling between startTime and endTime.
   1021         assertLessThan(startTime, lastTimeUsed);
   1022         assertLessThan(lastTimeUsed, endTime);
   1023         final long totalTimeUsed = stats.getTotalTimeForegroundServiceUsed();
   1024         // because we slept for 500 milliseconds earlier, we know the totalTimeUsed must be more
   1025         // more than 500 milliseconds.
   1026         assertLessThan(sleepTime, totalTimeUsed);
   1027     }
   1028 
   1029     @AppModeFull(reason = "No usage events access in instant apps")
   1030     @Test
   1031     public void testTaskRootEventField() throws Exception {
   1032         final KeyguardManager kmgr = InstrumentationRegistry.getInstrumentation()
   1033                 .getContext().getSystemService(KeyguardManager.class);
   1034         mUiDevice.wakeUp();
   1035         // Also want to start out with the keyguard dismissed.
   1036         if (kmgr.isKeyguardLocked()) {
   1037             final long startTime = getEvents(KEYGUARD_EVENTS, 0, null) + 1;
   1038             mUiDevice.executeShellCommand("wm dismiss-keyguard");
   1039             ArrayList<Event> events = waitForEventCount(KEYGUARD_EVENTS, startTime, 1);
   1040             assertEquals(Event.KEYGUARD_HIDDEN, events.get(0).getEventType());
   1041             SystemClock.sleep(500);
   1042         }
   1043 
   1044         final long startTime = System.currentTimeMillis();
   1045         launchSubActivity(TaskRootActivity.class);
   1046         final long endTime = System.currentTimeMillis();
   1047         UsageEvents events = mUsageStatsManager.queryEvents(startTime, endTime);
   1048 
   1049         while (events.hasNextEvent()) {
   1050             UsageEvents.Event event = new UsageEvents.Event();
   1051             assertTrue(events.getNextEvent(event));
   1052             if (TaskRootActivity.TEST_APP_PKG.equals(event.getPackageName())
   1053                     && TaskRootActivity.TEST_APP_CLASS.equals(event.getClassName())) {
   1054                 assertEquals(mTargetPackage, event.getTaskRootPackageName());
   1055                 assertEquals(TaskRootActivity.class.getCanonicalName(),
   1056                         event.getTaskRootClassName());
   1057                 return;
   1058             }
   1059         }
   1060         fail("Did not find nested activity name in usage events");
   1061     }
   1062 
   1063     @AppModeFull(reason = "No usage events access in instant apps")
   1064     @Test
   1065     public void testUsageSourceAttribution() throws Exception {
   1066         final KeyguardManager kmgr = InstrumentationRegistry.getInstrumentation()
   1067                 .getContext().getSystemService(KeyguardManager.class);
   1068         mUiDevice.wakeUp();
   1069         // Also want to start out with the keyguard dismissed.
   1070         if (kmgr.isKeyguardLocked()) {
   1071             final long startTime = getEvents(KEYGUARD_EVENTS, 0, null) + 1;
   1072             mUiDevice.executeShellCommand("wm dismiss-keyguard");
   1073             ArrayList<Event> events = waitForEventCount(KEYGUARD_EVENTS, startTime, 1);
   1074             assertEquals(Event.KEYGUARD_HIDDEN, events.get(0).getEventType());
   1075             SystemClock.sleep(500);
   1076         }
   1077 
   1078         mUiDevice.pressHome();
   1079 
   1080         setUsageSourceSetting(Integer.toString(mUsageStatsManager.USAGE_SOURCE_CURRENT_ACTIVITY));
   1081         launchSubActivity(TaskRootActivity.class);
   1082         // Usage should be attributed to the test app package
   1083         assertAppOrTokenUsed(TaskRootActivity.TEST_APP_PKG, true);
   1084 
   1085         mUiDevice.pressHome();
   1086 
   1087         setUsageSourceSetting(Integer.toString(
   1088                 mUsageStatsManager.USAGE_SOURCE_TASK_ROOT_ACTIVITY));
   1089         launchSubActivity(TaskRootActivity.class);
   1090         // Usage should be attributed to this package
   1091         assertAppOrTokenUsed(mTargetPackage, true);
   1092     }
   1093 
   1094     @AppModeInstant
   1095     @Test
   1096     public void testInstantAppUsageEventsObfuscated() throws Exception {
   1097         @SuppressWarnings("unchecked")
   1098         final Class<? extends Activity>[] activitySequence = new Class[] {
   1099                 Activities.ActivityOne.class,
   1100                 Activities.ActivityTwo.class,
   1101                 Activities.ActivityThree.class,
   1102         };
   1103         mUiDevice.wakeUp();
   1104         mUiDevice.pressHome();
   1105 
   1106         final long startTime = System.currentTimeMillis();
   1107         // Launch the series of Activities.
   1108         launchSubActivities(activitySequence);
   1109         SystemClock.sleep(250);
   1110 
   1111         final long endTime = System.currentTimeMillis();
   1112         final UsageEvents events = mUsageStatsManager.queryEvents(startTime, endTime);
   1113 
   1114         int resumes = 0;
   1115         int pauses = 0;
   1116         int stops = 0;
   1117 
   1118         // Only look at events belongs to mTargetPackage.
   1119         ArrayList<UsageEvents.Event> eventList = new ArrayList<>();
   1120         while (events.hasNextEvent()) {
   1121             final UsageEvents.Event event = new UsageEvents.Event();
   1122             assertTrue(events.getNextEvent(event));
   1123             // There should be no events with this packages name
   1124             assertFalse("Instant app package name found in usage event list",
   1125                     mTargetPackage.equals(event.getPackageName()));
   1126 
   1127             // Look for the obfuscated instant app string instead
   1128             if(UsageEvents.INSTANT_APP_PACKAGE_NAME.equals(event.getPackageName())) {
   1129                 switch (event.mEventType) {
   1130                     case Event.ACTIVITY_RESUMED:
   1131                         resumes++;
   1132                         break;
   1133                     case Event.ACTIVITY_PAUSED:
   1134                         pauses++;
   1135                         break;
   1136                     case Event.ACTIVITY_STOPPED:
   1137                         stops++;
   1138                         break;
   1139                 }
   1140             }
   1141         }
   1142         assertEquals("Unexpected number of activity resumes", 3, resumes);
   1143         assertEquals("Unexpected number of activity pauses", 2, pauses);
   1144         assertEquals("Unexpected number of activity stops", 2, stops);
   1145     }
   1146 
   1147 
   1148 
   1149     @AppModeFull(reason = "No usage events access in instant apps")
   1150     @Test
   1151     public void testSuddenDestroy() throws Exception {
   1152         final Context context = InstrumentationRegistry.getInstrumentation().getContext();
   1153         final KeyguardManager kmgr = InstrumentationRegistry.getInstrumentation()
   1154                 .getContext().getSystemService(KeyguardManager.class);
   1155         mUiDevice.wakeUp();
   1156         // Also want to start out with the keyguard dismissed.
   1157         if (kmgr.isKeyguardLocked()) {
   1158             final long startTime = getEvents(KEYGUARD_EVENTS, 0, null) + 1;
   1159             mUiDevice.executeShellCommand("wm dismiss-keyguard");
   1160             ArrayList<Event> events = waitForEventCount(KEYGUARD_EVENTS, startTime, 1);
   1161             assertEquals(Event.KEYGUARD_HIDDEN, events.get(0).getEventType());
   1162             SystemClock.sleep(500);
   1163         }
   1164 
   1165         mUiDevice.pressHome();
   1166 
   1167         final long startTime = System.currentTimeMillis();
   1168         final ActivityManager mAm = context.getSystemService(ActivityManager.class);
   1169 
   1170         Intent intent = new Intent();
   1171         intent.setClassName(TEST_APP_PKG, TEST_APP_CLASS);
   1172         intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
   1173         context.startActivity(intent);
   1174         mUiDevice.wait(Until.hasObject(By.clazz(TEST_APP_PKG, TEST_APP_CLASS)), TIMEOUT);
   1175         SystemClock.sleep(500);
   1176 
   1177         // Destroy the activity
   1178         SystemUtil.runWithShellPermissionIdentity(() -> mAm.forceStopPackage(TEST_APP_PKG));
   1179         mUiDevice.wait(Until.gone(By.clazz(TEST_APP_PKG, TEST_APP_CLASS)), TIMEOUT);
   1180         SystemClock.sleep(500);
   1181 
   1182         final long endTime = System.currentTimeMillis();
   1183         final UsageEvents events = mUsageStatsManager.queryEvents(startTime, endTime);
   1184 
   1185         int resumes = 0;
   1186         int stops = 0;
   1187 
   1188         while (events.hasNextEvent()) {
   1189             final UsageEvents.Event event = new UsageEvents.Event();
   1190             assertTrue(events.getNextEvent(event));
   1191 
   1192             if(TEST_APP_PKG.equals(event.getPackageName())) {
   1193                 switch (event.mEventType) {
   1194                     case Event.ACTIVITY_RESUMED:
   1195                         assertNotNull("ACTIVITY_RESUMED event Task Root should not be null",
   1196                                 event.getTaskRootPackageName());
   1197                         resumes++;
   1198                         break;
   1199                     case Event.ACTIVITY_STOPPED:
   1200                         assertNotNull("ACTIVITY_STOPPED event Task Root should not be null",
   1201                                 event.getTaskRootPackageName());
   1202                         stops++;
   1203                         break;
   1204                 }
   1205             }
   1206         }
   1207         assertEquals("Unexpected number of activity resumes", 1, resumes);
   1208         assertEquals("Unexpected number of activity stops", 1, stops);
   1209     }
   1210 
   1211     private void pressWakeUp() {
   1212         mUiDevice.pressKeyCode(KeyEvent.KEYCODE_WAKEUP);
   1213     }
   1214 
   1215     private void pressSleep() {
   1216         mUiDevice.pressKeyCode(KeyEvent.KEYCODE_SLEEP);
   1217     }
   1218 
   1219     /**
   1220      * Assert on an app or token's usage state.
   1221      * @param entity name of the app or token
   1222      * @param expected expected usage state, true for in use, false for not in use
   1223      */
   1224     private void assertAppOrTokenUsed(String entity, boolean expected) throws IOException {
   1225         final String activeUsages =
   1226                 mUiDevice.executeShellCommand("dumpsys usagestats apptimelimit actives");
   1227         final String[] actives = activeUsages.split("\n");
   1228         boolean found = false;
   1229 
   1230         for (String active : actives) {
   1231             if (active.equals(entity)) {
   1232                 found = true;
   1233                 break;
   1234             }
   1235         }
   1236         if (expected) {
   1237             assertTrue(entity + " not found in list of active activities and tokens\n"
   1238                     + activeUsages, found);
   1239         } else {
   1240             assertFalse(entity + " found in list of active activities and tokens\n"
   1241                     + activeUsages, found);
   1242         }
   1243     }
   1244 }
   1245