Merge "Crash dialog improvements, move crash code to AppErrors"
This commit is contained in:
70
core/res/res/layout/app_error_dialog.xml
Normal file
70
core/res/res/layout/app_error_dialog.xml
Normal file
@@ -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>
|
||||
@@ -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>
|
||||
@@ -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. -->
|
||||
|
||||
@@ -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">
|
||||
|
||||
@@ -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" />
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -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;
|
||||
@@ -667,38 +672,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) {
|
||||
|
||||
@@ -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) {
|
||||
|
||||
@@ -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) {
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
964
services/core/java/com/android/server/am/AppErrors.java
Normal file
964
services/core/java/com/android/server/am/AppErrors.java
Normal file
@@ -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;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -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);
|
||||
|
||||
|
||||
Reference in New Issue
Block a user