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