• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2016 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.ActivityTaskManager.INVALID_TASK_ID;
20 import static android.content.pm.ApplicationInfo.FLAG_SYSTEM;
21 
22 import static com.android.server.am.ActivityManagerConstants.PROCESS_CRASH_COUNT_LIMIT;
23 import static com.android.server.am.ActivityManagerConstants.PROCESS_CRASH_COUNT_RESET_INTERVAL;
24 import static com.android.server.am.ActivityManagerDebugConfig.TAG_AM;
25 import static com.android.server.am.ActivityManagerDebugConfig.TAG_WITH_CLASS_NAME;
26 import static com.android.server.am.ActivityManagerService.MY_PID;
27 import static com.android.server.wm.ActivityTaskManagerService.RELAUNCH_REASON_FREE_RESIZE;
28 import static com.android.server.wm.ActivityTaskManagerService.RELAUNCH_REASON_NONE;
29 
30 import android.app.ActivityManager;
31 import android.app.ActivityOptions;
32 import android.app.AnrController;
33 import android.app.ApplicationErrorReport;
34 import android.app.ApplicationExitInfo;
35 import android.app.usage.UsageStatsManager;
36 import android.content.ActivityNotFoundException;
37 import android.content.Context;
38 import android.content.Intent;
39 import android.content.pm.VersionedPackage;
40 import android.net.Uri;
41 import android.os.Binder;
42 import android.os.Build;
43 import android.os.Message;
44 import android.os.Process;
45 import android.os.SystemClock;
46 import android.os.UserHandle;
47 import android.provider.Settings;
48 import android.util.ArrayMap;
49 import android.util.ArraySet;
50 import android.util.EventLog;
51 import android.util.Pair;
52 import android.util.Slog;
53 import android.util.SparseArray;
54 import android.util.TimeUtils;
55 import android.util.proto.ProtoOutputStream;
56 
57 import com.android.internal.annotations.GuardedBy;
58 import com.android.internal.app.ProcessMap;
59 import com.android.internal.logging.MetricsLogger;
60 import com.android.internal.logging.nano.MetricsProto;
61 import com.android.server.LocalServices;
62 import com.android.server.PackageWatchdog;
63 import com.android.server.usage.AppStandbyInternal;
64 import com.android.server.wm.WindowProcessController;
65 
66 import java.io.FileDescriptor;
67 import java.io.PrintWriter;
68 import java.util.Collections;
69 import java.util.List;
70 
71 /**
72  * Controls error conditions in applications.
73  */
74 class AppErrors {
75 
76     private static final String TAG = TAG_WITH_CLASS_NAME ? "AppErrors" : TAG_AM;
77 
78     private final ActivityManagerService mService;
79     private final ActivityManagerGlobalLock mProcLock;
80     private final Context mContext;
81     private final PackageWatchdog mPackageWatchdog;
82 
83     @GuardedBy("mBadProcessLock")
84     private ArraySet<String> mAppsNotReportingCrashes;
85 
86     /**
87      * The last time that various processes have crashed since they were last explicitly started.
88      */
89     @GuardedBy("mBadProcessLock")
90     private final ProcessMap<Long> mProcessCrashTimes = new ProcessMap<>();
91 
92     /**
93      * The last time that various processes have crashed (not reset even when explicitly started).
94      */
95     @GuardedBy("mBadProcessLock")
96     private final ProcessMap<Long> mProcessCrashTimesPersistent = new ProcessMap<>();
97 
98     /**
99      * The last time that various processes have crashed and shown an error dialog.
100      */
101     @GuardedBy("mBadProcessLock")
102     private final ProcessMap<Long> mProcessCrashShowDialogTimes = new ProcessMap<>();
103 
104     /**
105      * A pairing between how many times various processes have crashed since a given time.
106      * Entry and exit conditions for this map are similar to mProcessCrashTimes.
107      */
108     @GuardedBy("mBadProcessLock")
109     private final ProcessMap<Pair<Long, Integer>> mProcessCrashCounts = new ProcessMap<>();
110 
111     /**
112      * Set of applications that we consider to be bad, and will reject
113      * incoming broadcasts from (which the user has no control over).
114      * Processes are added to this set when they have crashed twice within
115      * a minimum amount of time; they are removed from it when they are
116      * later restarted (hopefully due to some user action).  The value is the
117      * time it was added to the list.
118      *
119      * Read access is UNLOCKED, and must either be based on a single lookup
120      * call on the current mBadProcesses instance, or a local copy of that
121      * reference must be made and the local copy treated as the source of
122      * truth.  Mutations are performed by synchronizing on mBadProcessLock,
123      * cloning the existing mBadProcesses instance, performing the mutation,
124      * then changing the volatile "live" mBadProcesses reference to point to the
125      * mutated version.  These operations are very rare compared to lookups:
126      * we intentionally trade additional cost for mutations for eliminating
127      * lock operations from the simple lookup cases.
128      */
129     private volatile ProcessMap<BadProcessInfo> mBadProcesses = new ProcessMap<>();
130 
131     /**
132      * Dedicated lock for {@link #mAppsNotReportingCrashes}, {@link #mProcessCrashTimes},
133      * {@link #mProcessCrashTimesPersistent}, {@link #mProcessCrashShowDialogTimes},
134      * {@link #mProcessCrashCounts} and {@link #mBadProcesses}.
135      *
136      * <p>The naming convention of the function with this lock should be "-LBp"</b>
137      *
138      * @See mBadProcesses
139      */
140     private final Object mBadProcessLock = new Object();
141 
AppErrors(Context context, ActivityManagerService service, PackageWatchdog watchdog)142     AppErrors(Context context, ActivityManagerService service, PackageWatchdog watchdog) {
143         context.assertRuntimeOverlayThemable();
144         mService = service;
145         mProcLock = service.mProcLock;
146         mContext = context;
147         mPackageWatchdog = watchdog;
148     }
149 
150     /** Resets the current state but leaves the constructor-provided fields unchanged. */
resetState()151     public void resetState() {
152         Slog.i(TAG, "Resetting AppErrors");
153         synchronized (mBadProcessLock) {
154             mAppsNotReportingCrashes.clear();
155             mProcessCrashTimes.clear();
156             mProcessCrashTimesPersistent.clear();
157             mProcessCrashShowDialogTimes.clear();
158             mProcessCrashCounts.clear();
159             mBadProcesses = new ProcessMap<>();
160         }
161     }
162 
163     @GuardedBy("mProcLock")
dumpDebugLPr(ProtoOutputStream proto, long fieldId, String dumpPackage)164     void dumpDebugLPr(ProtoOutputStream proto, long fieldId, String dumpPackage) {
165         final ProcessMap<BadProcessInfo> badProcesses = mBadProcesses;
166         if (mProcessCrashTimes.getMap().isEmpty() && badProcesses.getMap().isEmpty()) {
167             return;
168         }
169 
170         final long token = proto.start(fieldId);
171         final long now = SystemClock.uptimeMillis();
172         proto.write(AppErrorsProto.NOW_UPTIME_MS, now);
173 
174         if (!badProcesses.getMap().isEmpty()) {
175             final ArrayMap<String, SparseArray<BadProcessInfo>> pmap = badProcesses.getMap();
176             final int processCount = pmap.size();
177             for (int ip = 0; ip < processCount; ip++) {
178                 final long btoken = proto.start(AppErrorsProto.BAD_PROCESSES);
179                 final String pname = pmap.keyAt(ip);
180                 final SparseArray<BadProcessInfo> uids = pmap.valueAt(ip);
181                 final int uidCount = uids.size();
182 
183                 proto.write(AppErrorsProto.BadProcess.PROCESS_NAME, pname);
184                 for (int i = 0; i < uidCount; i++) {
185                     final int puid = uids.keyAt(i);
186                     final ProcessRecord r = mService.getProcessNamesLOSP().get(pname, puid);
187                     if (dumpPackage != null && (r == null
188                             || !r.getPkgList().containsKey(dumpPackage))) {
189                         continue;
190                     }
191                     final BadProcessInfo info = uids.valueAt(i);
192                     final long etoken = proto.start(AppErrorsProto.BadProcess.ENTRIES);
193                     proto.write(AppErrorsProto.BadProcess.Entry.UID, puid);
194                     proto.write(AppErrorsProto.BadProcess.Entry.CRASHED_AT_MS, info.time);
195                     proto.write(AppErrorsProto.BadProcess.Entry.SHORT_MSG, info.shortMsg);
196                     proto.write(AppErrorsProto.BadProcess.Entry.LONG_MSG, info.longMsg);
197                     proto.write(AppErrorsProto.BadProcess.Entry.STACK, info.stack);
198                     proto.end(etoken);
199                 }
200                 proto.end(btoken);
201             }
202         }
203 
204         synchronized (mBadProcessLock) {
205             if (!mProcessCrashTimes.getMap().isEmpty()) {
206                 final ArrayMap<String, SparseArray<Long>> pmap = mProcessCrashTimes.getMap();
207                 final int procCount = pmap.size();
208                 for (int ip = 0; ip < procCount; ip++) {
209                     final long ctoken = proto.start(AppErrorsProto.PROCESS_CRASH_TIMES);
210                     final String pname = pmap.keyAt(ip);
211                     final SparseArray<Long> uids = pmap.valueAt(ip);
212                     final int uidCount = uids.size();
213 
214                     proto.write(AppErrorsProto.ProcessCrashTime.PROCESS_NAME, pname);
215                     for (int i = 0; i < uidCount; i++) {
216                         final int puid = uids.keyAt(i);
217                         final ProcessRecord r = mService.getProcessNamesLOSP().get(pname, puid);
218                         if (dumpPackage != null
219                                 && (r == null || !r.getPkgList().containsKey(dumpPackage))) {
220                             continue;
221                         }
222                         final long etoken = proto.start(AppErrorsProto.ProcessCrashTime.ENTRIES);
223                         proto.write(AppErrorsProto.ProcessCrashTime.Entry.UID, puid);
224                         proto.write(AppErrorsProto.ProcessCrashTime.Entry.LAST_CRASHED_AT_MS,
225                                 uids.valueAt(i));
226                         proto.end(etoken);
227                     }
228                     proto.end(ctoken);
229                 }
230             }
231         }
232 
233         proto.end(token);
234     }
235 
236     @GuardedBy("mProcLock")
dumpLPr(FileDescriptor fd, PrintWriter pw, boolean needSep, String dumpPackage)237     boolean dumpLPr(FileDescriptor fd, PrintWriter pw, boolean needSep, String dumpPackage) {
238         final long now = SystemClock.uptimeMillis();
239         synchronized (mBadProcessLock) {
240             if (!mProcessCrashTimes.getMap().isEmpty()) {
241                 boolean printed = false;
242                 final ArrayMap<String, SparseArray<Long>> pmap = mProcessCrashTimes.getMap();
243                 final int processCount = pmap.size();
244                 for (int ip = 0; ip < processCount; ip++) {
245                     final String pname = pmap.keyAt(ip);
246                     final SparseArray<Long> uids = pmap.valueAt(ip);
247                     final int uidCount = uids.size();
248                     for (int i = 0; i < uidCount; i++) {
249                         final int puid = uids.keyAt(i);
250                         final ProcessRecord r = mService.getProcessNamesLOSP().get(pname, puid);
251                         if (dumpPackage != null
252                                 && (r == null || !r.getPkgList().containsKey(dumpPackage))) {
253                             continue;
254                         }
255                         if (!printed) {
256                             if (needSep) pw.println();
257                             needSep = true;
258                             pw.println("  Time since processes crashed:");
259                             printed = true;
260                         }
261                         pw.print("    Process "); pw.print(pname);
262                         pw.print(" uid "); pw.print(puid);
263                         pw.print(": last crashed ");
264                         TimeUtils.formatDuration(now - uids.valueAt(i), pw);
265                         pw.println(" ago");
266                     }
267                 }
268             }
269 
270             if (!mProcessCrashCounts.getMap().isEmpty()) {
271                 boolean printed = false;
272                 final ArrayMap<String, SparseArray<Pair<Long, Integer>>> pmap =
273                         mProcessCrashCounts.getMap();
274                 final int processCount = pmap.size();
275                 for (int ip = 0; ip < processCount; ip++) {
276                     final String pname = pmap.keyAt(ip);
277                     final SparseArray<Pair<Long, Integer>> uids = pmap.valueAt(ip);
278                     final int uidCount = uids.size();
279                     for (int i = 0; i < uidCount; i++) {
280                         final int puid = uids.keyAt(i);
281                         final ProcessRecord r = mService.getProcessNamesLOSP().get(pname, puid);
282                         if (dumpPackage != null
283                                 && (r == null || !r.getPkgList().containsKey(dumpPackage))) {
284                             continue;
285                         }
286                         if (!printed) {
287                             if (needSep) pw.println();
288                             needSep = true;
289                             pw.println("  First time processes crashed and counts:");
290                             printed = true;
291                         }
292                         pw.print("    Process "); pw.print(pname);
293                         pw.print(" uid "); pw.print(puid);
294                         pw.print(": first crashed ");
295                         TimeUtils.formatDuration(now - uids.valueAt(i).first, pw);
296                         pw.print(" ago; crashes since then: "); pw.println(uids.valueAt(i).second);
297                     }
298                 }
299             }
300         }
301 
302         final ProcessMap<BadProcessInfo> badProcesses = mBadProcesses;
303         if (!badProcesses.getMap().isEmpty()) {
304             boolean printed = false;
305             final ArrayMap<String, SparseArray<BadProcessInfo>> pmap = badProcesses.getMap();
306             final int processCount = pmap.size();
307             for (int ip = 0; ip < processCount; ip++) {
308                 final String pname = pmap.keyAt(ip);
309                 final SparseArray<BadProcessInfo> uids = pmap.valueAt(ip);
310                 final int uidCount = uids.size();
311                 for (int i = 0; i < uidCount; i++) {
312                     final int puid = uids.keyAt(i);
313                     final ProcessRecord r = mService.getProcessNamesLOSP().get(pname, puid);
314                     if (dumpPackage != null && (r == null
315                             || !r.getPkgList().containsKey(dumpPackage))) {
316                         continue;
317                     }
318                     if (!printed) {
319                         if (needSep) pw.println();
320                         needSep = true;
321                         pw.println("  Bad processes:");
322                         printed = true;
323                     }
324                     final BadProcessInfo info = uids.valueAt(i);
325                     pw.print("    Bad process "); pw.print(pname);
326                     pw.print(" uid "); pw.print(puid);
327                     pw.print(": crashed at time "); pw.println(info.time);
328                     if (info.shortMsg != null) {
329                         pw.print("      Short msg: "); pw.println(info.shortMsg);
330                     }
331                     if (info.longMsg != null) {
332                         pw.print("      Long msg: "); pw.println(info.longMsg);
333                     }
334                     if (info.stack != null) {
335                         pw.println("      Stack:");
336                         int lastPos = 0;
337                         for (int pos = 0; pos < info.stack.length(); pos++) {
338                             if (info.stack.charAt(pos) == '\n') {
339                                 pw.print("        ");
340                                 pw.write(info.stack, lastPos, pos - lastPos);
341                                 pw.println();
342                                 lastPos = pos + 1;
343                             }
344                         }
345                         if (lastPos < info.stack.length()) {
346                             pw.print("        ");
347                             pw.write(info.stack, lastPos, info.stack.length() - lastPos);
348                             pw.println();
349                         }
350                     }
351                 }
352             }
353         }
354         return needSep;
355     }
356 
isBadProcess(final String processName, final int uid)357     boolean isBadProcess(final String processName, final int uid) {
358         // NO LOCKING for the simple lookup
359         return mBadProcesses.get(processName, uid) != null;
360     }
361 
clearBadProcess(final String processName, final int uid)362     void clearBadProcess(final String processName, final int uid) {
363         synchronized (mBadProcessLock) {
364             final ProcessMap<BadProcessInfo> badProcesses = new ProcessMap<>();
365             badProcesses.putAll(mBadProcesses);
366             badProcesses.remove(processName, uid);
367             mBadProcesses = badProcesses;
368         }
369     }
370 
markBadProcess(final String processName, final int uid, BadProcessInfo info)371     void markBadProcess(final String processName, final int uid, BadProcessInfo info) {
372         synchronized (mBadProcessLock) {
373             final ProcessMap<BadProcessInfo> badProcesses = new ProcessMap<>();
374             badProcesses.putAll(mBadProcesses);
375             badProcesses.put(processName, uid, info);
376             mBadProcesses = badProcesses;
377         }
378     }
379 
resetProcessCrashTime(final String processName, final int uid)380     void resetProcessCrashTime(final String processName, final int uid) {
381         synchronized (mBadProcessLock) {
382             mProcessCrashTimes.remove(processName, uid);
383             mProcessCrashCounts.remove(processName, uid);
384         }
385     }
386 
resetProcessCrashTime(boolean resetEntireUser, int appId, int userId)387     void resetProcessCrashTime(boolean resetEntireUser, int appId, int userId) {
388         synchronized (mBadProcessLock) {
389             final ArrayMap<String, SparseArray<Long>> pTimeMap = mProcessCrashTimes.getMap();
390             for (int ip = pTimeMap.size() - 1; ip >= 0; ip--) {
391                 SparseArray<Long> ba = pTimeMap.valueAt(ip);
392                 resetProcessCrashMapLBp(ba, resetEntireUser, appId, userId);
393                 if (ba.size() == 0) {
394                     pTimeMap.removeAt(ip);
395                 }
396             }
397 
398             final ArrayMap<String, SparseArray<Pair<Long, Integer>>> pCountMap =
399                     mProcessCrashCounts.getMap();
400             for (int ip = pCountMap.size() - 1; ip >= 0; ip--) {
401                 SparseArray<Pair<Long, Integer>> ba = pCountMap.valueAt(ip);
402                 resetProcessCrashMapLBp(ba, resetEntireUser, appId, userId);
403                 if (ba.size() == 0) {
404                     pCountMap.removeAt(ip);
405                 }
406             }
407         }
408     }
409 
410     @GuardedBy("mBadProcessLock")
resetProcessCrashMapLBp(SparseArray<?> ba, boolean resetEntireUser, int appId, int userId)411     private void resetProcessCrashMapLBp(SparseArray<?> ba, boolean resetEntireUser,
412             int appId, int userId) {
413         for (int i = ba.size() - 1; i >= 0; i--) {
414             boolean remove = false;
415             final int entUid = ba.keyAt(i);
416             if (!resetEntireUser) {
417                 if (userId == UserHandle.USER_ALL) {
418                     if (UserHandle.getAppId(entUid) == appId) {
419                         remove = true;
420                     }
421                 } else {
422                     if (entUid == UserHandle.getUid(userId, appId)) {
423                         remove = true;
424                     }
425                 }
426             } else if (UserHandle.getUserId(entUid) == userId) {
427                 remove = true;
428             }
429             if (remove) {
430                 ba.removeAt(i);
431             }
432         }
433     }
434 
loadAppsNotReportingCrashesFromConfig(String appsNotReportingCrashesConfig)435     void loadAppsNotReportingCrashesFromConfig(String appsNotReportingCrashesConfig) {
436         if (appsNotReportingCrashesConfig != null) {
437             final String[] split = appsNotReportingCrashesConfig.split(",");
438             if (split.length > 0) {
439                 synchronized (mBadProcessLock) {
440                     mAppsNotReportingCrashes = new ArraySet<>();
441                     Collections.addAll(mAppsNotReportingCrashes, split);
442                 }
443             }
444         }
445     }
446 
447     @GuardedBy("mService")
killAppAtUserRequestLocked(ProcessRecord app)448     void killAppAtUserRequestLocked(ProcessRecord app) {
449         ErrorDialogController controller = app.mErrorState.getDialogController();
450 
451         int reasonCode = ApplicationExitInfo.REASON_ANR;
452         int subReason = ApplicationExitInfo.SUBREASON_UNKNOWN;
453         synchronized (mProcLock) {
454             if (controller.hasDebugWaitingDialog()) {
455                 reasonCode = ApplicationExitInfo.REASON_OTHER;
456                 subReason = ApplicationExitInfo.SUBREASON_WAIT_FOR_DEBUGGER;
457             }
458             controller.clearAllErrorDialogs();
459             killAppImmediateLSP(app, reasonCode, subReason,
460                     "user-terminated", "user request after error");
461         }
462     }
463 
464     @GuardedBy({"mService", "mProcLock"})
killAppImmediateLSP(ProcessRecord app, int reasonCode, int subReason, String reason, String killReason)465     private void killAppImmediateLSP(ProcessRecord app, int reasonCode, int subReason,
466             String reason, String killReason) {
467         final ProcessErrorStateRecord errState = app.mErrorState;
468         errState.setCrashing(false);
469         errState.setCrashingReport(null);
470         errState.setNotResponding(false);
471         errState.setNotRespondingReport(null);
472         final int pid = errState.mApp.getPid();
473         if (pid > 0 && pid != MY_PID) {
474             synchronized (mBadProcessLock) {
475                 handleAppCrashLSPB(app, reason,
476                         null /*shortMsg*/, null /*longMsg*/, null /*stackTrace*/, null /*data*/);
477             }
478             app.killLocked(killReason, reasonCode, subReason, true);
479         }
480     }
481 
482     /**
483      * Induce a crash in the given app.
484      *
485      * @param uid if nonnegative, the required matching uid of the target to crash
486      * @param initialPid fast-path match for the target to crash
487      * @param packageName fallback match if the stated pid is not found or doesn't match uid
488      * @param userId If nonnegative, required to identify a match by package name
489      * @param message
490      */
scheduleAppCrashLocked(int uid, int initialPid, String packageName, int userId, String message, boolean force, int exceptionTypeId)491     void scheduleAppCrashLocked(int uid, int initialPid, String packageName, int userId,
492             String message, boolean force, int exceptionTypeId) {
493         ProcessRecord proc = null;
494 
495         // Figure out which process to kill.  We don't trust that initialPid
496         // still has any relation to current pids, so must scan through the
497         // list.
498 
499         synchronized (mService.mPidsSelfLocked) {
500             for (int i=0; i<mService.mPidsSelfLocked.size(); i++) {
501                 ProcessRecord p = mService.mPidsSelfLocked.valueAt(i);
502                 if (uid >= 0 && p.uid != uid) {
503                     continue;
504                 }
505                 if (p.getPid() == initialPid) {
506                     proc = p;
507                     break;
508                 }
509                 if (p.getPkgList().containsKey(packageName)
510                         && (userId < 0 || p.userId == userId)) {
511                     proc = p;
512                 }
513             }
514         }
515 
516         if (proc == null) {
517             Slog.w(TAG, "crashApplication: nothing for uid=" + uid
518                     + " initialPid=" + initialPid
519                     + " packageName=" + packageName
520                     + " userId=" + userId);
521             return;
522         }
523 
524         proc.scheduleCrashLocked(message, exceptionTypeId);
525         if (force) {
526             // If the app is responsive, the scheduled crash will happen as expected
527             // and then the delayed summary kill will be a no-op.
528             final ProcessRecord p = proc;
529             mService.mHandler.postDelayed(
530                     () -> {
531                         synchronized (mService) {
532                             synchronized (mProcLock) {
533                                 killAppImmediateLSP(p, ApplicationExitInfo.REASON_OTHER,
534                                         ApplicationExitInfo.SUBREASON_INVALID_STATE,
535                                         "forced", "killed for invalid state");
536                             }
537                         }
538                     },
539                     5000L);
540         }
541     }
542 
543     /**
544      * Bring up the "unexpected error" dialog box for a crashing app.
545      * Deal with edge cases (intercepts from instrumented applications,
546      * ActivityController, error intent receivers, that sort of thing).
547      * @param r the application crashing
548      * @param crashInfo describing the failure
549      */
crashApplication(ProcessRecord r, ApplicationErrorReport.CrashInfo crashInfo)550     void crashApplication(ProcessRecord r, ApplicationErrorReport.CrashInfo crashInfo) {
551         final int callingPid = Binder.getCallingPid();
552         final int callingUid = Binder.getCallingUid();
553 
554         final long origId = Binder.clearCallingIdentity();
555         try {
556             crashApplicationInner(r, crashInfo, callingPid, callingUid);
557         } finally {
558             Binder.restoreCallingIdentity(origId);
559         }
560     }
561 
crashApplicationInner(ProcessRecord r, ApplicationErrorReport.CrashInfo crashInfo, int callingPid, int callingUid)562     private void crashApplicationInner(ProcessRecord r, ApplicationErrorReport.CrashInfo crashInfo,
563             int callingPid, int callingUid) {
564         long timeMillis = System.currentTimeMillis();
565         String shortMsg = crashInfo.exceptionClassName;
566         String longMsg = crashInfo.exceptionMessage;
567         String stackTrace = crashInfo.stackTrace;
568         if (shortMsg != null && longMsg != null) {
569             longMsg = shortMsg + ": " + longMsg;
570         } else if (shortMsg != null) {
571             longMsg = shortMsg;
572         }
573 
574         if (r != null) {
575             mPackageWatchdog.onPackageFailure(r.getPackageListWithVersionCode(),
576                     PackageWatchdog.FAILURE_REASON_APP_CRASH);
577 
578             mService.mProcessList.noteAppKill(r, (crashInfo != null
579                       && "Native crash".equals(crashInfo.exceptionClassName))
580                       ? ApplicationExitInfo.REASON_CRASH_NATIVE
581                       : ApplicationExitInfo.REASON_CRASH,
582                       ApplicationExitInfo.SUBREASON_UNKNOWN,
583                     "crash");
584         }
585 
586         final int relaunchReason = r != null
587                 ? r.getWindowProcessController().computeRelaunchReason() : RELAUNCH_REASON_NONE;
588 
589         AppErrorResult result = new AppErrorResult();
590         int taskId;
591         synchronized (mService) {
592             /**
593              * If crash is handled by instance of {@link android.app.IActivityController},
594              * finish now and don't show the app error dialog.
595              */
596             if (handleAppCrashInActivityController(r, crashInfo, shortMsg, longMsg, stackTrace,
597                     timeMillis, callingPid, callingUid)) {
598                 return;
599             }
600 
601             // Suppress crash dialog if the process is being relaunched due to a crash during a free
602             // resize.
603             if (relaunchReason == RELAUNCH_REASON_FREE_RESIZE) {
604                 return;
605             }
606 
607             /**
608              * If this process was running instrumentation, finish now - it will be handled in
609              * {@link ActivityManagerService#handleAppDiedLocked}.
610              */
611             if (r != null && r.getActiveInstrumentation() != null) {
612                 return;
613             }
614 
615             // Log crash in battery stats.
616             if (r != null) {
617                 mService.mBatteryStatsService.noteProcessCrash(r.processName, r.uid);
618             }
619 
620             AppErrorDialog.Data data = new AppErrorDialog.Data();
621             data.result = result;
622             data.proc = r;
623 
624             // If we can't identify the process or it's already exceeded its crash quota,
625             // quit right away without showing a crash dialog.
626             if (r == null || !makeAppCrashingLocked(r, shortMsg, longMsg, stackTrace, data)) {
627                 return;
628             }
629 
630             final Message msg = Message.obtain();
631             msg.what = ActivityManagerService.SHOW_ERROR_UI_MSG;
632 
633             taskId = data.taskId;
634             msg.obj = data;
635             mService.mUiHandler.sendMessage(msg);
636         }
637 
638         int res = result.get();
639 
640         Intent appErrorIntent = null;
641         MetricsLogger.action(mContext, MetricsProto.MetricsEvent.ACTION_APP_CRASH, res);
642         if (res == AppErrorDialog.TIMEOUT || res == AppErrorDialog.CANCEL) {
643             res = AppErrorDialog.FORCE_QUIT;
644         }
645         switch (res) {
646             case AppErrorDialog.MUTE:
647                 synchronized (mBadProcessLock) {
648                     stopReportingCrashesLBp(r);
649                 }
650                 break;
651             case AppErrorDialog.RESTART:
652                 synchronized (mService) {
653                     mService.mProcessList.removeProcessLocked(r, false, true,
654                             ApplicationExitInfo.REASON_CRASH, "crash");
655                 }
656                 if (taskId != INVALID_TASK_ID) {
657                     try {
658                         mService.startActivityFromRecents(taskId,
659                                 ActivityOptions.makeBasic().toBundle());
660                     } catch (IllegalArgumentException e) {
661                         // Hmm...that didn't work. Task should either be in recents or associated
662                         // with a stack.
663                         Slog.e(TAG, "Could not restart taskId=" + taskId, e);
664                     }
665                 }
666                 break;
667             case AppErrorDialog.FORCE_QUIT:
668                 final long orig = Binder.clearCallingIdentity();
669                 try {
670                     // Kill it with fire!
671                     mService.mAtmInternal.onHandleAppCrash(r.getWindowProcessController());
672                     if (!r.isPersistent()) {
673                         synchronized (mService) {
674                             mService.mProcessList.removeProcessLocked(r, false, false,
675                                     ApplicationExitInfo.REASON_CRASH, "crash");
676                         }
677                         mService.mAtmInternal.resumeTopActivities(false /* scheduleIdle */);
678                     }
679                 } finally {
680                     Binder.restoreCallingIdentity(orig);
681                 }
682                 break;
683             case AppErrorDialog.APP_INFO:
684                 appErrorIntent = new Intent(Settings.ACTION_APPLICATION_DETAILS_SETTINGS);
685                 appErrorIntent.setData(Uri.parse("package:" + r.info.packageName));
686                 appErrorIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
687                 break;
688             case AppErrorDialog.FORCE_QUIT_AND_REPORT:
689                 synchronized (mProcLock) {
690                     appErrorIntent = createAppErrorIntentLOSP(r, timeMillis, crashInfo);
691                 }
692                 break;
693         }
694 
695         if (appErrorIntent != null) {
696             try {
697                 mContext.startActivityAsUser(appErrorIntent, new UserHandle(r.userId));
698             } catch (ActivityNotFoundException e) {
699                 Slog.w(TAG, "bug report receiver dissappeared", e);
700             }
701         }
702     }
703 
704     @GuardedBy("mService")
handleAppCrashInActivityController(ProcessRecord r, ApplicationErrorReport.CrashInfo crashInfo, String shortMsg, String longMsg, String stackTrace, long timeMillis, int callingPid, int callingUid)705     private boolean handleAppCrashInActivityController(ProcessRecord r,
706                                                        ApplicationErrorReport.CrashInfo crashInfo,
707                                                        String shortMsg, String longMsg,
708                                                        String stackTrace, long timeMillis,
709                                                        int callingPid, int callingUid) {
710         String name = r != null ? r.processName : null;
711         int pid = r != null ? r.getPid() : callingPid;
712         int uid = r != null ? r.info.uid : callingUid;
713 
714         return mService.mAtmInternal.handleAppCrashInActivityController(
715                 name, pid, shortMsg, longMsg, timeMillis, crashInfo.stackTrace, () -> {
716                 if (Build.IS_DEBUGGABLE
717                         && "Native crash".equals(crashInfo.exceptionClassName)) {
718                     Slog.w(TAG, "Skip killing native crashed app " + name
719                             + "(" + pid + ") during testing");
720                 } else {
721                     Slog.w(TAG, "Force-killing crashed app " + name + " at watcher's request");
722                     if (r != null) {
723                         if (!makeAppCrashingLocked(r, shortMsg, longMsg, stackTrace, null)) {
724                             r.killLocked("crash", ApplicationExitInfo.REASON_CRASH, true);
725                         }
726                     } else {
727                         // Huh.
728                         Process.killProcess(pid);
729                         ProcessList.killProcessGroup(uid, pid);
730                         mService.mProcessList.noteAppKill(pid, uid,
731                                 ApplicationExitInfo.REASON_CRASH,
732                                 ApplicationExitInfo.SUBREASON_UNKNOWN,
733                                 "crash");
734                     }
735                 }
736         });
737     }
738 
739     @GuardedBy("mService")
740     private boolean makeAppCrashingLocked(ProcessRecord app,
741             String shortMsg, String longMsg, String stackTrace, AppErrorDialog.Data data) {
742         synchronized (mProcLock) {
743             final ProcessErrorStateRecord errState = app.mErrorState;
744             errState.setCrashing(true);
745             errState.setCrashingReport(generateProcessError(app,
746                     ActivityManager.ProcessErrorStateInfo.CRASHED,
747                     null, shortMsg, longMsg, stackTrace));
748             errState.startAppProblemLSP();
749             app.getWindowProcessController().stopFreezingActivities();
750             synchronized (mBadProcessLock) {
751                 return handleAppCrashLSPB(app, "force-crash" /*reason*/, shortMsg, longMsg,
752                         stackTrace, data);
753             }
754         }
755     }
756 
757     /**
758      * Generate a process error record, suitable for attachment to a ProcessRecord.
759      *
760      * @param app The ProcessRecord in which the error occurred.
761      * @param condition Crashing, Application Not Responding, etc.  Values are defined in
762      *                      ActivityManager.ProcessErrorStateInfo
763      * @param activity The activity associated with the crash, if known.
764      * @param shortMsg Short message describing the crash.
765      * @param longMsg Long message describing the crash.
766      * @param stackTrace Full crash stack trace, may be null.
767      *
768      * @return Returns a fully-formed ProcessErrorStateInfo record.
769      */
770     ActivityManager.ProcessErrorStateInfo generateProcessError(ProcessRecord app,
771             int condition, String activity, String shortMsg, String longMsg, String stackTrace) {
772         ActivityManager.ProcessErrorStateInfo report = new ActivityManager.ProcessErrorStateInfo();
773 
774         report.condition = condition;
775         report.processName = app.processName;
776         report.pid = app.getPid();
777         report.uid = app.info.uid;
778         report.tag = activity;
779         report.shortMsg = shortMsg;
780         report.longMsg = longMsg;
781         report.stackTrace = stackTrace;
782 
783         return report;
784     }
785 
786     @GuardedBy(anyOf = {"mService", "mProcLock"})
787     Intent createAppErrorIntentLOSP(ProcessRecord r,
788             long timeMillis, ApplicationErrorReport.CrashInfo crashInfo) {
789         ApplicationErrorReport report = createAppErrorReportLOSP(r, timeMillis, crashInfo);
790         if (report == null) {
791             return null;
792         }
793         Intent result = new Intent(Intent.ACTION_APP_ERROR);
794         result.setComponent(r.mErrorState.getErrorReportReceiver());
795         result.putExtra(Intent.EXTRA_BUG_REPORT, report);
796         result.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
797         return result;
798     }
799 
800     @GuardedBy(anyOf = {"mService", "mProcLock"})
801     private ApplicationErrorReport createAppErrorReportLOSP(ProcessRecord r,
802             long timeMillis, ApplicationErrorReport.CrashInfo crashInfo) {
803         final ProcessErrorStateRecord errState = r.mErrorState;
804         if (errState.getErrorReportReceiver() == null) {
805             return null;
806         }
807 
808         if (!errState.isCrashing() && !errState.isNotResponding()
809                 && !errState.isForceCrashReport()) {
810             return null;
811         }
812 
813         ApplicationErrorReport report = new ApplicationErrorReport();
814         report.packageName = r.info.packageName;
815         report.installerPackageName = errState.getErrorReportReceiver().getPackageName();
816         report.processName = r.processName;
817         report.time = timeMillis;
818         report.systemApp = (r.info.flags & FLAG_SYSTEM) != 0;
819 
820         if (errState.isCrashing() || errState.isForceCrashReport()) {
821             report.type = ApplicationErrorReport.TYPE_CRASH;
822             report.crashInfo = crashInfo;
823         } else if (errState.isNotResponding()) {
824             report.type = ApplicationErrorReport.TYPE_ANR;
825             report.anrInfo = new ApplicationErrorReport.AnrInfo();
826 
827             report.anrInfo.activity = errState.getNotRespondingReport().tag;
828             report.anrInfo.cause = errState.getNotRespondingReport().shortMsg;
829             report.anrInfo.info = errState.getNotRespondingReport().longMsg;
830         }
831 
832         return report;
833     }
834 
835     @GuardedBy({"mService", "mProcLock", "mBadProcessLock"})
836     private boolean handleAppCrashLSPB(ProcessRecord app, String reason,
837             String shortMsg, String longMsg, String stackTrace, AppErrorDialog.Data data) {
838         final long now = SystemClock.uptimeMillis();
839         final boolean showBackground = Settings.Secure.getIntForUser(mContext.getContentResolver(),
840                 Settings.Secure.ANR_SHOW_BACKGROUND, 0,
841                 mService.mUserController.getCurrentUserId()) != 0;
842 
843         Long crashTime;
844         Long crashTimePersistent;
845         final String processName = app.processName;
846         final int uid = app.uid;
847         final int userId = app.userId;
848         final boolean isolated = app.isolated;
849         final boolean persistent = app.isPersistent();
850         final WindowProcessController proc = app.getWindowProcessController();
851         final ProcessErrorStateRecord errState = app.mErrorState;
852 
853         if (!app.isolated) {
854             crashTime = mProcessCrashTimes.get(processName, uid);
855             crashTimePersistent = mProcessCrashTimesPersistent.get(processName, uid);
856         } else {
857             crashTime = crashTimePersistent = null;
858         }
859 
860         // Bump up the crash count of any services currently running in the proc.
861         boolean tryAgain = app.mServices.incServiceCrashCountLocked(now);
862 
863         final boolean quickCrash = crashTime != null
864                 && now < crashTime + ActivityManagerConstants.MIN_CRASH_INTERVAL;
865         if (quickCrash || isProcOverCrashLimitLBp(app, now)) {
866             // The process either crashed again very quickly or has been crashing periodically in
867             // the last few hours. If it was a bound foreground service, let's try to restart again
868             // in a while, otherwise the process loses!
869             Slog.w(TAG, "Process " + processName + " has crashed too many times, killing!"
870                     + " Reason: " + (quickCrash ? "crashed quickly" : "over process crash limit"));
871             EventLog.writeEvent(EventLogTags.AM_PROCESS_CRASHED_TOO_MUCH,
872                     userId, processName, uid);
873             mService.mAtmInternal.onHandleAppCrash(proc);
874             if (!persistent) {
875                 // We don't want to start this process again until the user
876                 // explicitly does so...  but for persistent process, we really
877                 // need to keep it running.  If a persistent process is actually
878                 // repeatedly crashing, then badness for everyone.
879                 EventLog.writeEvent(EventLogTags.AM_PROC_BAD, userId, uid,
880                         processName);
881                 if (!isolated) {
882                     // XXX We don't have a way to mark isolated processes
883                     // as bad, since they don't have a persistent identity.
884                     markBadProcess(processName, app.uid,
885                             new BadProcessInfo(now, shortMsg, longMsg, stackTrace));
886                     mProcessCrashTimes.remove(processName, app.uid);
887                     mProcessCrashCounts.remove(processName, app.uid);
888                 }
889                 errState.setBad(true);
890                 app.setRemoved(true);
891                 final AppStandbyInternal appStandbyInternal =
892                         LocalServices.getService(AppStandbyInternal.class);
893                 if (appStandbyInternal != null) {
894                     appStandbyInternal.restrictApp(
895                             // Sometimes the processName is the same as the package name, so use
896                             // that if we don't have the ApplicationInfo object.
897                             // AppStandbyController will just return if it can't find the app.
898                             app.info != null ? app.info.packageName : processName,
899                             userId, UsageStatsManager.REASON_SUB_FORCED_SYSTEM_FLAG_BUGGY);
900                 }
901                 // Don't let services in this process be restarted and potentially
902                 // annoy the user repeatedly.  Unless it is persistent, since those
903                 // processes run critical code.
904                 mService.mProcessList.removeProcessLocked(app, false, tryAgain,
905                         ApplicationExitInfo.REASON_CRASH, "crash");
906                 mService.mAtmInternal.resumeTopActivities(false /* scheduleIdle */);
907                 if (!showBackground) {
908                     return false;
909                 }
910             }
911             mService.mAtmInternal.resumeTopActivities(false /* scheduleIdle */);
912         } else {
913             final int affectedTaskId = mService.mAtmInternal.finishTopCrashedActivities(
914                             proc, reason);
915             if (data != null) {
916                 data.taskId = affectedTaskId;
917             }
918             if (data != null && crashTimePersistent != null
919                     && now < crashTimePersistent + ActivityManagerConstants.MIN_CRASH_INTERVAL) {
920                 data.repeating = true;
921             }
922         }
923 
924         if (data != null && tryAgain) {
925             data.isRestartableForService = true;
926         }
927 
928         // If the crashing process is what we consider to be the "home process" and it has been
929         // replaced by a third-party app, clear the package preferred activities from packages
930         // with a home activity running in the process to prevent a repeatedly crashing app
931         // from blocking the user to manually clear the list.
932         if (proc.isHomeProcess() && proc.hasActivities() && (app.info.flags & FLAG_SYSTEM) == 0) {
933             proc.clearPackagePreferredForHomeActivities();
934         }
935 
936         if (!isolated) {
937             // XXX Can't keep track of crash times for isolated processes,
938             // because they don't have a persistent identity.
939             mProcessCrashTimes.put(processName, uid, now);
940             mProcessCrashTimesPersistent.put(processName, uid, now);
941             updateProcessCrashCountLBp(processName, uid, now);
942         }
943 
944         if (errState.getCrashHandler() != null) {
945             mService.mHandler.post(errState.getCrashHandler());
946         }
947         return true;
948     }
949 
950     @GuardedBy("mBadProcessLock")
951     private void updateProcessCrashCountLBp(String processName, int uid, long now) {
952         Pair<Long, Integer> count = mProcessCrashCounts.get(processName, uid);
953         if (count == null || (count.first + PROCESS_CRASH_COUNT_RESET_INTERVAL) < now) {
954             count = new Pair<>(now, 1);
955         } else {
956             count = new Pair<>(count.first, count.second + 1);
957         }
958         mProcessCrashCounts.put(processName, uid, count);
959     }
960 
961     @GuardedBy("mBadProcessLock")
962     private boolean isProcOverCrashLimitLBp(ProcessRecord app, long now) {
963         final Pair<Long, Integer> crashCount = mProcessCrashCounts.get(app.processName, app.uid);
964         return !app.isolated && crashCount != null
965                 && now < (crashCount.first + PROCESS_CRASH_COUNT_RESET_INTERVAL)
966                 && crashCount.second >= PROCESS_CRASH_COUNT_LIMIT;
967     }
968 
969     void handleShowAppErrorUi(Message msg) {
970         AppErrorDialog.Data data = (AppErrorDialog.Data) msg.obj;
971         boolean showBackground = Settings.Secure.getIntForUser(mContext.getContentResolver(),
972                 Settings.Secure.ANR_SHOW_BACKGROUND, 0,
973                 mService.mUserController.getCurrentUserId()) != 0;
974 
975         final int userId;
976         synchronized (mProcLock) {
977             final ProcessRecord proc = data.proc;
978             final AppErrorResult res = data.result;
979             if (proc == null) {
980                 Slog.e(TAG, "handleShowAppErrorUi: proc is null");
981                 return;
982             }
983             final ProcessErrorStateRecord errState = proc.mErrorState;
984             userId = proc.userId;
985             if (errState.getDialogController().hasCrashDialogs()) {
986                 Slog.e(TAG, "App already has crash dialog: " + proc);
987                 if (res != null) {
988                     res.set(AppErrorDialog.ALREADY_SHOWING);
989                 }
990                 return;
991             }
992             boolean isBackground = (UserHandle.getAppId(proc.uid)
993                     >= Process.FIRST_APPLICATION_UID
994                     && proc.getPid() != MY_PID);
995             for (int profileId : mService.mUserController.getCurrentProfileIds()) {
996                 isBackground &= (userId != profileId);
997             }
998             if (isBackground && !showBackground) {
999                 Slog.w(TAG, "Skipping crash dialog of " + proc + ": background");
1000                 if (res != null) {
1001                     res.set(AppErrorDialog.BACKGROUND_USER);
1002                 }
1003                 return;
1004             }
1005             Long crashShowErrorTime = null;
1006             synchronized (mBadProcessLock) {
1007                 if (!proc.isolated) {
1008                     crashShowErrorTime = mProcessCrashShowDialogTimes.get(proc.processName,
1009                             proc.uid);
1010                 }
1011                 final boolean showFirstCrash = Settings.Global.getInt(
1012                         mContext.getContentResolver(),
1013                         Settings.Global.SHOW_FIRST_CRASH_DIALOG, 0) != 0;
1014                 final boolean showFirstCrashDevOption = Settings.Secure.getIntForUser(
1015                         mContext.getContentResolver(),
1016                         Settings.Secure.SHOW_FIRST_CRASH_DIALOG_DEV_OPTION,
1017                         0,
1018                         mService.mUserController.getCurrentUserId()) != 0;
1019                 final boolean crashSilenced = mAppsNotReportingCrashes != null
1020                         && mAppsNotReportingCrashes.contains(proc.info.packageName);
1021                 final long now = SystemClock.uptimeMillis();
1022                 final boolean shouldThottle = crashShowErrorTime != null
1023                         && now < crashShowErrorTime + ActivityManagerConstants.MIN_CRASH_INTERVAL;
1024                 if ((mService.mAtmInternal.canShowErrorDialogs() || showBackground)
1025                         && !crashSilenced && !shouldThottle
1026                         && (showFirstCrash || showFirstCrashDevOption || data.repeating)) {
1027                     errState.getDialogController().showCrashDialogs(data);
1028                     if (!proc.isolated) {
1029                         mProcessCrashShowDialogTimes.put(proc.processName, proc.uid, now);
1030                     }
1031                 } else {
1032                     // The device is asleep, so just pretend that the user
1033                     // saw a crash dialog and hit "force quit".
1034                     if (res != null) {
1035                         res.set(AppErrorDialog.CANT_SHOW);
1036                     }
1037                 }
1038             }
1039         }
1040     }
1041 
1042     @GuardedBy("mBadProcessLock")
1043     private void stopReportingCrashesLBp(ProcessRecord proc) {
1044         if (mAppsNotReportingCrashes == null) {
1045             mAppsNotReportingCrashes = new ArraySet<>();
1046         }
1047         mAppsNotReportingCrashes.add(proc.info.packageName);
1048     }
1049 
1050     void handleShowAnrUi(Message msg) {
1051         List<VersionedPackage> packageList = null;
1052         boolean doKill = false;
1053         AppNotRespondingDialog.Data data = (AppNotRespondingDialog.Data) msg.obj;
1054         final ProcessRecord proc = data.proc;
1055         if (proc == null) {
1056             Slog.e(TAG, "handleShowAnrUi: proc is null");
1057             return;
1058         }
1059         synchronized (mProcLock) {
1060             final ProcessErrorStateRecord errState = proc.mErrorState;
1061             if (!proc.isPersistent()) {
1062                 packageList = proc.getPackageListWithVersionCode();
1063             }
1064             if (errState.getDialogController().hasAnrDialogs()) {
1065                 Slog.e(TAG, "App already has anr dialog: " + proc);
1066                 MetricsLogger.action(mContext, MetricsProto.MetricsEvent.ACTION_APP_ANR,
1067                         AppNotRespondingDialog.ALREADY_SHOWING);
1068                 return;
1069             }
1070 
1071             boolean showBackground = Settings.Secure.getIntForUser(mContext.getContentResolver(),
1072                     Settings.Secure.ANR_SHOW_BACKGROUND, 0,
1073                     mService.mUserController.getCurrentUserId()) != 0;
1074             if (mService.mAtmInternal.canShowErrorDialogs() || showBackground) {
1075                 AnrController anrController = errState.getDialogController().getAnrController();
1076                 if (anrController == null) {
1077                     errState.getDialogController().showAnrDialogs(data);
1078                 } else {
1079                     String packageName = proc.info.packageName;
1080                     int uid = proc.info.uid;
1081                     boolean showDialog = anrController.onAnrDelayCompleted(packageName, uid);
1082 
1083                     if (showDialog) {
1084                         Slog.d(TAG, "ANR delay completed. Showing ANR dialog for package: "
1085                                 + packageName);
1086                         errState.getDialogController().showAnrDialogs(data);
1087                     } else {
1088                         Slog.d(TAG, "ANR delay completed. Cancelling ANR dialog for package: "
1089                                 + packageName);
1090                         errState.setNotResponding(false);
1091                         errState.setNotRespondingReport(null);
1092                         errState.getDialogController().clearAnrDialogs();
1093                     }
1094                 }
1095             } else {
1096                 MetricsLogger.action(mContext, MetricsProto.MetricsEvent.ACTION_APP_ANR,
1097                         AppNotRespondingDialog.CANT_SHOW);
1098                 // Just kill the app if there is no dialog to be shown.
1099                 doKill = true;
1100             }
1101         }
1102         if (doKill) {
1103             mService.killAppAtUsersRequest(proc);
1104         }
1105         // Notify PackageWatchdog without the lock held
1106         if (packageList != null) {
1107             mPackageWatchdog.onPackageFailure(packageList,
1108                     PackageWatchdog.FAILURE_REASON_APP_NOT_RESPONDING);
1109         }
1110     }
1111 
1112     /**
1113      * Information about a process that is currently marked as bad.
1114      */
1115     static final class BadProcessInfo {
1116         BadProcessInfo(long time, String shortMsg, String longMsg, String stack) {
1117             this.time = time;
1118             this.shortMsg = shortMsg;
1119             this.longMsg = longMsg;
1120             this.stack = stack;
1121         }
1122 
1123         final long time;
1124         final String shortMsg;
1125         final String longMsg;
1126         final String stack;
1127     }
1128 
1129 }
1130