Add ANR Specific Subreasons to AppExitInfo

This update introduces specific ANR subreasons to the
ApplicationExitInfo subreason list. Additionally, TimeoutRecord is
modified to correlate its TimeoutKind with a corresponding
ApplicationExitInfo ANR subreason. The subreason is then passed to
ApplicationExitInfo objects when the system identifies an unresponsive
app to be used by apps for further analysis.

This change does not populate the ANR subreason when the user kills the
app from the ANR dialog.

Bug: 295599954
Test: atest CtsAppExitTestCases:ActivityManagerAppExitInfoTest
Flag: android.app.include_anr_subreason
Change-Id: I9e427ec74f7e149f6de050b5d3747de913e75ebc
diff --git a/core/java/android/app/ApplicationExitInfo.java b/core/java/android/app/ApplicationExitInfo.java
index 4a5770c..882f289 100644
--- a/core/java/android/app/ApplicationExitInfo.java
+++ b/core/java/android/app/ApplicationExitInfo.java
@@ -496,6 +496,119 @@
      */
     public static final int SUBREASON_EXCESSIVE_OUTGOING_BROADCASTS_WHILE_CACHED = 32;
 
+    /**
+     * The app itself due to its own internal logic or behavior has triggered an ANR.
+     *
+     * @hide
+     */
+    public static final int SUBREASON_ANR_TYPE_APP_TRIGGERED = 33;
+
+    /**
+     * The app took too long to start up.
+     *
+     * @hide
+     */
+    public static final int SUBREASON_ANR_TYPE_BIND_APPLICATION = 34;
+
+    /**
+     * The app's broadcast receiver took too long to process the message.
+     *
+     * @hide
+     */
+    public static final int SUBREASON_ANR_TYPE_BROADCAST_OF_INTENT = 35;
+
+    /**
+     * The app's content provider took too long to respond.
+     *
+     * @hide
+     */
+    public static final int SUBREASON_ANR_TYPE_CONTENT_PROVIDER_NOT_RESPONDING = 36;
+
+    /**
+     * The app's service took too long to finish Service.onCreate() and Service.onStartCommand() /
+     * Service.onBind()
+     *
+     * @hide
+     */
+    public static final int SUBREASON_ANR_TYPE_EXECUTING_SERVICE = 37;
+
+    /**
+     * Foreground service took too long to respond to onTimeout().
+     *
+     * @hide
+     */
+    public static final int SUBREASON_ANR_TYPE_FOREGROUND_SERVICE_TIMEOUT = 38;
+
+    /**
+     * A foreground short service took too long to respond to onTimeout().
+     *
+     * @hide
+     */
+    public static final int SUBREASON_ANR_TYPE_FOREGROUND_SHORT_SERVICE_TIMEOUT = 39;
+
+    /**
+     * Triggered when BLASTbufferQueue processing is hung.
+     *
+     * @hide
+     */
+    public static final int SUBREASON_ANR_TYPE_GPU_HANG = 40;
+
+    /**
+     * The app took too long to respond to an input event.
+     *
+     * @hide
+     */
+    public static final int SUBREASON_ANR_TYPE_INPUT_DISPATCHING_TIMEOUT = 41;
+
+    /**
+     * The app took too long to respond to an input event because no window was focused.
+     *
+     * @hide
+     */
+    public static final int SUBREASON_ANR_TYPE_INPUT_DISPATCHING_TIMEOUT_NO_FOCUSED_WINDOW = 42;
+
+    /**
+     * Job service took too long to bind.
+     *
+     * @hide
+     */
+    public static final int SUBREASON_ANR_TYPE_JOB_SERVICE_BIND = 43;
+
+    /**
+     * The job service took too long to send a notification.
+     *
+     * @hide
+     */
+    public static final int SUBREASON_ANR_TYPE_JOB_SERVICE_NOTIFICATION_NOT_PROVIDED = 44;
+
+    /**
+     * The job service took too long to start.
+     *
+     * @hide
+     */
+    public static final int SUBREASON_ANR_TYPE_JOB_SERVICE_START = 45;
+
+    /**
+     * The job service took too long to stop.
+     *
+     * @hide
+     */
+    public static final int SUBREASON_ANR_TYPE_JOB_SERVICE_STOP = 46;
+
+    /**
+     * The foreground service took too long to start.
+     *
+     * @hide
+     */
+    public static final int SUBREASON_ANR_TYPE_START_FOREGROUND_SERVICE = 47;
+
+    /**
+     * System server watchdog triggered ANR
+     *
+     * @hide
+     */
+    public static final int SUBREASON_ANR_TYPE_SYSTEM_SERVER_WATCHDOG_TIMEOUT = 48;
+
     // If there is any OEM code which involves additional app kill reasons, it should
     // be categorized in {@link #REASON_OTHER}, with subreason code starting from 1000.
 
@@ -691,6 +804,22 @@
         SUBREASON_OOM_KILL,
         SUBREASON_FREEZER_BINDER_ASYNC_FULL,
         SUBREASON_EXCESSIVE_OUTGOING_BROADCASTS_WHILE_CACHED,
+        SUBREASON_ANR_TYPE_APP_TRIGGERED,
+        SUBREASON_ANR_TYPE_BIND_APPLICATION,
+        SUBREASON_ANR_TYPE_BROADCAST_OF_INTENT,
+        SUBREASON_ANR_TYPE_CONTENT_PROVIDER_NOT_RESPONDING,
+        SUBREASON_ANR_TYPE_EXECUTING_SERVICE,
+        SUBREASON_ANR_TYPE_FOREGROUND_SERVICE_TIMEOUT,
+        SUBREASON_ANR_TYPE_FOREGROUND_SHORT_SERVICE_TIMEOUT,
+        SUBREASON_ANR_TYPE_GPU_HANG,
+        SUBREASON_ANR_TYPE_INPUT_DISPATCHING_TIMEOUT,
+        SUBREASON_ANR_TYPE_INPUT_DISPATCHING_TIMEOUT_NO_FOCUSED_WINDOW,
+        SUBREASON_ANR_TYPE_JOB_SERVICE_BIND,
+        SUBREASON_ANR_TYPE_JOB_SERVICE_NOTIFICATION_NOT_PROVIDED,
+        SUBREASON_ANR_TYPE_JOB_SERVICE_START,
+        SUBREASON_ANR_TYPE_JOB_SERVICE_STOP,
+        SUBREASON_ANR_TYPE_START_FOREGROUND_SERVICE,
+        SUBREASON_ANR_TYPE_SYSTEM_SERVER_WATCHDOG_TIMEOUT,
     })
     @Retention(RetentionPolicy.SOURCE)
     public @interface SubReason {
@@ -1457,6 +1586,38 @@
                 return "FREEZER BINDER ASYNC FULL";
             case SUBREASON_EXCESSIVE_OUTGOING_BROADCASTS_WHILE_CACHED:
                 return "EXCESSIVE_OUTGOING_BROADCASTS_WHILE_CACHED";
+            case SUBREASON_ANR_TYPE_APP_TRIGGERED:
+                return "APP TRIGGERED ANR";
+            case SUBREASON_ANR_TYPE_BIND_APPLICATION:
+                return "BIND APPLICATION ANR";
+            case SUBREASON_ANR_TYPE_BROADCAST_OF_INTENT:
+                return "BROADCAST OF INTENT ANR";
+            case SUBREASON_ANR_TYPE_CONTENT_PROVIDER_NOT_RESPONDING:
+                return "CONTENT PROVIDER NOT RESPONDING ANR";
+            case SUBREASON_ANR_TYPE_EXECUTING_SERVICE:
+                return "EXECUTING SERVICE ANR";
+            case SUBREASON_ANR_TYPE_FOREGROUND_SERVICE_TIMEOUT:
+                return "FOREGROUND SERVICE TIMEOUT ANR";
+            case SUBREASON_ANR_TYPE_FOREGROUND_SHORT_SERVICE_TIMEOUT:
+                return "FOREGROUND SHORT SERVICE TIMEOUT ANR";
+            case SUBREASON_ANR_TYPE_GPU_HANG:
+                return "GPU HANG ANR";
+            case SUBREASON_ANR_TYPE_INPUT_DISPATCHING_TIMEOUT:
+                return "INPUT DISPATCHING TIMEOUT ANR";
+            case SUBREASON_ANR_TYPE_INPUT_DISPATCHING_TIMEOUT_NO_FOCUSED_WINDOW:
+                return "INPUT DISPATCHING TIMEOUT NO FOCUSED WINDOW ANR";
+            case SUBREASON_ANR_TYPE_JOB_SERVICE_BIND:
+                return "JOB SERVICE BIND ANR";
+            case SUBREASON_ANR_TYPE_JOB_SERVICE_NOTIFICATION_NOT_PROVIDED:
+                return "JOB SERVICE NOTIFICATION NOT PROVIDED ANR";
+            case SUBREASON_ANR_TYPE_JOB_SERVICE_START:
+                return "JOB SERVICE START ANR";
+            case SUBREASON_ANR_TYPE_JOB_SERVICE_STOP:
+                return "JOB SERVICE STOP ANR";
+            case SUBREASON_ANR_TYPE_START_FOREGROUND_SERVICE:
+                return "START FOREGROUND SERVICE ANR";
+            case SUBREASON_ANR_TYPE_SYSTEM_SERVER_WATCHDOG_TIMEOUT:
+                return "SYSTEM SERVER WATCHDOG TIMEOUT ANR";
             case SUBREASON_UNKNOWN:
             default:
                 return "UNKNOWN";
diff --git a/core/java/com/android/internal/os/TimeoutRecord.java b/core/java/com/android/internal/os/TimeoutRecord.java
index 5f6e82f..ca9d80c 100644
--- a/core/java/com/android/internal/os/TimeoutRecord.java
+++ b/core/java/com/android/internal/os/TimeoutRecord.java
@@ -19,9 +19,12 @@
 import android.annotation.IntDef;
 import android.annotation.NonNull;
 import android.annotation.Nullable;
+import android.app.ApplicationExitInfo;
+import android.app.ApplicationExitInfo.SubReason;
 import android.content.ComponentName;
 import android.content.Intent;
 import android.os.SystemClock;
+import android.util.Slog;
 
 import com.android.internal.os.anr.AnrLatencyTracker;
 
@@ -45,6 +48,7 @@
             TimeoutKind.APP_REGISTERED,
             TimeoutKind.SHORT_FGS_TIMEOUT,
             TimeoutKind.JOB_SERVICE,
+            TimeoutKind.APP_START,
     })
 
     @Retention(RetentionPolicy.SOURCE)
@@ -83,6 +87,8 @@
     /** A handle to the timer that expired.  A value of null means "no timer". */
     private AutoCloseable mExpiredTimer;
 
+    private static final String TAG = "TimeoutRecord";
+
     private TimeoutRecord(@TimeoutKind int kind, @NonNull String reason, long endUptimeMillis,
             boolean endTakenBeforeLocks) {
         this.mKind = kind;
@@ -227,4 +233,42 @@
             throw new RuntimeException(e);
         }
     }
+
+    /**
+     * Maps a {@link TimeoutRecord.TimeoutKind} to its corresponding
+     * {@link ApplicationExitInfo.SubReason} for ANR (Application Not Responding)
+     * events.
+     *
+     * @return The {@link ApplicationExitInfo.SubReason} corresponding to the
+     *         internal {@code mKind}. Returns {@link ApplicationExitInfo#SUBREASON_UNKNOWN}
+     *         if the {@code mKind} does not match any known ANR subreason.
+     */
+    public @SubReason int getAppExitInfoAnrSubreason() {
+        return switch (mKind) {
+            case TimeoutRecord.TimeoutKind.APP_REGISTERED ->
+                    ApplicationExitInfo.SUBREASON_ANR_TYPE_APP_TRIGGERED;
+            case TimeoutRecord.TimeoutKind.APP_START ->
+                    ApplicationExitInfo.SUBREASON_ANR_TYPE_BIND_APPLICATION;
+            case TimeoutRecord.TimeoutKind.BROADCAST_RECEIVER ->
+                    ApplicationExitInfo.SUBREASON_ANR_TYPE_BROADCAST_OF_INTENT;
+            case TimeoutRecord.TimeoutKind.CONTENT_PROVIDER ->
+                    ApplicationExitInfo.SUBREASON_ANR_TYPE_CONTENT_PROVIDER_NOT_RESPONDING;
+            case TimeoutRecord.TimeoutKind.SERVICE_EXEC ->
+                    ApplicationExitInfo.SUBREASON_ANR_TYPE_EXECUTING_SERVICE;
+            case TimeoutRecord.TimeoutKind.SHORT_FGS_TIMEOUT ->
+                    ApplicationExitInfo.SUBREASON_ANR_TYPE_FOREGROUND_SHORT_SERVICE_TIMEOUT;
+            case TimeoutRecord.TimeoutKind.INPUT_DISPATCH_WINDOW_UNRESPONSIVE ->
+                    ApplicationExitInfo.SUBREASON_ANR_TYPE_INPUT_DISPATCHING_TIMEOUT;
+            case TimeoutRecord.TimeoutKind.INPUT_DISPATCH_NO_FOCUSED_WINDOW -> ApplicationExitInfo
+                    .SUBREASON_ANR_TYPE_INPUT_DISPATCHING_TIMEOUT_NO_FOCUSED_WINDOW;
+            case TimeoutRecord.TimeoutKind.JOB_SERVICE ->
+                    ApplicationExitInfo.SUBREASON_ANR_TYPE_JOB_SERVICE_START;
+            case TimeoutRecord.TimeoutKind.SERVICE_START ->
+                    ApplicationExitInfo.SUBREASON_ANR_TYPE_START_FOREGROUND_SERVICE;
+            default -> {
+                Slog.e(TAG, "Unknown TimeoutKind: " + mKind);
+                yield ApplicationExitInfo.SUBREASON_UNKNOWN;
+            }
+        };
+    }
 }
diff --git a/services/core/java/com/android/server/am/ProcessErrorStateRecord.java b/services/core/java/com/android/server/am/ProcessErrorStateRecord.java
index 17fcbf4..fb5340f 100644
--- a/services/core/java/com/android/server/am/ProcessErrorStateRecord.java
+++ b/services/core/java/com/android/server/am/ProcessErrorStateRecord.java
@@ -18,11 +18,11 @@
 
 import static android.os.Process.SYSTEM_UID;
 
+import static com.android.internal.os.ProcfsMemoryUtil.readMemorySnapshotFromProcfs;
 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 static com.android.internal.os.ProcfsMemoryUtil.readMemorySnapshotFromProcfs;
 
 import android.annotation.Nullable;
 import android.app.ActivityManager;
@@ -52,13 +52,13 @@
 import com.android.internal.annotations.GuardedBy;
 import com.android.internal.annotations.VisibleForTesting;
 import com.android.internal.os.ProcessCpuTracker;
+import com.android.internal.os.ProcfsMemoryUtil.MemorySnapshot;
 import com.android.internal.os.TimeoutRecord;
 import com.android.internal.os.anr.AnrLatencyTracker;
 import com.android.internal.util.FrameworkStatsLog;
 import com.android.modules.expresslog.Counter;
 import com.android.server.ResourcePressureUtil;
 import com.android.server.criticalevents.CriticalEventLog;
-import com.android.internal.os.ProcfsMemoryUtil.MemorySnapshot;
 import com.android.server.wm.WindowProcessController;
 
 import java.io.File;
@@ -317,7 +317,12 @@
                 latencyTracker.waitingOnAMSLockEnded();
                 // Store annotation here as instance below races with this killLocked.
                 setAnrAnnotation(annotation);
-                mApp.killLocked("anr", ApplicationExitInfo.REASON_ANR, true);
+                if (android.app.Flags.includeAnrSubreason()) {
+                    mApp.killLocked("anr", ApplicationExitInfo.REASON_ANR,
+                            timeoutRecord.getAppExitInfoAnrSubreason(), true);
+                } else {
+                    mApp.killLocked("anr", ApplicationExitInfo.REASON_ANR, true);
+                }
             }
         });
 
@@ -651,7 +656,12 @@
         if (mApp.getWindowProcessController().appNotResponding(info.toString(),
                 () -> {
                     synchronized (mService) {
-                        mApp.killLocked("anr", ApplicationExitInfo.REASON_ANR, true);
+                        if (android.app.Flags.includeAnrSubreason()) {
+                            mApp.killLocked("anr", ApplicationExitInfo.REASON_ANR,
+                                    timeoutRecord.getAppExitInfoAnrSubreason(), true);
+                        } else {
+                            mApp.killLocked("anr", ApplicationExitInfo.REASON_ANR, true);
+                        }
                     }
                 },
                 () -> {
@@ -670,7 +680,12 @@
             }
 
             if (isSilentAnr() && !mApp.isDebugging()) {
-                mApp.killLocked("bg anr", ApplicationExitInfo.REASON_ANR, true);
+                if (android.app.Flags.includeAnrSubreason()) {
+                    mApp.killLocked("bg anr", ApplicationExitInfo.REASON_ANR,
+                            timeoutRecord.getAppExitInfoAnrSubreason(), true);
+                } else {
+                    mApp.killLocked("bg anr", ApplicationExitInfo.REASON_ANR, true);
+                }
                 return;
             }