diff options
| author | 2016-01-26 17:42:09 +0000 | |
|---|---|---|
| committer | 2016-01-26 17:42:09 +0000 | |
| commit | ebefdd3f7ca7dc7cf0cb3b8d4afa7226c2072151 (patch) | |
| tree | b24c42d92b8cf267fcecd6205e87a587d701b74e | |
| parent | 5407f1e6523c947d5491f8662ed6ba2d5e69daeb (diff) | |
| parent | 20d7df3c3ff0000678a208b25fcf7ddf90c5abe4 (diff) | |
Merge "Crash dialog improvements, move crash code to AppErrors"
14 files changed, 1235 insertions, 910 deletions
diff --git a/core/res/res/layout/app_error_dialog.xml b/core/res/res/layout/app_error_dialog.xml new file mode 100644 index 000000000000..aaa2dbc389dc --- /dev/null +++ b/core/res/res/layout/app_error_dialog.xml @@ -0,0 +1,70 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- +/* +** +** Copyright 2015, The Android Open Source Project +** +** Licensed under the Apache License, Version 2.0 (the "License"); +** you may not use this file except in compliance with the License. +** You may obtain a copy of the License at +** +** http://www.apache.org/licenses/LICENSE-2.0 +** +** Unless required by applicable law or agreed to in writing, software +** distributed under the License is distributed on an "AS IS" BASIS, +** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +** See the License for the specific language governing permissions and +** limitations under the License. +*/ +--> +<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" + android:layout_width="match_parent" + android:layout_height="wrap_content" + android:orientation="vertical" + android:paddingTop="@dimen/dialog_list_padding_vertical_material" + android:paddingBottom="@dimen/dialog_list_padding_vertical_material" +> + + + <TextView + android:id="@+id/aerr_restart" + android:layout_width="match_parent" + android:layout_height="wrap_content" + android:text="@string/aerr_restart" + style="@style/aerr_list_item" + /> + + <TextView + android:id="@+id/aerr_reset" + android:layout_width="match_parent" + android:layout_height="wrap_content" + android:text="@string/aerr_reset" + style="@style/aerr_list_item" + /> + + <TextView + android:id="@+id/aerr_report" + android:layout_width="match_parent" + android:layout_height="wrap_content" + android:text="@string/aerr_report" + style="@style/aerr_list_item" + /> + + <TextView + android:id="@+id/aerr_close" + android:layout_width="match_parent" + android:layout_height="wrap_content" + android:text="@string/aerr_close" + style="@style/aerr_list_item" + /> + + <TextView + android:id="@+id/aerr_mute" + android:layout_width="match_parent" + android:layout_height="wrap_content" + android:text="@string/aerr_mute" + style="@style/aerr_list_item" + /> + + +</LinearLayout> diff --git a/core/res/res/layout/app_error_dialog_dont_show_again.xml b/core/res/res/layout/app_error_dialog_dont_show_again.xml deleted file mode 100644 index ba79ecd1fa8c..000000000000 --- a/core/res/res/layout/app_error_dialog_dont_show_again.xml +++ /dev/null @@ -1,40 +0,0 @@ -<?xml version="1.0" encoding="utf-8"?> -<!-- -/* -** -** Copyright 2015, The Android Open Source Project -** -** Licensed under the Apache License, Version 2.0 (the "License"); -** you may not use this file except in compliance with the License. -** You may obtain a copy of the License at -** -** http://www.apache.org/licenses/LICENSE-2.0 -** -** Unless required by applicable law or agreed to in writing, software -** distributed under the License is distributed on an "AS IS" BASIS, -** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -** See the License for the specific language governing permissions and -** limitations under the License. -*/ ---> -<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" - android:layout_width="wrap_content" - android:layout_height="wrap_content" - android:paddingStart="14dp" - android:paddingEnd="10dp" - android:gravity="center_vertical" - > - <CheckBox - android:id="@+id/checkbox" - android:layout_width="wrap_content" - android:layout_height="wrap_content" - /> - <TextView - android:id="@+id/text" - android:layout_width="wrap_content" - android:layout_height="wrap_content" - android:layout_marginTop="8dp" - android:layout_marginBottom="8dp" - /> - -</LinearLayout> diff --git a/core/res/res/values/strings.xml b/core/res/res/values/strings.xml index 28c76bb2d38d..2f2c5e447d1e 100644 --- a/core/res/res/values/strings.xml +++ b/core/res/res/values/strings.xml @@ -2588,16 +2588,27 @@ <!-- Text to display when there are no activities found to display in the activity chooser. See the "Select an action" title. --> <string name="noApplications">No apps can perform this action.</string> - <!-- Title of the alert when an application has crashed. --> - <string name="aerr_title"></string> <!-- Text of the alert that is displayed when an application has crashed. --> - <string name="aerr_application">Unfortunately, <xliff:g id="application">%1$s</xliff:g> has stopped.</string> - <!-- Text of the alert that is displayed when an application has crashed. --> - <string name="aerr_process">Unfortunately, the process <xliff:g id="process">%1$s</xliff:g> has - stopped.</string> - <!-- Text of the alert that is displayed when an application has crashed. --> - <string name="aerr_process_silence">Silence crashes from <xliff:g id="process">%1$s</xliff:g> - until reboot.</string> + <string name="aerr_application"><xliff:g id="application">%1$s</xliff:g> has stopped</string> + <!-- Text of the alert that is displayed when a process has crashed. --> + <string name="aerr_process"><xliff:g id="process">%1$s</xliff:g> has + stopped</string> + <!-- Text of the alert that is displayed when an application has crashed repeatedly. --> + <string name="aerr_application_repeated"><xliff:g id="application">%1$s</xliff:g> is repeatedly stopping</string> + <!-- Text of the alert that is displayed when a process has crashed repeatedly. --> + <string name="aerr_process_repeated"><xliff:g id="process">%1$s</xliff:g> is + repeatedly stopping</string> + <!-- Button that restarts a crashed application --> + <string name="aerr_restart">Restart app</string> + <!-- Button that clears cache and restarts a crashed application --> + <string name="aerr_reset">Reset and restart app</string> + <!-- Button that sends feedback about a crashed application --> + <string name="aerr_report">Send feedback</string> + <!-- Button that closes a crashed application --> + <string name="aerr_close">Close</string> + <!-- Button that mutes further crashes of the crashed application--> + <string name="aerr_mute">Mute</string> + <!-- Title of the alert when an application is not responding. --> <string name="anr_title"></string> <!-- Text of the alert that is displayed when an application is not responding. --> diff --git a/core/res/res/values/styles.xml b/core/res/res/values/styles.xml index d5349b2ee9f8..b660277847c4 100644 --- a/core/res/res/values/styles.xml +++ b/core/res/res/values/styles.xml @@ -1397,6 +1397,16 @@ please see styles_device_defaults.xml. <item name="pointerIconGrabbing">@drawable/pointer_grabbing_large_icon</item> </style> + <!-- @hide --> + <style name="aerr_list_item" parent="Widget.Material.Light.TextView"> + <item name="minHeight">?attr/listPreferredItemHeightSmall</item> + <item name="textAppearance">?attr/textAppearanceListItemSmall</item> + <item name="textColor">?attr/textColorAlertDialogListItem</item> + <item name="gravity">center_vertical</item> + <item name="paddingStart">?attr/listPreferredItemPaddingStart</item> + <item name="paddingEnd">?attr/listPreferredItemPaddingEnd</item> + </style> + <!-- Wifi dialog styles --> <!-- @hide --> <style name="wifi_item"> diff --git a/core/res/res/values/symbols.xml b/core/res/res/values/symbols.xml index 57ff243eeab0..1f2c6e1e20d8 100644 --- a/core/res/res/values/symbols.xml +++ b/core/res/res/values/symbols.xml @@ -1763,7 +1763,7 @@ <java-symbol type="layout" name="launch_warning" /> <java-symbol type="layout" name="safe_mode" /> <java-symbol type="layout" name="simple_list_item_2_single_choice" /> - <java-symbol type="layout" name="app_error_dialog_dont_show_again" /> + <java-symbol type="layout" name="app_error_dialog" /> <java-symbol type="plurals" name="wifi_available" /> <java-symbol type="plurals" name="wifi_available_detailed" /> <java-symbol type="string" name="accessibility_binding_label" /> @@ -1777,8 +1777,8 @@ <java-symbol type="string" name="remote_bugreport_progress_notification_message_can_cancel" /> <java-symbol type="string" name="aerr_application" /> <java-symbol type="string" name="aerr_process" /> - <java-symbol type="string" name="aerr_process_silence" /> - <java-symbol type="string" name="aerr_title" /> + <java-symbol type="string" name="aerr_application_repeated" /> + <java-symbol type="string" name="aerr_process_repeated" /> <java-symbol type="string" name="android_upgrading_fstrim" /> <java-symbol type="string" name="android_upgrading_apk" /> <java-symbol type="string" name="android_upgrading_complete" /> @@ -2429,6 +2429,12 @@ <java-symbol type="id" name="work_widget_app_icon" /> <java-symbol type="drawable" name="work_widget_mask_view_background" /> + <java-symbol type="id" name="aerr_report" /> + <java-symbol type="id" name="aerr_reset" /> + <java-symbol type="id" name="aerr_restart" /> + <java-symbol type="id" name="aerr_close" /> + <java-symbol type="id" name="aerr_mute" /> + <!-- Framework-private Material.DayNight styles. --> <java-symbol type="style" name="Theme.Material.DayNight" /> <java-symbol type="style" name="Theme.Material.DayNight.DarkActionBar" /> diff --git a/services/core/java/com/android/server/am/ActiveServices.java b/services/core/java/com/android/server/am/ActiveServices.java index 7ba633878b29..8c0ec78e7a72 100755 --- a/services/core/java/com/android/server/am/ActiveServices.java +++ b/services/core/java/com/android/server/am/ActiveServices.java @@ -2775,7 +2775,7 @@ public final class ActiveServices { } if (anrMessage != null) { - mAm.appNotResponding(proc, null, null, false, anrMessage); + mAm.mAppErrors.appNotResponding(proc, null, null, false, anrMessage); } } diff --git a/services/core/java/com/android/server/am/ActivityManagerService.java b/services/core/java/com/android/server/am/ActivityManagerService.java index b61dafe7348e..2c55ee26cf8f 100644 --- a/services/core/java/com/android/server/am/ActivityManagerService.java +++ b/services/core/java/com/android/server/am/ActivityManagerService.java @@ -277,7 +277,6 @@ import static com.android.internal.util.XmlUtils.readLongAttribute; import static com.android.internal.util.XmlUtils.writeBooleanAttribute; import static com.android.internal.util.XmlUtils.writeIntAttribute; import static com.android.internal.util.XmlUtils.writeLongAttribute; -import static com.android.server.Watchdog.NATIVE_STACKS_OF_INTEREST; import static com.android.server.am.ActivityManagerDebugConfig.DEBUG_ALL; import static com.android.server.am.ActivityManagerDebugConfig.DEBUG_BACKUP; import static com.android.server.am.ActivityManagerDebugConfig.DEBUG_BROADCAST; @@ -392,7 +391,7 @@ public final class ActivityManagerService extends ActivityManagerNative // The flags that are set for all calls we make to the package manager. static final int STOCK_PM_FLAGS = PackageManager.GET_SHARED_LIBRARY_FILES; - private static final String SYSTEM_DEBUGGABLE = "ro.debuggable"; + static final String SYSTEM_DEBUGGABLE = "ro.debuggable"; static final boolean IS_USER_BUILD = "user".equals(Build.TYPE); @@ -598,6 +597,12 @@ public final class ActivityManagerService extends ActivityManagerNative final UserController mUserController; + final AppErrors mAppErrors; + + public boolean canShowErrorDialogs() { + return mShowDialogs && !mSleeping && !mShuttingDown; + } + public class PendingAssistExtras extends Binder implements Runnable { public final ActivityRecord activity; public final Bundle extras; @@ -668,38 +673,6 @@ public final class ActivityManagerService extends ActivityManagerNative ProcessRecord mHeavyWeightProcess = null; /** - * The last time that various processes have crashed. - */ - final ProcessMap<Long> mProcessCrashTimes = new ProcessMap<Long>(); - - /** - * Information about a process that is currently marked as bad. - */ - static final class BadProcessInfo { - BadProcessInfo(long time, String shortMsg, String longMsg, String stack) { - this.time = time; - this.shortMsg = shortMsg; - this.longMsg = longMsg; - this.stack = stack; - } - - final long time; - final String shortMsg; - final String longMsg; - final String stack; - } - - /** - * Set of applications that we consider to be bad, and will reject - * incoming broadcasts from (which the user has no control over). - * Processes are added to this set when they have crashed twice within - * a minimum amount of time; they are removed from it when they are - * later restarted (hopefully due to some user action). The value is the - * time it was added to the list. - */ - final ProcessMap<BadProcessInfo> mBadProcesses = new ProcessMap<BadProcessInfo>(); - - /** * All of the processes we currently have running organized by pid. * The keys are the pid running the application. * @@ -1351,8 +1324,6 @@ public final class ActivityManagerService extends ActivityManagerNative final ArrayList<UidRecord.ChangeItem> mPendingUidChanges = new ArrayList<>(); final ArrayList<UidRecord.ChangeItem> mAvailUidChanges = new ArrayList<>(); - ArraySet<String> mAppsNotReportingCrashes; - /** * Runtime CPU use collection thread. This object's lock is used to * perform synchronization with the thread (notifying it to run). @@ -1511,80 +1482,11 @@ public final class ActivityManagerService extends ActivityManagerNative public void handleMessage(Message msg) { switch (msg.what) { case SHOW_ERROR_UI_MSG: { - HashMap<String, Object> data = (HashMap<String, Object>) msg.obj; - boolean showBackground = Settings.Secure.getInt(mContext.getContentResolver(), - Settings.Secure.ANR_SHOW_BACKGROUND, 0) != 0; - synchronized (ActivityManagerService.this) { - ProcessRecord proc = (ProcessRecord)data.get("app"); - AppErrorResult res = (AppErrorResult) data.get("result"); - if (proc != null && proc.crashDialog != null) { - Slog.e(TAG, "App already has crash dialog: " + proc); - if (res != null) { - res.set(0); - } - return; - } - boolean isBackground = (UserHandle.getAppId(proc.uid) - >= Process.FIRST_APPLICATION_UID - && proc.pid != MY_PID); - for (int userId : mUserController.getCurrentProfileIdsLocked()) { - isBackground &= (proc.userId != userId); - } - if (isBackground && !showBackground) { - Slog.w(TAG, "Skipping crash dialog of " + proc + ": background"); - if (res != null) { - res.set(0); - } - return; - } - final boolean crashSilenced = mAppsNotReportingCrashes != null && - mAppsNotReportingCrashes.contains(proc.info.packageName); - if (mShowDialogs && !mSleeping && !mShuttingDown && !crashSilenced) { - Dialog d = new AppErrorDialog(mContext, - ActivityManagerService.this, res, proc); - d.show(); - proc.crashDialog = d; - } else { - // The device is asleep, so just pretend that the user - // saw a crash dialog and hit "force quit". - if (res != null) { - res.set(0); - } - } - } - + mAppErrors.handleShowAppErrorUi(msg); ensureBootCompleted(); } break; case SHOW_NOT_RESPONDING_UI_MSG: { - synchronized (ActivityManagerService.this) { - HashMap<String, Object> data = (HashMap<String, Object>) msg.obj; - ProcessRecord proc = (ProcessRecord)data.get("app"); - if (proc != null && proc.anrDialog != null) { - Slog.e(TAG, "App already has anr dialog: " + proc); - return; - } - - Intent intent = new Intent("android.intent.action.ANR"); - if (!mProcessesReady) { - intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY - | Intent.FLAG_RECEIVER_FOREGROUND); - } - broadcastIntentLocked(null, null, intent, - null, null, 0, null, null, null, AppOpsManager.OP_NONE, - null, false, false, MY_PID, Process.SYSTEM_UID, 0 /* TODO: Verify */); - - if (mShowDialogs) { - Dialog d = new AppNotRespondingDialog(ActivityManagerService.this, - mContext, proc, (ActivityRecord)data.get("activity"), - msg.arg1 != 0); - d.show(); - proc.anrDialog = d; - } else { - // Just kill the app if there is no dialog to be shown. - killAppAtUsersRequest(proc, null); - } - } - + mAppErrors.handleShowAnrUi(msg); ensureBootCompleted(); } break; case SHOW_STRICT_MODE_VIOLATION_UI_MSG: { @@ -2509,6 +2411,7 @@ public final class ActivityManagerService extends ActivityManagerNative mServices = new ActiveServices(this); mProviderMap = new ProviderMap(this); + mAppErrors = new AppErrors(mContext, this); // TODO: Move creation of battery stats service outside of activity manager service. File dataDir = Environment.getDataDirectory(); @@ -3027,7 +2930,7 @@ public final class ActivityManagerService extends ActivityManagerNative return index; } - private static void killProcessGroup(int uid, int pid) { + static void killProcessGroup(int uid, int pid) { Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, "killProcessGroup"); Process.killProcessGroup(uid, pid); Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER); @@ -3349,7 +3252,7 @@ public final class ActivityManagerService extends ActivityManagerNative if ((intentFlags & Intent.FLAG_FROM_BACKGROUND) != 0) { // If we are in the background, then check to see if this process // is bad. If so, we will just silently fail. - if (mBadProcesses.get(info.processName, info.uid) != null) { + if (mAppErrors.isBadProcessLocked(info)) { if (DEBUG_PROCESSES) Slog.v(TAG, "Bad process: " + info.uid + "/" + info.processName); return null; @@ -3361,12 +3264,12 @@ public final class ActivityManagerService extends ActivityManagerNative // if it had been bad. if (DEBUG_PROCESSES) Slog.v(TAG, "Clearing bad process: " + info.uid + "/" + info.processName); - mProcessCrashTimes.remove(info.processName, info.uid); - if (mBadProcesses.get(info.processName, info.uid) != null) { + mAppErrors.resetProcessCrashTimeLocked(info); + if (mAppErrors.isBadProcessLocked(info)) { EventLog.writeEvent(EventLogTags.AM_PROC_GOOD, UserHandle.getUserId(info.uid), info.uid, info.processName); - mBadProcesses.remove(info.processName, info.uid); + mAppErrors.clearBadProcessLocked(info); if (app != null) { app.bad = false; } @@ -4774,46 +4677,7 @@ public final class ActivityManagerService extends ActivityManagerNative } synchronized(this) { - ProcessRecord proc = null; - - // Figure out which process to kill. We don't trust that initialPid - // still has any relation to current pids, so must scan through the - // list. - synchronized (mPidsSelfLocked) { - for (int i=0; i<mPidsSelfLocked.size(); i++) { - ProcessRecord p = mPidsSelfLocked.valueAt(i); - if (p.uid != uid) { - continue; - } - if (p.pid == initialPid) { - proc = p; - break; - } - if (p.pkgList.containsKey(packageName)) { - proc = p; - } - } - } - - if (proc == null) { - Slog.w(TAG, "crashApplication: nothing for uid=" + uid - + " initialPid=" + initialPid - + " packageName=" + packageName); - return; - } - - if (proc.thread != null) { - if (proc.pid == Process.myPid()) { - Log.w(TAG, "crashApplication: trying to crash self!"); - return; - } - long ident = Binder.clearCallingIdentity(); - try { - proc.thread.scheduleCrash(message); - } catch (RemoteException e) { - } - Binder.restoreCallingIdentity(ident); - } + mAppErrors.scheduleAppCrashLocked(uid, initialPid, packageName, message); } } @@ -5294,169 +5158,6 @@ public final class ActivityManagerService extends ActivityManagerNative } } - final void appNotResponding(ProcessRecord app, ActivityRecord activity, - ActivityRecord parent, boolean aboveSystem, final String annotation) { - ArrayList<Integer> firstPids = new ArrayList<Integer>(5); - SparseArray<Boolean> lastPids = new SparseArray<Boolean>(20); - - if (mController != null) { - try { - // 0 == continue, -1 = kill process immediately - int res = mController.appEarlyNotResponding(app.processName, app.pid, annotation); - if (res < 0 && app.pid != MY_PID) { - app.kill("anr", true); - } - } catch (RemoteException e) { - mController = null; - Watchdog.getInstance().setActivityController(null); - } - } - - long anrTime = SystemClock.uptimeMillis(); - if (MONITOR_CPU_USAGE) { - updateCpuStatsNow(); - } - - synchronized (this) { - // PowerManager.reboot() can block for a long time, so ignore ANRs while shutting down. - if (mShuttingDown) { - Slog.i(TAG, "During shutdown skipping ANR: " + app + " " + annotation); - return; - } else if (app.notResponding) { - Slog.i(TAG, "Skipping duplicate ANR: " + app + " " + annotation); - return; - } else if (app.crashing) { - Slog.i(TAG, "Crashing app skipping ANR: " + app + " " + annotation); - return; - } - - // In case we come through here for the same app before completing - // this one, mark as anring now so we will bail out. - app.notResponding = true; - - // Log the ANR to the event log. - EventLog.writeEvent(EventLogTags.AM_ANR, app.userId, app.pid, - app.processName, app.info.flags, annotation); - - // Dump thread traces as quickly as we can, starting with "interesting" processes. - firstPids.add(app.pid); - - int parentPid = app.pid; - if (parent != null && parent.app != null && parent.app.pid > 0) parentPid = parent.app.pid; - if (parentPid != app.pid) firstPids.add(parentPid); - - if (MY_PID != app.pid && MY_PID != parentPid) firstPids.add(MY_PID); - - for (int i = mLruProcesses.size() - 1; i >= 0; i--) { - ProcessRecord r = mLruProcesses.get(i); - if (r != null && r.thread != null) { - int pid = r.pid; - if (pid > 0 && pid != app.pid && pid != parentPid && pid != MY_PID) { - if (r.persistent) { - firstPids.add(pid); - } else { - lastPids.put(pid, Boolean.TRUE); - } - } - } - } - } - - // Log the ANR to the main log. - StringBuilder info = new StringBuilder(); - info.setLength(0); - info.append("ANR in ").append(app.processName); - if (activity != null && activity.shortComponentName != null) { - info.append(" (").append(activity.shortComponentName).append(")"); - } - info.append("\n"); - info.append("PID: ").append(app.pid).append("\n"); - if (annotation != null) { - info.append("Reason: ").append(annotation).append("\n"); - } - if (parent != null && parent != activity) { - info.append("Parent: ").append(parent.shortComponentName).append("\n"); - } - - final ProcessCpuTracker processCpuTracker = new ProcessCpuTracker(true); - - File tracesFile = dumpStackTraces(true, firstPids, processCpuTracker, lastPids, - NATIVE_STACKS_OF_INTEREST); - - String cpuInfo = null; - if (MONITOR_CPU_USAGE) { - updateCpuStatsNow(); - synchronized (mProcessCpuTracker) { - cpuInfo = mProcessCpuTracker.printCurrentState(anrTime); - } - info.append(processCpuTracker.printCurrentLoad()); - info.append(cpuInfo); - } - - info.append(processCpuTracker.printCurrentState(anrTime)); - - Slog.e(TAG, info.toString()); - if (tracesFile == null) { - // There is no trace file, so dump (only) the alleged culprit's threads to the log - Process.sendSignal(app.pid, Process.SIGNAL_QUIT); - } - - addErrorToDropBox("anr", app, app.processName, activity, parent, annotation, - cpuInfo, tracesFile, null); - - if (mController != null) { - try { - // 0 == show dialog, 1 = keep waiting, -1 = kill process immediately - int res = mController.appNotResponding(app.processName, app.pid, info.toString()); - if (res != 0) { - if (res < 0 && app.pid != MY_PID) { - app.kill("anr", true); - } else { - synchronized (this) { - mServices.scheduleServiceTimeoutLocked(app); - } - } - return; - } - } catch (RemoteException e) { - mController = null; - Watchdog.getInstance().setActivityController(null); - } - } - - // Unless configured otherwise, swallow ANRs in background processes & kill the process. - boolean showBackground = Settings.Secure.getInt(mContext.getContentResolver(), - Settings.Secure.ANR_SHOW_BACKGROUND, 0) != 0; - - synchronized (this) { - mBatteryStatsService.noteProcessAnr(app.processName, app.uid); - - if (!showBackground && !app.isInterestingToUserLocked() && app.pid != MY_PID) { - app.kill("bg anr", true); - return; - } - - // Set the app's notResponding state, and look up the errorReportReceiver - makeAppNotRespondingLocked(app, - activity != null ? activity.shortComponentName : null, - annotation != null ? "ANR " + annotation : "ANR", - info.toString()); - - // Bring up the infamous App Not Responding dialog - Message msg = Message.obtain(); - HashMap<String, Object> map = new HashMap<String, Object>(); - msg.what = SHOW_NOT_RESPONDING_UI_MSG; - msg.obj = map; - msg.arg1 = aboveSystem ? 1 : 0; - map.put("app", app); - if (activity != null) { - map.put("activity", activity); - } - - mUiHandler.sendMessage(msg); - } - } - final void showLaunchWarningLocked(final ActivityRecord cur, final ActivityRecord next) { if (!mLaunchWarningShown) { mLaunchWarningShown = true; @@ -6079,33 +5780,7 @@ public final class ActivityManagerService extends ActivityManagerNative Slog.i(TAG, "Force stopping u" + userId + ": " + reason); } - final ArrayMap<String, SparseArray<Long>> pmap = mProcessCrashTimes.getMap(); - for (int ip = pmap.size() - 1; ip >= 0; ip--) { - SparseArray<Long> ba = pmap.valueAt(ip); - for (i = ba.size() - 1; i >= 0; i--) { - boolean remove = false; - final int entUid = ba.keyAt(i); - if (packageName != null) { - if (userId == UserHandle.USER_ALL) { - if (UserHandle.getAppId(entUid) == appId) { - remove = true; - } - } else { - if (entUid == UserHandle.getUid(userId, appId)) { - remove = true; - } - } - } else if (UserHandle.getUserId(entUid) == userId) { - remove = true; - } - if (remove) { - ba.removeAt(i); - } - } - if (ba.size() == 0) { - pmap.removeAt(ip); - } - } + mAppErrors.resetProcessCrashTimeLocked(packageName == null, appId, userId); } boolean didSomething = killPackageProcessesLocked(packageName, appId, userId, @@ -6270,7 +5945,7 @@ public final class ActivityManagerService extends ActivityManagerNative } } - private final boolean removeProcessLocked(ProcessRecord app, + boolean removeProcessLocked(ProcessRecord app, boolean callerWillRestart, boolean allowRestart, String reason) { final String name = app.processName; final int uid = app.uid; @@ -10938,7 +10613,7 @@ public final class ActivityManagerService extends ActivityManagerNative final long token = Binder.clearCallingIdentity(); try { - appNotResponding(host, null, null, false, "ContentProvider not responding"); + mAppErrors.appNotResponding(host, null, null, false, "ContentProvider not responding"); } finally { Binder.restoreCallingIdentity(token); } @@ -11702,7 +11377,7 @@ public final class ActivityManagerService extends ActivityManagerNative mHandler.post(new Runnable() { @Override public void run() { - appNotResponding(proc, activity, parent, aboveSystem, annotation); + mAppErrors.appNotResponding(proc, activity, parent, aboveSystem, annotation); } }); } @@ -12496,17 +12171,8 @@ public final class ActivityManagerService extends ActivityManagerNative com.android.internal.R.dimen.thumbnail_height); mDefaultPinnedStackBounds = Rect.unflattenFromString(res.getString( com.android.internal.R.string.config_defaultPictureInPictureBounds)); - final String appsNotReportingCrashes = res.getString( - com.android.internal.R.string.config_appsNotReportingCrashes); - if (appsNotReportingCrashes != null) { - final String[] split = appsNotReportingCrashes.split(","); - if (split.length > 0) { - mAppsNotReportingCrashes = new ArraySet<>(); - for (int i = 0; i < split.length; i++) { - mAppsNotReportingCrashes.add(split[i]); - } - } - } + mAppErrors.loadAppsNotReportingCrashesFromConfigLocked(res.getString( + com.android.internal.R.string.config_appsNotReportingCrashes)); } } @@ -12911,174 +12577,12 @@ public final class ActivityManagerService extends ActivityManagerNative } } - private boolean makeAppCrashingLocked(ProcessRecord app, - String shortMsg, String longMsg, String stackTrace) { - app.crashing = true; - app.crashingReport = generateProcessError(app, - ActivityManager.ProcessErrorStateInfo.CRASHED, null, shortMsg, longMsg, stackTrace); - startAppProblemLocked(app); - app.stopFreezingAllLocked(); - return handleAppCrashLocked(app, "force-crash" /*reason*/, shortMsg, longMsg, stackTrace); - } - - private void makeAppNotRespondingLocked(ProcessRecord app, - String activity, String shortMsg, String longMsg) { - app.notResponding = true; - app.notRespondingReport = generateProcessError(app, - ActivityManager.ProcessErrorStateInfo.NOT_RESPONDING, - activity, shortMsg, longMsg, null); - startAppProblemLocked(app); - app.stopFreezingAllLocked(); - } - - /** - * Generate a process error record, suitable for attachment to a ProcessRecord. - * - * @param app The ProcessRecord in which the error occurred. - * @param condition Crashing, Application Not Responding, etc. Values are defined in - * ActivityManager.AppErrorStateInfo - * @param activity The activity associated with the crash, if known. - * @param shortMsg Short message describing the crash. - * @param longMsg Long message describing the crash. - * @param stackTrace Full crash stack trace, may be null. - * - * @return Returns a fully-formed AppErrorStateInfo record. - */ - private ActivityManager.ProcessErrorStateInfo generateProcessError(ProcessRecord app, - int condition, String activity, String shortMsg, String longMsg, String stackTrace) { - ActivityManager.ProcessErrorStateInfo report = new ActivityManager.ProcessErrorStateInfo(); - - report.condition = condition; - report.processName = app.processName; - report.pid = app.pid; - report.uid = app.info.uid; - report.tag = activity; - report.shortMsg = shortMsg; - report.longMsg = longMsg; - report.stackTrace = stackTrace; - - return report; - } - void killAppAtUsersRequest(ProcessRecord app, Dialog fromDialog) { synchronized (this) { - app.crashing = false; - app.crashingReport = null; - app.notResponding = false; - app.notRespondingReport = null; - if (app.anrDialog == fromDialog) { - app.anrDialog = null; - } - if (app.waitDialog == fromDialog) { - app.waitDialog = null; - } - if (app.pid > 0 && app.pid != MY_PID) { - handleAppCrashLocked(app, "user-terminated" /*reason*/, - null /*shortMsg*/, null /*longMsg*/, null /*stackTrace*/); - app.kill("user request after error", true); - } + mAppErrors.killAppAtUserRequestLocked(app, fromDialog); } } - private boolean handleAppCrashLocked(ProcessRecord app, String reason, - String shortMsg, String longMsg, String stackTrace) { - long now = SystemClock.uptimeMillis(); - - Long crashTime; - if (!app.isolated) { - crashTime = mProcessCrashTimes.get(app.info.processName, app.uid); - } else { - crashTime = null; - } - if (crashTime != null && now < crashTime+ProcessList.MIN_CRASH_INTERVAL) { - // This process loses! - Slog.w(TAG, "Process " + app.info.processName - + " has crashed too many times: killing!"); - EventLog.writeEvent(EventLogTags.AM_PROCESS_CRASHED_TOO_MUCH, - app.userId, app.info.processName, app.uid); - mStackSupervisor.handleAppCrashLocked(app); - if (!app.persistent) { - // We don't want to start this process again until the user - // explicitly does so... but for persistent process, we really - // need to keep it running. If a persistent process is actually - // repeatedly crashing, then badness for everyone. - EventLog.writeEvent(EventLogTags.AM_PROC_BAD, app.userId, app.uid, - app.info.processName); - if (!app.isolated) { - // XXX We don't have a way to mark isolated processes - // as bad, since they don't have a peristent identity. - mBadProcesses.put(app.info.processName, app.uid, - new BadProcessInfo(now, shortMsg, longMsg, stackTrace)); - mProcessCrashTimes.remove(app.info.processName, app.uid); - } - app.bad = true; - app.removed = true; - // Don't let services in this process be restarted and potentially - // annoy the user repeatedly. Unless it is persistent, since those - // processes run critical code. - removeProcessLocked(app, false, false, "crash"); - mStackSupervisor.resumeFocusedStackTopActivityLocked(); - return false; - } - mStackSupervisor.resumeFocusedStackTopActivityLocked(); - } else { - mStackSupervisor.finishTopRunningActivityLocked(app, reason); - } - - // Bump up the crash count of any services currently running in the proc. - for (int i=app.services.size()-1; i>=0; i--) { - // Any services running in the application need to be placed - // back in the pending list. - ServiceRecord sr = app.services.valueAt(i); - sr.crashCount++; - } - - // If the crashing process is what we consider to be the "home process" and it has been - // replaced by a third-party app, clear the package preferred activities from packages - // with a home activity running in the process to prevent a repeatedly crashing app - // from blocking the user to manually clear the list. - final ArrayList<ActivityRecord> activities = app.activities; - if (app == mHomeProcess && activities.size() > 0 - && (mHomeProcess.info.flags & ApplicationInfo.FLAG_SYSTEM) == 0) { - for (int activityNdx = activities.size() - 1; activityNdx >= 0; --activityNdx) { - final ActivityRecord r = activities.get(activityNdx); - if (r.isHomeActivity()) { - Log.i(TAG, "Clearing package preferred activities from " + r.packageName); - try { - ActivityThread.getPackageManager() - .clearPackagePreferredActivities(r.packageName); - } catch (RemoteException c) { - // pm is in same process, this will never happen. - } - } - } - } - - if (!app.isolated) { - // XXX Can't keep track of crash times for isolated processes, - // because they don't have a perisistent identity. - mProcessCrashTimes.put(app.info.processName, app.uid, now); - } - - if (app.crashHandler != null) mHandler.post(app.crashHandler); - return true; - } - - void startAppProblemLocked(ProcessRecord app) { - // If this app is not running under the current user, then we - // can't give it a report button because that would require - // launching the report UI under a different user. - app.errorReportReceiver = null; - - for (int userId : mUserController.getCurrentProfileIdsLocked()) { - if (app.userId == userId) { - app.errorReportReceiver = ApplicationErrorReport.getErrorReportReceiver( - mContext, app.info.packageName, app.info.flags); - } - } - skipCurrentReceiverLocked(app); - } - void skipCurrentReceiverLocked(ProcessRecord app) { for (BroadcastQueue queue : mBroadcastQueues) { queue.skipCurrentReceiverLocked(app); @@ -13114,7 +12618,7 @@ public final class ActivityManagerService extends ActivityManagerNative addErrorToDropBox(eventType, r, processName, null, null, null, null, null, crashInfo); - crashApplication(r, crashInfo); + mAppErrors.crashApplication(r, crashInfo); } public void handleApplicationStrictModeViolation( @@ -13325,7 +12829,7 @@ public final class ActivityManagerService extends ActivityManagerNative if (r != null && r.pid != Process.myPid() && Settings.Global.getInt(mContext.getContentResolver(), Settings.Global.WTF_IS_FATAL, 0) != 0) { - crashApplication(r, crashInfo); + mAppErrors.crashApplication(r, crashInfo); return true; } else { return false; @@ -13534,164 +13038,6 @@ public final class ActivityManagerService extends ActivityManagerNative } } - /** - * Bring up the "unexpected error" dialog box for a crashing app. - * Deal with edge cases (intercepts from instrumented applications, - * ActivityController, error intent receivers, that sort of thing). - * @param r the application crashing - * @param crashInfo describing the failure - */ - private void crashApplication(ProcessRecord r, ApplicationErrorReport.CrashInfo crashInfo) { - long timeMillis = System.currentTimeMillis(); - String shortMsg = crashInfo.exceptionClassName; - String longMsg = crashInfo.exceptionMessage; - String stackTrace = crashInfo.stackTrace; - if (shortMsg != null && longMsg != null) { - longMsg = shortMsg + ": " + longMsg; - } else if (shortMsg != null) { - longMsg = shortMsg; - } - - AppErrorResult result = new AppErrorResult(); - synchronized (this) { - if (mController != null) { - try { - String name = r != null ? r.processName : null; - int pid = r != null ? r.pid : Binder.getCallingPid(); - int uid = r != null ? r.info.uid : Binder.getCallingUid(); - if (!mController.appCrashed(name, pid, - shortMsg, longMsg, timeMillis, crashInfo.stackTrace)) { - if ("1".equals(SystemProperties.get(SYSTEM_DEBUGGABLE, "0")) - && "Native crash".equals(crashInfo.exceptionClassName)) { - Slog.w(TAG, "Skip killing native crashed app " + name - + "(" + pid + ") during testing"); - } else { - Slog.w(TAG, "Force-killing crashed app " + name - + " at watcher's request"); - if (r != null) { - r.kill("crash", true); - } else { - // Huh. - Process.killProcess(pid); - killProcessGroup(uid, pid); - } - } - return; - } - } catch (RemoteException e) { - mController = null; - Watchdog.getInstance().setActivityController(null); - } - } - - final long origId = Binder.clearCallingIdentity(); - - // If this process is running instrumentation, finish it. - if (r != null && r.instrumentationClass != null) { - Slog.w(TAG, "Error in app " + r.processName - + " running instrumentation " + r.instrumentationClass + ":"); - if (shortMsg != null) Slog.w(TAG, " " + shortMsg); - if (longMsg != null) Slog.w(TAG, " " + longMsg); - Bundle info = new Bundle(); - info.putString("shortMsg", shortMsg); - info.putString("longMsg", longMsg); - finishInstrumentationLocked(r, Activity.RESULT_CANCELED, info); - Binder.restoreCallingIdentity(origId); - return; - } - - // Log crash in battery stats. - if (r != null) { - mBatteryStatsService.noteProcessCrash(r.processName, r.uid); - } - - // If we can't identify the process or it's already exceeded its crash quota, - // quit right away without showing a crash dialog. - if (r == null || !makeAppCrashingLocked(r, shortMsg, longMsg, stackTrace)) { - Binder.restoreCallingIdentity(origId); - return; - } - - Message msg = Message.obtain(); - msg.what = SHOW_ERROR_UI_MSG; - HashMap data = new HashMap(); - data.put("result", result); - data.put("app", r); - msg.obj = data; - mUiHandler.sendMessage(msg); - - Binder.restoreCallingIdentity(origId); - } - - int res = result.get(); - - Intent appErrorIntent = null; - synchronized (this) { - if (r != null && !r.isolated) { - // XXX Can't keep track of crash time for isolated processes, - // since they don't have a persistent identity. - mProcessCrashTimes.put(r.info.processName, r.uid, - SystemClock.uptimeMillis()); - } - if (res == AppErrorDialog.FORCE_QUIT_AND_REPORT) { - appErrorIntent = createAppErrorIntentLocked(r, timeMillis, crashInfo); - } - } - - if (appErrorIntent != null) { - try { - mContext.startActivityAsUser(appErrorIntent, new UserHandle(r.userId)); - } catch (ActivityNotFoundException e) { - Slog.w(TAG, "bug report receiver dissappeared", e); - } - } - } - - Intent createAppErrorIntentLocked(ProcessRecord r, - long timeMillis, ApplicationErrorReport.CrashInfo crashInfo) { - ApplicationErrorReport report = createAppErrorReportLocked(r, timeMillis, crashInfo); - if (report == null) { - return null; - } - Intent result = new Intent(Intent.ACTION_APP_ERROR); - result.setComponent(r.errorReportReceiver); - result.putExtra(Intent.EXTRA_BUG_REPORT, report); - result.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK); - return result; - } - - private ApplicationErrorReport createAppErrorReportLocked(ProcessRecord r, - long timeMillis, ApplicationErrorReport.CrashInfo crashInfo) { - if (r.errorReportReceiver == null) { - return null; - } - - if (!r.crashing && !r.notResponding && !r.forceCrashReport) { - return null; - } - - ApplicationErrorReport report = new ApplicationErrorReport(); - report.packageName = r.info.packageName; - report.installerPackageName = r.errorReportReceiver.getPackageName(); - report.processName = r.processName; - report.time = timeMillis; - report.systemApp = (r.info.flags & ApplicationInfo.FLAG_SYSTEM) != 0; - - if (r.crashing || r.forceCrashReport) { - report.type = ApplicationErrorReport.TYPE_CRASH; - report.crashInfo = crashInfo; - } else if (r.notResponding) { - report.type = ApplicationErrorReport.TYPE_ANR; - report.anrInfo = new ApplicationErrorReport.AnrInfo(); - - report.anrInfo.activity = r.notRespondingReport.tag; - report.anrInfo.cause = r.notRespondingReport.shortMsg; - report.anrInfo.info = r.notRespondingReport.longMsg; - } - - return report; - } - public List<ActivityManager.ProcessErrorStateInfo> getProcessesInErrorState() { enforceNotIsolatedCaller("getProcessesInErrorState"); // assume our apps are happy - lazy create the list @@ -14429,88 +13775,9 @@ public final class ActivityManagerService extends ActivityManagerNative needSep = dumpProcessesToGc(fd, pw, args, opti, needSep, dumpAll, dumpPackage); - if (mProcessCrashTimes.getMap().size() > 0) { - boolean printed = false; - long now = SystemClock.uptimeMillis(); - final ArrayMap<String, SparseArray<Long>> pmap = mProcessCrashTimes.getMap(); - final int NP = pmap.size(); - for (int ip=0; ip<NP; ip++) { - String pname = pmap.keyAt(ip); - SparseArray<Long> uids = pmap.valueAt(ip); - final int N = uids.size(); - for (int i=0; i<N; i++) { - int puid = uids.keyAt(i); - ProcessRecord r = mProcessNames.get(pname, puid); - if (dumpPackage != null && (r == null - || !r.pkgList.containsKey(dumpPackage))) { - continue; - } - if (!printed) { - if (needSep) pw.println(); - needSep = true; - pw.println(" Time since processes crashed:"); - printed = true; - printedAnything = true; - } - pw.print(" Process "); pw.print(pname); - pw.print(" uid "); pw.print(puid); - pw.print(": last crashed "); - TimeUtils.formatDuration(now-uids.valueAt(i), pw); - pw.println(" ago"); - } - } - } - - if (mBadProcesses.getMap().size() > 0) { - boolean printed = false; - final ArrayMap<String, SparseArray<BadProcessInfo>> pmap = mBadProcesses.getMap(); - final int NP = pmap.size(); - for (int ip=0; ip<NP; ip++) { - String pname = pmap.keyAt(ip); - SparseArray<BadProcessInfo> uids = pmap.valueAt(ip); - final int N = uids.size(); - for (int i=0; i<N; i++) { - int puid = uids.keyAt(i); - ProcessRecord r = mProcessNames.get(pname, puid); - if (dumpPackage != null && (r == null - || !r.pkgList.containsKey(dumpPackage))) { - continue; - } - if (!printed) { - if (needSep) pw.println(); - needSep = true; - pw.println(" Bad processes:"); - printedAnything = true; - } - BadProcessInfo info = uids.valueAt(i); - pw.print(" Bad process "); pw.print(pname); - pw.print(" uid "); pw.print(puid); - pw.print(": crashed at time "); pw.println(info.time); - if (info.shortMsg != null) { - pw.print(" Short msg: "); pw.println(info.shortMsg); - } - if (info.longMsg != null) { - pw.print(" Long msg: "); pw.println(info.longMsg); - } - if (info.stack != null) { - pw.println(" Stack:"); - int lastPos = 0; - for (int pos=0; pos<info.stack.length(); pos++) { - if (info.stack.charAt(pos) == '\n') { - pw.print(" "); - pw.write(info.stack, lastPos, pos-lastPos); - pw.println(); - lastPos = pos+1; - } - } - if (lastPos < info.stack.length()) { - pw.print(" "); - pw.write(info.stack, lastPos, info.stack.length()-lastPos); - pw.println(); - } - } - } - } + needSep = mAppErrors.dumpLocked(fd, pw, needSep, dumpPackage); + if (needSep) { + printedAnything = true; } if (dumpPackage == null) { @@ -21173,13 +20440,6 @@ public final class ActivityManagerService extends ActivityManagerNative } } - void stopReportingCrashesLocked(ProcessRecord proc) { - if (mAppsNotReportingCrashes == null) { - mAppsNotReportingCrashes = new ArraySet<>(); - } - mAppsNotReportingCrashes.add(proc.info.packageName); - } - private final class LocalService extends ActivityManagerInternal { @Override public void onWakefulnessChanged(int wakefulness) { diff --git a/services/core/java/com/android/server/am/ActivityStack.java b/services/core/java/com/android/server/am/ActivityStack.java index 4bac2d6fd69a..ef8d2305fb2b 100644 --- a/services/core/java/com/android/server/am/ActivityStack.java +++ b/services/core/java/com/android/server/am/ActivityStack.java @@ -3041,42 +3041,44 @@ final class ActivityStack { mService.updateOomAdjLocked(); } - final void finishTopRunningActivityLocked(ProcessRecord app, String reason) { + final TaskRecord finishTopRunningActivityLocked(ProcessRecord app, String reason) { ActivityRecord r = topRunningActivityLocked(); - if (r != null && r.app == app) { - // If the top running activity is from this crashing - // process, then terminate it to avoid getting in a loop. - Slog.w(TAG, " Force finishing activity " - + r.intent.getComponent().flattenToShortString()); - int taskNdx = mTaskHistory.indexOf(r.task); - int activityNdx = r.task.mActivities.indexOf(r); - finishActivityLocked(r, Activity.RESULT_CANCELED, null, reason, false); - // Also terminate any activities below it that aren't yet - // stopped, to avoid a situation where one will get - // re-start our crashing activity once it gets resumed again. - --activityNdx; - if (activityNdx < 0) { - do { - --taskNdx; - if (taskNdx < 0) { - break; - } - activityNdx = mTaskHistory.get(taskNdx).mActivities.size() - 1; - } while (activityNdx < 0); - } - if (activityNdx >= 0) { - r = mTaskHistory.get(taskNdx).mActivities.get(activityNdx); - if (r.state == ActivityState.RESUMED - || r.state == ActivityState.PAUSING - || r.state == ActivityState.PAUSED) { - if (!r.isHomeActivity() || mService.mHomeProcess != r.app) { - Slog.w(TAG, " Force finishing activity " - + r.intent.getComponent().flattenToShortString()); - finishActivityLocked(r, Activity.RESULT_CANCELED, null, reason, false); - } + TaskRecord finishedTask = null; + if (r == null || r.app != app) { + return null; + } + Slog.w(TAG, " Force finishing activity " + + r.intent.getComponent().flattenToShortString()); + int taskNdx = mTaskHistory.indexOf(r.task); + int activityNdx = r.task.mActivities.indexOf(r); + finishActivityLocked(r, Activity.RESULT_CANCELED, null, reason, false); + finishedTask = r.task; + // Also terminate any activities below it that aren't yet + // stopped, to avoid a situation where one will get + // re-start our crashing activity once it gets resumed again. + --activityNdx; + if (activityNdx < 0) { + do { + --taskNdx; + if (taskNdx < 0) { + break; + } + activityNdx = mTaskHistory.get(taskNdx).mActivities.size() - 1; + } while (activityNdx < 0); + } + if (activityNdx >= 0) { + r = mTaskHistory.get(taskNdx).mActivities.get(activityNdx); + if (r.state == ActivityState.RESUMED + || r.state == ActivityState.PAUSING + || r.state == ActivityState.PAUSED) { + if (!r.isHomeActivity() || mService.mHomeProcess != r.app) { + Slog.w(TAG, " Force finishing activity " + + r.intent.getComponent().flattenToShortString()); + finishActivityLocked(r, Activity.RESULT_CANCELED, null, reason, false); } } } + return finishedTask; } final void finishVoiceTask(IVoiceInteractionSession session) { diff --git a/services/core/java/com/android/server/am/ActivityStackSupervisor.java b/services/core/java/com/android/server/am/ActivityStackSupervisor.java index 1fc674b7235d..8db2f8ff50f3 100644 --- a/services/core/java/com/android/server/am/ActivityStackSupervisor.java +++ b/services/core/java/com/android/server/am/ActivityStackSupervisor.java @@ -1668,15 +1668,21 @@ public final class ActivityStackSupervisor implements DisplayListener { return false; } - void finishTopRunningActivityLocked(ProcessRecord app, String reason) { + TaskRecord finishTopRunningActivityLocked(ProcessRecord app, String reason) { + TaskRecord finishedTask = null; + ActivityStack focusedStack = getFocusedStack(); for (int displayNdx = mActivityDisplays.size() - 1; displayNdx >= 0; --displayNdx) { final ArrayList<ActivityStack> stacks = mActivityDisplays.valueAt(displayNdx).mStacks; final int numStacks = stacks.size(); for (int stackNdx = 0; stackNdx < numStacks; ++stackNdx) { final ActivityStack stack = stacks.get(stackNdx); - stack.finishTopRunningActivityLocked(app, reason); + TaskRecord t = stack.finishTopRunningActivityLocked(app, reason); + if (stack == focusedStack || finishedTask == null) { + finishedTask = t; + } } } + return finishedTask; } void finishVoiceTask(IVoiceInteractionSession session) { diff --git a/services/core/java/com/android/server/am/AppErrorDialog.java b/services/core/java/com/android/server/am/AppErrorDialog.java index c87eae0d61e2..b746a4b6b11c 100644 --- a/services/core/java/com/android/server/am/AppErrorDialog.java +++ b/services/core/java/com/android/server/am/AppErrorDialog.java @@ -16,73 +16,74 @@ package com.android.server.am; +import android.app.ActivityManagerInternal; +import android.app.ActivityOptions; import android.content.Context; -import android.content.DialogInterface; +import android.content.pm.IPackageDataObserver; +import android.content.pm.PackageManager; import android.content.res.Resources; import android.os.Bundle; import android.os.Handler; import android.os.Message; +import android.util.Slog; import android.view.LayoutInflater; import android.view.View; -import android.view.ViewGroup; import android.view.WindowManager; -import android.widget.CheckBox; import android.widget.FrameLayout; import android.widget.TextView; -final class AppErrorDialog extends BaseErrorDialog { +import java.util.List; + +import static com.android.server.am.ActivityManagerService.IS_USER_BUILD; + +final class AppErrorDialog extends BaseErrorDialog implements View.OnClickListener { private final ActivityManagerService mService; private final AppErrorResult mResult; private final ProcessRecord mProc; + private final boolean mRepeating; + private CharSequence mName; // Event 'what' codes - static final int FORCE_QUIT = 0; - static final int FORCE_QUIT_AND_REPORT = 1; + static final int FORCE_QUIT = 1; + static final int FORCE_QUIT_AND_REPORT = 2; + static final int RESTART = 3; + static final int RESET = 4; + static final int MUTE = 5; // 5-minute timeout, then we automatically dismiss the crash dialog static final long DISMISS_TIMEOUT = 1000 * 60 * 5; - - public AppErrorDialog(Context context, ActivityManagerService service, - AppErrorResult result, ProcessRecord app) { - super(context); + public AppErrorDialog(Context context, ActivityManagerService service, Data data) { + super(context); Resources res = context.getResources(); mService = service; - mProc = app; - mResult = result; - if ((app.pkgList.size() == 1) && - (mName = context.getPackageManager().getApplicationLabel(app.info)) != null) { - setMessage(res.getString( - com.android.internal.R.string.aerr_application, - mName.toString(), app.info.processName)); + mProc = data.proc; + mResult = data.result; + mRepeating = data.repeating; + if ((mProc.pkgList.size() == 1) && + (mName = context.getPackageManager().getApplicationLabel(mProc.info)) != null) { + setTitle(res.getString( + mRepeating ? com.android.internal.R.string.aerr_application_repeated + : com.android.internal.R.string.aerr_application, + mName.toString(), mProc.info.processName)); } else { - mName = app.processName; - setMessage(res.getString( - com.android.internal.R.string.aerr_process, + mName = mProc.processName; + setTitle(res.getString( + mRepeating ? com.android.internal.R.string.aerr_process_repeated + : com.android.internal.R.string.aerr_process, mName.toString())); } setCancelable(false); - setButton(DialogInterface.BUTTON_POSITIVE, - res.getText(com.android.internal.R.string.force_close), - mHandler.obtainMessage(FORCE_QUIT)); - - if (app.errorReportReceiver != null) { - setButton(DialogInterface.BUTTON_NEGATIVE, - res.getText(com.android.internal.R.string.report), - mHandler.obtainMessage(FORCE_QUIT_AND_REPORT)); - } - - setTitle(res.getText(com.android.internal.R.string.aerr_title)); WindowManager.LayoutParams attrs = getWindow().getAttributes(); - attrs.setTitle("Application Error: " + app.info.processName); + attrs.setTitle("Application Error: " + mProc.info.processName); attrs.privateFlags |= WindowManager.LayoutParams.PRIVATE_FLAG_SYSTEM_ERROR | WindowManager.LayoutParams.PRIVATE_FLAG_SHOW_FOR_ALL_USERS; getWindow().setAttributes(attrs); - if (app.persistent) { + if (mProc.persistent) { getWindow().setType(WindowManager.LayoutParams.TYPE_SYSTEM_ERROR); } @@ -95,38 +96,44 @@ final class AppErrorDialog extends BaseErrorDialog { @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); - if (!ActivityManagerService.IS_USER_BUILD) { - FrameLayout frame = (FrameLayout) findViewById(android.R.id.custom); - Context context = getContext(); - LayoutInflater.from(context).inflate( - com.android.internal.R.layout.app_error_dialog_dont_show_again, frame, true); - ((TextView) frame.findViewById(com.android.internal.R.id.text)).setText( - context.getResources().getString( - com.android.internal.R.string.aerr_process_silence, - mName.toString())); - findViewById(com.android.internal.R.id.customPanel).setVisibility(View.VISIBLE); - } + final FrameLayout frame = (FrameLayout) findViewById(android.R.id.custom); + final Context context = getContext(); + LayoutInflater.from(context).inflate( + com.android.internal.R.layout.app_error_dialog, frame, true); + + final TextView restart = (TextView) findViewById(com.android.internal.R.id.aerr_restart); + restart.setOnClickListener(this); + restart.setVisibility(!mRepeating ? View.VISIBLE : View.GONE); + final TextView reset = (TextView) findViewById(com.android.internal.R.id.aerr_reset); + reset.setOnClickListener(this); + reset.setVisibility(mRepeating ? View.VISIBLE : View.GONE); + final TextView report = (TextView) findViewById(com.android.internal.R.id.aerr_report); + report.setOnClickListener(this); + final boolean hasReceiver = mProc.errorReportReceiver != null; + report.setVisibility(hasReceiver ? View.VISIBLE : View.GONE); + final TextView close = (TextView) findViewById(com.android.internal.R.id.aerr_close); + close.setOnClickListener(this); + final TextView mute = (TextView) findViewById(com.android.internal.R.id.aerr_mute); + mute.setOnClickListener(this); + mute.setVisibility(!IS_USER_BUILD ? View.VISIBLE : View.GONE); + + findViewById(com.android.internal.R.id.customPanel).setVisibility(View.VISIBLE); } private final Handler mHandler = new Handler() { public void handleMessage(Message msg) { - View view = findViewById(com.android.internal.R.id.checkbox); - final boolean stopReporting = view != null && ((CheckBox) view).isChecked(); + final int result = msg.what; + synchronized (mService) { if (mProc != null && mProc.crashDialog == AppErrorDialog.this) { mProc.crashDialog = null; } - if (stopReporting) { - mService.stopReportingCrashesLocked(mProc); - } } - mResult.set(msg.what); + mResult.set(result); // Make sure we don't have time timeout still hanging around. removeMessages(FORCE_QUIT); - // If this is a timeout we won't be automatically closed, so go - // ahead and explicitly dismiss ourselves just in case. dismiss(); } }; @@ -139,4 +146,34 @@ final class AppErrorDialog extends BaseErrorDialog { } super.dismiss(); } + + @Override + public void onClick(View v) { + switch (v.getId()) { + case com.android.internal.R.id.aerr_restart: + mHandler.obtainMessage(RESTART).sendToTarget(); + break; + case com.android.internal.R.id.aerr_reset: + mHandler.obtainMessage(RESET).sendToTarget(); + break; + case com.android.internal.R.id.aerr_report: + mHandler.obtainMessage(FORCE_QUIT_AND_REPORT).sendToTarget(); + break; + case com.android.internal.R.id.aerr_close: + mHandler.obtainMessage(FORCE_QUIT).sendToTarget(); + break; + case com.android.internal.R.id.aerr_mute: + mHandler.obtainMessage(MUTE).sendToTarget(); + break; + default: + break; + } + } + + static class Data { + AppErrorResult result; + TaskRecord task; + boolean repeating; + ProcessRecord proc; + } } diff --git a/services/core/java/com/android/server/am/AppErrors.java b/services/core/java/com/android/server/am/AppErrors.java new file mode 100644 index 000000000000..58d9f451b9c9 --- /dev/null +++ b/services/core/java/com/android/server/am/AppErrors.java @@ -0,0 +1,964 @@ +/* + * Copyright (C) 2016 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License + */ + +package com.android.server.am; + +import com.android.internal.app.ProcessMap; +import com.android.internal.os.ProcessCpuTracker; +import com.android.server.Watchdog; + +import android.app.Activity; +import android.app.ActivityManager; +import android.app.ActivityOptions; +import android.app.ActivityThread; +import android.app.AppOpsManager; +import android.app.ApplicationErrorReport; +import android.app.Dialog; +import android.content.ActivityNotFoundException; +import android.content.Context; +import android.content.Intent; +import android.content.pm.ApplicationInfo; +import android.content.pm.IPackageDataObserver; +import android.content.pm.PackageManager; +import android.os.Binder; +import android.os.Bundle; +import android.os.Message; +import android.os.Process; +import android.os.RemoteException; +import android.os.SystemClock; +import android.os.SystemProperties; +import android.os.UserHandle; +import android.provider.Settings; +import android.util.ArrayMap; +import android.util.ArraySet; +import android.util.EventLog; +import android.util.Log; +import android.util.Slog; +import android.util.SparseArray; +import android.util.TimeUtils; + +import java.io.File; +import java.io.FileDescriptor; +import java.io.PrintWriter; +import java.util.ArrayList; +import java.util.Collections; +import java.util.HashMap; +import java.util.concurrent.Semaphore; + +import static com.android.server.Watchdog.NATIVE_STACKS_OF_INTEREST; +import static com.android.server.am.ActivityManagerDebugConfig.TAG_AM; +import static com.android.server.am.ActivityManagerDebugConfig.TAG_WITH_CLASS_NAME; +import static com.android.server.am.ActivityManagerService.MY_PID; +import static com.android.server.am.ActivityManagerService.SYSTEM_DEBUGGABLE; + +/** + * Controls error conditions in applications. + */ +class AppErrors { + + private static final String TAG = TAG_WITH_CLASS_NAME ? "AppErrors" : TAG_AM; + + private final ActivityManagerService mService; + private final Context mContext; + + private ArraySet<String> mAppsNotReportingCrashes; + + /** + * The last time that various processes have crashed since they were last explicitly started. + */ + private final ProcessMap<Long> mProcessCrashTimes = new ProcessMap<>(); + + /** + * The last time that various processes have crashed (not reset even when explicitly started). + */ + private final ProcessMap<Long> mProcessCrashTimesPersistent = new ProcessMap<>(); + + /** + * Set of applications that we consider to be bad, and will reject + * incoming broadcasts from (which the user has no control over). + * Processes are added to this set when they have crashed twice within + * a minimum amount of time; they are removed from it when they are + * later restarted (hopefully due to some user action). The value is the + * time it was added to the list. + */ + private final ProcessMap<BadProcessInfo> mBadProcesses = new ProcessMap<>(); + + + AppErrors(Context context, ActivityManagerService service) { + mService = service; + mContext = context; + } + + boolean dumpLocked(FileDescriptor fd, PrintWriter pw, boolean needSep, + String dumpPackage) { + if (!mProcessCrashTimes.getMap().isEmpty()) { + boolean printed = false; + final long now = SystemClock.uptimeMillis(); + final ArrayMap<String, SparseArray<Long>> pmap = mProcessCrashTimes.getMap(); + final int processCount = pmap.size(); + for (int ip = 0; ip < processCount; ip++) { + final String pname = pmap.keyAt(ip); + final SparseArray<Long> uids = pmap.valueAt(ip); + final int uidCount = uids.size(); + for (int i = 0; i < uidCount; i++) { + final int puid = uids.keyAt(i); + final ProcessRecord r = mService.mProcessNames.get(pname, puid); + if (dumpPackage != null && (r == null + || !r.pkgList.containsKey(dumpPackage))) { + continue; + } + if (!printed) { + if (needSep) pw.println(); + needSep = true; + pw.println(" Time since processes crashed:"); + printed = true; + } + pw.print(" Process "); pw.print(pname); + pw.print(" uid "); pw.print(puid); + pw.print(": last crashed "); + TimeUtils.formatDuration(now-uids.valueAt(i), pw); + pw.println(" ago"); + } + } + } + + if (!mBadProcesses.getMap().isEmpty()) { + boolean printed = false; + final ArrayMap<String, SparseArray<BadProcessInfo>> pmap = mBadProcesses.getMap(); + final int processCount = pmap.size(); + for (int ip = 0; ip < processCount; ip++) { + final String pname = pmap.keyAt(ip); + final SparseArray<BadProcessInfo> uids = pmap.valueAt(ip); + final int uidCount = uids.size(); + for (int i = 0; i < uidCount; i++) { + final int puid = uids.keyAt(i); + final ProcessRecord r = mService.mProcessNames.get(pname, puid); + if (dumpPackage != null && (r == null + || !r.pkgList.containsKey(dumpPackage))) { + continue; + } + if (!printed) { + if (needSep) pw.println(); + needSep = true; + pw.println(" Bad processes:"); + printed = true; + } + final BadProcessInfo info = uids.valueAt(i); + pw.print(" Bad process "); pw.print(pname); + pw.print(" uid "); pw.print(puid); + pw.print(": crashed at time "); pw.println(info.time); + if (info.shortMsg != null) { + pw.print(" Short msg: "); pw.println(info.shortMsg); + } + if (info.longMsg != null) { + pw.print(" Long msg: "); pw.println(info.longMsg); + } + if (info.stack != null) { + pw.println(" Stack:"); + int lastPos = 0; + for (int pos = 0; pos < info.stack.length(); pos++) { + if (info.stack.charAt(pos) == '\n') { + pw.print(" "); + pw.write(info.stack, lastPos, pos-lastPos); + pw.println(); + lastPos = pos+1; + } + } + if (lastPos < info.stack.length()) { + pw.print(" "); + pw.write(info.stack, lastPos, info.stack.length()-lastPos); + pw.println(); + } + } + } + } + } + return needSep; + } + + boolean isBadProcessLocked(ApplicationInfo info) { + return mBadProcesses.get(info.processName, info.uid) != null; + } + + void clearBadProcessLocked(ApplicationInfo info) { + mBadProcesses.remove(info.processName, info.uid); + } + + void resetProcessCrashTimeLocked(ApplicationInfo info) { + mProcessCrashTimes.remove(info.processName, info.uid); + } + + void resetProcessCrashTimeLocked(boolean resetEntireUser, int appId, int userId) { + final ArrayMap<String, SparseArray<Long>> pmap = mProcessCrashTimes.getMap(); + for (int ip = pmap.size() - 1; ip >= 0; ip--) { + SparseArray<Long> ba = pmap.valueAt(ip); + for (int i = ba.size() - 1; i >= 0; i--) { + boolean remove = false; + final int entUid = ba.keyAt(i); + if (!resetEntireUser) { + if (userId == UserHandle.USER_ALL) { + if (UserHandle.getAppId(entUid) == appId) { + remove = true; + } + } else { + if (entUid == UserHandle.getUid(userId, appId)) { + remove = true; + } + } + } else if (UserHandle.getUserId(entUid) == userId) { + remove = true; + } + if (remove) { + ba.removeAt(i); + } + } + if (ba.size() == 0) { + pmap.removeAt(ip); + } + } + } + + void loadAppsNotReportingCrashesFromConfigLocked(String appsNotReportingCrashesConfig) { + if (appsNotReportingCrashesConfig != null) { + final String[] split = appsNotReportingCrashesConfig.split(","); + if (split.length > 0) { + mAppsNotReportingCrashes = new ArraySet<>(); + Collections.addAll(mAppsNotReportingCrashes, split); + } + } + } + + void killAppAtUserRequestLocked(ProcessRecord app, Dialog fromDialog) { + app.crashing = false; + app.crashingReport = null; + app.notResponding = false; + app.notRespondingReport = null; + if (app.anrDialog == fromDialog) { + app.anrDialog = null; + } + if (app.waitDialog == fromDialog) { + app.waitDialog = null; + } + if (app.pid > 0 && app.pid != MY_PID) { + handleAppCrashLocked(app, "user-terminated" /*reason*/, + null /*shortMsg*/, null /*longMsg*/, null /*stackTrace*/, null /*data*/); + app.kill("user request after error", true); + } + } + + void scheduleAppCrashLocked(int uid, int initialPid, String packageName, + String message) { + ProcessRecord proc = null; + + // Figure out which process to kill. We don't trust that initialPid + // still has any relation to current pids, so must scan through the + // list. + + synchronized (mService.mPidsSelfLocked) { + for (int i=0; i<mService.mPidsSelfLocked.size(); i++) { + ProcessRecord p = mService.mPidsSelfLocked.valueAt(i); + if (p.uid != uid) { + continue; + } + if (p.pid == initialPid) { + proc = p; + break; + } + if (p.pkgList.containsKey(packageName)) { + proc = p; + } + } + } + + if (proc == null) { + Slog.w(TAG, "crashApplication: nothing for uid=" + uid + + " initialPid=" + initialPid + + " packageName=" + packageName); + return; + } + + if (proc.thread != null) { + if (proc.pid == Process.myPid()) { + Log.w(TAG, "crashApplication: trying to crash self!"); + return; + } + long ident = Binder.clearCallingIdentity(); + try { + proc.thread.scheduleCrash(message); + } catch (RemoteException e) { + } finally { + Binder.restoreCallingIdentity(ident); + } + } + } + + /** + * Bring up the "unexpected error" dialog box for a crashing app. + * Deal with edge cases (intercepts from instrumented applications, + * ActivityController, error intent receivers, that sort of thing). + * @param r the application crashing + * @param crashInfo describing the failure + */ + void crashApplication(ProcessRecord r, ApplicationErrorReport.CrashInfo crashInfo) { + long timeMillis = System.currentTimeMillis(); + String shortMsg = crashInfo.exceptionClassName; + String longMsg = crashInfo.exceptionMessage; + String stackTrace = crashInfo.stackTrace; + if (shortMsg != null && longMsg != null) { + longMsg = shortMsg + ": " + longMsg; + } else if (shortMsg != null) { + longMsg = shortMsg; + } + + AppErrorResult result = new AppErrorResult(); + TaskRecord task; + synchronized (mService) { + if (mService.mController != null) { + try { + String name = r != null ? r.processName : null; + int pid = r != null ? r.pid : Binder.getCallingPid(); + int uid = r != null ? r.info.uid : Binder.getCallingUid(); + if (!mService.mController.appCrashed(name, pid, + shortMsg, longMsg, timeMillis, crashInfo.stackTrace)) { + if ("1".equals(SystemProperties.get(SYSTEM_DEBUGGABLE, "0")) + && "Native crash".equals(crashInfo.exceptionClassName)) { + Slog.w(TAG, "Skip killing native crashed app " + name + + "(" + pid + ") during testing"); + } else { + Slog.w(TAG, "Force-killing crashed app " + name + + " at watcher's request"); + if (r != null) { + r.kill("crash", true); + } else { + // Huh. + Process.killProcess(pid); + ActivityManagerService.killProcessGroup(uid, pid); + } + } + return; + } + } catch (RemoteException e) { + mService.mController = null; + Watchdog.getInstance().setActivityController(null); + } + } + + final long origId = Binder.clearCallingIdentity(); + + // If this process is running instrumentation, finish it. + if (r != null && r.instrumentationClass != null) { + Slog.w(TAG, "Error in app " + r.processName + + " running instrumentation " + r.instrumentationClass + ":"); + if (shortMsg != null) Slog.w(TAG, " " + shortMsg); + if (longMsg != null) Slog.w(TAG, " " + longMsg); + Bundle info = new Bundle(); + info.putString("shortMsg", shortMsg); + info.putString("longMsg", longMsg); + mService.finishInstrumentationLocked(r, Activity.RESULT_CANCELED, info); + Binder.restoreCallingIdentity(origId); + return; + } + + // Log crash in battery stats. + if (r != null) { + mService.mBatteryStatsService.noteProcessCrash(r.processName, r.uid); + } + + AppErrorDialog.Data data = new AppErrorDialog.Data(); + data.result = result; + data.proc = r; + + // If we can't identify the process or it's already exceeded its crash quota, + // quit right away without showing a crash dialog. + if (r == null || !makeAppCrashingLocked(r, shortMsg, longMsg, stackTrace, data)) { + Binder.restoreCallingIdentity(origId); + return; + } + + Message msg = Message.obtain(); + msg.what = ActivityManagerService.SHOW_ERROR_UI_MSG; + + task = data.task; + msg.obj = data; + mService.mUiHandler.sendMessage(msg); + + Binder.restoreCallingIdentity(origId); + } + + int res = result.get(); + + Intent appErrorIntent = null; + final long ident = Binder.clearCallingIdentity(); + try { + if (res == AppErrorDialog.RESET) { + String[] packageList = r.getPackageList(); + if (packageList != null) { + PackageManager pm = mContext.getPackageManager(); + final Semaphore s = new Semaphore(0); + for (int i = 0; i < packageList.length; i++) { + if (i < packageList.length - 1) { + pm.deleteApplicationCacheFiles(packageList[i], null); + } else { + pm.deleteApplicationCacheFiles(packageList[i], + new IPackageDataObserver.Stub() { + @Override + public void onRemoveCompleted(String packageName, + boolean succeeded) { + s.release(); + } + }); + + // Wait until cache has been cleared before we restart. + try { + s.acquire(); + } catch (InterruptedException e) { + } + } + } + } + // If there was nothing to reset, just restart; + res = AppErrorDialog.RESTART; + } + synchronized (mService) { + if (res == AppErrorDialog.MUTE) { + stopReportingCrashesLocked(r); + } + if (res == AppErrorDialog.RESTART) { + mService.removeProcessLocked(r, false, true, "crash"); + if (task != null) { + try { + mService.startActivityFromRecents(task.taskId, + ActivityOptions.makeBasic().toBundle()); + } catch (IllegalArgumentException e) { + // Hmm, that didn't work, app might have crashed before creating a + // recents entry. Let's see if we have a safe-to-restart intent. + if (task.intent.getCategories().contains( + Intent.CATEGORY_LAUNCHER)) { + mService.startActivityInPackage(task.mCallingUid, + task.mCallingPackage, task.intent, + null, null, null, 0, 0, + ActivityOptions.makeBasic().toBundle(), + task.userId, null, null); + } + } + } + } + if (res == AppErrorDialog.FORCE_QUIT) { + long orig = Binder.clearCallingIdentity(); + try { + // Kill it with fire! + mService.mStackSupervisor.handleAppCrashLocked(r); + if (!r.persistent) { + mService.removeProcessLocked(r, false, false, "crash"); + mService.mStackSupervisor.resumeFocusedStackTopActivityLocked(); + } + } finally { + Binder.restoreCallingIdentity(orig); + } + } + if (res == AppErrorDialog.FORCE_QUIT_AND_REPORT) { + appErrorIntent = createAppErrorIntentLocked(r, timeMillis, crashInfo); + } + if (r != null && !r.isolated && res != AppErrorDialog.RESTART) { + // XXX Can't keep track of crash time for isolated processes, + // since they don't have a persistent identity. + mProcessCrashTimes.put(r.info.processName, r.uid, + SystemClock.uptimeMillis()); + } + } + } finally { + Binder.restoreCallingIdentity(ident); + } + + if (appErrorIntent != null) { + try { + mContext.startActivityAsUser(appErrorIntent, new UserHandle(r.userId)); + } catch (ActivityNotFoundException e) { + Slog.w(TAG, "bug report receiver dissappeared", e); + } + } + } + + private boolean makeAppCrashingLocked(ProcessRecord app, + String shortMsg, String longMsg, String stackTrace, AppErrorDialog.Data data) { + app.crashing = true; + app.crashingReport = generateProcessError(app, + ActivityManager.ProcessErrorStateInfo.CRASHED, null, shortMsg, longMsg, stackTrace); + startAppProblemLocked(app); + app.stopFreezingAllLocked(); + return handleAppCrashLocked(app, "force-crash" /*reason*/, shortMsg, longMsg, stackTrace, + data); + } + + void startAppProblemLocked(ProcessRecord app) { + // If this app is not running under the current user, then we + // can't give it a report button because that would require + // launching the report UI under a different user. + app.errorReportReceiver = null; + + for (int userId : mService.mUserController.getCurrentProfileIdsLocked()) { + if (app.userId == userId) { + app.errorReportReceiver = ApplicationErrorReport.getErrorReportReceiver( + mContext, app.info.packageName, app.info.flags); + } + } + mService.skipCurrentReceiverLocked(app); + } + + /** + * Generate a process error record, suitable for attachment to a ProcessRecord. + * + * @param app The ProcessRecord in which the error occurred. + * @param condition Crashing, Application Not Responding, etc. Values are defined in + * ActivityManager.AppErrorStateInfo + * @param activity The activity associated with the crash, if known. + * @param shortMsg Short message describing the crash. + * @param longMsg Long message describing the crash. + * @param stackTrace Full crash stack trace, may be null. + * + * @return Returns a fully-formed AppErrorStateInfo record. + */ + private ActivityManager.ProcessErrorStateInfo generateProcessError(ProcessRecord app, + int condition, String activity, String shortMsg, String longMsg, String stackTrace) { + ActivityManager.ProcessErrorStateInfo report = new ActivityManager.ProcessErrorStateInfo(); + + report.condition = condition; + report.processName = app.processName; + report.pid = app.pid; + report.uid = app.info.uid; + report.tag = activity; + report.shortMsg = shortMsg; + report.longMsg = longMsg; + report.stackTrace = stackTrace; + + return report; + } + + Intent createAppErrorIntentLocked(ProcessRecord r, + long timeMillis, ApplicationErrorReport.CrashInfo crashInfo) { + ApplicationErrorReport report = createAppErrorReportLocked(r, timeMillis, crashInfo); + if (report == null) { + return null; + } + Intent result = new Intent(Intent.ACTION_APP_ERROR); + result.setComponent(r.errorReportReceiver); + result.putExtra(Intent.EXTRA_BUG_REPORT, report); + result.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK); + return result; + } + + private ApplicationErrorReport createAppErrorReportLocked(ProcessRecord r, + long timeMillis, ApplicationErrorReport.CrashInfo crashInfo) { + if (r.errorReportReceiver == null) { + return null; + } + + if (!r.crashing && !r.notResponding && !r.forceCrashReport) { + return null; + } + + ApplicationErrorReport report = new ApplicationErrorReport(); + report.packageName = r.info.packageName; + report.installerPackageName = r.errorReportReceiver.getPackageName(); + report.processName = r.processName; + report.time = timeMillis; + report.systemApp = (r.info.flags & ApplicationInfo.FLAG_SYSTEM) != 0; + + if (r.crashing || r.forceCrashReport) { + report.type = ApplicationErrorReport.TYPE_CRASH; + report.crashInfo = crashInfo; + } else if (r.notResponding) { + report.type = ApplicationErrorReport.TYPE_ANR; + report.anrInfo = new ApplicationErrorReport.AnrInfo(); + + report.anrInfo.activity = r.notRespondingReport.tag; + report.anrInfo.cause = r.notRespondingReport.shortMsg; + report.anrInfo.info = r.notRespondingReport.longMsg; + } + + return report; + } + + boolean handleAppCrashLocked(ProcessRecord app, String reason, + String shortMsg, String longMsg, String stackTrace, AppErrorDialog.Data data) { + long now = SystemClock.uptimeMillis(); + + Long crashTime; + Long crashTimePersistent; + if (!app.isolated) { + crashTime = mProcessCrashTimes.get(app.info.processName, app.uid); + crashTimePersistent = mProcessCrashTimesPersistent.get(app.info.processName, app.uid); + } else { + crashTime = crashTimePersistent = null; + } + if (crashTime != null && now < crashTime+ProcessList.MIN_CRASH_INTERVAL) { + // This process loses! + Slog.w(TAG, "Process " + app.info.processName + + " has crashed too many times: killing!"); + EventLog.writeEvent(EventLogTags.AM_PROCESS_CRASHED_TOO_MUCH, + app.userId, app.info.processName, app.uid); + mService.mStackSupervisor.handleAppCrashLocked(app); + if (!app.persistent) { + // We don't want to start this process again until the user + // explicitly does so... but for persistent process, we really + // need to keep it running. If a persistent process is actually + // repeatedly crashing, then badness for everyone. + EventLog.writeEvent(EventLogTags.AM_PROC_BAD, app.userId, app.uid, + app.info.processName); + if (!app.isolated) { + // XXX We don't have a way to mark isolated processes + // as bad, since they don't have a peristent identity. + mBadProcesses.put(app.info.processName, app.uid, + new BadProcessInfo(now, shortMsg, longMsg, stackTrace)); + mProcessCrashTimes.remove(app.info.processName, app.uid); + } + app.bad = true; + app.removed = true; + // Don't let services in this process be restarted and potentially + // annoy the user repeatedly. Unless it is persistent, since those + // processes run critical code. + mService.removeProcessLocked(app, false, false, "crash"); + mService.mStackSupervisor.resumeFocusedStackTopActivityLocked(); + return false; + } + mService.mStackSupervisor.resumeFocusedStackTopActivityLocked(); + } else { + TaskRecord affectedTask = + mService.mStackSupervisor.finishTopRunningActivityLocked(app, reason); + if (data != null) { + data.task = affectedTask; + } + if (data != null && crashTimePersistent != null + && now < crashTimePersistent + ProcessList.MIN_CRASH_INTERVAL) { + data.repeating = true; + } + } + + // Bump up the crash count of any services currently running in the proc. + for (int i=app.services.size()-1; i>=0; i--) { + // Any services running in the application need to be placed + // back in the pending list. + ServiceRecord sr = app.services.valueAt(i); + sr.crashCount++; + } + + // If the crashing process is what we consider to be the "home process" and it has been + // replaced by a third-party app, clear the package preferred activities from packages + // with a home activity running in the process to prevent a repeatedly crashing app + // from blocking the user to manually clear the list. + final ArrayList<ActivityRecord> activities = app.activities; + if (app == mService.mHomeProcess && activities.size() > 0 + && (mService.mHomeProcess.info.flags & ApplicationInfo.FLAG_SYSTEM) == 0) { + for (int activityNdx = activities.size() - 1; activityNdx >= 0; --activityNdx) { + final ActivityRecord r = activities.get(activityNdx); + if (r.isHomeActivity()) { + Log.i(TAG, "Clearing package preferred activities from " + r.packageName); + try { + ActivityThread.getPackageManager() + .clearPackagePreferredActivities(r.packageName); + } catch (RemoteException c) { + // pm is in same process, this will never happen. + } + } + } + } + + if (!app.isolated) { + // XXX Can't keep track of crash times for isolated processes, + // because they don't have a perisistent identity. + mProcessCrashTimes.put(app.info.processName, app.uid, now); + mProcessCrashTimesPersistent.put(app.info.processName, app.uid, now); + } + + if (app.crashHandler != null) mService.mHandler.post(app.crashHandler); + return true; + } + + void handleShowAppErrorUi(Message msg) { + AppErrorDialog.Data data = (AppErrorDialog.Data) msg.obj; + boolean showBackground = Settings.Secure.getInt(mContext.getContentResolver(), + Settings.Secure.ANR_SHOW_BACKGROUND, 0) != 0; + synchronized (mService) { + ProcessRecord proc = data.proc; + AppErrorResult res = data.result; + if (proc != null && proc.crashDialog != null) { + Slog.e(TAG, "App already has crash dialog: " + proc); + if (res != null) { + res.set(0); + } + return; + } + boolean isBackground = (UserHandle.getAppId(proc.uid) + >= Process.FIRST_APPLICATION_UID + && proc.pid != MY_PID); + for (int userId : mService.mUserController.getCurrentProfileIdsLocked()) { + isBackground &= (proc.userId != userId); + } + if (isBackground && !showBackground) { + Slog.w(TAG, "Skipping crash dialog of " + proc + ": background"); + if (res != null) { + res.set(0); + } + return; + } + final boolean crashSilenced = mAppsNotReportingCrashes != null && + mAppsNotReportingCrashes.contains(proc.info.packageName); + if (mService.canShowErrorDialogs() && !crashSilenced) { + Dialog d = new AppErrorDialog(mContext, mService, data); + d.show(); + proc.crashDialog = d; + } else { + // The device is asleep, so just pretend that the user + // saw a crash dialog and hit "force quit". + if (res != null) { + res.set(0); + } + } + } + } + + void stopReportingCrashesLocked(ProcessRecord proc) { + if (mAppsNotReportingCrashes == null) { + mAppsNotReportingCrashes = new ArraySet<>(); + } + mAppsNotReportingCrashes.add(proc.info.packageName); + } + + final void appNotResponding(ProcessRecord app, ActivityRecord activity, + ActivityRecord parent, boolean aboveSystem, final String annotation) { + ArrayList<Integer> firstPids = new ArrayList<Integer>(5); + SparseArray<Boolean> lastPids = new SparseArray<Boolean>(20); + + if (mService.mController != null) { + try { + // 0 == continue, -1 = kill process immediately + int res = mService.mController.appEarlyNotResponding(app.processName, app.pid, annotation); + if (res < 0 && app.pid != MY_PID) { + app.kill("anr", true); + } + } catch (RemoteException e) { + mService.mController = null; + Watchdog.getInstance().setActivityController(null); + } + } + + long anrTime = SystemClock.uptimeMillis(); + if (ActivityManagerService.MONITOR_CPU_USAGE) { + mService.updateCpuStatsNow(); + } + + synchronized (mService) { + // PowerManager.reboot() can block for a long time, so ignore ANRs while shutting down. + if (mService.mShuttingDown) { + Slog.i(TAG, "During shutdown skipping ANR: " + app + " " + annotation); + return; + } else if (app.notResponding) { + Slog.i(TAG, "Skipping duplicate ANR: " + app + " " + annotation); + return; + } else if (app.crashing) { + Slog.i(TAG, "Crashing app skipping ANR: " + app + " " + annotation); + return; + } + + // In case we come through here for the same app before completing + // this one, mark as anring now so we will bail out. + app.notResponding = true; + + // Log the ANR to the event log. + EventLog.writeEvent(EventLogTags.AM_ANR, app.userId, app.pid, + app.processName, app.info.flags, annotation); + + // Dump thread traces as quickly as we can, starting with "interesting" processes. + firstPids.add(app.pid); + + int parentPid = app.pid; + if (parent != null && parent.app != null && parent.app.pid > 0) { + parentPid = parent.app.pid; + } + if (parentPid != app.pid) firstPids.add(parentPid); + + if (MY_PID != app.pid && MY_PID != parentPid) firstPids.add(MY_PID); + + for (int i = mService.mLruProcesses.size() - 1; i >= 0; i--) { + ProcessRecord r = mService.mLruProcesses.get(i); + if (r != null && r.thread != null) { + int pid = r.pid; + if (pid > 0 && pid != app.pid && pid != parentPid && pid != MY_PID) { + if (r.persistent) { + firstPids.add(pid); + } else { + lastPids.put(pid, Boolean.TRUE); + } + } + } + } + } + + // Log the ANR to the main log. + StringBuilder info = new StringBuilder(); + info.setLength(0); + info.append("ANR in ").append(app.processName); + if (activity != null && activity.shortComponentName != null) { + info.append(" (").append(activity.shortComponentName).append(")"); + } + info.append("\n"); + info.append("PID: ").append(app.pid).append("\n"); + if (annotation != null) { + info.append("Reason: ").append(annotation).append("\n"); + } + if (parent != null && parent != activity) { + info.append("Parent: ").append(parent.shortComponentName).append("\n"); + } + + final ProcessCpuTracker processCpuTracker = new ProcessCpuTracker(true); + + File tracesFile = mService.dumpStackTraces(true, firstPids, processCpuTracker, lastPids, + NATIVE_STACKS_OF_INTEREST); + + String cpuInfo = null; + if (ActivityManagerService.MONITOR_CPU_USAGE) { + mService.updateCpuStatsNow(); + synchronized (mService.mProcessCpuTracker) { + cpuInfo = mService.mProcessCpuTracker.printCurrentState(anrTime); + } + info.append(processCpuTracker.printCurrentLoad()); + info.append(cpuInfo); + } + + info.append(processCpuTracker.printCurrentState(anrTime)); + + Slog.e(TAG, info.toString()); + if (tracesFile == null) { + // There is no trace file, so dump (only) the alleged culprit's threads to the log + Process.sendSignal(app.pid, Process.SIGNAL_QUIT); + } + + mService.addErrorToDropBox("anr", app, app.processName, activity, parent, annotation, + cpuInfo, tracesFile, null); + + if (mService.mController != null) { + try { + // 0 == show dialog, 1 = keep waiting, -1 = kill process immediately + int res = mService.mController.appNotResponding( + app.processName, app.pid, info.toString()); + if (res != 0) { + if (res < 0 && app.pid != MY_PID) { + app.kill("anr", true); + } else { + synchronized (mService) { + mService.mServices.scheduleServiceTimeoutLocked(app); + } + } + return; + } + } catch (RemoteException e) { + mService.mController = null; + Watchdog.getInstance().setActivityController(null); + } + } + + // Unless configured otherwise, swallow ANRs in background processes & kill the process. + boolean showBackground = Settings.Secure.getInt(mContext.getContentResolver(), + Settings.Secure.ANR_SHOW_BACKGROUND, 0) != 0; + + synchronized (mService) { + mService.mBatteryStatsService.noteProcessAnr(app.processName, app.uid); + + if (!showBackground && !app.isInterestingToUserLocked() && app.pid != MY_PID) { + app.kill("bg anr", true); + return; + } + + // Set the app's notResponding state, and look up the errorReportReceiver + makeAppNotRespondingLocked(app, + activity != null ? activity.shortComponentName : null, + annotation != null ? "ANR " + annotation : "ANR", + info.toString()); + + // Bring up the infamous App Not Responding dialog + Message msg = Message.obtain(); + HashMap<String, Object> map = new HashMap<String, Object>(); + msg.what = ActivityManagerService.SHOW_NOT_RESPONDING_UI_MSG; + msg.obj = map; + msg.arg1 = aboveSystem ? 1 : 0; + map.put("app", app); + if (activity != null) { + map.put("activity", activity); + } + + mService.mUiHandler.sendMessage(msg); + } + } + + private void makeAppNotRespondingLocked(ProcessRecord app, + String activity, String shortMsg, String longMsg) { + app.notResponding = true; + app.notRespondingReport = generateProcessError(app, + ActivityManager.ProcessErrorStateInfo.NOT_RESPONDING, + activity, shortMsg, longMsg, null); + startAppProblemLocked(app); + app.stopFreezingAllLocked(); + } + + void handleShowAnrUi(Message msg) { + synchronized (mService) { + HashMap<String, Object> data = (HashMap<String, Object>) msg.obj; + ProcessRecord proc = (ProcessRecord)data.get("app"); + if (proc != null && proc.anrDialog != null) { + Slog.e(TAG, "App already has anr dialog: " + proc); + return; + } + + Intent intent = new Intent("android.intent.action.ANR"); + if (!mService.mProcessesReady) { + intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY + | Intent.FLAG_RECEIVER_FOREGROUND); + } + mService.broadcastIntentLocked(null, null, intent, + null, null, 0, null, null, null, AppOpsManager.OP_NONE, + null, false, false, MY_PID, Process.SYSTEM_UID, 0 /* TODO: Verify */); + + if (mService.canShowErrorDialogs()) { + Dialog d = new AppNotRespondingDialog(mService, + mContext, proc, (ActivityRecord)data.get("activity"), + msg.arg1 != 0); + d.show(); + proc.anrDialog = d; + } else { + // Just kill the app if there is no dialog to be shown. + mService.killAppAtUsersRequest(proc, null); + } + } + } + + /** + * Information about a process that is currently marked as bad. + */ + static final class BadProcessInfo { + BadProcessInfo(long time, String shortMsg, String longMsg, String stack) { + this.time = time; + this.shortMsg = shortMsg; + this.longMsg = longMsg; + this.stack = stack; + } + + final long time; + final String shortMsg; + final String longMsg; + final String stack; + } + +} diff --git a/services/core/java/com/android/server/am/AppNotRespondingDialog.java b/services/core/java/com/android/server/am/AppNotRespondingDialog.java index f4c166421c35..4587b720d814 100644 --- a/services/core/java/com/android/server/am/AppNotRespondingDialog.java +++ b/services/core/java/com/android/server/am/AppNotRespondingDialog.java @@ -117,7 +117,7 @@ final class AppNotRespondingDialog extends BaseErrorDialog { ProcessRecord app = mProc; if (msg.what == WAIT_AND_REPORT) { - appErrorIntent = mService.createAppErrorIntentLocked(app, + appErrorIntent = mService.mAppErrors.createAppErrorIntentLocked(app, System.currentTimeMillis(), null); } diff --git a/services/core/java/com/android/server/am/BroadcastQueue.java b/services/core/java/com/android/server/am/BroadcastQueue.java index 622aa16cb852..37b0af11a7a6 100644 --- a/services/core/java/com/android/server/am/BroadcastQueue.java +++ b/services/core/java/com/android/server/am/BroadcastQueue.java @@ -199,7 +199,7 @@ public final class BroadcastQueue { @Override public void run() { - mService.appNotResponding(mApp, null, null, false, mAnnotation); + mService.mAppErrors.appNotResponding(mApp, null, null, false, mAnnotation); } } diff --git a/services/core/java/com/android/server/am/StrictModeViolationDialog.java b/services/core/java/com/android/server/am/StrictModeViolationDialog.java index fda1ec130980..6da84bd589fc 100644 --- a/services/core/java/com/android/server/am/StrictModeViolationDialog.java +++ b/services/core/java/com/android/server/am/StrictModeViolationDialog.java @@ -73,7 +73,6 @@ final class StrictModeViolationDialog extends BaseErrorDialog { mHandler.obtainMessage(ACTION_OK_AND_REPORT)); } - setTitle(res.getText(com.android.internal.R.string.aerr_title)); getWindow().addPrivateFlags(PRIVATE_FLAG_SYSTEM_ERROR); getWindow().setTitle("Strict Mode Violation: " + app.info.processName); |