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