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