• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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