1 /*
2  * Copyright (C) 2019 The Android Open Source Project
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 
17 package com.android.server.am;
18 
19 import static android.app.ActivityManager.PROCESS_STATE_BOUND_FOREGROUND_SERVICE;
20 import static android.app.ActivityManager.PROCESS_STATE_BOUND_TOP;
21 import static android.app.ActivityManager.PROCESS_STATE_CACHED_EMPTY;
22 import static android.app.ActivityManager.PROCESS_STATE_LAST_ACTIVITY;
23 import static android.app.ActivityManager.PROCESS_STATE_RECEIVER;
24 import static android.app.ActivityManager.RunningAppProcessInfo.IMPORTANCE_CACHED;
25 import static android.app.ActivityManager.RunningAppProcessInfo.IMPORTANCE_FOREGROUND;
26 import static android.app.ActivityManager.RunningAppProcessInfo.IMPORTANCE_FOREGROUND_SERVICE;
27 import static android.app.ActivityManager.RunningAppProcessInfo.IMPORTANCE_SERVICE;
28 
29 import static androidx.test.platform.app.InstrumentationRegistry.getInstrumentation;
30 
31 import static com.android.server.am.ActivityManagerService.Injector;
32 
33 import static org.junit.Assert.assertEquals;
34 import static org.junit.Assert.assertNotNull;
35 import static org.junit.Assert.assertNull;
36 import static org.junit.Assert.assertTrue;
37 import static org.mockito.Matchers.anyBoolean;
38 import static org.mockito.Matchers.anyInt;
39 import static org.mockito.Matchers.anyLong;
40 import static org.mockito.Mockito.doNothing;
41 import static org.mockito.Mockito.doReturn;
42 import static org.mockito.Mockito.spy;
43 
44 import android.annotation.CurrentTimeMillisLong;
45 import android.app.ApplicationExitInfo;
46 import android.content.ComponentName;
47 import android.content.Context;
48 import android.content.pm.ApplicationInfo;
49 import android.content.pm.PackageManagerInternal;
50 import android.os.Debug;
51 import android.os.FileUtils;
52 import android.os.Handler;
53 import android.os.HandlerThread;
54 import android.os.Process;
55 import android.os.UserHandle;
56 import android.platform.test.annotations.Presubmit;
57 import android.system.OsConstants;
58 import android.text.TextUtils;
59 import android.util.Pair;
60 
61 import com.android.internal.util.ArrayUtils;
62 import com.android.server.LocalServices;
63 import com.android.server.ServiceThread;
64 import com.android.server.appop.AppOpsService;
65 import com.android.server.wm.ActivityTaskManagerService;
66 
67 import org.junit.After;
68 import org.junit.Before;
69 import org.junit.BeforeClass;
70 import org.junit.Rule;
71 import org.junit.Test;
72 import org.junit.rules.TestRule;
73 import org.junit.runner.Description;
74 import org.junit.runners.model.Statement;
75 import org.mockito.Mock;
76 import org.mockito.MockitoAnnotations;
77 
78 import java.io.BufferedInputStream;
79 import java.io.BufferedOutputStream;
80 import java.io.File;
81 import java.io.FileInputStream;
82 import java.io.FileOutputStream;
83 import java.io.IOException;
84 import java.lang.reflect.Field;
85 import java.lang.reflect.Modifier;
86 import java.util.ArrayList;
87 import java.util.Random;
88 import java.util.zip.GZIPInputStream;
89 
90 /**
91  * Test class for {@link android.app.ApplicationExitInfo}.
92  *
93  * Build/Install/Run:
94  *  atest ApplicationExitInfoTest
95  */
96 @Presubmit
97 public class ApplicationExitInfoTest {
98     private static final String TAG = ApplicationExitInfoTest.class.getSimpleName();
99 
100     @Rule public ServiceThreadRule mServiceThreadRule = new ServiceThreadRule();
101     @Mock private AppOpsService mAppOpsService;
102     @Mock private PackageManagerInternal mPackageManagerInt;
103 
104     private Context mContext = getInstrumentation().getTargetContext();
105     private TestInjector mInjector;
106     private ActivityManagerService mAms;
107     private ProcessList mProcessList;
108     private AppExitInfoTracker mAppExitInfoTracker;
109     private Handler mHandler;
110     private HandlerThread mHandlerThread;
111 
112     @BeforeClass
setUpOnce()113     public static void setUpOnce() {
114         System.setProperty("dexmaker.share_classloader", "true");
115     }
116 
117     @Before
setUp()118     public void setUp() {
119         MockitoAnnotations.initMocks(this);
120 
121         mHandlerThread = new HandlerThread(TAG);
122         mHandlerThread.start();
123         mHandler = new Handler(mHandlerThread.getLooper());
124         mProcessList = spy(new ProcessList());
125         ProcessList.sKillHandler = null;
126         mAppExitInfoTracker = spy(new AppExitInfoTracker());
127         setFieldValue(AppExitInfoTracker.class, mAppExitInfoTracker, "mIsolatedUidRecords",
128                 spy(mAppExitInfoTracker.new IsolatedUidRecords()));
129         setFieldValue(AppExitInfoTracker.class, mAppExitInfoTracker, "mAppExitInfoSourceZygote",
130                 spy(mAppExitInfoTracker.new AppExitInfoExternalSource("zygote", null)));
131         setFieldValue(AppExitInfoTracker.class, mAppExitInfoTracker, "mAppExitInfoSourceLmkd",
132                 spy(mAppExitInfoTracker.new AppExitInfoExternalSource("lmkd",
133                 ApplicationExitInfo.REASON_LOW_MEMORY)));
134         setFieldValue(AppExitInfoTracker.class, mAppExitInfoTracker, "mAppTraceRetriever",
135                 spy(mAppExitInfoTracker.new AppTraceRetriever()));
136         setFieldValue(ProcessList.class, mProcessList, "mAppExitInfoTracker", mAppExitInfoTracker);
137         mInjector = new TestInjector(mContext);
138         mAms = new ActivityManagerService(mInjector, mServiceThreadRule.getThread());
139         mAms.mActivityTaskManager = new ActivityTaskManagerService(mContext);
140         mAms.mActivityTaskManager.initialize(null, null, mContext.getMainLooper());
141         mAms.mAtmInternal = spy(mAms.mActivityTaskManager.getAtmInternal());
142         mAms.mPackageManagerInt = mPackageManagerInt;
143         doReturn(new ComponentName("", "")).when(mPackageManagerInt).getSystemUiServiceComponent();
144         // Remove stale instance of PackageManagerInternal if there is any
145         LocalServices.removeServiceForTest(PackageManagerInternal.class);
146         LocalServices.addService(PackageManagerInternal.class, mPackageManagerInt);
147     }
148 
149     @After
tearDown()150     public void tearDown() {
151         LocalServices.removeServiceForTest(PackageManagerInternal.class);
152         mHandlerThread.quit();
153         ProcessList.sKillHandler = null;
154     }
155 
setFieldValue(Class clazz, Object obj, String fieldName, T val)156     private static <T> void setFieldValue(Class clazz, Object obj, String fieldName, T val) {
157         try {
158             Field field = clazz.getDeclaredField(fieldName);
159             field.setAccessible(true);
160             Field mfield = Field.class.getDeclaredField("accessFlags");
161             mfield.setAccessible(true);
162             mfield.setInt(field, mfield.getInt(field) & ~(Modifier.FINAL | Modifier.PRIVATE));
163             field.set(obj, val);
164         } catch (NoSuchFieldException | IllegalAccessException e) {
165         }
166     }
167 
updateExitInfo(ProcessRecord app, @CurrentTimeMillisLong long timestamp)168     private void updateExitInfo(ProcessRecord app, @CurrentTimeMillisLong long timestamp) {
169         ApplicationExitInfo raw = mAppExitInfoTracker.obtainRawRecord(app, timestamp);
170         mAppExitInfoTracker.handleNoteProcessDiedLocked(raw);
171         mAppExitInfoTracker.recycleRawRecord(raw);
172     }
173 
noteAppKill(ProcessRecord app, int reason, int subReason, String msg, @CurrentTimeMillisLong long timestamp)174     private void noteAppKill(ProcessRecord app, int reason, int subReason, String msg,
175             @CurrentTimeMillisLong long timestamp) {
176         ApplicationExitInfo raw = mAppExitInfoTracker.obtainRawRecord(app, timestamp);
177         raw.setReason(reason);
178         raw.setSubReason(subReason);
179         raw.setDescription(msg);
180         mAppExitInfoTracker.handleNoteAppKillLocked(raw);
181         mAppExitInfoTracker.recycleRawRecord(raw);
182     }
183 
184     @Test
testApplicationExitInfo()185     public void testApplicationExitInfo() throws Exception {
186         mAppExitInfoTracker.clearProcessExitInfo(true);
187         mAppExitInfoTracker.mAppExitInfoLoaded.set(true);
188         mAppExitInfoTracker.mProcExitStoreDir = new File(mContext.getFilesDir(),
189                 AppExitInfoTracker.APP_EXIT_STORE_DIR);
190         assertTrue(FileUtils.createDir(mAppExitInfoTracker.mProcExitStoreDir));
191         mAppExitInfoTracker.mProcExitInfoFile = new File(mAppExitInfoTracker.mProcExitStoreDir,
192                 AppExitInfoTracker.APP_EXIT_INFO_FILE);
193 
194         // Test application calls System.exit()
195         doNothing().when(mAppExitInfoTracker).schedulePersistProcessExitInfo(anyBoolean());
196         doReturn(true).when(mAppExitInfoTracker).isFresh(anyLong());
197 
198         final int app1Uid = 10123;
199         final int app1Pid1 = 12345;
200         final int app1Pid2 = 12346;
201         final int app1sPid1 = 13456;
202         final int app1DefiningUid = 23456;
203         final int app1ConnectiongGroup = 10;
204         final int app1UidUser2 = 1010123;
205         final int app1PidUser2 = 12347;
206         final long app1Pss1 = 34567;
207         final long app1Rss1 = 45678;
208         final long app1Pss2 = 34568;
209         final long app1Rss2 = 45679;
210         final long app1Pss3 = 34569;
211         final long app1Rss3 = 45680;
212         final long app1sPss1 = 56789;
213         final long app1sRss1 = 67890;
214         final String app1ProcessName = "com.android.test.stub1:process";
215         final String app1PackageName = "com.android.test.stub1";
216         final String app1sProcessName = "com.android.test.stub_shared:process";
217         final String app1sPackageName = "com.android.test.stub_shared";
218         final byte[] app1Cookie1 = {(byte) 0x01, (byte) 0x02, (byte) 0x03, (byte) 0x04,
219                 (byte) 0x05, (byte) 0x06, (byte) 0x07, (byte) 0x08};
220         final byte[] app1Cookie2 = {(byte) 0x08, (byte) 0x07, (byte) 0x06, (byte) 0x05,
221                 (byte) 0x04, (byte) 0x03, (byte) 0x02, (byte) 0x01};
222 
223         final long now1 = 1;
224         ProcessRecord app = makeProcessRecord(
225                 app1Pid1,                    // pid
226                 app1Uid,                     // uid
227                 app1Uid,                     // packageUid
228                 null,                        // definingUid
229                 0,                           // connectionGroup
230                 PROCESS_STATE_LAST_ACTIVITY, // procstate
231                 app1Pss1,                    // pss
232                 app1Rss1,                    // rss
233                 app1ProcessName,             // processName
234                 app1PackageName);            // packageName
235 
236         // Case 1: basic System.exit() test
237         int exitCode = 5;
238         mAppExitInfoTracker.setProcessStateSummary(app1Uid, app1Pid1, app1Cookie1);
239         assertTrue(ArrayUtils.equals(mAppExitInfoTracker.getProcessStateSummary(app1Uid,
240                 app1Pid1), app1Cookie1, app1Cookie1.length));
241         doReturn(new Pair<Long, Object>(now1, Integer.valueOf(makeExitStatus(exitCode))))
242                 .when(mAppExitInfoTracker.mAppExitInfoSourceZygote)
243                 .remove(anyInt(), anyInt());
244         doReturn(null)
245                 .when(mAppExitInfoTracker.mAppExitInfoSourceLmkd)
246                 .remove(anyInt(), anyInt());
247         updateExitInfo(app, now1);
248 
249         ArrayList<ApplicationExitInfo> list = new ArrayList<ApplicationExitInfo>();
250         mAppExitInfoTracker.getExitInfo(app1PackageName, app1Uid, app1Pid1, 0, list);
251         assertEquals(1, list.size());
252 
253         ApplicationExitInfo info = list.get(0);
254 
255         verifyApplicationExitInfo(
256                 info,                                 // info
257                 now1,                                 // timestamp
258                 app1Pid1,                             // pid
259                 app1Uid,                              // uid
260                 app1Uid,                              // packageUid
261                 null,                                 // definingUid
262                 app1ProcessName,                      // processName
263                 0,                                    // connectionGroup
264                 ApplicationExitInfo.REASON_EXIT_SELF, // reason
265                 null,                                 // subReason
266                 exitCode,                             // status
267                 app1Pss1,                             // pss
268                 app1Rss1,                             // rss
269                 IMPORTANCE_CACHED,                    // importance
270                 null);                                // description
271 
272         assertTrue(ArrayUtils.equals(info.getProcessStateSummary(), app1Cookie1,
273                 app1Cookie1.length));
274         assertEquals(info.getTraceInputStream(), null);
275 
276         // Now create a process record from a different package but shared UID.
277         sleep(1);
278         final long now1s = System.currentTimeMillis();
279         app = makeProcessRecord(
280                 app1sPid1,                   // pid
281                 app1Uid,                     // uid
282                 app1Uid,                     // packageUid
283                 null,                        // definingUid
284                 0,                           // connectionGroup
285                 PROCESS_STATE_BOUND_TOP,     // procstate
286                 app1sPss1,                   // pss
287                 app1sRss1,                   // rss
288                 app1sProcessName,            // processName
289                 app1sPackageName);           // packageName
290         doReturn(new Pair<Long, Object>(now1s, Integer.valueOf(0)))
291                 .when(mAppExitInfoTracker.mAppExitInfoSourceZygote)
292                 .remove(anyInt(), anyInt());
293         doReturn(null)
294                 .when(mAppExitInfoTracker.mAppExitInfoSourceLmkd)
295                 .remove(anyInt(), anyInt());
296         noteAppKill(app, ApplicationExitInfo.REASON_USER_REQUESTED,
297                 ApplicationExitInfo.SUBREASON_UNKNOWN, null, now1s);
298 
299         // Case 2: create another app1 process record with a different pid
300         sleep(1);
301         final long now2 = 2;
302         app = makeProcessRecord(
303                 app1Pid2,               // pid
304                 app1Uid,                // uid
305                 app1Uid,                // packageUid
306                 app1DefiningUid,        // definingUid
307                 app1ConnectiongGroup,   // connectionGroup
308                 PROCESS_STATE_RECEIVER, // procstate
309                 app1Pss2,               // pss
310                 app1Rss2,               // rss
311                 app1ProcessName,        // processName
312                 app1PackageName);       // packageName
313         exitCode = 6;
314 
315         mAppExitInfoTracker.setProcessStateSummary(app1Uid, app1Pid2, app1Cookie1);
316         // Override with a different cookie
317         mAppExitInfoTracker.setProcessStateSummary(app1Uid, app1Pid2, app1Cookie2);
318         assertTrue(ArrayUtils.equals(mAppExitInfoTracker.getProcessStateSummary(app1Uid,
319                 app1Pid2), app1Cookie2, app1Cookie2.length));
320         doReturn(new Pair<Long, Object>(now2, Integer.valueOf(makeExitStatus(exitCode))))
321                 .when(mAppExitInfoTracker.mAppExitInfoSourceZygote)
322                 .remove(anyInt(), anyInt());
323         updateExitInfo(app, now2);
324         list.clear();
325 
326         // Get all the records for app1Uid
327         mAppExitInfoTracker.getExitInfo(null, app1Uid, 0, 0, list);
328         assertEquals(3, list.size());
329 
330         info = list.get(1);
331 
332         verifyApplicationExitInfo(
333                 info,                                 // info
334                 now2,                                 // timestamp
335                 app1Pid2,                             // pid
336                 app1Uid,                              // uid
337                 app1Uid,                              // packageUid
338                 app1DefiningUid,                      // definingUid
339                 app1ProcessName,                      // processName
340                 app1ConnectiongGroup,                 // connectionGroup
341                 ApplicationExitInfo.REASON_EXIT_SELF, // reason
342                 null,                                 // subReason
343                 exitCode,                             // status
344                 app1Pss2,                             // pss
345                 app1Rss2,                             // rss
346                 IMPORTANCE_SERVICE,                   // importance
347                 null);                                // description
348 
349         assertTrue(ArrayUtils.equals(info.getProcessStateSummary(), app1Cookie2,
350                 app1Cookie2.length));
351 
352         info = list.get(0);
353         verifyApplicationExitInfo(
354                 info,                                      // info
355                 now1s,                                     // timestamp
356                 app1sPid1,                                 // pid
357                 app1Uid,                                   // uid
358                 app1Uid,                                   // packageUid
359                 null,                                      // definingUid
360                 app1sProcessName,                          // processName
361                 0,                                         // connectionGroup
362                 ApplicationExitInfo.REASON_USER_REQUESTED, // reason
363                 null,                                      // subReason
364                 null,                                      // status
365                 app1sPss1,                                 // pss
366                 app1sRss1,                                 // rss
367                 IMPORTANCE_FOREGROUND,                     // importance
368                 null);                                     // description
369 
370         info = list.get(2);
371         assertTrue(ArrayUtils.equals(info.getProcessStateSummary(), app1Cookie1,
372                 app1Cookie1.length));
373 
374         // Case 3: Create an instance of app1 with different user, and died because of SIGKILL
375         sleep(1);
376         final long now3 = System.currentTimeMillis();
377         int sigNum = OsConstants.SIGKILL;
378         app = makeProcessRecord(
379                 app1PidUser2,                           // pid
380                 app1UidUser2,                           // uid
381                 app1UidUser2,                           // packageUid
382                 null,                                   // definingUid
383                 0,                                      // connectionGroup
384                 PROCESS_STATE_BOUND_FOREGROUND_SERVICE, // procstate
385                 app1Pss3,                               // pss
386                 app1Rss3,                               // rss
387                 app1ProcessName,                        // processName
388                 app1PackageName);                       // packageName
389         doReturn(new Pair<Long, Object>(now3, Integer.valueOf(makeSignalStatus(sigNum))))
390                 .when(mAppExitInfoTracker.mAppExitInfoSourceZygote)
391                 .remove(anyInt(), anyInt());
392         updateExitInfo(app, now3);
393         list.clear();
394         mAppExitInfoTracker.getExitInfo(app1PackageName, app1UidUser2, app1PidUser2, 0, list);
395 
396         assertEquals(1, list.size());
397 
398         info = list.get(0);
399 
400         verifyApplicationExitInfo(
401                 info,                                // info
402                 now3,                                // timestamp
403                 app1PidUser2,                        // pid
404                 app1UidUser2,                        // uid
405                 app1UidUser2,                        // packageUid
406                 null,                                // definingUid
407                 app1ProcessName,                     // processName
408                 0,                                   // connectionGroup
409                 ApplicationExitInfo.REASON_SIGNALED, // reason
410                 null,                                 // subReason
411                 sigNum,                              // status
412                 app1Pss3,                            // pss
413                 app1Rss3,                            // rss
414                 IMPORTANCE_FOREGROUND_SERVICE,       // importance
415                 null);                               // description
416 
417         // try go get all from app1UidUser2
418         list.clear();
419         mAppExitInfoTracker.getExitInfo(app1PackageName, app1UidUser2, 0, 0, list);
420         assertEquals(1, list.size());
421 
422         info = list.get(0);
423 
424         verifyApplicationExitInfo(
425                 info,                                // info
426                 now3,                                // timestamp
427                 app1PidUser2,                        // pid
428                 app1UidUser2,                        // uid
429                 app1UidUser2,                        // packageUid
430                 null,                                // definingUid
431                 app1ProcessName,                     // processName
432                 0,                                   // connectionGroup
433                 ApplicationExitInfo.REASON_SIGNALED, // reason
434                 null,                                // subReason
435                 sigNum,                              // status
436                 app1Pss3,                            // pss
437                 app1Rss3,                            // rss
438                 IMPORTANCE_FOREGROUND_SERVICE,       // importance
439                 null);                               // description
440 
441         // Case 4: Create a process from another package with kill from lmkd
442         final int app2UidUser2 = 1010234;
443         final int app2PidUser2 = 12348;
444         final long app2Pss1 = 54321;
445         final long app2Rss1 = 65432;
446         final String app2ProcessName = "com.android.test.stub2:process";
447         final String app2PackageName = "com.android.test.stub2";
448 
449         sleep(1);
450         final long now4 = System.currentTimeMillis();
451         doReturn(new Pair<Long, Object>(now4, Integer.valueOf(0)))
452                 .when(mAppExitInfoTracker.mAppExitInfoSourceZygote)
453                 .remove(anyInt(), anyInt());
454         doReturn(new Pair<Long, Object>(now4, null))
455                 .when(mAppExitInfoTracker.mAppExitInfoSourceLmkd)
456                 .remove(anyInt(), anyInt());
457 
458         app = makeProcessRecord(
459                 app2PidUser2,                // pid
460                 app2UidUser2,                // uid
461                 app2UidUser2,                // packageUid
462                 null,                        // definingUid
463                 0,                           // connectionGroup
464                 PROCESS_STATE_CACHED_EMPTY,  // procstate
465                 app2Pss1,                    // pss
466                 app2Rss1,                    // rss
467                 app2ProcessName,             // processName
468                 app2PackageName);            // packageName
469         updateExitInfo(app, now4);
470         list.clear();
471         mAppExitInfoTracker.getExitInfo(app2PackageName, app2UidUser2, app2PidUser2, 0, list);
472         assertEquals(1, list.size());
473 
474         info = list.get(0);
475 
476         verifyApplicationExitInfo(
477                 info,                                     // info
478                 now4,                                     // timestamp
479                 app2PidUser2,                             // pid
480                 app2UidUser2,                             // uid
481                 app2UidUser2,                             // packageUid
482                 null,                                     // definingUid
483                 app2ProcessName,                          // processName
484                 0,                                        // connectionGroup
485                 ApplicationExitInfo.REASON_LOW_MEMORY,    // reason
486                 null,                                     // subReason
487                 0,                                        // status
488                 app2Pss1,                                 // pss
489                 app2Rss1,                                 // rss
490                 IMPORTANCE_CACHED,                        // importance
491                 null);                                    // description
492 
493         // Verify to get all from User2 regarding app2
494         list.clear();
495         mAppExitInfoTracker.getExitInfo(null, app2UidUser2, 0, 0, list);
496         assertEquals(1, list.size());
497 
498         // Case 5: App native crash
499         final int app3UidUser2 = 1010345;
500         final int app3PidUser2 = 12349;
501         final int app3ConnectiongGroup = 4;
502         final long app3Pss1 = 54320;
503         final long app3Rss1 = 65430;
504         final String app3ProcessName = "com.android.test.stub3:process";
505         final String app3PackageName = "com.android.test.stub3";
506         final String app3Description = "native crash";
507 
508         sleep(1);
509         final long now5 = System.currentTimeMillis();
510         sigNum = OsConstants.SIGABRT;
511         doReturn(new Pair<Long, Object>(now5, Integer.valueOf(makeSignalStatus(sigNum))))
512                 .when(mAppExitInfoTracker.mAppExitInfoSourceZygote)
513                 .remove(anyInt(), anyInt());
514         doReturn(null)
515                 .when(mAppExitInfoTracker.mAppExitInfoSourceLmkd)
516                 .remove(anyInt(), anyInt());
517         app = makeProcessRecord(
518                 app3PidUser2,            // pid
519                 app3UidUser2,            // uid
520                 app3UidUser2,            // packageUid
521                 null,                    // definingUid
522                 app3ConnectiongGroup,    // connectionGroup
523                 PROCESS_STATE_BOUND_TOP, // procstate
524                 app3Pss1,                // pss
525                 app3Rss1,                // rss
526                 app3ProcessName,         // processName
527                 app3PackageName);        // packageName
528         noteAppKill(app, ApplicationExitInfo.REASON_CRASH_NATIVE,
529                 ApplicationExitInfo.SUBREASON_UNKNOWN, app3Description, now5);
530 
531         updateExitInfo(app, now5);
532         list.clear();
533         mAppExitInfoTracker.getExitInfo(app3PackageName, app3UidUser2, app3PidUser2, 0, list);
534         assertEquals(1, list.size());
535 
536         info = list.get(0);
537 
538         verifyApplicationExitInfo(
539                 info,                                            // info
540                 now5,                                            // timestamp
541                 app3PidUser2,                                    // pid
542                 app3UidUser2,                                    // uid
543                 app3UidUser2,                                    // packageUid
544                 null,                                            // definingUid
545                 app3ProcessName,                                 // processName
546                 app3ConnectiongGroup,                            // connectionGroup
547                 ApplicationExitInfo.REASON_CRASH_NATIVE,         // reason
548                 null,                                            // subReason
549                 sigNum,                                          // status
550                 app3Pss1,                                        // pss
551                 app3Rss1,                                        // rss
552                 IMPORTANCE_FOREGROUND,                           // importance
553                 app3Description);                                // description
554 
555         // Verify the most recent kills, sorted by timestamp
556         int maxNum = 3;
557         list.clear();
558         mAppExitInfoTracker.getExitInfo(null, app3UidUser2, 0, maxNum, list);
559         assertEquals(1, list.size());
560 
561         info = list.get(0);
562 
563         verifyApplicationExitInfo(
564                 info,                                            // info
565                 now5,                                            // timestamp
566                 app3PidUser2,                                    // pid
567                 app3UidUser2,                                    // uid
568                 app3UidUser2,                                    // packageUid
569                 null,                                            // definingUid
570                 app3ProcessName,                                 // processName
571                 app3ConnectiongGroup,                            // connectionGroup
572                 ApplicationExitInfo.REASON_CRASH_NATIVE,         // reason
573                 null,                                            // subReason
574                 sigNum,                                          // status
575                 app3Pss1,                                        // pss
576                 app3Rss1,                                        // rss
577                 IMPORTANCE_FOREGROUND,                           // importance
578                 app3Description);                                // description
579 
580         list.clear();
581         mAppExitInfoTracker.getExitInfo(null, app2UidUser2, 0, maxNum, list);
582         assertEquals(1, list.size());
583         info = list.get(0);
584 
585         verifyApplicationExitInfo(
586                 info,                                     // info
587                 now4,                                     // timestamp
588                 app2PidUser2,                             // pid
589                 app2UidUser2,                             // uid
590                 app2UidUser2,                             // packageUid
591                 null,                                     // definingUid
592                 app2ProcessName,                          // processName
593                 0,                                        // connectionGroup
594                 ApplicationExitInfo.REASON_LOW_MEMORY,    // reason
595                 null,                                     // subReason
596                 0,                                        // status
597                 app2Pss1,                                 // pss
598                 app2Rss1,                                 // rss
599                 IMPORTANCE_CACHED,                        // importance
600                 null);                                    // description
601 
602         list.clear();
603         mAppExitInfoTracker.getExitInfo(null, app1UidUser2, 0, maxNum, list);
604         assertEquals(1, list.size());
605         info = list.get(0);
606 
607         sigNum = OsConstants.SIGKILL;
608         verifyApplicationExitInfo(
609                 info,                                // info
610                 now3,                                // timestamp
611                 app1PidUser2,                        // pid
612                 app1UidUser2,                        // uid
613                 app1UidUser2,                        // packageUid
614                 null,                                // definingUid
615                 app1ProcessName,                     // processName
616                 0,                                   // connectionGroup
617                 ApplicationExitInfo.REASON_SIGNALED, // reason
618                 null,                                // subReason
619                 sigNum,                              // status
620                 app1Pss3,                            // pss
621                 app1Rss3,                            // rss
622                 IMPORTANCE_FOREGROUND_SERVICE,       // importance
623                 null);                               // description
624 
625         // Case 6: App Java crash
626         final int app3Uid = 10345;
627         final int app3IsolatedUid = 99001; // it's an isolated process
628         final int app3Pid = 12350;
629         final long app3Pss2 = 23232;
630         final long app3Rss2 = 32323;
631         final String app3Description2 = "force close";
632 
633         sleep(1);
634         final long now6 = System.currentTimeMillis();
635         doReturn(null)
636                 .when(mAppExitInfoTracker.mAppExitInfoSourceZygote)
637                 .remove(anyInt(), anyInt());
638         doReturn(null)
639                 .when(mAppExitInfoTracker.mAppExitInfoSourceLmkd)
640                 .remove(anyInt(), anyInt());
641         app = makeProcessRecord(
642                 app3Pid,                     // pid
643                 app3IsolatedUid,             // uid
644                 app3Uid,                     // packageUid
645                 null,                        // definingUid
646                 0,                           // connectionGroup
647                 PROCESS_STATE_CACHED_EMPTY,  // procstate
648                 app3Pss2,                    // pss
649                 app3Rss2,                    // rss
650                 app3ProcessName,             // processName
651                 app3PackageName);            // packageName
652         mAppExitInfoTracker.mIsolatedUidRecords.addIsolatedUid(app3IsolatedUid, app3Uid);
653         noteAppKill(app, ApplicationExitInfo.REASON_CRASH,
654                 ApplicationExitInfo.SUBREASON_UNKNOWN, app3Description2, now6);
655 
656         assertEquals(app3Uid, mAppExitInfoTracker.mIsolatedUidRecords
657                 .getUidByIsolatedUid(app3IsolatedUid).longValue());
658         updateExitInfo(app, now6);
659         assertNull(mAppExitInfoTracker.mIsolatedUidRecords.getUidByIsolatedUid(app3IsolatedUid));
660 
661         list.clear();
662         mAppExitInfoTracker.getExitInfo(app3PackageName, app3Uid, 0, 1, list);
663         assertEquals(1, list.size());
664 
665         info = list.get(0);
666 
667         verifyApplicationExitInfo(
668                 info,                                     // info
669                 now6,                                     // timestamp
670                 app3Pid,                                  // pid
671                 app3IsolatedUid,                          // uid
672                 app3Uid,                                  // packageUid
673                 null,                                     // definingUid
674                 app3ProcessName,                          // processName
675                 0,                                        // connectionGroup
676                 ApplicationExitInfo.REASON_CRASH,         // reason
677                 null,                                     // subReason
678                 0,                                        // status
679                 app3Pss2,                                 // pss
680                 app3Rss2,                                 // rss
681                 IMPORTANCE_CACHED,                        // importance
682                 app3Description2);                        // description
683 
684         // Case 7: App1 is "uninstalled" from User2
685         mAppExitInfoTracker.onPackageRemoved(app1PackageName, app1UidUser2, false);
686         list.clear();
687         mAppExitInfoTracker.getExitInfo(app1PackageName, app1UidUser2, 0, 0, list);
688         assertEquals(0, list.size());
689 
690         list.clear();
691         mAppExitInfoTracker.getExitInfo(app1PackageName, app1Uid, 0, 0, list);
692         assertEquals(2, list.size());
693 
694         info = list.get(0);
695 
696         verifyApplicationExitInfo(
697                 info,                                 // info
698                 now2,                                 // timestamp
699                 app1Pid2,                             // pid
700                 app1Uid,                              // uid
701                 app1Uid,                              // packageUid
702                 app1DefiningUid,                      // definingUid
703                 app1ProcessName,                      // processName
704                 app1ConnectiongGroup,                 // connectionGroup
705                 ApplicationExitInfo.REASON_EXIT_SELF, // reason
706                 null,                                 // subReason
707                 exitCode,                             // status
708                 app1Pss2,                             // pss
709                 app1Rss2,                             // rss
710                 IMPORTANCE_SERVICE,                   // importance
711                 null);                                // description
712 
713         // Case 8: App1 gets "remove task"
714         sleep(1);
715         final int app1IsolatedUidUser2 = 1099002; // isolated uid
716         final long app1Pss4 = 34343;
717         final long app1Rss4 = 43434;
718         final long now8 = System.currentTimeMillis();
719         sigNum = OsConstants.SIGKILL;
720         doReturn(new Pair<Long, Object>(now8, makeSignalStatus(sigNum)))
721                 .when(mAppExitInfoTracker.mAppExitInfoSourceZygote)
722                 .remove(anyInt(), anyInt());
723         doReturn(null)
724                 .when(mAppExitInfoTracker.mAppExitInfoSourceLmkd)
725                 .remove(anyInt(), anyInt());
726         app = makeProcessRecord(
727                 app1PidUser2,                 // pid
728                 app1IsolatedUidUser2,         // uid
729                 app1UidUser2,                 // packageUid
730                 null,                         // definingUid
731                 0,                            // connectionGroup
732                 PROCESS_STATE_CACHED_EMPTY,   // procstate
733                 app1Pss4,                     // pss
734                 app1Rss4,                     // rss
735                 app1ProcessName,              // processName
736                 app1PackageName);             // packageName
737 
738         mAppExitInfoTracker.mIsolatedUidRecords.addIsolatedUid(app1IsolatedUidUser2, app1UidUser2);
739         noteAppKill(app, ApplicationExitInfo.REASON_OTHER,
740                 ApplicationExitInfo.SUBREASON_REMOVE_TASK, null, now8);
741 
742         updateExitInfo(app, now8);
743         list.clear();
744         mAppExitInfoTracker.getExitInfo(app1PackageName, app1UidUser2, app1PidUser2, 1, list);
745         assertEquals(1, list.size());
746 
747         info = list.get(0);
748 
749         verifyApplicationExitInfo(
750                 info,                                       // info
751                 now8,                                       // timestamp
752                 app1PidUser2,                               // pid
753                 app1IsolatedUidUser2,                       // uid
754                 app1UidUser2,                               // packageUid
755                 null,                                       // definingUid
756                 app1ProcessName,                            // processName
757                 0,                                          // connectionGroup
758                 ApplicationExitInfo.REASON_OTHER,           // reason
759                 ApplicationExitInfo.SUBREASON_REMOVE_TASK,  // subReason
760                 0,                                          // status
761                 app1Pss4,                                   // pss
762                 app1Rss4,                                   // rss
763                 IMPORTANCE_CACHED,                          // importance
764                 null);                                      // description
765 
766         // App1 gets "too many empty"
767         final String app1Description2 = "too many empty";
768         sleep(1);
769         final int app1Pid2User2 = 56565;
770         final int app1IsolatedUid2User2 = 1099003; // isolated uid
771         final long app1Pss5 = 34344;
772         final long app1Rss5 = 43435;
773         final long now9 = System.currentTimeMillis();
774         sigNum = OsConstants.SIGKILL;
775         doReturn(new Pair<Long, Object>(now9, makeSignalStatus(sigNum)))
776                 .when(mAppExitInfoTracker.mAppExitInfoSourceZygote)
777                 .remove(anyInt(), anyInt());
778         doReturn(null)
779                 .when(mAppExitInfoTracker.mAppExitInfoSourceLmkd)
780                 .remove(anyInt(), anyInt());
781         app = makeProcessRecord(
782                 app1Pid2User2,                // pid
783                 app1IsolatedUid2User2,        // uid
784                 app1UidUser2,                 // packageUid
785                 null,                         // definingUid
786                 0,                            // connectionGroup
787                 PROCESS_STATE_CACHED_EMPTY,   // procstate
788                 app1Pss5,                     // pss
789                 app1Rss5,                     // rss
790                 app1ProcessName,              // processName
791                 app1PackageName);             // packageName
792 
793         mAppExitInfoTracker.mIsolatedUidRecords.addIsolatedUid(app1IsolatedUid2User2, app1UidUser2);
794 
795         // Pretent it gets an ANR trace too (although the reason here should be REASON_ANR)
796         final File traceFile = new File(mContext.getFilesDir(), "anr_original.txt");
797         final int traceSize = 10240;
798         final int traceStart = 1024;
799         final int traceEnd = 8192;
800         createRandomFile(traceFile, traceSize);
801         assertEquals(traceSize, traceFile.length());
802         mAppExitInfoTracker.handleLogAnrTrace(app.getPid(), app.uid, app.getPackageList(),
803                 traceFile, traceStart, traceEnd);
804 
805         noteAppKill(app, ApplicationExitInfo.REASON_OTHER,
806                 ApplicationExitInfo.SUBREASON_TOO_MANY_EMPTY, app1Description2, now9);
807         updateExitInfo(app, now9);
808         list.clear();
809         mAppExitInfoTracker.getExitInfo(app1PackageName, app1UidUser2, app1Pid2User2, 1, list);
810         assertEquals(1, list.size());
811 
812         info = list.get(0);
813 
814         verifyApplicationExitInfo(
815                 info,                                         // info
816                 now9,                                         // timestamp
817                 app1Pid2User2,                                // pid
818                 app1IsolatedUid2User2,                        // uid
819                 app1UidUser2,                                 // packageUid
820                 null,                                         // definingUid
821                 app1ProcessName,                              // processName
822                 0,                                            // connectionGroup
823                 ApplicationExitInfo.REASON_OTHER,             // reason
824                 ApplicationExitInfo.SUBREASON_TOO_MANY_EMPTY, // subReason
825                 0,                                            // status
826                 app1Pss5,                                     // pss
827                 app1Rss5,                                     // rss
828                 IMPORTANCE_CACHED,                            // importance
829                 app1Description2);                            // description
830 
831         // Verify if the traceFile get copied into the records correctly.
832         verifyTraceFile(traceFile, traceStart, info.getTraceFile(), 0, traceEnd - traceStart);
833         traceFile.delete();
834         info.getTraceFile().delete();
835 
836         // Case 9: User2 gets removed
837         sleep(1);
838         mAppExitInfoTracker.mIsolatedUidRecords.addIsolatedUid(app1IsolatedUidUser2, app1UidUser2);
839         mAppExitInfoTracker.mIsolatedUidRecords.addIsolatedUid(app3IsolatedUid, app3Uid);
840 
841         mAppExitInfoTracker.onUserRemoved(UserHandle.getUserId(app1UidUser2));
842 
843         assertNull(mAppExitInfoTracker.mIsolatedUidRecords.getUidByIsolatedUid(
844                 app1IsolatedUidUser2));
845         assertNotNull(mAppExitInfoTracker.mIsolatedUidRecords.getUidByIsolatedUid(
846                 app3IsolatedUid));
847         mAppExitInfoTracker.mIsolatedUidRecords.addIsolatedUid(
848                 app1IsolatedUidUser2, app1UidUser2);
849         mAppExitInfoTracker.mIsolatedUidRecords.removeAppUid(app1UidUser2, false);
850         assertNull(mAppExitInfoTracker.mIsolatedUidRecords.getUidByIsolatedUid(
851                 app1IsolatedUidUser2));
852         mAppExitInfoTracker.mIsolatedUidRecords.removeAppUid(app3Uid, true);
853         assertNull(mAppExitInfoTracker.mIsolatedUidRecords.getUidByIsolatedUid(app3IsolatedUid));
854 
855         list.clear();
856         mAppExitInfoTracker.getExitInfo(null, app1UidUser2, 0, 0, list);
857         assertEquals(0, list.size());
858 
859         list.clear();
860         mAppExitInfoTracker.getExitInfo(null, app1Uid, 0, 0, list);
861         assertEquals(3, list.size());
862 
863         info = list.get(1);
864 
865         exitCode = 6;
866         verifyApplicationExitInfo(
867                 info,                                 // info
868                 now2,                                 // timestamp
869                 app1Pid2,                             // pid
870                 app1Uid,                              // uid
871                 app1Uid,                              // packageUid
872                 app1DefiningUid,                      // definingUid
873                 app1ProcessName,                      // processName
874                 app1ConnectiongGroup,                 // connectionGroup
875                 ApplicationExitInfo.REASON_EXIT_SELF, // reason
876                 null,                                 // subReason
877                 exitCode,                             // status
878                 app1Pss2,                             // pss
879                 app1Rss2,                             // rss
880                 IMPORTANCE_SERVICE,                   // importance
881                 null);                                // description
882 
883         info = list.get(0);
884         verifyApplicationExitInfo(
885                 info,                                      // info
886                 now1s,                                     // timestamp
887                 app1sPid1,                                 // pid
888                 app1Uid,                                   // uid
889                 app1Uid,                                   // packageUid
890                 null,                                      // definingUid
891                 app1sProcessName,                          // processName
892                 0,                                         // connectionGroup
893                 ApplicationExitInfo.REASON_USER_REQUESTED, // reason
894                 null,                                      // subReason
895                 null,                                      // status
896                 app1sPss1,                                 // pss
897                 app1sRss1,                                 // rss
898                 IMPORTANCE_FOREGROUND,                     // importance
899                 null);                                     // description
900 
901         info = list.get(2);
902         exitCode = 5;
903         verifyApplicationExitInfo(
904                 info,                                 // info
905                 now1,                                 // timestamp
906                 app1Pid1,                             // pid
907                 app1Uid,                              // uid
908                 app1Uid,                              // packageUid
909                 null,                                 // definingUid
910                 app1ProcessName,                      // processName
911                 0,                                    // connectionGroup
912                 ApplicationExitInfo.REASON_EXIT_SELF, // reason
913                 null,                                 // subReason
914                 exitCode,                             // status
915                 app1Pss1,                             // pss
916                 app1Rss1,                             // rss
917                 IMPORTANCE_CACHED,                    // importance
918                 null);                                // description
919 
920         // Case 10: Save the info and load them again
921         ArrayList<ApplicationExitInfo> original = new ArrayList<ApplicationExitInfo>();
922         mAppExitInfoTracker.getExitInfo(null, app1Uid, 0, 0, original);
923         assertTrue(original.size() > 0);
924 
925         mAppExitInfoTracker.persistProcessExitInfo();
926         assertTrue(mAppExitInfoTracker.mProcExitInfoFile.exists());
927 
928         mAppExitInfoTracker.clearProcessExitInfo(false);
929         list.clear();
930         mAppExitInfoTracker.getExitInfo(null, app1Uid, 0, 0, list);
931         assertEquals(0, list.size());
932 
933         mAppExitInfoTracker.loadExistingProcessExitInfo();
934         list.clear();
935         mAppExitInfoTracker.getExitInfo(null, app1Uid, 0, 0, list);
936         assertEquals(original.size(), list.size());
937 
938         for (int i = list.size() - 1; i >= 0; i--) {
939             assertTrue(list.get(i).equals(original.get(i)));
940         }
941     }
942 
makeExitStatus(int exitCode)943     private static int makeExitStatus(int exitCode) {
944         return (exitCode << 8) & 0xff00;
945     }
946 
makeSignalStatus(int sigNum)947     private static int makeSignalStatus(int sigNum) {
948         return sigNum & 0x7f;
949     }
950 
sleep(long ms)951     private void sleep(long ms) {
952         try {
953             Thread.sleep(ms);
954         } catch (InterruptedException e) {
955         }
956     }
957 
createRandomFile(File file, int size)958     private static void createRandomFile(File file, int size) throws IOException {
959         try (BufferedOutputStream out = new BufferedOutputStream(new FileOutputStream(file))) {
960             Random random = new Random();
961             byte[] buf = random.ints('a', 'z').limit(size).collect(
962                     StringBuilder::new, StringBuilder::appendCodePoint, StringBuilder::append)
963                     .toString().getBytes();
964             out.write(buf);
965         }
966     }
967 
verifyTraceFile(File originFile, int originStart, File traceFile, int traceStart, int length)968     private static void verifyTraceFile(File originFile, int originStart, File traceFile,
969             int traceStart, int length) throws IOException {
970         assertTrue(originFile.exists());
971         assertTrue(traceFile.exists());
972         assertTrue(originStart < originFile.length());
973         try (GZIPInputStream traceIn = new GZIPInputStream(new FileInputStream(traceFile));
974             BufferedInputStream originIn = new BufferedInputStream(
975                     new FileInputStream(originFile))) {
976             assertEquals(traceStart, traceIn.skip(traceStart));
977             assertEquals(originStart, originIn.skip(originStart));
978             byte[] buf1 = new byte[8192];
979             byte[] buf2 = new byte[8192];
980             while (length > 0) {
981                 int len = traceIn.read(buf1, 0, Math.min(buf1.length, length));
982                 assertEquals(len, originIn.read(buf2, 0, len));
983                 assertTrue(ArrayUtils.equals(buf1, buf2, len));
984                 length -= len;
985             }
986         }
987     }
988 
makeProcessRecord(int pid, int uid, int packageUid, Integer definingUid, int connectionGroup, int procState, long pss, long rss, String processName, String packageName)989     private ProcessRecord makeProcessRecord(int pid, int uid, int packageUid, Integer definingUid,
990             int connectionGroup, int procState, long pss, long rss,
991             String processName, String packageName) {
992         return makeProcessRecord(pid, uid, packageUid, definingUid, connectionGroup,
993                 procState, pss, rss, processName, packageName, mAms);
994     }
995 
makeProcessRecord(int pid, int uid, int packageUid, Integer definingUid, int connectionGroup, int procState, long pss, long rss, String processName, String packageName, ActivityManagerService ams)996     static ProcessRecord makeProcessRecord(int pid, int uid, int packageUid, Integer definingUid,
997             int connectionGroup, int procState, long pss, long rss,
998             String processName, String packageName, ActivityManagerService ams) {
999         ApplicationInfo ai = new ApplicationInfo();
1000         ai.packageName = packageName;
1001         ProcessRecord app = new ProcessRecord(ams, ai, processName, uid);
1002         app.setPid(pid);
1003         app.info.uid = packageUid;
1004         if (definingUid != null) {
1005             final String dummyPackageName = "com.android.test";
1006             final String dummyClassName = ".Foo";
1007             app.setHostingRecord(HostingRecord.byAppZygote(new ComponentName(
1008                     dummyPackageName, dummyClassName), "", definingUid, ""));
1009         }
1010         app.mServices.setConnectionGroup(connectionGroup);
1011         app.mState.setReportedProcState(procState);
1012         app.mProfile.setLastMemInfo(spy(new Debug.MemoryInfo()));
1013         app.mProfile.setLastPss(pss);
1014         app.mProfile.setLastRss(rss);
1015         return app;
1016     }
1017 
verifyApplicationExitInfo(ApplicationExitInfo info, Long timestamp, Integer pid, Integer uid, Integer packageUid, Integer definingUid, String processName, Integer connectionGroup, Integer reason, Integer subReason, Integer status, Long pss, Long rss, Integer importance, String description)1018     private void verifyApplicationExitInfo(ApplicationExitInfo info,
1019             Long timestamp, Integer pid, Integer uid, Integer packageUid,
1020             Integer definingUid, String processName, Integer connectionGroup,
1021             Integer reason, Integer subReason, Integer status,
1022             Long pss, Long rss, Integer importance, String description) {
1023         assertNotNull(info);
1024 
1025         if (timestamp != null) {
1026             final long tolerance = 10000; // ms
1027             assertTrue(timestamp - tolerance <= info.getTimestamp());
1028             assertTrue(timestamp + tolerance >= info.getTimestamp());
1029         }
1030         if (pid != null) {
1031             assertEquals(pid.intValue(), info.getPid());
1032         }
1033         if (uid != null) {
1034             assertEquals(uid.intValue(), info.getRealUid());
1035         }
1036         if (packageUid != null) {
1037             assertEquals(packageUid.intValue(), info.getPackageUid());
1038         }
1039         if (definingUid != null) {
1040             assertEquals(definingUid.intValue(), info.getDefiningUid());
1041         }
1042         if (processName != null) {
1043             assertTrue(TextUtils.equals(processName, info.getProcessName()));
1044         }
1045         if (connectionGroup != null) {
1046             assertEquals(connectionGroup.intValue(), info.getConnectionGroup());
1047         }
1048         if (reason != null) {
1049             assertEquals(reason.intValue(), info.getReason());
1050         }
1051         if (subReason != null) {
1052             assertEquals(subReason.intValue(), info.getSubReason());
1053         }
1054         if (status != null) {
1055             assertEquals(status.intValue(), info.getStatus());
1056         }
1057         if (pss != null) {
1058             assertEquals(pss.longValue(), info.getPss());
1059         }
1060         if (rss != null) {
1061             assertEquals(rss.longValue(), info.getRss());
1062         }
1063         if (importance != null) {
1064             assertEquals(importance.intValue(), info.getImportance());
1065         }
1066 
1067         // info.getDescription returns a combination of subReason & description
1068         if ((subReason != null) && (subReason != ApplicationExitInfo.SUBREASON_UNKNOWN)
1069                 && (description != null)) {
1070             assertTrue(TextUtils.equals(
1071                     "[" + info.subreasonToString(subReason) + "] " + description,
1072                     info.getDescription()));
1073         } else if ((subReason != null) && (subReason != ApplicationExitInfo.SUBREASON_UNKNOWN)) {
1074             assertTrue(TextUtils.equals(
1075                     "[" + info.subreasonToString(subReason) + "]",
1076                     info.getDescription()));
1077         } else if (description != null) {
1078             assertTrue(TextUtils.equals(description, info.getDescription()));
1079         }
1080     }
1081 
1082     private class TestInjector extends Injector {
TestInjector(Context context)1083         TestInjector(Context context) {
1084             super(context);
1085         }
1086 
1087         @Override
getAppOpsService(File recentAccessesFile, File storageFile, Handler handler)1088         public AppOpsService getAppOpsService(File recentAccessesFile, File storageFile,
1089                 Handler handler) {
1090             return mAppOpsService;
1091         }
1092 
1093         @Override
getUiHandler(ActivityManagerService service)1094         public Handler getUiHandler(ActivityManagerService service) {
1095             return mHandler;
1096         }
1097 
1098         @Override
getProcessList(ActivityManagerService service)1099         public ProcessList getProcessList(ActivityManagerService service) {
1100             return mProcessList;
1101         }
1102     }
1103 
1104     static class ServiceThreadRule implements TestRule {
1105 
1106         private ServiceThread mThread;
1107 
getThread()1108         ServiceThread getThread() {
1109             return mThread;
1110         }
1111 
1112         @Override
apply(Statement base, Description description)1113         public Statement apply(Statement base, Description description) {
1114             return new Statement() {
1115                 @Override
1116                 public void evaluate() throws Throwable {
1117                     mThread = new ServiceThread("TestServiceThread",
1118                             Process.THREAD_PRIORITY_DEFAULT, true /* allowIo */);
1119                     mThread.start();
1120                     try {
1121                         base.evaluate();
1122                     } finally {
1123                         mThread.getThreadHandler().runWithScissors(mThread::quit, 0 /* timeout */);
1124                     }
1125                 }
1126             };
1127         }
1128     }
1129 
1130     // TODO: [b/302724778] Remove manual JNI load
1131     static {
1132         System.loadLibrary("mockingservicestestjni");
1133     }
1134 }
1135