diff --git a/core/java/android/app/ApplicationExitInfo.java b/core/java/android/app/ApplicationExitInfo.java index c97ffbcfe5e71..4f94c9b5045b6 100644 --- a/core/java/android/app/ApplicationExitInfo.java +++ b/core/java/android/app/ApplicationExitInfo.java @@ -444,6 +444,13 @@ public final class ApplicationExitInfo implements Parcelable { */ private IParcelFileDescriptorRetriever mNativeTombstoneRetriever; + /** + * Whether or not we've logged this into the statsd. + * + * for system internal use only, will not retain across processes. + */ + private boolean mLoggedInStatsd; + /** @hide */ @IntDef(prefix = { "REASON_" }, value = { REASON_UNKNOWN, @@ -890,6 +897,24 @@ public final class ApplicationExitInfo implements Parcelable { mNativeTombstoneRetriever = retriever; } + /** + * @see #mLoggedInStatsd + * + * @hide + */ + public boolean isLoggedInStatsd() { + return mLoggedInStatsd; + } + + /** + * @see #mLoggedInStatsd + * + * @hide + */ + public void setLoggedInStatsd(boolean loggedInStatsd) { + mLoggedInStatsd = loggedInStatsd; + } + @Override public int describeContents() { return 0; diff --git a/core/proto/android/app/appexit_enums.proto b/core/proto/android/app/appexit_enums.proto deleted file mode 100644 index 298d06213806f..0000000000000 --- a/core/proto/android/app/appexit_enums.proto +++ /dev/null @@ -1,245 +0,0 @@ -/* - * 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. - */ - -syntax = "proto2"; -option java_multiple_files = true; - -package android.app; - -/** - * The reason code that why app process is killed. - */ -enum AppExitReasonCode { - /** - * Application process died due to unknown reason. - */ - REASON_UNKNOWN = 0; - - /** - * Application process exit normally by itself, for example, - * via {@link android.os.Process#exit}; {@link #status} will specify the exit code. - * - *
Applications should normally not do this, as the system has a better knowledge - * in terms of process management.
- */ - REASON_EXIT_SELF = 1; - - /** - * Application process died due to the result of an OS signal; for example, - * {@link android.os.Process#SIGNAL_KILL}; {@link #status} will specify the signum. - */ - REASON_SIGNALED = 2; - - /** - * Application process was killed by the system low memory killer, meaning the system was - * under memory pressure at the time of kill. - */ - REASON_LOW_MEMORY = 3; - - /** - * Application process died because of an unhandled exception in Java code. - */ - REASON_CRASH = 4; - - /** - * Application process died because it's crashed due to a native code crash. - */ - REASON_CRASH_NATIVE = 5; - - /** - * Application process was killed due to being unresponsive (ANR). - */ - REASON_ANR = 6; - - /** - * Application process was killed because it took too long to attach to the system - * during the start. - */ - REASON_INITIALIZATION_FAILURE = 7; - - /** - * Application process was killed because of initialization failure, - * for example, it took too long to attach to the system during the start, - * or there was an error during initialization. - */ - REASON_PERMISSION_CHANGE = 8; - - /** - * Application process was killed by the activity manager due to excessive resource usage. - */ - REASON_EXCESSIVE_RESOURCE_USAGE = 9; - - /** - * Application process was killed because of the user request, for example, - * user clicked the "Force stop" button of the application in the Settings, - * or swiped away the application from Recents. - */ - REASON_USER_REQUESTED = 10; - - /** - * Application process was killed, because the user they are running as on devices - * with mutlple users, was stopped. - */ - REASON_USER_STOPPED = 11; - - /** - * Application process was killed because its dependency was going away, for example, - * a stable content provider connection's client will be killed if the provider is killed. - */ - REASON_DEPENDENCY_DIED = 12; - - /** - * Application process was killed by the system for various other reasons, - * for example, the application package got disabled by the user; - * {@link #description} will specify the cause given by the system. - */ - REASON_OTHER = 13; -} - -/** - * The supplemental reason code that why app process is killed - */ -enum AppExitSubReasonCode { - /** - * Application process kills subReason is unknown. - */ - SUBREASON_UNKNOWN = 0; - - /** - * Application process was killed because user quit it on the "wait for debugger" dialog. - */ - SUBREASON_WAIT_FOR_DEBUGGER = 1; - - /** - * Application process was killed by the activity manager because there were too many cached - * processes. - */ - SUBREASON_TOO_MANY_CACHED = 2; - - /** - * Application process was killed by the activity manager because there were too many empty - * processes. - */ - SUBREASON_TOO_MANY_EMPTY = 3; - - /** - * Application process was killed by the activity manager because there were too many cached - * processes and this process had been in empty state for a long time. - */ - SUBREASON_TRIM_EMPTY = 4; - - /** - * Application process was killed by the activity manager because system was on - * memory pressure and this process took large amount of cached memory. - */ - SUBREASON_LARGE_CACHED = 5; - - /** - * Application process was killed by the activity manager because the system was on - * low memory pressure for a significant amount of time since last idle. - */ - SUBREASON_MEMORY_PRESSURE = 6; - - /** - * Application process was killed by the activity manager due to excessive CPU usage. - */ - SUBREASON_EXCESSIVE_CPU = 7; - - /** - * System update has done (so the system update process should be killed). - */ - SUBREASON_SYSTEM_UPDATE_DONE = 8; - - /** - * Kill all foreground services, for now it only occurs when enabling the quiet - * mode for the managed profile. - */ - SUBREASON_KILL_ALL_FG = 9; - - /** - * All background processes except certain ones were killed, for now it only occurs - * when the density of the default display is changed. - */ - SUBREASON_KILL_ALL_BG_EXCEPT = 10; - - /** - * The process associated with the UID was explicitly killed, for example, - * it could be because of permission changes. - */ - SUBREASON_KILL_UID = 11; - - /** - * The process was explicitly killed with its PID, typically because of - * the low memory for surfaces. - */ - SUBREASON_KILL_PID = 12; - - /** - * The start of the process was invalid. - */ - SUBREASON_INVALID_START = 13; - - /** - * The process was killed because it's in an invalid state, typically - * it's triggered from SHELL. - */ - SUBREASON_INVALID_STATE = 14; - - /** - * The process was killed when it's imperceptible to user, because it was - * in a bad state. - */ - SUBREASON_IMPERCEPTIBLE = 15; - - /** - * The process was killed because it's being moved out from LRU list. - */ - SUBREASON_REMOVE_LRU = 16; - - /** - * The process was killed because it's isolated and was in a cached state. - */ - SUBREASON_ISOLATED_NOT_NEEDED = 17; - - /** - * The process was killed because it's in forced-app-standby state, and it's cached and - * its uid state is idle; this would be set only when the reason is {@link #REASON_OTHER}. - */ - SUBREASON_CACHED_IDLE_FORCED_APP_STANDBY = 18; -} - -/** * The relative importance level that the system places on a process. - * Keep sync with the definitions in - * {@link android.app.ActivityManager.RunningAppProcessInfo} - */ -enum Importance { - option allow_alias = true; - - IMPORTANCE_FOREGROUND = 100; - IMPORTANCE_FOREGROUND_SERVICE = 125; - IMPORTANCE_TOP_SLEEPING_PRE_28 = 150; - IMPORTANCE_VISIBLE = 200; - IMPORTANCE_PERCEPTIBLE_PRE_26 = 130; - IMPORTANCE_PERCEPTIBLE = 230; - IMPORTANCE_CANT_SAVE_STATE_PRE_26 = 170; - IMPORTANCE_SERVICE = 300; - IMPORTANCE_TOP_SLEEPING = 325; - IMPORTANCE_CANT_SAVE_STATE = 350; - IMPORTANCE_CACHED = 400; - IMPORTANCE_BACKGROUND = 400; - IMPORTANCE_EMPTY = 500; - IMPORTANCE_GONE = 1000; -} diff --git a/core/proto/android/app/appexitinfo.proto b/core/proto/android/app/appexitinfo.proto index cc3d367ab837e..3abc462671a20 100644 --- a/core/proto/android/app/appexitinfo.proto +++ b/core/proto/android/app/appexitinfo.proto @@ -20,7 +20,7 @@ option java_multiple_files = true; package android.app; import "frameworks/base/core/proto/android/privacy.proto"; -import "frameworks/base/core/proto/android/app/appexit_enums.proto"; +import "frameworks/proto_logging/stats/enums/app/enums.proto"; /** * An android.app.ApplicationExitInfo object. diff --git a/services/core/java/com/android/server/am/AppExitInfoTracker.java b/services/core/java/com/android/server/am/AppExitInfoTracker.java index b85d7292e7386..da06753ef0f46 100644 --- a/services/core/java/com/android/server/am/AppExitInfoTracker.java +++ b/services/core/java/com/android/server/am/AppExitInfoTracker.java @@ -61,6 +61,7 @@ import com.android.internal.annotations.GuardedBy; import com.android.internal.annotations.VisibleForTesting; import com.android.internal.app.ProcessMap; import com.android.internal.util.ArrayUtils; +import com.android.internal.util.FrameworkStatsLog; import com.android.internal.util.function.pooled.PooledLambda; import com.android.server.IoThread; import com.android.server.LocalServices; @@ -108,6 +109,13 @@ public final class AppExitInfoTracker { private static final int APP_EXIT_RAW_INFO_POOL_SIZE = 8; + /** + * How long we're going to hold before logging an app exit info into statsd; + * we do this is because there could be multiple sources signaling an app exit, we'd like to + * gather the most accurate information before logging into statsd. + */ + private static final long APP_EXIT_INFO_STATSD_LOG_DEBOUNCE = TimeUnit.SECONDS.toMillis(15); + @VisibleForTesting static final String APP_EXIT_STORE_DIR = "procexitstore"; @@ -384,6 +392,8 @@ public final class AppExitInfoTracker { ApplicationExitInfo.REASON_LOW_MEMORY); } else if (zygote != null) { updateExistingExitInfoRecordLocked(info, (Integer) zygote.second, null); + } else { + scheduleLogToStatsdLocked(info, false); } } } @@ -398,7 +408,7 @@ public final class AppExitInfoTracker { raw.getPackageName(), raw.getPackageUid(), raw.getPid()); if (info == null) { - addExitInfoLocked(raw); + info = addExitInfoLocked(raw); } else { // always override the existing info since we are now more informational. info.setReason(raw.getReason()); @@ -407,6 +417,7 @@ public final class AppExitInfoTracker { info.setTimestamp(System.currentTimeMillis()); info.setDescription(raw.getDescription()); } + scheduleLogToStatsdLocked(info, true); } @GuardedBy("mLock") @@ -438,22 +449,29 @@ public final class AppExitInfoTracker { // if the record is way outdated, don't update it then (because of potential pid reuse) return; } + boolean immediateLog = false; if (status != null) { if (OsConstants.WIFEXITED(status)) { info.setReason(ApplicationExitInfo.REASON_EXIT_SELF); info.setStatus(OsConstants.WEXITSTATUS(status)); + immediateLog = true; } else if (OsConstants.WIFSIGNALED(status)) { if (info.getReason() == ApplicationExitInfo.REASON_UNKNOWN) { info.setReason(ApplicationExitInfo.REASON_SIGNALED); info.setStatus(OsConstants.WTERMSIG(status)); } else if (info.getReason() == ApplicationExitInfo.REASON_CRASH_NATIVE) { info.setStatus(OsConstants.WTERMSIG(status)); + immediateLog = true; } } } if (reason != null) { info.setReason(reason); + if (reason == ApplicationExitInfo.REASON_LOW_MEMORY) { + immediateLog = true; + } } + scheduleLogToStatsdLocked(info, immediateLog); } /** @@ -836,6 +854,40 @@ public final class AppExitInfoTracker { container.addExitInfoLocked(info); } + @GuardedBy("mLock") + private void scheduleLogToStatsdLocked(ApplicationExitInfo info, boolean immediate) { + if (info.isLoggedInStatsd()) { + return; + } + if (immediate) { + mKillHandler.removeMessages(KillHandler.MSG_STATSD_LOG, info); + performLogToStatsdLocked(info); + } else if (!mKillHandler.hasMessages(KillHandler.MSG_STATSD_LOG, info)) { + mKillHandler.sendMessageDelayed(mKillHandler.obtainMessage( + KillHandler.MSG_STATSD_LOG, info), APP_EXIT_INFO_STATSD_LOG_DEBOUNCE); + } + } + + @GuardedBy("mLock") + private void performLogToStatsdLocked(ApplicationExitInfo info) { + if (info.isLoggedInStatsd()) { + return; + } + info.setLoggedInStatsd(true); + final String pkgName = info.getPackageName(); + String processName = info.getProcessName(); + if (TextUtils.equals(pkgName, processName)) { + // Omit the process name here to save space + processName = null; + } else if (processName != null && processName.startsWith(pkgName)) { + // Strip the prefix to save space + processName = processName.substring(pkgName.length()); + } + FrameworkStatsLog.write(FrameworkStatsLog.APP_PROCESS_DIED, + info.getPackageUid(), processName, info.getReason(), info.getSubReason(), + info.getImportance(), (int) info.getPss(), (int) info.getRss()); + } + @GuardedBy("mLock") private void forEachPackageLocked( BiFunction