Merge "Fix crash loop when activity controller was set" into nyc-dev
This commit is contained in:
committed by
Android (Google) Code Review
commit
099347936b
@@ -304,6 +304,15 @@ class AppErrors {
|
|||||||
* @param crashInfo describing the failure
|
* @param crashInfo describing the failure
|
||||||
*/
|
*/
|
||||||
void crashApplication(ProcessRecord r, ApplicationErrorReport.CrashInfo crashInfo) {
|
void crashApplication(ProcessRecord r, ApplicationErrorReport.CrashInfo crashInfo) {
|
||||||
|
final long origId = Binder.clearCallingIdentity();
|
||||||
|
try {
|
||||||
|
crashApplicationInner(r, crashInfo);
|
||||||
|
} finally {
|
||||||
|
Binder.restoreCallingIdentity(origId);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void crashApplicationInner(ProcessRecord r, ApplicationErrorReport.CrashInfo crashInfo) {
|
||||||
long timeMillis = System.currentTimeMillis();
|
long timeMillis = System.currentTimeMillis();
|
||||||
String shortMsg = crashInfo.exceptionClassName;
|
String shortMsg = crashInfo.exceptionClassName;
|
||||||
String longMsg = crashInfo.exceptionMessage;
|
String longMsg = crashInfo.exceptionMessage;
|
||||||
@@ -317,49 +326,20 @@ class AppErrors {
|
|||||||
AppErrorResult result = new AppErrorResult();
|
AppErrorResult result = new AppErrorResult();
|
||||||
TaskRecord task;
|
TaskRecord task;
|
||||||
synchronized (mService) {
|
synchronized (mService) {
|
||||||
if (mService.mController != null) {
|
/**
|
||||||
try {
|
* If crash is handled by instance of {@link android.app.IActivityController},
|
||||||
String name = r != null ? r.processName : null;
|
* finish now and don't show the app error dialog.
|
||||||
int pid = r != null ? r.pid : Binder.getCallingPid();
|
*/
|
||||||
int uid = r != null ? r.info.uid : Binder.getCallingUid();
|
if (handleAppCrashInActivityController(r, crashInfo, shortMsg, longMsg, stackTrace,
|
||||||
if (!mService.mController.appCrashed(name, pid,
|
timeMillis)) {
|
||||||
shortMsg, longMsg, timeMillis, crashInfo.stackTrace)) {
|
return;
|
||||||
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 was running instrumentation, finish now - it will be handled in
|
||||||
// If this process is running instrumentation, finish it.
|
* {@link ActivityManagerService#handleAppDiedLocked}.
|
||||||
|
*/
|
||||||
if (r != null && r.instrumentationClass != null) {
|
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;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -375,7 +355,6 @@ class AppErrors {
|
|||||||
// If we can't identify the process or it's already exceeded its crash quota,
|
// If we can't identify the process or it's already exceeded its crash quota,
|
||||||
// quit right away without showing a crash dialog.
|
// quit right away without showing a crash dialog.
|
||||||
if (r == null || !makeAppCrashingLocked(r, shortMsg, longMsg, stackTrace, data)) {
|
if (r == null || !makeAppCrashingLocked(r, shortMsg, longMsg, stackTrace, data)) {
|
||||||
Binder.restoreCallingIdentity(origId);
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -385,97 +364,90 @@ class AppErrors {
|
|||||||
task = data.task;
|
task = data.task;
|
||||||
msg.obj = data;
|
msg.obj = data;
|
||||||
mService.mUiHandler.sendMessage(msg);
|
mService.mUiHandler.sendMessage(msg);
|
||||||
|
|
||||||
Binder.restoreCallingIdentity(origId);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
int res = result.get();
|
int res = result.get();
|
||||||
|
|
||||||
Intent appErrorIntent = null;
|
Intent appErrorIntent = null;
|
||||||
final long ident = Binder.clearCallingIdentity();
|
MetricsLogger.action(mContext, MetricsProto.MetricsEvent.ACTION_APP_CRASH, res);
|
||||||
try {
|
if (res == AppErrorDialog.TIMEOUT) {
|
||||||
MetricsLogger.action(mContext, MetricsProto.MetricsEvent.ACTION_APP_CRASH, res);
|
res = AppErrorDialog.FORCE_QUIT;
|
||||||
if (res == AppErrorDialog.TIMEOUT) {
|
}
|
||||||
res = AppErrorDialog.FORCE_QUIT;
|
if (res == AppErrorDialog.RESET) {
|
||||||
}
|
String[] packageList = r.getPackageList();
|
||||||
if (res == AppErrorDialog.RESET) {
|
if (packageList != null) {
|
||||||
String[] packageList = r.getPackageList();
|
PackageManager pm = mContext.getPackageManager();
|
||||||
if (packageList != null) {
|
final Semaphore s = new Semaphore(0);
|
||||||
PackageManager pm = mContext.getPackageManager();
|
for (int i = 0; i < packageList.length; i++) {
|
||||||
final Semaphore s = new Semaphore(0);
|
if (i < packageList.length - 1) {
|
||||||
for (int i = 0; i < packageList.length; i++) {
|
pm.deleteApplicationCacheFiles(packageList[i], null);
|
||||||
if (i < packageList.length - 1) {
|
} else {
|
||||||
pm.deleteApplicationCacheFiles(packageList[i], null);
|
pm.deleteApplicationCacheFiles(packageList[i],
|
||||||
} else {
|
new IPackageDataObserver.Stub() {
|
||||||
pm.deleteApplicationCacheFiles(packageList[i],
|
@Override
|
||||||
new IPackageDataObserver.Stub() {
|
public void onRemoveCompleted(String packageName,
|
||||||
@Override
|
boolean succeeded) {
|
||||||
public void onRemoveCompleted(String packageName,
|
s.release();
|
||||||
boolean succeeded) {
|
}
|
||||||
s.release();
|
});
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
// Wait until cache has been cleared before we restart.
|
// 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 {
|
try {
|
||||||
mService.startActivityFromRecents(task.taskId,
|
s.acquire();
|
||||||
ActivityOptions.makeBasic().toBundle());
|
} catch (InterruptedException e) {
|
||||||
} 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 {
|
// If there was nothing to reset, just restart;
|
||||||
Binder.restoreCallingIdentity(ident);
|
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());
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (appErrorIntent != null) {
|
if (appErrorIntent != null) {
|
||||||
@@ -487,6 +459,47 @@ class AppErrors {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private boolean handleAppCrashInActivityController(ProcessRecord r,
|
||||||
|
ApplicationErrorReport.CrashInfo crashInfo,
|
||||||
|
String shortMsg, String longMsg,
|
||||||
|
String stackTrace, long timeMillis) {
|
||||||
|
if (mService.mController == null) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
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) {
|
||||||
|
if (!makeAppCrashingLocked(r, shortMsg, longMsg, stackTrace, null))
|
||||||
|
{
|
||||||
|
r.kill("crash", true);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// Huh.
|
||||||
|
Process.killProcess(pid);
|
||||||
|
ActivityManagerService.killProcessGroup(uid, pid);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
} catch (RemoteException e) {
|
||||||
|
mService.mController = null;
|
||||||
|
Watchdog.getInstance().setActivityController(null);
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
private boolean makeAppCrashingLocked(ProcessRecord app,
|
private boolean makeAppCrashingLocked(ProcessRecord app,
|
||||||
String shortMsg, String longMsg, String stackTrace, AppErrorDialog.Data data) {
|
String shortMsg, String longMsg, String stackTrace, AppErrorDialog.Data data) {
|
||||||
app.crashing = true;
|
app.crashing = true;
|
||||||
|
|||||||
Reference in New Issue
Block a user