1 /* 2 * Copyright (C) 2021 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy 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, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17 package com.android.server.usage; 18 19 import static android.app.usage.UsageEvents.Event.ACTIVITY_RESUMED; 20 import static android.app.usage.UsageEvents.Event.APP_COMPONENT_USED; 21 import static android.app.usage.UsageEvents.Event.NOTIFICATION_SEEN; 22 23 import static org.junit.Assert.assertEquals; 24 import static org.junit.Assert.assertFalse; 25 import static org.junit.Assert.assertNotNull; 26 import static org.junit.Assert.assertTrue; 27 import static org.mockito.Mockito.mockitoSession; 28 29 import android.app.usage.UsageEvents; 30 import android.app.usage.UsageEvents.Event; 31 import android.content.Context; 32 import android.os.SystemClock; 33 import android.text.format.DateUtils; 34 import android.util.Log; 35 36 import androidx.test.InstrumentationRegistry; 37 import androidx.test.runner.AndroidJUnit4; 38 39 import com.android.server.usage.UserUsageStatsService.StatsUpdatedListener; 40 41 import org.junit.After; 42 import org.junit.Before; 43 import org.junit.Test; 44 import org.junit.runner.RunWith; 45 import org.mockito.Mock; 46 import org.mockito.MockitoSession; 47 import org.mockito.quality.Strictness; 48 49 import java.io.File; 50 import java.util.HashMap; 51 52 @RunWith(AndroidJUnit4.class) 53 public class UserUsageStatsServiceTest { 54 private static final String TAG = UserUsageStatsServiceTest.class.getSimpleName(); 55 56 private static final int TEST_USER_ID = 0; 57 private static final String TEST_PACKAGE_NAME = "test.package"; 58 private static final long TIME_INTERVAL_MILLIS = DateUtils.DAY_IN_MILLIS; 59 60 private UserUsageStatsService mService; 61 private MockitoSession mMockitoSession; 62 63 private File mDir; 64 65 @Mock 66 private Context mContext; 67 @Mock 68 private StatsUpdatedListener mStatsUpdatedListener; 69 70 @Before setUp()71 public void setUp() { 72 mMockitoSession = mockitoSession() 73 .initMocks(this) 74 .strictness(Strictness.LENIENT) 75 .startMocking(); 76 77 // Deleting in tearDown() doesn't always work, so adding a unique suffix to each test 78 // directory to ensure sequential test runs don't interfere with each other. 79 mDir = new File(InstrumentationRegistry.getContext().getCacheDir(), 80 "test_" + System.currentTimeMillis()); 81 mService = new UserUsageStatsService(mContext, TEST_USER_ID, mDir, mStatsUpdatedListener); 82 83 HashMap<String, Long> installedPkgs = new HashMap<>(); 84 installedPkgs.put(TEST_PACKAGE_NAME, System.currentTimeMillis()); 85 86 mService.init(System.currentTimeMillis(), installedPkgs, true); 87 } 88 89 @After tearDown()90 public void tearDown() { 91 if (mDir != null && mDir.exists() && !mDir.delete()) { 92 Log.d(TAG, "Failed to delete test directory"); 93 } 94 if (mMockitoSession != null) { 95 mMockitoSession.finishMocking(); 96 } 97 } 98 99 @Test testReportEvent_eventAppearsInQueries()100 public void testReportEvent_eventAppearsInQueries() { 101 Event event = new Event(ACTIVITY_RESUMED, SystemClock.elapsedRealtime()); 102 event.mPackage = TEST_PACKAGE_NAME; 103 mService.reportEvent(event); 104 105 // Force persist the event instead of waiting for it to be processed on the handler. 106 mService.persistActiveStats(); 107 108 long now = System.currentTimeMillis(); 109 long startTime = now - TIME_INTERVAL_MILLIS; 110 UsageEvents events = mService.queryEventsForPackage( 111 startTime, now, TEST_PACKAGE_NAME, false /* includeTaskRoot */); 112 113 boolean hasTestEvent = false; 114 while (events != null && events.hasNextEvent()) { 115 Event outEvent = new Event(); 116 events.getNextEvent(outEvent); 117 if (outEvent.mEventType == ACTIVITY_RESUMED) { 118 hasTestEvent = true; 119 } 120 } 121 assertTrue(hasTestEvent); 122 } 123 124 @Test testReportEvent_packageUsedEventNotTracked()125 public void testReportEvent_packageUsedEventNotTracked() { 126 // For APP_COMPONENT_USED event, the time stamp should have been converted to current time 127 // before reported here. 128 Event event = new Event(APP_COMPONENT_USED, System.currentTimeMillis()); 129 event.mPackage = TEST_PACKAGE_NAME; 130 mService.reportEvent(event); 131 132 // Force persist the event instead of waiting for it to be processed on the handler. 133 mService.persistActiveStats(); 134 135 long now = System.currentTimeMillis(); 136 long startTime = now - TIME_INTERVAL_MILLIS; 137 UsageEvents events = mService.queryEventsForPackage( 138 startTime, now, TEST_PACKAGE_NAME, false /* includeTaskRoot */); 139 140 boolean hasTestEvent = false; 141 while (events != null && events.hasNextEvent()) { 142 Event outEvent = new Event(); 143 events.getNextEvent(outEvent); 144 if (outEvent.mEventType == APP_COMPONENT_USED) { 145 hasTestEvent = true; 146 } 147 } 148 assertFalse(hasTestEvent); 149 } 150 151 @Test testQueryEarliestEventsForPackage()152 public void testQueryEarliestEventsForPackage() { 153 Event event1 = new Event(NOTIFICATION_SEEN, SystemClock.elapsedRealtime()); 154 event1.mPackage = TEST_PACKAGE_NAME; 155 mService.reportEvent(event1); 156 Event event2 = new Event(ACTIVITY_RESUMED, SystemClock.elapsedRealtime()); 157 event2.mPackage = TEST_PACKAGE_NAME; 158 mService.reportEvent(event2); 159 160 // Force persist the events instead of waiting for them to be processed on the handler. 161 mService.persistActiveStats(); 162 163 long now = System.currentTimeMillis(); 164 long startTime = now - TIME_INTERVAL_MILLIS; 165 UsageEvents events = mService.queryEarliestEventsForPackage( 166 startTime, now, TEST_PACKAGE_NAME, ACTIVITY_RESUMED); 167 168 assertNotNull(events); 169 boolean hasTestEvent = false; 170 int count = 0; 171 while (events.hasNextEvent()) { 172 count++; 173 Event outEvent = new Event(); 174 events.getNextEvent(outEvent); 175 if (outEvent.mEventType == ACTIVITY_RESUMED) { 176 hasTestEvent = true; 177 } 178 } 179 assertTrue(hasTestEvent); 180 assertEquals(2, count); 181 } 182 183 /** Tests that the API works as expected even with the caching system. */ 184 @Test testQueryEarliestEventsForPackage_Caching()185 public void testQueryEarliestEventsForPackage_Caching() throws Exception { 186 final long forcedDiff = 5000; 187 Event event1 = new Event(NOTIFICATION_SEEN, SystemClock.elapsedRealtime()); 188 event1.mPackage = TEST_PACKAGE_NAME; 189 mService.reportEvent(event1); 190 final long event1ReportTime = System.currentTimeMillis(); 191 Thread.sleep(forcedDiff); 192 Event event2 = new Event(ACTIVITY_RESUMED, SystemClock.elapsedRealtime()); 193 event2.mPackage = TEST_PACKAGE_NAME; 194 mService.reportEvent(event2); 195 final long event2ReportTime = System.currentTimeMillis(); 196 197 // Force persist the events instead of waiting for them to be processed on the handler. 198 mService.persistActiveStats(); 199 200 long now = System.currentTimeMillis(); 201 long startTime = now - forcedDiff * 2; 202 UsageEvents events = mService.queryEarliestEventsForPackage( 203 startTime, now, TEST_PACKAGE_NAME, ACTIVITY_RESUMED); 204 205 assertNotNull(events); 206 boolean hasTestEvent = false; 207 int count = 0; 208 while (events.hasNextEvent()) { 209 count++; 210 Event outEvent = new Event(); 211 events.getNextEvent(outEvent); 212 if (outEvent.mEventType == ACTIVITY_RESUMED) { 213 hasTestEvent = true; 214 } 215 } 216 assertTrue(hasTestEvent); 217 assertEquals(2, count); 218 219 // Query again 220 events = mService.queryEarliestEventsForPackage( 221 startTime, now, TEST_PACKAGE_NAME, ACTIVITY_RESUMED); 222 223 assertNotNull(events); 224 hasTestEvent = false; 225 count = 0; 226 while (events.hasNextEvent()) { 227 count++; 228 Event outEvent = new Event(); 229 events.getNextEvent(outEvent); 230 if (outEvent.mEventType == ACTIVITY_RESUMED) { 231 hasTestEvent = true; 232 } 233 } 234 assertTrue(hasTestEvent); 235 assertEquals(2, count); 236 237 // Query around just the first event 238 now = event1ReportTime; 239 startTime = now - forcedDiff * 2; 240 events = mService.queryEarliestEventsForPackage( 241 startTime, now, TEST_PACKAGE_NAME, ACTIVITY_RESUMED); 242 243 assertNotNull(events); 244 hasTestEvent = false; 245 count = 0; 246 while (events.hasNextEvent()) { 247 count++; 248 Event outEvent = new Event(); 249 events.getNextEvent(outEvent); 250 if (outEvent.mEventType == ACTIVITY_RESUMED) { 251 hasTestEvent = true; 252 } 253 } 254 assertFalse(hasTestEvent); 255 assertEquals(1, count); 256 257 // Shift query around the first event, still exclude the second 258 now = event1ReportTime + forcedDiff / 2; 259 startTime = event1ReportTime - forcedDiff / 2; 260 events = mService.queryEarliestEventsForPackage( 261 startTime, now, TEST_PACKAGE_NAME, ACTIVITY_RESUMED); 262 263 assertNotNull(events); 264 hasTestEvent = false; 265 count = 0; 266 while (events.hasNextEvent()) { 267 count++; 268 Event outEvent = new Event(); 269 events.getNextEvent(outEvent); 270 if (outEvent.mEventType == ACTIVITY_RESUMED) { 271 hasTestEvent = true; 272 } 273 } 274 assertFalse(hasTestEvent); 275 assertEquals(1, count); 276 277 // Shift query around the second event only 278 now = event2ReportTime + 1; 279 startTime = event1ReportTime + forcedDiff / 4; 280 events = mService.queryEarliestEventsForPackage( 281 startTime, now, TEST_PACKAGE_NAME, ACTIVITY_RESUMED); 282 283 assertNotNull(events); 284 hasTestEvent = false; 285 count = 0; 286 while (events.hasNextEvent()) { 287 count++; 288 Event outEvent = new Event(); 289 events.getNextEvent(outEvent); 290 if (outEvent.mEventType == ACTIVITY_RESUMED) { 291 hasTestEvent = true; 292 } 293 } 294 assertTrue(hasTestEvent); 295 assertEquals(1, count); 296 297 // Shift query around both events 298 now = event2ReportTime + 1; 299 startTime = now - forcedDiff * 2; 300 events = mService.queryEarliestEventsForPackage( 301 startTime, now, TEST_PACKAGE_NAME, ACTIVITY_RESUMED); 302 303 assertNotNull(events); 304 hasTestEvent = false; 305 count = 0; 306 while (events.hasNextEvent()) { 307 count++; 308 Event outEvent = new Event(); 309 events.getNextEvent(outEvent); 310 if (outEvent.mEventType == ACTIVITY_RESUMED) { 311 hasTestEvent = true; 312 } 313 } 314 assertTrue(hasTestEvent); 315 assertEquals(2, count); 316 317 // Query around just the first event and then shift end time to include second event 318 now = event1ReportTime; 319 startTime = now - forcedDiff * 2; 320 events = mService.queryEarliestEventsForPackage( 321 startTime, now, TEST_PACKAGE_NAME, ACTIVITY_RESUMED); 322 323 assertNotNull(events); 324 hasTestEvent = false; 325 count = 0; 326 while (events.hasNextEvent()) { 327 count++; 328 Event outEvent = new Event(); 329 events.getNextEvent(outEvent); 330 if (outEvent.mEventType == ACTIVITY_RESUMED) { 331 hasTestEvent = true; 332 } 333 } 334 assertFalse(hasTestEvent); 335 assertEquals(1, count); 336 337 now = event2ReportTime + 1; 338 events = mService.queryEarliestEventsForPackage( 339 startTime, now, TEST_PACKAGE_NAME, ACTIVITY_RESUMED); 340 341 assertNotNull(events); 342 hasTestEvent = false; 343 count = 0; 344 while (events.hasNextEvent()) { 345 count++; 346 Event outEvent = new Event(); 347 events.getNextEvent(outEvent); 348 if (outEvent.mEventType == ACTIVITY_RESUMED) { 349 hasTestEvent = true; 350 } 351 } 352 assertTrue(hasTestEvent); 353 assertEquals(2, count); 354 } 355 } 356