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