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 sleep(1); 715 final int app1IsolatedUidUser2 = 1099002; // isolated uid 716 final long app1Pss4 = 34343; 717 final long app1Rss4 = 43434; 718 final long now8 = System.currentTimeMillis(); 719 sigNum = OsConstants.SIGKILL; 720 doReturn(new Pair<Long, Object>(now8, makeSignalStatus(sigNum))) 721 .when(mAppExitInfoTracker.mAppExitInfoSourceZygote) 722 .remove(anyInt(), anyInt()); 723 doReturn(null) 724 .when(mAppExitInfoTracker.mAppExitInfoSourceLmkd) 725 .remove(anyInt(), anyInt()); 726 app = makeProcessRecord( 727 app1PidUser2, // pid 728 app1IsolatedUidUser2, // uid 729 app1UidUser2, // packageUid 730 null, // definingUid 731 0, // connectionGroup 732 PROCESS_STATE_CACHED_EMPTY, // procstate 733 app1Pss4, // pss 734 app1Rss4, // rss 735 app1ProcessName, // processName 736 app1PackageName); // packageName 737 738 mAppExitInfoTracker.mIsolatedUidRecords.addIsolatedUid(app1IsolatedUidUser2, app1UidUser2); 739 noteAppKill(app, ApplicationExitInfo.REASON_OTHER, 740 ApplicationExitInfo.SUBREASON_REMOVE_TASK, null, now8); 741 742 updateExitInfo(app, now8); 743 list.clear(); 744 mAppExitInfoTracker.getExitInfo(app1PackageName, app1UidUser2, app1PidUser2, 1, list); 745 assertEquals(1, list.size()); 746 747 info = list.get(0); 748 749 verifyApplicationExitInfo( 750 info, // info 751 now8, // timestamp 752 app1PidUser2, // pid 753 app1IsolatedUidUser2, // uid 754 app1UidUser2, // packageUid 755 null, // definingUid 756 app1ProcessName, // processName 757 0, // connectionGroup 758 ApplicationExitInfo.REASON_OTHER, // reason 759 ApplicationExitInfo.SUBREASON_REMOVE_TASK, // subReason 760 0, // status 761 app1Pss4, // pss 762 app1Rss4, // rss 763 IMPORTANCE_CACHED, // importance 764 null); // description 765 766 // App1 gets "too many empty" 767 final String app1Description2 = "too many empty"; 768 sleep(1); 769 final int app1Pid2User2 = 56565; 770 final int app1IsolatedUid2User2 = 1099003; // isolated uid 771 final long app1Pss5 = 34344; 772 final long app1Rss5 = 43435; 773 final long now9 = System.currentTimeMillis(); 774 sigNum = OsConstants.SIGKILL; 775 doReturn(new Pair<Long, Object>(now9, makeSignalStatus(sigNum))) 776 .when(mAppExitInfoTracker.mAppExitInfoSourceZygote) 777 .remove(anyInt(), anyInt()); 778 doReturn(null) 779 .when(mAppExitInfoTracker.mAppExitInfoSourceLmkd) 780 .remove(anyInt(), anyInt()); 781 app = makeProcessRecord( 782 app1Pid2User2, // pid 783 app1IsolatedUid2User2, // uid 784 app1UidUser2, // packageUid 785 null, // definingUid 786 0, // connectionGroup 787 PROCESS_STATE_CACHED_EMPTY, // procstate 788 app1Pss5, // pss 789 app1Rss5, // rss 790 app1ProcessName, // processName 791 app1PackageName); // packageName 792 793 mAppExitInfoTracker.mIsolatedUidRecords.addIsolatedUid(app1IsolatedUid2User2, app1UidUser2); 794 795 // Pretent it gets an ANR trace too (although the reason here should be REASON_ANR) 796 final File traceFile = new File(mContext.getFilesDir(), "anr_original.txt"); 797 final int traceSize = 10240; 798 final int traceStart = 1024; 799 final int traceEnd = 8192; 800 createRandomFile(traceFile, traceSize); 801 assertEquals(traceSize, traceFile.length()); 802 mAppExitInfoTracker.handleLogAnrTrace(app.getPid(), app.uid, app.getPackageList(), 803 traceFile, traceStart, traceEnd); 804 805 noteAppKill(app, ApplicationExitInfo.REASON_OTHER, 806 ApplicationExitInfo.SUBREASON_TOO_MANY_EMPTY, app1Description2, now9); 807 updateExitInfo(app, now9); 808 list.clear(); 809 mAppExitInfoTracker.getExitInfo(app1PackageName, app1UidUser2, app1Pid2User2, 1, list); 810 assertEquals(1, list.size()); 811 812 info = list.get(0); 813 814 verifyApplicationExitInfo( 815 info, // info 816 now9, // timestamp 817 app1Pid2User2, // pid 818 app1IsolatedUid2User2, // uid 819 app1UidUser2, // packageUid 820 null, // definingUid 821 app1ProcessName, // processName 822 0, // connectionGroup 823 ApplicationExitInfo.REASON_OTHER, // reason 824 ApplicationExitInfo.SUBREASON_TOO_MANY_EMPTY, // subReason 825 0, // status 826 app1Pss5, // pss 827 app1Rss5, // rss 828 IMPORTANCE_CACHED, // importance 829 app1Description2); // description 830 831 // Verify if the traceFile get copied into the records correctly. 832 verifyTraceFile(traceFile, traceStart, info.getTraceFile(), 0, traceEnd - traceStart); 833 traceFile.delete(); 834 info.getTraceFile().delete(); 835 836 // Case 9: User2 gets removed 837 sleep(1); 838 mAppExitInfoTracker.mIsolatedUidRecords.addIsolatedUid(app1IsolatedUidUser2, app1UidUser2); 839 mAppExitInfoTracker.mIsolatedUidRecords.addIsolatedUid(app3IsolatedUid, app3Uid); 840 841 mAppExitInfoTracker.onUserRemoved(UserHandle.getUserId(app1UidUser2)); 842 843 assertNull(mAppExitInfoTracker.mIsolatedUidRecords.getUidByIsolatedUid( 844 app1IsolatedUidUser2)); 845 assertNotNull(mAppExitInfoTracker.mIsolatedUidRecords.getUidByIsolatedUid( 846 app3IsolatedUid)); 847 mAppExitInfoTracker.mIsolatedUidRecords.addIsolatedUid( 848 app1IsolatedUidUser2, app1UidUser2); 849 mAppExitInfoTracker.mIsolatedUidRecords.removeAppUid(app1UidUser2, false); 850 assertNull(mAppExitInfoTracker.mIsolatedUidRecords.getUidByIsolatedUid( 851 app1IsolatedUidUser2)); 852 mAppExitInfoTracker.mIsolatedUidRecords.removeAppUid(app3Uid, true); 853 assertNull(mAppExitInfoTracker.mIsolatedUidRecords.getUidByIsolatedUid(app3IsolatedUid)); 854 855 list.clear(); 856 mAppExitInfoTracker.getExitInfo(null, app1UidUser2, 0, 0, list); 857 assertEquals(0, list.size()); 858 859 list.clear(); 860 mAppExitInfoTracker.getExitInfo(null, app1Uid, 0, 0, list); 861 assertEquals(3, list.size()); 862 863 info = list.get(1); 864 865 exitCode = 6; 866 verifyApplicationExitInfo( 867 info, // info 868 now2, // timestamp 869 app1Pid2, // pid 870 app1Uid, // uid 871 app1Uid, // packageUid 872 app1DefiningUid, // definingUid 873 app1ProcessName, // processName 874 app1ConnectiongGroup, // connectionGroup 875 ApplicationExitInfo.REASON_EXIT_SELF, // reason 876 null, // subReason 877 exitCode, // status 878 app1Pss2, // pss 879 app1Rss2, // rss 880 IMPORTANCE_SERVICE, // importance 881 null); // description 882 883 info = list.get(0); 884 verifyApplicationExitInfo( 885 info, // info 886 now1s, // timestamp 887 app1sPid1, // pid 888 app1Uid, // uid 889 app1Uid, // packageUid 890 null, // definingUid 891 app1sProcessName, // processName 892 0, // connectionGroup 893 ApplicationExitInfo.REASON_USER_REQUESTED, // reason 894 null, // subReason 895 null, // status 896 app1sPss1, // pss 897 app1sRss1, // rss 898 IMPORTANCE_FOREGROUND, // importance 899 null); // description 900 901 info = list.get(2); 902 exitCode = 5; 903 verifyApplicationExitInfo( 904 info, // info 905 now1, // timestamp 906 app1Pid1, // pid 907 app1Uid, // uid 908 app1Uid, // packageUid 909 null, // definingUid 910 app1ProcessName, // processName 911 0, // connectionGroup 912 ApplicationExitInfo.REASON_EXIT_SELF, // reason 913 null, // subReason 914 exitCode, // status 915 app1Pss1, // pss 916 app1Rss1, // rss 917 IMPORTANCE_CACHED, // importance 918 null); // description 919 920 // Case 10: Save the info and load them again 921 ArrayList<ApplicationExitInfo> original = new ArrayList<ApplicationExitInfo>(); 922 mAppExitInfoTracker.getExitInfo(null, app1Uid, 0, 0, original); 923 assertTrue(original.size() > 0); 924 925 mAppExitInfoTracker.persistProcessExitInfo(); 926 assertTrue(mAppExitInfoTracker.mProcExitInfoFile.exists()); 927 928 mAppExitInfoTracker.clearProcessExitInfo(false); 929 list.clear(); 930 mAppExitInfoTracker.getExitInfo(null, app1Uid, 0, 0, list); 931 assertEquals(0, list.size()); 932 933 mAppExitInfoTracker.loadExistingProcessExitInfo(); 934 list.clear(); 935 mAppExitInfoTracker.getExitInfo(null, app1Uid, 0, 0, list); 936 assertEquals(original.size(), list.size()); 937 938 for (int i = list.size() - 1; i >= 0; i--) { 939 assertTrue(list.get(i).equals(original.get(i))); 940 } 941 } 942 makeExitStatus(int exitCode)943 private static int makeExitStatus(int exitCode) { 944 return (exitCode << 8) & 0xff00; 945 } 946 makeSignalStatus(int sigNum)947 private static int makeSignalStatus(int sigNum) { 948 return sigNum & 0x7f; 949 } 950 sleep(long ms)951 private void sleep(long ms) { 952 try { 953 Thread.sleep(ms); 954 } catch (InterruptedException e) { 955 } 956 } 957 createRandomFile(File file, int size)958 private static void createRandomFile(File file, int size) throws IOException { 959 try (BufferedOutputStream out = new BufferedOutputStream(new FileOutputStream(file))) { 960 Random random = new Random(); 961 byte[] buf = random.ints('a', 'z').limit(size).collect( 962 StringBuilder::new, StringBuilder::appendCodePoint, StringBuilder::append) 963 .toString().getBytes(); 964 out.write(buf); 965 } 966 } 967 verifyTraceFile(File originFile, int originStart, File traceFile, int traceStart, int length)968 private static void verifyTraceFile(File originFile, int originStart, File traceFile, 969 int traceStart, int length) throws IOException { 970 assertTrue(originFile.exists()); 971 assertTrue(traceFile.exists()); 972 assertTrue(originStart < originFile.length()); 973 try (GZIPInputStream traceIn = new GZIPInputStream(new FileInputStream(traceFile)); 974 BufferedInputStream originIn = new BufferedInputStream( 975 new FileInputStream(originFile))) { 976 assertEquals(traceStart, traceIn.skip(traceStart)); 977 assertEquals(originStart, originIn.skip(originStart)); 978 byte[] buf1 = new byte[8192]; 979 byte[] buf2 = new byte[8192]; 980 while (length > 0) { 981 int len = traceIn.read(buf1, 0, Math.min(buf1.length, length)); 982 assertEquals(len, originIn.read(buf2, 0, len)); 983 assertTrue(ArrayUtils.equals(buf1, buf2, len)); 984 length -= len; 985 } 986 } 987 } 988 makeProcessRecord(int pid, int uid, int packageUid, Integer definingUid, int connectionGroup, int procState, long pss, long rss, String processName, String packageName)989 private ProcessRecord makeProcessRecord(int pid, int uid, int packageUid, Integer definingUid, 990 int connectionGroup, int procState, long pss, long rss, 991 String processName, String packageName) { 992 return makeProcessRecord(pid, uid, packageUid, definingUid, connectionGroup, 993 procState, pss, rss, processName, packageName, mAms); 994 } 995 makeProcessRecord(int pid, int uid, int packageUid, Integer definingUid, int connectionGroup, int procState, long pss, long rss, String processName, String packageName, ActivityManagerService ams)996 static ProcessRecord makeProcessRecord(int pid, int uid, int packageUid, Integer definingUid, 997 int connectionGroup, int procState, long pss, long rss, 998 String processName, String packageName, ActivityManagerService ams) { 999 ApplicationInfo ai = new ApplicationInfo(); 1000 ai.packageName = packageName; 1001 ProcessRecord app = new ProcessRecord(ams, ai, processName, uid); 1002 app.setPid(pid); 1003 app.info.uid = packageUid; 1004 if (definingUid != null) { 1005 final String dummyPackageName = "com.android.test"; 1006 final String dummyClassName = ".Foo"; 1007 app.setHostingRecord(HostingRecord.byAppZygote(new ComponentName( 1008 dummyPackageName, dummyClassName), "", definingUid, "")); 1009 } 1010 app.mServices.setConnectionGroup(connectionGroup); 1011 app.mState.setReportedProcState(procState); 1012 app.mProfile.setLastMemInfo(spy(new Debug.MemoryInfo())); 1013 app.mProfile.setLastPss(pss); 1014 app.mProfile.setLastRss(rss); 1015 return app; 1016 } 1017 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)1018 private void verifyApplicationExitInfo(ApplicationExitInfo info, 1019 Long timestamp, Integer pid, Integer uid, Integer packageUid, 1020 Integer definingUid, String processName, Integer connectionGroup, 1021 Integer reason, Integer subReason, Integer status, 1022 Long pss, Long rss, Integer importance, String description) { 1023 assertNotNull(info); 1024 1025 if (timestamp != null) { 1026 final long tolerance = 10000; // ms 1027 assertTrue(timestamp - tolerance <= info.getTimestamp()); 1028 assertTrue(timestamp + tolerance >= info.getTimestamp()); 1029 } 1030 if (pid != null) { 1031 assertEquals(pid.intValue(), info.getPid()); 1032 } 1033 if (uid != null) { 1034 assertEquals(uid.intValue(), info.getRealUid()); 1035 } 1036 if (packageUid != null) { 1037 assertEquals(packageUid.intValue(), info.getPackageUid()); 1038 } 1039 if (definingUid != null) { 1040 assertEquals(definingUid.intValue(), info.getDefiningUid()); 1041 } 1042 if (processName != null) { 1043 assertTrue(TextUtils.equals(processName, info.getProcessName())); 1044 } 1045 if (connectionGroup != null) { 1046 assertEquals(connectionGroup.intValue(), info.getConnectionGroup()); 1047 } 1048 if (reason != null) { 1049 assertEquals(reason.intValue(), info.getReason()); 1050 } 1051 if (subReason != null) { 1052 assertEquals(subReason.intValue(), info.getSubReason()); 1053 } 1054 if (status != null) { 1055 assertEquals(status.intValue(), info.getStatus()); 1056 } 1057 if (pss != null) { 1058 assertEquals(pss.longValue(), info.getPss()); 1059 } 1060 if (rss != null) { 1061 assertEquals(rss.longValue(), info.getRss()); 1062 } 1063 if (importance != null) { 1064 assertEquals(importance.intValue(), info.getImportance()); 1065 } 1066 1067 // info.getDescription returns a combination of subReason & description 1068 if ((subReason != null) && (subReason != ApplicationExitInfo.SUBREASON_UNKNOWN) 1069 && (description != null)) { 1070 assertTrue(TextUtils.equals( 1071 "[" + info.subreasonToString(subReason) + "] " + description, 1072 info.getDescription())); 1073 } else if ((subReason != null) && (subReason != ApplicationExitInfo.SUBREASON_UNKNOWN)) { 1074 assertTrue(TextUtils.equals( 1075 "[" + info.subreasonToString(subReason) + "]", 1076 info.getDescription())); 1077 } else if (description != null) { 1078 assertTrue(TextUtils.equals(description, info.getDescription())); 1079 } 1080 } 1081 1082 private class TestInjector extends Injector { TestInjector(Context context)1083 TestInjector(Context context) { 1084 super(context); 1085 } 1086 1087 @Override getAppOpsService(File recentAccessesFile, File storageFile, Handler handler)1088 public AppOpsService getAppOpsService(File recentAccessesFile, File storageFile, 1089 Handler handler) { 1090 return mAppOpsService; 1091 } 1092 1093 @Override getUiHandler(ActivityManagerService service)1094 public Handler getUiHandler(ActivityManagerService service) { 1095 return mHandler; 1096 } 1097 1098 @Override getProcessList(ActivityManagerService service)1099 public ProcessList getProcessList(ActivityManagerService service) { 1100 return mProcessList; 1101 } 1102 } 1103 1104 static class ServiceThreadRule implements TestRule { 1105 1106 private ServiceThread mThread; 1107 getThread()1108 ServiceThread getThread() { 1109 return mThread; 1110 } 1111 1112 @Override apply(Statement base, Description description)1113 public Statement apply(Statement base, Description description) { 1114 return new Statement() { 1115 @Override 1116 public void evaluate() throws Throwable { 1117 mThread = new ServiceThread("TestServiceThread", 1118 Process.THREAD_PRIORITY_DEFAULT, true /* allowIo */); 1119 mThread.start(); 1120 try { 1121 base.evaluate(); 1122 } finally { 1123 mThread.getThreadHandler().runWithScissors(mThread::quit, 0 /* timeout */); 1124 } 1125 } 1126 }; 1127 } 1128 } 1129 1130 // TODO: [b/302724778] Remove manual JNI load 1131 static { 1132 System.loadLibrary("mockingservicestestjni"); 1133 } 1134 } 1135