blob: 653ece4a883c2a1753d0cefb8bd6b21b393a40e0 [file] [log] [blame]
/*
* Copyright (C) 2020 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 static android.os.Process.SYSTEM_UID;
import static com.android.server.Watchdog.NATIVE_STACKS_OF_INTEREST;
import static com.android.server.am.ActivityManagerDebugConfig.DEBUG_ANR;
import static com.android.server.am.ActivityManagerService.MY_PID;
import static com.android.server.am.ProcessRecord.TAG;
import android.app.ActivityManager;
import android.app.AnrController;
import android.app.ApplicationErrorReport;
import android.app.ApplicationExitInfo;
import android.content.ComponentName;
import android.content.Context;
import android.content.pm.ApplicationInfo;
import android.content.pm.IncrementalStatesInfo;
import android.content.pm.PackageManagerInternal;
import android.os.IBinder;
import android.os.Message;
import android.os.Process;
import android.os.ServiceManager;
import android.os.SystemClock;
import android.os.incremental.IIncrementalService;
import android.os.incremental.IncrementalManager;
import android.os.incremental.IncrementalMetrics;
import android.provider.Settings;
import android.util.EventLog;
import android.util.Slog;
import android.util.SparseArray;
import com.android.internal.annotations.CompositeRWLock;
import com.android.internal.annotations.GuardedBy;
import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.os.ProcessCpuTracker;
import com.android.internal.util.FrameworkStatsLog;
import com.android.server.MemoryPressureUtil;
import com.android.server.criticalevents.CriticalEventLog;
import com.android.server.wm.WindowProcessController;
import java.io.File;
import java.io.PrintWriter;
import java.io.StringWriter;
import java.util.ArrayList;
import java.util.UUID;
/**
* The error state of the process, such as if it's crashing/ANR etc.
*/
class ProcessErrorStateRecord {
final ProcessRecord mApp;
private final ActivityManagerService mService;
private final ActivityManagerGlobalLock mProcLock;
/**
* True if disabled in the bad process list.
*/
@CompositeRWLock({"mService", "mProcLock"})
private boolean mBad;
/**
* Are we in the process of crashing?
*/
@CompositeRWLock({"mService", "mProcLock"})
private boolean mCrashing;
/**
* Suppress normal auto-dismiss of crash dialog & report UI?
*/
@CompositeRWLock({"mService", "mProcLock"})
private boolean mForceCrashReport;
/**
* Does the app have a not responding dialog?
*/
@CompositeRWLock({"mService", "mProcLock"})
private boolean mNotResponding;
/**
* The report about crash of the app, generated & stored when an app gets into a crash.
* Will be "null" when all is OK.
*/
@CompositeRWLock({"mService", "mProcLock"})
private ActivityManager.ProcessErrorStateInfo mCrashingReport;
/**
* The report about ANR of the app, generated & stored when an app gets into an ANR.
* Will be "null" when all is OK.
*/
@CompositeRWLock({"mService", "mProcLock"})
private ActivityManager.ProcessErrorStateInfo mNotRespondingReport;
/**
* Controller for error dialogs.
*/
@CompositeRWLock({"mService", "mProcLock"})
private final ErrorDialogController mDialogController;
/**
* Who will be notified of the error. This is usually an activity in the
* app that installed the package.
*/
@CompositeRWLock({"mService", "mProcLock"})
private ComponentName mErrorReportReceiver;
/**
* ANR dialog data used to dismiss any visible ANR dialogs if the app becomes responsive.
*/
@CompositeRWLock({"mService", "mProcLock"})
private AppNotRespondingDialog.Data mAnrData;
/**
* Annotation from process killed due to an ANR.
*/
@GuardedBy("mService")
private String mAnrAnnotation;
/**
* Optional local handler to be invoked in the process crash.
*/
@CompositeRWLock({"mService", "mProcLock"})
private Runnable mCrashHandler;
@GuardedBy(anyOf = {"mService", "mProcLock"})
boolean isBad() {
return mBad;
}
@GuardedBy({"mService", "mProcLock"})
void setBad(boolean bad) {
mBad = bad;
}
@GuardedBy(anyOf = {"mService", "mProcLock"})
boolean isCrashing() {
return mCrashing;
}
@GuardedBy({"mService", "mProcLock"})
void setCrashing(boolean crashing) {
mCrashing = crashing;
mApp.getWindowProcessController().setCrashing(crashing);
}
@GuardedBy(anyOf = {"mService", "mProcLock"})
boolean isForceCrashReport() {
return mForceCrashReport;
}
@GuardedBy({"mService", "mProcLock"})
void setForceCrashReport(boolean forceCrashReport) {
mForceCrashReport = forceCrashReport;
}
@GuardedBy(anyOf = {"mService", "mProcLock"})
boolean isNotResponding() {
return mNotResponding;
}
@GuardedBy({"mService", "mProcLock"})
void setNotResponding(boolean notResponding) {
mNotResponding = notResponding;
mApp.getWindowProcessController().setNotResponding(notResponding);
}
@GuardedBy(anyOf = {"mService", "mProcLock"})
Runnable getCrashHandler() {
return mCrashHandler;
}
@GuardedBy({"mService", "mProcLock"})
void setCrashHandler(Runnable crashHandler) {
mCrashHandler = crashHandler;
}
@GuardedBy(anyOf = {"mService", "mProcLock"})
ActivityManager.ProcessErrorStateInfo getCrashingReport() {
return mCrashingReport;
}
@GuardedBy({"mService", "mProcLock"})
void setCrashingReport(ActivityManager.ProcessErrorStateInfo crashingReport) {
mCrashingReport = crashingReport;
}
@GuardedBy("mService")
String getAnrAnnotation() {
return mAnrAnnotation;
}
@GuardedBy("mService")
void setAnrAnnotation(String anrAnnotation) {
mAnrAnnotation = anrAnnotation;
}
@GuardedBy(anyOf = {"mService", "mProcLock"})
ActivityManager.ProcessErrorStateInfo getNotRespondingReport() {
return mNotRespondingReport;
}
@GuardedBy({"mService", "mProcLock"})
void setNotRespondingReport(ActivityManager.ProcessErrorStateInfo notRespondingReport) {
mNotRespondingReport = notRespondingReport;
}
@GuardedBy(anyOf = {"mService", "mProcLock"})
ComponentName getErrorReportReceiver() {
return mErrorReportReceiver;
}
@GuardedBy({"mService", "mProcLock"})
void setErrorReportReceiver(ComponentName errorReportReceiver) {
mErrorReportReceiver = errorReportReceiver;
}
@GuardedBy(anyOf = {"mService", "mProcLock"})
ErrorDialogController getDialogController() {
return mDialogController;
}
@GuardedBy({"mService", "mProcLock"})
void setAnrData(AppNotRespondingDialog.Data data) {
mAnrData = data;
}
@GuardedBy(anyOf = {"mService", "mProcLock"})
AppNotRespondingDialog.Data getAnrData() {
return mAnrData;
}
ProcessErrorStateRecord(ProcessRecord app) {
mApp = app;
mService = app.mService;
mProcLock = mService.mProcLock;
mDialogController = new ErrorDialogController(app);
}
void appNotResponding(String activityShortComponentName, ApplicationInfo aInfo,
String parentShortComponentName, WindowProcessController parentProcess,
boolean aboveSystem, String annotation, boolean onlyDumpSelf) {
ArrayList<Integer> firstPids = new ArrayList<>(5);
SparseArray<Boolean> lastPids = new SparseArray<>(20);
mApp.getWindowProcessController().appEarlyNotResponding(annotation, () -> {
synchronized (mService) {
// Store annotation here as instance below races with this killLocked.
setAnrAnnotation(annotation);
mApp.killLocked("anr", ApplicationExitInfo.REASON_ANR, true);
}
});
long anrTime = SystemClock.uptimeMillis();
if (isMonitorCpuUsage()) {
mService.updateCpuStatsNow();
}
final boolean isSilentAnr;
final int pid = mApp.getPid();
final UUID errorId;
synchronized (mService) {
// Store annotation here as instance above will not be hit on all paths.
setAnrAnnotation(annotation);
// PowerManager.reboot() can block for a long time, so ignore ANRs while shutting down.
if (mService.mAtmInternal.isShuttingDown()) {
Slog.i(TAG, "During shutdown skipping ANR: " + this + " " + annotation);
return;
} else if (isNotResponding()) {
Slog.i(TAG, "Skipping duplicate ANR: " + this + " " + annotation);
return;
} else if (isCrashing()) {
Slog.i(TAG, "Crashing app skipping ANR: " + this + " " + annotation);
return;
} else if (mApp.isKilledByAm()) {
Slog.i(TAG, "App already killed by AM skipping ANR: " + this + " " + annotation);
return;
} else if (mApp.isKilled()) {
Slog.i(TAG, "Skipping died app ANR: " + this + " " + 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.
synchronized (mProcLock) {
setNotResponding(true);
}
// Log the ANR to the event log.
EventLog.writeEvent(EventLogTags.AM_ANR, mApp.userId, pid, mApp.processName,
mApp.info.flags, annotation);
if (mService.mTraceErrorLogger != null
&& mService.mTraceErrorLogger.isAddErrorIdEnabled()) {
errorId = mService.mTraceErrorLogger.generateErrorId();
mService.mTraceErrorLogger.addErrorIdToTrace(mApp.processName, errorId);
mService.mTraceErrorLogger.addSubjectToTrace(annotation, errorId);
} else {
errorId = null;
}
// This atom is only logged with the purpose of triggering Perfetto and the logging
// needs to happen as close as possible to the time when the ANR is detected.
// Also, it needs to be logged after adding the error id to the trace, to make sure
// the error id is present in the trace when the Perfetto trace is captured.
FrameworkStatsLog.write(FrameworkStatsLog.ANR_OCCURRED_PROCESSING_STARTED,
mApp.processName);
// Dump thread traces as quickly as we can, starting with "interesting" processes.
firstPids.add(pid);
// Don't dump other PIDs if it's a background ANR or is requested to only dump self.
isSilentAnr = isSilentAnr();
if (!isSilentAnr && !onlyDumpSelf) {
int parentPid = pid;
if (parentProcess != null && parentProcess.getPid() > 0) {
parentPid = parentProcess.getPid();
}
if (parentPid != pid) firstPids.add(parentPid);
if (MY_PID != pid && MY_PID != parentPid) firstPids.add(MY_PID);
final int ppid = parentPid;
mService.mProcessList.forEachLruProcessesLOSP(false, r -> {
if (r != null && r.getThread() != null) {
int myPid = r.getPid();
if (myPid > 0 && myPid != pid && myPid != ppid && myPid != MY_PID) {
if (r.isPersistent()) {
firstPids.add(myPid);
if (DEBUG_ANR) Slog.i(TAG, "Adding persistent proc: " + r);
} else if (r.mServices.isTreatedLikeActivity()) {
firstPids.add(myPid);
if (DEBUG_ANR) Slog.i(TAG, "Adding likely IME: " + r);
} else {
lastPids.put(myPid, Boolean.TRUE);
if (DEBUG_ANR) Slog.i(TAG, "Adding ANR proc: " + r);
}
}
}
});
}
}
// Get critical event log before logging the ANR so that it doesn't occur in the log.
final String criticalEventLog =
CriticalEventLog.getInstance().logLinesForTraceFile(
mApp.getProcessClassEnum(), mApp.processName, mApp.uid);
CriticalEventLog.getInstance().logAnr(annotation, mApp.getProcessClassEnum(),
mApp.processName, mApp.uid, mApp.mPid);
// Log the ANR to the main log.
StringBuilder info = new StringBuilder();
info.setLength(0);
info.append("ANR in ").append(mApp.processName);
if (activityShortComponentName != null) {
info.append(" (").append(activityShortComponentName).append(")");
}
info.append("\n");
info.append("PID: ").append(pid).append("\n");
if (annotation != null) {
info.append("Reason: ").append(annotation).append("\n");
}
if (parentShortComponentName != null
&& parentShortComponentName.equals(activityShortComponentName)) {
info.append("Parent: ").append(parentShortComponentName).append("\n");
}
if (errorId != null) {
info.append("ErrorId: ").append(errorId.toString()).append("\n");
}
info.append("Frozen: ").append(mApp.mOptRecord.isFrozen()).append("\n");
// Retrieve controller with max ANR delay from AnrControllers
// Note that we retrieve the controller before dumping stacks because dumping stacks can
// take a few seconds, after which the cause of the ANR delay might have completed and
// there might no longer be a valid ANR controller to cancel the dialog in that case
AnrController anrController = mService.mActivityTaskManager.getAnrController(aInfo);
long anrDialogDelayMs = 0;
if (anrController != null) {
String packageName = aInfo.packageName;
int uid = aInfo.uid;
anrDialogDelayMs = anrController.getAnrDelayMillis(packageName, uid);
// Might execute an async binder call to a system app to show an interim
// ANR progress UI
anrController.onAnrDelayStarted(packageName, uid);
Slog.i(TAG, "ANR delay of " + anrDialogDelayMs + "ms started for " + packageName);
}
StringBuilder report = new StringBuilder();
report.append(MemoryPressureUtil.currentPsiState());
ProcessCpuTracker processCpuTracker = new ProcessCpuTracker(true);
// don't dump native PIDs for background ANRs unless it is the process of interest
String[] nativeProcs = null;
if (isSilentAnr || onlyDumpSelf) {
for (int i = 0; i < NATIVE_STACKS_OF_INTEREST.length; i++) {
if (NATIVE_STACKS_OF_INTEREST[i].equals(mApp.processName)) {
nativeProcs = new String[] { mApp.processName };
break;
}
}
} else {
nativeProcs = NATIVE_STACKS_OF_INTEREST;
}
int[] pids = nativeProcs == null ? null : Process.getPidsForCommands(nativeProcs);
ArrayList<Integer> nativePids = null;
if (pids != null) {
nativePids = new ArrayList<>(pids.length);
for (int i : pids) {
nativePids.add(i);
}
}
// For background ANRs, don't pass the ProcessCpuTracker to
// avoid spending 1/2 second collecting stats to rank lastPids.
StringWriter tracesFileException = new StringWriter();
// To hold the start and end offset to the ANR trace file respectively.
final long[] offsets = new long[2];
File tracesFile = ActivityManagerService.dumpStackTraces(firstPids,
isSilentAnr ? null : processCpuTracker, isSilentAnr ? null : lastPids,
nativePids, tracesFileException, offsets, annotation, criticalEventLog);
if (isMonitorCpuUsage()) {
mService.updateCpuStatsNow();
mService.mAppProfiler.printCurrentCpuState(report, anrTime);
info.append(processCpuTracker.printCurrentLoad());
info.append(report);
}
report.append(tracesFileException.getBuffer());
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(pid, Process.SIGNAL_QUIT);
} else if (offsets[1] > 0) {
// We've dumped into the trace file successfully
mService.mProcessList.mAppExitInfoTracker.scheduleLogAnrTrace(
pid, mApp.uid, mApp.getPackageList(), tracesFile, offsets[0], offsets[1]);
}
// Check if package is still being loaded
float loadingProgress = 1;
IncrementalMetrics incrementalMetrics = null;
final PackageManagerInternal packageManagerInternal = mService.getPackageManagerInternal();
if (mApp.info != null && mApp.info.packageName != null && packageManagerInternal != null) {
IncrementalStatesInfo incrementalStatesInfo =
packageManagerInternal.getIncrementalStatesInfo(
mApp.info.packageName, SYSTEM_UID, mApp.userId);
if (incrementalStatesInfo != null) {
loadingProgress = incrementalStatesInfo.getProgress();
}
final String codePath = mApp.info.getCodePath();
if (codePath != null && !codePath.isEmpty()
&& IncrementalManager.isIncrementalPath(codePath)) {
// Report in the main log that the incremental package is still loading
Slog.e(TAG, "App ANR on incremental package " + mApp.info.packageName
+ " which is " + ((int) (loadingProgress * 100)) + "% loaded.");
final IBinder incrementalService = ServiceManager.getService(
Context.INCREMENTAL_SERVICE);
if (incrementalService != null) {
final IncrementalManager incrementalManager = new IncrementalManager(
IIncrementalService.Stub.asInterface(incrementalService));
incrementalMetrics = incrementalManager.getMetrics(codePath);
}
}
}
if (incrementalMetrics != null) {
// Report in the main log about the incremental package
info.append("Package is ").append((int) (loadingProgress * 100)).append("% loaded.\n");
}
FrameworkStatsLog.write(FrameworkStatsLog.ANR_OCCURRED, mApp.uid, mApp.processName,
activityShortComponentName == null ? "unknown" : activityShortComponentName,
annotation,
(mApp.info != null) ? (mApp.info.isInstantApp()
? FrameworkStatsLog.ANROCCURRED__IS_INSTANT_APP__TRUE
: FrameworkStatsLog.ANROCCURRED__IS_INSTANT_APP__FALSE)
: FrameworkStatsLog.ANROCCURRED__IS_INSTANT_APP__UNAVAILABLE,
mApp.isInterestingToUserLocked()
? FrameworkStatsLog.ANROCCURRED__FOREGROUND_STATE__FOREGROUND
: FrameworkStatsLog.ANROCCURRED__FOREGROUND_STATE__BACKGROUND,
mApp.getProcessClassEnum(),
(mApp.info != null) ? mApp.info.packageName : "",
incrementalMetrics != null /* isIncremental */, loadingProgress,
incrementalMetrics != null ? incrementalMetrics.getMillisSinceOldestPendingRead()
: -1,
incrementalMetrics != null ? incrementalMetrics.getStorageHealthStatusCode()
: -1,
incrementalMetrics != null ? incrementalMetrics.getDataLoaderStatusCode()
: -1,
incrementalMetrics != null && incrementalMetrics.getReadLogsEnabled(),
incrementalMetrics != null ? incrementalMetrics.getMillisSinceLastDataLoaderBind()
: -1,
incrementalMetrics != null ? incrementalMetrics.getDataLoaderBindDelayMillis()
: -1,
incrementalMetrics != null ? incrementalMetrics.getTotalDelayedReads()
: -1,
incrementalMetrics != null ? incrementalMetrics.getTotalFailedReads()
: -1,
incrementalMetrics != null ? incrementalMetrics.getLastReadErrorUid()
: -1,
incrementalMetrics != null ? incrementalMetrics.getMillisSinceLastReadError()
: -1,
incrementalMetrics != null ? incrementalMetrics.getLastReadErrorNumber()
: 0,
incrementalMetrics != null ? incrementalMetrics.getTotalDelayedReadsDurationMillis()
: -1);
final ProcessRecord parentPr = parentProcess != null
? (ProcessRecord) parentProcess.mOwner : null;
mService.addErrorToDropBox("anr", mApp, mApp.processName, activityShortComponentName,
parentShortComponentName, parentPr, null, report.toString(), tracesFile,
null, new Float(loadingProgress), incrementalMetrics, errorId);
if (mApp.getWindowProcessController().appNotResponding(info.toString(),
() -> {
synchronized (mService) {
mApp.killLocked("anr", ApplicationExitInfo.REASON_ANR, true);
}
},
() -> {
synchronized (mService) {
mService.mServices.scheduleServiceTimeoutLocked(mApp);
}
})) {
return;
}
synchronized (mService) {
// mBatteryStatsService can be null if the AMS is constructed with injector only. This
// will only happen in tests.
if (mService.mBatteryStatsService != null) {
mService.mBatteryStatsService.noteProcessAnr(mApp.processName, mApp.uid);
}
if (isSilentAnr() && !mApp.isDebugging()) {
mApp.killLocked("bg anr", ApplicationExitInfo.REASON_ANR, true);
return;
}
synchronized (mProcLock) {
// Set the app's notResponding state, and look up the errorReportReceiver
makeAppNotRespondingLSP(activityShortComponentName,
annotation != null ? "ANR " + annotation : "ANR", info.toString());
mDialogController.setAnrController(anrController);
}
// mUiHandler can be null if the AMS is constructed with injector only. This will only
// happen in tests.
if (mService.mUiHandler != null) {
// Bring up the infamous App Not Responding dialog
Message msg = Message.obtain();
msg.what = ActivityManagerService.SHOW_NOT_RESPONDING_UI_MSG;
msg.obj = new AppNotRespondingDialog.Data(mApp, aInfo, aboveSystem);
mService.mUiHandler.sendMessageDelayed(msg, anrDialogDelayMs);
}
}
}
@GuardedBy({"mService", "mProcLock"})
private void makeAppNotRespondingLSP(String activity, String shortMsg, String longMsg) {
setNotResponding(true);
// mAppErrors can be null if the AMS is constructed with injector only. This will only
// happen in tests.
if (mService.mAppErrors != null) {
mNotRespondingReport = mService.mAppErrors.generateProcessError(mApp,
ActivityManager.ProcessErrorStateInfo.NOT_RESPONDING,
activity, shortMsg, longMsg, null);
}
startAppProblemLSP();
mApp.getWindowProcessController().stopFreezingActivities();
}
@GuardedBy({"mService", "mProcLock"})
void startAppProblemLSP() {
// 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.
mErrorReportReceiver = null;
for (int userId : mService.mUserController.getCurrentProfileIds()) {
if (mApp.userId == userId) {
mErrorReportReceiver = ApplicationErrorReport.getErrorReportReceiver(
mService.mContext, mApp.info.packageName, mApp.info.flags);
}
}
mService.skipCurrentReceiverLocked(mApp);
}
@GuardedBy("mService")
private boolean isInterestingForBackgroundTraces() {
// The system_server is always considered interesting.
if (mApp.getPid() == MY_PID) {
return true;
}
// A package is considered interesting if any of the following is true :
//
// - It's displaying an activity.
// - It's the SystemUI.
// - It has an overlay or a top UI visible.
//
// NOTE: The check whether a given ProcessRecord belongs to the systemui
// process is a bit of a kludge, but the same pattern seems repeated at
// several places in the system server.
return mApp.isInterestingToUserLocked()
|| (mApp.info != null && "com.android.systemui".equals(mApp.info.packageName))
|| (mApp.mState.hasTopUi() || mApp.mState.hasOverlayUi());
}
private boolean getShowBackground() {
return Settings.Secure.getInt(mService.mContext.getContentResolver(),
Settings.Secure.ANR_SHOW_BACKGROUND, 0) != 0;
}
/**
* Unless configured otherwise, swallow ANRs in background processes & kill the process.
* Non-private access is for tests only.
*/
@VisibleForTesting
@GuardedBy("mService")
boolean isSilentAnr() {
return !getShowBackground() && !isInterestingForBackgroundTraces();
}
/** Non-private access is for tests only. */
@VisibleForTesting
boolean isMonitorCpuUsage() {
return mService.mAppProfiler.MONITOR_CPU_USAGE;
}
@GuardedBy({"mService", "mProcLock"})
void onCleanupApplicationRecordLSP() {
// Dismiss any open dialogs.
getDialogController().clearAllErrorDialogs();
setCrashing(false);
setNotResponding(false);
}
void dump(PrintWriter pw, String prefix, long nowUptime) {
synchronized (mProcLock) {
if (mCrashing || mDialogController.hasCrashDialogs() || mNotResponding
|| mDialogController.hasAnrDialogs() || mBad) {
pw.print(prefix);
pw.print(" mCrashing=" + mCrashing);
pw.print(" " + mDialogController.getCrashDialogs());
pw.print(" mNotResponding=" + mNotResponding);
pw.print(" " + mDialogController.getAnrDialogs());
pw.print(" bad=" + mBad);
// mCrashing or mNotResponding is always set before errorReportReceiver
if (mErrorReportReceiver != null) {
pw.print(" errorReportReceiver=");
pw.print(mErrorReportReceiver.flattenToShortString());
}
pw.println();
}
}
}
}