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