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 android.app.cts;
18 
19 import static com.android.compatibility.common.util.SystemUtil.runWithShellPermissionIdentity;
20 
21 import static com.google.common.truth.Truth.assertWithMessage;
22 
23 import static org.junit.Assert.assertEquals;
24 import static org.junit.Assert.assertFalse;
25 import static org.junit.Assert.assertNotNull;
26 import static org.junit.Assert.assertTrue;
27 import static org.junit.Assert.fail;
28 import static org.junit.Assume.assumeTrue;
29 
30 import android.Manifest;
31 import android.app.Activity;
32 import android.app.ActivityManager;
33 import android.app.ActivityManager.RunningAppProcessInfo;
34 import android.app.ApplicationExitInfo;
35 import android.app.Instrumentation;
36 import android.app.cts.android.app.cts.tools.WatchUidRunner;
37 import android.app.stubs.IHeartbeat;
38 import android.content.BroadcastReceiver;
39 import android.content.ComponentName;
40 import android.content.Context;
41 import android.content.Intent;
42 import android.content.IntentFilter;
43 import android.content.ServiceConnection;
44 import android.content.pm.PackageManager;
45 import android.externalservice.common.RunningServiceInfo;
46 import android.externalservice.common.ServiceMessages;
47 import android.os.AsyncTask;
48 import android.os.Binder;
49 import android.os.Build;
50 import android.os.Bundle;
51 import android.os.DropBoxManager;
52 import android.os.Flags;
53 import android.os.Handler;
54 import android.os.HandlerThread;
55 import android.os.IBinder;
56 import android.os.Looper;
57 import android.os.Message;
58 import android.os.Messenger;
59 import android.os.Process;
60 import android.os.RemoteException;
61 import android.os.SystemClock;
62 import android.os.SystemProperties;
63 import android.os.UserHandle;
64 import android.os.UserManager;
65 import android.provider.Settings;
66 import android.server.wm.settings.SettingsSession;
67 import android.system.OsConstants;
68 import android.text.TextUtils;
69 import android.util.DebugUtils;
70 import android.util.Log;
71 import android.util.Pair;
72 
73 import androidx.test.ext.junit.runners.AndroidJUnit4;
74 import androidx.test.platform.app.InstrumentationRegistry;
75 
76 import com.android.compatibility.common.util.AmMonitor;
77 import com.android.compatibility.common.util.ApiLevelUtil;
78 import com.android.compatibility.common.util.CddTest;
79 import com.android.compatibility.common.util.PollingCheck;
80 import com.android.compatibility.common.util.ShellIdentityUtils;
81 import com.android.compatibility.common.util.SystemUtil;
82 import com.android.internal.util.ArrayUtils;
83 import com.android.internal.util.MemInfoReader;
84 import com.android.server.os.TombstoneProtos.Tombstone;
85 
86 import org.junit.After;
87 import org.junit.Before;
88 import org.junit.Test;
89 import org.junit.runner.RunWith;
90 
91 import java.io.BufferedInputStream;
92 import java.io.IOException;
93 import java.io.InputStream;
94 import java.util.ArrayList;
95 import java.util.List;
96 import java.util.concurrent.CountDownLatch;
97 import java.util.concurrent.TimeUnit;
98 import java.util.regex.Matcher;
99 import java.util.regex.Pattern;
100 
101 @RunWith(AndroidJUnit4.class)
102 public final class ActivityManagerAppExitInfoTest {
103     private static final String TAG = ActivityManagerAppExitInfoTest.class.getSimpleName();
104 
105     public static final boolean FIRST_SDK_IS_AT_LEAST_U =
106             ApiLevelUtil.isFirstApiAfter(Build.VERSION_CODES.TIRAMISU);
107 
108     private static final String STUB_PACKAGE_NAME =
109             "com.android.cts.launcherapps.simpleapp";
110     private static final String STUB_SERVICE_NAME =
111             "com.android.cts.launcherapps.simpleapp.SimpleService4";
112     private static final String STUB_SERVICE_REMOTE_NAME =
113             "com.android.cts.launcherapps.simpleapp.SimpleService5";
114     private static final String STUB_SERVICE_ISOLATED_NAME =
115             "com.android.cts.launcherapps.simpleapp.SimpleService6";
116     private static final String STUB_RECEIVER_NAME =
117             "com.android.cts.launcherapps.simpleapp.SimpleReceiver";
118     private static final String STUB_PROCESS_NAME = STUB_PACKAGE_NAME;
119     private static final String STUB_REMOTE_PROCESS_NAME = STUB_PROCESS_NAME + ":remote";
120     private static final String SIMPLE_ACTIVITY = ".SimpleActivity";
121 
122     private static final String HEARTBEAT_PACKAGE = "android.app.stubs";
123     private static final String HEARTBEAT_PROCESS = HEARTBEAT_PACKAGE + ":hbact";
124     private static final String HEARTBEAT_ACTIVITY = HEARTBEAT_PACKAGE + ".HeartbeatActivity";
125     private static final String HEARTBEAT_SERVICE = HEARTBEAT_PACKAGE + ".HeartbeatService";
126     private static final String HEARTBEAT_PROCESS_DEAD = "dead";
127     private static final String HEARTBEAT_COUNTDOWN_NAME = "countdown";
128     private static final String HEARTBEAT_INTERVAL_NAME = "interval";
129     private static final int HEARTBEAT_COUNTDOWN = 15;
130     private static final long HEARTBEAT_INTERVAL = 1000;
131     private static final long HEARTBEAT_FREEZER_LONG = 30000;
132     private static final long HEARTBEAT_FREEZER_SHORT = 5000;
133     private static final long FREEZER_TIMEOUT_FLOOR = 10000;
134 
135     private static final String EXIT_ACTION =
136             "com.android.cts.launchertests.simpleapp.EXIT_ACTION";
137     private static final String EXTRA_ACTION = "action";
138     private static final String EXTRA_MESSENGER = "messenger";
139     private static final String EXTRA_PROCESS_NAME = "process";
140     private static final String EXTRA_COOKIE = "cookie";
141 
142     private static final int ACTION_NONE = 0;
143     private static final int ACTION_FINISH = 1;
144     private static final int ACTION_EXIT = 2;
145     private static final int ACTION_ANR = 3;
146     private static final int ACTION_NATIVE_CRASH = 4;
147     private static final int ACTION_KILL = 5;
148     private static final int ACTION_ACQUIRE_STABLE_PROVIDER = 6;
149     private static final int ACTION_KILL_PROVIDER = 7;
150     private static final int EXIT_CODE = 123;
151     private static final int CRASH_SIGNAL = OsConstants.SIGSEGV;
152 
153     private static final long TOMBSTONE_FETCH_TIMEOUT_MS = 10_000;
154 
155     private static final long WAITFOR_MSEC = 10000;
156     private static final long WAITFOR_SETTLE_DOWN = 2000;
157 
158     private static final int CMD_PID = 1;
159 
160     private Context mContext;
161     private Instrumentation mInstrumentation;
162     private int mStubPackageUid;
163     private int mStubPackagePid;
164     private int mStubPackageRemotePid;
165     private int mStubPackageOtherUid;
166     private int mStubPackageOtherUserPid;
167     private int mStubPackageRemoteOtherUserPid;
168     private int mStubPackageIsolatedUid;
169     private int mStubPackageIsolatedPid;
170     private String mStubPackageIsolatedProcessName;
171     private WatchUidRunner mWatcher;
172     private WatchUidRunner mOtherUidWatcher;
173     private ActivityManager mActivityManager;
174     private CountDownLatch mLatch;
175     private UserManager mUserManager;
176     private HandlerThread mHandlerThread;
177     private Handler mHandler;
178     private Messenger mMessenger;
179     private boolean mSupportMultipleUsers;
180     private int mCurrentUserId;
181     private UserHandle mCurrentUserHandle;
182     private int mOtherUserId;
183     private UserHandle mOtherUserHandle;
184     private DropBoxManager.Entry mAnrEntry;
185     private SettingsSession<String> mDataAnrSettings;
186     private SettingsSession<String> mHiddenApiSettings;
187     private int mProcSeqNum;
188     private String mFreezerTimeout;
189     private boolean mIsProfilingPss;
190     private boolean mHeartbeatDead;
191 
192     @Before
setUp()193     public void setUp() throws Exception {
194         mInstrumentation = InstrumentationRegistry.getInstrumentation();
195         mContext = mInstrumentation.getContext();
196         mStubPackageUid = mContext.getPackageManager().getPackageUid(STUB_PACKAGE_NAME, 0);
197         mWatcher = new WatchUidRunner(mInstrumentation, mStubPackageUid, WAITFOR_MSEC);
198         mActivityManager = mContext.getSystemService(ActivityManager.class);
199         mUserManager = UserManager.get(mContext);
200         mCurrentUserId = UserHandle.getUserId(Process.myUid());
201         mCurrentUserHandle = Process.myUserHandle();
202         mSupportMultipleUsers = mUserManager.supportsMultipleUsers();
203         mHandlerThread = new HandlerThread("receiver");
204         mHandlerThread.start();
205         mHandler = new H(mHandlerThread.getLooper());
206         mMessenger = new Messenger(mHandler);
207         executeShellCmd("cmd deviceidle whitelist +" + STUB_PACKAGE_NAME);
208         executeShellCmd("cmd deviceidle whitelist +" + HEARTBEAT_PACKAGE);
209         mDataAnrSettings = new SettingsSession<>(
210                 Settings.Global.getUriFor(
211                         Settings.Global.DROPBOX_TAG_PREFIX + "data_app_anr"),
212                 Settings.Global::getString, Settings.Global::putString);
213         mDataAnrSettings.set("enabled");
214         mHiddenApiSettings = new SettingsSession<>(
215                 Settings.Global.getUriFor(
216                         Settings.Global.HIDDEN_API_BLACKLIST_EXEMPTIONS),
217                 Settings.Global::getString, Settings.Global::putString);
218         mHiddenApiSettings.set("*");
219         mFreezerTimeout = executeShellCmd(
220                 "device_config get activity_manager_native_boot freeze_debounce_timeout");
221         mIsProfilingPss = !Flags.removeAppProfilerPssCollection()
222                 || (Settings.Global.getInt(mContext.getContentResolver(),
223                         Settings.Global.FORCE_ENABLE_PSS_PROFILING, 0) == 1);
224 
225         mInstrumentation.getUiAutomation().adoptShellPermissionIdentity(
226                 android.Manifest.permission.CHANGE_COMPONENT_ENABLED_STATE);
227         mContext.getPackageManager().setApplicationEnabledSetting(
228                 STUB_PACKAGE_NAME,
229                 PackageManager.COMPONENT_ENABLED_STATE_ENABLED,
230                 0);
231     }
232 
handleMessagePid(Message msg)233     private void handleMessagePid(Message msg) {
234         boolean didSomething = false;
235         Bundle b = (Bundle) msg.obj;
236         String processName = b.getString(EXTRA_PROCESS_NAME);
237 
238         if (STUB_PROCESS_NAME.equals(processName)) {
239             if (mOtherUserId != 0 && UserHandle.getUserId(msg.arg2) == mOtherUserId) {
240                 mStubPackageOtherUserPid = msg.arg1;
241                 assertTrue(mStubPackageOtherUserPid > 0);
242             } else {
243                 mStubPackagePid = msg.arg1;
244                 assertTrue(mStubPackagePid > 0);
245             }
246         } else if (STUB_REMOTE_PROCESS_NAME.equals(processName)) {
247             if (mOtherUserId != 0 && UserHandle.getUserId(msg.arg2) == mOtherUserId) {
248                 mStubPackageRemoteOtherUserPid = msg.arg1;
249                 assertTrue(mStubPackageRemoteOtherUserPid > 0);
250             } else {
251                 mStubPackageRemotePid = msg.arg1;
252                 assertTrue(mStubPackageRemotePid > 0);
253             }
254         } else if (HEARTBEAT_PROCESS.equals(processName)) {
255             mStubPackagePid = msg.arg1;
256             mStubPackageUid = msg.arg2;
257             mHeartbeatDead = b.getBoolean(HEARTBEAT_PROCESS_DEAD);
258             assertTrue(mStubPackagePid > 0);
259             assertTrue(mStubPackageUid > 0);
260         } else { // must be isolated process
261             mStubPackageIsolatedPid = msg.arg1;
262             mStubPackageIsolatedUid = msg.arg2;
263             mStubPackageIsolatedProcessName = processName;
264             assertTrue(mStubPackageIsolatedPid > 0);
265             assertTrue(mStubPackageIsolatedUid > 0);
266             assertNotNull(processName);
267         }
268 
269         if (mLatch != null) {
270             Log.d(TAG, "Counting down latch on message " + msg + " for process " + processName);
271             mLatch.countDown();
272         }
273     }
274 
275     private class H extends Handler {
H(Looper looper)276         H(Looper looper) {
277             super(looper);
278         }
279 
280         @Override
handleMessage(Message msg)281         public void handleMessage(Message msg) {
282             switch (msg.what) {
283                 case CMD_PID:
284                     handleMessagePid(msg);
285                     break;
286                 default:
287                     break;
288             }
289         }
290     }
291 
292     @After
tearDown()293     public void tearDown() throws Exception {
294         mWatcher.finish();
295         executeShellCmd(
296                 "device_config put activity_manager_native_boot freeze_debounce_timeout "
297                         + mFreezerTimeout);
298         executeShellCmd("cmd deviceidle whitelist -" + STUB_PACKAGE_NAME);
299         executeShellCmd("cmd deviceidle whitelist -" + HEARTBEAT_PACKAGE);
300         executeShellCmd("am force-stop " + STUB_PACKAGE_NAME);
301         executeShellCmd("am force-stop " + HEARTBEAT_PACKAGE);
302         mInstrumentation.getUiAutomation().dropShellPermissionIdentity();
303 
304         removeTestUserIfNecessary();
305         mHandlerThread.quitSafely();
306         if (mDataAnrSettings != null) {
307             mDataAnrSettings.close();
308         }
309         if (mHiddenApiSettings != null) {
310             mHiddenApiSettings.close();
311         }
312     }
313 
createUser(String name, boolean guest)314     private int createUser(String name, boolean guest) throws Exception {
315         final String output = executeShellCmd(
316                 "pm create-user " + (guest ? "--guest " : "") + name);
317         if (output.startsWith("Success")) {
318             int userId = Integer.parseInt(output.substring(output.lastIndexOf(" ")).trim());
319             Log.i(TAG, "Created user with id " + userId);
320             return userId;
321         }
322         throw new IllegalStateException(String.format("Failed to create user: %s", output));
323     }
324 
removeUser(final int userId)325     private boolean removeUser(final int userId) throws Exception {
326         final String output = executeShellCmd("pm remove-user %s", userId);
327         if (output.startsWith("Error")) {
328             Log.w(TAG, "Failed to remove user: " + output);
329             return false;
330         }
331         return true;
332     }
333 
startUser(int userId, boolean waitFlag)334     private boolean startUser(int userId, boolean waitFlag) throws Exception {
335         String cmd = "am start-user " + (waitFlag ? "-w " : "") + userId;
336 
337         final String output = executeShellCmd(cmd);
338         if (output.startsWith("Error")) {
339             Log.w(TAG, "Failed to start user: " + output);
340             return false;
341         }
342         if (waitFlag) {
343             String state = executeShellCmd("am get-started-user-state " + userId);
344             if (!state.contains("RUNNING_UNLOCKED")) {
345                 return false;
346             }
347         }
348         return true;
349     }
350 
stopUser(int userId, boolean waitFlag, boolean forceFlag)351     private boolean stopUser(int userId, boolean waitFlag, boolean forceFlag)
352             throws Exception {
353         StringBuilder cmd = new StringBuilder("am stop-user ");
354         if (waitFlag) {
355             cmd.append("-w ");
356         }
357         if (forceFlag) {
358             cmd.append("-f ");
359         }
360         cmd.append(userId);
361 
362         final String output = executeShellCmd(cmd.toString());
363         if (output.contains("Error: Can't stop system user")) {
364             return false;
365         }
366         return true;
367     }
368 
installExistingPackageAsUser(String packageName, int userId)369     private void installExistingPackageAsUser(String packageName, int userId)
370             throws Exception {
371 
372         // Makes sure package doesn't exist yet, otherwise pm install will hang
373         assertWithMessage("package %s for user %s exists", packageName, userId)
374                 .that(isPackageInstalledAsUser(packageName, userId)).isFalse();
375 
376         Log.i(TAG, "installing existing " + packageName + " on user" + userId);
377         executeShellCmd("pm install-existing --user %d --wait %s", userId, packageName);
378     }
379 
isPackageInstalledAsUser(String packageName, int userId)380     private boolean isPackageInstalledAsUser(String packageName, int userId) throws Exception {
381         String output = executeShellCmd("pm list packages --user %d %s", userId, packageName);
382         return output.contains("package:" + packageName + "\n");
383     }
384 
executeShellCmd(String cmdFormat, Object... args)385     private String executeShellCmd(String cmdFormat, Object... args) throws Exception {
386         String cmd = String.format(cmdFormat, args);
387         String result = SystemUtil.runShellCommand(mInstrumentation, cmd);
388         Log.d(TAG, String.format("Output for '%s': %s", cmd, result));
389         return result;
390     }
391 
awaitForLatch(CountDownLatch latch, String reasonFormat, Object... reasonArgs)392     private void awaitForLatch(CountDownLatch latch, String reasonFormat,
393             Object... reasonArgs) {
394         awaitForLatch(latch, WAITFOR_MSEC, reasonFormat, reasonArgs);
395     }
396 
awaitForLatch(CountDownLatch latch, long timeout, String reasonFormat, Object... reasonArgs)397     private void awaitForLatch(CountDownLatch latch, long timeout, String reasonFormat,
398             Object... reasonArgs) {
399         String reason = String.format(reasonFormat, reasonArgs);
400         Log.d(TAG, "waiting " + WAITFOR_MSEC + " for " + reason);
401         try {
402             assertTrue("Timeout for waiting", latch.await(timeout, TimeUnit.MILLISECONDS));
403             Log.d(TAG, "latch counted down");
404         } catch (InterruptedException e) {
405             Thread.currentThread().interrupt();
406             fail("Interrupted");
407         }
408     }
409 
410     // Start the target package
startService(int commandCode, String serviceName, boolean waitForGone, boolean other)411     private void startService(int commandCode, String serviceName, boolean waitForGone,
412             boolean other) {
413         startService(commandCode, serviceName, waitForGone, true, other, false, null);
414     }
415 
startService(int commandCode, String serviceName, boolean waitForGone, boolean waitForIdle, boolean other, boolean includeCookie, byte[] cookie)416     private void startService(int commandCode, String serviceName, boolean waitForGone,
417             boolean waitForIdle, boolean other, boolean includeCookie, byte[] cookie) {
418         Intent intent = new Intent(EXIT_ACTION);
419         intent.setClassName(STUB_PACKAGE_NAME, serviceName);
420         intent.putExtra(EXTRA_ACTION, commandCode);
421         intent.putExtra(EXTRA_MESSENGER, mMessenger);
422         if (includeCookie) {
423             intent.putExtra(EXTRA_COOKIE, cookie);
424         }
425         mLatch = new CountDownLatch(1);
426         UserHandle user = other ? mOtherUserHandle : mCurrentUserHandle;
427         WatchUidRunner watcher = other ? mOtherUidWatcher : mWatcher;
428         Log.i(TAG, "Starting service " + serviceName + ": waitForGone=" + waitForGone
429                 + ", waitForIdle=" + waitForIdle + ",intent=" + intent + ", user=" + user);
430         mContext.startServiceAsUser(intent, user);
431         if (waitForIdle) {
432             watcher.waitFor(WatchUidRunner.CMD_IDLE, null);
433         }
434         if (waitForGone) {
435             waitForGone(watcher);
436         }
437         awaitForLatch(mLatch, "service %s to start on user %s", serviceName, user);
438     }
439 
startIsolatedService(int commandCode, String serviceName)440     private void startIsolatedService(int commandCode, String serviceName) {
441         Intent intent = new Intent(EXIT_ACTION);
442         intent.setClassName(STUB_PACKAGE_NAME, serviceName);
443         intent.putExtra(EXTRA_ACTION, commandCode);
444         intent.putExtra(EXTRA_MESSENGER, mMessenger);
445         mLatch = new CountDownLatch(1);
446         mContext.startServiceAsUser(intent, mCurrentUserHandle);
447         awaitForLatch(mLatch, "service %s to start", serviceName);
448     }
449 
waitForGone(WatchUidRunner watcher)450     private void waitForGone(WatchUidRunner watcher) {
451         watcher.waitFor(WatchUidRunner.CMD_GONE, null);
452         // Give a few seconds to generate the exit report.
453         sleep(WAITFOR_SETTLE_DOWN);
454     }
455 
clearHistoricalExitInfo()456     private void clearHistoricalExitInfo() throws Exception {
457         executeShellCmd("am clear-exit-info --user all " + STUB_PACKAGE_NAME);
458     }
459 
sleep(long timeout)460     private void sleep(long timeout) {
461         try {
462             Thread.sleep(timeout);
463         } catch (InterruptedException e) {
464         }
465     }
466 
getHistoricalProcessExitReasonsAsUser( final String packageName, final int pid, final int max, final int userId)467     private List<ApplicationExitInfo> getHistoricalProcessExitReasonsAsUser(
468             final String packageName, final int pid, final int max, final int userId) {
469         Context context = mContext.createContextAsUser(UserHandle.of(userId), 0);
470         ActivityManager am = context.getSystemService(ActivityManager.class);
471         return am.getHistoricalProcessExitReasons(packageName, pid, max);
472     }
473 
474     @Test
testExitCode()475     public void testExitCode() throws Exception {
476         // Remove old records to avoid interference with the test.
477         clearHistoricalExitInfo();
478 
479         long now = System.currentTimeMillis();
480         // Start a process and let it call System.exit() right away.
481         startService(ACTION_EXIT, STUB_SERVICE_NAME, true, false);
482 
483         long now2 = System.currentTimeMillis();
484         // Query with the current package name, but the mStubPackagePid belongs to the
485         // target package, so the below call should return an empty result.
486         List<ApplicationExitInfo> list = null;
487         try {
488             list = mActivityManager.getHistoricalProcessExitReasons(
489                     STUB_PACKAGE_NAME, mStubPackagePid, 1);
490             fail("Shouldn't be able to query other package");
491         } catch (SecurityException e) {
492             // expected
493         }
494 
495         // Now query with the advanced version
496         try {
497             list = getHistoricalProcessExitReasonsAsUser(STUB_PACKAGE_NAME,
498                     mStubPackagePid, 1, mCurrentUserId);
499             fail("Shouldn't be able to query other package");
500         } catch (SecurityException e) {
501             // expected
502         }
503 
504         list = ShellIdentityUtils.invokeMethodWithShellPermissions(
505                 STUB_PACKAGE_NAME, mStubPackagePid, 1, mCurrentUserId,
506                 this::getHistoricalProcessExitReasonsAsUser,
507                 android.Manifest.permission.DUMP);
508 
509         assertTrue(list != null && list.size() == 1);
510         verify(list.get(0), mStubPackagePid, mStubPackageUid, STUB_PACKAGE_NAME,
511                 ApplicationExitInfo.REASON_EXIT_SELF, EXIT_CODE, null, now, now2);
512     }
513 
fillUpMemoryAndCheck( final MemoryConsumerService.TestFuncInterface testFunc, final List<ApplicationExitInfo> list)514     private List<ServiceConnection> fillUpMemoryAndCheck(
515             final MemoryConsumerService.TestFuncInterface testFunc,
516             final List<ApplicationExitInfo> list) throws Exception {
517         final String procNamePrefix = "memconsumer_";
518         final ArrayList<ServiceConnection> memConsumers = new ArrayList<>();
519         Pair<IBinder, ServiceConnection> p = MemoryConsumerService.bindToService(
520                 mContext, testFunc, procNamePrefix + mProcSeqNum++);
521         IBinder consumer = p.first;
522         memConsumers.add(p.second);
523 
524         while (list.size() == 0) {
525             // Get the meminfo firstly
526             MemInfoReader reader = new MemInfoReader();
527             reader.readMemInfo();
528 
529             long totalMb = (reader.getFreeSizeKb() + reader.getCachedSizeKb()) >> 10;
530             if (!MemoryConsumerService.runOnce(consumer, totalMb) && list.size() == 0) {
531                 // Need to create a new consumer (the present one might be running out of space)
532                 p = MemoryConsumerService.bindToService(mContext, testFunc,
533                         procNamePrefix + mProcSeqNum++);
534                 consumer = p.first;
535                 memConsumers.add(p.second);
536             }
537             // make sure we have cached process killed
538             String output = executeShellCmd("dumpsys activity lru");
539             if (output == null && output.indexOf(" cch+") == -1) {
540                 break;
541             }
542         }
543 
544         return memConsumers;
545     }
546 
547     @Test
testLmkdKill()548     public void testLmkdKill() throws Exception {
549         // Remove old records to avoid interference with the test.
550         clearHistoricalExitInfo();
551 
552         long now = System.currentTimeMillis();
553         boolean lmkdReportSupported = ActivityManager.isLowMemoryKillReportSupported();
554 
555         // Start a process and do nothing
556         startService(ACTION_FINISH, STUB_SERVICE_NAME, false, false);
557 
558         final ArrayList<IBinder> memConsumers = new ArrayList<>();
559         List<ApplicationExitInfo> list = new ArrayList<>();
560         final MemoryConsumerService.TestFuncInterface testFunc =
561                 new MemoryConsumerService.TestFuncInterface(() -> {
562                     final long token = Binder.clearCallingIdentity();
563                     try {
564                         List<ApplicationExitInfo> result =
565                                 ShellIdentityUtils.invokeMethodWithShellPermissions(
566                                         STUB_PACKAGE_NAME, mStubPackagePid, 1,
567                                         mActivityManager::getHistoricalProcessExitReasons,
568                                         android.Manifest.permission.DUMP);
569                         if (result != null && result.size() == 1) {
570                             list.add(result.get(0));
571                             return true;
572                         }
573                     } finally {
574                         Binder.restoreCallingIdentity(token);
575                     }
576                     return false;
577                 });
578 
579         List<ServiceConnection> services = fillUpMemoryAndCheck(testFunc, list);
580 
581         // Unbind all the service connections firstly
582         for (int i = services.size() - 1; i >= 0; i--) {
583             mContext.unbindService(services.get(i));
584         }
585 
586         long now2 = System.currentTimeMillis();
587         assertTrue(list != null && list.size() == 1);
588         ApplicationExitInfo info = list.get(0);
589         assertNotNull(info);
590         if (lmkdReportSupported) {
591             verify(info, mStubPackagePid, mStubPackageUid, STUB_PACKAGE_NAME,
592                     ApplicationExitInfo.REASON_LOW_MEMORY, null, null, now, now2);
593         } else {
594             verify(info, mStubPackagePid, mStubPackageUid, STUB_PACKAGE_NAME,
595                     ApplicationExitInfo.REASON_SIGNALED, OsConstants.SIGKILL, null, now, now2);
596         }
597     }
598 
599     @Test
testKillBySignal()600     public void testKillBySignal() throws Exception {
601         // Remove old records to avoid interference with the test.
602         clearHistoricalExitInfo();
603 
604         long now = System.currentTimeMillis();
605 
606         // Start a process and kill itself
607         startService(ACTION_KILL, STUB_SERVICE_NAME, true, false);
608 
609         long now2 = System.currentTimeMillis();
610         List<ApplicationExitInfo> list = ShellIdentityUtils.invokeMethodWithShellPermissions(
611                 STUB_PACKAGE_NAME, mStubPackagePid, 1,
612                 mActivityManager::getHistoricalProcessExitReasons,
613                 android.Manifest.permission.DUMP);
614 
615         assertTrue(list != null && list.size() == 1);
616         verify(list.get(0), mStubPackagePid, mStubPackageUid, STUB_PACKAGE_NAME,
617                 ApplicationExitInfo.REASON_SIGNALED, OsConstants.SIGKILL, null, now, now2);
618     }
619 
620     @Test
testAnr()621     public void testAnr() throws Exception {
622         // Remove old records to avoid interference with the test.
623         clearHistoricalExitInfo();
624 
625         final DropBoxManager dbox = mContext.getSystemService(DropBoxManager.class);
626         final CountDownLatch dboxLatch = new CountDownLatch(1);
627         final BroadcastReceiver receiver = new BroadcastReceiver() {
628             @Override
629             public void onReceive(Context context, Intent intent) {
630                 final String tag_anr = "data_app_anr";
631                 if (tag_anr.equals(intent.getStringExtra(DropBoxManager.EXTRA_TAG))) {
632                     mAnrEntry = dbox.getNextEntry(tag_anr, intent.getLongExtra(
633                             DropBoxManager.EXTRA_TIME, 0) - 1);
634                     Log.d(TAG, "Counting down latch onReceive(" + intent + ")");
635                     dboxLatch.countDown();
636                 }
637             }
638         };
639         mContext.registerReceiver(receiver,
640                 new IntentFilter(DropBoxManager.ACTION_DROPBOX_ENTRY_ADDED));
641         final long timeout = Settings.Global.getInt(mContext.getContentResolver(),
642                 Settings.Global.BROADCAST_FG_CONSTANTS, 10 * 1000) * 3;
643 
644         long now = System.currentTimeMillis();
645 
646         // Start a process and block its main thread
647         startService(ACTION_ANR, STUB_SERVICE_NAME, false, false);
648 
649         // Sleep for a while to make sure it's already blocking its main thread.
650         sleep(WAITFOR_MSEC);
651 
652         AmMonitor monitor = new AmMonitor(mInstrumentation,
653                 new String[]{AmMonitor.WAIT_FOR_CRASHED});
654 
655         Intent intent = new Intent();
656         intent.setComponent(new ComponentName(STUB_PACKAGE_NAME, STUB_RECEIVER_NAME));
657         intent.addFlags(Intent.FLAG_RECEIVER_FOREGROUND);
658         // This will result an ANR
659         mContext.sendOrderedBroadcast(intent, null);
660 
661         // Wait for the early ANR
662         monitor.waitFor(AmMonitor.WAIT_FOR_EARLY_ANR, timeout);
663         // Continue, so we could collect ANR traces
664         monitor.sendCommand(AmMonitor.CMD_CONTINUE);
665         // Wait for the ANR
666         monitor.waitFor(AmMonitor.WAIT_FOR_ANR, timeout);
667         // Kill it
668         monitor.sendCommand(AmMonitor.CMD_KILL);
669         // Wait the process gone
670         waitForGone(mWatcher);
671         long now2 = System.currentTimeMillis();
672 
673         awaitForLatch(dboxLatch, "broadcast for %s be received",
674                 DropBoxManager.ACTION_DROPBOX_ENTRY_ADDED);
675         assertTrue(mAnrEntry != null);
676 
677         List<ApplicationExitInfo> list = ShellIdentityUtils.invokeMethodWithShellPermissions(
678                 STUB_PACKAGE_NAME, mStubPackagePid, 1,
679                 mActivityManager::getHistoricalProcessExitReasons,
680                 android.Manifest.permission.DUMP);
681 
682         assertTrue(list != null && list.size() == 1);
683         ApplicationExitInfo info = list.get(0);
684         verify(info, mStubPackagePid, mStubPackageUid, STUB_PACKAGE_NAME,
685                 ApplicationExitInfo.REASON_ANR, null, null, now, now2);
686         assertEquals(mStubPackageUid, info.getPackageUid());
687         assertEquals(mStubPackageUid, info.getDefiningUid());
688 
689         // Verify the traces
690 
691         // Read from dropbox
692         final String dboxTrace = mAnrEntry.getText(0x100000 /* 1M */);
693         assertFalse(TextUtils.isEmpty(dboxTrace));
694 
695         // Read the input stream from the ApplicationExitInfo
696         String trace = ShellIdentityUtils.invokeMethodWithShellPermissions(info, (i) -> {
697             try (BufferedInputStream input = new BufferedInputStream(i.getTraceInputStream())) {
698                 StringBuilder sb = new StringBuilder();
699                 byte[] buf = new byte[8192];
700                 while (true) {
701                     final int len = input.read(buf, 0, buf.length);
702                     if (len <= 0) {
703                         break;
704                     }
705                     sb.append(new String(buf, 0, len));
706                 }
707                 return sb.toString();
708             } catch (IOException e) {
709                 return null;
710             }
711         }, android.Manifest.permission.DUMP);
712         assertFalse(TextUtils.isEmpty(trace));
713         assertTrue(trace.indexOf(Integer.toString(info.getPid())) >= 0);
714         assertTrue(trace.indexOf("Cmd line: " + STUB_PACKAGE_NAME) >= 0);
715         assertTrue(dboxTrace.indexOf(trace) >= 0);
716 
717         monitor.finish();
718         mContext.unregisterReceiver(receiver);
719     }
720 
721     @Test
testOther()722     public void testOther() throws Exception {
723         // Remove old records to avoid interference with the test.
724         clearHistoricalExitInfo();
725 
726         final String servicePackage = "android.externalservice.service";
727         final String keyIBinder = "ibinder";
728         final CountDownLatch latch = new CountDownLatch(1);
729         final Bundle holder = new Bundle();
730         final ServiceConnection connection = new ServiceConnection() {
731             @Override
732             public void onServiceConnected(ComponentName name, IBinder service) {
733                 holder.putBinder(keyIBinder, service);
734                 Log.d(TAG, "Counting down latch onServiceConnected(" + name + ")");
735                 latch.countDown();
736             }
737 
738             @Override
739             public void onServiceDisconnected(ComponentName name) {
740             }
741         };
742 
743         final Intent intent = new Intent();
744         ComponentName serviceComponent = new ComponentName(servicePackage,
745                 servicePackage + ".ExternalServiceWithZygote");
746         intent.setComponent(serviceComponent);
747 
748         // Bind to that external service, which will become an isolated process
749         // running in the current package user id.
750         assertTrue(mContext.bindService(intent,
751                 Context.BIND_AUTO_CREATE | Context.BIND_EXTERNAL_SERVICE,
752                 AsyncTask.THREAD_POOL_EXECUTOR, connection));
753 
754         awaitForLatch(latch, "service %s to bind", serviceComponent.flattenToShortString());
755 
756         final IBinder service = holder.getBinder(keyIBinder);
757         assertNotNull(service);
758 
759         // Retrieve its uid/pd/package name info.
760         Messenger remote = new Messenger(service);
761         RunningServiceInfo id = identifyService(remote);
762         assertNotNull(id);
763 
764         assertFalse(id.uid == 0 || id.pid == 0);
765         assertFalse(Process.myUid() == id.uid);
766         assertFalse(Process.myPid() == id.pid);
767         assertEquals(mContext.getPackageName(), id.packageName);
768 
769         final WatchUidRunner watcher = new WatchUidRunner(mInstrumentation,
770                 id.uid, WAITFOR_MSEC);
771 
772         long now = System.currentTimeMillis();
773 
774         // Remove the service connection
775         mContext.unbindService(connection);
776 
777         try {
778             // Isolated process should have been killed as long as its service is done.
779             waitForGone(watcher);
780         } finally {
781             watcher.finish();
782         }
783 
784         long now2 = System.currentTimeMillis();
785         final ActivityManager am = mContext.getSystemService(ActivityManager.class);
786         final List<ApplicationExitInfo> list = am.getHistoricalProcessExitReasons(null, id.pid, 1);
787         assertTrue(list != null && list.size() == 1);
788 
789         ApplicationExitInfo info = list.get(0);
790         verify(info, id.pid, id.uid, null, ApplicationExitInfo.REASON_OTHER, null,
791                 "isolated not needed", now, now2);
792         assertEquals(Process.myUid(), info.getPackageUid());
793         assertEquals(mContext.getPackageManager().getPackageUid(servicePackage, 0),
794                 info.getDefiningUid());
795         assertEquals(ActivityManager.RunningAppProcessInfo.IMPORTANCE_SERVICE,
796                 info.getImportance());
797     }
798 
extractMemString(String dump, String prefix, char nextSep)799     private String extractMemString(String dump, String prefix, char nextSep) {
800         int start = dump.indexOf(prefix);
801         assertTrue(start >= 0);
802         start += prefix.length();
803         int end = dump.indexOf(nextSep, start);
804         assertTrue(end > start);
805         return dump.substring(start, end);
806     }
807 
808     @Test
testPermissionChange()809     public void testPermissionChange() throws Exception {
810         // Remove old records to avoid interference with the test.
811         clearHistoricalExitInfo();
812 
813         // Grant the read calendar permission
814         mInstrumentation.getUiAutomation().grantRuntimePermission(
815                 STUB_PACKAGE_NAME, android.Manifest.permission.READ_CALENDAR);
816         long now = System.currentTimeMillis();
817 
818         // Start a process and do nothing
819         startService(ACTION_FINISH, STUB_SERVICE_NAME, false, false);
820 
821         // Enable high frequency memory sampling
822         executeShellCmd("dumpsys procstats --start-testing");
823         // Sleep for a while to wait for the sampling of memory info
824         sleep(10000);
825         // Stop the high frequency memory sampling
826         executeShellCmd("dumpsys procstats --stop-testing");
827         // Get the memory info from it.
828         String dump = executeShellCmd("dumpsys activity processes " + STUB_PACKAGE_NAME);
829         assertNotNull(dump);
830         String lastPss = null;
831         String lastRss = null;
832         if (mIsProfilingPss) {
833             lastPss = extractMemString(dump, " lastPss=", ' ');
834             lastRss = extractMemString(dump, " lastRss=", '\n');
835         } else {
836             // lastRss is not the final field in the dump, so the next separator is not a newline.
837             lastRss = extractMemString(dump, " lastRss=", ' ');
838         }
839 
840         // Revoke the read calendar permission
841         mInstrumentation.getUiAutomation().revokeRuntimePermission(
842                 STUB_PACKAGE_NAME, android.Manifest.permission.READ_CALENDAR);
843         waitForGone(mWatcher);
844         long now2 = System.currentTimeMillis();
845 
846         List<ApplicationExitInfo> list = ShellIdentityUtils.invokeMethodWithShellPermissions(
847                 STUB_PACKAGE_NAME, mStubPackagePid, 1,
848                 mActivityManager::getHistoricalProcessExitReasons,
849                 android.Manifest.permission.DUMP);
850 
851         assertTrue(list != null && list.size() == 1);
852 
853         ApplicationExitInfo info = list.get(0);
854         verify(info, mStubPackagePid, mStubPackageUid, STUB_PACKAGE_NAME,
855                 ApplicationExitInfo.REASON_PERMISSION_CHANGE, null, null, now, now2);
856 
857         // Also verify that we get the expected meminfo
858         if (mIsProfilingPss) {
859             assertEquals(lastPss, DebugUtils.sizeValueToString(
860                     info.getPss() * 1024, new StringBuilder()));
861         }
862         assertEquals(lastRss, DebugUtils.sizeValueToString(
863                 info.getRss() * 1024, new StringBuilder()));
864     }
865 
866     // A clone of testPermissionChange using a different revoke api
867     @Test
testPermissionChangeWithReason()868     public void testPermissionChangeWithReason() throws Exception {
869         String revokeReason = "test reason";
870         // Remove old records to avoid interference with the test.
871         clearHistoricalExitInfo();
872 
873         // Grant the read calendar permission
874         mInstrumentation.getUiAutomation().grantRuntimePermission(
875                 STUB_PACKAGE_NAME, android.Manifest.permission.READ_CALENDAR);
876         long now = System.currentTimeMillis();
877 
878         // Start a process and do nothing
879         startService(ACTION_FINISH, STUB_SERVICE_NAME, false, false);
880 
881         // Enable high frequency memory sampling
882         executeShellCmd("dumpsys procstats --start-testing");
883         // Sleep for a while to wait for the sampling of memory info
884         sleep(10000);
885         // Stop the high frequency memory sampling
886         executeShellCmd("dumpsys procstats --stop-testing");
887         // Get the memory info from it.
888         String dump = executeShellCmd("dumpsys activity processes " + STUB_PACKAGE_NAME);
889         assertNotNull(dump);
890         String lastPss = null;
891         String lastRss = null;
892         if (mIsProfilingPss) {
893             lastPss = extractMemString(dump, " lastPss=", ' ');
894             lastRss = extractMemString(dump, " lastRss=", '\n');
895         } else {
896             // lastRss is not the final field in the dump, so the next separator is not a newline.
897             lastRss = extractMemString(dump, " lastRss=", ' ');
898         }
899 
900         // Revoke the read calendar permission
901         runWithShellPermissionIdentity(() -> {
902             mContext.getPackageManager().revokeRuntimePermission(STUB_PACKAGE_NAME,
903                     android.Manifest.permission.READ_CALENDAR, Process.myUserHandle(),
904                     revokeReason);
905         });
906         waitForGone(mWatcher);
907         long now2 = System.currentTimeMillis();
908 
909         List<ApplicationExitInfo> list = ShellIdentityUtils.invokeMethodWithShellPermissions(
910                 STUB_PACKAGE_NAME, mStubPackagePid, 1,
911                 mActivityManager::getHistoricalProcessExitReasons,
912                 android.Manifest.permission.DUMP);
913 
914         assertTrue(list != null && list.size() == 1);
915 
916         ApplicationExitInfo info = list.get(0);
917         verify(info, mStubPackagePid, mStubPackageUid, STUB_PACKAGE_NAME,
918                 ApplicationExitInfo.REASON_PERMISSION_CHANGE, null, null, now, now2);
919         assertEquals(revokeReason, info.getDescription());
920 
921         // Also verify that we get the expected meminfo
922         if (mIsProfilingPss) {
923             assertEquals(lastPss, DebugUtils.sizeValueToString(
924                     info.getPss() * 1024, new StringBuilder()));
925         }
926         assertEquals(lastRss, DebugUtils.sizeValueToString(
927                 info.getRss() * 1024, new StringBuilder()));
928     }
929 
930     @Test
testCrash()931     public void testCrash() throws Exception {
932         // Remove old records to avoid interference with the test.
933         clearHistoricalExitInfo();
934 
935         long now = System.currentTimeMillis();
936 
937         // Start a process and do nothing
938         startService(ACTION_NONE, STUB_SERVICE_NAME, false, false);
939 
940         // Induce a crash
941         executeShellCmd("am crash " + STUB_PACKAGE_NAME);
942         waitForGone(mWatcher);
943         long now2 = System.currentTimeMillis();
944 
945         List<ApplicationExitInfo> list = ShellIdentityUtils.invokeMethodWithShellPermissions(
946                 STUB_PACKAGE_NAME, mStubPackagePid, 1,
947                 mActivityManager::getHistoricalProcessExitReasons,
948                 android.Manifest.permission.DUMP);
949 
950         assertTrue(list != null && list.size() == 1);
951         verify(list.get(0), mStubPackagePid, mStubPackageUid, STUB_PACKAGE_NAME,
952                 ApplicationExitInfo.REASON_CRASH, null, null, now, now2);
953     }
954 
955     @Test
testNativeCrash()956     public void testNativeCrash() throws Exception {
957         // Remove old records to avoid interference with the test.
958         clearHistoricalExitInfo();
959 
960         long now = System.currentTimeMillis();
961 
962         // Start a process and crash it
963         startService(ACTION_NATIVE_CRASH, STUB_SERVICE_NAME, true, false);
964 
965         // Native crashes are handled asynchronously from the actual crash, so
966         // it's possible for us to notice that the process crashed before an
967         // actual tombstone exists.
968         Thread.sleep(1000);
969 
970         long now2 = System.currentTimeMillis();
971         List<ApplicationExitInfo> list = ShellIdentityUtils.invokeMethodWithShellPermissions(
972                 STUB_PACKAGE_NAME, mStubPackagePid, 1,
973                 mActivityManager::getHistoricalProcessExitReasons,
974                 android.Manifest.permission.DUMP);
975 
976         assertTrue(list != null && list.size() == 1);
977         verify(list.get(0), mStubPackagePid, mStubPackageUid, STUB_PACKAGE_NAME,
978                 ApplicationExitInfo.REASON_CRASH_NATIVE, null, null, now, now2);
979 
980         TombstoneFetcher tombstoneFetcher = new TombstoneFetcher(list.get(0));
981         PollingCheck.check("not able to get tombstone", TOMBSTONE_FETCH_TIMEOUT_MS,
982                 () -> tombstoneFetcher.fetchTrace());
983 
984         InputStream trace = tombstoneFetcher.getTrace();
985         assertNotNull(trace);
986         Tombstone tombstone = Tombstone.parseFrom(trace);
987         assertEquals(tombstone.getPid(), mStubPackagePid);
988     }
989 
990     @Test
testUserRequested()991     public void testUserRequested() throws Exception {
992         // Remove old records to avoid interference with the test.
993         clearHistoricalExitInfo();
994 
995         long now = System.currentTimeMillis();
996 
997         // Start a process and do nothing
998         startService(ACTION_NONE, STUB_SERVICE_NAME, false, false);
999 
1000         // Force stop the test package
1001         executeShellCmd("am force-stop " + STUB_PACKAGE_NAME);
1002 
1003         // Wait the process gone
1004         waitForGone(mWatcher);
1005 
1006         long now2 = System.currentTimeMillis();
1007         List<ApplicationExitInfo> list = ShellIdentityUtils.invokeMethodWithShellPermissions(
1008                 STUB_PACKAGE_NAME, mStubPackagePid, 1,
1009                 mActivityManager::getHistoricalProcessExitReasons,
1010                 android.Manifest.permission.DUMP);
1011 
1012         assertTrue(list != null && list.size() == 1);
1013         verify(list.get(0), mStubPackagePid, mStubPackageUid, STUB_PACKAGE_NAME,
1014                 ApplicationExitInfo.REASON_USER_REQUESTED, null, null, now, now2);
1015     }
1016 
1017     @Test
testPackageDisabled()1018     public void testPackageDisabled() throws Exception {
1019         // Remove old records to avoid interference with the test.
1020         clearHistoricalExitInfo();
1021 
1022         // Start a process and do nothing
1023         startService(ACTION_NONE, STUB_SERVICE_NAME, false, false);
1024 
1025         //disable the app and kill it
1026         mInstrumentation.getUiAutomation().adoptShellPermissionIdentity(
1027                 android.Manifest.permission.CHANGE_COMPONENT_ENABLED_STATE);
1028         PackageManager packageManager = mContext.getPackageManager();
1029         packageManager.setApplicationEnabledSetting(
1030                 STUB_PACKAGE_NAME,
1031                 PackageManager.COMPONENT_ENABLED_STATE_DISABLED,
1032                 0);
1033 
1034         waitForGone(mWatcher);
1035 
1036         List<ApplicationExitInfo> list = ShellIdentityUtils.invokeMethodWithShellPermissions(
1037                 STUB_PACKAGE_NAME, mStubPackagePid, 1,
1038                 mActivityManager::getHistoricalProcessExitReasons,
1039                 android.Manifest.permission.DUMP);
1040 
1041         assertTrue(list != null && list.size() == 1);
1042         assertEquals(ApplicationExitInfo.REASON_PACKAGE_STATE_CHANGE, list.get(0).getReason());
1043         assertEquals(ApplicationExitInfo.SUBREASON_UNKNOWN, list.get(0).getSubReason());
1044     }
1045 
1046     @Test
testPackageUpdated()1047     public void testPackageUpdated() throws Exception {
1048         // Remove old records to avoid interference with the test.
1049         clearHistoricalExitInfo();
1050 
1051         // Start a process and do nothing
1052         startService(ACTION_NONE, STUB_SERVICE_NAME, false, false);
1053 
1054         // Update the package
1055         executeShellCmd("pm install -r /data/local/tmp/cts/content/CtsSimpleApp.apk");
1056 
1057         waitForGone(mWatcher);
1058 
1059         List<ApplicationExitInfo> list =
1060                 ShellIdentityUtils.invokeMethodWithShellPermissions(
1061                         STUB_PACKAGE_NAME, mStubPackagePid, 1,
1062                         mActivityManager::getHistoricalProcessExitReasons,
1063                         Manifest.permission.DUMP);
1064 
1065         assertTrue(list != null && list.size() == 1);
1066         assertEquals(ApplicationExitInfo.REASON_PACKAGE_UPDATED, list.get(0).getReason());
1067         assertEquals(ApplicationExitInfo.SUBREASON_UNKNOWN, list.get(0).getSubReason());
1068     }
1069 
1070     @Test
testDependencyDied()1071     public void testDependencyDied() throws Exception {
1072         // Remove old records to avoid interference with the test.
1073         clearHistoricalExitInfo();
1074 
1075         // Start a process and acquire the provider
1076         startService(ACTION_ACQUIRE_STABLE_PROVIDER, STUB_SERVICE_NAME, false, false);
1077 
1078         final ActivityManager am = mContext.getSystemService(ActivityManager.class);
1079         long now = System.currentTimeMillis();
1080         final long timeout = now + WAITFOR_MSEC;
1081         int providerPid = -1;
1082         while (now < timeout && providerPid < 0) {
1083             sleep(1000);
1084             List<RunningAppProcessInfo> list = ShellIdentityUtils.invokeMethodWithShellPermissions(
1085                     am, ActivityManager::getRunningAppProcesses,
1086                     android.Manifest.permission.REAL_GET_TASKS);
1087             for (RunningAppProcessInfo info: list) {
1088                 if (info.processName.equals(STUB_REMOTE_PROCESS_NAME)) {
1089                     providerPid = info.pid;
1090                     break;
1091                 }
1092             }
1093             now = System.currentTimeMillis();
1094         }
1095         assertTrue(providerPid > 0);
1096 
1097         now = System.currentTimeMillis();
1098         // Now let the provider exit itself
1099         startService(ACTION_KILL_PROVIDER, STUB_SERVICE_NAME, false, false, false, false, null);
1100 
1101         // Wait for both of the processes gone
1102         waitForGone(mWatcher);
1103         final long now2 = System.currentTimeMillis();
1104 
1105         List<ApplicationExitInfo> list = ShellIdentityUtils.invokeMethodWithShellPermissions(
1106                 STUB_PACKAGE_NAME, mStubPackagePid, 1,
1107                 mActivityManager::getHistoricalProcessExitReasons,
1108                 android.Manifest.permission.DUMP);
1109 
1110         assertTrue(list != null && list.size() == 1);
1111         verify(list.get(0), mStubPackagePid, mStubPackageUid, STUB_PACKAGE_NAME,
1112                 ApplicationExitInfo.REASON_DEPENDENCY_DIED, null, null, now, now2);
1113     }
1114 
1115     @Test
testMultipleProcess()1116     public void testMultipleProcess() throws Exception {
1117         // Remove old records to avoid interference with the test.
1118         clearHistoricalExitInfo();
1119 
1120         long now = System.currentTimeMillis();
1121 
1122         // Start a process and kill itself
1123         startService(ACTION_KILL, STUB_SERVICE_NAME, true, false);
1124 
1125         long now2 = System.currentTimeMillis();
1126 
1127         // Start a remote process and exit
1128         startService(ACTION_EXIT, STUB_SERVICE_REMOTE_NAME, true, false);
1129 
1130         long now3 = System.currentTimeMillis();
1131         // Now to get the two reports
1132         List<ApplicationExitInfo> list = ShellIdentityUtils.invokeMethodWithShellPermissions(
1133                 STUB_PACKAGE_NAME, 0, 2,
1134                 mActivityManager::getHistoricalProcessExitReasons,
1135                 android.Manifest.permission.DUMP);
1136 
1137         assertTrue(list != null && list.size() == 2);
1138         verify(list.get(0), mStubPackageRemotePid, mStubPackageUid, STUB_REMOTE_PROCESS_NAME,
1139                 ApplicationExitInfo.REASON_EXIT_SELF, EXIT_CODE, null, now2, now3);
1140         verify(list.get(1), mStubPackagePid, mStubPackageUid, STUB_PROCESS_NAME,
1141                 ApplicationExitInfo.REASON_SIGNALED, OsConstants.SIGKILL, null, now, now2);
1142 
1143         // If we only retrieve one report
1144         list = ShellIdentityUtils.invokeMethodWithShellPermissions(
1145                 STUB_PACKAGE_NAME, 0, 1,
1146                 mActivityManager::getHistoricalProcessExitReasons,
1147                 android.Manifest.permission.DUMP);
1148 
1149         assertTrue(list != null && list.size() == 1);
1150         verify(list.get(0), mStubPackageRemotePid, mStubPackageUid, STUB_REMOTE_PROCESS_NAME,
1151                 ApplicationExitInfo.REASON_EXIT_SELF, EXIT_CODE, null, now2, now3);
1152     }
1153 
identifyService(Messenger service)1154     private RunningServiceInfo identifyService(Messenger service) throws Exception {
1155         final CountDownLatch latch = new CountDownLatch(1);
1156         class IdentifyHandler extends Handler {
1157             IdentifyHandler() {
1158                 super(Looper.getMainLooper());
1159             }
1160 
1161             RunningServiceInfo mInfo;
1162 
1163             @Override
1164             public void handleMessage(Message msg) {
1165                 Log.d(TAG, "Received message: " + msg);
1166                 switch (msg.what) {
1167                     case ServiceMessages.MSG_IDENTIFY_RESPONSE:
1168                         msg.getData().setClassLoader(RunningServiceInfo.class.getClassLoader());
1169                         mInfo = msg.getData().getParcelable(ServiceMessages.IDENTIFY_INFO);
1170                         Log.d(TAG, "Counting down latch on IdentifyHandler msg: " + msg);
1171                         latch.countDown();
1172                         break;
1173                 }
1174                 super.handleMessage(msg);
1175             }
1176         }
1177 
1178         IdentifyHandler handler = new IdentifyHandler();
1179         Messenger local = new Messenger(handler);
1180 
1181         Message msg = Message.obtain(null, ServiceMessages.MSG_IDENTIFY);
1182         msg.replyTo = local;
1183         service.send(msg);
1184         awaitForLatch(latch, "service to receive message");
1185 
1186         return handler.mInfo;
1187     }
1188 
prepareTestUser()1189     private void prepareTestUser() throws Exception {
1190         Log.d(TAG, "prepareTestUser()");
1191         // Create the test user
1192         mOtherUserId = createUser("TestUser_" + SystemClock.uptimeMillis(), false);
1193         Log.d(TAG, "user created: " + mOtherUserId);
1194         mOtherUserHandle = UserHandle.of(mOtherUserId);
1195         // Start the other user
1196         assertTrue(startUser(mOtherUserId, true));
1197         Log.d(TAG, "user started");
1198         // Install the test helper APK into the other user
1199         installExistingPackageAsUser(STUB_PACKAGE_NAME, mOtherUserId);
1200         installExistingPackageAsUser(mContext.getPackageName(), mOtherUserId);
1201         mStubPackageOtherUid = mContext.getPackageManager().getPackageUidAsUser(
1202                 STUB_PACKAGE_NAME, 0, mOtherUserId);
1203         Log.d(TAG, "UID of " + STUB_PACKAGE_NAME + ": " + mStubPackageOtherUid);
1204         mOtherUidWatcher = new WatchUidRunner(mInstrumentation, mStubPackageOtherUid,
1205                 WAITFOR_MSEC);
1206     }
1207 
removeTestUserIfNecessary()1208     private void removeTestUserIfNecessary() throws Exception {
1209         if (mSupportMultipleUsers && mOtherUserId > 0) {
1210             // Stop the test user
1211             assertTrue(stopUser(mOtherUserId, true, true));
1212             // Remove the test user
1213             removeUser(mOtherUserId);
1214             if (mOtherUidWatcher != null) {
1215                 mOtherUidWatcher.finish();
1216             }
1217         }
1218     }
1219 
1220     @Test
testSecondaryUser()1221     public void testSecondaryUser() throws Exception {
1222         if (!mSupportMultipleUsers) {
1223             return;
1224         }
1225 
1226         // Remove old records to avoid interference with the test.
1227         clearHistoricalExitInfo();
1228 
1229         // Get the full user permission in order to start service as other user
1230         mInstrumentation.getUiAutomation().adoptShellPermissionIdentity(
1231                 android.Manifest.permission.INTERACT_ACROSS_USERS,
1232                 android.Manifest.permission.INTERACT_ACROSS_USERS_FULL);
1233 
1234         // Create the test user, we'll remove it during tearDown
1235         prepareTestUser();
1236 
1237         final byte[] cookie0 = {(byte) 0x00, (byte) 0x01, (byte) 0x02, (byte) 0x03,
1238                 (byte) 0x04, (byte) 0x05, (byte) 0x06, (byte) 0x07};
1239         final byte[] cookie1 = {(byte) 0x01, (byte) 0x02, (byte) 0x03, (byte) 0x04,
1240                 (byte) 0x05, (byte) 0x06, (byte) 0x07, (byte) 0x08};
1241         final byte[] cookie2 = {(byte) 0x02, (byte) 0x03, (byte) 0x04, (byte) 0x05,
1242                 (byte) 0x06, (byte) 0x07, (byte) 0x08, (byte) 0x01};
1243         final byte[] cookie3 = {(byte) 0x03, (byte) 0x04, (byte) 0x05, (byte) 0x06,
1244                 (byte) 0x07, (byte) 0x08, (byte) 0x01, (byte) 0x02};
1245         final byte[] cookie4 = {(byte) 0x04, (byte) 0x05, (byte) 0x06, (byte) 0x07,
1246                 (byte) 0x08, (byte) 0x01, (byte) 0x02, (byte) 0x03};
1247         final byte[] cookie5 = null;
1248 
1249         long now = System.currentTimeMillis();
1250 
1251         // Start a process and do nothing
1252         startService(ACTION_NONE, STUB_SERVICE_NAME, false, true, false, true, cookie0);
1253         // request to exit by itself with a different cookie
1254         startService(ACTION_EXIT, STUB_SERVICE_NAME, true, false, false, true, cookie1);
1255 
1256         long now2 = System.currentTimeMillis();
1257 
1258         // Start the process in a secondary user and kill itself
1259         startService(ACTION_KILL, STUB_SERVICE_NAME, true, true, true, true, cookie2);
1260 
1261         long now3 = System.currentTimeMillis();
1262 
1263         // Start a remote process in a secondary user and exit
1264         startService(ACTION_EXIT, STUB_SERVICE_REMOTE_NAME, true, true, true, true, cookie3);
1265 
1266         long now4 = System.currentTimeMillis();
1267 
1268         // Start a remote process and kill itself
1269         startService(ACTION_KILL, STUB_SERVICE_REMOTE_NAME, true, true, false, true, cookie4);
1270 
1271         long now5 = System.currentTimeMillis();
1272         // drop the permissions
1273         mInstrumentation.getUiAutomation().dropShellPermissionIdentity();
1274 
1275         List<ApplicationExitInfo> list = null;
1276 
1277         // Now try to query for all users
1278         try {
1279             list = ShellIdentityUtils.invokeMethodWithShellPermissions(
1280                     STUB_PACKAGE_NAME, 0, 0, UserHandle.USER_ALL,
1281                     this::getHistoricalProcessExitReasonsAsUser,
1282                     android.Manifest.permission.DUMP);
1283             fail("Shouldn't be able to query all users");
1284         } catch (IllegalArgumentException e) {
1285             // expected
1286         }
1287 
1288         // Now try to query for "current" user
1289         try {
1290             list = ShellIdentityUtils.invokeMethodWithShellPermissions(
1291                     STUB_PACKAGE_NAME, 0, 0, UserHandle.USER_CURRENT,
1292                     this::getHistoricalProcessExitReasonsAsUser,
1293                     android.Manifest.permission.DUMP);
1294             fail("Shouldn't be able to query current user, explicit user-Id is expected");
1295         } catch (IllegalArgumentException e) {
1296             // expected
1297         }
1298 
1299         // Now only try the current user
1300         list = ShellIdentityUtils.invokeMethodWithShellPermissions(
1301                 STUB_PACKAGE_NAME, 0, 0, mCurrentUserId,
1302                 this::getHistoricalProcessExitReasonsAsUser,
1303                 android.Manifest.permission.DUMP);
1304 
1305         assertTrue(list != null && list.size() == 2);
1306         verify(list.get(0), mStubPackageRemotePid, mStubPackageUid, STUB_REMOTE_PROCESS_NAME,
1307                 ApplicationExitInfo.REASON_SIGNALED, OsConstants.SIGKILL, null, now4, now5,
1308                 cookie4);
1309         verify(list.get(1), mStubPackagePid, mStubPackageUid, STUB_PROCESS_NAME,
1310                 ApplicationExitInfo.REASON_EXIT_SELF, EXIT_CODE, null, now, now2, cookie1);
1311 
1312         // Now try the other user
1313         try {
1314             list = ShellIdentityUtils.invokeMethodWithShellPermissions(
1315                     STUB_PACKAGE_NAME, 0, 0, mOtherUserId,
1316                     this::getHistoricalProcessExitReasonsAsUser,
1317                     android.Manifest.permission.DUMP);
1318             fail("Shouldn't be able to query other users");
1319         } catch (SecurityException e) {
1320             // expected
1321         }
1322 
1323         // Now try the other user with proper permissions
1324         list = ShellIdentityUtils.invokeMethodWithShellPermissions(
1325                 STUB_PACKAGE_NAME, 0, 0, mOtherUserId,
1326                 this::getHistoricalProcessExitReasonsAsUser,
1327                 android.Manifest.permission.DUMP,
1328                 android.Manifest.permission.INTERACT_ACROSS_USERS);
1329 
1330         assertTrue(list != null && list.size() == 2);
1331         verify(list.get(0), mStubPackageRemoteOtherUserPid, mStubPackageOtherUid,
1332                 STUB_REMOTE_PROCESS_NAME, ApplicationExitInfo.REASON_EXIT_SELF, EXIT_CODE,
1333                 null, now3, now4, cookie3);
1334         verify(list.get(1), mStubPackageOtherUserPid, mStubPackageOtherUid, STUB_PROCESS_NAME,
1335                 ApplicationExitInfo.REASON_SIGNALED, OsConstants.SIGKILL, null,
1336                 now2, now3, cookie2);
1337 
1338         // Get the full user permission in order to start service as other user
1339         mInstrumentation.getUiAutomation().adoptShellPermissionIdentity(
1340                 android.Manifest.permission.INTERACT_ACROSS_USERS,
1341                 android.Manifest.permission.INTERACT_ACROSS_USERS_FULL);
1342         // Start the process in a secondary user and do nothing
1343         startService(ACTION_NONE, STUB_SERVICE_NAME, false, true, true, true, cookie5);
1344         // drop the permissions
1345         mInstrumentation.getUiAutomation().dropShellPermissionIdentity();
1346 
1347         long now6 = System.currentTimeMillis();
1348         // Stop the test user
1349         assertTrue(stopUser(mOtherUserId, true, true));
1350         // Wait for being killed
1351         waitForGone(mOtherUidWatcher);
1352 
1353         long now7 = System.currentTimeMillis();
1354         list = ShellIdentityUtils.invokeMethodWithShellPermissions(
1355                 STUB_PACKAGE_NAME, 0, 1, mOtherUserId,
1356                 this::getHistoricalProcessExitReasonsAsUser,
1357                 android.Manifest.permission.DUMP,
1358                 android.Manifest.permission.INTERACT_ACROSS_USERS);
1359         verify(list.get(0), mStubPackageOtherUserPid, mStubPackageOtherUid, STUB_PROCESS_NAME,
1360                 ApplicationExitInfo.REASON_USER_STOPPED, null, null, now6, now7, cookie5);
1361 
1362         int otherUserId = mOtherUserId;
1363         // Now remove the other user
1364         removeUser(mOtherUserId);
1365         mOtherUidWatcher.finish();
1366         mOtherUserId = 0;
1367 
1368         // Poll userInfo to check if the user has been removed, wait up to 10 mins
1369         for (int i = 0; i < 600; i++) {
1370             if (ShellIdentityUtils.invokeMethodWithShellPermissions(otherUserId,
1371                     mUserManager::getUserInfo,
1372                     android.Manifest.permission.CREATE_USERS) != null) {
1373                 // We can still get the userInfo, sleep 1 second and try again
1374                 sleep(1000);
1375             } else {
1376                 Log.d(TAG, "User " + otherUserId + " has been removed");
1377                 break;
1378             }
1379         }
1380         // For now the ACTION_USER_REMOVED should have been sent to all receives,
1381         // we take an extra nap to make sure we've had the broadcast handling settled.
1382         sleep(15 * 1000);
1383 
1384         // Now query the other userId, and it should return nothing.
1385         final Context context = mContext.createPackageContextAsUser("android", 0,
1386                 UserHandle.of(otherUserId));
1387         final ActivityManager am = context.getSystemService(ActivityManager.class);
1388         list = ShellIdentityUtils.invokeMethodWithShellPermissions(
1389                 STUB_PACKAGE_NAME, 0, 0,
1390                 am::getHistoricalProcessExitReasons,
1391                 android.Manifest.permission.DUMP,
1392                 android.Manifest.permission.INTERACT_ACROSS_USERS);
1393         assertTrue(list == null || list.size() == 0);
1394 
1395         // The current user shouldn't be impacted.
1396         list = ShellIdentityUtils.invokeMethodWithShellPermissions(
1397                 STUB_PACKAGE_NAME, 0, 0, mCurrentUserId,
1398                 this::getHistoricalProcessExitReasonsAsUser,
1399                 android.Manifest.permission.DUMP,
1400                 android.Manifest.permission.INTERACT_ACROSS_USERS);
1401 
1402         assertTrue(list != null && list.size() == 2);
1403         verify(list.get(0), mStubPackageRemotePid, mStubPackageUid, STUB_REMOTE_PROCESS_NAME,
1404                 ApplicationExitInfo.REASON_SIGNALED, OsConstants.SIGKILL, null, now4, now5,
1405                 cookie4);
1406         verify(list.get(1), mStubPackagePid, mStubPackageUid, STUB_PROCESS_NAME,
1407                 ApplicationExitInfo.REASON_EXIT_SELF, EXIT_CODE, null, now, now2, cookie1);
1408     }
1409 
1410     /**
1411      * By design, an app's process in cached state is subject to being killed due
1412      * to system memory pressure. Any work in this state, e.g. an {@link Activity}
1413      * trying to execute extra code after the {@link Activity#onStop()} method has
1414      * been called and returned, is unreliable and strongly discouraged. For more
1415      * details see <a
1416      * href="https://developer.android.com/guide/components/activities/process-lifecycle">
1417      * Processes and app lifecycle</a>.
1418      * <p>
1419      * Starting in {@link android.os.Build.VERSION_CODES#UPSIDE_DOWN_CAKE}, OS
1420      * enforces cached-app resource usage. This test checks whether the Freezer
1421      * has been correctly enabled to be consistent with the documented developer
1422      * expectations.
1423      */
1424     @CddTest(requirements = {"3.5/C-0-2"})
1425     @Test
testFreezerEnabled()1426     public void testFreezerEnabled() throws Exception {
1427         if (FIRST_SDK_IS_AT_LEAST_U
1428                 && SystemProperties.get("ro.kernel.version").compareTo("5") >= 0) {
1429             // We expect all devices with kernel 5.x that first shipped with U to support Freezer
1430             assertTrue(ActivityManager.getService().isAppFreezerSupported());
1431         } else {
1432             // For old devices OTA'ed to U, check if Linux kernel and vendor partition is too old
1433             assumeTrue(ActivityManager.getService().isAppFreezerSupported());
1434         }
1435 
1436         // Freezer must be enabled as long as it's supported
1437         assertTrue(ActivityManager.getService().isAppFreezerEnabled());
1438 
1439         // Check dumpsys to verify the Freezer configurations in use
1440         final String output = executeShellCmd("dumpsys activity");
1441         Pattern pattern = Pattern.compile("freeze_debounce_timeout=(\\d+)");
1442         Matcher matcher = pattern.matcher(output);
1443         assertTrue(matcher.find());
1444         final long timeout = Long.parseLong(matcher.group(1));
1445         assertTrue(timeout >= FREEZER_TIMEOUT_FLOOR);
1446     }
1447 
1448     @Test
testFreezerNormalExitCode()1449     public void testFreezerNormalExitCode() throws Exception {
1450         // The app should NOT be frozen with 30s freeze timeout configuration
1451         runFreezerTest(HEARTBEAT_FREEZER_LONG, false, ApplicationExitInfo.REASON_SIGNALED);
1452     }
1453 
1454     @Test
testFreezerKillExitCode()1455     public void testFreezerKillExitCode() throws Exception {
1456         // The app should be frozen and killed with 5s freeze timeout configuration
1457         assumeTrue(ActivityManager.getService().isAppFreezerEnabled());
1458         runFreezerTest(HEARTBEAT_FREEZER_SHORT, true, ApplicationExitInfo.REASON_FREEZER);
1459     }
1460 
runFreezerTest(long freezerTimeout, boolean dead, int reason)1461     public void runFreezerTest(long freezerTimeout, boolean dead, int reason) throws Exception {
1462         // Remove old records to avoid interference with the test.
1463         clearHistoricalExitInfo();
1464 
1465         executeShellCmd(
1466                 "device_config put activity_manager_native_boot freeze_debounce_timeout "
1467                         + freezerTimeout);
1468 
1469         long now = System.currentTimeMillis();
1470 
1471         mLatch = new CountDownLatch(1);
1472 
1473         // Start the HeartbeatService to wait for HeartbeatActivity
1474         Intent serviceIntent = new Intent(HEARTBEAT_SERVICE);
1475         serviceIntent.setPackage(HEARTBEAT_PACKAGE);
1476         ServiceConnection connection = new ServiceConnection() {
1477             @Override
1478             public void onServiceConnected(ComponentName name, IBinder service) {
1479                 Log.d(TAG, "onServiceConnected(" + name + "): " + service);
1480                 IHeartbeat heartbeat = IHeartbeat.Stub.asInterface(service);
1481                 try {
1482                     heartbeat.monitor(mMessenger);
1483                 } catch (RemoteException e) {
1484                     fail("Failed to monitor Heartbeat service");
1485                 }
1486             }
1487 
1488             @Override
1489             public void onServiceDisconnected(ComponentName name) {
1490             }
1491         };
1492         mContext.bindService(serviceIntent, connection, Context.BIND_AUTO_CREATE);
1493 
1494         // Launch the HeartbeatActivity to talk to the HeartbeatService
1495         Intent clientIntent = new Intent(Intent.ACTION_MAIN);
1496         clientIntent.setClassName(HEARTBEAT_PACKAGE, HEARTBEAT_ACTIVITY);
1497         clientIntent.putExtra(HEARTBEAT_COUNTDOWN_NAME, HEARTBEAT_COUNTDOWN);
1498         clientIntent.putExtra(HEARTBEAT_INTERVAL_NAME, HEARTBEAT_INTERVAL);
1499         clientIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
1500         mContext.startActivity(clientIntent);
1501         sleep(1000);
1502 
1503         // Launch another app to bring the HeartbeatActivity to background
1504         Intent intent1 = new Intent(Intent.ACTION_MAIN);
1505         intent1.setClassName(STUB_PACKAGE_NAME, STUB_PACKAGE_NAME + SIMPLE_ACTIVITY);
1506         intent1.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
1507         mContext.startActivity(intent1);
1508         sleep(1000);
1509 
1510         // Launch another activity to make sure the HeartbeatActivity is in cached mode
1511         Intent intent2 = new Intent(Intent.ACTION_MAIN);
1512         intent2.setClassName(STUB_PACKAGE_NAME, STUB_PACKAGE_NAME + ".NonLauncherActivity");
1513         intent2.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
1514         mContext.startActivity(intent2);
1515 
1516         // Wait until the HeartbeatService finishes
1517         awaitForLatch(mLatch, HEARTBEAT_COUNTDOWN * HEARTBEAT_INTERVAL, "heartbeat");
1518         mContext.unbindService(connection);
1519         sleep(1000);
1520 
1521         // Check if the frozen app is killed
1522         assertEquals(dead, mHeartbeatDead);
1523         int uid = mContext.getPackageManager().getPackageUid(HEARTBEAT_PACKAGE,
1524                 PackageManager.PackageInfoFlags.of(0));
1525         assertEquals(uid, mStubPackageUid);
1526 
1527         long now2 = System.currentTimeMillis();
1528 
1529         List<ApplicationExitInfo> list = ShellIdentityUtils.invokeMethodWithShellPermissions(
1530                 HEARTBEAT_PACKAGE, mStubPackagePid, 1, mCurrentUserId,
1531                 this::getHistoricalProcessExitReasonsAsUser,
1532                 android.Manifest.permission.DUMP);
1533 
1534         assertNotNull(list);
1535         assertEquals(1, list.size());
1536         verify(list.get(0), mStubPackagePid, uid, HEARTBEAT_PROCESS,
1537                 reason, null, null, now, now2);
1538     }
1539 
verify(ApplicationExitInfo info, int pid, int uid, String processName, int reason, Integer status, String description, long before, long after)1540     private void verify(ApplicationExitInfo info, int pid, int uid, String processName,
1541             int reason, Integer status, String description, long before, long after) {
1542         verify(info, pid, uid, processName, reason, status, description, before, after, null);
1543     }
1544 
verify(ApplicationExitInfo info, int pid, int uid, String processName, int reason, Integer status, String description, long before, long after, byte[] cookie)1545     private void verify(ApplicationExitInfo info, int pid, int uid, String processName,
1546             int reason, Integer status, String description, long before, long after,
1547             byte[] cookie) {
1548         assertNotNull(info);
1549         assertEquals(pid, info.getPid());
1550         assertEquals(uid, info.getRealUid());
1551         assertEquals(UserHandle.of(UserHandle.getUserId(uid)), info.getUserHandle());
1552         if (processName != null) {
1553             assertEquals(processName, info.getProcessName());
1554         }
1555         assertEquals(reason, info.getReason());
1556         if (status != null) {
1557             assertEquals(status.intValue(), info.getStatus());
1558         }
1559 
1560         if (description != null) {
1561             assertTrue(info.getDescription().contains(description));
1562         }
1563 
1564         assertTrue(before <= info.getTimestamp());
1565         assertTrue(after >= info.getTimestamp());
1566         assertTrue(ArrayUtils.equals(info.getProcessStateSummary(), cookie,
1567                 cookie == null ? 0 : cookie.length));
1568     }
1569 
1570     private static class TombstoneFetcher {
1571         private InputStream mTrace = null;
1572         private final ApplicationExitInfo mExitInfo;
1573 
TombstoneFetcher(ApplicationExitInfo exitInfo)1574         TombstoneFetcher(ApplicationExitInfo exitInfo) {
1575             mExitInfo = exitInfo;
1576         }
1577 
getTrace()1578         public InputStream getTrace() {
1579             return mTrace;
1580         }
1581 
fetchTrace()1582         public boolean fetchTrace() throws Exception {
1583             mTrace = ShellIdentityUtils.invokeMethodWithShellPermissions(
1584                     mExitInfo,
1585                     (i) -> {
1586                         try {
1587                             return i.getTraceInputStream();
1588                         } catch (IOException ex) {
1589                             return null;
1590                         }
1591                     },
1592                     android.Manifest.permission.DUMP);
1593             return (mTrace != null);
1594         }
1595     }
1596 }
1597