diff --git a/proto/src/metrics_constants.proto b/proto/src/metrics_constants.proto index cd026a8b40a42..49972d6541f8d 100644 --- a/proto/src/metrics_constants.proto +++ b/proto/src/metrics_constants.proto @@ -441,5 +441,11 @@ message MetricsEvent { // Tuner: Change do not disturb volume buttons shortcut. ACTION_TUNER_DO_NOT_DISTURB_VOLUME_SHORTCUT = 315; + + // Logs the action the user takes when an app crashed. + ACTION_APP_CRASH = 316; + + // Logs the action the user takes when an app ANR'd. + ACTION_APP_ANR = 317; } } diff --git a/services/core/java/com/android/server/am/AppErrorDialog.java b/services/core/java/com/android/server/am/AppErrorDialog.java index b746a4b6b11cb..86cdbcc43edf3 100644 --- a/services/core/java/com/android/server/am/AppErrorDialog.java +++ b/services/core/java/com/android/server/am/AppErrorDialog.java @@ -37,6 +37,7 @@ 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; @@ -44,12 +45,17 @@ final class AppErrorDialog extends BaseErrorDialog implements View.OnClickListen private CharSequence mName; + static int CANT_SHOW = -1; + static int BACKGROUND_USER = -2; + static int ALREADY_SHOWING = -3; + // Event 'what' codes 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; + static final int TIMEOUT = 6; // 5-minute timeout, then we automatically dismiss the crash dialog static final long DISMISS_TIMEOUT = 1000 * 60 * 5; @@ -89,7 +95,7 @@ final class AppErrorDialog extends BaseErrorDialog implements View.OnClickListen // After the timeout, pretend the user clicked the quit button mHandler.sendMessageDelayed( - mHandler.obtainMessage(FORCE_QUIT), + mHandler.obtainMessage(TIMEOUT), DISMISS_TIMEOUT); } @@ -132,7 +138,7 @@ final class AppErrorDialog extends BaseErrorDialog implements View.OnClickListen mResult.set(result); // Make sure we don't have time timeout still hanging around. - removeMessages(FORCE_QUIT); + removeMessages(TIMEOUT); dismiss(); } diff --git a/services/core/java/com/android/server/am/AppErrors.java b/services/core/java/com/android/server/am/AppErrors.java index 190e9e1141b02..055935dd995ee 100644 --- a/services/core/java/com/android/server/am/AppErrors.java +++ b/services/core/java/com/android/server/am/AppErrors.java @@ -17,6 +17,8 @@ package com.android.server.am; import com.android.internal.app.ProcessMap; +import com.android.internal.logging.MetricsLogger; +import com.android.internal.logging.MetricsProto; import com.android.internal.os.ProcessCpuTracker; import com.android.server.Watchdog; @@ -403,6 +405,10 @@ class AppErrors { Intent appErrorIntent = null; final long ident = Binder.clearCallingIdentity(); try { + MetricsLogger.action(mContext, MetricsProto.MetricsEvent.ACTION_APP_CRASH, res); + if (res == AppErrorDialog.TIMEOUT) { + res = AppErrorDialog.FORCE_QUIT; + } if (res == AppErrorDialog.RESET) { String[] packageList = r.getPackageList(); if (packageList != null) { @@ -697,7 +703,7 @@ class AppErrors { if (proc != null && proc.crashDialog != null) { Slog.e(TAG, "App already has crash dialog: " + proc); if (res != null) { - res.set(0); + res.set(AppErrorDialog.ALREADY_SHOWING); } return; } @@ -710,7 +716,7 @@ class AppErrors { if (isBackground && !showBackground) { Slog.w(TAG, "Skipping crash dialog of " + proc + ": background"); if (res != null) { - res.set(0); + res.set(AppErrorDialog.BACKGROUND_USER); } return; } @@ -724,7 +730,7 @@ class AppErrors { // The device is asleep, so just pretend that the user // saw a crash dialog and hit "force quit". if (res != null) { - res.set(0); + res.set(AppErrorDialog.CANT_SHOW); } } } @@ -920,6 +926,8 @@ class AppErrors { ProcessRecord proc = (ProcessRecord)data.get("app"); if (proc != null && proc.anrDialog != null) { Slog.e(TAG, "App already has anr dialog: " + proc); + MetricsLogger.action(mContext, MetricsProto.MetricsEvent.ACTION_APP_ANR, + AppNotRespondingDialog.ALREADY_SHOWING); return; } @@ -939,6 +947,8 @@ class AppErrors { d.show(); proc.anrDialog = d; } else { + MetricsLogger.action(mContext, MetricsProto.MetricsEvent.ACTION_APP_ANR, + AppNotRespondingDialog.CANT_SHOW); // Just kill the app if there is no dialog to be shown. mService.killAppAtUsersRequest(proc, null); } diff --git a/services/core/java/com/android/server/am/AppNotRespondingDialog.java b/services/core/java/com/android/server/am/AppNotRespondingDialog.java index 987588759ae84..6d1d9f38c0553 100644 --- a/services/core/java/com/android/server/am/AppNotRespondingDialog.java +++ b/services/core/java/com/android/server/am/AppNotRespondingDialog.java @@ -16,6 +16,9 @@ package com.android.server.am; +import com.android.internal.logging.MetricsLogger; +import com.android.internal.logging.MetricsProto; + import android.content.ActivityNotFoundException; import android.content.Context; import android.content.DialogInterface; @@ -41,6 +44,9 @@ final class AppNotRespondingDialog extends BaseErrorDialog implements View.OnCli static final int WAIT = 2; static final int WAIT_AND_REPORT = 3; + public static final int CANT_SHOW = -1; + public static final int ALREADY_SHOWING = -2; + private final ActivityManagerService mService; private final ProcessRecord mProc; @@ -132,6 +138,10 @@ final class AppNotRespondingDialog extends BaseErrorDialog implements View.OnCli private final Handler mHandler = new Handler() { public void handleMessage(Message msg) { Intent appErrorIntent = null; + + MetricsLogger.action(getContext(), MetricsProto.MetricsEvent.ACTION_APP_ANR, + msg.what); + switch (msg.what) { case FORCE_CLOSE: // Kill the application.