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