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.RunningAppProcessInfo.procStateToImportance; 20 import static android.app.ActivityManagerInternal.ALLOW_NON_FULL; 21 import static android.os.Process.THREAD_PRIORITY_BACKGROUND; 22 23 import static com.android.server.am.ActivityManagerDebugConfig.DEBUG_PROCESSES; 24 import static com.android.server.am.ActivityManagerDebugConfig.TAG_AM; 25 import static com.android.server.am.ActivityManagerDebugConfig.TAG_WITH_CLASS_NAME; 26 27 import android.annotation.CurrentTimeMillisLong; 28 import android.annotation.Nullable; 29 import android.app.ApplicationExitInfo; 30 import android.app.ApplicationExitInfo.Reason; 31 import android.app.ApplicationExitInfo.SubReason; 32 import android.app.IAppTraceRetriever; 33 import android.content.BroadcastReceiver; 34 import android.content.Context; 35 import android.content.Intent; 36 import android.content.IntentFilter; 37 import android.content.pm.PackageManager; 38 import android.icu.text.SimpleDateFormat; 39 import android.os.Binder; 40 import android.os.FileUtils; 41 import android.os.Handler; 42 import android.os.Looper; 43 import android.os.Message; 44 import android.os.ParcelFileDescriptor; 45 import android.os.Process; 46 import android.os.SystemProperties; 47 import android.os.UserHandle; 48 import android.system.OsConstants; 49 import android.text.TextUtils; 50 import android.util.ArrayMap; 51 import android.util.ArraySet; 52 import android.util.AtomicFile; 53 import android.util.Pair; 54 import android.util.Pools.SynchronizedPool; 55 import android.util.Slog; 56 import android.util.SparseArray; 57 import android.util.proto.ProtoInputStream; 58 import android.util.proto.ProtoOutputStream; 59 import android.util.proto.WireTypeMismatchException; 60 61 import com.android.internal.annotations.GuardedBy; 62 import com.android.internal.annotations.VisibleForTesting; 63 import com.android.internal.app.ProcessMap; 64 import com.android.internal.util.ArrayUtils; 65 import com.android.internal.util.FrameworkStatsLog; 66 import com.android.internal.util.function.pooled.PooledLambda; 67 import com.android.server.IoThread; 68 import com.android.server.LocalServices; 69 import com.android.server.ServiceThread; 70 import com.android.server.SystemServiceManager; 71 import com.android.server.os.NativeTombstoneManager; 72 73 import java.io.BufferedInputStream; 74 import java.io.BufferedOutputStream; 75 import java.io.File; 76 import java.io.FileInputStream; 77 import java.io.FileNotFoundException; 78 import java.io.FileOutputStream; 79 import java.io.IOException; 80 import java.io.PrintWriter; 81 import java.util.ArrayList; 82 import java.util.Collections; 83 import java.util.Date; 84 import java.util.List; 85 import java.util.Optional; 86 import java.util.concurrent.TimeUnit; 87 import java.util.concurrent.atomic.AtomicBoolean; 88 import java.util.function.BiConsumer; 89 import java.util.function.BiFunction; 90 import java.util.function.Consumer; 91 import java.util.function.Predicate; 92 import java.util.function.Supplier; 93 import java.util.zip.GZIPOutputStream; 94 95 /** 96 * A class to manage all the {@link android.app.ApplicationExitInfo} records. 97 */ 98 public final class AppExitInfoTracker { 99 private static final String TAG = TAG_WITH_CLASS_NAME ? "AppExitInfoTracker" : TAG_AM; 100 101 /** 102 * Interval of persisting the app exit info to persistent storage. 103 */ 104 private static final long APP_EXIT_INFO_PERSIST_INTERVAL = TimeUnit.MINUTES.toMillis(30); 105 106 /** These are actions that the forEach* should take after each iteration */ 107 private static final int FOREACH_ACTION_NONE = 0; 108 private static final int FOREACH_ACTION_REMOVE_ITEM = 1; 109 private static final int FOREACH_ACTION_STOP_ITERATION = 2; 110 111 private static final int APP_EXIT_RAW_INFO_POOL_SIZE = 8; 112 113 /** 114 * How long we're going to hold before logging an app exit info into statsd; 115 * we do this is because there could be multiple sources signaling an app exit, we'd like to 116 * gather the most accurate information before logging into statsd. 117 */ 118 private static final long APP_EXIT_INFO_STATSD_LOG_DEBOUNCE = TimeUnit.SECONDS.toMillis(15); 119 120 @VisibleForTesting 121 static final String APP_EXIT_STORE_DIR = "procexitstore"; 122 123 @VisibleForTesting 124 static final String APP_EXIT_INFO_FILE = "procexitinfo"; 125 126 private static final String APP_TRACE_FILE_SUFFIX = ".gz"; 127 128 private final Object mLock = new Object(); 129 130 /** 131 * Initialized in {@link #init} and read-only after that. 132 */ 133 private ActivityManagerService mService; 134 135 /** 136 * Initialized in {@link #init} and read-only after that. 137 */ 138 private KillHandler mKillHandler; 139 140 /** 141 * The task to persist app process exit info 142 */ 143 @GuardedBy("mLock") 144 private Runnable mAppExitInfoPersistTask = null; 145 146 /** 147 * Last time(in ms) since epoch that the app exit info was persisted into persistent storage. 148 */ 149 @GuardedBy("mLock") 150 private long mLastAppExitInfoPersistTimestamp = 0L; 151 152 /** 153 * Retention policy: keep up to X historical exit info per package. 154 * 155 * Initialized in {@link #init} and read-only after that. 156 * Not lock is needed. 157 */ 158 private int mAppExitInfoHistoryListSize; 159 160 /* 161 * PackageName/uid -> [pid/info, ...] holder, the uid here is the package uid. 162 */ 163 @GuardedBy("mLock") 164 private final ProcessMap<AppExitInfoContainer> mData; 165 166 /** A pool of raw {@link android.app.ApplicationExitInfo} records. */ 167 @GuardedBy("mLock") 168 private final SynchronizedPool<ApplicationExitInfo> mRawRecordsPool; 169 170 /** 171 * Wheather or not we've loaded the historical app process exit info from 172 * persistent storage. 173 */ 174 @VisibleForTesting 175 AtomicBoolean mAppExitInfoLoaded = new AtomicBoolean(); 176 177 /** 178 * Temporary list being used to filter/sort intermediate results in {@link #getExitInfo}. 179 */ 180 @GuardedBy("mLock") 181 final ArrayList<ApplicationExitInfo> mTmpInfoList = new ArrayList<ApplicationExitInfo>(); 182 183 /** 184 * Temporary list being used to filter/sort intermediate results in {@link #getExitInfo}. 185 */ 186 @GuardedBy("mLock") 187 final ArrayList<ApplicationExitInfo> mTmpInfoList2 = new ArrayList<ApplicationExitInfo>(); 188 189 /** 190 * The path to the directory which includes the historical proc exit info file 191 * as specified in {@link #mProcExitInfoFile}, as well as the associated trace files. 192 */ 193 @VisibleForTesting 194 File mProcExitStoreDir; 195 196 /** 197 * The path to the historical proc exit info file, persisted in the storage. 198 */ 199 @VisibleForTesting 200 File mProcExitInfoFile; 201 202 /** 203 * Mapping between the isolated UID to its application uid. 204 */ 205 final IsolatedUidRecords mIsolatedUidRecords = 206 new IsolatedUidRecords(); 207 208 /** 209 * Bookkeeping app process exit info from Zygote. 210 */ 211 final AppExitInfoExternalSource mAppExitInfoSourceZygote = 212 new AppExitInfoExternalSource("zygote", null); 213 214 /** 215 * Bookkeeping low memory kills info from lmkd. 216 */ 217 final AppExitInfoExternalSource mAppExitInfoSourceLmkd = 218 new AppExitInfoExternalSource("lmkd", ApplicationExitInfo.REASON_LOW_MEMORY); 219 220 /** 221 * The active per-UID/PID state data set by 222 * {@link android.app.ActivityManager#setProcessStateSummary}; 223 * these state data are to be "claimed" when its process dies, by then the data will be moved 224 * from this list to the new instance of ApplicationExitInfo. 225 * 226 * <p> The mapping here is UID -> PID -> state </p> 227 * 228 * @see android.app.ActivityManager#setProcessStateSummary(byte[]) 229 */ 230 @GuardedBy("mLock") 231 final SparseArray<SparseArray<byte[]>> mActiveAppStateSummary = new SparseArray<>(); 232 233 /** 234 * The active per-UID/PID trace file when an ANR occurs but the process hasn't been killed yet, 235 * each record is a path to the actual trace file; these files are to be "claimed" 236 * when its process dies, by then the "ownership" of the files will be transferred 237 * from this list to the new instance of ApplicationExitInfo. 238 * 239 * <p> The mapping here is UID -> PID -> file </p> 240 */ 241 @GuardedBy("mLock") 242 final SparseArray<SparseArray<File>> mActiveAppTraces = new SparseArray<>(); 243 244 /** 245 * The implementation of the interface IAppTraceRetriever. 246 */ 247 final AppTraceRetriever mAppTraceRetriever = new AppTraceRetriever(); 248 AppExitInfoTracker()249 AppExitInfoTracker() { 250 mData = new ProcessMap<AppExitInfoContainer>(); 251 mRawRecordsPool = new SynchronizedPool<ApplicationExitInfo>(APP_EXIT_RAW_INFO_POOL_SIZE); 252 } 253 init(ActivityManagerService service)254 void init(ActivityManagerService service) { 255 mService = service; 256 ServiceThread thread = new ServiceThread(TAG + ":killHandler", 257 THREAD_PRIORITY_BACKGROUND, true /* allowIo */); 258 thread.start(); 259 mKillHandler = new KillHandler(thread.getLooper()); 260 261 mProcExitStoreDir = new File(SystemServiceManager.ensureSystemDir(), APP_EXIT_STORE_DIR); 262 if (!FileUtils.createDir(mProcExitStoreDir)) { 263 Slog.e(TAG, "Unable to create " + mProcExitStoreDir); 264 return; 265 } 266 mProcExitInfoFile = new File(mProcExitStoreDir, APP_EXIT_INFO_FILE); 267 268 mAppExitInfoHistoryListSize = service.mContext.getResources().getInteger( 269 com.android.internal.R.integer.config_app_exit_info_history_list_size); 270 } 271 onSystemReady()272 void onSystemReady() { 273 registerForUserRemoval(); 274 registerForPackageRemoval(); 275 IoThread.getHandler().post(() -> { 276 // Read the sysprop set by lmkd and set this to persist so app could read it. 277 SystemProperties.set("persist.sys.lmk.reportkills", 278 Boolean.toString(SystemProperties.getBoolean("sys.lmk.reportkills", false))); 279 loadExistingProcessExitInfo(); 280 }); 281 } 282 scheduleNoteProcessDied(final ProcessRecord app)283 void scheduleNoteProcessDied(final ProcessRecord app) { 284 if (app == null || app.info == null) { 285 return; 286 } 287 288 if (!mAppExitInfoLoaded.get()) { 289 return; 290 } 291 mKillHandler.obtainMessage(KillHandler.MSG_PROC_DIED, 292 obtainRawRecord(app, System.currentTimeMillis())).sendToTarget(); 293 } 294 scheduleNoteAppKill(final ProcessRecord app, final @Reason int reason, final @SubReason int subReason, final String msg)295 void scheduleNoteAppKill(final ProcessRecord app, final @Reason int reason, 296 final @SubReason int subReason, final String msg) { 297 if (!mAppExitInfoLoaded.get()) { 298 return; 299 } 300 if (app == null || app.info == null) { 301 return; 302 } 303 304 ApplicationExitInfo raw = obtainRawRecord(app, System.currentTimeMillis()); 305 raw.setReason(reason); 306 raw.setSubReason(subReason); 307 raw.setDescription(msg); 308 mKillHandler.obtainMessage(KillHandler.MSG_APP_KILL, raw).sendToTarget(); 309 } 310 scheduleNoteAppKill(final int pid, final int uid, final @Reason int reason, final @SubReason int subReason, final String msg)311 void scheduleNoteAppKill(final int pid, final int uid, final @Reason int reason, 312 final @SubReason int subReason, final String msg) { 313 if (!mAppExitInfoLoaded.get()) { 314 return; 315 } 316 ProcessRecord app; 317 synchronized (mService.mPidsSelfLocked) { 318 app = mService.mPidsSelfLocked.get(pid); 319 } 320 if (app == null) { 321 if (DEBUG_PROCESSES) { 322 Slog.w(TAG, "Skipping saving the kill reason for pid " + pid 323 + "(uid=" + uid + ") since its process record is not found"); 324 } 325 } else { 326 scheduleNoteAppKill(app, reason, subReason, msg); 327 } 328 } 329 330 interface LmkdKillListener { 331 /** 332 * Called when there is a process kill by lmkd. 333 */ onLmkdKillOccurred(int pid, int uid)334 void onLmkdKillOccurred(int pid, int uid); 335 } 336 setLmkdKillListener(final LmkdKillListener listener)337 void setLmkdKillListener(final LmkdKillListener listener) { 338 synchronized (mLock) { 339 mAppExitInfoSourceLmkd.setOnProcDiedListener((pid, uid) -> 340 listener.onLmkdKillOccurred(pid, uid)); 341 } 342 } 343 344 /** Called when there is a low memory kill */ scheduleNoteLmkdProcKilled(final int pid, final int uid)345 void scheduleNoteLmkdProcKilled(final int pid, final int uid) { 346 mKillHandler.obtainMessage(KillHandler.MSG_LMKD_PROC_KILLED, pid, uid) 347 .sendToTarget(); 348 } 349 scheduleChildProcDied(int pid, int uid, int status)350 private void scheduleChildProcDied(int pid, int uid, int status) { 351 mKillHandler.obtainMessage(KillHandler.MSG_CHILD_PROC_DIED, pid, uid, (Integer) status) 352 .sendToTarget(); 353 } 354 355 /** Calls when zygote sends us SIGCHLD */ handleZygoteSigChld(int pid, int uid, int status)356 void handleZygoteSigChld(int pid, int uid, int status) { 357 if (DEBUG_PROCESSES) { 358 Slog.i(TAG, "Got SIGCHLD from zygote: pid=" + pid + ", uid=" + uid 359 + ", status=" + Integer.toHexString(status)); 360 } 361 scheduleChildProcDied(pid, uid, status); 362 } 363 364 /** 365 * Main routine to create or update the {@link android.app.ApplicationExitInfo} for the given 366 * ProcessRecord, also query the zygote and lmkd records to make the information more accurate. 367 */ 368 @VisibleForTesting 369 @GuardedBy("mLock") handleNoteProcessDiedLocked(final ApplicationExitInfo raw)370 void handleNoteProcessDiedLocked(final ApplicationExitInfo raw) { 371 if (raw != null) { 372 if (DEBUG_PROCESSES) { 373 Slog.i(TAG, "Update process exit info for " + raw.getPackageName() 374 + "(" + raw.getPid() + "/u" + raw.getRealUid() + ")"); 375 } 376 377 ApplicationExitInfo info = getExitInfoLocked(raw.getPackageName(), 378 raw.getPackageUid(), raw.getPid()); 379 380 // query zygote and lmkd to get the exit info, and clear the saved info 381 Pair<Long, Object> zygote = mAppExitInfoSourceZygote.remove( 382 raw.getPid(), raw.getRealUid()); 383 Pair<Long, Object> lmkd = mAppExitInfoSourceLmkd.remove( 384 raw.getPid(), raw.getRealUid()); 385 mIsolatedUidRecords.removeIsolatedUidLocked(raw.getRealUid()); 386 387 if (info == null) { 388 info = addExitInfoLocked(raw); 389 } 390 391 if (lmkd != null) { 392 updateExistingExitInfoRecordLocked(info, null, 393 ApplicationExitInfo.REASON_LOW_MEMORY); 394 } else if (zygote != null) { 395 updateExistingExitInfoRecordLocked(info, (Integer) zygote.second, null); 396 } else { 397 scheduleLogToStatsdLocked(info, false); 398 } 399 } 400 } 401 402 /** 403 * Make note when ActivityManagerService decides to kill an application process. 404 */ 405 @VisibleForTesting 406 @GuardedBy("mLock") handleNoteAppKillLocked(final ApplicationExitInfo raw)407 void handleNoteAppKillLocked(final ApplicationExitInfo raw) { 408 ApplicationExitInfo info = getExitInfoLocked( 409 raw.getPackageName(), raw.getPackageUid(), raw.getPid()); 410 411 if (info == null) { 412 info = addExitInfoLocked(raw); 413 } else { 414 // always override the existing info since we are now more informational. 415 info.setReason(raw.getReason()); 416 info.setSubReason(raw.getSubReason()); 417 info.setStatus(0); 418 info.setTimestamp(System.currentTimeMillis()); 419 info.setDescription(raw.getDescription()); 420 } 421 scheduleLogToStatsdLocked(info, true); 422 } 423 424 @GuardedBy("mLock") addExitInfoLocked(ApplicationExitInfo raw)425 private ApplicationExitInfo addExitInfoLocked(ApplicationExitInfo raw) { 426 if (!mAppExitInfoLoaded.get()) { 427 Slog.w(TAG, "Skipping saving the exit info due to ongoing loading from storage"); 428 return null; 429 } 430 431 final ApplicationExitInfo info = new ApplicationExitInfo(raw); 432 final String[] packages = raw.getPackageList(); 433 int uid = raw.getRealUid(); 434 if (UserHandle.isIsolated(uid)) { 435 Integer k = mIsolatedUidRecords.getUidByIsolatedUid(uid); 436 if (k != null) { 437 uid = k; 438 } 439 } 440 for (int i = 0; i < packages.length; i++) { 441 addExitInfoInnerLocked(packages[i], uid, info); 442 } 443 444 schedulePersistProcessExitInfo(false); 445 446 return info; 447 } 448 449 /** 450 * Update an existing {@link android.app.ApplicationExitInfo} record with given information. 451 */ 452 @GuardedBy("mLock") updateExistingExitInfoRecordLocked(ApplicationExitInfo info, Integer status, Integer reason)453 private void updateExistingExitInfoRecordLocked(ApplicationExitInfo info, 454 Integer status, Integer reason) { 455 if (info == null || !isFresh(info.getTimestamp())) { 456 // if the record is way outdated, don't update it then (because of potential pid reuse) 457 return; 458 } 459 boolean immediateLog = false; 460 if (status != null) { 461 if (OsConstants.WIFEXITED(status)) { 462 info.setReason(ApplicationExitInfo.REASON_EXIT_SELF); 463 info.setStatus(OsConstants.WEXITSTATUS(status)); 464 immediateLog = true; 465 } else if (OsConstants.WIFSIGNALED(status)) { 466 if (info.getReason() == ApplicationExitInfo.REASON_UNKNOWN) { 467 info.setReason(ApplicationExitInfo.REASON_SIGNALED); 468 info.setStatus(OsConstants.WTERMSIG(status)); 469 } else if (info.getReason() == ApplicationExitInfo.REASON_CRASH_NATIVE) { 470 info.setStatus(OsConstants.WTERMSIG(status)); 471 immediateLog = true; 472 } 473 } 474 } 475 if (reason != null) { 476 info.setReason(reason); 477 if (reason == ApplicationExitInfo.REASON_LOW_MEMORY) { 478 immediateLog = true; 479 } 480 } 481 scheduleLogToStatsdLocked(info, immediateLog); 482 } 483 484 /** 485 * Update an existing {@link android.app.ApplicationExitInfo} record with given information. 486 * 487 * @return true if a recond is updated 488 */ 489 @GuardedBy("mLock") updateExitInfoIfNecessaryLocked( int pid, int uid, Integer status, Integer reason)490 private boolean updateExitInfoIfNecessaryLocked( 491 int pid, int uid, Integer status, Integer reason) { 492 Integer k = mIsolatedUidRecords.getUidByIsolatedUid(uid); 493 if (k != null) { 494 uid = k; 495 } 496 ArrayList<ApplicationExitInfo> tlist = mTmpInfoList; 497 tlist.clear(); 498 final int targetUid = uid; 499 forEachPackageLocked((packageName, records) -> { 500 AppExitInfoContainer container = records.get(targetUid); 501 if (container == null) { 502 return FOREACH_ACTION_NONE; 503 } 504 tlist.clear(); 505 container.getExitInfoLocked(pid, 1, tlist); 506 if (tlist.size() == 0) { 507 return FOREACH_ACTION_NONE; 508 } 509 ApplicationExitInfo info = tlist.get(0); 510 if (info.getRealUid() != targetUid) { 511 tlist.clear(); 512 return FOREACH_ACTION_NONE; 513 } 514 // Okay found it, update its reason. 515 updateExistingExitInfoRecordLocked(info, status, reason); 516 517 return FOREACH_ACTION_STOP_ITERATION; 518 }); 519 return tlist.size() > 0; 520 } 521 522 /** 523 * Get the exit info with matching package name, filterUid and filterPid (if > 0) 524 */ 525 @VisibleForTesting getExitInfo(final String packageName, final int filterUid, final int filterPid, final int maxNum, final ArrayList<ApplicationExitInfo> results)526 void getExitInfo(final String packageName, final int filterUid, 527 final int filterPid, final int maxNum, final ArrayList<ApplicationExitInfo> results) { 528 final long identity = Binder.clearCallingIdentity(); 529 try { 530 synchronized (mLock) { 531 boolean emptyPackageName = TextUtils.isEmpty(packageName); 532 if (!emptyPackageName) { 533 // fast path 534 AppExitInfoContainer container = mData.get(packageName, filterUid); 535 if (container != null) { 536 container.getExitInfoLocked(filterPid, maxNum, results); 537 } 538 } else { 539 // slow path 540 final ArrayList<ApplicationExitInfo> list = mTmpInfoList2; 541 list.clear(); 542 // get all packages 543 forEachPackageLocked((name, records) -> { 544 AppExitInfoContainer container = records.get(filterUid); 545 if (container != null) { 546 mTmpInfoList.clear(); 547 list.addAll(container.toListLocked(mTmpInfoList, filterPid)); 548 } 549 return AppExitInfoTracker.FOREACH_ACTION_NONE; 550 }); 551 552 Collections.sort(list, 553 (a, b) -> Long.compare(b.getTimestamp(), a.getTimestamp())); 554 int size = list.size(); 555 if (maxNum > 0) { 556 size = Math.min(size, maxNum); 557 } 558 for (int i = 0; i < size; i++) { 559 results.add(list.get(i)); 560 } 561 list.clear(); 562 } 563 } 564 } finally { 565 Binder.restoreCallingIdentity(identity); 566 } 567 } 568 569 /** 570 * Return the first matching exit info record, for internal use, the parameters are not supposed 571 * to be empty. 572 */ 573 @GuardedBy("mLock") getExitInfoLocked(final String packageName, final int filterUid, final int filterPid)574 private ApplicationExitInfo getExitInfoLocked(final String packageName, 575 final int filterUid, final int filterPid) { 576 ArrayList<ApplicationExitInfo> list = mTmpInfoList; 577 list.clear(); 578 getExitInfo(packageName, filterUid, filterPid, 1, list); 579 580 ApplicationExitInfo info = list.size() > 0 ? list.get(0) : null; 581 list.clear(); 582 return info; 583 } 584 585 @VisibleForTesting onUserRemoved(int userId)586 void onUserRemoved(int userId) { 587 mAppExitInfoSourceZygote.removeByUserId(userId); 588 mAppExitInfoSourceLmkd.removeByUserId(userId); 589 mIsolatedUidRecords.removeByUserId(userId); 590 synchronized (mLock) { 591 removeByUserIdLocked(userId); 592 schedulePersistProcessExitInfo(true); 593 } 594 } 595 596 @VisibleForTesting onPackageRemoved(String packageName, int uid, boolean allUsers)597 void onPackageRemoved(String packageName, int uid, boolean allUsers) { 598 if (packageName != null) { 599 final boolean removeUid = TextUtils.isEmpty( 600 mService.mPackageManagerInt.getNameForUid(uid)); 601 synchronized (mLock) { 602 if (removeUid) { 603 mAppExitInfoSourceZygote.removeByUidLocked(uid, allUsers); 604 mAppExitInfoSourceLmkd.removeByUidLocked(uid, allUsers); 605 mIsolatedUidRecords.removeAppUid(uid, allUsers); 606 } 607 removePackageLocked(packageName, uid, removeUid, 608 allUsers ? UserHandle.USER_ALL : UserHandle.getUserId(uid)); 609 schedulePersistProcessExitInfo(true); 610 } 611 } 612 } 613 registerForUserRemoval()614 private void registerForUserRemoval() { 615 IntentFilter filter = new IntentFilter(); 616 filter.addAction(Intent.ACTION_USER_REMOVED); 617 mService.mContext.registerReceiverForAllUsers(new BroadcastReceiver() { 618 @Override 619 public void onReceive(Context context, Intent intent) { 620 int userId = intent.getIntExtra(Intent.EXTRA_USER_HANDLE, -1); 621 if (userId < 1) return; 622 onUserRemoved(userId); 623 } 624 }, filter, null, mKillHandler); 625 } 626 registerForPackageRemoval()627 private void registerForPackageRemoval() { 628 IntentFilter filter = new IntentFilter(); 629 filter.addAction(Intent.ACTION_PACKAGE_REMOVED); 630 filter.addDataScheme("package"); 631 mService.mContext.registerReceiverForAllUsers(new BroadcastReceiver() { 632 @Override 633 public void onReceive(Context context, Intent intent) { 634 boolean replacing = intent.getBooleanExtra( 635 Intent.EXTRA_REPLACING, false); 636 if (replacing) { 637 return; 638 } 639 int uid = intent.getIntExtra(Intent.EXTRA_UID, UserHandle.USER_NULL); 640 boolean allUsers = intent.getBooleanExtra( 641 Intent.EXTRA_REMOVED_FOR_ALL_USERS, false); 642 onPackageRemoved(intent.getData().getSchemeSpecificPart(), uid, allUsers); 643 } 644 }, filter, null, mKillHandler); 645 } 646 647 /** 648 * Load the existing {@link android.app.ApplicationExitInfo} records from persistent storage. 649 */ 650 @VisibleForTesting loadExistingProcessExitInfo()651 void loadExistingProcessExitInfo() { 652 if (!mProcExitInfoFile.canRead()) { 653 mAppExitInfoLoaded.set(true); 654 return; 655 } 656 657 FileInputStream fin = null; 658 try { 659 AtomicFile af = new AtomicFile(mProcExitInfoFile); 660 fin = af.openRead(); 661 ProtoInputStream proto = new ProtoInputStream(fin); 662 for (int next = proto.nextField(); 663 next != ProtoInputStream.NO_MORE_FIELDS; 664 next = proto.nextField()) { 665 switch (next) { 666 case (int) AppsExitInfoProto.LAST_UPDATE_TIMESTAMP: 667 synchronized (mLock) { 668 mLastAppExitInfoPersistTimestamp = 669 proto.readLong(AppsExitInfoProto.LAST_UPDATE_TIMESTAMP); 670 } 671 break; 672 case (int) AppsExitInfoProto.PACKAGES: 673 loadPackagesFromProto(proto, next); 674 break; 675 } 676 } 677 } catch (IOException | IllegalArgumentException | WireTypeMismatchException e) { 678 Slog.w(TAG, "Error in loading historical app exit info from persistent storage: " + e); 679 } finally { 680 if (fin != null) { 681 try { 682 fin.close(); 683 } catch (IOException e) { 684 } 685 } 686 } 687 synchronized (mLock) { 688 pruneAnrTracesIfNecessaryLocked(); 689 mAppExitInfoLoaded.set(true); 690 } 691 } 692 loadPackagesFromProto(ProtoInputStream proto, long fieldId)693 private void loadPackagesFromProto(ProtoInputStream proto, long fieldId) 694 throws IOException, WireTypeMismatchException { 695 long token = proto.start(fieldId); 696 String pkgName = ""; 697 for (int next = proto.nextField(); 698 next != ProtoInputStream.NO_MORE_FIELDS; 699 next = proto.nextField()) { 700 switch (next) { 701 case (int) AppsExitInfoProto.Package.PACKAGE_NAME: 702 pkgName = proto.readString(AppsExitInfoProto.Package.PACKAGE_NAME); 703 break; 704 case (int) AppsExitInfoProto.Package.USERS: 705 AppExitInfoContainer container = new AppExitInfoContainer( 706 mAppExitInfoHistoryListSize); 707 int uid = container.readFromProto(proto, AppsExitInfoProto.Package.USERS); 708 synchronized (mLock) { 709 mData.put(pkgName, uid, container); 710 } 711 break; 712 } 713 } 714 proto.end(token); 715 } 716 717 /** 718 * Persist the existing {@link android.app.ApplicationExitInfo} records to storage. 719 */ 720 @VisibleForTesting persistProcessExitInfo()721 void persistProcessExitInfo() { 722 AtomicFile af = new AtomicFile(mProcExitInfoFile); 723 FileOutputStream out = null; 724 long now = System.currentTimeMillis(); 725 try { 726 out = af.startWrite(); 727 ProtoOutputStream proto = new ProtoOutputStream(out); 728 proto.write(AppsExitInfoProto.LAST_UPDATE_TIMESTAMP, now); 729 synchronized (mLock) { 730 forEachPackageLocked((packageName, records) -> { 731 long token = proto.start(AppsExitInfoProto.PACKAGES); 732 proto.write(AppsExitInfoProto.Package.PACKAGE_NAME, packageName); 733 int uidArraySize = records.size(); 734 for (int j = 0; j < uidArraySize; j++) { 735 records.valueAt(j).writeToProto(proto, AppsExitInfoProto.Package.USERS); 736 } 737 proto.end(token); 738 return AppExitInfoTracker.FOREACH_ACTION_NONE; 739 }); 740 mLastAppExitInfoPersistTimestamp = now; 741 } 742 proto.flush(); 743 af.finishWrite(out); 744 } catch (IOException e) { 745 Slog.w(TAG, "Unable to write historical app exit info into persistent storage: " + e); 746 af.failWrite(out); 747 } 748 synchronized (mLock) { 749 mAppExitInfoPersistTask = null; 750 } 751 } 752 753 /** 754 * Schedule a task to persist the {@link android.app.ApplicationExitInfo} records to storage. 755 */ 756 @VisibleForTesting schedulePersistProcessExitInfo(boolean immediately)757 void schedulePersistProcessExitInfo(boolean immediately) { 758 synchronized (mLock) { 759 if (mAppExitInfoPersistTask == null || immediately) { 760 if (mAppExitInfoPersistTask != null) { 761 IoThread.getHandler().removeCallbacks(mAppExitInfoPersistTask); 762 } 763 mAppExitInfoPersistTask = this::persistProcessExitInfo; 764 IoThread.getHandler().postDelayed(mAppExitInfoPersistTask, 765 immediately ? 0 : APP_EXIT_INFO_PERSIST_INTERVAL); 766 } 767 } 768 } 769 770 /** 771 * Helper function for testing only. 772 */ 773 @VisibleForTesting clearProcessExitInfo(boolean removeFile)774 void clearProcessExitInfo(boolean removeFile) { 775 synchronized (mLock) { 776 if (mAppExitInfoPersistTask != null) { 777 IoThread.getHandler().removeCallbacks(mAppExitInfoPersistTask); 778 mAppExitInfoPersistTask = null; 779 } 780 if (removeFile && mProcExitInfoFile != null) { 781 mProcExitInfoFile.delete(); 782 } 783 mData.getMap().clear(); 784 mActiveAppStateSummary.clear(); 785 mActiveAppTraces.clear(); 786 pruneAnrTracesIfNecessaryLocked(); 787 } 788 } 789 790 /** 791 * Helper function for shell command 792 */ clearHistoryProcessExitInfo(String packageName, int userId)793 void clearHistoryProcessExitInfo(String packageName, int userId) { 794 NativeTombstoneManager tombstoneService = LocalServices.getService( 795 NativeTombstoneManager.class); 796 Optional<Integer> appId = Optional.empty(); 797 798 if (TextUtils.isEmpty(packageName)) { 799 synchronized (mLock) { 800 removeByUserIdLocked(userId); 801 } 802 } else { 803 final int uid = mService.mPackageManagerInt.getPackageUid(packageName, 804 PackageManager.MATCH_ALL, userId); 805 appId = Optional.of(UserHandle.getAppId(uid)); 806 synchronized (mLock) { 807 removePackageLocked(packageName, uid, true, userId); 808 } 809 } 810 811 tombstoneService.purge(Optional.of(userId), appId); 812 schedulePersistProcessExitInfo(true); 813 } 814 dumpHistoryProcessExitInfo(PrintWriter pw, String packageName)815 void dumpHistoryProcessExitInfo(PrintWriter pw, String packageName) { 816 pw.println("ACTIVITY MANAGER PROCESS EXIT INFO (dumpsys activity exit-info)"); 817 SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss.SSS"); 818 synchronized (mLock) { 819 pw.println("Last Timestamp of Persistence Into Persistent Storage: " 820 + sdf.format(new Date(mLastAppExitInfoPersistTimestamp))); 821 if (TextUtils.isEmpty(packageName)) { 822 forEachPackageLocked((name, records) -> { 823 dumpHistoryProcessExitInfoLocked(pw, " ", name, records, sdf); 824 return AppExitInfoTracker.FOREACH_ACTION_NONE; 825 }); 826 } else { 827 SparseArray<AppExitInfoContainer> array = mData.getMap().get(packageName); 828 if (array != null) { 829 dumpHistoryProcessExitInfoLocked(pw, " ", packageName, array, sdf); 830 } 831 } 832 } 833 } 834 835 @GuardedBy("mLock") dumpHistoryProcessExitInfoLocked(PrintWriter pw, String prefix, String packageName, SparseArray<AppExitInfoContainer> array, SimpleDateFormat sdf)836 private void dumpHistoryProcessExitInfoLocked(PrintWriter pw, String prefix, 837 String packageName, SparseArray<AppExitInfoContainer> array, 838 SimpleDateFormat sdf) { 839 pw.println(prefix + "package: " + packageName); 840 int size = array.size(); 841 for (int i = 0; i < size; i++) { 842 pw.println(prefix + " Historical Process Exit for uid=" + array.keyAt(i)); 843 array.valueAt(i).dumpLocked(pw, prefix + " ", sdf); 844 } 845 } 846 847 @GuardedBy("mLock") addExitInfoInnerLocked(String packageName, int uid, ApplicationExitInfo info)848 private void addExitInfoInnerLocked(String packageName, int uid, ApplicationExitInfo info) { 849 AppExitInfoContainer container = mData.get(packageName, uid); 850 if (container == null) { 851 container = new AppExitInfoContainer(mAppExitInfoHistoryListSize); 852 if (UserHandle.isIsolated(info.getRealUid())) { 853 Integer k = mIsolatedUidRecords.getUidByIsolatedUid(info.getRealUid()); 854 if (k != null) { 855 container.mUid = k; 856 } 857 } else { 858 container.mUid = info.getRealUid(); 859 } 860 mData.put(packageName, uid, container); 861 } 862 container.addExitInfoLocked(info); 863 } 864 865 @GuardedBy("mLock") scheduleLogToStatsdLocked(ApplicationExitInfo info, boolean immediate)866 private void scheduleLogToStatsdLocked(ApplicationExitInfo info, boolean immediate) { 867 if (info.isLoggedInStatsd()) { 868 return; 869 } 870 if (immediate) { 871 mKillHandler.removeMessages(KillHandler.MSG_STATSD_LOG, info); 872 performLogToStatsdLocked(info); 873 } else if (!mKillHandler.hasMessages(KillHandler.MSG_STATSD_LOG, info)) { 874 mKillHandler.sendMessageDelayed(mKillHandler.obtainMessage( 875 KillHandler.MSG_STATSD_LOG, info), APP_EXIT_INFO_STATSD_LOG_DEBOUNCE); 876 } 877 } 878 879 @GuardedBy("mLock") performLogToStatsdLocked(ApplicationExitInfo info)880 private void performLogToStatsdLocked(ApplicationExitInfo info) { 881 if (info.isLoggedInStatsd()) { 882 return; 883 } 884 info.setLoggedInStatsd(true); 885 final String pkgName = info.getPackageName(); 886 String processName = info.getProcessName(); 887 if (TextUtils.equals(pkgName, processName)) { 888 // Omit the process name here to save space 889 processName = null; 890 } else if (processName != null && pkgName != null && processName.startsWith(pkgName)) { 891 // Strip the prefix to save space 892 processName = processName.substring(pkgName.length()); 893 } 894 FrameworkStatsLog.write(FrameworkStatsLog.APP_PROCESS_DIED, 895 info.getPackageUid(), processName, info.getReason(), info.getSubReason(), 896 info.getImportance(), (int) info.getPss(), (int) info.getRss(), 897 info.hasForegroundServices()); 898 } 899 900 @GuardedBy("mLock") forEachPackageLocked( BiFunction<String, SparseArray<AppExitInfoContainer>, Integer> callback)901 private void forEachPackageLocked( 902 BiFunction<String, SparseArray<AppExitInfoContainer>, Integer> callback) { 903 if (callback != null) { 904 ArrayMap<String, SparseArray<AppExitInfoContainer>> map = mData.getMap(); 905 for (int i = map.size() - 1; i >= 0; i--) { 906 switch (callback.apply(map.keyAt(i), map.valueAt(i))) { 907 case FOREACH_ACTION_REMOVE_ITEM: 908 final SparseArray<AppExitInfoContainer> records = map.valueAt(i); 909 for (int j = records.size() - 1; j >= 0; j--) { 910 records.valueAt(j).destroyLocked(); 911 } 912 map.removeAt(i); 913 break; 914 case FOREACH_ACTION_STOP_ITERATION: 915 i = 0; 916 break; 917 case FOREACH_ACTION_NONE: 918 default: 919 break; 920 } 921 } 922 } 923 } 924 925 @GuardedBy("mLock") removePackageLocked(String packageName, int uid, boolean removeUid, int userId)926 private void removePackageLocked(String packageName, int uid, boolean removeUid, int userId) { 927 if (removeUid) { 928 mActiveAppStateSummary.remove(uid); 929 final int idx = mActiveAppTraces.indexOfKey(uid); 930 if (idx >= 0) { 931 final SparseArray<File> array = mActiveAppTraces.valueAt(idx); 932 for (int i = array.size() - 1; i >= 0; i--) { 933 array.valueAt(i).delete(); 934 } 935 mActiveAppTraces.removeAt(idx); 936 } 937 } 938 ArrayMap<String, SparseArray<AppExitInfoContainer>> map = mData.getMap(); 939 SparseArray<AppExitInfoContainer> array = map.get(packageName); 940 if (array == null) { 941 return; 942 } 943 if (userId == UserHandle.USER_ALL) { 944 for (int i = array.size() - 1; i >= 0; i--) { 945 array.valueAt(i).destroyLocked(); 946 } 947 mData.getMap().remove(packageName); 948 } else { 949 for (int i = array.size() - 1; i >= 0; i--) { 950 if (UserHandle.getUserId(array.keyAt(i)) == userId) { 951 array.valueAt(i).destroyLocked(); 952 array.removeAt(i); 953 break; 954 } 955 } 956 if (array.size() == 0) { 957 map.remove(packageName); 958 } 959 } 960 } 961 962 @GuardedBy("mLock") removeByUserIdLocked(final int userId)963 private void removeByUserIdLocked(final int userId) { 964 if (userId == UserHandle.USER_ALL) { 965 mData.getMap().clear(); 966 mActiveAppStateSummary.clear(); 967 mActiveAppTraces.clear(); 968 pruneAnrTracesIfNecessaryLocked(); 969 return; 970 } 971 removeFromSparse2dArray(mActiveAppStateSummary, 972 (v) -> UserHandle.getUserId(v) == userId, null, null); 973 removeFromSparse2dArray(mActiveAppTraces, 974 (v) -> UserHandle.getUserId(v) == userId, null, (v) -> v.delete()); 975 forEachPackageLocked((packageName, records) -> { 976 for (int i = records.size() - 1; i >= 0; i--) { 977 if (UserHandle.getUserId(records.keyAt(i)) == userId) { 978 records.valueAt(i).destroyLocked(); 979 records.removeAt(i); 980 break; 981 } 982 } 983 return records.size() == 0 ? FOREACH_ACTION_REMOVE_ITEM : FOREACH_ACTION_NONE; 984 }); 985 } 986 987 @VisibleForTesting obtainRawRecord(ProcessRecord app, @CurrentTimeMillisLong long timestamp)988 ApplicationExitInfo obtainRawRecord(ProcessRecord app, @CurrentTimeMillisLong long timestamp) { 989 ApplicationExitInfo info = mRawRecordsPool.acquire(); 990 if (info == null) { 991 info = new ApplicationExitInfo(); 992 } 993 994 synchronized (mService.mProcLock) { 995 final int definingUid = app.getHostingRecord() != null 996 ? app.getHostingRecord().getDefiningUid() : 0; 997 info.setPid(app.getPid()); 998 info.setRealUid(app.uid); 999 info.setPackageUid(app.info.uid); 1000 info.setDefiningUid(definingUid > 0 ? definingUid : app.info.uid); 1001 info.setProcessName(app.processName); 1002 info.setConnectionGroup(app.mServices.getConnectionGroup()); 1003 info.setPackageName(app.info.packageName); 1004 info.setPackageList(app.getPackageList()); 1005 info.setReason(ApplicationExitInfo.REASON_UNKNOWN); 1006 info.setSubReason(ApplicationExitInfo.SUBREASON_UNKNOWN); 1007 info.setStatus(0); 1008 info.setImportance(procStateToImportance(app.mState.getReportedProcState())); 1009 info.setPss(app.mProfile.getLastPss()); 1010 info.setRss(app.mProfile.getLastRss()); 1011 info.setTimestamp(timestamp); 1012 info.setHasForegroundServices(app.mServices.hasReportedForegroundServices()); 1013 } 1014 1015 return info; 1016 } 1017 1018 @VisibleForTesting recycleRawRecord(ApplicationExitInfo info)1019 void recycleRawRecord(ApplicationExitInfo info) { 1020 info.setProcessName(null); 1021 info.setDescription(null); 1022 info.setPackageList(null); 1023 1024 mRawRecordsPool.release(info); 1025 } 1026 1027 /** 1028 * Called from {@link ActivityManagerService#setProcessStateSummary}. 1029 */ 1030 @VisibleForTesting setProcessStateSummary(int uid, final int pid, final byte[] data)1031 void setProcessStateSummary(int uid, final int pid, final byte[] data) { 1032 synchronized (mLock) { 1033 Integer k = mIsolatedUidRecords.getUidByIsolatedUid(uid); 1034 if (k != null) { 1035 uid = k; 1036 } 1037 putToSparse2dArray(mActiveAppStateSummary, uid, pid, data, SparseArray::new, null); 1038 } 1039 } 1040 1041 @VisibleForTesting getProcessStateSummary(int uid, final int pid)1042 @Nullable byte[] getProcessStateSummary(int uid, final int pid) { 1043 synchronized (mLock) { 1044 Integer k = mIsolatedUidRecords.getUidByIsolatedUid(uid); 1045 if (k != null) { 1046 uid = k; 1047 } 1048 int index = mActiveAppStateSummary.indexOfKey(uid); 1049 if (index < 0) { 1050 return null; 1051 } 1052 return mActiveAppStateSummary.valueAt(index).get(pid); 1053 } 1054 } 1055 1056 /** 1057 * Called from ProcessRecord when an ANR occurred and the ANR trace is taken. 1058 */ scheduleLogAnrTrace(final int pid, final int uid, final String[] packageList, final File traceFile, final long startOff, final long endOff)1059 void scheduleLogAnrTrace(final int pid, final int uid, final String[] packageList, 1060 final File traceFile, final long startOff, final long endOff) { 1061 mKillHandler.sendMessage(PooledLambda.obtainMessage( 1062 this::handleLogAnrTrace, pid, uid, packageList, 1063 traceFile, startOff, endOff)); 1064 } 1065 1066 /** 1067 * Copy and compress the given ANR trace file 1068 */ 1069 @VisibleForTesting handleLogAnrTrace(final int pid, int uid, final String[] packageList, final File traceFile, final long startOff, final long endOff)1070 void handleLogAnrTrace(final int pid, int uid, final String[] packageList, 1071 final File traceFile, final long startOff, final long endOff) { 1072 if (!traceFile.exists() || ArrayUtils.isEmpty(packageList)) { 1073 return; 1074 } 1075 final long size = traceFile.length(); 1076 final long length = endOff - startOff; 1077 if (startOff >= size || endOff > size || length <= 0) { 1078 return; 1079 } 1080 1081 final File outFile = new File(mProcExitStoreDir, traceFile.getName() 1082 + APP_TRACE_FILE_SUFFIX); 1083 // Copy & compress 1084 if (copyToGzFile(traceFile, outFile, startOff, length)) { 1085 // Wrote successfully. 1086 synchronized (mLock) { 1087 Integer k = mIsolatedUidRecords.getUidByIsolatedUid(uid); 1088 if (k != null) { 1089 uid = k; 1090 } 1091 if (DEBUG_PROCESSES) { 1092 Slog.i(TAG, "Stored ANR traces of " + pid + "/u" + uid + " in " + outFile); 1093 } 1094 boolean pending = true; 1095 // Unlikely but possible: the app has died 1096 for (int i = 0; i < packageList.length; i++) { 1097 final AppExitInfoContainer container = mData.get(packageList[i], uid); 1098 // Try to see if we could append this trace to an existing record 1099 if (container != null && container.appendTraceIfNecessaryLocked(pid, outFile)) { 1100 // Okay someone took it 1101 pending = false; 1102 } 1103 } 1104 if (pending) { 1105 // Save it into a temporary list for later use (when the app dies). 1106 putToSparse2dArray(mActiveAppTraces, uid, pid, outFile, 1107 SparseArray::new, (v) -> v.delete()); 1108 } 1109 } 1110 } 1111 } 1112 1113 /** 1114 * Copy the given portion of the file into a gz file. 1115 * 1116 * @param inFile The source file. 1117 * @param outFile The destination file, which will be compressed in gzip format. 1118 * @param start The start offset where the copy should start from. 1119 * @param length The number of bytes that should be copied. 1120 * @return If the copy was successful or not. 1121 */ copyToGzFile(final File inFile, final File outFile, final long start, final long length)1122 private static boolean copyToGzFile(final File inFile, final File outFile, 1123 final long start, final long length) { 1124 long remaining = length; 1125 try ( 1126 BufferedInputStream in = new BufferedInputStream(new FileInputStream(inFile)); 1127 GZIPOutputStream out = new GZIPOutputStream(new BufferedOutputStream( 1128 new FileOutputStream(outFile)))) { 1129 final byte[] buffer = new byte[8192]; 1130 in.skip(start); 1131 while (remaining > 0) { 1132 int t = in.read(buffer, 0, (int) Math.min(buffer.length, remaining)); 1133 if (t < 0) { 1134 break; 1135 } 1136 out.write(buffer, 0, t); 1137 remaining -= t; 1138 } 1139 } catch (IOException e) { 1140 if (DEBUG_PROCESSES) { 1141 Slog.e(TAG, "Error in copying ANR trace from " + inFile + " to " + outFile, e); 1142 } 1143 return false; 1144 } 1145 return remaining == 0 && outFile.exists(); 1146 } 1147 1148 /** 1149 * In case there is any orphan ANR trace file, remove it. 1150 */ 1151 @GuardedBy("mLock") pruneAnrTracesIfNecessaryLocked()1152 private void pruneAnrTracesIfNecessaryLocked() { 1153 final ArraySet<String> allFiles = new ArraySet(); 1154 final File[] files = mProcExitStoreDir.listFiles((f) -> { 1155 final String name = f.getName(); 1156 boolean trace = name.startsWith(ActivityManagerService.ANR_FILE_PREFIX) 1157 && name.endsWith(APP_TRACE_FILE_SUFFIX); 1158 if (trace) { 1159 allFiles.add(name); 1160 } 1161 return trace; 1162 }); 1163 if (ArrayUtils.isEmpty(files)) { 1164 return; 1165 } 1166 // Find out the owners from the existing records 1167 forEachPackageLocked((name, records) -> { 1168 for (int i = records.size() - 1; i >= 0; i--) { 1169 final AppExitInfoContainer container = records.valueAt(i); 1170 container.forEachRecordLocked((pid, info) -> { 1171 final File traceFile = info.getTraceFile(); 1172 if (traceFile != null) { 1173 allFiles.remove(traceFile.getName()); 1174 } 1175 return FOREACH_ACTION_NONE; 1176 }); 1177 } 1178 return AppExitInfoTracker.FOREACH_ACTION_NONE; 1179 }); 1180 // See if there is any active process owns it. 1181 forEachSparse2dArray(mActiveAppTraces, (v) -> allFiles.remove(v.getName())); 1182 1183 // Remove orphan traces if nobody claims it. 1184 for (int i = allFiles.size() - 1; i >= 0; i--) { 1185 (new File(mProcExitStoreDir, allFiles.valueAt(i))).delete(); 1186 } 1187 } 1188 1189 /** 1190 * A utility function to add the given value to the given 2d SparseArray 1191 */ putToSparse2dArray(final SparseArray<T> array, final int outerKey, final int innerKey, final U value, final Supplier<T> newInstance, final Consumer<U> actionToOldValue)1192 private static <T extends SparseArray<U>, U> void putToSparse2dArray(final SparseArray<T> array, 1193 final int outerKey, final int innerKey, final U value, final Supplier<T> newInstance, 1194 final Consumer<U> actionToOldValue) { 1195 int idx = array.indexOfKey(outerKey); 1196 T innerArray = null; 1197 if (idx < 0) { 1198 innerArray = newInstance.get(); 1199 array.put(outerKey, innerArray); 1200 } else { 1201 innerArray = array.valueAt(idx); 1202 } 1203 idx = innerArray.indexOfKey(innerKey); 1204 if (idx >= 0) { 1205 if (actionToOldValue != null) { 1206 actionToOldValue.accept(innerArray.valueAt(idx)); 1207 } 1208 innerArray.setValueAt(idx, value); 1209 } else { 1210 innerArray.put(innerKey, value); 1211 } 1212 } 1213 1214 /** 1215 * A utility function to iterate through the given 2d SparseArray 1216 */ forEachSparse2dArray( final SparseArray<T> array, final Consumer<U> action)1217 private static <T extends SparseArray<U>, U> void forEachSparse2dArray( 1218 final SparseArray<T> array, final Consumer<U> action) { 1219 if (action != null) { 1220 for (int i = array.size() - 1; i >= 0; i--) { 1221 T innerArray = array.valueAt(i); 1222 if (innerArray == null) { 1223 continue; 1224 } 1225 for (int j = innerArray.size() - 1; j >= 0; j--) { 1226 action.accept(innerArray.valueAt(j)); 1227 } 1228 } 1229 } 1230 } 1231 1232 /** 1233 * A utility function to remove elements from the given 2d SparseArray 1234 */ removeFromSparse2dArray( final SparseArray<T> array, final Predicate<Integer> outerPredicate, final Predicate<Integer> innerPredicate, final Consumer<U> action)1235 private static <T extends SparseArray<U>, U> void removeFromSparse2dArray( 1236 final SparseArray<T> array, final Predicate<Integer> outerPredicate, 1237 final Predicate<Integer> innerPredicate, final Consumer<U> action) { 1238 for (int i = array.size() - 1; i >= 0; i--) { 1239 if (outerPredicate == null || outerPredicate.test(array.keyAt(i))) { 1240 final T innerArray = array.valueAt(i); 1241 if (innerArray == null) { 1242 continue; 1243 } 1244 for (int j = innerArray.size() - 1; j >= 0; j--) { 1245 if (innerPredicate == null || innerPredicate.test(innerArray.keyAt(j))) { 1246 if (action != null) { 1247 action.accept(innerArray.valueAt(j)); 1248 } 1249 innerArray.removeAt(j); 1250 } 1251 } 1252 if (innerArray.size() == 0) { 1253 array.removeAt(i); 1254 } 1255 } 1256 } 1257 } 1258 1259 /** 1260 * A utility function to find and remove elements from the given 2d SparseArray. 1261 */ findAndRemoveFromSparse2dArray( final SparseArray<T> array, final int outerKey, final int innerKey)1262 private static <T extends SparseArray<U>, U> U findAndRemoveFromSparse2dArray( 1263 final SparseArray<T> array, final int outerKey, final int innerKey) { 1264 final int idx = array.indexOfKey(outerKey); 1265 if (idx >= 0) { 1266 T p = array.valueAt(idx); 1267 if (p == null) { 1268 return null; 1269 } 1270 final int innerIdx = p.indexOfKey(innerKey); 1271 if (innerIdx >= 0) { 1272 final U ret = p.valueAt(innerIdx); 1273 p.removeAt(innerIdx); 1274 if (p.size() == 0) { 1275 array.removeAt(idx); 1276 } 1277 return ret; 1278 } 1279 } 1280 return null; 1281 } 1282 1283 /** 1284 * A container class of {@link android.app.ApplicationExitInfo} 1285 */ 1286 final class AppExitInfoContainer { 1287 private SparseArray<ApplicationExitInfo> mInfos; // index is pid 1288 private int mMaxCapacity; 1289 private int mUid; // Application uid, not isolated uid. 1290 AppExitInfoContainer(final int maxCapacity)1291 AppExitInfoContainer(final int maxCapacity) { 1292 mInfos = new SparseArray<ApplicationExitInfo>(); 1293 mMaxCapacity = maxCapacity; 1294 } 1295 1296 @GuardedBy("mLock") getExitInfoLocked(final int filterPid, final int maxNum, ArrayList<ApplicationExitInfo> results)1297 void getExitInfoLocked(final int filterPid, final int maxNum, 1298 ArrayList<ApplicationExitInfo> results) { 1299 if (filterPid > 0) { 1300 ApplicationExitInfo r = mInfos.get(filterPid); 1301 if (r != null) { 1302 results.add(r); 1303 } 1304 } else { 1305 final int numRep = mInfos.size(); 1306 if (maxNum <= 0 || numRep <= maxNum) { 1307 // Return all records. 1308 for (int i = 0; i < numRep; i++) { 1309 results.add(mInfos.valueAt(i)); 1310 } 1311 Collections.sort(results, 1312 (a, b) -> Long.compare(b.getTimestamp(), a.getTimestamp())); 1313 } else { 1314 if (maxNum == 1) { 1315 // Most of the caller might be only interested with the most recent one 1316 ApplicationExitInfo r = mInfos.valueAt(0); 1317 for (int i = 1; i < numRep; i++) { 1318 ApplicationExitInfo t = mInfos.valueAt(i); 1319 if (r.getTimestamp() < t.getTimestamp()) { 1320 r = t; 1321 } 1322 } 1323 results.add(r); 1324 } else { 1325 // Huh, need to sort it out then. 1326 ArrayList<ApplicationExitInfo> list = mTmpInfoList2; 1327 list.clear(); 1328 for (int i = 0; i < numRep; i++) { 1329 list.add(mInfos.valueAt(i)); 1330 } 1331 Collections.sort(list, 1332 (a, b) -> Long.compare(b.getTimestamp(), a.getTimestamp())); 1333 for (int i = 0; i < maxNum; i++) { 1334 results.add(list.get(i)); 1335 } 1336 list.clear(); 1337 } 1338 } 1339 } 1340 } 1341 1342 @GuardedBy("mLock") addExitInfoLocked(ApplicationExitInfo info)1343 void addExitInfoLocked(ApplicationExitInfo info) { 1344 int size; 1345 if ((size = mInfos.size()) >= mMaxCapacity) { 1346 int oldestIndex = -1; 1347 long oldestTimeStamp = Long.MAX_VALUE; 1348 for (int i = 0; i < size; i++) { 1349 ApplicationExitInfo r = mInfos.valueAt(i); 1350 if (r.getTimestamp() < oldestTimeStamp) { 1351 oldestTimeStamp = r.getTimestamp(); 1352 oldestIndex = i; 1353 } 1354 } 1355 if (oldestIndex >= 0) { 1356 final File traceFile = mInfos.valueAt(oldestIndex).getTraceFile(); 1357 if (traceFile != null) { 1358 traceFile.delete(); 1359 } 1360 mInfos.removeAt(oldestIndex); 1361 } 1362 } 1363 // Claim the state information if there is any 1364 final int uid = info.getPackageUid(); 1365 final int pid = info.getPid(); 1366 info.setProcessStateSummary(findAndRemoveFromSparse2dArray( 1367 mActiveAppStateSummary, uid, pid)); 1368 info.setTraceFile(findAndRemoveFromSparse2dArray(mActiveAppTraces, uid, pid)); 1369 info.setAppTraceRetriever(mAppTraceRetriever); 1370 mInfos.append(pid, info); 1371 } 1372 1373 @GuardedBy("mLock") appendTraceIfNecessaryLocked(final int pid, final File traceFile)1374 boolean appendTraceIfNecessaryLocked(final int pid, final File traceFile) { 1375 final ApplicationExitInfo r = mInfos.get(pid); 1376 if (r != null) { 1377 r.setTraceFile(traceFile); 1378 r.setAppTraceRetriever(mAppTraceRetriever); 1379 return true; 1380 } 1381 return false; 1382 } 1383 1384 @GuardedBy("mLock") destroyLocked()1385 void destroyLocked() { 1386 for (int i = mInfos.size() - 1; i >= 0; i--) { 1387 ApplicationExitInfo ai = mInfos.valueAt(i); 1388 final File traceFile = ai.getTraceFile(); 1389 if (traceFile != null) { 1390 traceFile.delete(); 1391 } 1392 ai.setTraceFile(null); 1393 ai.setAppTraceRetriever(null); 1394 } 1395 } 1396 1397 @GuardedBy("mLock") forEachRecordLocked(final BiFunction<Integer, ApplicationExitInfo, Integer> callback)1398 void forEachRecordLocked(final BiFunction<Integer, ApplicationExitInfo, Integer> callback) { 1399 if (callback != null) { 1400 for (int i = mInfos.size() - 1; i >= 0; i--) { 1401 switch (callback.apply(mInfos.keyAt(i), mInfos.valueAt(i))) { 1402 case FOREACH_ACTION_REMOVE_ITEM: 1403 final File traceFile = mInfos.valueAt(i).getTraceFile(); 1404 if (traceFile != null) { 1405 traceFile.delete(); 1406 } 1407 mInfos.removeAt(i); 1408 break; 1409 case FOREACH_ACTION_STOP_ITERATION: 1410 i = 0; 1411 break; 1412 case FOREACH_ACTION_NONE: 1413 default: 1414 break; 1415 } 1416 } 1417 } 1418 } 1419 1420 @GuardedBy("mLock") dumpLocked(PrintWriter pw, String prefix, SimpleDateFormat sdf)1421 void dumpLocked(PrintWriter pw, String prefix, SimpleDateFormat sdf) { 1422 ArrayList<ApplicationExitInfo> list = new ArrayList<ApplicationExitInfo>(); 1423 for (int i = mInfos.size() - 1; i >= 0; i--) { 1424 list.add(mInfos.valueAt(i)); 1425 } 1426 Collections.sort(list, (a, b) -> Long.compare(b.getTimestamp(), a.getTimestamp())); 1427 int size = list.size(); 1428 for (int i = 0; i < size; i++) { 1429 list.get(i).dump(pw, prefix + " ", "#" + i, sdf); 1430 } 1431 } 1432 1433 @GuardedBy("mLock") writeToProto(ProtoOutputStream proto, long fieldId)1434 void writeToProto(ProtoOutputStream proto, long fieldId) { 1435 long token = proto.start(fieldId); 1436 proto.write(AppsExitInfoProto.Package.User.UID, mUid); 1437 int size = mInfos.size(); 1438 for (int i = 0; i < size; i++) { 1439 mInfos.valueAt(i).writeToProto(proto, AppsExitInfoProto.Package.User.APP_EXIT_INFO); 1440 } 1441 proto.end(token); 1442 } 1443 readFromProto(ProtoInputStream proto, long fieldId)1444 int readFromProto(ProtoInputStream proto, long fieldId) 1445 throws IOException, WireTypeMismatchException { 1446 long token = proto.start(fieldId); 1447 for (int next = proto.nextField(); 1448 next != ProtoInputStream.NO_MORE_FIELDS; 1449 next = proto.nextField()) { 1450 switch (next) { 1451 case (int) AppsExitInfoProto.Package.User.UID: 1452 mUid = proto.readInt(AppsExitInfoProto.Package.User.UID); 1453 break; 1454 case (int) AppsExitInfoProto.Package.User.APP_EXIT_INFO: 1455 ApplicationExitInfo info = new ApplicationExitInfo(); 1456 info.readFromProto(proto, AppsExitInfoProto.Package.User.APP_EXIT_INFO); 1457 mInfos.put(info.getPid(), info); 1458 break; 1459 } 1460 } 1461 proto.end(token); 1462 return mUid; 1463 } 1464 1465 @GuardedBy("mLock") toListLocked(List<ApplicationExitInfo> list, int filterPid)1466 List<ApplicationExitInfo> toListLocked(List<ApplicationExitInfo> list, int filterPid) { 1467 if (list == null) { 1468 list = new ArrayList<ApplicationExitInfo>(); 1469 } 1470 for (int i = mInfos.size() - 1; i >= 0; i--) { 1471 if (filterPid == 0 || filterPid == mInfos.keyAt(i)) { 1472 list.add(mInfos.valueAt(i)); 1473 } 1474 } 1475 return list; 1476 } 1477 } 1478 1479 /** 1480 * Maintains the mapping between real UID and the application uid. 1481 */ 1482 final class IsolatedUidRecords { 1483 /** 1484 * A mapping from application uid (with the userId) to isolated uids. 1485 */ 1486 @GuardedBy("mLock") 1487 private final SparseArray<ArraySet<Integer>> mUidToIsolatedUidMap; 1488 1489 /** 1490 * A mapping from isolated uids to application uid (with the userId) 1491 */ 1492 @GuardedBy("mLock") 1493 private final SparseArray<Integer> mIsolatedUidToUidMap; 1494 IsolatedUidRecords()1495 IsolatedUidRecords() { 1496 mUidToIsolatedUidMap = new SparseArray<ArraySet<Integer>>(); 1497 mIsolatedUidToUidMap = new SparseArray<Integer>(); 1498 } 1499 addIsolatedUid(int isolatedUid, int uid)1500 void addIsolatedUid(int isolatedUid, int uid) { 1501 synchronized (mLock) { 1502 ArraySet<Integer> set = mUidToIsolatedUidMap.get(uid); 1503 if (set == null) { 1504 set = new ArraySet<Integer>(); 1505 mUidToIsolatedUidMap.put(uid, set); 1506 } 1507 set.add(isolatedUid); 1508 1509 mIsolatedUidToUidMap.put(isolatedUid, uid); 1510 } 1511 } 1512 removeIsolatedUid(int isolatedUid, int uid)1513 void removeIsolatedUid(int isolatedUid, int uid) { 1514 synchronized (mLock) { 1515 final int index = mUidToIsolatedUidMap.indexOfKey(uid); 1516 if (index >= 0) { 1517 final ArraySet<Integer> set = mUidToIsolatedUidMap.valueAt(index); 1518 set.remove(isolatedUid); 1519 if (set.isEmpty()) { 1520 mUidToIsolatedUidMap.removeAt(index); 1521 } 1522 } 1523 mIsolatedUidToUidMap.remove(isolatedUid); 1524 } 1525 } 1526 1527 @GuardedBy("mLock") getUidByIsolatedUid(int isolatedUid)1528 Integer getUidByIsolatedUid(int isolatedUid) { 1529 if (UserHandle.isIsolated(isolatedUid)) { 1530 synchronized (mLock) { 1531 return mIsolatedUidToUidMap.get(isolatedUid); 1532 } 1533 } 1534 return isolatedUid; 1535 } 1536 1537 @GuardedBy("mLock") removeAppUidLocked(int uid)1538 private void removeAppUidLocked(int uid) { 1539 ArraySet<Integer> set = mUidToIsolatedUidMap.get(uid); 1540 if (set != null) { 1541 for (int i = set.size() - 1; i >= 0; i--) { 1542 int isolatedUid = set.removeAt(i); 1543 mIsolatedUidToUidMap.remove(isolatedUid); 1544 } 1545 } 1546 } 1547 1548 @VisibleForTesting removeAppUid(int uid, boolean allUsers)1549 void removeAppUid(int uid, boolean allUsers) { 1550 synchronized (mLock) { 1551 if (allUsers) { 1552 uid = UserHandle.getAppId(uid); 1553 for (int i = mUidToIsolatedUidMap.size() - 1; i >= 0; i--) { 1554 int u = mUidToIsolatedUidMap.keyAt(i); 1555 if (uid == UserHandle.getAppId(u)) { 1556 removeAppUidLocked(u); 1557 } 1558 mUidToIsolatedUidMap.removeAt(i); 1559 } 1560 } else { 1561 removeAppUidLocked(uid); 1562 mUidToIsolatedUidMap.remove(uid); 1563 } 1564 } 1565 } 1566 1567 @GuardedBy("mLock") removeIsolatedUidLocked(int isolatedUid)1568 int removeIsolatedUidLocked(int isolatedUid) { 1569 if (!UserHandle.isIsolated(isolatedUid)) { 1570 return isolatedUid; 1571 } 1572 int uid = mIsolatedUidToUidMap.get(isolatedUid, -1); 1573 if (uid == -1) { 1574 return isolatedUid; 1575 } 1576 mIsolatedUidToUidMap.remove(isolatedUid); 1577 ArraySet<Integer> set = mUidToIsolatedUidMap.get(uid); 1578 if (set != null) { 1579 set.remove(isolatedUid); 1580 } 1581 // let the ArraySet stay in the mUidToIsolatedUidMap even if it's empty 1582 return uid; 1583 } 1584 removeByUserId(int userId)1585 void removeByUserId(int userId) { 1586 if (userId == UserHandle.USER_CURRENT) { 1587 userId = mService.mUserController.getCurrentUserId(); 1588 } 1589 synchronized (mLock) { 1590 if (userId == UserHandle.USER_ALL) { 1591 mIsolatedUidToUidMap.clear(); 1592 mUidToIsolatedUidMap.clear(); 1593 return; 1594 } 1595 for (int i = mIsolatedUidToUidMap.size() - 1; i >= 0; i--) { 1596 int isolatedUid = mIsolatedUidToUidMap.keyAt(i); 1597 int uid = mIsolatedUidToUidMap.valueAt(i); 1598 if (UserHandle.getUserId(uid) == userId) { 1599 mIsolatedUidToUidMap.removeAt(i); 1600 mUidToIsolatedUidMap.remove(uid); 1601 } 1602 } 1603 } 1604 } 1605 } 1606 1607 final class KillHandler extends Handler { 1608 static final int MSG_LMKD_PROC_KILLED = 4101; 1609 static final int MSG_CHILD_PROC_DIED = 4102; 1610 static final int MSG_PROC_DIED = 4103; 1611 static final int MSG_APP_KILL = 4104; 1612 static final int MSG_STATSD_LOG = 4105; 1613 KillHandler(Looper looper)1614 KillHandler(Looper looper) { 1615 super(looper, null, true); 1616 } 1617 1618 @Override handleMessage(Message msg)1619 public void handleMessage(Message msg) { 1620 switch (msg.what) { 1621 case MSG_LMKD_PROC_KILLED: 1622 mAppExitInfoSourceLmkd.onProcDied(msg.arg1 /* pid */, msg.arg2 /* uid */, 1623 null /* status */); 1624 break; 1625 case MSG_CHILD_PROC_DIED: 1626 mAppExitInfoSourceZygote.onProcDied(msg.arg1 /* pid */, msg.arg2 /* uid */, 1627 (Integer) msg.obj /* status */); 1628 break; 1629 case MSG_PROC_DIED: { 1630 ApplicationExitInfo raw = (ApplicationExitInfo) msg.obj; 1631 synchronized (mLock) { 1632 handleNoteProcessDiedLocked(raw); 1633 } 1634 recycleRawRecord(raw); 1635 } 1636 break; 1637 case MSG_APP_KILL: { 1638 ApplicationExitInfo raw = (ApplicationExitInfo) msg.obj; 1639 synchronized (mLock) { 1640 handleNoteAppKillLocked(raw); 1641 } 1642 recycleRawRecord(raw); 1643 } 1644 break; 1645 case MSG_STATSD_LOG: { 1646 synchronized (mLock) { 1647 performLogToStatsdLocked((ApplicationExitInfo) msg.obj); 1648 } 1649 } 1650 break; 1651 default: 1652 super.handleMessage(msg); 1653 } 1654 } 1655 } 1656 1657 @VisibleForTesting isFresh(long timestamp)1658 boolean isFresh(long timestamp) { 1659 // A process could be dying but being stuck in some state, i.e., 1660 // being TRACED by tombstoned, thus the zygote receives SIGCHILD 1661 // way after we already knew the kill (maybe because we did the kill :P), 1662 // so here check if the last known kill information is "fresh" enough. 1663 long now = System.currentTimeMillis(); 1664 1665 return (timestamp + AppExitInfoExternalSource.APP_EXIT_INFO_FRESHNESS_MS) >= now; 1666 } 1667 1668 /** 1669 * Keep the raw information about app kills from external sources, i.e., lmkd 1670 */ 1671 final class AppExitInfoExternalSource { 1672 private static final long APP_EXIT_INFO_FRESHNESS_MS = 300 * 1000; 1673 1674 /** 1675 * A mapping between uid -> pid -> {timestamp, extra info(Nullable)}. 1676 * The uid here is the application uid, not the isolated uid. 1677 */ 1678 @GuardedBy("mLock") 1679 private final SparseArray<SparseArray<Pair<Long, Object>>> mData; 1680 1681 /** A tag for logging only */ 1682 private final String mTag; 1683 1684 /** A preset reason in case a proc dies */ 1685 private final Integer mPresetReason; 1686 1687 /** A callback that will be notified when a proc dies */ 1688 private BiConsumer<Integer, Integer> mProcDiedListener; 1689 AppExitInfoExternalSource(String tag, Integer reason)1690 AppExitInfoExternalSource(String tag, Integer reason) { 1691 mData = new SparseArray<SparseArray<Pair<Long, Object>>>(); 1692 mTag = tag; 1693 mPresetReason = reason; 1694 } 1695 1696 @GuardedBy("mLock") addLocked(int pid, int uid, Object extra)1697 private void addLocked(int pid, int uid, Object extra) { 1698 Integer k = mIsolatedUidRecords.getUidByIsolatedUid(uid); 1699 if (k != null) { 1700 uid = k; 1701 } 1702 1703 SparseArray<Pair<Long, Object>> array = mData.get(uid); 1704 if (array == null) { 1705 array = new SparseArray<Pair<Long, Object>>(); 1706 mData.put(uid, array); 1707 } 1708 array.put(pid, new Pair<Long, Object>(System.currentTimeMillis(), extra)); 1709 } 1710 1711 @VisibleForTesting remove(int pid, int uid)1712 Pair<Long, Object> remove(int pid, int uid) { 1713 synchronized (mLock) { 1714 Integer k = mIsolatedUidRecords.getUidByIsolatedUid(uid); 1715 if (k != null) { 1716 uid = k; 1717 } 1718 1719 SparseArray<Pair<Long, Object>> array = mData.get(uid); 1720 if (array != null) { 1721 Pair<Long, Object> p = array.get(pid); 1722 if (p != null) { 1723 array.remove(pid); 1724 return isFresh(p.first) ? p : null; 1725 } 1726 } 1727 return null; 1728 } 1729 } 1730 removeByUserId(int userId)1731 void removeByUserId(int userId) { 1732 if (userId == UserHandle.USER_CURRENT) { 1733 userId = mService.mUserController.getCurrentUserId(); 1734 } 1735 synchronized (mLock) { 1736 if (userId == UserHandle.USER_ALL) { 1737 mData.clear(); 1738 return; 1739 } 1740 for (int i = mData.size() - 1; i >= 0; i--) { 1741 int uid = mData.keyAt(i); 1742 if (UserHandle.getUserId(uid) == userId) { 1743 mData.removeAt(i); 1744 } 1745 } 1746 } 1747 } 1748 1749 @GuardedBy("mLock") removeByUidLocked(int uid, boolean allUsers)1750 void removeByUidLocked(int uid, boolean allUsers) { 1751 if (UserHandle.isIsolated(uid)) { 1752 Integer k = mIsolatedUidRecords.getUidByIsolatedUid(uid); 1753 if (k != null) { 1754 uid = k; 1755 } 1756 } 1757 1758 if (allUsers) { 1759 uid = UserHandle.getAppId(uid); 1760 for (int i = mData.size() - 1; i >= 0; i--) { 1761 if (UserHandle.getAppId(mData.keyAt(i)) == uid) { 1762 mData.removeAt(i); 1763 } 1764 } 1765 } else { 1766 mData.remove(uid); 1767 } 1768 } 1769 setOnProcDiedListener(BiConsumer<Integer, Integer> listener)1770 void setOnProcDiedListener(BiConsumer<Integer, Integer> listener) { 1771 synchronized (mLock) { 1772 mProcDiedListener = listener; 1773 } 1774 } 1775 onProcDied(final int pid, final int uid, final Integer status)1776 void onProcDied(final int pid, final int uid, final Integer status) { 1777 if (DEBUG_PROCESSES) { 1778 Slog.i(TAG, mTag + ": proc died: pid=" + pid + " uid=" + uid 1779 + ", status=" + status); 1780 } 1781 1782 if (mService == null) { 1783 return; 1784 } 1785 1786 // Unlikely but possible: the record has been created 1787 // Let's update it if we could find a ApplicationExitInfo record 1788 synchronized (mLock) { 1789 if (!updateExitInfoIfNecessaryLocked(pid, uid, status, mPresetReason)) { 1790 addLocked(pid, uid, status); 1791 } 1792 1793 // Notify any interesed party regarding the lmkd kills 1794 final BiConsumer<Integer, Integer> listener = mProcDiedListener; 1795 if (listener != null) { 1796 mService.mHandler.post(()-> listener.accept(pid, uid)); 1797 } 1798 } 1799 } 1800 } 1801 1802 /** 1803 * The implementation to the IAppTraceRetriever interface. 1804 */ 1805 @VisibleForTesting 1806 class AppTraceRetriever extends IAppTraceRetriever.Stub { 1807 @Override getTraceFileDescriptor(final String packageName, final int uid, final int pid)1808 public ParcelFileDescriptor getTraceFileDescriptor(final String packageName, 1809 final int uid, final int pid) { 1810 mService.enforceNotIsolatedCaller("getTraceFileDescriptor"); 1811 1812 if (TextUtils.isEmpty(packageName)) { 1813 throw new IllegalArgumentException("Invalid package name"); 1814 } 1815 final int callingPid = Binder.getCallingPid(); 1816 final int callingUid = Binder.getCallingUid(); 1817 final int callingUserId = UserHandle.getCallingUserId(); 1818 final int userId = UserHandle.getUserId(uid); 1819 1820 mService.mUserController.handleIncomingUser(callingPid, callingUid, userId, true, 1821 ALLOW_NON_FULL, "getTraceFileDescriptor", null); 1822 if (mService.enforceDumpPermissionForPackage(packageName, userId, 1823 callingUid, "getTraceFileDescriptor") != Process.INVALID_UID) { 1824 synchronized (mLock) { 1825 final ApplicationExitInfo info = getExitInfoLocked(packageName, uid, pid); 1826 if (info == null) { 1827 return null; 1828 } 1829 final File traceFile = info.getTraceFile(); 1830 if (traceFile == null) { 1831 return null; 1832 } 1833 final long identity = Binder.clearCallingIdentity(); 1834 try { 1835 // The fd will be closed after being written into Parcel 1836 return ParcelFileDescriptor.open(traceFile, 1837 ParcelFileDescriptor.MODE_READ_ONLY); 1838 } catch (FileNotFoundException e) { 1839 return null; 1840 } finally { 1841 Binder.restoreCallingIdentity(identity); 1842 } 1843 } 1844 } 1845 return null; 1846 } 1847 } 1848 } 1849