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