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