1 package org.robolectric.shadows; 2 3 import static android.app.usage.UsageStatsManager.INTERVAL_DAILY; 4 import static android.app.usage.UsageStatsManager.INTERVAL_WEEKLY; 5 import static android.content.Context.USAGE_STATS_SERVICE; 6 import static android.os.Build.VERSION_CODES.LOLLIPOP; 7 import static com.google.common.truth.Truth.assertThat; 8 import static java.util.concurrent.TimeUnit.HOURS; 9 import static org.robolectric.Shadows.shadowOf; 10 11 import android.app.Application; 12 import android.app.PendingIntent; 13 import android.app.usage.UsageEvents; 14 import android.app.usage.UsageEvents.Event; 15 import android.app.usage.UsageStats; 16 import android.app.usage.UsageStatsManager; 17 import android.content.Intent; 18 import android.os.Build; 19 import androidx.test.core.app.ApplicationProvider; 20 import androidx.test.ext.junit.runners.AndroidJUnit4; 21 import com.google.common.collect.ImmutableList; 22 import java.util.Collections; 23 import java.util.List; 24 import java.util.Map; 25 import java.util.concurrent.TimeUnit; 26 import org.junit.Before; 27 import org.junit.Test; 28 import org.junit.runner.RunWith; 29 import org.robolectric.annotation.Config; 30 import org.robolectric.shadows.ShadowUsageStatsManager.AppUsageObserver; 31 import org.robolectric.shadows.ShadowUsageStatsManager.UsageStatsBuilder; 32 33 /** Test for {@link ShadowUsageStatsManager}. */ 34 @RunWith(AndroidJUnit4.class) 35 @Config(minSdk = LOLLIPOP) 36 public class ShadowUsageStatsManagerTest { 37 38 private static final String TEST_PACKAGE_NAME1 = "com.company1.pkg1"; 39 private static final String TEST_PACKAGE_NAME2 = "com.company2.pkg2"; 40 private static final String TEST_ACTIVITY_NAME = "com.company2.pkg2.Activity"; 41 42 private UsageStatsManager usageStatsManager; 43 private Application context; 44 45 @Before 46 public void setUp() throws Exception { 47 usageStatsManager = 48 (UsageStatsManager) 49 ApplicationProvider.getApplicationContext().getSystemService(USAGE_STATS_SERVICE); 50 context = ApplicationProvider.getApplicationContext(); 51 } 52 53 @Test 54 public void testQueryEvents_emptyEvents() throws Exception { 55 UsageEvents events = usageStatsManager.queryEvents(1000L, 2000L); 56 Event event = new Event(); 57 58 assertThat(events.hasNextEvent()).isFalse(); 59 assertThat(events.getNextEvent(event)).isFalse(); 60 } 61 62 @Test 63 public void testQueryEvents_appendEventData_shouldCombineWithPreviousData() throws Exception { 64 shadowOf(usageStatsManager).addEvent(TEST_PACKAGE_NAME1, 500L, Event.MOVE_TO_FOREGROUND); 65 shadowOf(usageStatsManager).addEvent(TEST_PACKAGE_NAME1, 1000L, Event.MOVE_TO_BACKGROUND); 66 shadowOf(usageStatsManager) 67 .addEvent( 68 ShadowUsageStatsManager.EventBuilder.buildEvent() 69 .setTimeStamp(1500L) 70 .setPackage(TEST_PACKAGE_NAME2) 71 .setClass(TEST_ACTIVITY_NAME) 72 .setEventType(Event.MOVE_TO_FOREGROUND) 73 .build()); 74 shadowOf(usageStatsManager).addEvent(TEST_PACKAGE_NAME2, 2000L, Event.MOVE_TO_BACKGROUND); 75 shadowOf(usageStatsManager) 76 .addEvent( 77 ShadowUsageStatsManager.EventBuilder.buildEvent() 78 .setTimeStamp(2500L) 79 .setPackage(TEST_PACKAGE_NAME1) 80 .setEventType(Event.MOVE_TO_FOREGROUND) 81 .setClass(TEST_ACTIVITY_NAME) 82 .build()); 83 84 UsageEvents events = usageStatsManager.queryEvents(1000L, 2000L); 85 Event event = new Event(); 86 87 assertThat(events.hasNextEvent()).isTrue(); 88 assertThat(events.getNextEvent(event)).isTrue(); 89 assertThat(event.getPackageName()).isEqualTo(TEST_PACKAGE_NAME1); 90 assertThat(event.getTimeStamp()).isEqualTo(1000L); 91 assertThat(event.getEventType()).isEqualTo(Event.MOVE_TO_BACKGROUND); 92 93 assertThat(events.hasNextEvent()).isTrue(); 94 assertThat(events.getNextEvent(event)).isTrue(); 95 assertThat(event.getPackageName()).isEqualTo(TEST_PACKAGE_NAME2); 96 assertThat(event.getTimeStamp()).isEqualTo(1500L); 97 assertThat(event.getEventType()).isEqualTo(Event.MOVE_TO_FOREGROUND); 98 assertThat(event.getClassName()).isEqualTo(TEST_ACTIVITY_NAME); 99 100 assertThat(events.hasNextEvent()).isFalse(); 101 assertThat(events.getNextEvent(event)).isFalse(); 102 } 103 104 @Test 105 public void testQueryEvents_appendEventData_simulateTimeChange_shouldAddOffsetToPreviousData() 106 throws Exception { 107 shadowOf(usageStatsManager).addEvent(TEST_PACKAGE_NAME1, 500L, Event.MOVE_TO_FOREGROUND); 108 shadowOf(usageStatsManager).addEvent(TEST_PACKAGE_NAME1, 1000L, Event.MOVE_TO_BACKGROUND); 109 shadowOf(usageStatsManager) 110 .addEvent( 111 ShadowUsageStatsManager.EventBuilder.buildEvent() 112 .setTimeStamp(1500L) 113 .setPackage(TEST_PACKAGE_NAME2) 114 .setClass(TEST_ACTIVITY_NAME) 115 .setEventType(Event.MOVE_TO_FOREGROUND) 116 .build()); 117 shadowOf(usageStatsManager).addEvent(TEST_PACKAGE_NAME2, 2000L, Event.MOVE_TO_BACKGROUND); 118 shadowOf(usageStatsManager) 119 .addEvent( 120 ShadowUsageStatsManager.EventBuilder.buildEvent() 121 .setTimeStamp(2500L) 122 .setPackage(TEST_PACKAGE_NAME1) 123 .setEventType(Event.MOVE_TO_FOREGROUND) 124 .setClass(TEST_ACTIVITY_NAME) 125 .build()); 126 shadowOf(usageStatsManager).simulateTimeChange(10000L); 127 128 UsageEvents events = usageStatsManager.queryEvents(11000L, 12000L); 129 Event event = new Event(); 130 131 assertThat(events.hasNextEvent()).isTrue(); 132 assertThat(events.getNextEvent(event)).isTrue(); 133 assertThat(event.getPackageName()).isEqualTo(TEST_PACKAGE_NAME1); 134 assertThat(event.getTimeStamp()).isEqualTo(11000L); 135 assertThat(event.getEventType()).isEqualTo(Event.MOVE_TO_BACKGROUND); 136 137 assertThat(events.hasNextEvent()).isTrue(); 138 assertThat(events.getNextEvent(event)).isTrue(); 139 assertThat(event.getPackageName()).isEqualTo(TEST_PACKAGE_NAME2); 140 assertThat(event.getTimeStamp()).isEqualTo(11500L); 141 assertThat(event.getEventType()).isEqualTo(Event.MOVE_TO_FOREGROUND); 142 assertThat(event.getClassName()).isEqualTo(TEST_ACTIVITY_NAME); 143 144 assertThat(events.hasNextEvent()).isFalse(); 145 assertThat(events.getNextEvent(event)).isFalse(); 146 } 147 148 @Test 149 @Config(minSdk = Build.VERSION_CODES.P) 150 public void testGetAppStandbyBucket_withPackageName() throws Exception { 151 assertThat(shadowOf(usageStatsManager).getAppStandbyBuckets()).isEmpty(); 152 153 shadowOf(usageStatsManager).setAppStandbyBucket("app1", UsageStatsManager.STANDBY_BUCKET_RARE); 154 assertThat(shadowOf(usageStatsManager).getAppStandbyBucket("app1")) 155 .isEqualTo(UsageStatsManager.STANDBY_BUCKET_RARE); 156 assertThat(shadowOf(usageStatsManager).getAppStandbyBuckets().keySet()).containsExactly("app1"); 157 assertThat(shadowOf(usageStatsManager).getAppStandbyBuckets().get("app1")) 158 .isEqualTo(UsageStatsManager.STANDBY_BUCKET_RARE); 159 160 assertThat(shadowOf(usageStatsManager).getAppStandbyBucket("app_unset")) 161 .isEqualTo(UsageStatsManager.STANDBY_BUCKET_ACTIVE); 162 } 163 164 @Test 165 @Config(minSdk = Build.VERSION_CODES.P) 166 public void testSetAppStandbyBuckets() throws Exception { 167 assertThat(shadowOf(usageStatsManager).getAppStandbyBuckets()).isEmpty(); 168 assertThat(shadowOf(usageStatsManager).getAppStandbyBucket("app1")) 169 .isEqualTo(UsageStatsManager.STANDBY_BUCKET_ACTIVE); 170 171 Map<String, Integer> appBuckets = 172 Collections.singletonMap("app1", UsageStatsManager.STANDBY_BUCKET_RARE); 173 shadowOf(usageStatsManager).setAppStandbyBuckets(appBuckets); 174 175 assertThat(shadowOf(usageStatsManager).getAppStandbyBuckets()).isEqualTo(appBuckets); 176 assertThat(shadowOf(usageStatsManager).getAppStandbyBucket("app1")) 177 .isEqualTo(UsageStatsManager.STANDBY_BUCKET_RARE); 178 } 179 180 @Test 181 @Config(minSdk = Build.VERSION_CODES.P) 182 public void testGetAppStandbyBucket_currentApp() throws Exception { 183 shadowOf(usageStatsManager).setCurrentAppStandbyBucket(UsageStatsManager.STANDBY_BUCKET_RARE); 184 assertThat(shadowOf(usageStatsManager).getAppStandbyBucket()) 185 .isEqualTo(UsageStatsManager.STANDBY_BUCKET_RARE); 186 ShadowUsageStatsManager.reset(); 187 assertThat(shadowOf(usageStatsManager).getAppStandbyBucket()) 188 .isEqualTo(UsageStatsManager.STANDBY_BUCKET_ACTIVE); 189 } 190 191 @Test 192 @Config(minSdk = Build.VERSION_CODES.P) 193 public void testRegisterAppUsageObserver_uniqueObserverIds_shouldAddBothObservers() { 194 PendingIntent pendingIntent1 = PendingIntent.getBroadcast(context, 0, new Intent("ACTION1"), 0); 195 usageStatsManager.registerAppUsageObserver( 196 12, new String[] {"com.package1", "com.package2"}, 123L, TimeUnit.MINUTES, pendingIntent1); 197 PendingIntent pendingIntent2 = PendingIntent.getBroadcast(context, 0, new Intent("ACTION2"), 0); 198 usageStatsManager.registerAppUsageObserver( 199 24, new String[] {"com.package3"}, 456L, TimeUnit.SECONDS, pendingIntent2); 200 201 assertThat(shadowOf(usageStatsManager).getRegisteredAppUsageObservers()) 202 .containsExactly( 203 new AppUsageObserver( 204 12, 205 ImmutableList.of("com.package1", "com.package2"), 206 123L, 207 TimeUnit.MINUTES, 208 pendingIntent1), 209 new AppUsageObserver( 210 24, ImmutableList.of("com.package3"), 456L, TimeUnit.SECONDS, pendingIntent2)); 211 } 212 213 @Test 214 @Config(minSdk = Build.VERSION_CODES.P) 215 public void testRegisterAppUsageObserver_duplicateObserverIds_shouldOverrideExistingObserver() { 216 PendingIntent pendingIntent1 = PendingIntent.getBroadcast(context, 0, new Intent("ACTION1"), 0); 217 usageStatsManager.registerAppUsageObserver( 218 12, new String[] {"com.package1", "com.package2"}, 123L, TimeUnit.MINUTES, pendingIntent1); 219 PendingIntent pendingIntent2 = PendingIntent.getBroadcast(context, 0, new Intent("ACTION2"), 0); 220 usageStatsManager.registerAppUsageObserver( 221 12, new String[] {"com.package3"}, 456L, TimeUnit.SECONDS, pendingIntent2); 222 223 assertThat(shadowOf(usageStatsManager).getRegisteredAppUsageObservers()) 224 .containsExactly( 225 new AppUsageObserver( 226 12, ImmutableList.of("com.package3"), 456L, TimeUnit.SECONDS, pendingIntent2)); 227 } 228 229 @Test 230 @Config(minSdk = Build.VERSION_CODES.P) 231 public void testUnregisterAppUsageObserver_existingObserverId_shouldRemoveObserver() { 232 PendingIntent pendingIntent1 = PendingIntent.getBroadcast(context, 0, new Intent("ACTION1"), 0); 233 usageStatsManager.registerAppUsageObserver( 234 12, new String[] {"com.package1", "com.package2"}, 123L, TimeUnit.MINUTES, pendingIntent1); 235 PendingIntent pendingIntent2 = PendingIntent.getBroadcast(context, 0, new Intent("ACTION2"), 0); 236 usageStatsManager.registerAppUsageObserver( 237 24, new String[] {"com.package3"}, 456L, TimeUnit.SECONDS, pendingIntent2); 238 239 usageStatsManager.unregisterAppUsageObserver(12); 240 241 assertThat(shadowOf(usageStatsManager).getRegisteredAppUsageObservers()) 242 .containsExactly( 243 new AppUsageObserver( 244 24, ImmutableList.of("com.package3"), 456L, TimeUnit.SECONDS, pendingIntent2)); 245 } 246 247 @Test 248 @Config(minSdk = Build.VERSION_CODES.P) 249 public void testUnregisterAppUsageObserver_nonExistentObserverId_shouldBeNoOp() { 250 PendingIntent pendingIntent1 = PendingIntent.getBroadcast(context, 0, new Intent("ACTION1"), 0); 251 usageStatsManager.registerAppUsageObserver( 252 12, new String[] {"com.package1", "com.package2"}, 123L, TimeUnit.MINUTES, pendingIntent1); 253 PendingIntent pendingIntent2 = PendingIntent.getBroadcast(context, 0, new Intent("ACTION2"), 0); 254 usageStatsManager.registerAppUsageObserver( 255 24, new String[] {"com.package3"}, 456L, TimeUnit.SECONDS, pendingIntent2); 256 257 usageStatsManager.unregisterAppUsageObserver(36); 258 259 assertThat(shadowOf(usageStatsManager).getRegisteredAppUsageObservers()) 260 .containsExactly( 261 new AppUsageObserver( 262 12, 263 ImmutableList.of("com.package1", "com.package2"), 264 123L, 265 TimeUnit.MINUTES, 266 pendingIntent1), 267 new AppUsageObserver( 268 24, ImmutableList.of("com.package3"), 456L, TimeUnit.SECONDS, pendingIntent2)); 269 } 270 271 @Test 272 @Config(minSdk = Build.VERSION_CODES.P) 273 public void testTriggerRegisteredAppUsageObserver_shouldSendIntentAndRemoveObserver() { 274 PendingIntent pendingIntent1 = PendingIntent.getBroadcast(context, 0, new Intent("ACTION1"), 0); 275 usageStatsManager.registerAppUsageObserver( 276 12, new String[] {"com.package1", "com.package2"}, 123L, TimeUnit.MINUTES, pendingIntent1); 277 PendingIntent pendingIntent2 = PendingIntent.getBroadcast(context, 0, new Intent("ACTION2"), 0); 278 usageStatsManager.registerAppUsageObserver( 279 24, new String[] {"com.package3"}, 456L, TimeUnit.SECONDS, pendingIntent2); 280 281 shadowOf(usageStatsManager).triggerRegisteredAppUsageObserver(24, 500000L); 282 283 List<Intent> broadcastIntents = shadowOf(context).getBroadcastIntents(); 284 assertThat(broadcastIntents).hasSize(1); 285 Intent broadcastIntent = broadcastIntents.get(0); 286 assertThat(broadcastIntent.getAction()).isEqualTo("ACTION2"); 287 assertThat(broadcastIntent.getIntExtra(UsageStatsManager.EXTRA_OBSERVER_ID, 0)).isEqualTo(24); 288 assertThat(broadcastIntent.getLongExtra(UsageStatsManager.EXTRA_TIME_LIMIT, 0)) 289 .isEqualTo(456000L); 290 assertThat(broadcastIntent.getLongExtra(UsageStatsManager.EXTRA_TIME_USED, 0)) 291 .isEqualTo(500000L); 292 assertThat(shadowOf(usageStatsManager).getRegisteredAppUsageObservers()) 293 .containsExactly( 294 new AppUsageObserver( 295 12, 296 ImmutableList.of("com.package1", "com.package2"), 297 123L, 298 TimeUnit.MINUTES, 299 pendingIntent1)); 300 } 301 302 @Test 303 public void queryUsageStats_noStatsAdded() { 304 List<UsageStats> results = usageStatsManager.queryUsageStats(INTERVAL_WEEKLY, 0, 3000); 305 assertThat(results).isEmpty(); 306 } 307 308 @Test 309 public void queryUsageStats() { 310 UsageStats usageStats1 = newUsageStats(TEST_PACKAGE_NAME1, 0, 1000); 311 UsageStats usageStats2 = newUsageStats(TEST_PACKAGE_NAME1, 1001, 2000); 312 UsageStats usageStats3 = newUsageStats(TEST_PACKAGE_NAME1, 2001, 3000); 313 UsageStats usageStats4 = newUsageStats(TEST_PACKAGE_NAME1, 3001, 4000); 314 shadowOf(usageStatsManager).addUsageStats(INTERVAL_WEEKLY, usageStats1); 315 shadowOf(usageStatsManager).addUsageStats(INTERVAL_WEEKLY, usageStats2); 316 shadowOf(usageStatsManager).addUsageStats(INTERVAL_WEEKLY, usageStats3); 317 shadowOf(usageStatsManager).addUsageStats(INTERVAL_WEEKLY, usageStats4); 318 // Query fully covers usageStats 2 and 3, and partially overlaps with 4. 319 List<UsageStats> results = usageStatsManager.queryUsageStats(INTERVAL_WEEKLY, 1001, 3500); 320 assertThat(results).containsExactly(usageStats2, usageStats3, usageStats4); 321 } 322 323 @Test 324 public void queryUsageStats_multipleIntervalTypes() { 325 // Weekly data. 326 UsageStats usageStats1 = newUsageStats(TEST_PACKAGE_NAME1, 1000, 2000); 327 UsageStats usageStats2 = newUsageStats(TEST_PACKAGE_NAME1, 2001, 3000); 328 shadowOf(usageStatsManager).addUsageStats(INTERVAL_WEEKLY, usageStats1); 329 shadowOf(usageStatsManager).addUsageStats(INTERVAL_WEEKLY, usageStats2); 330 331 // Daily data. 332 UsageStats usageStats3 = newUsageStats(TEST_PACKAGE_NAME1, 2001, 3000); 333 shadowOf(usageStatsManager).addUsageStats(INTERVAL_DAILY, usageStats3); 334 335 List<UsageStats> results = usageStatsManager.queryUsageStats(INTERVAL_WEEKLY, 0, 3000); 336 assertThat(results).containsExactly(usageStats1, usageStats2); 337 results = usageStatsManager.queryUsageStats(INTERVAL_DAILY, 0, 3000); 338 assertThat(results).containsExactly(usageStats3); 339 } 340 341 private UsageStats newUsageStats(String packageName, long firstTimeStamp, long lastTimeStamp) { 342 return UsageStatsBuilder.newBuilder() 343 .setPackageName(packageName) 344 .setFirstTimeStamp(firstTimeStamp) 345 .setLastTimeStamp(lastTimeStamp) 346 .build(); 347 } 348 349 @Test 350 public void usageStatsBuilder_noFieldsSet() { 351 UsageStats usage = 352 UsageStatsBuilder.newBuilder() 353 // Don't set any fields; the object should still build. 354 .build(); 355 assertThat(usage.getPackageName()).isNull(); 356 assertThat(usage.getFirstTimeStamp()).isEqualTo(0); 357 assertThat(usage.getLastTimeStamp()).isEqualTo(0); 358 assertThat(usage.getLastTimeUsed()).isEqualTo(0); 359 assertThat(usage.getTotalTimeInForeground()).isEqualTo(0); 360 } 361 362 @Test 363 public void usageStatsBuilder() { 364 long firstTimestamp = 1_500_000_000_000L; 365 long lastTimestamp = firstTimestamp + 10000; 366 long lastTimeUsed = firstTimestamp + 100; 367 long totalTimeInForeground = HOURS.toMillis(10); 368 369 UsageStats usage = 370 UsageStatsBuilder.newBuilder() 371 // Set all fields 372 .setPackageName(TEST_PACKAGE_NAME1) 373 .setFirstTimeStamp(firstTimestamp) 374 .setLastTimeStamp(lastTimestamp) 375 .setLastTimeUsed(lastTimeUsed) 376 .setTotalTimeInForeground(totalTimeInForeground) 377 .build(); 378 assertThat(usage.getPackageName()).isEqualTo(TEST_PACKAGE_NAME1); 379 assertThat(usage.getFirstTimeStamp()).isEqualTo(firstTimestamp); 380 assertThat(usage.getLastTimeStamp()).isEqualTo(lastTimestamp); 381 assertThat(usage.getLastTimeUsed()).isEqualTo(lastTimeUsed); 382 assertThat(usage.getTotalTimeInForeground()).isEqualTo(totalTimeInForeground); 383 } 384 } 385