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