1 /* 2 * Copyright (C) 2024 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 android.profiling.cts; 18 19 import static android.os.profiling.ProfilingService.TracingState; 20 21 import static org.junit.Assert.assertEquals; 22 import static org.junit.Assert.assertFalse; 23 import static org.junit.Assert.assertNotEquals; 24 import static org.junit.Assert.assertNotNull; 25 import static org.junit.Assert.assertNull; 26 import static org.junit.Assert.assertTrue; 27 import static org.junit.Assert.fail; 28 import static org.mockito.ArgumentMatchers.any; 29 import static org.mockito.ArgumentMatchers.anyInt; 30 import static org.mockito.ArgumentMatchers.eq; 31 import static org.mockito.Mockito.doNothing; 32 import static org.mockito.Mockito.doReturn; 33 import static org.mockito.Mockito.doThrow; 34 import static org.mockito.Mockito.spy; 35 import static org.mockito.Mockito.times; 36 import static org.mockito.Mockito.verify; 37 38 import android.app.Instrumentation; 39 import android.content.Context; 40 import android.os.Binder; 41 import android.os.Bundle; 42 import android.os.Handler; 43 import android.os.IProfilingResultCallback; 44 import android.os.ProfilingManager; 45 import android.os.ProfilingResult; 46 import android.os.ProfilingTrigger; 47 import android.os.profiling.DeviceConfigHelper; 48 import android.os.profiling.ProfilingService; 49 import android.os.profiling.ProfilingTriggerData; 50 import android.os.profiling.RateLimiter; 51 import android.os.profiling.TracingSession; 52 import android.platform.test.annotations.EnableFlags; 53 import android.platform.test.flag.junit.CheckFlagsRule; 54 import android.platform.test.flag.junit.DeviceFlagsValueProvider; 55 import android.platform.test.flag.junit.SetFlagsRule; 56 import android.util.SparseArray; 57 58 import androidx.test.core.app.ApplicationProvider; 59 import androidx.test.platform.app.InstrumentationRegistry; 60 import androidx.test.runner.AndroidJUnit4; 61 62 import com.android.compatibility.common.util.SystemUtil; 63 64 import com.google.errorprone.annotations.FormatMethod; 65 66 import org.junit.After; 67 import org.junit.Before; 68 import org.junit.Rule; 69 import org.junit.Test; 70 import org.junit.runner.RunWith; 71 import org.mockito.Mock; 72 import org.mockito.MockitoAnnotations; 73 74 import java.io.File; 75 import java.io.FileOutputStream; 76 import java.util.ArrayList; 77 import java.util.Arrays; 78 import java.util.List; 79 import java.util.UUID; 80 import java.util.concurrent.TimeUnit; 81 82 /** 83 * Tests in this class are for testing the ProfilingService directly without the need to get a 84 * reference to the service via the call to getSystemService(). 85 */ 86 87 @RunWith(AndroidJUnit4.class) 88 public final class ProfilingServiceTests { 89 90 private static final String APP_PACKAGE_NAME = "com.android.profiling.tests"; 91 private static final String REQUEST_TAG = "some unique string"; 92 93 private static final String OVERRIDE_DEVICE_CONFIG_INT = "device_config put %s %s %d"; 94 private static final String GET_DEVICE_CONFIG = "device_config get %s %s"; 95 private static final String DELETE_DEVICE_CONFIG = "device_config delete %s %s"; 96 private static final String RESET_NAMESPACE = "device_config reset trusted_defaults %s"; 97 98 private static final String PERSIST_TEST_DIR = "testdir"; 99 private static final String PERSIST_TEST_FILE = "testfile"; 100 101 // Key most and least significant bits are used to generate a unique key specific to each 102 // request. Key is used to pair request back to caller and callbacks so test to keep consistent. 103 private static final long KEY_MOST_SIG_BITS = 456L; 104 private static final long KEY_LEAST_SIG_BITS = 123L; 105 106 private static final int FAKE_UID = 12345; 107 private static final int FAKE_UID_2 = 12346; 108 109 // Stub value for tests when system triggered api is not guaranteed to be enabled so 110 // {@link ProfilingTrigger#TRIGGER_TYPE_NONE} cannot be accessed. Value is the same as trigger 111 // type none. When cleaning up system triggered flag, remove this value and replace with 112 // {@link ProfilingTrigger#TRIGGER_TYPE_NONE}. 113 private static final int TRIGGER_TYPE_NONE = 0; 114 115 private static final int RATE_LIMITING_0_HOURS_BETWEEN = 0; 116 117 private static final int DEFAULT_LIMIT_PROCESS_HOUR = 5; 118 private static final int DEFAULT_LIMIT_PROCESS_DAY = 20; 119 private static final int DEFAULT_LIMIT_PROCESS_WEEK = 50; 120 private static final int DEFAULT_LIMIT_SYSTEM_HOUR = 10; 121 private static final int DEFAULT_LIMIT_SYSTEM_DAY = 50; 122 private static final int DEFAULT_LIMIT_SYSTEM_WEEK = 100; 123 private static final int DEFAULT_PROFILING_RUN_COST = 1; 124 private static final int DEFAULT_PERSIST_TO_DISK_FREQUENCY = 0; 125 126 @Rule 127 public final CheckFlagsRule mCheckFlagsRule = DeviceFlagsValueProvider.createCheckFlagsRule(); 128 @Rule public final SetFlagsRule mSetFlagsRule = new SetFlagsRule(); 129 130 @Mock private Process mActiveTrace; 131 132 private Context mContext = ApplicationProvider.getApplicationContext(); 133 private Instrumentation mInstrumentation; 134 private ProfilingService mProfilingService; 135 private RateLimiter mRateLimiter; 136 137 @Before setUp()138 public void setUp() throws Exception { 139 MockitoAnnotations.initMocks(this); 140 mInstrumentation = InstrumentationRegistry.getInstrumentation(); 141 142 executeShellCmd(RESET_NAMESPACE, DeviceConfigHelper.NAMESPACE); 143 executeShellCmd(RESET_NAMESPACE, DeviceConfigHelper.NAMESPACE_TESTING); 144 145 mContext = spy(ApplicationProvider.getApplicationContext()); 146 mProfilingService = spy(new ProfilingService(mContext)); 147 mRateLimiter = spy(new RateLimiter(new RateLimiter.HandlerCallback() { 148 @Override 149 public Handler obtainHandler() { 150 return null; 151 } 152 })); 153 mProfilingService.mRateLimiter = mRateLimiter; 154 155 // Override the persist file/directory, for both queue and rate limiter, and instead point 156 // to our own file/directory in app storage, since the test app context can't access 157 // /data/system 158 doReturn(true).when(mRateLimiter).setupPersistFiles(); 159 mRateLimiter.mPersistStoreDir = new File(mContext.getFilesDir(), PERSIST_TEST_DIR); 160 mRateLimiter.mPersistStoreDir.mkdir(); 161 mRateLimiter.mPersistFile = new File(mRateLimiter.mPersistStoreDir, PERSIST_TEST_FILE); 162 163 doReturn(true).when(mProfilingService).setupPersistQueueFiles(); 164 mProfilingService.mPersistStoreDir = 165 new File(mContext.getFilesDir(), PERSIST_TEST_DIR); 166 // Same dir for both, no need to create the 2nd time. 167 mProfilingService.mPersistQueueFile = 168 new File(mProfilingService.mPersistStoreDir, PERSIST_TEST_FILE); 169 170 doReturn(true).when(mProfilingService).setupPersistAppTriggerFiles(); 171 mProfilingService.mPersistAppTriggersFile = 172 new File(mProfilingService.mPersistStoreDir, PERSIST_TEST_FILE); 173 174 // Since we use mock files we can't rely on the setup call that would typically come from 175 // initialization of rate limiter, so trigger setup manually. 176 mRateLimiter.setupFromPersistedData(); 177 } 178 179 @After cleanup()180 public void cleanup() throws Exception { 181 // Delete any local persist files. 182 if (mRateLimiter.mPersistFile != null) { 183 mRateLimiter.mPersistFile.delete(); 184 } 185 if (mProfilingService.mPersistQueueFile != null) { 186 // This doesn't really do anything as the 2 file objects point to the same actual file 187 // on disk, but just in case that changes try the delete here too. 188 mProfilingService.mPersistQueueFile.delete(); 189 } 190 191 // Remove any overrides set for period. 192 executeShellCmd(DELETE_DEVICE_CONFIG, DeviceConfigHelper.NAMESPACE, 193 DeviceConfigHelper.SYSTEM_TRIGGERED_TRACE_MIN_PERIOD_SECONDS); 194 executeShellCmd(DELETE_DEVICE_CONFIG, DeviceConfigHelper.NAMESPACE, 195 DeviceConfigHelper.SYSTEM_TRIGGERED_TRACE_MAX_PERIOD_SECONDS); 196 } 197 198 /** Test that registering binder callbacks works as expected. */ 199 @Test testRegisterResultCallback()200 public void testRegisterResultCallback() { 201 ProfilingResultCallback callback = new ProfilingResultCallback(); 202 203 // Register callback. 204 mProfilingService.registerResultsCallback(true, callback); 205 206 // Confirm callback is registered. 207 assertEquals(callback, 208 mProfilingService.mResultCallbacks.get(Binder.getCallingUid()).get(0)); 209 } 210 211 /** Test that only the callback belonging to the requesting uid is triggered. */ 212 @Test testRequestProfiling_OnlyRequestingProcessCallbackTriggered()213 public void testRequestProfiling_OnlyRequestingProcessCallbackTriggered() { 214 // Mock traces running check to simulate collection running so it fails early. 215 doReturn(true).when(mProfilingService).areAnyTracesRunning(); 216 217 ProfilingResultCallback callback = new ProfilingResultCallback(); 218 ProfilingResultCallback mockProcessCallback = new ProfilingResultCallback(); 219 220 // Register callback. 221 mProfilingService.registerResultsCallback(false, callback); 222 223 // Add other process callback manually to mock uid. 224 List<IProfilingResultCallback> callbacks = Arrays.asList(mockProcessCallback); 225 mProfilingService.mResultCallbacks.put(FAKE_UID, callbacks); 226 227 // Confirm both callbacks are registered. 228 assertEquals(callback, 229 mProfilingService.mResultCallbacks.get(Binder.getCallingUid()).get(0)); 230 assertEquals(mockProcessCallback, 231 mProfilingService.mResultCallbacks.get(FAKE_UID).get(0)); 232 233 // Kick off request. 234 mProfilingService.requestProfiling(ProfilingManager.PROFILING_TYPE_JAVA_HEAP_DUMP, null, 235 REQUEST_TAG, KEY_MOST_SIG_BITS, KEY_LEAST_SIG_BITS, APP_PACKAGE_NAME); 236 237 // Confirm callbacks was triggered for callback registered to this process. 238 assertTrue(callback.mResultSent); 239 240 // Confirm callbacks was not triggered for callback registered to other process. 241 assertFalse(mockProcessCallback.mResultSent); 242 } 243 244 /** Test that multiple callbacks belonging to the requesting uid are all triggered. */ 245 @Test testRequestProfiling_MultipleCallbackTriggered()246 public void testRequestProfiling_MultipleCallbackTriggered() { 247 // Mock traces running check to simulate collection running so it fails early. 248 doReturn(true).when(mProfilingService).areAnyTracesRunning(); 249 250 ProfilingResultCallback callbackOne = new ProfilingResultCallback(); 251 ProfilingResultCallback callbackTwo = new ProfilingResultCallback(); 252 253 // Register callbacks. 254 mProfilingService.registerResultsCallback(true, callbackOne); 255 mProfilingService.registerResultsCallback(true, callbackTwo); 256 257 // Confirm both callbacks are registered. 258 assertEquals(callbackOne, 259 mProfilingService.mResultCallbacks.get(Binder.getCallingUid()).get(0)); 260 assertEquals(callbackTwo, 261 mProfilingService.mResultCallbacks.get(Binder.getCallingUid()).get(1)); 262 263 // Kick off request. 264 mProfilingService.requestProfiling(ProfilingManager.PROFILING_TYPE_JAVA_HEAP_DUMP, null, 265 REQUEST_TAG, KEY_MOST_SIG_BITS, KEY_LEAST_SIG_BITS, APP_PACKAGE_NAME); 266 267 // Confirm callbacks was triggered for callback registered to this process. 268 assertTrue(callbackOne.mResultSent); 269 assertTrue(callbackTwo.mResultSent); 270 } 271 272 /** 273 * Test that requesting profiling while another profiling is in progress fails with correct 274 * error codes. 275 */ 276 @Test testRequestProfiling_ProfilingRunning_Fails()277 public void testRequestProfiling_ProfilingRunning_Fails() { 278 // Mock traces running check to simulate collection running. 279 doReturn(true).when(mProfilingService).areAnyTracesRunning(); 280 281 // Register callback. 282 ProfilingResultCallback callback = new ProfilingResultCallback(); 283 mProfilingService.registerResultsCallback(false, callback); 284 285 // Kick off request. 286 mProfilingService.requestProfiling(ProfilingManager.PROFILING_TYPE_JAVA_HEAP_DUMP, null, 287 REQUEST_TAG, KEY_MOST_SIG_BITS, KEY_LEAST_SIG_BITS, APP_PACKAGE_NAME); 288 289 // Confirm result matches failure expectation. 290 confirmResultCallback(callback, null, KEY_MOST_SIG_BITS, KEY_LEAST_SIG_BITS, 291 ProfilingResult.ERROR_FAILED_PROFILING_IN_PROGRESS, REQUEST_TAG, false); 292 } 293 294 /** 295 * Test that requesting profiling with an invalid request byte array fails with correct error 296 * codes. 297 */ 298 @Test testRequestProfiling_InvalidRequest_Fails()299 public void testRequestProfiling_InvalidRequest_Fails() { 300 // Bypass traces running check, we're not testing that here. 301 doReturn(false).when(mProfilingService).areAnyTracesRunning(); 302 303 // Register callback. 304 ProfilingResultCallback callback = new ProfilingResultCallback(); 305 mProfilingService.registerResultsCallback(false, callback); 306 307 // Kick off request. 308 mProfilingService.requestProfiling(-1, null, REQUEST_TAG, KEY_MOST_SIG_BITS, 309 KEY_LEAST_SIG_BITS, APP_PACKAGE_NAME); 310 311 // Confirm result matches failure expectation. 312 confirmResultCallback(callback, null, KEY_MOST_SIG_BITS, KEY_LEAST_SIG_BITS, 313 ProfilingResult.ERROR_FAILED_INVALID_REQUEST, REQUEST_TAG, true); 314 } 315 316 /** Test that requesting where we cannot access the package name fails. */ 317 @Test testRequestProfiling_PackageNameNotFound_Fails()318 public void testRequestProfiling_PackageNameNotFound_Fails() { 319 // Register callback. 320 ProfilingResultCallback callback = new ProfilingResultCallback(); 321 mProfilingService.registerResultsCallback(false, callback); 322 323 // Kick off request. 324 mProfilingService.requestProfiling(ProfilingManager.PROFILING_TYPE_JAVA_HEAP_DUMP, null, 325 REQUEST_TAG, KEY_MOST_SIG_BITS, KEY_LEAST_SIG_BITS, null); 326 327 // Confirm result matches failure expectation. 328 confirmResultCallback(callback, null, KEY_MOST_SIG_BITS, KEY_LEAST_SIG_BITS, 329 ProfilingResult.ERROR_UNKNOWN, REQUEST_TAG, true); 330 } 331 332 /** Test that requesting with a package name not associated with the calling uid fails. */ 333 @Test testRequestProfiling_PackageNameNotAssociatedWithCaller_Fails()334 public void testRequestProfiling_PackageNameNotAssociatedWithCaller_Fails() { 335 // Register callback. 336 ProfilingResultCallback callback = new ProfilingResultCallback(); 337 mProfilingService.registerResultsCallback(false, callback); 338 339 // Kick off request. 340 mProfilingService.requestProfiling(ProfilingManager.PROFILING_TYPE_JAVA_HEAP_DUMP, null, 341 REQUEST_TAG, KEY_MOST_SIG_BITS, KEY_LEAST_SIG_BITS, "not.my.application"); 342 343 // Confirm result matches failure expectation. 344 confirmResultCallback(callback, null, KEY_MOST_SIG_BITS, KEY_LEAST_SIG_BITS, 345 ProfilingResult.ERROR_FAILED_INVALID_REQUEST, REQUEST_TAG, true); 346 } 347 348 /** Test that failing rate limiting blocks trace from running. */ 349 @Test testRequestProfiling_RateLimitBlocked_Fails()350 public void testRequestProfiling_RateLimitBlocked_Fails() { 351 // Bypass traces running check, we're not testing that here. 352 doReturn(false).when(mProfilingService).areAnyTracesRunning(); 353 354 // Mock rate limiter result to simulate failure case. 355 doReturn(RateLimiter.RATE_LIMIT_RESULT_BLOCKED_PROCESS).when(mRateLimiter) 356 .isProfilingRequestAllowed(anyInt(), anyInt(), eq(false), any()); 357 358 // Register callback. 359 ProfilingResultCallback callback = new ProfilingResultCallback(); 360 mProfilingService.registerResultsCallback(false, callback); 361 362 // Kick off request. 363 mProfilingService.requestProfiling(ProfilingManager.PROFILING_TYPE_JAVA_HEAP_DUMP, null, 364 REQUEST_TAG, KEY_MOST_SIG_BITS, KEY_LEAST_SIG_BITS, APP_PACKAGE_NAME); 365 366 // Confirm result matches failure expectation. 367 confirmResultCallback(callback, null, KEY_MOST_SIG_BITS, KEY_LEAST_SIG_BITS, 368 ProfilingResult.ERROR_FAILED_RATE_LIMIT_PROCESS, REQUEST_TAG, false); 369 } 370 371 /** Test that if we can't contact Perfetto, we'll see an error callback. */ 372 @Test testRequestProfiling_Allowed_PerfettoPermissions_Fails()373 public void testRequestProfiling_Allowed_PerfettoPermissions_Fails() { 374 // Throw a RuntimeException when we try to query Perfetto for running traces. 375 // This implies that we can't contact Perfetto. 376 doThrow(RuntimeException.class).when(mProfilingService).areAnyTracesRunning(); 377 378 // Register callback. 379 ProfilingResultCallback callback = new ProfilingResultCallback(); 380 mProfilingService.registerResultsCallback(false, callback); 381 382 // Kick off request. 383 mProfilingService.requestProfiling(ProfilingManager.PROFILING_TYPE_JAVA_HEAP_DUMP, null, 384 REQUEST_TAG, KEY_MOST_SIG_BITS, KEY_LEAST_SIG_BITS, APP_PACKAGE_NAME); 385 386 // Perfetto cannot be run from this context, ensure it was attempted and failed permissions. 387 confirmResultCallback(callback, null, KEY_MOST_SIG_BITS, KEY_LEAST_SIG_BITS, 388 ProfilingResult.ERROR_UNKNOWN, REQUEST_TAG, true); 389 assertEquals("Error communicating with perfetto", callback.mError); 390 } 391 392 /** Test that checking if any traces are running works when trace is running. */ 393 @Test testAreAnyTracesRunning_True()394 public void testAreAnyTracesRunning_True() { 395 // Ensure no active tracing sessions tracked. 396 mProfilingService.mActiveTracingSessions.clear(); 397 assertFalse(mProfilingService.areAnyTracesRunning()); 398 399 // Create a tracing session. 400 TracingSession tracingSession = new TracingSession( 401 ProfilingManager.PROFILING_TYPE_JAVA_HEAP_DUMP, null, 123, APP_PACKAGE_NAME, 402 REQUEST_TAG, KEY_MOST_SIG_BITS, KEY_LEAST_SIG_BITS, TRIGGER_TYPE_NONE); 403 404 // Mock tracing session to be running. 405 doReturn(true).when(mActiveTrace).isAlive(); 406 407 // Add trace to session and session to ProfilingService tracked sessions. 408 tracingSession.setActiveTrace(mActiveTrace); 409 mProfilingService.mActiveTracingSessions.put( 410 (new UUID(KEY_MOST_SIG_BITS, KEY_LEAST_SIG_BITS)).toString(), tracingSession); 411 412 // Confirm check returns that a trace is running. 413 assertTrue(mProfilingService.areAnyTracesRunning()); 414 } 415 416 /** Test that checking if any traces are running works when trace is not running. */ 417 @Test testAreAnyTracesRunning_False()418 public void testAreAnyTracesRunning_False() { 419 mProfilingService.mActiveTracingSessions.clear(); 420 assertEquals(0, mProfilingService.mActiveTracingSessions.size()); 421 assertFalse(mProfilingService.areAnyTracesRunning()); 422 423 TracingSession tracingSession = new TracingSession( 424 ProfilingManager.PROFILING_TYPE_JAVA_HEAP_DUMP, null, 123, APP_PACKAGE_NAME, 425 REQUEST_TAG, KEY_MOST_SIG_BITS, KEY_LEAST_SIG_BITS, TRIGGER_TYPE_NONE); 426 mProfilingService.mActiveTracingSessions.put( 427 (new UUID(KEY_MOST_SIG_BITS, KEY_LEAST_SIG_BITS)).toString(), tracingSession); 428 429 // Confirm no traces are running because the 1 we added is not in a running state. 430 assertFalse(mProfilingService.areAnyTracesRunning()); 431 } 432 433 /** Test that cleaning up active traces list works correctly. */ 434 @Test testActiveTracesCleanup()435 public void testActiveTracesCleanup() { 436 mProfilingService.mActiveTracingSessions.clear(); 437 assertEquals(0, mProfilingService.mActiveTracingSessions.size()); 438 assertFalse(mProfilingService.areAnyTracesRunning()); 439 440 TracingSession tracingSession = new TracingSession( 441 ProfilingManager.PROFILING_TYPE_JAVA_HEAP_DUMP, null, 123, APP_PACKAGE_NAME, 442 REQUEST_TAG, KEY_MOST_SIG_BITS, KEY_LEAST_SIG_BITS, TRIGGER_TYPE_NONE); 443 mProfilingService.mActiveTracingSessions.put( 444 (new UUID(KEY_MOST_SIG_BITS, KEY_LEAST_SIG_BITS)).toString(), tracingSession); 445 446 // Confirm the session was added. 447 assertEquals(1, mProfilingService.mActiveTracingSessions.size()); 448 449 // Confirm no traces are running because the 1 we added is not in a running state. 450 assertFalse(mProfilingService.areAnyTracesRunning()); 451 452 // Now run a cleanup of the non running session. 453 mProfilingService.cleanupActiveTracingSessions(); 454 455 // Confirm the non running session was cleaned up. 456 assertEquals(0, mProfilingService.mActiveTracingSessions.size()); 457 } 458 459 /** Test that request cancel trace does nothing if no trace is running. */ 460 @Test testRequestCancel_NotRunning()461 public void testRequestCancel_NotRunning() { 462 // Ensure no active tracing sessions tracked. 463 mProfilingService.mActiveTracingSessions.clear(); 464 assertFalse(mProfilingService.areAnyTracesRunning()); 465 466 // Register callback. 467 ProfilingResultCallback callback = new ProfilingResultCallback(); 468 mProfilingService.registerResultsCallback(false, callback); 469 470 // Request cancellation. 471 mProfilingService.requestCancel(KEY_MOST_SIG_BITS, KEY_LEAST_SIG_BITS); 472 473 // Confirm callback was not triggerd with a result because there was no trace to stop. 474 assertFalse(callback.mResultSent); 475 } 476 477 /** Test that rate limiter correctly persists and restores data. */ 478 @Test testRateLimiter_PersistAndRestore()479 public void testRateLimiter_PersistAndRestore() throws Exception { 480 overrideRateLimiterDefaults(); 481 482 // Remove all records 483 long currentTimeMillis = System.currentTimeMillis(); 484 mRateLimiter.mPastRunsHour.removeOlderThan(currentTimeMillis); 485 mRateLimiter.mPastRunsDay.removeOlderThan(currentTimeMillis); 486 mRateLimiter.mPastRunsWeek.removeOlderThan(currentTimeMillis); 487 488 // Add some records. Since records are being added directly rather than through normal 489 // request flow, this will not trigger a persist regardless of persist frequency. 490 mRateLimiter.mPastRunsHour.add(1, 1, currentTimeMillis - 1000); 491 mRateLimiter.mPastRunsDay.add(1, 1, currentTimeMillis - 1000); 492 mRateLimiter.mPastRunsWeek.add(1, 1, currentTimeMillis - 1000); 493 mRateLimiter.mPastRunsDay.add(2, 1, currentTimeMillis - (60 * 60 * 1000) - 1000); 494 mRateLimiter.mPastRunsWeek.add(2, 1, currentTimeMillis - (60 * 60 * 1000) - 1000); 495 mRateLimiter.mPastRunsWeek.add(2, 1, currentTimeMillis - (24 * 60 * 60 * 1000) - 1000); 496 497 // Store a copy of the backing data for each type 498 RateLimiter.CollectionEntry[] hourEntriesOriginal = 499 mRateLimiter.mPastRunsHour.getEntriesCopy(); 500 RateLimiter.CollectionEntry[] dayEntriesOriginal = 501 mRateLimiter.mPastRunsDay.getEntriesCopy(); 502 RateLimiter.CollectionEntry[] weekEntriesOriginal = 503 mRateLimiter.mPastRunsWeek.getEntriesCopy(); 504 505 // Confirm collections are correct size. 506 assertEquals(1, hourEntriesOriginal.length); 507 assertEquals(2, dayEntriesOriginal.length); 508 assertEquals(3, weekEntriesOriginal.length); 509 510 // Now persist the records to disk 511 mRateLimiter.persistToDisk(); 512 513 // Remove all records again 514 currentTimeMillis = System.currentTimeMillis(); 515 mRateLimiter.mPastRunsHour.removeOlderThan(currentTimeMillis); 516 mRateLimiter.mPastRunsDay.removeOlderThan(currentTimeMillis); 517 mRateLimiter.mPastRunsWeek.removeOlderThan(currentTimeMillis); 518 519 // Confirm records have been removed 520 assertEquals(0, mRateLimiter.mPastRunsHour.getEntriesCopy().length); 521 assertEquals(0, mRateLimiter.mPastRunsDay.getEntriesCopy().length); 522 assertEquals(0, mRateLimiter.mPastRunsWeek.getEntriesCopy().length); 523 524 // Now load the persisted records from disk using the overridden files we set up earlier. 525 mRateLimiter.setupFromPersistedData(); 526 527 // Finally, verify the records. 528 confirmRateLimiterEntriesEqual(hourEntriesOriginal, 529 mRateLimiter.mPastRunsHour.getEntriesCopy()); 530 confirmRateLimiterEntriesEqual(dayEntriesOriginal, 531 mRateLimiter.mPastRunsDay.getEntriesCopy()); 532 confirmRateLimiterEntriesEqual(weekEntriesOriginal, 533 mRateLimiter.mPastRunsWeek.getEntriesCopy()); 534 } 535 536 /** 537 * Test that rate limiter handles no persist file correctly. 538 * 539 * - Test setup ensures records are empty and that no file exists. 540 * - Rate limiter is expected to handle no file as a "profiling has never been used" state, 541 * resulting in the records remaining empty and data load being marked complete. 542 */ 543 @Test testRateLimiter_NoPersistFile()544 public void testRateLimiter_NoPersistFile() throws Exception { 545 overrideRateLimiterDefaults(); 546 547 // Ensure file doesn't exist 548 mRateLimiter.mPersistFile.delete(); 549 550 // Remove all records 551 long currentTimeMillis = System.currentTimeMillis(); 552 mRateLimiter.mPastRunsHour.removeOlderThan(currentTimeMillis); 553 mRateLimiter.mPastRunsDay.removeOlderThan(currentTimeMillis); 554 mRateLimiter.mPastRunsWeek.removeOlderThan(currentTimeMillis); 555 556 // Confirm records have been removed 557 assertEquals(0, mRateLimiter.mPastRunsHour.getEntriesCopy().length); 558 assertEquals(0, mRateLimiter.mPastRunsDay.getEntriesCopy().length); 559 assertEquals(0, mRateLimiter.mPastRunsWeek.getEntriesCopy().length); 560 561 // Now load the persisted records from disk using the overridden files we set up earlier. 562 mRateLimiter.setupFromPersistedData(); 563 564 // Confirm load is marked complete 565 assertTrue(mRateLimiter.mDataLoaded.get()); 566 567 // Confirm records are still empty 568 assertEquals(0, mRateLimiter.mPastRunsHour.getEntriesCopy().length); 569 assertEquals(0, mRateLimiter.mPastRunsDay.getEntriesCopy().length); 570 assertEquals(0, mRateLimiter.mPastRunsWeek.getEntriesCopy().length); 571 } 572 573 /** 574 * Test that rate limiter handles an empty persist file correctly. 575 * 576 * - Test setup ensures records are empty and that an empty file exists. 577 * - Rate limiter is expected to handle the empty file as a "profiling has never been used" 578 * state, resulting in the records remaining empty and data load being marked complete. 579 */ 580 @Test testRateLimiter_EmptyPersistFile()581 public void testRateLimiter_EmptyPersistFile() throws Exception { 582 overrideRateLimiterDefaults(); 583 584 // Ensure file exists and is empty 585 mRateLimiter.mPersistFile.delete(); 586 mRateLimiter.mPersistFile.createNewFile(); 587 assertTrue(mRateLimiter.mPersistFile.exists()); 588 589 // Remove all records 590 long currentTimeMillis = System.currentTimeMillis(); 591 mRateLimiter.mPastRunsHour.removeOlderThan(currentTimeMillis); 592 mRateLimiter.mPastRunsDay.removeOlderThan(currentTimeMillis); 593 mRateLimiter.mPastRunsWeek.removeOlderThan(currentTimeMillis); 594 595 // Confirm records have been removed 596 assertEquals(0, mRateLimiter.mPastRunsHour.getEntriesCopy().length); 597 assertEquals(0, mRateLimiter.mPastRunsDay.getEntriesCopy().length); 598 assertEquals(0, mRateLimiter.mPastRunsWeek.getEntriesCopy().length); 599 600 // Now load the persisted records from disk using the overridden files we set up earlier. 601 mRateLimiter.setupFromPersistedData(); 602 603 // Confirm load is marked complete 604 assertTrue(mRateLimiter.mDataLoaded.get()); 605 606 // Confirm records are still empty 607 assertEquals(0, mRateLimiter.mPastRunsHour.getEntriesCopy().length); 608 assertEquals(0, mRateLimiter.mPastRunsDay.getEntriesCopy().length); 609 assertEquals(0, mRateLimiter.mPastRunsWeek.getEntriesCopy().length); 610 } 611 612 /** 613 * Test that rate limiter handles a invalid persist file with remediation success correctly. 614 * 615 * - Test setup ensures records are empty, that a file with contents not of expected proto 616 * type exists, and that remediation succeeds. 617 * - Rate limiter is expected to handle the invalid file contents by attempting remediation and 618 * succeeding, resulting in stub records being added and data load being marked complete. 619 */ 620 @Test testRateLimiter_BadFile_RemediateSuccess()621 public void testRateLimiter_BadFile_RemediateSuccess() throws Exception { 622 overrideRateLimiterDefaults(); 623 624 // Ensure file exists and is written with data not matching proto expectation 625 mRateLimiter.mPersistFile.delete(); 626 mRateLimiter.mPersistFile.createNewFile(); 627 FileOutputStream fileOutputStream = new FileOutputStream(mRateLimiter.mPersistFile); 628 fileOutputStream.write("some text that is definitely not a proto".getBytes()); 629 fileOutputStream.close(); 630 631 // Remove all records 632 long currentTimeMillis = System.currentTimeMillis(); 633 mRateLimiter.mPastRunsHour.removeOlderThan(currentTimeMillis); 634 mRateLimiter.mPastRunsDay.removeOlderThan(currentTimeMillis); 635 mRateLimiter.mPastRunsWeek.removeOlderThan(currentTimeMillis); 636 637 // Confirm records have been removed 638 assertEquals(0, mRateLimiter.mPastRunsHour.getEntriesCopy().length); 639 assertEquals(0, mRateLimiter.mPastRunsDay.getEntriesCopy().length); 640 assertEquals(0, mRateLimiter.mPastRunsWeek.getEntriesCopy().length); 641 642 // Now load the persisted records from disk using the overridden files we set up earlier. 643 mRateLimiter.setupFromPersistedData(); 644 645 // Confirm load is marked complete 646 assertTrue(mRateLimiter.mDataLoaded.get()); 647 648 // Confirm fake records have been added 649 assertEquals(1, mRateLimiter.mPastRunsHour.getEntriesCopy().length); 650 assertEquals(Integer.MAX_VALUE, mRateLimiter.mPastRunsHour.getEntriesCopy()[0].mCost); 651 assertEquals(1, mRateLimiter.mPastRunsDay.getEntriesCopy().length); 652 assertEquals(Integer.MAX_VALUE, mRateLimiter.mPastRunsDay.getEntriesCopy()[0].mCost); 653 assertEquals(1, mRateLimiter.mPastRunsWeek.getEntriesCopy().length); 654 assertEquals(Integer.MAX_VALUE, mRateLimiter.mPastRunsWeek.getEntriesCopy()[0].mCost); 655 } 656 657 /** 658 * Test that rate limiter handles a invalid persist file with remediation failure correctly. 659 * 660 * - Test setup ensures records are empty, that a file with contents not of expected proto 661 * type exists, and that remediation fails. 662 * - Rate limiter is expected to handle the invalid file contents by attempting remediation and 663 * failing, resulting in records remaining empty and data load being marked incomplete. 664 */ 665 @Test testRateLimiter_BadFile_RemediateFailure()666 public void testRateLimiter_BadFile_RemediateFailure() throws Exception { 667 overrideRateLimiterDefaults(); 668 669 // Mock failure of handleBadFile. 670 doReturn(false).when(mRateLimiter).handleBadFile(); 671 672 // Ensure file exists and is written with data not matching proto expectation 673 mRateLimiter.mPersistFile.delete(); 674 mRateLimiter.mPersistFile.createNewFile(); 675 FileOutputStream fileOutputStream = new FileOutputStream(mRateLimiter.mPersistFile); 676 fileOutputStream.write("some text that is definitely not a proto".getBytes()); 677 fileOutputStream.close(); 678 679 // Remove all records 680 long currentTimeMillis = System.currentTimeMillis(); 681 mRateLimiter.mPastRunsHour.removeOlderThan(currentTimeMillis); 682 mRateLimiter.mPastRunsDay.removeOlderThan(currentTimeMillis); 683 mRateLimiter.mPastRunsWeek.removeOlderThan(currentTimeMillis); 684 685 // Confirm records have been removed 686 assertEquals(0, mRateLimiter.mPastRunsHour.getEntriesCopy().length); 687 assertEquals(0, mRateLimiter.mPastRunsDay.getEntriesCopy().length); 688 assertEquals(0, mRateLimiter.mPastRunsWeek.getEntriesCopy().length); 689 690 // Now load the persisted records from disk using the overridden files we set up earlier. 691 mRateLimiter.setupFromPersistedData(); 692 693 // Confirm load is marked incomplete 694 assertFalse(mRateLimiter.mDataLoaded.get()); 695 696 // Confirm records are still empty 697 assertEquals(0, mRateLimiter.mPastRunsHour.getEntriesCopy().length); 698 assertEquals(0, mRateLimiter.mPastRunsDay.getEntriesCopy().length); 699 assertEquals(0, mRateLimiter.mPastRunsWeek.getEntriesCopy().length); 700 } 701 702 /** Test that rate limiter check for request allows as expected. */ 703 @Test testRateLimiter_RequestAllow()704 public void testRateLimiter_RequestAllow() throws Exception { 705 overrideRateLimiterDefaults(); 706 707 // Send a request for profiling. 708 int result = mRateLimiter.isProfilingRequestAllowed( 709 FAKE_UID, ProfilingManager.PROFILING_TYPE_HEAP_PROFILE, false, null); 710 711 // Confirm request passes as allowed. 712 assertEquals(RateLimiter.RATE_LIMIT_RESULT_ALLOWED, result); 713 } 714 715 /** Test that rate limiter check for request denies for process hour limit as expected. */ 716 @Test testRateLimiter_RequestDeny_ProcessHour()717 public void testRateLimiter_RequestDeny_ProcessHour() throws Exception { 718 overrideRateLimiterDefaults(); 719 720 // Add a fake run to the same UID with a cost value equal to the process limit but lower 721 // than system limit for this time bucket so that it passes system but fails process. 722 mRateLimiter.mPastRunsHour.add(FAKE_UID, DEFAULT_LIMIT_PROCESS_HOUR, 723 System.currentTimeMillis()); 724 725 // Send a request for profiling. 726 int result = mRateLimiter.isProfilingRequestAllowed( 727 FAKE_UID, ProfilingManager.PROFILING_TYPE_HEAP_PROFILE, false, null); 728 729 // Confirm request is denied with process reason. 730 assertEquals(RateLimiter.RATE_LIMIT_RESULT_BLOCKED_PROCESS, result); 731 } 732 733 /** Test that rate limiter check for request denies for process day limit as expected. */ 734 @Test testRateLimiter_RequestDeny_ProcessDay()735 public void testRateLimiter_RequestDeny_ProcessDay() throws Exception { 736 overrideRateLimiterDefaults(); 737 738 // Add a fake run to the same UID with a cost value equal to the process limit but lower 739 // than system limit for this time bucket so that it passes system but fails process. 740 mRateLimiter.mPastRunsDay.add(FAKE_UID, DEFAULT_LIMIT_PROCESS_DAY, 741 System.currentTimeMillis()); 742 743 // Send a request for profiling. 744 int result = mRateLimiter.isProfilingRequestAllowed( 745 FAKE_UID, ProfilingManager.PROFILING_TYPE_HEAP_PROFILE, false, null); 746 747 // Confirm request is denied with process reason. 748 assertEquals(RateLimiter.RATE_LIMIT_RESULT_BLOCKED_PROCESS, result); 749 } 750 751 /** Test that rate limiter check for request denies for process week limit as expected. */ 752 @Test testRateLimiter_RequestDeny_ProcessWeek()753 public void testRateLimiter_RequestDeny_ProcessWeek() throws Exception { 754 overrideRateLimiterDefaults(); 755 756 // Add a fake run to the same UID with a cost value equal to the process limit but lower 757 // than system limit for this time bucket so that it passes system but fails process. 758 mRateLimiter.mPastRunsWeek.add(FAKE_UID, DEFAULT_LIMIT_PROCESS_WEEK, 759 System.currentTimeMillis()); 760 761 // Send a request for profiling. 762 int result = mRateLimiter.isProfilingRequestAllowed( 763 FAKE_UID, ProfilingManager.PROFILING_TYPE_HEAP_PROFILE, false, null); 764 765 // Confirm request is denied with process reason. 766 assertEquals(RateLimiter.RATE_LIMIT_RESULT_BLOCKED_PROCESS, result); 767 } 768 769 /** Test that rate limiter check for request denies for system hour limit as expected. */ 770 @Test testRateLimiter_RequestDeny_SystemHour()771 public void testRateLimiter_RequestDeny_SystemHour() throws Exception { 772 overrideRateLimiterDefaults(); 773 774 // Add a fake run to a different UID than will be used for the request, with a cost value 775 // equal to the system limit for this time bucket. 776 mRateLimiter.mPastRunsHour.add(FAKE_UID_2, DEFAULT_LIMIT_SYSTEM_HOUR, 777 System.currentTimeMillis()); 778 779 // Send a request for profiling. 780 int result = mRateLimiter.isProfilingRequestAllowed( 781 FAKE_UID, ProfilingManager.PROFILING_TYPE_HEAP_PROFILE, false, null); 782 783 // Confirm request is denied with system reason. 784 assertEquals(RateLimiter.RATE_LIMIT_RESULT_BLOCKED_SYSTEM, result); 785 } 786 787 /** Test that rate limiter check for request denies for system day limit as expected. */ 788 @Test testRateLimiter_RequestDeny_SystemDay()789 public void testRateLimiter_RequestDeny_SystemDay() throws Exception { 790 overrideRateLimiterDefaults(); 791 792 // Add a fake run to a different UID than will be used for the request, with a cost value 793 // equal to the system limit for this time bucket. 794 mRateLimiter.mPastRunsDay.add(FAKE_UID_2, DEFAULT_LIMIT_SYSTEM_DAY, 795 System.currentTimeMillis()); 796 797 // Send a request for profiling. 798 int result = mRateLimiter.isProfilingRequestAllowed( 799 FAKE_UID, ProfilingManager.PROFILING_TYPE_HEAP_PROFILE, false, null); 800 801 // Confirm request is denied with system reason. 802 assertEquals(RateLimiter.RATE_LIMIT_RESULT_BLOCKED_SYSTEM, result); 803 } 804 805 /** Test that rate limiter check for request denies for system week limit as expected. */ 806 @Test testRateLimiter_RequestDeny_SystemWeek()807 public void testRateLimiter_RequestDeny_SystemWeek() throws Exception { 808 overrideRateLimiterDefaults(); 809 810 // Add a fake run to a different UID than will be used for the request, with a cost value 811 // equal to the system limit for this time bucket. 812 mRateLimiter.mPastRunsWeek.add(FAKE_UID_2, DEFAULT_LIMIT_SYSTEM_WEEK, 813 System.currentTimeMillis()); 814 815 // Send a request for profiling. 816 int result = mRateLimiter.isProfilingRequestAllowed( 817 FAKE_UID, ProfilingManager.PROFILING_TYPE_HEAP_PROFILE, false, null); 818 819 // Confirm request is denied with system reason. 820 assertEquals(RateLimiter.RATE_LIMIT_RESULT_BLOCKED_SYSTEM, result); 821 } 822 823 /** Test that rate limiter check for trigger allows as expected. */ 824 @Test testRateLimiter_TriggeredAllow()825 public void testRateLimiter_TriggeredAllow() throws Exception { 826 overrideRateLimiterDefaults(); 827 828 // Send a request for a trigger. 829 int result = mRateLimiter.isProfilingRequestAllowed(FAKE_UID, 830 ProfilingManager.PROFILING_TYPE_SYSTEM_TRACE, true, null); 831 832 // Confirm request passes as allowed. 833 assertEquals(RateLimiter.RATE_LIMIT_RESULT_ALLOWED, result); 834 } 835 836 /** Test that rate limiter check for trigger denies when expected. */ 837 @Test testRateLimiter_TriggerDeny()838 public void testRateLimiter_TriggerDeny() throws Exception { 839 overrideRateLimiterDefaults(); 840 841 // Add a fake run with a high cost value. 842 mRateLimiter.mPastRunsHour.add(FAKE_UID, 1000, System.currentTimeMillis()); 843 844 // Send a request for a trigger. 845 int result = mRateLimiter.isProfilingRequestAllowed( 846 FAKE_UID, ProfilingManager.PROFILING_TYPE_SYSTEM_TRACE, true, null); 847 848 // Confirm request does not pass as allowed. 849 assertNotEquals(RateLimiter.RATE_LIMIT_RESULT_ALLOWED, result); 850 } 851 852 /** Test that advancing state in forward direction works as expected. */ 853 @Test testSessionState_AdvanceForwardSucceeds()854 public void testSessionState_AdvanceForwardSucceeds() { 855 // Create a session with some state. 856 TracingSession session = new TracingSession( 857 ProfilingManager.PROFILING_TYPE_HEAP_PROFILE, 858 new Bundle(), 859 FAKE_UID, 860 APP_PACKAGE_NAME, 861 REQUEST_TAG, 862 KEY_LEAST_SIG_BITS, 863 KEY_MOST_SIG_BITS, 864 TRIGGER_TYPE_NONE); 865 session.setState(TracingState.PROFILING_FINISHED); 866 867 // Trigger an advance to a subsequent state. 868 mProfilingService.advanceTracingSession(session, TracingState.ERROR_OCCURRED); 869 870 // Ensure it does try to advance. 871 verify(mProfilingService, times(1)).processTracingSessionResultCallback(any(), eq(true)); 872 } 873 874 /** Test that advancing state in backwards direction does not work. */ 875 @Test testSessionState_AdvanceBackwardsFails()876 public void testSessionState_AdvanceBackwardsFails() { 877 // Override cleanupTracingSession to do nothing or it will call through to 878 // advanceTracingSession after cleanup and we need to confirm that advanceTracingSession is 879 // not immediately called. 880 doNothing().when(mProfilingService).cleanupTracingSession(any()); 881 882 // Create a session with some state. 883 TracingSession session = new TracingSession( 884 ProfilingManager.PROFILING_TYPE_HEAP_PROFILE, 885 new Bundle(), 886 FAKE_UID, 887 APP_PACKAGE_NAME, 888 REQUEST_TAG, 889 KEY_LEAST_SIG_BITS, 890 KEY_MOST_SIG_BITS, 891 TRIGGER_TYPE_NONE); 892 session.setState(TracingState.APPROVED); 893 894 // Attempt to advance to earlier state. 895 mProfilingService.advanceTracingSession(session, TracingState.REQUESTED); 896 897 // Ensure service determines something is broken, does not try to advance, and triggers a 898 // cleanup. 899 verify(mProfilingService, times(1)).cleanupTracingSession(any()); 900 } 901 902 /** 903 * Test that advancing state with a null new state and no retries (i.e. not from queue retry) 904 * does not work. */ 905 @Test testSessionState_AdvanceNullFails()906 public void testSessionState_AdvanceNullFails() { 907 // Override cleanupTracingSession to do nothing or it will call through to 908 // advanceTracingSession after cleanup and we need to confirm that advanceTracingSession is 909 // not immediately called. 910 doNothing().when(mProfilingService).cleanupTracingSession(any()); 911 912 // Create a session with some state. 913 TracingSession session = new TracingSession( 914 ProfilingManager.PROFILING_TYPE_HEAP_PROFILE, 915 new Bundle(), 916 FAKE_UID, 917 APP_PACKAGE_NAME, 918 REQUEST_TAG, 919 KEY_LEAST_SIG_BITS, 920 KEY_MOST_SIG_BITS, 921 TRIGGER_TYPE_NONE); 922 session.setState(TracingState.REQUESTED); 923 924 // Make sure retry count is 0 (default value). 925 assertEquals(0, session.getRetryCount()); 926 927 // Attempt to advance with null new state. 928 mProfilingService.advanceTracingSession(session, null); 929 930 // Ensure service determines something is broken, does not try to advance, and triggers a 931 // cleanup. 932 verify(mProfilingService, times(1)).cleanupTracingSession(any()); 933 } 934 935 /** 936 * Test that persisting the queue and then reloading it from disk works correctly, loading the 937 * previous queue and all persistable fields. 938 */ 939 @Test 940 @EnableFlags(android.os.profiling.Flags.FLAG_PERSIST_QUEUE) testQueuePersist_PersistAndRestore()941 public void testQueuePersist_PersistAndRestore() { 942 // Clear the queue. 943 mProfilingService.mQueuedTracingResults.clear(); 944 945 // A 2nd fake uid so we can have records belonging to multiple uids. 946 int fakeUid2 = FAKE_UID + 1; 947 948 // Create 3 fake sessions with various fields set on each. 949 TracingSession session1 = new TracingSession( 950 ProfilingManager.PROFILING_TYPE_HEAP_PROFILE, 951 new Bundle(), 952 FAKE_UID, 953 APP_PACKAGE_NAME, 954 REQUEST_TAG, 955 KEY_LEAST_SIG_BITS, 956 KEY_MOST_SIG_BITS, 957 TRIGGER_TYPE_NONE); 958 session1.setProfilingStartTimeMs(System.currentTimeMillis()); 959 session1.setState(TracingState.PROFILING_FINISHED); 960 961 TracingSession session2 = new TracingSession( 962 ProfilingManager.PROFILING_TYPE_JAVA_HEAP_DUMP, 963 new Bundle(), 964 FAKE_UID, 965 APP_PACKAGE_NAME, 966 REQUEST_TAG, 967 KEY_LEAST_SIG_BITS, 968 KEY_MOST_SIG_BITS, 969 TRIGGER_TYPE_NONE); 970 session2.setProfilingStartTimeMs(System.currentTimeMillis()); 971 session2.setState(TracingState.ERROR_OCCURRED); 972 session2.setError(ProfilingResult.ERROR_FAILED_POST_PROCESSING, "some error message"); 973 974 TracingSession session3 = new TracingSession( 975 ProfilingManager.PROFILING_TYPE_SYSTEM_TRACE, 976 new Bundle(), 977 fakeUid2, 978 APP_PACKAGE_NAME, 979 REQUEST_TAG, 980 KEY_LEAST_SIG_BITS, 981 KEY_MOST_SIG_BITS, 982 TRIGGER_TYPE_NONE); 983 session3.setProfilingStartTimeMs(System.currentTimeMillis()); 984 session3.setState(TracingState.REDACTED); 985 986 // Create 2 session lists. 987 List<TracingSession> sessionListUid1 = new ArrayList<TracingSession>(); 988 List<TracingSession> sessionListUid2 = new ArrayList<TracingSession>(); 989 990 // Add 2 sessions to the first list and 1 to the second list. 991 sessionListUid1.add(session1); 992 sessionListUid1.add(session2); 993 sessionListUid2.add(session3); 994 995 // Add each of the lists to the queue. 996 mProfilingService.mQueuedTracingResults.put(FAKE_UID, sessionListUid1); 997 mProfilingService.mQueuedTracingResults.put(fakeUid2, sessionListUid2); 998 999 // Trigger a persist. 1000 mProfilingService.persistQueueToDisk(); 1001 1002 // Confirm file was written to 1003 confirmNonEmptyFileExists(mProfilingService.mPersistQueueFile); 1004 1005 // Clear the queue so we can ensure it is reloaded properly. 1006 mProfilingService.mQueuedTracingResults.clear(); 1007 assertEquals(0, mProfilingService.mQueuedTracingResults.size()); 1008 1009 // Load the queue from disk. 1010 mProfilingService.loadQueueFromPersistedData(); 1011 1012 // Finally, verify the loaded contents match the ones that were persisted. 1013 // First check that the queue contains 2 lists, as added above. 1014 assertEquals(2, mProfilingService.mQueuedTracingResults.size()); 1015 1016 // Now, confirm that there are 2 queued results belonging to the first uid, and 1 belonging 1017 // to the 2nd uid, as defined above. 1018 assertEquals(2, mProfilingService.mQueuedTracingResults.get(FAKE_UID).size()); 1019 assertEquals(1, mProfilingService.mQueuedTracingResults.get(fakeUid2).size()); 1020 1021 // Lastly, check that each loaded session is equal its persisted counterpart. 1022 confirmTracingSessionsEqual(session1, 1023 mProfilingService.mQueuedTracingResults.get(FAKE_UID).get(0)); 1024 confirmTracingSessionsEqual(session2, 1025 mProfilingService.mQueuedTracingResults.get(FAKE_UID).get(1)); 1026 confirmTracingSessionsEqual(session3, 1027 mProfilingService.mQueuedTracingResults.get(fakeUid2).get(0)); 1028 } 1029 1030 /** 1031 * Test that loading queue with no persist file works as intended with no records added and 1032 * correct methods called. 1033 */ 1034 @Test 1035 @EnableFlags(android.os.profiling.Flags.FLAG_PERSIST_QUEUE) testQueuePersist_NoPersistFile()1036 public void testQueuePersist_NoPersistFile() { 1037 // Clear the queue. 1038 mProfilingService.mQueuedTracingResults.clear(); 1039 1040 // Ensure the file doesn't exist. 1041 mProfilingService.mPersistQueueFile.delete(); 1042 assertFalse(mProfilingService.mPersistQueueFile.exists()); 1043 1044 // Load the queue from disk. 1045 mProfilingService.loadQueueFromPersistedData(); 1046 1047 // Ensure queue still empty. 1048 assertEquals(0, mProfilingService.mQueuedTracingResults.size()); 1049 verify(mProfilingService, times(0)).deletePersistQueueFile(); 1050 } 1051 1052 /** 1053 * Test that loading queue with an empty persist file works as intended with no records added 1054 * and correct methods called. 1055 */ 1056 @Test 1057 @EnableFlags(android.os.profiling.Flags.FLAG_PERSIST_QUEUE) testQueuePersist_EmptyPersistFile()1058 public void testQueuePersist_EmptyPersistFile() throws Exception { 1059 // Clear the queue. 1060 mProfilingService.mQueuedTracingResults.clear(); 1061 1062 // Ensure the file exists and is empty. 1063 mProfilingService.mPersistQueueFile.delete(); 1064 assertFalse(mProfilingService.mPersistQueueFile.exists()); 1065 mProfilingService.mPersistQueueFile.createNewFile(); 1066 assertTrue(mProfilingService.mPersistQueueFile.exists()); 1067 assertEquals(0L, mProfilingService.mPersistQueueFile.length()); 1068 1069 // Load the queue from disk. 1070 mProfilingService.loadQueueFromPersistedData(); 1071 1072 // Ensure that the queue is still empty and that a delete was attempted as expected for the 1073 // bad file state. 1074 assertEquals(0, mProfilingService.mQueuedTracingResults.size()); 1075 verify(mProfilingService, times(1)).deletePersistQueueFile(); 1076 } 1077 1078 /** 1079 * Test that loading queue with a invalid persist file works as intended with no records added 1080 * and correct methods called. 1081 */ 1082 @Test 1083 @EnableFlags(android.os.profiling.Flags.FLAG_PERSIST_QUEUE) testQueuePersist_BadPersistFile()1084 public void testQueuePersist_BadPersistFile() throws Exception { 1085 // Clear the queue. 1086 mProfilingService.mQueuedTracingResults.clear(); 1087 1088 // Ensure the file exists and is empty. 1089 mProfilingService.mPersistQueueFile.delete(); 1090 mProfilingService.mPersistQueueFile.createNewFile(); 1091 FileOutputStream fileOutputStream = new FileOutputStream( 1092 mProfilingService.mPersistQueueFile); 1093 fileOutputStream.write("some text that is definitely not a proto".getBytes()); 1094 fileOutputStream.close(); 1095 confirmNonEmptyFileExists(mProfilingService.mPersistQueueFile); 1096 1097 // Load the queue from disk. 1098 mProfilingService.loadQueueFromPersistedData(); 1099 1100 // Ensure that the queue is still empty and that a delete was attempted as expected for the 1101 // bad file state. 1102 assertEquals(0, mProfilingService.mQueuedTracingResults.size()); 1103 verify(mProfilingService, times(1)).deletePersistQueueFile(); 1104 } 1105 1106 /** 1107 * Test that persisting queue respects the frequency defined, allowing the persist on the first 1108 * instance but rejecting the subsequent persist. 1109 * 1110 * While this test focuses on queue persist, the logic for respect frequency is shared with 1111 * triggers so this test covers both. 1112 */ 1113 @Test 1114 @EnableFlags(android.os.profiling.Flags.FLAG_PERSIST_QUEUE) testQueuePersist_RespectFrequency()1115 public void testQueuePersist_RespectFrequency() throws Exception { 1116 // Override persist frequency to something large. 1117 updateDeviceConfigAndWaitForChange(DeviceConfigHelper.NAMESPACE, 1118 DeviceConfigHelper.PERSIST_TO_DISK_FREQUENCY_MS, 60 * 60 * 1000); 1119 1120 // Clear the queue. 1121 mProfilingService.mQueuedTracingResults.clear(); 1122 1123 // Populate the queue. 1124 List<TracingSession> sessionList = new ArrayList<TracingSession>(); 1125 TracingSession session1 = new TracingSession( 1126 ProfilingManager.PROFILING_TYPE_HEAP_PROFILE, 1127 new Bundle(), 1128 FAKE_UID, 1129 APP_PACKAGE_NAME, 1130 REQUEST_TAG, 1131 KEY_LEAST_SIG_BITS, 1132 KEY_MOST_SIG_BITS, 1133 TRIGGER_TYPE_NONE); 1134 session1.setProfilingStartTimeMs(System.currentTimeMillis()); 1135 session1.setState(TracingState.PROFILING_FINISHED); 1136 TracingSession session2 = new TracingSession( 1137 ProfilingManager.PROFILING_TYPE_JAVA_HEAP_DUMP, 1138 new Bundle(), 1139 FAKE_UID, 1140 APP_PACKAGE_NAME, 1141 REQUEST_TAG, 1142 KEY_LEAST_SIG_BITS, 1143 KEY_MOST_SIG_BITS, 1144 TRIGGER_TYPE_NONE); 1145 session2.setProfilingStartTimeMs(System.currentTimeMillis()); 1146 session2.setState(TracingState.ERROR_OCCURRED); 1147 session2.setError(ProfilingResult.ERROR_FAILED_POST_PROCESSING, "some error message"); 1148 1149 sessionList.add(session1); 1150 sessionList.add(session2); 1151 mProfilingService.mQueuedTracingResults.put(FAKE_UID, sessionList); 1152 1153 // Trigger a persist. 1154 mProfilingService.maybePersistToDisk(); 1155 1156 // Confirm that it actually persisted. 1157 verify(mProfilingService, times(1)).persistQueueToDisk(); 1158 assertTrue(mProfilingService.mPersistQueueFile.exists()); 1159 1160 // Delete the file so we can confirm the next call does nothing. 1161 assertTrue(mProfilingService.mPersistQueueFile.delete()); 1162 1163 // Finally, trigger another persist. 1164 mProfilingService.maybePersistToDisk(); 1165 1166 // And confirm the persist did not immediately run. 1167 assertFalse(mProfilingService.mPersistQueueFile.exists()); 1168 // Verify with same value as earlier so we know it didn't get triggered again. 1169 verify(mProfilingService, times(1)).persistQueueToDisk(); 1170 } 1171 1172 /** 1173 * Test that persists that are scheduled for the future due to a persist having recently 1174 * occurred, occur at a future time as expected. 1175 * 1176 * While this test focuses on queue persist, the logic for scheduling is shared with triggers so 1177 * this test covers both. 1178 */ 1179 @Test 1180 @EnableFlags(android.os.profiling.Flags.FLAG_PERSIST_QUEUE) testQueuePersist_Scheduling()1181 public void testQueuePersist_Scheduling() throws Exception { 1182 // Override persist frequency to 5 seconds that way we can confirm both that the persist did 1183 // not happen immediately and that it did eventually happen. This is the time from the first 1184 // call to maybePersistToDisk until the next call to the same method for the scheduling 1185 // of the next persist to occur as expected, rather than immediately persisting. 1186 updateDeviceConfigAndWaitForChange(DeviceConfigHelper.NAMESPACE, 1187 DeviceConfigHelper.PERSIST_TO_DISK_FREQUENCY_MS, 5 * 1000); 1188 1189 // Clear the queue. 1190 mProfilingService.mQueuedTracingResults.clear(); 1191 1192 // Populate the queue. 1193 TracingSession session = new TracingSession( 1194 ProfilingManager.PROFILING_TYPE_HEAP_PROFILE, 1195 new Bundle(), 1196 FAKE_UID, 1197 APP_PACKAGE_NAME, 1198 REQUEST_TAG, 1199 KEY_LEAST_SIG_BITS, 1200 KEY_MOST_SIG_BITS, 1201 TRIGGER_TYPE_NONE); 1202 session.setProfilingStartTimeMs(System.currentTimeMillis()); 1203 session.setState(TracingState.PROFILING_FINISHED); 1204 1205 List<TracingSession> sessionList = new ArrayList<TracingSession>(); 1206 sessionList.add(session); 1207 mProfilingService.mQueuedTracingResults.put(FAKE_UID, sessionList); 1208 1209 // Trigger a persist. 1210 mProfilingService.maybePersistToDisk(); 1211 1212 // Confirm that it actually persisted. 1213 assertTrue(mProfilingService.mPersistQueueFile.exists()); 1214 1215 // Delete the file so that we can later use its existence to confirm whether the next 1216 // persist occurred. 1217 assertTrue(mProfilingService.mPersistQueueFile.delete()); 1218 1219 // Trigger another persist. 1220 mProfilingService.maybePersistToDisk(); 1221 1222 // And confirm the persist did not immediately run. 1223 assertFalse(mProfilingService.mPersistQueueFile.exists()); 1224 1225 // Wait 1 second longer than the configured delay to be sure the persist had time to finish. 1226 sleep(6 * 1000); 1227 1228 // Finally, confirm that the file now exists. 1229 assertTrue(mProfilingService.mPersistQueueFile.exists()); 1230 } 1231 1232 /** 1233 * Test that persisting app triggers and then reloading them from disk works correctly, loading 1234 * all previous triggers. 1235 */ 1236 @Test 1237 @EnableFlags(android.os.profiling.Flags.FLAG_SYSTEM_TRIGGERED_PROFILING_NEW) testAppTriggersPersist_PersistAndRestore()1238 public void testAppTriggersPersist_PersistAndRestore() { 1239 // First, clear the data structure. 1240 mProfilingService.mAppTriggers.getMap().clear(); 1241 1242 // Create 3 triggers belonging to 2 uids. Add a last triggered time to one of them. 1243 ProfilingTriggerData trigger1 = new ProfilingTriggerData(FAKE_UID, APP_PACKAGE_NAME, 1244 ProfilingTrigger.TRIGGER_TYPE_APP_FULLY_DRAWN, 0); 1245 1246 ProfilingTriggerData trigger2 = new ProfilingTriggerData(FAKE_UID, APP_PACKAGE_NAME, 1247 ProfilingTrigger.TRIGGER_TYPE_ANR, 1); 1248 trigger2.setLastTriggeredTimeMs(123L); 1249 1250 ProfilingTriggerData trigger3 = new ProfilingTriggerData(FAKE_UID_2, APP_PACKAGE_NAME, 1251 ProfilingTrigger.TRIGGER_TYPE_APP_FULLY_DRAWN, 2); 1252 1253 // Group into sparse arrays by uid. 1254 SparseArray<ProfilingTriggerData> triggerArray1 = new SparseArray<ProfilingTriggerData>(); 1255 triggerArray1.put(ProfilingTrigger.TRIGGER_TYPE_APP_FULLY_DRAWN, trigger1); 1256 triggerArray1.put(ProfilingTrigger.TRIGGER_TYPE_ANR, trigger2); 1257 1258 SparseArray<ProfilingTriggerData> triggerArray2 = new SparseArray<ProfilingTriggerData>(); 1259 triggerArray2.put(ProfilingTrigger.TRIGGER_TYPE_APP_FULLY_DRAWN, trigger3); 1260 1261 mProfilingService.mAppTriggers.put(APP_PACKAGE_NAME, FAKE_UID, triggerArray1); 1262 mProfilingService.mAppTriggers.put(APP_PACKAGE_NAME, FAKE_UID_2, triggerArray2); 1263 1264 // Trigger a persist. 1265 mProfilingService.persistAppTriggersToDisk(); 1266 1267 // Confirm file was written to 1268 confirmNonEmptyFileExists(mProfilingService.mPersistAppTriggersFile); 1269 1270 // Clear app triggers so we can ensure it is reloaded properly. 1271 mProfilingService.mAppTriggers.getMap().clear(); 1272 assertEquals(0, mProfilingService.mAppTriggers.getMap().size()); 1273 1274 // Load app triggers from disk. 1275 mProfilingService.loadAppTriggersFromPersistedData(); 1276 1277 // Finally, verify the loaded contents match the ones that were persisted. 1278 confirmProfilingTriggerEquals(trigger1, 1279 mProfilingService.mAppTriggers.get(APP_PACKAGE_NAME, FAKE_UID) 1280 .get(ProfilingTrigger.TRIGGER_TYPE_APP_FULLY_DRAWN)); 1281 confirmProfilingTriggerEquals(trigger2, 1282 mProfilingService.mAppTriggers.get(APP_PACKAGE_NAME, FAKE_UID) 1283 .get(ProfilingTrigger.TRIGGER_TYPE_ANR)); 1284 confirmProfilingTriggerEquals(trigger3, 1285 mProfilingService.mAppTriggers.get(APP_PACKAGE_NAME, FAKE_UID_2) 1286 .get(ProfilingTrigger.TRIGGER_TYPE_APP_FULLY_DRAWN)); 1287 } 1288 1289 /** 1290 * Test that loading app triggers with no persist file works as intended with no triggers added, 1291 * loaded set to true, and correct methods called. 1292 */ 1293 @Test 1294 @EnableFlags(android.os.profiling.Flags.FLAG_SYSTEM_TRIGGERED_PROFILING_NEW) testAppTriggersPersist_NoPersistFile()1295 public void testAppTriggersPersist_NoPersistFile() { 1296 // First, clear the data structure. 1297 mProfilingService.mAppTriggers.getMap().clear(); 1298 1299 // Ensure the file doesn't exist. 1300 mProfilingService.mPersistAppTriggersFile.delete(); 1301 assertFalse(mProfilingService.mPersistAppTriggersFile.exists()); 1302 1303 // Load app triggers from disk. 1304 mProfilingService.loadAppTriggersFromPersistedData(); 1305 1306 // Ensure that the triggers are still empty, that loaded was set to true, and that a delete 1307 // was not attempted as there was no file to delete. 1308 assertEquals(0, mProfilingService.mAppTriggers.getMap().size()); 1309 assertTrue(mProfilingService.mAppTriggersLoaded); 1310 verify(mProfilingService, times(0)).deletePersistAppTriggersFile(); 1311 } 1312 1313 /** 1314 * Test that loading app triggers with an empty persist file works as intended with no triggers 1315 * added, loaded set to true, and correct methods called. 1316 */ 1317 @Test 1318 @EnableFlags(android.os.profiling.Flags.FLAG_PERSIST_QUEUE) testAppTriggersPersist_EmptyPersistFile()1319 public void testAppTriggersPersist_EmptyPersistFile() throws Exception { 1320 // First, clear the data structure. 1321 mProfilingService.mAppTriggers.getMap().clear(); 1322 1323 // Ensure the file exists and is empty. 1324 mProfilingService.mPersistAppTriggersFile.delete(); 1325 assertFalse(mProfilingService.mPersistAppTriggersFile.exists()); 1326 mProfilingService.mPersistAppTriggersFile.createNewFile(); 1327 assertTrue(mProfilingService.mPersistAppTriggersFile.exists()); 1328 assertEquals(0L, mProfilingService.mPersistAppTriggersFile.length()); 1329 1330 // Load app triggers from disk. 1331 mProfilingService.loadAppTriggersFromPersistedData(); 1332 1333 // Ensure that the triggers are still empty, that loaded was set to true, and that a delete 1334 // was attempted as expected for the bad file state. 1335 assertEquals(0, mProfilingService.mAppTriggers.getMap().size()); 1336 assertTrue(mProfilingService.mAppTriggersLoaded); 1337 verify(mProfilingService, times(1)).deletePersistAppTriggersFile(); 1338 } 1339 1340 /** 1341 * Test that loading app triggers with an invalid persist file works as intended with no 1342 * triggers added, loaded set to true, and correct methods called. 1343 */ 1344 @Test 1345 @EnableFlags(android.os.profiling.Flags.FLAG_PERSIST_QUEUE) testAppTriggersPersist_BadPersistFile()1346 public void testAppTriggersPersist_BadPersistFile() throws Exception { 1347 // First, clear the data structure. 1348 mProfilingService.mAppTriggers.getMap().clear(); 1349 1350 // Ensure the file exists and contains some non proto contents. 1351 mProfilingService.mPersistAppTriggersFile.delete(); 1352 mProfilingService.mPersistAppTriggersFile.createNewFile(); 1353 FileOutputStream fileOutputStream = new FileOutputStream( 1354 mProfilingService.mPersistAppTriggersFile); 1355 fileOutputStream.write("some text that is definitely not a proto".getBytes()); 1356 fileOutputStream.close(); 1357 confirmNonEmptyFileExists(mProfilingService.mPersistAppTriggersFile); 1358 1359 // Load app triggers from disk. 1360 mProfilingService.loadAppTriggersFromPersistedData(); 1361 1362 // Ensure that the triggers are still empty, that loaded was set to true, and that a delete 1363 // was attempted as expected for the bad file state. 1364 assertEquals(0, mProfilingService.mAppTriggers.getMap().size()); 1365 assertTrue(mProfilingService.mAppTriggersLoaded); 1366 verify(mProfilingService, times(1)).deletePersistAppTriggersFile(); 1367 } 1368 1369 /** Test that adding a specific listener does not trigger handling queued results. */ 1370 @Test testQueuedResult_RequestSpecificListener()1371 public void testQueuedResult_RequestSpecificListener() { 1372 // Add a request specific callback. 1373 ProfilingResultCallback callback = new ProfilingResultCallback(); 1374 mProfilingService.registerResultsCallback(false, callback); 1375 1376 // Confirm handling queued results was not attempted. 1377 verify(mProfilingService, times(0)).handleQueuedResults(anyInt()); 1378 } 1379 1380 /** Test that adding a general listener does trigger handling queued results. */ 1381 @Test testQueuedResult_GeneralListenerCallbackRegistered()1382 public void testQueuedResult_GeneralListenerCallbackRegistered() { 1383 // Add a general callback. 1384 ProfilingResultCallback callback = new ProfilingResultCallback(); 1385 mProfilingService.registerResultsCallback(true, callback); 1386 1387 // Confirm handling queued results was attempted. 1388 verify(mProfilingService, times(1)).handleQueuedResults(anyInt()); 1389 } 1390 1391 /** 1392 * Test that notifying of a general listener added to existing callback does trigger handling 1393 * queued results. 1394 */ 1395 @Test testQueuedResult_GeneralListenerAdded()1396 public void testQueuedResult_GeneralListenerAdded() { 1397 // Notify service that a general listener was added to existing callback. 1398 mProfilingService.generalListenerAdded(); 1399 1400 // Confirm handling queued results was attempted. 1401 verify(mProfilingService, times(1)).handleQueuedResults(anyInt()); 1402 } 1403 1404 /** Test that a queued result with an invalid state is discarded with no callback. */ 1405 @Test testQueuedResult_InvalidState()1406 public void testQueuedResult_InvalidState() { 1407 // Clear all existing queued results. 1408 mProfilingService.mQueuedTracingResults.clear(); 1409 1410 // Add a in progress session to queue with invalid state. PROFILING_STARTED is an invalid 1411 // state because mQueuedTracingResults should only contain sessions that have completed with 1412 // a result. 1413 List<TracingSession> queue = new ArrayList<TracingSession>(); 1414 TracingSession session = new TracingSession( 1415 ProfilingManager.PROFILING_TYPE_HEAP_PROFILE, 1416 new Bundle(), 1417 FAKE_UID, 1418 APP_PACKAGE_NAME, 1419 REQUEST_TAG, 1420 KEY_LEAST_SIG_BITS, 1421 KEY_MOST_SIG_BITS, 1422 TRIGGER_TYPE_NONE); 1423 session.setState(TracingState.PROFILING_STARTED); 1424 queue.add(session); 1425 mProfilingService.mQueuedTracingResults.put(FAKE_UID, queue); 1426 1427 // Add a callback directly with fake uid 1428 ProfilingResultCallback callback = new ProfilingResultCallback(); 1429 mProfilingService.mResultCallbacks.put(FAKE_UID, Arrays.asList(callback)); 1430 1431 // Trigger handle queued results 1432 mProfilingService.handleQueuedResults(FAKE_UID); 1433 1434 // Confirm that the in progress result was deleted without triggering the callback 1435 assertFalse(mProfilingService.mQueuedTracingResults.contains(FAKE_UID)); 1436 assertFalse(callback.mResultSent); 1437 } 1438 1439 /** 1440 * Test that a queued result which is over the max retry limit is discarded with no callback. 1441 */ 1442 @Test testQueuedResult_OverMaxRetries()1443 public void testQueuedResult_OverMaxRetries() throws Exception { 1444 // Clear all existing queued results. 1445 mProfilingService.mQueuedTracingResults.clear(); 1446 1447 // Override the retry count 1448 executeShellCmd(OVERRIDE_DEVICE_CONFIG_INT, DeviceConfigHelper.NAMESPACE, 1449 DeviceConfigHelper.MAX_RESULT_REDELIVERY_COUNT, 3); 1450 1451 // Add a in progress session to queue with too many retries 1452 List<TracingSession> queue = new ArrayList<TracingSession>(); 1453 TracingSession session = new TracingSession( 1454 ProfilingManager.PROFILING_TYPE_HEAP_PROFILE, 1455 new Bundle(), 1456 FAKE_UID, 1457 APP_PACKAGE_NAME, 1458 REQUEST_TAG, 1459 KEY_LEAST_SIG_BITS, 1460 KEY_MOST_SIG_BITS, 1461 TRIGGER_TYPE_NONE); 1462 session.setState(TracingState.PROFILING_FINISHED); 1463 session.setRetryCount(3); 1464 queue.add(session); 1465 mProfilingService.mQueuedTracingResults.put(FAKE_UID, queue); 1466 1467 // Add a callback directly with fake uid 1468 ProfilingResultCallback callback = new ProfilingResultCallback(); 1469 mProfilingService.mResultCallbacks.put(FAKE_UID, Arrays.asList(callback)); 1470 1471 // Trigger handle queued results 1472 mProfilingService.handleQueuedResults(FAKE_UID); 1473 1474 // Confirm that the in progress result was deleted without triggering the callback 1475 assertFalse(mProfilingService.mQueuedTracingResults.contains(FAKE_UID)); 1476 assertFalse(callback.mResultSent); 1477 } 1478 1479 /** 1480 * Test that a queued result for a finished non-trace profiling follows the path to be redacted. 1481 */ 1482 @Test testQueuedResult_ProfilingFinished()1483 public void testQueuedResult_ProfilingFinished() { 1484 // Clear all existing queued results. 1485 mProfilingService.mQueuedTracingResults.clear(); 1486 1487 int uid = Binder.getCallingUid(); 1488 1489 // Add a in progress session to queue with too many retries 1490 List<TracingSession> queue = new ArrayList<TracingSession>(); 1491 TracingSession session = new TracingSession( 1492 ProfilingManager.PROFILING_TYPE_STACK_SAMPLING, 1493 new Bundle(), 1494 uid, 1495 APP_PACKAGE_NAME, 1496 REQUEST_TAG, 1497 KEY_LEAST_SIG_BITS, 1498 KEY_MOST_SIG_BITS, 1499 TRIGGER_TYPE_NONE); 1500 session.setState(TracingState.PROFILING_FINISHED); 1501 queue.add(session); 1502 mProfilingService.mQueuedTracingResults.put(uid, queue); 1503 1504 // Add a callback directly with fake uid 1505 ProfilingResultCallback callback = new ProfilingResultCallback(); 1506 mProfilingService.mResultCallbacks.put(uid, Arrays.asList(callback)); 1507 1508 // Trigger handle queued results 1509 mProfilingService.handleQueuedResults(uid); 1510 1511 // Confirm that the correct path was called. Callback will be for failed post processing 1512 // because we cannot copy from this context. 1513 verify(mProfilingService, times(1)).beginMoveFileToAppStorage(any()); 1514 verify(mProfilingService, times(1)).processTracingSessionResultCallback(any(), eq(false)); 1515 assertTrue(callback.mFileRequested); 1516 assertTrue(callback.mResultSent); 1517 assertEquals(ProfilingResult.ERROR_FAILED_POST_PROCESSING, callback.mStatus); 1518 } 1519 1520 /** 1521 * Test that a queued result for an unredacted trace follows the path to be redacted. It will 1522 * not actually be redacted because there was no trace run to redact. */ 1523 @Test testQueuedResult_TraceUnredacted()1524 public void testQueuedResult_TraceUnredacted() { 1525 // Clear all existing queued results. 1526 mProfilingService.mQueuedTracingResults.clear(); 1527 1528 // Add a in progress session to queue with too many retries 1529 List<TracingSession> queue = new ArrayList<TracingSession>(); 1530 TracingSession session = new TracingSession( 1531 ProfilingManager.PROFILING_TYPE_SYSTEM_TRACE, 1532 new Bundle(), 1533 FAKE_UID, 1534 APP_PACKAGE_NAME, 1535 REQUEST_TAG, 1536 KEY_LEAST_SIG_BITS, 1537 KEY_MOST_SIG_BITS, 1538 TRIGGER_TYPE_NONE); 1539 session.setState(TracingState.PROFILING_FINISHED); 1540 queue.add(session); 1541 mProfilingService.mQueuedTracingResults.put(FAKE_UID, queue); 1542 1543 // Add a callback directly with fake uid 1544 ProfilingResultCallback callback = new ProfilingResultCallback(); 1545 mProfilingService.mResultCallbacks.put(FAKE_UID, Arrays.asList(callback)); 1546 1547 // Trigger handle queued results 1548 mProfilingService.handleQueuedResults(FAKE_UID); 1549 1550 // Confirm that the correct path was called. Callback will be for failed post processing 1551 // because we cannot copy from this context. 1552 verify(mProfilingService, times(1)).handleRedactionRequiredResult(any()); 1553 assertTrue(callback.mResultSent); 1554 assertEquals(ProfilingResult.ERROR_FAILED_POST_PROCESSING, callback.mStatus); 1555 } 1556 1557 1558 /** Test that a queued result for an unredacted trace follows the path to be redacted. */ 1559 @Test testQueuedResult_TraceRedacted()1560 public void testQueuedResult_TraceRedacted() { 1561 // Clear all existing queued results. 1562 mProfilingService.mQueuedTracingResults.clear(); 1563 1564 int uid = Binder.getCallingUid(); 1565 1566 // Add a in progress session to queue with state redacted 1567 List<TracingSession> queue = new ArrayList<TracingSession>(); 1568 TracingSession session = new TracingSession( 1569 ProfilingManager.PROFILING_TYPE_SYSTEM_TRACE, 1570 new Bundle(), 1571 uid, 1572 APP_PACKAGE_NAME, 1573 REQUEST_TAG, 1574 KEY_LEAST_SIG_BITS, 1575 KEY_MOST_SIG_BITS, 1576 TRIGGER_TYPE_NONE); 1577 session.setState(TracingState.REDACTED); 1578 session.setProfilingStartTimeMs(System.currentTimeMillis()); 1579 queue.add(session); 1580 mProfilingService.mQueuedTracingResults.put(uid, queue); 1581 1582 // Add a callback directly 1583 ProfilingResultCallback callback = new ProfilingResultCallback(); 1584 mProfilingService.mResultCallbacks.put(uid, Arrays.asList(callback)); 1585 1586 // Trigger handle queued results 1587 mProfilingService.handleQueuedResults(uid); 1588 1589 // Confirm that the correct path was called. 1590 verify(mProfilingService, times(1)).beginMoveFileToAppStorage(any()); 1591 verify(mProfilingService, times(1)).processTracingSessionResultCallback(any(), eq(false)); 1592 assertTrue(callback.mFileRequested); 1593 assertTrue(callback.mResultSent); 1594 assertEquals(ProfilingResult.ERROR_FAILED_POST_PROCESSING, callback.mStatus); 1595 } 1596 1597 /** 1598 * Test that a queued result for an already redacted and copied trace successfully triggers a 1599 * callback. 1600 * 1601 * Success callback will be received in this case because there is no attempt to re-copy files 1602 * which would have failed from this context. 1603 */ 1604 @Test testQueuedResult_AlreadyCopied()1605 public void testQueuedResult_AlreadyCopied() { 1606 // Clear all existing queued results. 1607 mProfilingService.mQueuedTracingResults.clear(); 1608 1609 // Add a in progress session to queue with too many retries 1610 List<TracingSession> queue = new ArrayList<TracingSession>(); 1611 TracingSession session = new TracingSession( 1612 ProfilingManager.PROFILING_TYPE_SYSTEM_TRACE, 1613 new Bundle(), 1614 FAKE_UID, 1615 APP_PACKAGE_NAME, 1616 REQUEST_TAG, 1617 KEY_LEAST_SIG_BITS, 1618 KEY_MOST_SIG_BITS, 1619 TRIGGER_TYPE_NONE); 1620 session.setState(TracingState.COPIED_FILE); 1621 session.setError(ProfilingResult.ERROR_NONE); 1622 queue.add(session); 1623 mProfilingService.mQueuedTracingResults.put(FAKE_UID, queue); 1624 1625 // Add a callback directly with fake uid 1626 ProfilingResultCallback callback = new ProfilingResultCallback(); 1627 mProfilingService.mResultCallbacks.put(FAKE_UID, Arrays.asList(callback)); 1628 1629 // Trigger handle queued results 1630 mProfilingService.handleQueuedResults(FAKE_UID); 1631 1632 // Confirm that the correct path was called that a success callback was received. 1633 verify(mProfilingService, times(1)).processTracingSessionResultCallback(any(), eq(true)); 1634 assertFalse(mProfilingService.mQueuedTracingResults.contains(FAKE_UID)); 1635 } 1636 1637 /** 1638 * Test that a queued result for a session with state of error occurred correctly progresses 1639 * to next step of triggering callback. 1640 */ 1641 @Test testQueuedResult_ErrorOccurred()1642 public void testQueuedResult_ErrorOccurred() { 1643 // Clear all existing queued results. 1644 mProfilingService.mQueuedTracingResults.clear(); 1645 1646 // Add a in progress session to queue with state error occurred 1647 List<TracingSession> queue = new ArrayList<TracingSession>(); 1648 TracingSession session = new TracingSession( 1649 ProfilingManager.PROFILING_TYPE_SYSTEM_TRACE, 1650 new Bundle(), 1651 FAKE_UID, 1652 APP_PACKAGE_NAME, 1653 REQUEST_TAG, 1654 KEY_LEAST_SIG_BITS, 1655 KEY_MOST_SIG_BITS, 1656 TRIGGER_TYPE_NONE); 1657 session.setState(TracingState.ERROR_OCCURRED); 1658 session.setError(ProfilingResult.ERROR_UNKNOWN); 1659 queue.add(session); 1660 mProfilingService.mQueuedTracingResults.put(FAKE_UID, queue); 1661 1662 // Add a callback directly with fake uid 1663 ProfilingResultCallback callback = new ProfilingResultCallback(); 1664 mProfilingService.mResultCallbacks.put(FAKE_UID, Arrays.asList(callback)); 1665 1666 // Trigger handle queued results 1667 mProfilingService.handleQueuedResults(FAKE_UID); 1668 1669 // Confirm that the correct path was called that an error callback was received. 1670 verify(mProfilingService, times(1)).processTracingSessionResultCallback(any(), eq(true)); 1671 assertTrue(callback.mResultSent); 1672 assertEquals(ProfilingResult.ERROR_UNKNOWN, callback.mStatus); 1673 } 1674 1675 /** 1676 * Test that a queued result for a session with state of notified requester correctly progresses 1677 * to next step of cleaning up and does not trigger any further callbacks. 1678 */ 1679 @Test testQueuedResult_NotifiedRequester()1680 public void testQueuedResult_NotifiedRequester() { 1681 // Clear all existing queued results. 1682 mProfilingService.mQueuedTracingResults.clear(); 1683 1684 // Add a in progress session to queue with state notified requester 1685 List<TracingSession> queue = new ArrayList<TracingSession>(); 1686 TracingSession session = new TracingSession( 1687 ProfilingManager.PROFILING_TYPE_SYSTEM_TRACE, 1688 new Bundle(), 1689 FAKE_UID, 1690 APP_PACKAGE_NAME, 1691 REQUEST_TAG, 1692 KEY_LEAST_SIG_BITS, 1693 KEY_MOST_SIG_BITS, 1694 TRIGGER_TYPE_NONE); 1695 session.setState(TracingState.NOTIFIED_REQUESTER); 1696 session.setError(ProfilingResult.ERROR_NONE); 1697 queue.add(session); 1698 mProfilingService.mQueuedTracingResults.put(FAKE_UID, queue); 1699 1700 // Add a callback directly with fake uid 1701 ProfilingResultCallback callback = new ProfilingResultCallback(); 1702 mProfilingService.mResultCallbacks.put(FAKE_UID, Arrays.asList(callback)); 1703 1704 // Trigger handle queued results 1705 mProfilingService.handleQueuedResults(FAKE_UID); 1706 1707 // Confirm that the correct path was called. 1708 verify(mProfilingService, times(1)).cleanupTracingSession(any()); 1709 assertFalse(mProfilingService.mQueuedTracingResults.contains(FAKE_UID)); 1710 assertFalse(callback.mResultSent); 1711 } 1712 1713 /** 1714 * Test that a queued result that was started longer than max queue time ago is successfully 1715 * cleaned up when the queue is triggered for a different uid. 1716 */ 1717 @Test testQueuedResult_Cleanup()1718 public void testQueuedResult_Cleanup() { 1719 // Clear all existing queued results. 1720 mProfilingService.mQueuedTracingResults.clear(); 1721 1722 // Add a in progress session to queue that was started more than max duration ago. 1723 List<TracingSession> queue = new ArrayList<TracingSession>(); 1724 TracingSession session = new TracingSession( 1725 ProfilingManager.PROFILING_TYPE_SYSTEM_TRACE, 1726 new Bundle(), 1727 FAKE_UID, 1728 APP_PACKAGE_NAME, 1729 REQUEST_TAG, 1730 KEY_LEAST_SIG_BITS, 1731 KEY_MOST_SIG_BITS, 1732 TRIGGER_TYPE_NONE); 1733 session.setState(TracingState.COPIED_FILE); 1734 session.setProfilingStartTimeMs(System.currentTimeMillis() - 1000 1735 - ProfilingService.QUEUED_RESULT_MAX_RETAINED_DURATION_MS); 1736 queue.add(session); 1737 mProfilingService.mQueuedTracingResults.put(FAKE_UID, queue); 1738 1739 int fakeUid2 = FAKE_UID + 1; 1740 1741 // Add a callback directly with a different fake uid 1742 ProfilingResultCallback callback = new ProfilingResultCallback(); 1743 mProfilingService.mResultCallbacks.put(fakeUid2, Arrays.asList(callback)); 1744 1745 // Trigger handle queued results 1746 mProfilingService.handleQueuedResults(fakeUid2); 1747 1748 // Confirm the old result was cleaned up. 1749 assertEquals(0, mProfilingService.mQueuedTracingResults.size()); 1750 } 1751 1752 @SuppressWarnings("GuardedBy") // Suppress warning for mProfilingService lock. 1753 @Test testTemporaryDirectoryCleanup_inActiveSession()1754 public void testTemporaryDirectoryCleanup_inActiveSession() throws Exception { 1755 // Setup the temporary directory in app storage so we can access it from this context. Make 1756 // sure it exists and is empty. 1757 File directory = new File(mContext.getFilesDir().getPath()); 1758 directory.delete(); 1759 directory.mkdirs(); 1760 assertTrue(directory.isDirectory()); 1761 1762 // Create 3 files, 1 to be tracked and 2 not to be tracked 1763 File trackedFile = createAndConfirmFileExists(directory, "tracked_active_file"); 1764 File untrackedFile1 = createAndConfirmFileExists(directory, "untracked_file_1"); 1765 File untrackedFile2 = createAndConfirmFileExists(directory, "untracked_file_2"); 1766 1767 // Add the tracked file to active sessions 1768 TracingSession session = new TracingSession( 1769 ProfilingManager.PROFILING_TYPE_SYSTEM_TRACE, 1770 new Bundle(), 1771 FAKE_UID, 1772 APP_PACKAGE_NAME, 1773 REQUEST_TAG, 1774 KEY_LEAST_SIG_BITS, 1775 KEY_MOST_SIG_BITS, 1776 TRIGGER_TYPE_NONE); 1777 session.setFileName(trackedFile.getName()); 1778 mProfilingService.mActiveTracingSessions.put(session.getKey(), session); 1779 assertEquals(1, mProfilingService.mActiveTracingSessions.size()); 1780 1781 // Now trigger the cleanup 1782 mProfilingService.cleanupTemporaryDirectoryLocked(directory.getPath()); 1783 1784 // Finally, confirm that the 1 tracked file is still present and that the 2 non-tracked 1785 // files were deleted. 1786 confirmNonEmptyFileExists(trackedFile); 1787 assertFalse(untrackedFile1.exists()); 1788 assertFalse(untrackedFile2.exists()); 1789 } 1790 1791 @SuppressWarnings("GuardedBy") // Suppress warning for mProfilingService lock. 1792 @Test testTemporaryDirectoryCleanup_inQueuedSession()1793 public void testTemporaryDirectoryCleanup_inQueuedSession() throws Exception { 1794 // Setup the temporary directory in app storage so we can access it from this context. Make 1795 // sure it exists and is empty. 1796 File directory = new File(mContext.getFilesDir().getPath()); 1797 directory.delete(); 1798 directory.mkdirs(); 1799 assertTrue(directory.isDirectory()); 1800 1801 // Create 5 files, 3 to be tracked and 2 not to be tracked 1802 File trackedFile1 = createAndConfirmFileExists(directory, "tracked_queued_file_1"); 1803 File trackedFile2 = createAndConfirmFileExists(directory, "tracked_queued_file_2"); 1804 File trackedFile3 = createAndConfirmFileExists(directory, "tracked_queued_file_3"); 1805 File untrackedFile1 = createAndConfirmFileExists(directory, "untracked_file_1"); 1806 File untrackedFile2 = createAndConfirmFileExists(directory, "untracked_file_2"); 1807 1808 // Add the 3 tracked files to active sessions, 2 under 1 uid and 1 under another. 1809 int fakeUid2 = FAKE_UID + 1; 1810 // Create the fake sessions and set their filenames. 1811 TracingSession session1 = new TracingSession( 1812 ProfilingManager.PROFILING_TYPE_SYSTEM_TRACE, 1813 new Bundle(), 1814 FAKE_UID, 1815 APP_PACKAGE_NAME, 1816 REQUEST_TAG, 1817 KEY_LEAST_SIG_BITS, 1818 KEY_MOST_SIG_BITS, 1819 TRIGGER_TYPE_NONE); 1820 session1.setRedactedFileName(trackedFile1.getName()); 1821 TracingSession session2 = new TracingSession( 1822 ProfilingManager.PROFILING_TYPE_JAVA_HEAP_DUMP, 1823 new Bundle(), 1824 fakeUid2, 1825 APP_PACKAGE_NAME, 1826 REQUEST_TAG, 1827 KEY_LEAST_SIG_BITS, 1828 KEY_MOST_SIG_BITS, 1829 TRIGGER_TYPE_NONE); 1830 session2.setFileName(trackedFile2.getName()); 1831 TracingSession session3 = new TracingSession( 1832 ProfilingManager.PROFILING_TYPE_JAVA_HEAP_DUMP, 1833 new Bundle(), 1834 fakeUid2, 1835 APP_PACKAGE_NAME, 1836 REQUEST_TAG, 1837 KEY_LEAST_SIG_BITS, 1838 KEY_MOST_SIG_BITS, 1839 TRIGGER_TYPE_NONE); 1840 session3.setFileName(trackedFile3.getName()); 1841 // Put 1 session in one list. 1842 List<TracingSession> sessionList1 = new ArrayList<TracingSession>(Arrays.asList(session1)); 1843 mProfilingService.mQueuedTracingResults.put(FAKE_UID, sessionList1); 1844 // Put 2 sessions in the other list. 1845 List<TracingSession> sessionList2 = new ArrayList<TracingSession>( 1846 Arrays.asList(session2, session3)); 1847 mProfilingService.mQueuedTracingResults.put(fakeUid2, sessionList2); 1848 // Add an empty list just for fun. 1849 mProfilingService.mQueuedTracingResults.put(fakeUid2 + 1, new ArrayList<TracingSession>()); 1850 // Make sure all lists have been added. 1851 assertEquals(3, mProfilingService.mQueuedTracingResults.size()); 1852 1853 // Now trigger the cleanup 1854 mProfilingService.cleanupTemporaryDirectoryLocked(directory.getPath()); 1855 1856 // Finally, confirm that the 3 tracked files are still present and that the 2 non-tracked 1857 // files were deleted. 1858 confirmNonEmptyFileExists(trackedFile1); 1859 confirmNonEmptyFileExists(trackedFile2); 1860 confirmNonEmptyFileExists(trackedFile3); 1861 assertFalse(untrackedFile1.exists()); 1862 assertFalse(untrackedFile2.exists()); 1863 } 1864 1865 /** Test that result callbacks are correctly cleaned up when new callbacks are added. */ 1866 @Test testResultCallbacksCleanup()1867 public void testResultCallbacksCleanup() throws Exception { 1868 mProfilingService.mResultCallbacks.clear(); 1869 1870 // Create 4 callbacks and mock binder dead in 2 of them. 1871 ProfilingResultCallback callbackAlive1 = new ProfilingResultCallback(); 1872 ProfilingResultCallback callbackAlive2 = new ProfilingResultCallback(); 1873 ProfilingResultCallback callbackDead1 = spy(new ProfilingResultCallback()); 1874 doReturn(false).when(callbackDead1).isBinderAlive(); 1875 ProfilingResultCallback callbackDead2 = spy(new ProfilingResultCallback()); 1876 doReturn(false).when(callbackDead2).isBinderAlive(); 1877 1878 // Register alive callback and confirm it's retained. 1879 mProfilingService.registerResultsCallback(true, callbackAlive1); 1880 assertEquals(1, mProfilingService.mResultCallbacks.get(Binder.getCallingUid()).size()); 1881 1882 // Register dead callback. Cleanup is not performed on just added callback so expect 2 1883 // callbacks to be present. 1884 mProfilingService.registerResultsCallback(true, callbackDead1); 1885 assertEquals(2, mProfilingService.mResultCallbacks.get(Binder.getCallingUid()).size()); 1886 1887 // Register another dead callback. Cleanup is expected to remove the first dead callback and 1888 // leave the new one so size should still be 2. 1889 mProfilingService.registerResultsCallback(true, callbackDead2); 1890 assertEquals(2, mProfilingService.mResultCallbacks.get(Binder.getCallingUid()).size()); 1891 1892 // Register another alive callback. Cleanup should now remove the 2nd dead callback leaving 1893 // the 2 alive callbacks in place. 1894 mProfilingService.registerResultsCallback(true, callbackAlive2); 1895 assertEquals(2, mProfilingService.mResultCallbacks.get(Binder.getCallingUid()).size()); 1896 } 1897 1898 /** 1899 * Test that adding triggers adds to the correct process and overwrites with new results when 1900 * the same trigger, uid, and process name are used. 1901 */ 1902 @Test 1903 @EnableFlags(android.os.profiling.Flags.FLAG_SYSTEM_TRIGGERED_PROFILING_NEW) testAddTriggers()1904 public void testAddTriggers() throws Exception { 1905 // First, clear the data structure. 1906 mProfilingService.mAppTriggers.getMap().clear(); 1907 1908 // Now add several triggers: 1909 // First add 2 different triggers to the same uid/package 1910 mProfilingService.addTrigger(FAKE_UID, APP_PACKAGE_NAME, 1911 ProfilingTrigger.TRIGGER_TYPE_APP_FULLY_DRAWN, RATE_LIMITING_0_HOURS_BETWEEN); 1912 mProfilingService.addTrigger(FAKE_UID, APP_PACKAGE_NAME, 1913 ProfilingTrigger.TRIGGER_TYPE_ANR, RATE_LIMITING_0_HOURS_BETWEEN); 1914 // And add one to another uid with the same package name. 1915 mProfilingService.addTrigger(FAKE_UID_2, APP_PACKAGE_NAME, 1916 ProfilingTrigger.TRIGGER_TYPE_ANR, RATE_LIMITING_0_HOURS_BETWEEN); 1917 1918 // Grab the per process arrays. 1919 SparseArray<ProfilingTriggerData> uid1Triggers = 1920 mProfilingService.mAppTriggers.get(APP_PACKAGE_NAME, FAKE_UID); 1921 SparseArray<ProfilingTriggerData> uid2Triggers = 1922 mProfilingService.mAppTriggers.get(APP_PACKAGE_NAME, FAKE_UID_2); 1923 1924 // Confirm they are represented correctly. 1925 assertEquals(2, uid1Triggers.size()); 1926 assertEquals(1, uid2Triggers.size()); 1927 confirmProfilingTriggerEquals( 1928 uid1Triggers.get(ProfilingTrigger.TRIGGER_TYPE_APP_FULLY_DRAWN), FAKE_UID, 1929 APP_PACKAGE_NAME, ProfilingTrigger.TRIGGER_TYPE_APP_FULLY_DRAWN, 0); 1930 confirmProfilingTriggerEquals( 1931 uid1Triggers.get(ProfilingTrigger.TRIGGER_TYPE_ANR), FAKE_UID, APP_PACKAGE_NAME, 1932 ProfilingTrigger.TRIGGER_TYPE_ANR, 0); 1933 confirmProfilingTriggerEquals( 1934 uid2Triggers.get(ProfilingTrigger.TRIGGER_TYPE_ANR), FAKE_UID_2, APP_PACKAGE_NAME, 1935 ProfilingTrigger.TRIGGER_TYPE_ANR, 0); 1936 1937 // Now add a repeated trigger with 1 field changed. 1938 mProfilingService.addTrigger(FAKE_UID, APP_PACKAGE_NAME, ProfilingTrigger.TRIGGER_TYPE_ANR, 1939 100); 1940 1941 // Confirm the new value is set. 1942 assertEquals(100, mProfilingService.mAppTriggers.get(APP_PACKAGE_NAME, FAKE_UID) 1943 .get(ProfilingTrigger.TRIGGER_TYPE_ANR).getRateLimitingPeriodHours()); 1944 } 1945 1946 /** Test that app level rate limiting works correctly in the allow case. */ 1947 @Test 1948 @EnableFlags(android.os.profiling.Flags.FLAG_SYSTEM_TRIGGERED_PROFILING_NEW) testProcessTrigger_appLevelRateLimit_allow()1949 public void testProcessTrigger_appLevelRateLimit_allow() throws Exception { 1950 // First, clear the data structure. 1951 mProfilingService.mAppTriggers.getMap().clear(); 1952 1953 // Override the system rate limiter to always pass, we're not testing that here. 1954 doReturn(RateLimiter.RATE_LIMIT_RESULT_ALLOWED).when(mRateLimiter) 1955 .isProfilingRequestAllowed(anyInt(), anyInt(), eq(true), any()); 1956 1957 // And setup some mocks. 1958 mProfilingService.mSystemTriggeredTraceUniqueSessionName = "something_non_null"; 1959 doReturn(true).when(mActiveTrace).isAlive(); 1960 mProfilingService.mSystemTriggeredTraceProcess = mActiveTrace; 1961 1962 // Setup some rate limiting values. Since this is an allow test, set the last run to be 1 1963 // hour more than the rate limiting period. 1964 int rateLimitingPeriodHours = 10; 1965 long fakeLastTriggerTimeMs = System.currentTimeMillis() 1966 - ((rateLimitingPeriodHours + 1) * 60L * 60L * 1000L); 1967 1968 // Add the trigger we'll use. 1969 mProfilingService.addTrigger(FAKE_UID, APP_PACKAGE_NAME, ProfilingTrigger.TRIGGER_TYPE_ANR, 1970 rateLimitingPeriodHours); 1971 1972 // Set the last run time. 1973 mProfilingService.mAppTriggers.get(APP_PACKAGE_NAME, FAKE_UID) 1974 .get(ProfilingTrigger.TRIGGER_TYPE_ANR) 1975 .setLastTriggeredTimeMs(fakeLastTriggerTimeMs); 1976 1977 // Now process the trigger. 1978 mProfilingService.processTriggerInternal(FAKE_UID, APP_PACKAGE_NAME, 1979 ProfilingTrigger.TRIGGER_TYPE_ANR); 1980 1981 // Get the new trigger time and make sure it's later than the fake one, indicating it ran. 1982 long newTriggerTime = mProfilingService.mAppTriggers.get(APP_PACKAGE_NAME, FAKE_UID) 1983 .get(ProfilingTrigger.TRIGGER_TYPE_ANR).getLastTriggeredTimeMs(); 1984 assertTrue(newTriggerTime > fakeLastTriggerTimeMs); 1985 } 1986 1987 /** Test that app level rate limiting works correctly in the deny case. */ 1988 @Test 1989 @EnableFlags(android.os.profiling.Flags.FLAG_SYSTEM_TRIGGERED_PROFILING_NEW) testProcessTrigger_appLevelRateLimit_deny()1990 public void testProcessTrigger_appLevelRateLimit_deny() throws Exception { 1991 // First, clear the data structure. 1992 mProfilingService.mAppTriggers.getMap().clear(); 1993 1994 // Override the system rate limiter to always pass, we're not testing that here. 1995 doReturn(RateLimiter.RATE_LIMIT_RESULT_ALLOWED).when(mRateLimiter) 1996 .isProfilingRequestAllowed(anyInt(), anyInt(), eq(true), any()); 1997 1998 // And setup some mocks. 1999 mProfilingService.mSystemTriggeredTraceUniqueSessionName = "something_non_null"; 2000 doReturn(true).when(mActiveTrace).isAlive(); 2001 mProfilingService.mSystemTriggeredTraceProcess = mActiveTrace; 2002 2003 // Setup some rate limiting values. Since this is a deny test, set the last run to be 1 hour 2004 // less than the rate limiting period. 2005 int rateLimitingPeriodHours = 10; 2006 long fakeLastTriggerTimeMs = System.currentTimeMillis() 2007 - ((rateLimitingPeriodHours - 1) * 60L * 60L * 1000L); 2008 2009 // Add the trigger we'll use, 2010 mProfilingService.addTrigger(FAKE_UID, APP_PACKAGE_NAME, ProfilingTrigger.TRIGGER_TYPE_ANR, 2011 rateLimitingPeriodHours); 2012 2013 // Set the last run time. 2014 mProfilingService.mAppTriggers.get(APP_PACKAGE_NAME, FAKE_UID) 2015 .get(ProfilingTrigger.TRIGGER_TYPE_ANR) 2016 .setLastTriggeredTimeMs(fakeLastTriggerTimeMs); 2017 2018 // Now process the trigger. 2019 mProfilingService.processTriggerInternal(FAKE_UID, APP_PACKAGE_NAME, 2020 ProfilingTrigger.TRIGGER_TYPE_ANR); 2021 2022 // Get the new trigger time and make sure it's equal to the fake one, indicating it did not 2023 // run. 2024 long newTriggerTime = mProfilingService.mAppTriggers.get(APP_PACKAGE_NAME, FAKE_UID) 2025 .get(ProfilingTrigger.TRIGGER_TYPE_ANR).getLastTriggeredTimeMs(); 2026 assertEquals(fakeLastTriggerTimeMs, newTriggerTime); 2027 } 2028 2029 /** Test that system level rate limiting works correctly in the allow case. */ 2030 @Test 2031 @EnableFlags(android.os.profiling.Flags.FLAG_SYSTEM_TRIGGERED_PROFILING_NEW) testProcessTrigger_systemLevelRateLimit_allow()2032 public void testProcessTrigger_systemLevelRateLimit_allow() throws Exception { 2033 overrideRateLimiterDefaults(); 2034 2035 // First, clear the data structure. 2036 mProfilingService.mAppTriggers.getMap().clear(); 2037 2038 // And setup some mocks. 2039 mProfilingService.mSystemTriggeredTraceUniqueSessionName = "something_non_null"; 2040 doReturn(true).when(mActiveTrace).isAlive(); 2041 mProfilingService.mSystemTriggeredTraceProcess = mActiveTrace; 2042 2043 // Add the trigger we'll use. 2044 mProfilingService.addTrigger(FAKE_UID, APP_PACKAGE_NAME, 2045 ProfilingTrigger.TRIGGER_TYPE_APP_FULLY_DRAWN, 2046 RATE_LIMITING_0_HOURS_BETWEEN/*Set to 0 as we're not testing rate limiting here.*/); 2047 2048 // Now process the trigger. 2049 mProfilingService.processTriggerInternal(FAKE_UID, APP_PACKAGE_NAME, 2050 ProfilingTrigger.TRIGGER_TYPE_APP_FULLY_DRAWN); 2051 2052 // Get the new trigger time and make sure it's later than 0, indicating it ran. 2053 long newTriggerTime = mProfilingService.mAppTriggers.get(APP_PACKAGE_NAME, FAKE_UID) 2054 .get(ProfilingTrigger.TRIGGER_TYPE_APP_FULLY_DRAWN).getLastTriggeredTimeMs(); 2055 assertTrue(newTriggerTime > 0); 2056 } 2057 2058 /** Test that system level rate limiting works correctly in the deny case. */ 2059 @Test 2060 @EnableFlags(android.os.profiling.Flags.FLAG_SYSTEM_TRIGGERED_PROFILING_NEW) testProcessTrigger_systemLevelRateLimit_deny()2061 public void testProcessTrigger_systemLevelRateLimit_deny() throws Exception { 2062 overrideRateLimiterDefaults(); 2063 2064 // First, clear the data structure. 2065 mProfilingService.mAppTriggers.getMap().clear(); 2066 2067 // And setup some mocks. 2068 mProfilingService.mSystemTriggeredTraceUniqueSessionName = "something_non_null"; 2069 doReturn(true).when(mActiveTrace).isAlive(); 2070 mProfilingService.mSystemTriggeredTraceProcess = mActiveTrace; 2071 2072 // Add record with high cost to rate limiter so that it won't allow future runs. 2073 mRateLimiter.mPastRunsHour.add(FAKE_UID, 1000, System.currentTimeMillis()); 2074 2075 // Wait 1 ms to ensure time has ticked and avoid potential flake. 2076 sleep(1); 2077 2078 // Add the trigger we'll use, 2079 mProfilingService.addTrigger(FAKE_UID, APP_PACKAGE_NAME, 2080 ProfilingTrigger.TRIGGER_TYPE_APP_FULLY_DRAWN, 2081 RATE_LIMITING_0_HOURS_BETWEEN/*Set to 0 as we're not testing rate limiting here.*/); 2082 2083 // Now process the trigger. 2084 mProfilingService.processTriggerInternal(FAKE_UID, APP_PACKAGE_NAME, 2085 ProfilingTrigger.TRIGGER_TYPE_APP_FULLY_DRAWN); 2086 2087 // Get the new trigger time and make sure it's equal to 0, indicating it did not run. 2088 long newTriggerTime = mProfilingService.mAppTriggers.get(APP_PACKAGE_NAME, FAKE_UID) 2089 .get(ProfilingTrigger.TRIGGER_TYPE_APP_FULLY_DRAWN).getLastTriggeredTimeMs(); 2090 assertEquals(0, newTriggerTime); 2091 } 2092 2093 /** 2094 * Test that scheduling for system triggered profiling trace start works correctly, configuring 2095 * run delay for correct amount of time. 2096 */ 2097 @Test 2098 @EnableFlags(android.os.profiling.Flags.FLAG_SYSTEM_TRIGGERED_PROFILING_NEW) testSystemTriggeredProfiling_Scheduling()2099 public void testSystemTriggeredProfiling_Scheduling() throws Exception { 2100 // Override system triggered trace start values so that the trace will be attempted to be 2101 // started within the test duration. If these values are changed, make sure to update the 2102 // additional delay below as well. 2103 executeShellCmd(OVERRIDE_DEVICE_CONFIG_INT, DeviceConfigHelper.NAMESPACE, 2104 DeviceConfigHelper.SYSTEM_TRIGGERED_TRACE_MIN_PERIOD_SECONDS, 3); 2105 updateDeviceConfigAndWaitForChange(DeviceConfigHelper.NAMESPACE, 2106 DeviceConfigHelper.SYSTEM_TRIGGERED_TRACE_MAX_PERIOD_SECONDS, 4); 2107 2108 // Cancel the already scheduled future and set to null, if applicable. 2109 if (mProfilingService.mStartSystemTriggeredTraceScheduledFuture != null) { 2110 mProfilingService.mStartSystemTriggeredTraceScheduledFuture.cancel(true); 2111 mProfilingService.mStartSystemTriggeredTraceScheduledFuture = null; 2112 } 2113 2114 // Schedule a start of system triggered trace. 2115 mProfilingService.scheduleNextSystemTriggeredTraceStart(); 2116 2117 // Confirm the future is scheduled and that an attempt to start the trace has not occurred 2118 // yet. 2119 assertNotNull(mProfilingService.mStartSystemTriggeredTraceScheduledFuture); 2120 assertFalse(mProfilingService.mStartSystemTriggeredTraceScheduledFuture.isDone()); 2121 verify(mProfilingService, times(0)).startSystemTriggeredTrace(); 2122 2123 // Wait for 2 seconds longer than the scheduled future delay so that the future can execute 2124 // once, but not twice. 2 seconds is selected as the extra delay because it is less than 3 2125 // which is set as min for period above, but also the highest value possible to give time to 2126 // execute. 2127 long delay = mProfilingService.mStartSystemTriggeredTraceScheduledFuture.getDelay( 2128 TimeUnit.SECONDS); 2129 sleep((delay + 2L) * 1000L); 2130 2131 // Finally, confirm that the future ran by confirming that an attempt to start the trace was 2132 // made. We don't confirm that it actually started as we can't actually start the trace from 2133 // this context. 2134 verify(mProfilingService, times(1)).startSystemTriggeredTrace(); 2135 } 2136 createAndConfirmFileExists(File directory, String fileName)2137 private File createAndConfirmFileExists(File directory, String fileName) throws Exception { 2138 File file = new File(directory, fileName); 2139 file.createNewFile(); 2140 FileOutputStream fileOutputStream = new FileOutputStream(file); 2141 fileOutputStream.write("some stub text".getBytes()); 2142 fileOutputStream.close(); 2143 confirmNonEmptyFileExists(file); 2144 return file; 2145 } 2146 confirmNonEmptyFileExists(File file)2147 private void confirmNonEmptyFileExists(File file) { 2148 assertTrue(file.exists()); 2149 assertTrue(file.length() > 0L); 2150 } 2151 overrideRateLimiterDefaults()2152 private void overrideRateLimiterDefaults() throws Exception { 2153 // Update DeviceConfig defaults to general high enough limits, cost of 1, and persist 2154 // frequency 0. 2155 overrideRateLimiterDefaults( 2156 DEFAULT_LIMIT_SYSTEM_HOUR, 2157 DEFAULT_LIMIT_PROCESS_HOUR, 2158 DEFAULT_LIMIT_SYSTEM_DAY, 2159 DEFAULT_LIMIT_PROCESS_DAY, 2160 DEFAULT_LIMIT_SYSTEM_WEEK, 2161 DEFAULT_LIMIT_PROCESS_WEEK, 2162 DEFAULT_PROFILING_RUN_COST, 2163 DEFAULT_PROFILING_RUN_COST, 2164 DEFAULT_PROFILING_RUN_COST, 2165 DEFAULT_PROFILING_RUN_COST, 2166 DEFAULT_PROFILING_RUN_COST, 2167 DEFAULT_PERSIST_TO_DISK_FREQUENCY); 2168 } 2169 overrideRateLimiterDefaults(int systemHour, int processHour, int systemDay, int processDay, int systemWeek, int processWeek, int costHeapDump, int costHeapProfile, int costStackSampling, int costSystemTrace, int costSystemTriggeredSystemProfiling, int persistToDiskFrequency)2170 private void overrideRateLimiterDefaults(int systemHour, int processHour, int systemDay, 2171 int processDay, int systemWeek, int processWeek, int costHeapDump, int costHeapProfile, 2172 int costStackSampling, int costSystemTrace, int costSystemTriggeredSystemProfiling, 2173 int persistToDiskFrequency) { 2174 mRateLimiter.mPastRunsHour.maybeUpdateMaxCosts(systemHour, processHour); 2175 mRateLimiter.mPastRunsDay.maybeUpdateMaxCosts(systemDay, processDay); 2176 mRateLimiter.mPastRunsWeek.maybeUpdateMaxCosts(systemWeek, processWeek); 2177 mRateLimiter.mCostJavaHeapDump = costHeapDump; 2178 mRateLimiter.mCostHeapProfile = costHeapProfile; 2179 mRateLimiter.mCostStackSampling = costStackSampling; 2180 mRateLimiter.mCostSystemTrace = costSystemTrace; 2181 mRateLimiter.mCostSystemTriggeredSystemTrace = costSystemTriggeredSystemProfiling; 2182 mRateLimiter.mPersistToDiskFrequency = persistToDiskFrequency; 2183 } 2184 2185 @FormatMethod executeShellCmd(String cmdFormat, Object... args)2186 private String executeShellCmd(String cmdFormat, Object... args) throws Exception { 2187 String cmd = String.format(cmdFormat, args); 2188 return SystemUtil.runShellCommand(mInstrumentation, cmd); 2189 } 2190 confirmRateLimiterEntriesEqual(RateLimiter.CollectionEntry[] collectionOne, RateLimiter.CollectionEntry[] collectionTwo)2191 private void confirmRateLimiterEntriesEqual(RateLimiter.CollectionEntry[] collectionOne, 2192 RateLimiter.CollectionEntry[] collectionTwo) { 2193 assertEquals(collectionOne.length, collectionTwo.length); 2194 for (int i = 0; i < collectionOne.length; i++) { 2195 assertEquals(collectionOne[i].mUid, collectionTwo[i].mUid); 2196 assertEquals(collectionOne[i].mCost, collectionTwo[i].mCost); 2197 assertEquals(collectionOne[i].mTimestamp, collectionTwo[i].mTimestamp); 2198 } 2199 } 2200 2201 // LINT.IfChange(equals) confirmTracingSessionsEqual(TracingSession s1, TracingSession s2)2202 private void confirmTracingSessionsEqual(TracingSession s1, TracingSession s2) { 2203 assertEquals(s1.getProfilingType(), s2.getProfilingType()); 2204 assertEquals(s1.getUid(), s2.getUid()); 2205 assertEquals(s1.getPackageName(), s2.getPackageName()); 2206 assertEquals(s1.getTag(), s2.getTag()); 2207 assertEquals(s1.getKeyMostSigBits(), s2.getKeyMostSigBits()); 2208 assertEquals(s1.getKeyLeastSigBits(), s2.getKeyLeastSigBits()); 2209 assertEquals(s1.getFileName(), s2.getFileName()); 2210 assertEquals(s1.getRedactedFileName(), s2.getRedactedFileName()); 2211 assertEquals(s1.getState().getValue(), s2.getState().getValue()); 2212 assertEquals(s1.getRetryCount(), s2.getRetryCount()); 2213 assertEquals(s1.getErrorMessage(), s2.getErrorMessage()); 2214 assertEquals(s1.getErrorStatus(), s2.getErrorStatus()); 2215 assertEquals(s1.getTriggerType(), s2.getTriggerType()); 2216 assertEquals(s1.getProfilingStartTimeMs(), s2.getProfilingStartTimeMs()); 2217 } 2218 // LINT.ThenChange(/service/proto/android/os/queue.proto:proto) 2219 2220 // LINT.IfChange(trigger_equals) confirmProfilingTriggerEquals(ProfilingTriggerData t1, int uid, String packageName, int triggerType, int rateLimitingPeriodHours)2221 private void confirmProfilingTriggerEquals(ProfilingTriggerData t1, int uid, String packageName, 2222 int triggerType, int rateLimitingPeriodHours) { 2223 confirmProfilingTriggerEquals(t1, 2224 new ProfilingTriggerData(uid, packageName, triggerType, rateLimitingPeriodHours)); 2225 } 2226 confirmProfilingTriggerEquals(ProfilingTriggerData t1, ProfilingTriggerData t2)2227 private void confirmProfilingTriggerEquals(ProfilingTriggerData t1, ProfilingTriggerData t2) { 2228 assertEquals(t1.getUid(), t2.getUid()); 2229 assertEquals(t1.getPackageName(), t2.getPackageName()); 2230 assertEquals(t1.getTriggerType(), t2.getTriggerType()); 2231 assertEquals(t1.getRateLimitingPeriodHours(), t2.getRateLimitingPeriodHours()); 2232 assertEquals(t1.getLastTriggeredTimeMs(), t2.getLastTriggeredTimeMs()); 2233 } 2234 // LINT.ThenChange(/service/proto/android/os/trigger.proto:proto) 2235 2236 /** Confirm that all fields returned by callback match expectation. */ confirmResultCallback(ProfilingResultCallback callback, String resultFile, long keyMostSigBits, long keyLeastSigBits, int status, String tag, boolean errorExpected)2237 private void confirmResultCallback(ProfilingResultCallback callback, String resultFile, 2238 long keyMostSigBits, long keyLeastSigBits, int status, String tag, 2239 boolean errorExpected) { 2240 assertEquals(resultFile, callback.mResultFile); 2241 assertEquals(keyMostSigBits, callback.mKeyMostSigBits); 2242 assertEquals(keyLeastSigBits, callback.mKeyLeastSigBits); 2243 assertEquals(status, callback.mStatus); 2244 assertEquals(tag, callback.mTag); 2245 if (errorExpected) { 2246 assertNotNull(callback.mError); 2247 } else { 2248 assertNull(callback.mError); 2249 } 2250 } 2251 2252 /** 2253 * Update the provided device config value and wait for up to 2 seconds, checking every 100ms, 2254 * for the value change to take effect. 2255 */ updateDeviceConfigAndWaitForChange(String namespace, String config, int newValue)2256 private void updateDeviceConfigAndWaitForChange(String namespace, String config, int newValue) 2257 throws Exception { 2258 executeShellCmd(OVERRIDE_DEVICE_CONFIG_INT, namespace, config, newValue); 2259 for (int i = 0; i < 20; i++) { 2260 sleep(100); 2261 String s = executeShellCmd(GET_DEVICE_CONFIG, namespace, config); 2262 try { 2263 int val = Integer.parseInt(s.trim()); 2264 if (val == newValue) { 2265 return; 2266 } 2267 } catch (NumberFormatException e) { 2268 // Ignore and continue. 2269 } 2270 } 2271 fail("DeviceConfig value never updated to match expected value."); 2272 } 2273 sleep(long ms)2274 private static void sleep(long ms) { 2275 try { 2276 Thread.sleep(ms); 2277 } catch (InterruptedException e) { 2278 // Do nothing. 2279 } 2280 } 2281 2282 public class ProfilingResultCallback extends IProfilingResultCallback.Stub { 2283 boolean mResultSent = false; 2284 boolean mFileRequested = false; 2285 public String mResultFile; 2286 public long mKeyMostSigBits; 2287 public long mKeyLeastSigBits; 2288 public int mStatus; 2289 public String mTag; 2290 public String mError; 2291 public int mTriggerType; 2292 2293 @Override sendResult(String resultFile, long keyMostSigBits, long keyLeastSigBits, int status, String tag, String error, int triggerType)2294 public void sendResult(String resultFile, long keyMostSigBits, 2295 long keyLeastSigBits, int status, String tag, String error, int triggerType) { 2296 mResultSent = true; 2297 mResultFile = resultFile; 2298 mKeyMostSigBits = keyMostSigBits; 2299 mKeyLeastSigBits = keyLeastSigBits; 2300 mStatus = status; 2301 mTag = tag; 2302 mError = error; 2303 mTriggerType = triggerType; 2304 } 2305 2306 @Override generateFile(String filePathAbsolute, String fileName, long keyMostSigBits, long keyLeastSigBits)2307 public void generateFile(String filePathAbsolute, String fileName, long keyMostSigBits, 2308 long keyLeastSigBits) { 2309 mFileRequested = true; 2310 2311 // To properly mock the flow for copy, pass null back to service via 2312 // receiveFileDescriptor. This will then fail at the copy step and the test will receive 2313 // a callback. In actual use and end-to-end testing via {@link ProfilingFrameworkTests}, 2314 // this would be done by creating a real file in {@link ProfilingManager}. If we simply 2315 // ignored this call then the test flow would not progress past the beginning of copy 2316 // attempt. 2317 mProfilingService.receiveFileDescriptor(null, keyMostSigBits, keyLeastSigBits); 2318 } 2319 2320 @Override deleteFile(String filePathAndName)2321 public void deleteFile(String filePathAndName) { 2322 // Ignore 2323 } 2324 } 2325 } 2326 2327 2328