Merge "Merge "[GWP-ASan] Ensure recoverable crashes show up in AppExitInfo." am: 0a52acbd6c am: 3598f43e8f am: f6915b7b17" into udc-dev
diff --git a/core/proto/android/server/activitymanagerservice.proto b/core/proto/android/server/activitymanagerservice.proto
index ed612a0..025a57d 100644
--- a/core/proto/android/server/activitymanagerservice.proto
+++ b/core/proto/android/server/activitymanagerservice.proto
@@ -1035,6 +1035,7 @@
optional int32 uid = 1;
repeated .android.app.ApplicationExitInfoProto app_exit_info = 2;
+ repeated .android.app.ApplicationExitInfoProto app_recoverable_crash = 3;
}
repeated User users = 2;
}
diff --git a/services/core/java/com/android/server/am/ActivityManagerService.java b/services/core/java/com/android/server/am/ActivityManagerService.java
index 4a6c9b2..b32f8c9 100644
--- a/services/core/java/com/android/server/am/ActivityManagerService.java
+++ b/services/core/java/com/android/server/am/ActivityManagerService.java
@@ -8736,7 +8736,9 @@
// 'recoverable' is that the app doesn't crash). Normally, for nonrecoreable native crashes,
// debuggerd will terminate the process, but there's a backup where ActivityManager will
// also kill it. Avoid that.
- if (!recoverable) {
+ if (recoverable) {
+ mAppErrors.sendRecoverableCrashToAppExitInfo(r, crashInfo);
+ } else {
mAppErrors.crashApplication(r, crashInfo);
}
}
diff --git a/services/core/java/com/android/server/am/AppErrors.java b/services/core/java/com/android/server/am/AppErrors.java
index c343ec2..061bcd7 100644
--- a/services/core/java/com/android/server/am/AppErrors.java
+++ b/services/core/java/com/android/server/am/AppErrors.java
@@ -555,6 +555,15 @@
}
}
+ void sendRecoverableCrashToAppExitInfo(
+ ProcessRecord r, ApplicationErrorReport.CrashInfo crashInfo) {
+ if (r == null || crashInfo == null
+ || !"Native crash".equals(crashInfo.exceptionClassName)) return;
+ synchronized (mService) {
+ mService.mProcessList.noteAppRecoverableCrash(r);
+ }
+ }
+
/**
* Bring up the "unexpected error" dialog box for a crashing app.
* Deal with edge cases (intercepts from instrumented applications,
diff --git a/services/core/java/com/android/server/am/AppExitInfoTracker.java b/services/core/java/com/android/server/am/AppExitInfoTracker.java
index 4443636..4c0dd11 100644
--- a/services/core/java/com/android/server/am/AppExitInfoTracker.java
+++ b/services/core/java/com/android/server/am/AppExitInfoTracker.java
@@ -308,6 +308,16 @@
mKillHandler.obtainMessage(KillHandler.MSG_APP_KILL, raw).sendToTarget();
}
+ void scheduleNoteAppRecoverableCrash(final ProcessRecord app) {
+ if (!mAppExitInfoLoaded.get() || app == null || app.info == null) return;
+
+ ApplicationExitInfo raw = obtainRawRecord(app, System.currentTimeMillis());
+ raw.setReason(ApplicationExitInfo.REASON_CRASH_NATIVE);
+ raw.setSubReason(ApplicationExitInfo.SUBREASON_UNKNOWN);
+ raw.setDescription("recoverable_crash");
+ mKillHandler.obtainMessage(KillHandler.MSG_APP_RECOVERABLE_CRASH, raw).sendToTarget();
+ }
+
void scheduleNoteAppKill(final int pid, final int uid, final @Reason int reason,
final @SubReason int subReason, final String msg) {
if (!mAppExitInfoLoaded.get()) {
@@ -421,8 +431,24 @@
scheduleLogToStatsdLocked(info, true);
}
+ /**
+ * Make note when ActivityManagerService gets a recoverable native crash, as the process isn't
+ * being killed but the crash should still be added to AppExitInfo. Also, because we're not
+ * crashing, don't log out to statsd.
+ */
+ @VisibleForTesting
+ @GuardedBy("mLock")
+ void handleNoteAppRecoverableCrashLocked(final ApplicationExitInfo raw) {
+ addExitInfoLocked(raw, /* recoverable */ true);
+ }
+
@GuardedBy("mLock")
private ApplicationExitInfo addExitInfoLocked(ApplicationExitInfo raw) {
+ return addExitInfoLocked(raw, /* recoverable */ false);
+ }
+
+ @GuardedBy("mLock")
+ private ApplicationExitInfo addExitInfoLocked(ApplicationExitInfo raw, boolean recoverable) {
if (!mAppExitInfoLoaded.get()) {
Slog.w(TAG, "Skipping saving the exit info due to ongoing loading from storage");
return null;
@@ -438,7 +464,7 @@
}
}
for (int i = 0; i < packages.length; i++) {
- addExitInfoInnerLocked(packages[i], uid, info);
+ addExitInfoInnerLocked(packages[i], uid, info, recoverable);
}
schedulePersistProcessExitInfo(false);
@@ -845,7 +871,8 @@
}
@GuardedBy("mLock")
- private void addExitInfoInnerLocked(String packageName, int uid, ApplicationExitInfo info) {
+ private void addExitInfoInnerLocked(String packageName, int uid, ApplicationExitInfo info,
+ boolean recoverable) {
AppExitInfoContainer container = mData.get(packageName, uid);
if (container == null) {
container = new AppExitInfoContainer(mAppExitInfoHistoryListSize);
@@ -859,7 +886,11 @@
}
mData.put(packageName, uid, container);
}
- container.addExitInfoLocked(info);
+ if (recoverable) {
+ container.addRecoverableCrashLocked(info);
+ } else {
+ container.addExitInfoLocked(info);
+ }
}
@GuardedBy("mLock")
@@ -1284,38 +1315,40 @@
* A container class of {@link android.app.ApplicationExitInfo}
*/
final class AppExitInfoContainer {
- private SparseArray<ApplicationExitInfo> mInfos; // index is pid
+ private SparseArray<ApplicationExitInfo> mInfos; // index is a pid
+ private SparseArray<ApplicationExitInfo> mRecoverableCrashes; // index is a pid
private int mMaxCapacity;
private int mUid; // Application uid, not isolated uid.
AppExitInfoContainer(final int maxCapacity) {
mInfos = new SparseArray<ApplicationExitInfo>();
+ mRecoverableCrashes = new SparseArray<ApplicationExitInfo>();
mMaxCapacity = maxCapacity;
}
@GuardedBy("mLock")
- void getExitInfoLocked(final int filterPid, final int maxNum,
- ArrayList<ApplicationExitInfo> results) {
+ void getInfosLocked(SparseArray<ApplicationExitInfo> map, final int filterPid,
+ final int maxNum, ArrayList<ApplicationExitInfo> results) {
if (filterPid > 0) {
- ApplicationExitInfo r = mInfos.get(filterPid);
+ ApplicationExitInfo r = map.get(filterPid);
if (r != null) {
results.add(r);
}
} else {
- final int numRep = mInfos.size();
+ final int numRep = map.size();
if (maxNum <= 0 || numRep <= maxNum) {
// Return all records.
for (int i = 0; i < numRep; i++) {
- results.add(mInfos.valueAt(i));
+ results.add(map.valueAt(i));
}
Collections.sort(results,
(a, b) -> Long.compare(b.getTimestamp(), a.getTimestamp()));
} else {
if (maxNum == 1) {
// Most of the caller might be only interested with the most recent one
- ApplicationExitInfo r = mInfos.valueAt(0);
+ ApplicationExitInfo r = map.valueAt(0);
for (int i = 1; i < numRep; i++) {
- ApplicationExitInfo t = mInfos.valueAt(i);
+ ApplicationExitInfo t = map.valueAt(i);
if (r.getTimestamp() < t.getTimestamp()) {
r = t;
}
@@ -1326,7 +1359,7 @@
ArrayList<ApplicationExitInfo> list = mTmpInfoList2;
list.clear();
for (int i = 0; i < numRep; i++) {
- list.add(mInfos.valueAt(i));
+ list.add(map.valueAt(i));
}
Collections.sort(list,
(a, b) -> Long.compare(b.getTimestamp(), a.getTimestamp()));
@@ -1340,24 +1373,30 @@
}
@GuardedBy("mLock")
- void addExitInfoLocked(ApplicationExitInfo info) {
+ void getExitInfoLocked(final int filterPid, final int maxNum,
+ ArrayList<ApplicationExitInfo> results) {
+ getInfosLocked(mInfos, filterPid, maxNum, results);
+ }
+
+ @GuardedBy("mLock")
+ void addInfoLocked(SparseArray<ApplicationExitInfo> map, ApplicationExitInfo info) {
int size;
- if ((size = mInfos.size()) >= mMaxCapacity) {
+ if ((size = map.size()) >= mMaxCapacity) {
int oldestIndex = -1;
long oldestTimeStamp = Long.MAX_VALUE;
for (int i = 0; i < size; i++) {
- ApplicationExitInfo r = mInfos.valueAt(i);
+ ApplicationExitInfo r = map.valueAt(i);
if (r.getTimestamp() < oldestTimeStamp) {
oldestTimeStamp = r.getTimestamp();
oldestIndex = i;
}
}
if (oldestIndex >= 0) {
- final File traceFile = mInfos.valueAt(oldestIndex).getTraceFile();
+ final File traceFile = map.valueAt(oldestIndex).getTraceFile();
if (traceFile != null) {
traceFile.delete();
}
- mInfos.removeAt(oldestIndex);
+ map.removeAt(oldestIndex);
}
}
// Claim the state information if there is any
@@ -1367,7 +1406,17 @@
mActiveAppStateSummary, uid, pid));
info.setTraceFile(findAndRemoveFromSparse2dArray(mActiveAppTraces, uid, pid));
info.setAppTraceRetriever(mAppTraceRetriever);
- mInfos.append(pid, info);
+ map.append(pid, info);
+ }
+
+ @GuardedBy("mLock")
+ void addExitInfoLocked(ApplicationExitInfo info) {
+ addInfoLocked(mInfos, info);
+ }
+
+ @GuardedBy("mLock")
+ void addRecoverableCrashLocked(ApplicationExitInfo info) {
+ addInfoLocked(mRecoverableCrashes, info);
}
@GuardedBy("mLock")
@@ -1382,9 +1431,9 @@
}
@GuardedBy("mLock")
- void destroyLocked() {
- for (int i = mInfos.size() - 1; i >= 0; i--) {
- ApplicationExitInfo ai = mInfos.valueAt(i);
+ void destroyLocked(SparseArray<ApplicationExitInfo> map) {
+ for (int i = map.size() - 1; i >= 0; i--) {
+ ApplicationExitInfo ai = map.valueAt(i);
final File traceFile = ai.getTraceFile();
if (traceFile != null) {
traceFile.delete();
@@ -1395,24 +1444,37 @@
}
@GuardedBy("mLock")
+ void destroyLocked() {
+ destroyLocked(mInfos);
+ destroyLocked(mRecoverableCrashes);
+ }
+
+ @GuardedBy("mLock")
void forEachRecordLocked(final BiFunction<Integer, ApplicationExitInfo, Integer> callback) {
- if (callback != null) {
- for (int i = mInfos.size() - 1; i >= 0; i--) {
- switch (callback.apply(mInfos.keyAt(i), mInfos.valueAt(i))) {
- case FOREACH_ACTION_REMOVE_ITEM:
- final File traceFile = mInfos.valueAt(i).getTraceFile();
- if (traceFile != null) {
- traceFile.delete();
- }
- mInfos.removeAt(i);
- break;
- case FOREACH_ACTION_STOP_ITERATION:
- i = 0;
- break;
- case FOREACH_ACTION_NONE:
- default:
- break;
- }
+ if (callback == null) return;
+ for (int i = mInfos.size() - 1; i >= 0; i--) {
+ switch (callback.apply(mInfos.keyAt(i), mInfos.valueAt(i))) {
+ case FOREACH_ACTION_STOP_ITERATION: return;
+ case FOREACH_ACTION_REMOVE_ITEM:
+ final File traceFile = mInfos.valueAt(i).getTraceFile();
+ if (traceFile != null) {
+ traceFile.delete();
+ }
+ mInfos.removeAt(i);
+ break;
+ }
+ }
+ for (int i = mRecoverableCrashes.size() - 1; i >= 0; i--) {
+ switch (callback.apply(
+ mRecoverableCrashes.keyAt(i), mRecoverableCrashes.valueAt(i))) {
+ case FOREACH_ACTION_STOP_ITERATION: return;
+ case FOREACH_ACTION_REMOVE_ITEM:
+ final File traceFile = mRecoverableCrashes.valueAt(i).getTraceFile();
+ if (traceFile != null) {
+ traceFile.delete();
+ }
+ mRecoverableCrashes.removeAt(i);
+ break;
}
}
}
@@ -1423,6 +1485,9 @@
for (int i = mInfos.size() - 1; i >= 0; i--) {
list.add(mInfos.valueAt(i));
}
+ for (int i = mRecoverableCrashes.size() - 1; i >= 0; i--) {
+ list.add(mRecoverableCrashes.valueAt(i));
+ }
Collections.sort(list, (a, b) -> Long.compare(b.getTimestamp(), a.getTimestamp()));
int size = list.size();
for (int i = 0; i < size; i++) {
@@ -1434,10 +1499,13 @@
void writeToProto(ProtoOutputStream proto, long fieldId) {
long token = proto.start(fieldId);
proto.write(AppsExitInfoProto.Package.User.UID, mUid);
- int size = mInfos.size();
- for (int i = 0; i < size; i++) {
+ for (int i = 0; i < mInfos.size(); i++) {
mInfos.valueAt(i).writeToProto(proto, AppsExitInfoProto.Package.User.APP_EXIT_INFO);
}
+ for (int i = 0; i < mRecoverableCrashes.size(); i++) {
+ mRecoverableCrashes.valueAt(i).writeToProto(
+ proto, AppsExitInfoProto.Package.User.APP_RECOVERABLE_CRASH);
+ }
proto.end(token);
}
@@ -1448,14 +1516,23 @@
next != ProtoInputStream.NO_MORE_FIELDS;
next = proto.nextField()) {
switch (next) {
- case (int) AppsExitInfoProto.Package.User.UID:
+ case (int) AppsExitInfoProto.Package.User.UID: {
mUid = proto.readInt(AppsExitInfoProto.Package.User.UID);
break;
- case (int) AppsExitInfoProto.Package.User.APP_EXIT_INFO:
+ }
+ case (int) AppsExitInfoProto.Package.User.APP_EXIT_INFO: {
ApplicationExitInfo info = new ApplicationExitInfo();
info.readFromProto(proto, AppsExitInfoProto.Package.User.APP_EXIT_INFO);
mInfos.put(info.getPid(), info);
break;
+ }
+ case (int) AppsExitInfoProto.Package.User.APP_RECOVERABLE_CRASH: {
+ ApplicationExitInfo info = new ApplicationExitInfo();
+ info.readFromProto(
+ proto, AppsExitInfoProto.Package.User.APP_RECOVERABLE_CRASH);
+ mRecoverableCrashes.put(info.getPid(), info);
+ break;
+ }
}
}
proto.end(token);
@@ -1472,6 +1549,11 @@
list.add(mInfos.valueAt(i));
}
}
+ for (int i = mRecoverableCrashes.size() - 1; i >= 0; i--) {
+ if (filterPid == 0 || filterPid == mRecoverableCrashes.keyAt(i)) {
+ list.add(mRecoverableCrashes.valueAt(i));
+ }
+ }
return list;
}
}
@@ -1610,6 +1692,7 @@
static final int MSG_PROC_DIED = 4103;
static final int MSG_APP_KILL = 4104;
static final int MSG_STATSD_LOG = 4105;
+ static final int MSG_APP_RECOVERABLE_CRASH = 4106;
KillHandler(Looper looper) {
super(looper, null, true);
@@ -1648,6 +1731,14 @@
}
}
break;
+ case MSG_APP_RECOVERABLE_CRASH: {
+ ApplicationExitInfo raw = (ApplicationExitInfo) msg.obj;
+ synchronized (mLock) {
+ handleNoteAppRecoverableCrashLocked(raw);
+ }
+ recycleRawRecord(raw);
+ }
+ break;
default:
super.handleMessage(msg);
}
diff --git a/services/core/java/com/android/server/am/ProcessList.java b/services/core/java/com/android/server/am/ProcessList.java
index a237a07..312f98a 100644
--- a/services/core/java/com/android/server/am/ProcessList.java
+++ b/services/core/java/com/android/server/am/ProcessList.java
@@ -5205,6 +5205,17 @@
}
/**
+ * Called by ActivityManagerService when a recoverable native crash occurs.
+ */
+ @GuardedBy("mService")
+ void noteAppRecoverableCrash(final ProcessRecord app) {
+ if (DEBUG_PROCESSES) {
+ Slog.i(TAG, "note: " + app + " has a recoverable native crash");
+ }
+ mAppExitInfoTracker.scheduleNoteAppRecoverableCrash(app);
+ }
+
+ /**
* Called by ActivityManagerService when it decides to kill an application process.
*/
@GuardedBy("mService")