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 com.android.server.am; 18 19 import static android.app.ActivityManager.PROCESS_STATE_BOUND_FOREGROUND_SERVICE; 20 import static android.app.ActivityManager.PROCESS_STATE_BOUND_TOP; 21 import static android.app.ActivityManager.PROCESS_STATE_CACHED_EMPTY; 22 import static android.app.ActivityManager.PROCESS_STATE_LAST_ACTIVITY; 23 import static android.app.ActivityManager.PROCESS_STATE_RECEIVER; 24 import static android.app.ActivityManager.RunningAppProcessInfo.IMPORTANCE_CACHED; 25 import static android.app.ActivityManager.RunningAppProcessInfo.IMPORTANCE_FOREGROUND; 26 import static android.app.ActivityManager.RunningAppProcessInfo.IMPORTANCE_FOREGROUND_SERVICE; 27 import static android.app.ActivityManager.RunningAppProcessInfo.IMPORTANCE_SERVICE; 28 29 import static androidx.test.platform.app.InstrumentationRegistry.getInstrumentation; 30 31 import static com.android.server.am.ActivityManagerService.Injector; 32 33 import static org.junit.Assert.assertEquals; 34 import static org.junit.Assert.assertNotNull; 35 import static org.junit.Assert.assertNull; 36 import static org.junit.Assert.assertTrue; 37 import static org.mockito.Matchers.anyBoolean; 38 import static org.mockito.Matchers.anyInt; 39 import static org.mockito.Matchers.anyLong; 40 import static org.mockito.Mockito.doNothing; 41 import static org.mockito.Mockito.doReturn; 42 import static org.mockito.Mockito.spy; 43 44 import android.annotation.CurrentTimeMillisLong; 45 import android.app.ApplicationExitInfo; 46 import android.content.ComponentName; 47 import android.content.Context; 48 import android.content.pm.ApplicationInfo; 49 import android.content.pm.PackageManagerInternal; 50 import android.os.Debug; 51 import android.os.FileUtils; 52 import android.os.Handler; 53 import android.os.HandlerThread; 54 import android.os.Process; 55 import android.os.UserHandle; 56 import android.platform.test.annotations.Presubmit; 57 import android.system.OsConstants; 58 import android.text.TextUtils; 59 import android.util.Pair; 60 61 import com.android.internal.util.ArrayUtils; 62 import com.android.server.LocalServices; 63 import com.android.server.ServiceThread; 64 import com.android.server.appop.AppOpsService; 65 import com.android.server.wm.ActivityTaskManagerService; 66 67 import org.junit.After; 68 import org.junit.Before; 69 import org.junit.BeforeClass; 70 import org.junit.Rule; 71 import org.junit.Test; 72 import org.junit.rules.TestRule; 73 import org.junit.runner.Description; 74 import org.junit.runners.model.Statement; 75 import org.mockito.Mock; 76 import org.mockito.MockitoAnnotations; 77 78 import java.io.BufferedInputStream; 79 import java.io.BufferedOutputStream; 80 import java.io.File; 81 import java.io.FileInputStream; 82 import java.io.FileOutputStream; 83 import java.io.IOException; 84 import java.lang.reflect.Field; 85 import java.lang.reflect.Modifier; 86 import java.util.ArrayList; 87 import java.util.Random; 88 import java.util.zip.GZIPInputStream; 89 90 /** 91 * Test class for {@link android.app.ApplicationExitInfo}. 92 * 93 * Build/Install/Run: 94 * atest ApplicationExitInfoTest 95 */ 96 @Presubmit 97 public class ApplicationExitInfoTest { 98 private static final String TAG = ApplicationExitInfoTest.class.getSimpleName(); 99 100 @Rule public ServiceThreadRule mServiceThreadRule = new ServiceThreadRule(); 101 @Mock private AppOpsService mAppOpsService; 102 @Mock private PackageManagerInternal mPackageManagerInt; 103 104 private Context mContext = getInstrumentation().getTargetContext(); 105 private TestInjector mInjector; 106 private ActivityManagerService mAms; 107 private ProcessList mProcessList; 108 private AppExitInfoTracker mAppExitInfoTracker; 109 private Handler mHandler; 110 private HandlerThread mHandlerThread; 111 112 @BeforeClass setUpOnce()113 public static void setUpOnce() { 114 System.setProperty("dexmaker.share_classloader", "true"); 115 } 116 117 @Before setUp()118 public void setUp() { 119 MockitoAnnotations.initMocks(this); 120 121 mHandlerThread = new HandlerThread(TAG); 122 mHandlerThread.start(); 123 mHandler = new Handler(mHandlerThread.getLooper()); 124 mProcessList = spy(new ProcessList()); 125 ProcessList.sKillHandler = null; 126 mAppExitInfoTracker = spy(new AppExitInfoTracker()); 127 setFieldValue(AppExitInfoTracker.class, mAppExitInfoTracker, "mIsolatedUidRecords", 128 spy(mAppExitInfoTracker.new IsolatedUidRecords())); 129 setFieldValue(AppExitInfoTracker.class, mAppExitInfoTracker, "mAppExitInfoSourceZygote", 130 spy(mAppExitInfoTracker.new AppExitInfoExternalSource("zygote", null))); 131 setFieldValue(AppExitInfoTracker.class, mAppExitInfoTracker, "mAppExitInfoSourceLmkd", 132 spy(mAppExitInfoTracker.new AppExitInfoExternalSource("lmkd", 133 ApplicationExitInfo.REASON_LOW_MEMORY))); 134 setFieldValue(AppExitInfoTracker.class, mAppExitInfoTracker, "mAppTraceRetriever", 135 spy(mAppExitInfoTracker.new AppTraceRetriever())); 136 setFieldValue(ProcessList.class, mProcessList, "mAppExitInfoTracker", mAppExitInfoTracker); 137 mInjector = new TestInjector(mContext); 138 mAms = new ActivityManagerService(mInjector, mServiceThreadRule.getThread()); 139 mAms.mActivityTaskManager = new ActivityTaskManagerService(mContext); 140 mAms.mActivityTaskManager.initialize(null, null, mContext.getMainLooper()); 141 mAms.mAtmInternal = spy(mAms.mActivityTaskManager.getAtmInternal()); 142 mAms.mPackageManagerInt = mPackageManagerInt; 143 doReturn(new ComponentName("", "")).when(mPackageManagerInt).getSystemUiServiceComponent(); 144 // Remove stale instance of PackageManagerInternal if there is any 145 LocalServices.removeServiceForTest(PackageManagerInternal.class); 146 LocalServices.addService(PackageManagerInternal.class, mPackageManagerInt); 147 } 148 149 @After tearDown()150 public void tearDown() { 151 LocalServices.removeServiceForTest(PackageManagerInternal.class); 152 mHandlerThread.quit(); 153 ProcessList.sKillHandler = null; 154 } 155 setFieldValue(Class clazz, Object obj, String fieldName, T val)156 private static <T> void setFieldValue(Class clazz, Object obj, String fieldName, T val) { 157 try { 158 Field field = clazz.getDeclaredField(fieldName); 159 field.setAccessible(true); 160 Field mfield = Field.class.getDeclaredField("accessFlags"); 161 mfield.setAccessible(true); 162 mfield.setInt(field, mfield.getInt(field) & ~(Modifier.FINAL | Modifier.PRIVATE)); 163 field.set(obj, val); 164 } catch (NoSuchFieldException | IllegalAccessException e) { 165 } 166 } 167 updateExitInfo(ProcessRecord app, @CurrentTimeMillisLong long timestamp)168 private void updateExitInfo(ProcessRecord app, @CurrentTimeMillisLong long timestamp) { 169 ApplicationExitInfo raw = mAppExitInfoTracker.obtainRawRecord(app, timestamp); 170 mAppExitInfoTracker.handleNoteProcessDiedLocked(raw); 171 mAppExitInfoTracker.recycleRawRecord(raw); 172 } 173 noteAppKill(ProcessRecord app, int reason, int subReason, String msg, @CurrentTimeMillisLong long timestamp)174 private void noteAppKill(ProcessRecord app, int reason, int subReason, String msg, 175 @CurrentTimeMillisLong long timestamp) { 176 ApplicationExitInfo raw = mAppExitInfoTracker.obtainRawRecord(app, timestamp); 177 raw.setReason(reason); 178 raw.setSubReason(subReason); 179 raw.setDescription(msg); 180 mAppExitInfoTracker.handleNoteAppKillLocked(raw); 181 mAppExitInfoTracker.recycleRawRecord(raw); 182 } 183 184 @Test testApplicationExitInfo()185 public void testApplicationExitInfo() throws Exception { 186 mAppExitInfoTracker.clearProcessExitInfo(true); 187 mAppExitInfoTracker.mAppExitInfoLoaded.set(true); 188 mAppExitInfoTracker.mProcExitStoreDir = new File(mContext.getFilesDir(), 189 AppExitInfoTracker.APP_EXIT_STORE_DIR); 190 assertTrue(FileUtils.createDir(mAppExitInfoTracker.mProcExitStoreDir)); 191 mAppExitInfoTracker.mProcExitInfoFile = new File(mAppExitInfoTracker.mProcExitStoreDir, 192 AppExitInfoTracker.APP_EXIT_INFO_FILE); 193 194 // Test application calls System.exit() 195 doNothing().when(mAppExitInfoTracker).schedulePersistProcessExitInfo(anyBoolean()); 196 doReturn(true).when(mAppExitInfoTracker).isFresh(anyLong()); 197 198 final int app1Uid = 10123; 199 final int app1Pid1 = 12345; 200 final int app1Pid2 = 12346; 201 final int app1sPid1 = 13456; 202 final int app1DefiningUid = 23456; 203 final int app1ConnectiongGroup = 10; 204 final int app1UidUser2 = 1010123; 205 final int app1PidUser2 = 12347; 206 final long app1Pss1 = 34567; 207 final long app1Rss1 = 45678; 208 final long app1Pss2 = 34568; 209 final long app1Rss2 = 45679; 210 final long app1Pss3 = 34569; 211 final long app1Rss3 = 45680; 212 final long app1sPss1 = 56789; 213 final long app1sRss1 = 67890; 214 final String app1ProcessName = "com.android.test.stub1:process"; 215 final String app1PackageName = "com.android.test.stub1"; 216 final String app1sProcessName = "com.android.test.stub_shared:process"; 217 final String app1sPackageName = "com.android.test.stub_shared"; 218 final byte[] app1Cookie1 = {(byte) 0x01, (byte) 0x02, (byte) 0x03, (byte) 0x04, 219 (byte) 0x05, (byte) 0x06, (byte) 0x07, (byte) 0x08}; 220 final byte[] app1Cookie2 = {(byte) 0x08, (byte) 0x07, (byte) 0x06, (byte) 0x05, 221 (byte) 0x04, (byte) 0x03, (byte) 0x02, (byte) 0x01}; 222 223 final long now1 = 1; 224 ProcessRecord app = makeProcessRecord( 225 app1Pid1, // pid 226 app1Uid, // uid 227 app1Uid, // packageUid 228 null, // definingUid 229 0, // connectionGroup 230 PROCESS_STATE_LAST_ACTIVITY, // procstate 231 app1Pss1, // pss 232 app1Rss1, // rss 233 app1ProcessName, // processName 234 app1PackageName); // packageName 235 236 // Case 1: basic System.exit() test 237 int exitCode = 5; 238 mAppExitInfoTracker.setProcessStateSummary(app1Uid, app1Pid1, app1Cookie1); 239 assertTrue(ArrayUtils.equals(mAppExitInfoTracker.getProcessStateSummary(app1Uid, 240 app1Pid1), app1Cookie1, app1Cookie1.length)); 241 doReturn(new Pair<Long, Object>(now1, Integer.valueOf(makeExitStatus(exitCode)))) 242 .when(mAppExitInfoTracker.mAppExitInfoSourceZygote) 243 .remove(anyInt(), anyInt()); 244 doReturn(null) 245 .when(mAppExitInfoTracker.mAppExitInfoSourceLmkd) 246 .remove(anyInt(), anyInt()); 247 updateExitInfo(app, now1); 248 249 ArrayList<ApplicationExitInfo> list = new ArrayList<ApplicationExitInfo>(); 250 mAppExitInfoTracker.getExitInfo(app1PackageName, app1Uid, app1Pid1, 0, list); 251 assertEquals(1, list.size()); 252 253 ApplicationExitInfo info = list.get(0); 254 255 verifyApplicationExitInfo( 256 info, // info 257 now1, // timestamp 258 app1Pid1, // pid 259 app1Uid, // uid 260 app1Uid, // packageUid 261 null, // definingUid 262 app1ProcessName, // processName 263 0, // connectionGroup 264 ApplicationExitInfo.REASON_EXIT_SELF, // reason 265 null, // subReason 266 exitCode, // status 267 app1Pss1, // pss 268 app1Rss1, // rss 269 IMPORTANCE_CACHED, // importance 270 null); // description 271 272 assertTrue(ArrayUtils.equals(info.getProcessStateSummary(), app1Cookie1, 273 app1Cookie1.length)); 274 assertEquals(info.getTraceInputStream(), null); 275 276 // Now create a process record from a different package but shared UID. 277 sleep(1); 278 final long now1s = System.currentTimeMillis(); 279 app = makeProcessRecord( 280 app1sPid1, // pid 281 app1Uid, // uid 282 app1Uid, // packageUid 283 null, // definingUid 284 0, // connectionGroup 285 PROCESS_STATE_BOUND_TOP, // procstate 286 app1sPss1, // pss 287 app1sRss1, // rss 288 app1sProcessName, // processName 289 app1sPackageName); // packageName 290 doReturn(new Pair<Long, Object>(now1s, Integer.valueOf(0))) 291 .when(mAppExitInfoTracker.mAppExitInfoSourceZygote) 292 .remove(anyInt(), anyInt()); 293 doReturn(null) 294 .when(mAppExitInfoTracker.mAppExitInfoSourceLmkd) 295 .remove(anyInt(), anyInt()); 296 noteAppKill(app, ApplicationExitInfo.REASON_USER_REQUESTED, 297 ApplicationExitInfo.SUBREASON_UNKNOWN, null, now1s); 298 299 // Case 2: create another app1 process record with a different pid 300 sleep(1); 301 final long now2 = 2; 302 app = makeProcessRecord( 303 app1Pid2, // pid 304 app1Uid, // uid 305 app1Uid, // packageUid 306 app1DefiningUid, // definingUid 307 app1ConnectiongGroup, // connectionGroup 308 PROCESS_STATE_RECEIVER, // procstate 309 app1Pss2, // pss 310 app1Rss2, // rss 311 app1ProcessName, // processName 312 app1PackageName); // packageName 313 exitCode = 6; 314 315 mAppExitInfoTracker.setProcessStateSummary(app1Uid, app1Pid2, app1Cookie1); 316 // Override with a different cookie 317 mAppExitInfoTracker.setProcessStateSummary(app1Uid, app1Pid2, app1Cookie2); 318 assertTrue(ArrayUtils.equals(mAppExitInfoTracker.getProcessStateSummary(app1Uid, 319 app1Pid2), app1Cookie2, app1Cookie2.length)); 320 doReturn(new Pair<Long, Object>(now2, Integer.valueOf(makeExitStatus(exitCode)))) 321 .when(mAppExitInfoTracker.mAppExitInfoSourceZygote) 322 .remove(anyInt(), anyInt()); 323 updateExitInfo(app, now2); 324 list.clear(); 325 326 // Get all the records for app1Uid 327 mAppExitInfoTracker.getExitInfo(null, app1Uid, 0, 0, list); 328 assertEquals(3, list.size()); 329 330 info = list.get(1); 331 332 verifyApplicationExitInfo( 333 info, // info 334 now2, // timestamp 335 app1Pid2, // pid 336 app1Uid, // uid 337 app1Uid, // packageUid 338 app1DefiningUid, // definingUid 339 app1ProcessName, // processName 340 app1ConnectiongGroup, // connectionGroup 341 ApplicationExitInfo.REASON_EXIT_SELF, // reason 342 null, // subReason 343 exitCode, // status 344 app1Pss2, // pss 345 app1Rss2, // rss 346 IMPORTANCE_SERVICE, // importance 347 null); // description 348 349 assertTrue(ArrayUtils.equals(info.getProcessStateSummary(), app1Cookie2, 350 app1Cookie2.length)); 351 352 info = list.get(0); 353 verifyApplicationExitInfo( 354 info, // info 355 now1s, // timestamp 356 app1sPid1, // pid 357 app1Uid, // uid 358 app1Uid, // packageUid 359 null, // definingUid 360 app1sProcessName, // processName 361 0, // connectionGroup 362 ApplicationExitInfo.REASON_USER_REQUESTED, // reason 363 null, // subReason 364 null, // status 365 app1sPss1, // pss 366 app1sRss1, // rss 367 IMPORTANCE_FOREGROUND, // importance 368 null); // description 369 370 info = list.get(2); 371 assertTrue(ArrayUtils.equals(info.getProcessStateSummary(), app1Cookie1, 372 app1Cookie1.length)); 373 374 // Case 3: Create an instance of app1 with different user, and died because of SIGKILL 375 sleep(1); 376 final long now3 = System.currentTimeMillis(); 377 int sigNum = OsConstants.SIGKILL; 378 app = makeProcessRecord( 379 app1PidUser2, // pid 380 app1UidUser2, // uid 381 app1UidUser2, // packageUid 382 null, // definingUid 383 0, // connectionGroup 384 PROCESS_STATE_BOUND_FOREGROUND_SERVICE, // procstate 385 app1Pss3, // pss 386 app1Rss3, // rss 387 app1ProcessName, // processName 388 app1PackageName); // packageName 389 doReturn(new Pair<Long, Object>(now3, Integer.valueOf(makeSignalStatus(sigNum)))) 390 .when(mAppExitInfoTracker.mAppExitInfoSourceZygote) 391 .remove(anyInt(), anyInt()); 392 updateExitInfo(app, now3); 393 list.clear(); 394 mAppExitInfoTracker.getExitInfo(app1PackageName, app1UidUser2, app1PidUser2, 0, list); 395 396 assertEquals(1, list.size()); 397 398 info = list.get(0); 399 400 verifyApplicationExitInfo( 401 info, // info 402 now3, // timestamp 403 app1PidUser2, // pid 404 app1UidUser2, // uid 405 app1UidUser2, // packageUid 406 null, // definingUid 407 app1ProcessName, // processName 408 0, // connectionGroup 409 ApplicationExitInfo.REASON_SIGNALED, // reason 410 null, // subReason 411 sigNum, // status 412 app1Pss3, // pss 413 app1Rss3, // rss 414 IMPORTANCE_FOREGROUND_SERVICE, // importance 415 null); // description 416 417 // try go get all from app1UidUser2 418 list.clear(); 419 mAppExitInfoTracker.getExitInfo(app1PackageName, app1UidUser2, 0, 0, list); 420 assertEquals(1, list.size()); 421 422 info = list.get(0); 423 424 verifyApplicationExitInfo( 425 info, // info 426 now3, // timestamp 427 app1PidUser2, // pid 428 app1UidUser2, // uid 429 app1UidUser2, // packageUid 430 null, // definingUid 431 app1ProcessName, // processName 432 0, // connectionGroup 433 ApplicationExitInfo.REASON_SIGNALED, // reason 434 null, // subReason 435 sigNum, // status 436 app1Pss3, // pss 437 app1Rss3, // rss 438 IMPORTANCE_FOREGROUND_SERVICE, // importance 439 null); // description 440 441 // Case 4: Create a process from another package with kill from lmkd 442 final int app2UidUser2 = 1010234; 443 final int app2PidUser2 = 12348; 444 final long app2Pss1 = 54321; 445 final long app2Rss1 = 65432; 446 final String app2ProcessName = "com.android.test.stub2:process"; 447 final String app2PackageName = "com.android.test.stub2"; 448 449 sleep(1); 450 final long now4 = System.currentTimeMillis(); 451 doReturn(new Pair<Long, Object>(now4, Integer.valueOf(0))) 452 .when(mAppExitInfoTracker.mAppExitInfoSourceZygote) 453 .remove(anyInt(), anyInt()); 454 doReturn(new Pair<Long, Object>(now4, null)) 455 .when(mAppExitInfoTracker.mAppExitInfoSourceLmkd) 456 .remove(anyInt(), anyInt()); 457 458 app = makeProcessRecord( 459 app2PidUser2, // pid 460 app2UidUser2, // uid 461 app2UidUser2, // packageUid 462 null, // definingUid 463 0, // connectionGroup 464 PROCESS_STATE_CACHED_EMPTY, // procstate 465 app2Pss1, // pss 466 app2Rss1, // rss 467 app2ProcessName, // processName 468 app2PackageName); // packageName 469 updateExitInfo(app, now4); 470 list.clear(); 471 mAppExitInfoTracker.getExitInfo(app2PackageName, app2UidUser2, app2PidUser2, 0, list); 472 assertEquals(1, list.size()); 473 474 info = list.get(0); 475 476 verifyApplicationExitInfo( 477 info, // info 478 now4, // timestamp 479 app2PidUser2, // pid 480 app2UidUser2, // uid 481 app2UidUser2, // packageUid 482 null, // definingUid 483 app2ProcessName, // processName 484 0, // connectionGroup 485 ApplicationExitInfo.REASON_LOW_MEMORY, // reason 486 null, // subReason 487 0, // status 488 app2Pss1, // pss 489 app2Rss1, // rss 490 IMPORTANCE_CACHED, // importance 491 null); // description 492 493 // Verify to get all from User2 regarding app2 494 list.clear(); 495 mAppExitInfoTracker.getExitInfo(null, app2UidUser2, 0, 0, list); 496 assertEquals(1, list.size()); 497 498 // Case 5: App native crash 499 final int app3UidUser2 = 1010345; 500 final int app3PidUser2 = 12349; 501 final int app3ConnectiongGroup = 4; 502 final long app3Pss1 = 54320; 503 final long app3Rss1 = 65430; 504 final String app3ProcessName = "com.android.test.stub3:process"; 505 final String app3PackageName = "com.android.test.stub3"; 506 final String app3Description = "native crash"; 507 508 sleep(1); 509 final long now5 = System.currentTimeMillis(); 510 sigNum = OsConstants.SIGABRT; 511 doReturn(new Pair<Long, Object>(now5, Integer.valueOf(makeSignalStatus(sigNum)))) 512 .when(mAppExitInfoTracker.mAppExitInfoSourceZygote) 513 .remove(anyInt(), anyInt()); 514 doReturn(null) 515 .when(mAppExitInfoTracker.mAppExitInfoSourceLmkd) 516 .remove(anyInt(), anyInt()); 517 app = makeProcessRecord( 518 app3PidUser2, // pid 519 app3UidUser2, // uid 520 app3UidUser2, // packageUid 521 null, // definingUid 522 app3ConnectiongGroup, // connectionGroup 523 PROCESS_STATE_BOUND_TOP, // procstate 524 app3Pss1, // pss 525 app3Rss1, // rss 526 app3ProcessName, // processName 527 app3PackageName); // packageName 528 noteAppKill(app, ApplicationExitInfo.REASON_CRASH_NATIVE, 529 ApplicationExitInfo.SUBREASON_UNKNOWN, app3Description, now5); 530 531 updateExitInfo(app, now5); 532 list.clear(); 533 mAppExitInfoTracker.getExitInfo(app3PackageName, app3UidUser2, app3PidUser2, 0, list); 534 assertEquals(1, list.size()); 535 536 info = list.get(0); 537 538 verifyApplicationExitInfo( 539 info, // info 540 now5, // timestamp 541 app3PidUser2, // pid 542 app3UidUser2, // uid 543 app3UidUser2, // packageUid 544 null, // definingUid 545 app3ProcessName, // processName 546 app3ConnectiongGroup, // connectionGroup 547 ApplicationExitInfo.REASON_CRASH_NATIVE, // reason 548 null, // subReason 549 sigNum, // status 550 app3Pss1, // pss 551 app3Rss1, // rss 552 IMPORTANCE_FOREGROUND, // importance 553 app3Description); // description 554 555 // Verify the most recent kills, sorted by timestamp 556 int maxNum = 3; 557 list.clear(); 558 mAppExitInfoTracker.getExitInfo(null, app3UidUser2, 0, maxNum, list); 559 assertEquals(1, list.size()); 560 561 info = list.get(0); 562 563 verifyApplicationExitInfo( 564 info, // info 565 now5, // timestamp 566 app3PidUser2, // pid 567 app3UidUser2, // uid 568 app3UidUser2, // packageUid 569 null, // definingUid 570 app3ProcessName, // processName 571 app3ConnectiongGroup, // connectionGroup 572 ApplicationExitInfo.REASON_CRASH_NATIVE, // reason 573 null, // subReason 574 sigNum, // status 575 app3Pss1, // pss 576 app3Rss1, // rss 577 IMPORTANCE_FOREGROUND, // importance 578 app3Description); // description 579 580 list.clear(); 581 mAppExitInfoTracker.getExitInfo(null, app2UidUser2, 0, maxNum, list); 582 assertEquals(1, list.size()); 583 info = list.get(0); 584 585 verifyApplicationExitInfo( 586 info, // info 587 now4, // timestamp 588 app2PidUser2, // pid 589 app2UidUser2, // uid 590 app2UidUser2, // packageUid 591 null, // definingUid 592 app2ProcessName, // processName 593 0, // connectionGroup 594 ApplicationExitInfo.REASON_LOW_MEMORY, // reason 595 null, // subReason 596 0, // status 597 app2Pss1, // pss 598 app2Rss1, // rss 599 IMPORTANCE_CACHED, // importance 600 null); // description 601 602 list.clear(); 603 mAppExitInfoTracker.getExitInfo(null, app1UidUser2, 0, maxNum, list); 604 assertEquals(1, list.size()); 605 info = list.get(0); 606 607 sigNum = OsConstants.SIGKILL; 608 verifyApplicationExitInfo( 609 info, // info 610 now3, // timestamp 611 app1PidUser2, // pid 612 app1UidUser2, // uid 613 app1UidUser2, // packageUid 614 null, // definingUid 615 app1ProcessName, // processName 616 0, // connectionGroup 617 ApplicationExitInfo.REASON_SIGNALED, // reason 618 null, // subReason 619 sigNum, // status 620 app1Pss3, // pss 621 app1Rss3, // rss 622 IMPORTANCE_FOREGROUND_SERVICE, // importance 623 null); // description 624 625 // Case 6: App Java crash 626 final int app3Uid = 10345; 627 final int app3IsolatedUid = 99001; // it's an isolated process 628 final int app3Pid = 12350; 629 final long app3Pss2 = 23232; 630 final long app3Rss2 = 32323; 631 final String app3Description2 = "force close"; 632 633 sleep(1); 634 final long now6 = System.currentTimeMillis(); 635 doReturn(null) 636 .when(mAppExitInfoTracker.mAppExitInfoSourceZygote) 637 .remove(anyInt(), anyInt()); 638 doReturn(null) 639 .when(mAppExitInfoTracker.mAppExitInfoSourceLmkd) 640 .remove(anyInt(), anyInt()); 641 app = makeProcessRecord( 642 app3Pid, // pid 643 app3IsolatedUid, // uid 644 app3Uid, // packageUid 645 null, // definingUid 646 0, // connectionGroup 647 PROCESS_STATE_CACHED_EMPTY, // procstate 648 app3Pss2, // pss 649 app3Rss2, // rss 650 app3ProcessName, // processName 651 app3PackageName); // packageName 652 mAppExitInfoTracker.mIsolatedUidRecords.addIsolatedUid(app3IsolatedUid, app3Uid); 653 noteAppKill(app, ApplicationExitInfo.REASON_CRASH, 654 ApplicationExitInfo.SUBREASON_UNKNOWN, app3Description2, now6); 655 656 assertEquals(app3Uid, mAppExitInfoTracker.mIsolatedUidRecords 657 .getUidByIsolatedUid(app3IsolatedUid).longValue()); 658 updateExitInfo(app, now6); 659 assertNull(mAppExitInfoTracker.mIsolatedUidRecords.getUidByIsolatedUid(app3IsolatedUid)); 660 661 list.clear(); 662 mAppExitInfoTracker.getExitInfo(app3PackageName, app3Uid, 0, 1, list); 663 assertEquals(1, list.size()); 664 665 info = list.get(0); 666 667 verifyApplicationExitInfo( 668 info, // info 669 now6, // timestamp 670 app3Pid, // pid 671 app3IsolatedUid, // uid 672 app3Uid, // packageUid 673 null, // definingUid 674 app3ProcessName, // processName 675 0, // connectionGroup 676 ApplicationExitInfo.REASON_CRASH, // reason 677 null, // subReason 678 0, // status 679 app3Pss2, // pss 680 app3Rss2, // rss 681 IMPORTANCE_CACHED, // importance 682 app3Description2); // description 683 684 // Case 7: App1 is "uninstalled" from User2 685 mAppExitInfoTracker.onPackageRemoved(app1PackageName, app1UidUser2, false); 686 list.clear(); 687 mAppExitInfoTracker.getExitInfo(app1PackageName, app1UidUser2, 0, 0, list); 688 assertEquals(0, list.size()); 689 690 list.clear(); 691 mAppExitInfoTracker.getExitInfo(app1PackageName, app1Uid, 0, 0, list); 692 assertEquals(2, list.size()); 693 694 info = list.get(0); 695 696 verifyApplicationExitInfo( 697 info, // info 698 now2, // timestamp 699 app1Pid2, // pid 700 app1Uid, // uid 701 app1Uid, // packageUid 702 app1DefiningUid, // definingUid 703 app1ProcessName, // processName 704 app1ConnectiongGroup, // connectionGroup 705 ApplicationExitInfo.REASON_EXIT_SELF, // reason 706 null, // subReason 707 exitCode, // status 708 app1Pss2, // pss 709 app1Rss2, // rss 710 IMPORTANCE_SERVICE, // importance 711 null); // description 712 713 // Case 8: App1 gets "remove task" 714 final String app1Description = "remove task"; 715 716 sleep(1); 717 final int app1IsolatedUidUser2 = 1099002; // isolated uid 718 final long app1Pss4 = 34343; 719 final long app1Rss4 = 43434; 720 final long now8 = System.currentTimeMillis(); 721 sigNum = OsConstants.SIGKILL; 722 doReturn(new Pair<Long, Object>(now8, makeSignalStatus(sigNum))) 723 .when(mAppExitInfoTracker.mAppExitInfoSourceZygote) 724 .remove(anyInt(), anyInt()); 725 doReturn(null) 726 .when(mAppExitInfoTracker.mAppExitInfoSourceLmkd) 727 .remove(anyInt(), anyInt()); 728 app = makeProcessRecord( 729 app1PidUser2, // pid 730 app1IsolatedUidUser2, // uid 731 app1UidUser2, // packageUid 732 null, // definingUid 733 0, // connectionGroup 734 PROCESS_STATE_CACHED_EMPTY, // procstate 735 app1Pss4, // pss 736 app1Rss4, // rss 737 app1ProcessName, // processName 738 app1PackageName); // packageName 739 740 mAppExitInfoTracker.mIsolatedUidRecords.addIsolatedUid(app1IsolatedUidUser2, app1UidUser2); 741 noteAppKill(app, ApplicationExitInfo.REASON_OTHER, 742 ApplicationExitInfo.SUBREASON_UNKNOWN, app1Description, now8); 743 744 updateExitInfo(app, now8); 745 list.clear(); 746 mAppExitInfoTracker.getExitInfo(app1PackageName, app1UidUser2, app1PidUser2, 1, list); 747 assertEquals(1, list.size()); 748 749 info = list.get(0); 750 751 verifyApplicationExitInfo( 752 info, // info 753 now8, // timestamp 754 app1PidUser2, // pid 755 app1IsolatedUidUser2, // uid 756 app1UidUser2, // packageUid 757 null, // definingUid 758 app1ProcessName, // processName 759 0, // connectionGroup 760 ApplicationExitInfo.REASON_OTHER, // reason 761 ApplicationExitInfo.SUBREASON_UNKNOWN, // subReason 762 0, // status 763 app1Pss4, // pss 764 app1Rss4, // rss 765 IMPORTANCE_CACHED, // importance 766 app1Description); // description 767 768 // App1 gets "too many empty" 769 final String app1Description2 = "too many empty"; 770 sleep(1); 771 final int app1Pid2User2 = 56565; 772 final int app1IsolatedUid2User2 = 1099003; // isolated uid 773 final long app1Pss5 = 34344; 774 final long app1Rss5 = 43435; 775 final long now9 = System.currentTimeMillis(); 776 sigNum = OsConstants.SIGKILL; 777 doReturn(new Pair<Long, Object>(now9, makeSignalStatus(sigNum))) 778 .when(mAppExitInfoTracker.mAppExitInfoSourceZygote) 779 .remove(anyInt(), anyInt()); 780 doReturn(null) 781 .when(mAppExitInfoTracker.mAppExitInfoSourceLmkd) 782 .remove(anyInt(), anyInt()); 783 app = makeProcessRecord( 784 app1Pid2User2, // pid 785 app1IsolatedUid2User2, // uid 786 app1UidUser2, // packageUid 787 null, // definingUid 788 0, // connectionGroup 789 PROCESS_STATE_CACHED_EMPTY, // procstate 790 app1Pss5, // pss 791 app1Rss5, // rss 792 app1ProcessName, // processName 793 app1PackageName); // packageName 794 795 mAppExitInfoTracker.mIsolatedUidRecords.addIsolatedUid(app1IsolatedUid2User2, app1UidUser2); 796 797 // Pretent it gets an ANR trace too (although the reason here should be REASON_ANR) 798 final File traceFile = new File(mContext.getFilesDir(), "anr_original.txt"); 799 final int traceSize = 10240; 800 final int traceStart = 1024; 801 final int traceEnd = 8192; 802 createRandomFile(traceFile, traceSize); 803 assertEquals(traceSize, traceFile.length()); 804 mAppExitInfoTracker.handleLogAnrTrace(app.getPid(), app.uid, app.getPackageList(), 805 traceFile, traceStart, traceEnd); 806 807 noteAppKill(app, ApplicationExitInfo.REASON_OTHER, 808 ApplicationExitInfo.SUBREASON_TOO_MANY_EMPTY, app1Description2, now9); 809 updateExitInfo(app, now9); 810 list.clear(); 811 mAppExitInfoTracker.getExitInfo(app1PackageName, app1UidUser2, app1Pid2User2, 1, list); 812 assertEquals(1, list.size()); 813 814 info = list.get(0); 815 816 verifyApplicationExitInfo( 817 info, // info 818 now9, // timestamp 819 app1Pid2User2, // pid 820 app1IsolatedUid2User2, // uid 821 app1UidUser2, // packageUid 822 null, // definingUid 823 app1ProcessName, // processName 824 0, // connectionGroup 825 ApplicationExitInfo.REASON_OTHER, // reason 826 ApplicationExitInfo.SUBREASON_TOO_MANY_EMPTY, // subReason 827 0, // status 828 app1Pss5, // pss 829 app1Rss5, // rss 830 IMPORTANCE_CACHED, // importance 831 app1Description2); // description 832 833 // Verify if the traceFile get copied into the records correctly. 834 verifyTraceFile(traceFile, traceStart, info.getTraceFile(), 0, traceEnd - traceStart); 835 traceFile.delete(); 836 info.getTraceFile().delete(); 837 838 // Case 9: User2 gets removed 839 sleep(1); 840 mAppExitInfoTracker.mIsolatedUidRecords.addIsolatedUid(app1IsolatedUidUser2, app1UidUser2); 841 mAppExitInfoTracker.mIsolatedUidRecords.addIsolatedUid(app3IsolatedUid, app3Uid); 842 843 mAppExitInfoTracker.onUserRemoved(UserHandle.getUserId(app1UidUser2)); 844 845 assertNull(mAppExitInfoTracker.mIsolatedUidRecords.getUidByIsolatedUid( 846 app1IsolatedUidUser2)); 847 assertNotNull(mAppExitInfoTracker.mIsolatedUidRecords.getUidByIsolatedUid( 848 app3IsolatedUid)); 849 mAppExitInfoTracker.mIsolatedUidRecords.addIsolatedUid( 850 app1IsolatedUidUser2, app1UidUser2); 851 mAppExitInfoTracker.mIsolatedUidRecords.removeAppUid(app1UidUser2, false); 852 assertNull(mAppExitInfoTracker.mIsolatedUidRecords.getUidByIsolatedUid( 853 app1IsolatedUidUser2)); 854 mAppExitInfoTracker.mIsolatedUidRecords.removeAppUid(app3Uid, true); 855 assertNull(mAppExitInfoTracker.mIsolatedUidRecords.getUidByIsolatedUid(app3IsolatedUid)); 856 857 list.clear(); 858 mAppExitInfoTracker.getExitInfo(null, app1UidUser2, 0, 0, list); 859 assertEquals(0, list.size()); 860 861 list.clear(); 862 mAppExitInfoTracker.getExitInfo(null, app1Uid, 0, 0, list); 863 assertEquals(3, list.size()); 864 865 info = list.get(1); 866 867 exitCode = 6; 868 verifyApplicationExitInfo( 869 info, // info 870 now2, // timestamp 871 app1Pid2, // pid 872 app1Uid, // uid 873 app1Uid, // packageUid 874 app1DefiningUid, // definingUid 875 app1ProcessName, // processName 876 app1ConnectiongGroup, // connectionGroup 877 ApplicationExitInfo.REASON_EXIT_SELF, // reason 878 null, // subReason 879 exitCode, // status 880 app1Pss2, // pss 881 app1Rss2, // rss 882 IMPORTANCE_SERVICE, // importance 883 null); // description 884 885 info = list.get(0); 886 verifyApplicationExitInfo( 887 info, // info 888 now1s, // timestamp 889 app1sPid1, // pid 890 app1Uid, // uid 891 app1Uid, // packageUid 892 null, // definingUid 893 app1sProcessName, // processName 894 0, // connectionGroup 895 ApplicationExitInfo.REASON_USER_REQUESTED, // reason 896 null, // subReason 897 null, // status 898 app1sPss1, // pss 899 app1sRss1, // rss 900 IMPORTANCE_FOREGROUND, // importance 901 null); // description 902 903 info = list.get(2); 904 exitCode = 5; 905 verifyApplicationExitInfo( 906 info, // info 907 now1, // timestamp 908 app1Pid1, // pid 909 app1Uid, // uid 910 app1Uid, // packageUid 911 null, // definingUid 912 app1ProcessName, // processName 913 0, // connectionGroup 914 ApplicationExitInfo.REASON_EXIT_SELF, // reason 915 null, // subReason 916 exitCode, // status 917 app1Pss1, // pss 918 app1Rss1, // rss 919 IMPORTANCE_CACHED, // importance 920 null); // description 921 922 // Case 10: Save the info and load them again 923 ArrayList<ApplicationExitInfo> original = new ArrayList<ApplicationExitInfo>(); 924 mAppExitInfoTracker.getExitInfo(null, app1Uid, 0, 0, original); 925 assertTrue(original.size() > 0); 926 927 mAppExitInfoTracker.persistProcessExitInfo(); 928 assertTrue(mAppExitInfoTracker.mProcExitInfoFile.exists()); 929 930 mAppExitInfoTracker.clearProcessExitInfo(false); 931 list.clear(); 932 mAppExitInfoTracker.getExitInfo(null, app1Uid, 0, 0, list); 933 assertEquals(0, list.size()); 934 935 mAppExitInfoTracker.loadExistingProcessExitInfo(); 936 list.clear(); 937 mAppExitInfoTracker.getExitInfo(null, app1Uid, 0, 0, list); 938 assertEquals(original.size(), list.size()); 939 940 for (int i = list.size() - 1; i >= 0; i--) { 941 assertTrue(list.get(i).equals(original.get(i))); 942 } 943 } 944 makeExitStatus(int exitCode)945 private static int makeExitStatus(int exitCode) { 946 return (exitCode << 8) & 0xff00; 947 } 948 makeSignalStatus(int sigNum)949 private static int makeSignalStatus(int sigNum) { 950 return sigNum & 0x7f; 951 } 952 sleep(long ms)953 private void sleep(long ms) { 954 try { 955 Thread.sleep(ms); 956 } catch (InterruptedException e) { 957 } 958 } 959 createRandomFile(File file, int size)960 private static void createRandomFile(File file, int size) throws IOException { 961 try (BufferedOutputStream out = new BufferedOutputStream(new FileOutputStream(file))) { 962 Random random = new Random(); 963 byte[] buf = random.ints('a', 'z').limit(size).collect( 964 StringBuilder::new, StringBuilder::appendCodePoint, StringBuilder::append) 965 .toString().getBytes(); 966 out.write(buf); 967 } 968 } 969 verifyTraceFile(File originFile, int originStart, File traceFile, int traceStart, int length)970 private static void verifyTraceFile(File originFile, int originStart, File traceFile, 971 int traceStart, int length) throws IOException { 972 assertTrue(originFile.exists()); 973 assertTrue(traceFile.exists()); 974 assertTrue(originStart < originFile.length()); 975 try (GZIPInputStream traceIn = new GZIPInputStream(new FileInputStream(traceFile)); 976 BufferedInputStream originIn = new BufferedInputStream( 977 new FileInputStream(originFile))) { 978 assertEquals(traceStart, traceIn.skip(traceStart)); 979 assertEquals(originStart, originIn.skip(originStart)); 980 byte[] buf1 = new byte[8192]; 981 byte[] buf2 = new byte[8192]; 982 while (length > 0) { 983 int len = traceIn.read(buf1, 0, Math.min(buf1.length, length)); 984 assertEquals(len, originIn.read(buf2, 0, len)); 985 assertTrue(ArrayUtils.equals(buf1, buf2, len)); 986 length -= len; 987 } 988 } 989 } 990 makeProcessRecord(int pid, int uid, int packageUid, Integer definingUid, int connectionGroup, int procState, long pss, long rss, String processName, String packageName)991 private ProcessRecord makeProcessRecord(int pid, int uid, int packageUid, Integer definingUid, 992 int connectionGroup, int procState, long pss, long rss, 993 String processName, String packageName) { 994 ApplicationInfo ai = new ApplicationInfo(); 995 ai.packageName = packageName; 996 ProcessRecord app = new ProcessRecord(mAms, ai, processName, uid); 997 app.setPid(pid); 998 app.info.uid = packageUid; 999 if (definingUid != null) { 1000 final String dummyPackageName = "com.android.test"; 1001 final String dummyClassName = ".Foo"; 1002 app.setHostingRecord(HostingRecord.byAppZygote(new ComponentName( 1003 dummyPackageName, dummyClassName), "", definingUid, "")); 1004 } 1005 app.mServices.setConnectionGroup(connectionGroup); 1006 app.mState.setReportedProcState(procState); 1007 app.mProfile.setLastMemInfo(spy(new Debug.MemoryInfo())); 1008 app.mProfile.setLastPss(pss); 1009 app.mProfile.setLastRss(rss); 1010 return app; 1011 } 1012 verifyApplicationExitInfo(ApplicationExitInfo info, Long timestamp, Integer pid, Integer uid, Integer packageUid, Integer definingUid, String processName, Integer connectionGroup, Integer reason, Integer subReason, Integer status, Long pss, Long rss, Integer importance, String description)1013 private void verifyApplicationExitInfo(ApplicationExitInfo info, 1014 Long timestamp, Integer pid, Integer uid, Integer packageUid, 1015 Integer definingUid, String processName, Integer connectionGroup, 1016 Integer reason, Integer subReason, Integer status, 1017 Long pss, Long rss, Integer importance, String description) { 1018 assertNotNull(info); 1019 1020 if (timestamp != null) { 1021 final long tolerance = 10000; // ms 1022 assertTrue(timestamp - tolerance <= info.getTimestamp()); 1023 assertTrue(timestamp + tolerance >= info.getTimestamp()); 1024 } 1025 if (pid != null) { 1026 assertEquals(pid.intValue(), info.getPid()); 1027 } 1028 if (uid != null) { 1029 assertEquals(uid.intValue(), info.getRealUid()); 1030 } 1031 if (packageUid != null) { 1032 assertEquals(packageUid.intValue(), info.getPackageUid()); 1033 } 1034 if (definingUid != null) { 1035 assertEquals(definingUid.intValue(), info.getDefiningUid()); 1036 } 1037 if (processName != null) { 1038 assertTrue(TextUtils.equals(processName, info.getProcessName())); 1039 } 1040 if (connectionGroup != null) { 1041 assertEquals(connectionGroup.intValue(), info.getConnectionGroup()); 1042 } 1043 if (reason != null) { 1044 assertEquals(reason.intValue(), info.getReason()); 1045 } 1046 if (subReason != null) { 1047 assertEquals(subReason.intValue(), info.getSubReason()); 1048 } 1049 if (status != null) { 1050 assertEquals(status.intValue(), info.getStatus()); 1051 } 1052 if (pss != null) { 1053 assertEquals(pss.longValue(), info.getPss()); 1054 } 1055 if (rss != null) { 1056 assertEquals(rss.longValue(), info.getRss()); 1057 } 1058 if (importance != null) { 1059 assertEquals(importance.intValue(), info.getImportance()); 1060 } 1061 if (description != null) { 1062 assertTrue(TextUtils.equals(description, info.getDescription())); 1063 } 1064 } 1065 1066 private class TestInjector extends Injector { TestInjector(Context context)1067 TestInjector(Context context) { 1068 super(context); 1069 } 1070 1071 @Override getAppOpsService(File file, Handler handler)1072 public AppOpsService getAppOpsService(File file, Handler handler) { 1073 return mAppOpsService; 1074 } 1075 1076 @Override getUiHandler(ActivityManagerService service)1077 public Handler getUiHandler(ActivityManagerService service) { 1078 return mHandler; 1079 } 1080 1081 @Override getProcessList(ActivityManagerService service)1082 public ProcessList getProcessList(ActivityManagerService service) { 1083 return mProcessList; 1084 } 1085 } 1086 1087 static class ServiceThreadRule implements TestRule { 1088 1089 private ServiceThread mThread; 1090 getThread()1091 ServiceThread getThread() { 1092 return mThread; 1093 } 1094 1095 @Override apply(Statement base, Description description)1096 public Statement apply(Statement base, Description description) { 1097 return new Statement() { 1098 @Override 1099 public void evaluate() throws Throwable { 1100 mThread = new ServiceThread("TestServiceThread", 1101 Process.THREAD_PRIORITY_DEFAULT, true /* allowIo */); 1102 mThread.start(); 1103 try { 1104 base.evaluate(); 1105 } finally { 1106 mThread.getThreadHandler().runWithScissors(mThread::quit, 0 /* timeout */); 1107 } 1108 } 1109 }; 1110 } 1111 } 1112 } 1113