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 android.app.ActivityManager;
22 import android.app.ActivityManager.RunningAppProcessInfo;
23 import android.app.ApplicationExitInfo;
24 import android.app.Instrumentation;
25 import android.app.cts.android.app.cts.tools.WatchUidRunner;
26 import android.content.BroadcastReceiver;
27 import android.content.ComponentName;
28 import android.content.Context;
29 import android.content.Intent;
30 import android.content.IntentFilter;
31 import android.content.ServiceConnection;
32 import android.externalservice.common.RunningServiceInfo;
33 import android.externalservice.common.ServiceMessages;
34 import android.os.AsyncTask;
35 import android.os.Binder;
36 import android.os.Bundle;
37 import android.os.DropBoxManager;
38 import android.os.Handler;
39 import android.os.HandlerThread;
40 import android.os.IBinder;
41 import android.os.Looper;
42 import android.os.Message;
43 import android.os.Messenger;
44 import android.os.Process;
45 import android.os.SystemClock;
46 import android.os.UserHandle;
47 import android.os.UserManager;
48 import android.provider.Settings;
49 import android.server.wm.settings.SettingsSession;
50 import android.system.OsConstants;
51 import android.test.InstrumentationTestCase;
52 import android.text.TextUtils;
53 import android.util.DebugUtils;
54 import android.util.Log;
55 import android.util.Pair;
56 
57 import com.android.compatibility.common.util.AmMonitor;
58 import com.android.compatibility.common.util.ShellIdentityUtils;
59 import com.android.compatibility.common.util.SystemUtil;
60 import com.android.internal.util.ArrayUtils;
61 import com.android.internal.util.MemInfoReader;
62 import com.android.server.os.TombstoneProtos.Tombstone;
63 
64 import java.io.BufferedInputStream;
65 import java.io.IOException;
66 import java.io.InputStream;
67 import java.util.ArrayList;
68 import java.util.List;
69 import java.util.concurrent.CountDownLatch;
70 import java.util.concurrent.TimeUnit;
71 
72 public final class ActivityManagerAppExitInfoTest extends InstrumentationTestCase {
73     private static final String TAG = ActivityManagerAppExitInfoTest.class.getSimpleName();
74 
75     private static final String STUB_PACKAGE_NAME =
76             "com.android.cts.launcherapps.simpleapp";
77     private static final String STUB_SERVICE_NAME =
78             "com.android.cts.launcherapps.simpleapp.SimpleService4";
79     private static final String STUB_SERVICE_REMOTE_NAME =
80             "com.android.cts.launcherapps.simpleapp.SimpleService5";
81     private static final String STUB_SERVICE_ISOLATED_NAME =
82             "com.android.cts.launcherapps.simpleapp.SimpleService6";
83     private static final String STUB_RECEIVER_NAMWE =
84             "com.android.cts.launcherapps.simpleapp.SimpleReceiver";
85     private static final String STUB_ROCESS_NAME = STUB_PACKAGE_NAME;
86     private static final String STUB_REMOTE_ROCESS_NAME = STUB_ROCESS_NAME + ":remote";
87 
88     private static final String EXIT_ACTION =
89             "com.android.cts.launchertests.simpleapp.EXIT_ACTION";
90     private static final String EXTRA_ACTION = "action";
91     private static final String EXTRA_MESSENGER = "messenger";
92     private static final String EXTRA_PROCESS_NAME = "process";
93     private static final String EXTRA_COOKIE = "cookie";
94 
95     private static final int ACTION_NONE = 0;
96     private static final int ACTION_FINISH = 1;
97     private static final int ACTION_EXIT = 2;
98     private static final int ACTION_ANR = 3;
99     private static final int ACTION_NATIVE_CRASH = 4;
100     private static final int ACTION_KILL = 5;
101     private static final int ACTION_ACQUIRE_STABLE_PROVIDER = 6;
102     private static final int ACTION_KILL_PROVIDER = 7;
103     private static final int EXIT_CODE = 123;
104     private static final int CRASH_SIGNAL = OsConstants.SIGSEGV;
105 
106     private static final int WAITFOR_MSEC = 10000;
107     private static final int WAITFOR_SETTLE_DOWN = 2000;
108 
109     private static final int CMD_PID = 1;
110 
111     private Context mContext;
112     private Instrumentation mInstrumentation;
113     private int mStubPackageUid;
114     private int mStubPackagePid;
115     private int mStubPackageRemotePid;
116     private int mStubPackageOtherUid;
117     private int mStubPackageOtherUserPid;
118     private int mStubPackageRemoteOtherUserPid;
119     private int mStubPackageIsolatedUid;
120     private int mStubPackageIsolatedPid;
121     private String mStubPackageIsolatedProcessName;
122     private WatchUidRunner mWatcher;
123     private WatchUidRunner mOtherUidWatcher;
124     private ActivityManager mActivityManager;
125     private CountDownLatch mLatch;
126     private UserManager mUserManager;
127     private HandlerThread mHandlerThread;
128     private Handler mHandler;
129     private Messenger mMessenger;
130     private boolean mSupportMultipleUsers;
131     private int mCurrentUserId;
132     private UserHandle mCurrentUserHandle;
133     private int mOtherUserId;
134     private UserHandle mOtherUserHandle;
135     private DropBoxManager.Entry mAnrEntry;
136     private SettingsSession<String> mDataAnrSettings;
137     private SettingsSession<String> mHiddenApiSettings;
138     private int mProcSeqNum;
139 
140     @Override
setUp()141     protected void setUp() throws Exception {
142         super.setUp();
143         mInstrumentation = getInstrumentation();
144         mContext = mInstrumentation.getContext();
145         mStubPackageUid = mContext.getPackageManager().getPackageUid(STUB_PACKAGE_NAME, 0);
146         mWatcher = new WatchUidRunner(mInstrumentation, mStubPackageUid, WAITFOR_MSEC);
147         mActivityManager = mContext.getSystemService(ActivityManager.class);
148         mUserManager = UserManager.get(mContext);
149         mCurrentUserId = UserHandle.getUserId(Process.myUid());
150         mCurrentUserHandle = Process.myUserHandle();
151         mSupportMultipleUsers = mUserManager.supportsMultipleUsers();
152         mHandlerThread = new HandlerThread("receiver");
153         mHandlerThread.start();
154         mHandler = new H(mHandlerThread.getLooper());
155         mMessenger = new Messenger(mHandler);
156         executeShellCmd("cmd deviceidle whitelist +" + STUB_PACKAGE_NAME);
157         mDataAnrSettings = new SettingsSession<>(
158                 Settings.Global.getUriFor(
159                         Settings.Global.DROPBOX_TAG_PREFIX + "data_app_anr"),
160                 Settings.Global::getString, Settings.Global::putString);
161         mDataAnrSettings.set("enabled");
162         mHiddenApiSettings = new SettingsSession<>(
163                 Settings.Global.getUriFor(
164                         Settings.Global.HIDDEN_API_BLACKLIST_EXEMPTIONS),
165                 Settings.Global::getString, Settings.Global::putString);
166         mHiddenApiSettings.set("*");
167     }
168 
handleMessagePid(Message msg)169     private void handleMessagePid(Message msg) {
170         boolean didSomething = false;
171         Bundle b = (Bundle) msg.obj;
172         String processName = b.getString(EXTRA_PROCESS_NAME);
173 
174         if (STUB_ROCESS_NAME.equals(processName)) {
175             if (mOtherUserId != 0 && UserHandle.getUserId(msg.arg2) == mOtherUserId) {
176                 mStubPackageOtherUserPid = msg.arg1;
177                 assertTrue(mStubPackageOtherUserPid > 0);
178             } else {
179                 mStubPackagePid = msg.arg1;
180                 assertTrue(mStubPackagePid > 0);
181             }
182         } else if (STUB_REMOTE_ROCESS_NAME.equals(processName)) {
183             if (mOtherUserId != 0 && UserHandle.getUserId(msg.arg2) == mOtherUserId) {
184                 mStubPackageRemoteOtherUserPid = msg.arg1;
185                 assertTrue(mStubPackageRemoteOtherUserPid > 0);
186             } else {
187                 mStubPackageRemotePid = msg.arg1;
188                 assertTrue(mStubPackageRemotePid > 0);
189             }
190         } else { // must be isolated process
191             mStubPackageIsolatedPid = msg.arg1;
192             mStubPackageIsolatedUid = msg.arg2;
193             mStubPackageIsolatedProcessName = processName;
194             assertTrue(mStubPackageIsolatedPid > 0);
195             assertTrue(mStubPackageIsolatedUid > 0);
196             assertNotNull(processName);
197         }
198 
199         if (mLatch != null) {
200             mLatch.countDown();
201         }
202     }
203 
204     private class H extends Handler {
H(Looper looper)205         H(Looper looper) {
206             super(looper);
207         }
208 
209         @Override
handleMessage(Message msg)210         public void handleMessage(Message msg) {
211             switch (msg.what) {
212                 case CMD_PID:
213                     handleMessagePid(msg);
214                     break;
215                 default:
216                     break;
217             }
218         }
219     }
220 
221     @Override
tearDown()222     protected void tearDown() throws Exception {
223         mWatcher.finish();
224         executeShellCmd("cmd deviceidle whitelist -" + STUB_PACKAGE_NAME);
225         removeTestUserIfNecessary();
226         mHandlerThread.quitSafely();
227         if (mDataAnrSettings != null) {
228             mDataAnrSettings.close();
229         }
230         if (mHiddenApiSettings != null) {
231             mHiddenApiSettings.close();
232         }
233     }
234 
createUser(String name, boolean guest)235     private int createUser(String name, boolean guest) throws Exception {
236         final String output = executeShellCmd(
237                 "pm create-user " + (guest ? "--guest " : "") + name);
238         if (output.startsWith("Success")) {
239             return Integer.parseInt(output.substring(output.lastIndexOf(" ")).trim());
240         }
241         throw new IllegalStateException(String.format("Failed to create user: %s", output));
242     }
243 
removeUser(final int userId)244     private boolean removeUser(final int userId) throws Exception {
245         final String output = executeShellCmd(String.format("pm remove-user %s", userId));
246         if (output.startsWith("Error")) {
247             return false;
248         }
249         return true;
250     }
251 
startUser(int userId, boolean waitFlag)252     private boolean startUser(int userId, boolean waitFlag) throws Exception {
253         String cmd = "am start-user " + (waitFlag ? "-w " : "") + userId;
254 
255         final String output = executeShellCmd(cmd);
256         if (output.startsWith("Error")) {
257             return false;
258         }
259         if (waitFlag) {
260             String state = executeShellCmd("am get-started-user-state " + userId);
261             if (!state.contains("RUNNING_UNLOCKED")) {
262                 return false;
263             }
264         }
265         return true;
266     }
267 
stopUser(int userId, boolean waitFlag, boolean forceFlag)268     private boolean stopUser(int userId, boolean waitFlag, boolean forceFlag)
269             throws Exception {
270         StringBuilder cmd = new StringBuilder("am stop-user ");
271         if (waitFlag) {
272             cmd.append("-w ");
273         }
274         if (forceFlag) {
275             cmd.append("-f ");
276         }
277         cmd.append(userId);
278 
279         final String output = executeShellCmd(cmd.toString());
280         if (output.contains("Error: Can't stop system user")) {
281             return false;
282         }
283         return true;
284     }
285 
installExistingPackageAsUser(String packageName, int userId)286     private void installExistingPackageAsUser(String packageName, int userId)
287             throws Exception {
288         executeShellCmd(
289                 String.format("pm install-existing --user %d --wait %s", userId, packageName));
290     }
291 
executeShellCmd(String cmd)292     private String executeShellCmd(String cmd) throws Exception {
293         final String result = SystemUtil.runShellCommand(mInstrumentation, cmd);
294         Log.d(TAG, String.format("Output for '%s': %s", cmd, result));
295         return result;
296     }
297 
awaitForLatch(CountDownLatch latch)298     private void awaitForLatch(CountDownLatch latch) {
299         try {
300             assertTrue("Timeout for waiting", latch.await(WAITFOR_MSEC, TimeUnit.MILLISECONDS));
301         } catch (InterruptedException e) {
302             fail("Interrupted");
303         }
304     }
305 
306     // Start the target package
startService(int commandCode, String serviceName, boolean waitForGone, boolean other)307     private void startService(int commandCode, String serviceName, boolean waitForGone,
308             boolean other) {
309         startService(commandCode, serviceName, waitForGone, true, other, false, null);
310     }
311 
startService(int commandCode, String serviceName, boolean waitForGone, boolean waitForIdle, boolean other, boolean includeCookie, byte[] cookie)312     private void startService(int commandCode, String serviceName, boolean waitForGone,
313             boolean waitForIdle, boolean other, boolean includeCookie, byte[] cookie) {
314         Intent intent = new Intent(EXIT_ACTION);
315         intent.setClassName(STUB_PACKAGE_NAME, serviceName);
316         intent.putExtra(EXTRA_ACTION, commandCode);
317         intent.putExtra(EXTRA_MESSENGER, mMessenger);
318         if (includeCookie) {
319             intent.putExtra(EXTRA_COOKIE, cookie);
320         }
321         mLatch = new CountDownLatch(1);
322         UserHandle user = other ? mOtherUserHandle : mCurrentUserHandle;
323         WatchUidRunner watcher = other ? mOtherUidWatcher : mWatcher;
324         mContext.startServiceAsUser(intent, user);
325         if (waitForIdle) {
326             watcher.waitFor(WatchUidRunner.CMD_IDLE, null);
327         }
328         if (waitForGone) {
329             waitForGone(watcher);
330         }
331         awaitForLatch(mLatch);
332     }
333 
startIsolatedService(int commandCode, String serviceName)334     private void startIsolatedService(int commandCode, String serviceName) {
335         Intent intent = new Intent(EXIT_ACTION);
336         intent.setClassName(STUB_PACKAGE_NAME, serviceName);
337         intent.putExtra(EXTRA_ACTION, commandCode);
338         intent.putExtra(EXTRA_MESSENGER, mMessenger);
339         mLatch = new CountDownLatch(1);
340         mContext.startServiceAsUser(intent, mCurrentUserHandle);
341         awaitForLatch(mLatch);
342     }
343 
waitForGone(WatchUidRunner watcher)344     private void waitForGone(WatchUidRunner watcher) {
345         watcher.waitFor(WatchUidRunner.CMD_GONE, null);
346         // Give a few seconds to generate the exit report.
347         sleep(WAITFOR_SETTLE_DOWN);
348     }
349 
clearHistoricalExitInfo()350     private void clearHistoricalExitInfo() throws Exception {
351         executeShellCmd("am clear-exit-info --user all " + STUB_PACKAGE_NAME);
352     }
353 
sleep(long timeout)354     private void sleep(long timeout) {
355         try {
356             Thread.sleep(timeout);
357         } catch (InterruptedException e) {
358         }
359     }
360 
getHistoricalProcessExitReasonsAsUser( final String packageName, final int pid, final int max, final int userId)361     private List<ApplicationExitInfo> getHistoricalProcessExitReasonsAsUser(
362             final String packageName, final int pid, final int max, final int userId) {
363         Context context = mContext.createContextAsUser(UserHandle.of(userId), 0);
364         ActivityManager am = context.getSystemService(ActivityManager.class);
365         return am.getHistoricalProcessExitReasons(packageName, pid, max);
366     }
367 
testExitCode()368     public void testExitCode() throws Exception {
369         // Remove old records to avoid interference with the test.
370         clearHistoricalExitInfo();
371 
372         long now = System.currentTimeMillis();
373         // Start a process and let it call System.exit() right away.
374         startService(ACTION_EXIT, STUB_SERVICE_NAME, true, false);
375 
376         long now2 = System.currentTimeMillis();
377         // Query with the current package name, but the mStubPackagePid belongs to the
378         // target package, so the below call should return an empty result.
379         List<ApplicationExitInfo> list = null;
380         try {
381             list = mActivityManager.getHistoricalProcessExitReasons(
382                     STUB_PACKAGE_NAME, mStubPackagePid, 1);
383             fail("Shouldn't be able to query other package");
384         } catch (SecurityException e) {
385             // expected
386         }
387 
388         // Now query with the advanced version
389         try {
390             list = getHistoricalProcessExitReasonsAsUser(STUB_PACKAGE_NAME,
391                     mStubPackagePid, 1, mCurrentUserId);
392             fail("Shouldn't be able to query other package");
393         } catch (SecurityException e) {
394             // expected
395         }
396 
397         list = ShellIdentityUtils.invokeMethodWithShellPermissions(
398                 STUB_PACKAGE_NAME, mStubPackagePid, 1, mCurrentUserId,
399                 this::getHistoricalProcessExitReasonsAsUser,
400                 android.Manifest.permission.DUMP);
401 
402         assertTrue(list != null && list.size() == 1);
403         verify(list.get(0), mStubPackagePid, mStubPackageUid, STUB_PACKAGE_NAME,
404                 ApplicationExitInfo.REASON_EXIT_SELF, EXIT_CODE, null, now, now2);
405     }
406 
fillUpMemoryAndCheck( final MemoryConsumerService.TestFuncInterface testFunc, final List<ApplicationExitInfo> list)407     private List<ServiceConnection> fillUpMemoryAndCheck(
408             final MemoryConsumerService.TestFuncInterface testFunc,
409             final List<ApplicationExitInfo> list) throws Exception {
410         final String procNamePrefix = "memconsumer_";
411         final ArrayList<ServiceConnection> memConsumers = new ArrayList<>();
412         Pair<IBinder, ServiceConnection> p = MemoryConsumerService.bindToService(
413                 mContext, testFunc, procNamePrefix + mProcSeqNum++);
414         IBinder consumer = p.first;
415         memConsumers.add(p.second);
416 
417         while (list.size() == 0) {
418             // Get the meminfo firstly
419             MemInfoReader reader = new MemInfoReader();
420             reader.readMemInfo();
421 
422             long totalMb = (reader.getFreeSizeKb() + reader.getCachedSizeKb()) >> 10;
423             if (!MemoryConsumerService.runOnce(consumer, totalMb) && list.size() == 0) {
424                 // Need to create a new consumer (the present one might be running out of space)
425                 p = MemoryConsumerService.bindToService(mContext, testFunc,
426                         procNamePrefix + mProcSeqNum++);
427                 consumer = p.first;
428                 memConsumers.add(p.second);
429             }
430             // make sure we have cached process killed
431             String output = executeShellCmd("dumpsys activity lru");
432             if (output == null && output.indexOf(" cch+") == -1) {
433                 break;
434             }
435         }
436 
437         return memConsumers;
438     }
439 
testLmkdKill()440     public void testLmkdKill() throws Exception {
441         // Remove old records to avoid interference with the test.
442         clearHistoricalExitInfo();
443 
444         long now = System.currentTimeMillis();
445         boolean lmkdReportSupported = ActivityManager.isLowMemoryKillReportSupported();
446 
447         // Start a process and do nothing
448         startService(ACTION_FINISH, STUB_SERVICE_NAME, false, false);
449 
450         final ArrayList<IBinder> memConsumers = new ArrayList<>();
451         List<ApplicationExitInfo> list = new ArrayList<>();
452         final MemoryConsumerService.TestFuncInterface testFunc =
453                 new MemoryConsumerService.TestFuncInterface(() -> {
454                     final long token = Binder.clearCallingIdentity();
455                     try {
456                         List<ApplicationExitInfo> result =
457                                 ShellIdentityUtils.invokeMethodWithShellPermissions(
458                                         STUB_PACKAGE_NAME, mStubPackagePid, 1,
459                                         mActivityManager::getHistoricalProcessExitReasons,
460                                         android.Manifest.permission.DUMP);
461                         if (result != null && result.size() == 1) {
462                             list.add(result.get(0));
463                             return true;
464                         }
465                     } finally {
466                         Binder.restoreCallingIdentity(token);
467                     }
468                     return false;
469                 });
470 
471         List<ServiceConnection> services = fillUpMemoryAndCheck(testFunc, list);
472 
473         // Unbind all the service connections firstly
474         for (int i = services.size() - 1; i >= 0; i--) {
475             mContext.unbindService(services.get(i));
476         }
477 
478         long now2 = System.currentTimeMillis();
479         assertTrue(list != null && list.size() == 1);
480         ApplicationExitInfo info = list.get(0);
481         assertNotNull(info);
482         if (lmkdReportSupported) {
483             verify(info, mStubPackagePid, mStubPackageUid, STUB_PACKAGE_NAME,
484                     ApplicationExitInfo.REASON_LOW_MEMORY, null, null, now, now2);
485         } else {
486             verify(info, mStubPackagePid, mStubPackageUid, STUB_PACKAGE_NAME,
487                     ApplicationExitInfo.REASON_SIGNALED, OsConstants.SIGKILL, null, now, now2);
488         }
489     }
490 
testKillBySignal()491     public void testKillBySignal() throws Exception {
492         // Remove old records to avoid interference with the test.
493         clearHistoricalExitInfo();
494 
495         long now = System.currentTimeMillis();
496 
497         // Start a process and kill itself
498         startService(ACTION_KILL, STUB_SERVICE_NAME, true, false);
499 
500         long now2 = System.currentTimeMillis();
501         List<ApplicationExitInfo> list = ShellIdentityUtils.invokeMethodWithShellPermissions(
502                 STUB_PACKAGE_NAME, mStubPackagePid, 1,
503                 mActivityManager::getHistoricalProcessExitReasons,
504                 android.Manifest.permission.DUMP);
505 
506         assertTrue(list != null && list.size() == 1);
507         verify(list.get(0), mStubPackagePid, mStubPackageUid, STUB_PACKAGE_NAME,
508                 ApplicationExitInfo.REASON_SIGNALED, OsConstants.SIGKILL, null, now, now2);
509     }
510 
testAnr()511     public void testAnr() throws Exception {
512         // Remove old records to avoid interference with the test.
513         clearHistoricalExitInfo();
514 
515         final DropBoxManager dbox = mContext.getSystemService(DropBoxManager.class);
516         final CountDownLatch dboxLatch = new CountDownLatch(1);
517         final BroadcastReceiver receiver = new BroadcastReceiver() {
518             @Override
519             public void onReceive(Context context, Intent intent) {
520                 final String tag_anr = "data_app_anr";
521                 if (tag_anr.equals(intent.getStringExtra(DropBoxManager.EXTRA_TAG))) {
522                     mAnrEntry = dbox.getNextEntry(tag_anr, intent.getLongExtra(
523                             DropBoxManager.EXTRA_TIME, 0) - 1);
524                     dboxLatch.countDown();
525                 }
526             }
527         };
528         mContext.registerReceiver(receiver,
529                 new IntentFilter(DropBoxManager.ACTION_DROPBOX_ENTRY_ADDED));
530         final long timeout = Settings.Global.getInt(mContext.getContentResolver(),
531                 Settings.Global.BROADCAST_FG_CONSTANTS, 10 * 1000) * 3;
532 
533         long now = System.currentTimeMillis();
534 
535         // Start a process and block its main thread
536         startService(ACTION_ANR, STUB_SERVICE_NAME, false, false);
537 
538         // Sleep for a while to make sure it's already blocking its main thread.
539         sleep(WAITFOR_MSEC);
540 
541         AmMonitor monitor = new AmMonitor(mInstrumentation,
542                 new String[]{AmMonitor.WAIT_FOR_CRASHED});
543 
544         Intent intent = new Intent();
545         intent.setComponent(new ComponentName(STUB_PACKAGE_NAME, STUB_RECEIVER_NAMWE));
546         intent.addFlags(Intent.FLAG_RECEIVER_FOREGROUND);
547         // This will result an ANR
548         mContext.sendOrderedBroadcast(intent, null);
549 
550         // Wait for the early ANR
551         monitor.waitFor(AmMonitor.WAIT_FOR_EARLY_ANR, timeout);
552         // Continue, so we could collect ANR traces
553         monitor.sendCommand(AmMonitor.CMD_CONTINUE);
554         // Wait for the ANR
555         monitor.waitFor(AmMonitor.WAIT_FOR_ANR, timeout);
556         // Kill it
557         monitor.sendCommand(AmMonitor.CMD_KILL);
558         // Wait the process gone
559         waitForGone(mWatcher);
560         long now2 = System.currentTimeMillis();
561 
562         awaitForLatch(dboxLatch);
563         assertTrue(mAnrEntry != null);
564 
565         List<ApplicationExitInfo> list = ShellIdentityUtils.invokeMethodWithShellPermissions(
566                 STUB_PACKAGE_NAME, mStubPackagePid, 1,
567                 mActivityManager::getHistoricalProcessExitReasons,
568                 android.Manifest.permission.DUMP);
569 
570         assertTrue(list != null && list.size() == 1);
571         ApplicationExitInfo info = list.get(0);
572         verify(info, mStubPackagePid, mStubPackageUid, STUB_PACKAGE_NAME,
573                 ApplicationExitInfo.REASON_ANR, null, null, now, now2);
574         assertEquals(mStubPackageUid, info.getPackageUid());
575         assertEquals(mStubPackageUid, info.getDefiningUid());
576 
577         // Verify the traces
578 
579         // Read from dropbox
580         final String dboxTrace = mAnrEntry.getText(0x100000 /* 1M */);
581         assertFalse(TextUtils.isEmpty(dboxTrace));
582 
583         // Read the input stream from the ApplicationExitInfo
584         String trace = ShellIdentityUtils.invokeMethodWithShellPermissions(info, (i) -> {
585             try (BufferedInputStream input = new BufferedInputStream(i.getTraceInputStream())) {
586                 StringBuilder sb = new StringBuilder();
587                 byte[] buf = new byte[8192];
588                 while (true) {
589                     final int len = input.read(buf, 0, buf.length);
590                     if (len <= 0) {
591                         break;
592                     }
593                     sb.append(new String(buf, 0, len));
594                 }
595                 return sb.toString();
596             } catch (IOException e) {
597                 return null;
598             }
599         }, android.Manifest.permission.DUMP);
600         assertFalse(TextUtils.isEmpty(trace));
601         assertTrue(trace.indexOf(Integer.toString(info.getPid())) >= 0);
602         assertTrue(trace.indexOf("Cmd line: " + STUB_PACKAGE_NAME) >= 0);
603         assertTrue(dboxTrace.indexOf(trace) >= 0);
604 
605         monitor.finish();
606         mContext.unregisterReceiver(receiver);
607     }
608 
testOther()609     public void testOther() throws Exception {
610         // Remove old records to avoid interference with the test.
611         clearHistoricalExitInfo();
612 
613         final String servicePackage = "android.externalservice.service";
614         final String keyIBinder = "ibinder";
615         final CountDownLatch latch = new CountDownLatch(1);
616         final Bundle holder = new Bundle();
617         final ServiceConnection connection = new ServiceConnection() {
618             @Override
619             public void onServiceConnected(ComponentName name, IBinder service) {
620                 holder.putBinder(keyIBinder, service);
621                 latch.countDown();
622             }
623 
624             @Override
625             public void onServiceDisconnected(ComponentName name) {
626             }
627         };
628 
629         final Intent intent = new Intent();
630         intent.setComponent(new ComponentName(servicePackage,
631                 servicePackage + ".ExternalServiceWithZygote"));
632 
633         // Bind to that external service, which will become an isolated process
634         // running in the current package user id.
635         assertTrue(mContext.bindService(intent,
636                 Context.BIND_AUTO_CREATE | Context.BIND_EXTERNAL_SERVICE,
637                 AsyncTask.THREAD_POOL_EXECUTOR, connection));
638 
639         awaitForLatch(latch);
640 
641         final IBinder service = holder.getBinder(keyIBinder);
642         assertNotNull(service);
643 
644         // Retrieve its uid/pd/package name info.
645         Messenger remote = new Messenger(service);
646         RunningServiceInfo id = identifyService(remote);
647         assertNotNull(id);
648 
649         assertFalse(id.uid == 0 || id.pid == 0);
650         assertFalse(Process.myUid() == id.uid);
651         assertFalse(Process.myPid() == id.pid);
652         assertEquals(mContext.getPackageName(), id.packageName);
653 
654         final WatchUidRunner watcher = new WatchUidRunner(mInstrumentation,
655                 id.uid, WAITFOR_MSEC);
656 
657         long now = System.currentTimeMillis();
658 
659         // Remove the service connection
660         mContext.unbindService(connection);
661 
662         try {
663             // Isolated process should have been killed as long as its service is done.
664             waitForGone(watcher);
665         } finally {
666             watcher.finish();
667         }
668 
669         long now2 = System.currentTimeMillis();
670         final ActivityManager am = mContext.getSystemService(ActivityManager.class);
671         final List<ApplicationExitInfo> list = am.getHistoricalProcessExitReasons(null, id.pid, 1);
672         assertTrue(list != null && list.size() == 1);
673 
674         ApplicationExitInfo info = list.get(0);
675         verify(info, id.pid, id.uid, null, ApplicationExitInfo.REASON_OTHER, null,
676                 "isolated not needed", now, now2);
677         assertEquals(Process.myUid(), info.getPackageUid());
678         assertEquals(mContext.getPackageManager().getPackageUid(servicePackage, 0),
679                 info.getDefiningUid());
680         assertEquals(ActivityManager.RunningAppProcessInfo.IMPORTANCE_SERVICE,
681                 info.getImportance());
682     }
683 
extractMemString(String dump, String prefix, char nextSep)684     private String extractMemString(String dump, String prefix, char nextSep) {
685         int start = dump.indexOf(prefix);
686         assertTrue(start >= 0);
687         start += prefix.length();
688         int end = dump.indexOf(nextSep, start);
689         assertTrue(end > start);
690         return dump.substring(start, end);
691     }
692 
testPermissionChange()693     public void testPermissionChange() throws Exception {
694         // Remove old records to avoid interference with the test.
695         clearHistoricalExitInfo();
696 
697         // Grant the read calendar permission
698         mInstrumentation.getUiAutomation().grantRuntimePermission(
699                 STUB_PACKAGE_NAME, android.Manifest.permission.READ_CALENDAR);
700         long now = System.currentTimeMillis();
701 
702         // Start a process and do nothing
703         startService(ACTION_FINISH, STUB_SERVICE_NAME, false, false);
704 
705         // Enable high frequency memory sampling
706         executeShellCmd("dumpsys procstats --start-testing");
707         // Sleep for a while to wait for the sampling of memory info
708         sleep(10000);
709         // Stop the high frequency memory sampling
710         executeShellCmd("dumpsys procstats --stop-testing");
711         // Get the memory info from it.
712         String dump = executeShellCmd("dumpsys activity processes " + STUB_PACKAGE_NAME);
713         assertNotNull(dump);
714         final String lastPss = extractMemString(dump, " lastPss=", ' ');
715         final String lastRss = extractMemString(dump, " lastRss=", '\n');
716 
717         // Revoke the read calendar permission
718         mInstrumentation.getUiAutomation().revokeRuntimePermission(
719                 STUB_PACKAGE_NAME, android.Manifest.permission.READ_CALENDAR);
720         waitForGone(mWatcher);
721         long now2 = System.currentTimeMillis();
722 
723         List<ApplicationExitInfo> list = ShellIdentityUtils.invokeMethodWithShellPermissions(
724                 STUB_PACKAGE_NAME, mStubPackagePid, 1,
725                 mActivityManager::getHistoricalProcessExitReasons,
726                 android.Manifest.permission.DUMP);
727 
728         assertTrue(list != null && list.size() == 1);
729 
730         ApplicationExitInfo info = list.get(0);
731         verify(info, mStubPackagePid, mStubPackageUid, STUB_PACKAGE_NAME,
732                 ApplicationExitInfo.REASON_PERMISSION_CHANGE, null, null, now, now2);
733 
734         // Also verify that we get the expected meminfo
735         assertEquals(lastPss, DebugUtils.sizeValueToString(
736                 info.getPss() * 1024, new StringBuilder()));
737         assertEquals(lastRss, DebugUtils.sizeValueToString(
738                 info.getRss() * 1024, new StringBuilder()));
739     }
740 
741     // A clone of testPermissionChange using a different revoke api
testPermissionChangeWithReason()742     public void testPermissionChangeWithReason() throws Exception {
743         String revokeReason = "test reason";
744         // Remove old records to avoid interference with the test.
745         clearHistoricalExitInfo();
746 
747         // Grant the read calendar permission
748         mInstrumentation.getUiAutomation().grantRuntimePermission(
749                 STUB_PACKAGE_NAME, android.Manifest.permission.READ_CALENDAR);
750         long now = System.currentTimeMillis();
751 
752         // Start a process and do nothing
753         startService(ACTION_FINISH, STUB_SERVICE_NAME, false, false);
754 
755         // Enable high frequency memory sampling
756         executeShellCmd("dumpsys procstats --start-testing");
757         // Sleep for a while to wait for the sampling of memory info
758         sleep(10000);
759         // Stop the high frequency memory sampling
760         executeShellCmd("dumpsys procstats --stop-testing");
761         // Get the memory info from it.
762         String dump = executeShellCmd("dumpsys activity processes " + STUB_PACKAGE_NAME);
763         assertNotNull(dump);
764         final String lastPss = extractMemString(dump, " lastPss=", ' ');
765         final String lastRss = extractMemString(dump, " lastRss=", '\n');
766 
767         // Revoke the read calendar permission
768         runWithShellPermissionIdentity(() -> {
769             mContext.getPackageManager().revokeRuntimePermission(STUB_PACKAGE_NAME,
770                     android.Manifest.permission.READ_CALENDAR, Process.myUserHandle(),
771                     revokeReason);
772         });
773         waitForGone(mWatcher);
774         long now2 = System.currentTimeMillis();
775 
776         List<ApplicationExitInfo> list = ShellIdentityUtils.invokeMethodWithShellPermissions(
777                 STUB_PACKAGE_NAME, mStubPackagePid, 1,
778                 mActivityManager::getHistoricalProcessExitReasons,
779                 android.Manifest.permission.DUMP);
780 
781         assertTrue(list != null && list.size() == 1);
782 
783         ApplicationExitInfo info = list.get(0);
784         verify(info, mStubPackagePid, mStubPackageUid, STUB_PACKAGE_NAME,
785                 ApplicationExitInfo.REASON_PERMISSION_CHANGE, null, null, now, now2);
786         assertEquals(revokeReason, info.getDescription());
787 
788         // Also verify that we get the expected meminfo
789         assertEquals(lastPss, DebugUtils.sizeValueToString(
790                 info.getPss() * 1024, new StringBuilder()));
791         assertEquals(lastRss, DebugUtils.sizeValueToString(
792                 info.getRss() * 1024, new StringBuilder()));
793     }
794 
testCrash()795     public void testCrash() throws Exception {
796         // Remove old records to avoid interference with the test.
797         clearHistoricalExitInfo();
798 
799         long now = System.currentTimeMillis();
800 
801         // Start a process and do nothing
802         startService(ACTION_NONE, STUB_SERVICE_NAME, false, false);
803 
804         // Induce a crash
805         executeShellCmd("am crash " + STUB_PACKAGE_NAME);
806         waitForGone(mWatcher);
807         long now2 = System.currentTimeMillis();
808 
809         List<ApplicationExitInfo> list = ShellIdentityUtils.invokeMethodWithShellPermissions(
810                 STUB_PACKAGE_NAME, mStubPackagePid, 1,
811                 mActivityManager::getHistoricalProcessExitReasons,
812                 android.Manifest.permission.DUMP);
813 
814         assertTrue(list != null && list.size() == 1);
815         verify(list.get(0), mStubPackagePid, mStubPackageUid, STUB_PACKAGE_NAME,
816                 ApplicationExitInfo.REASON_CRASH, null, null, now, now2);
817     }
818 
testNativeCrash()819     public void testNativeCrash() throws Exception {
820         // Remove old records to avoid interference with the test.
821         clearHistoricalExitInfo();
822 
823         long now = System.currentTimeMillis();
824 
825         // Start a process and crash it
826         startService(ACTION_NATIVE_CRASH, STUB_SERVICE_NAME, true, false);
827 
828         // Native crashes are handled asynchronously from the actual crash, so
829         // it's possible for us to notice that the process crashed before an
830         // actual tombstone exists.
831         Thread.sleep(1000);
832 
833         long now2 = System.currentTimeMillis();
834         List<ApplicationExitInfo> list = ShellIdentityUtils.invokeMethodWithShellPermissions(
835                 STUB_PACKAGE_NAME, mStubPackagePid, 1,
836                 mActivityManager::getHistoricalProcessExitReasons,
837                 android.Manifest.permission.DUMP);
838 
839         assertTrue(list != null && list.size() == 1);
840         verify(list.get(0), mStubPackagePid, mStubPackageUid, STUB_PACKAGE_NAME,
841                 ApplicationExitInfo.REASON_CRASH_NATIVE, null, null, now, now2);
842 
843         InputStream trace = ShellIdentityUtils.invokeMethodWithShellPermissions(
844                 list.get(0),
845                 (i) -> {
846                     try {
847                         return i.getTraceInputStream();
848                     } catch (IOException ex) {
849                         return null;
850                     }
851                 },
852                 android.Manifest.permission.DUMP);
853 
854         assertNotNull(trace);
855         Tombstone tombstone = Tombstone.parseFrom(trace);
856         assertEquals(tombstone.getPid(), mStubPackagePid);
857     }
858 
testUserRequested()859     public void testUserRequested() throws Exception {
860         // Remove old records to avoid interference with the test.
861         clearHistoricalExitInfo();
862 
863         long now = System.currentTimeMillis();
864 
865         // Start a process and do nothing
866         startService(ACTION_NONE, STUB_SERVICE_NAME, false, false);
867 
868         // Force stop the test package
869         executeShellCmd("am force-stop " + STUB_PACKAGE_NAME);
870 
871         // Wait the process gone
872         waitForGone(mWatcher);
873 
874         long now2 = System.currentTimeMillis();
875         List<ApplicationExitInfo> list = ShellIdentityUtils.invokeMethodWithShellPermissions(
876                 STUB_PACKAGE_NAME, mStubPackagePid, 1,
877                 mActivityManager::getHistoricalProcessExitReasons,
878                 android.Manifest.permission.DUMP);
879 
880         assertTrue(list != null && list.size() == 1);
881         verify(list.get(0), mStubPackagePid, mStubPackageUid, STUB_PACKAGE_NAME,
882                 ApplicationExitInfo.REASON_USER_REQUESTED, null, null, now, now2);
883     }
884 
testDependencyDied()885     public void testDependencyDied() throws Exception {
886         // Remove old records to avoid interference with the test.
887         clearHistoricalExitInfo();
888 
889         // Start a process and acquire the provider
890         startService(ACTION_ACQUIRE_STABLE_PROVIDER, STUB_SERVICE_NAME, false, false);
891 
892         final ActivityManager am = mContext.getSystemService(ActivityManager.class);
893         long now = System.currentTimeMillis();
894         final long timeout = now + WAITFOR_MSEC;
895         int providerPid = -1;
896         while (now < timeout && providerPid < 0) {
897             sleep(1000);
898             List<RunningAppProcessInfo> list = ShellIdentityUtils.invokeMethodWithShellPermissions(
899                     am, (m) -> m.getRunningAppProcesses(),
900                     android.Manifest.permission.REAL_GET_TASKS);
901             for (RunningAppProcessInfo info: list) {
902                 if (info.processName.equals(STUB_REMOTE_ROCESS_NAME)) {
903                     providerPid = info.pid;
904                     break;
905                 }
906             }
907             now = System.currentTimeMillis();
908         }
909         assertTrue(providerPid > 0);
910 
911         now = System.currentTimeMillis();
912         // Now let the provider exit itself
913         startService(ACTION_KILL_PROVIDER, STUB_SERVICE_NAME, false, false, false, false, null);
914 
915         // Wait for both of the processes gone
916         waitForGone(mWatcher);
917         final long now2 = System.currentTimeMillis();
918 
919         List<ApplicationExitInfo> list = ShellIdentityUtils.invokeMethodWithShellPermissions(
920                 STUB_PACKAGE_NAME, mStubPackagePid, 1,
921                 mActivityManager::getHistoricalProcessExitReasons,
922                 android.Manifest.permission.DUMP);
923 
924         assertTrue(list != null && list.size() == 1);
925         verify(list.get(0), mStubPackagePid, mStubPackageUid, STUB_PACKAGE_NAME,
926                 ApplicationExitInfo.REASON_DEPENDENCY_DIED, null, null, now, now2);
927     }
928 
testMultipleProcess()929     public void testMultipleProcess() throws Exception {
930         // Remove old records to avoid interference with the test.
931         clearHistoricalExitInfo();
932 
933         long now = System.currentTimeMillis();
934 
935         // Start a process and kill itself
936         startService(ACTION_KILL, STUB_SERVICE_NAME, true, false);
937 
938         long now2 = System.currentTimeMillis();
939 
940         // Start a remote process and exit
941         startService(ACTION_EXIT, STUB_SERVICE_REMOTE_NAME, true, false);
942 
943         long now3 = System.currentTimeMillis();
944         // Now to get the two reports
945         List<ApplicationExitInfo> list = ShellIdentityUtils.invokeMethodWithShellPermissions(
946                 STUB_PACKAGE_NAME, 0, 2,
947                 mActivityManager::getHistoricalProcessExitReasons,
948                 android.Manifest.permission.DUMP);
949 
950         assertTrue(list != null && list.size() == 2);
951         verify(list.get(0), mStubPackageRemotePid, mStubPackageUid, STUB_REMOTE_ROCESS_NAME,
952                 ApplicationExitInfo.REASON_EXIT_SELF, EXIT_CODE, null, now2, now3);
953         verify(list.get(1), mStubPackagePid, mStubPackageUid, STUB_ROCESS_NAME,
954                 ApplicationExitInfo.REASON_SIGNALED, OsConstants.SIGKILL, null, now, now2);
955 
956         // If we only retrieve one report
957         list = ShellIdentityUtils.invokeMethodWithShellPermissions(
958                 STUB_PACKAGE_NAME, 0, 1,
959                 mActivityManager::getHistoricalProcessExitReasons,
960                 android.Manifest.permission.DUMP);
961 
962         assertTrue(list != null && list.size() == 1);
963         verify(list.get(0), mStubPackageRemotePid, mStubPackageUid, STUB_REMOTE_ROCESS_NAME,
964                 ApplicationExitInfo.REASON_EXIT_SELF, EXIT_CODE, null, now2, now3);
965     }
966 
identifyService(Messenger service)967     private RunningServiceInfo identifyService(Messenger service) throws Exception {
968         final CountDownLatch latch = new CountDownLatch(1);
969         class IdentifyHandler extends Handler {
970             IdentifyHandler() {
971                 super(Looper.getMainLooper());
972             }
973 
974             RunningServiceInfo mInfo;
975 
976             @Override
977             public void handleMessage(Message msg) {
978                 Log.d(TAG, "Received message: " + msg);
979                 switch (msg.what) {
980                     case ServiceMessages.MSG_IDENTIFY_RESPONSE:
981                         msg.getData().setClassLoader(RunningServiceInfo.class.getClassLoader());
982                         mInfo = msg.getData().getParcelable(ServiceMessages.IDENTIFY_INFO);
983                         latch.countDown();
984                         break;
985                 }
986                 super.handleMessage(msg);
987             }
988         }
989 
990         IdentifyHandler handler = new IdentifyHandler();
991         Messenger local = new Messenger(handler);
992 
993         Message msg = Message.obtain(null, ServiceMessages.MSG_IDENTIFY);
994         msg.replyTo = local;
995         service.send(msg);
996         awaitForLatch(latch);
997 
998         return handler.mInfo;
999     }
1000 
prepareTestUser()1001     private void prepareTestUser() throws Exception {
1002         // Create the test user
1003         mOtherUserId = createUser("TestUser_" + SystemClock.uptimeMillis(), false);
1004         mOtherUserHandle = UserHandle.of(mOtherUserId);
1005         // Start the other user
1006         assertTrue(startUser(mOtherUserId, true));
1007         // Install the test helper APK into the other user
1008         installExistingPackageAsUser(STUB_PACKAGE_NAME, mOtherUserId);
1009         installExistingPackageAsUser(mContext.getPackageName(), mOtherUserId);
1010         mStubPackageOtherUid = mContext.getPackageManager().getPackageUidAsUser(
1011                 STUB_PACKAGE_NAME, 0, mOtherUserId);
1012         mOtherUidWatcher = new WatchUidRunner(mInstrumentation, mStubPackageOtherUid,
1013                 WAITFOR_MSEC);
1014     }
1015 
removeTestUserIfNecessary()1016     private void removeTestUserIfNecessary() throws Exception {
1017         if (mSupportMultipleUsers && mOtherUserId > 0) {
1018             // Stop the test user
1019             assertTrue(stopUser(mOtherUserId, true, true));
1020             // Remove the test user
1021             removeUser(mOtherUserId);
1022             mOtherUidWatcher.finish();
1023             mOtherUserId = 0;
1024             mOtherUserHandle = null;
1025             mOtherUidWatcher = null;
1026         }
1027     }
1028 
testSecondaryUser()1029     public void testSecondaryUser() throws Exception {
1030         if (!mSupportMultipleUsers) {
1031             return;
1032         }
1033 
1034         // Remove old records to avoid interference with the test.
1035         clearHistoricalExitInfo();
1036 
1037         // Get the full user permission in order to start service as other user
1038         mInstrumentation.getUiAutomation().adoptShellPermissionIdentity(
1039                 android.Manifest.permission.INTERACT_ACROSS_USERS,
1040                 android.Manifest.permission.INTERACT_ACROSS_USERS_FULL);
1041 
1042         // Create the test user, we'll remove it during tearDown
1043         prepareTestUser();
1044 
1045         final byte[] cookie0 = {(byte) 0x00, (byte) 0x01, (byte) 0x02, (byte) 0x03,
1046                 (byte) 0x04, (byte) 0x05, (byte) 0x06, (byte) 0x07};
1047         final byte[] cookie1 = {(byte) 0x01, (byte) 0x02, (byte) 0x03, (byte) 0x04,
1048                 (byte) 0x05, (byte) 0x06, (byte) 0x07, (byte) 0x08};
1049         final byte[] cookie2 = {(byte) 0x02, (byte) 0x03, (byte) 0x04, (byte) 0x05,
1050                 (byte) 0x06, (byte) 0x07, (byte) 0x08, (byte) 0x01};
1051         final byte[] cookie3 = {(byte) 0x03, (byte) 0x04, (byte) 0x05, (byte) 0x06,
1052                 (byte) 0x07, (byte) 0x08, (byte) 0x01, (byte) 0x02};
1053         final byte[] cookie4 = {(byte) 0x04, (byte) 0x05, (byte) 0x06, (byte) 0x07,
1054                 (byte) 0x08, (byte) 0x01, (byte) 0x02, (byte) 0x03};
1055         final byte[] cookie5 = null;
1056 
1057         long now = System.currentTimeMillis();
1058 
1059         // Start a process and do nothing
1060         startService(ACTION_NONE, STUB_SERVICE_NAME, false, true, false, true, cookie0);
1061         // request to exit by itself with a different cookie
1062         startService(ACTION_EXIT, STUB_SERVICE_NAME, true, false, false, true, cookie1);
1063 
1064         long now2 = System.currentTimeMillis();
1065 
1066         // Start the process in a secondary user and kill itself
1067         startService(ACTION_KILL, STUB_SERVICE_NAME, true, true, true, true, cookie2);
1068 
1069         long now3 = System.currentTimeMillis();
1070 
1071         // Start a remote process in a secondary user and exit
1072         startService(ACTION_EXIT, STUB_SERVICE_REMOTE_NAME, true, true, true, true, cookie3);
1073 
1074         long now4 = System.currentTimeMillis();
1075 
1076         // Start a remote process and kill itself
1077         startService(ACTION_KILL, STUB_SERVICE_REMOTE_NAME, true, true, false, true, cookie4);
1078 
1079         long now5 = System.currentTimeMillis();
1080         // drop the permissions
1081         mInstrumentation.getUiAutomation().dropShellPermissionIdentity();
1082 
1083         List<ApplicationExitInfo> list = null;
1084 
1085         // Now try to query for all users
1086         try {
1087             list = ShellIdentityUtils.invokeMethodWithShellPermissions(
1088                     STUB_PACKAGE_NAME, 0, 0, UserHandle.USER_ALL,
1089                     this::getHistoricalProcessExitReasonsAsUser,
1090                     android.Manifest.permission.DUMP);
1091             fail("Shouldn't be able to query all users");
1092         } catch (IllegalArgumentException e) {
1093             // expected
1094         }
1095 
1096         // Now try to query for "current" user
1097         try {
1098             list = ShellIdentityUtils.invokeMethodWithShellPermissions(
1099                     STUB_PACKAGE_NAME, 0, 0, UserHandle.USER_CURRENT,
1100                     this::getHistoricalProcessExitReasonsAsUser,
1101                     android.Manifest.permission.DUMP);
1102             fail("Shouldn't be able to query current user, explicit user-Id is expected");
1103         } catch (IllegalArgumentException e) {
1104             // expected
1105         }
1106 
1107         // Now only try the current user
1108         list = ShellIdentityUtils.invokeMethodWithShellPermissions(
1109                 STUB_PACKAGE_NAME, 0, 0, mCurrentUserId,
1110                 this::getHistoricalProcessExitReasonsAsUser,
1111                 android.Manifest.permission.DUMP);
1112 
1113         assertTrue(list != null && list.size() == 2);
1114         verify(list.get(0), mStubPackageRemotePid, mStubPackageUid, STUB_REMOTE_ROCESS_NAME,
1115                 ApplicationExitInfo.REASON_SIGNALED, OsConstants.SIGKILL, null, now4, now5,
1116                 cookie4);
1117         verify(list.get(1), mStubPackagePid, mStubPackageUid, STUB_ROCESS_NAME,
1118                 ApplicationExitInfo.REASON_EXIT_SELF, EXIT_CODE, null, now, now2, cookie1);
1119 
1120         // Now try the other user
1121         try {
1122             list = ShellIdentityUtils.invokeMethodWithShellPermissions(
1123                     STUB_PACKAGE_NAME, 0, 0, mOtherUserId,
1124                     this::getHistoricalProcessExitReasonsAsUser,
1125                     android.Manifest.permission.DUMP);
1126             fail("Shouldn't be able to query other users");
1127         } catch (SecurityException e) {
1128             // expected
1129         }
1130 
1131         // Now try the other user with proper permissions
1132         list = ShellIdentityUtils.invokeMethodWithShellPermissions(
1133                 STUB_PACKAGE_NAME, 0, 0, mOtherUserId,
1134                 this::getHistoricalProcessExitReasonsAsUser,
1135                 android.Manifest.permission.DUMP,
1136                 android.Manifest.permission.INTERACT_ACROSS_USERS);
1137 
1138         assertTrue(list != null && list.size() == 2);
1139         verify(list.get(0), mStubPackageRemoteOtherUserPid, mStubPackageOtherUid,
1140                 STUB_REMOTE_ROCESS_NAME, ApplicationExitInfo.REASON_EXIT_SELF, EXIT_CODE,
1141                 null, now3, now4, cookie3);
1142         verify(list.get(1), mStubPackageOtherUserPid, mStubPackageOtherUid, STUB_ROCESS_NAME,
1143                 ApplicationExitInfo.REASON_SIGNALED, OsConstants.SIGKILL, null,
1144                 now2, now3, cookie2);
1145 
1146         // Get the full user permission in order to start service as other user
1147         mInstrumentation.getUiAutomation().adoptShellPermissionIdentity(
1148                 android.Manifest.permission.INTERACT_ACROSS_USERS,
1149                 android.Manifest.permission.INTERACT_ACROSS_USERS_FULL);
1150         // Start the process in a secondary user and do nothing
1151         startService(ACTION_NONE, STUB_SERVICE_NAME, false, true, true, true, cookie5);
1152         // drop the permissions
1153         mInstrumentation.getUiAutomation().dropShellPermissionIdentity();
1154 
1155         long now6 = System.currentTimeMillis();
1156         // Stop the test user
1157         assertTrue(stopUser(mOtherUserId, true, true));
1158         // Wait for being killed
1159         waitForGone(mOtherUidWatcher);
1160 
1161         long now7 = System.currentTimeMillis();
1162         list = ShellIdentityUtils.invokeMethodWithShellPermissions(
1163                 STUB_PACKAGE_NAME, 0, 1, mOtherUserId,
1164                 this::getHistoricalProcessExitReasonsAsUser,
1165                 android.Manifest.permission.DUMP,
1166                 android.Manifest.permission.INTERACT_ACROSS_USERS);
1167         verify(list.get(0), mStubPackageOtherUserPid, mStubPackageOtherUid, STUB_ROCESS_NAME,
1168                 ApplicationExitInfo.REASON_USER_STOPPED, null, null, now6, now7, cookie5);
1169 
1170         int otherUserId = mOtherUserId;
1171         // Now remove the other user
1172         removeUser(mOtherUserId);
1173         mOtherUidWatcher.finish();
1174         mOtherUserId = 0;
1175 
1176         // Poll userInfo to check if the user has been removed, wait up to 10 mins
1177         for (int i = 0; i < 600; i++) {
1178             if (ShellIdentityUtils.invokeMethodWithShellPermissions(otherUserId,
1179                     mUserManager::getUserInfo,
1180                     android.Manifest.permission.CREATE_USERS) != null) {
1181                 // We can still get the userInfo, sleep 1 second and try again
1182                 sleep(1000);
1183             } else {
1184                 Log.d(TAG, "User " + otherUserId + " has been removed");
1185                 break;
1186             }
1187         }
1188         // For now the ACTION_USER_REMOVED should have been sent to all receives,
1189         // we take an extra nap to make sure we've had the broadcast handling settled.
1190         sleep(15 * 1000);
1191 
1192         // Now query the other userId, and it should return nothing.
1193         final Context context = mContext.createPackageContextAsUser("android", 0,
1194                 UserHandle.of(otherUserId));
1195         final ActivityManager am = context.getSystemService(ActivityManager.class);
1196         list = ShellIdentityUtils.invokeMethodWithShellPermissions(
1197                 STUB_PACKAGE_NAME, 0, 0,
1198                 am::getHistoricalProcessExitReasons,
1199                 android.Manifest.permission.DUMP,
1200                 android.Manifest.permission.INTERACT_ACROSS_USERS);
1201         assertTrue(list == null || list.size() == 0);
1202 
1203         // The current user shouldn't be impacted.
1204         list = ShellIdentityUtils.invokeMethodWithShellPermissions(
1205                 STUB_PACKAGE_NAME, 0, 0, mCurrentUserId,
1206                 this::getHistoricalProcessExitReasonsAsUser,
1207                 android.Manifest.permission.DUMP,
1208                 android.Manifest.permission.INTERACT_ACROSS_USERS);
1209 
1210         assertTrue(list != null && list.size() == 2);
1211         verify(list.get(0), mStubPackageRemotePid, mStubPackageUid, STUB_REMOTE_ROCESS_NAME,
1212                 ApplicationExitInfo.REASON_SIGNALED, OsConstants.SIGKILL, null, now4, now5,
1213                 cookie4);
1214         verify(list.get(1), mStubPackagePid, mStubPackageUid, STUB_ROCESS_NAME,
1215                 ApplicationExitInfo.REASON_EXIT_SELF, EXIT_CODE, null, now, now2, cookie1);
1216     }
1217 
verify(ApplicationExitInfo info, int pid, int uid, String processName, int reason, Integer status, String description, long before, long after)1218     private void verify(ApplicationExitInfo info, int pid, int uid, String processName,
1219             int reason, Integer status, String description, long before, long after) {
1220         verify(info, pid, uid, processName, reason, status, description, before, after, null);
1221     }
1222 
verify(ApplicationExitInfo info, int pid, int uid, String processName, int reason, Integer status, String description, long before, long after, byte[] cookie)1223     private void verify(ApplicationExitInfo info, int pid, int uid, String processName,
1224             int reason, Integer status, String description, long before, long after,
1225             byte[] cookie) {
1226         assertNotNull(info);
1227         assertEquals(pid, info.getPid());
1228         assertEquals(uid, info.getRealUid());
1229         assertEquals(UserHandle.of(UserHandle.getUserId(uid)), info.getUserHandle());
1230         if (processName != null) {
1231             assertEquals(processName, info.getProcessName());
1232         }
1233         assertEquals(reason, info.getReason());
1234         if (status != null) {
1235             assertEquals(status.intValue(), info.getStatus());
1236         }
1237         if (description != null) {
1238             assertEquals(description, info.getDescription());
1239         }
1240         assertTrue(before <= info.getTimestamp());
1241         assertTrue(after >= info.getTimestamp());
1242         assertTrue(ArrayUtils.equals(info.getProcessStateSummary(), cookie,
1243                 cookie == null ? 0 : cookie.length));
1244     }
1245 }
1246