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