Merge "Add initial logging for App Background Restrictions metrics." into tm-dev
diff --git a/apex/jobscheduler/framework/java/android/os/PowerExemptionManager.java b/apex/jobscheduler/framework/java/android/os/PowerExemptionManager.java
index c4795f5..53a3889 100644
--- a/apex/jobscheduler/framework/java/android/os/PowerExemptionManager.java
+++ b/apex/jobscheduler/framework/java/android/os/PowerExemptionManager.java
@@ -32,6 +32,8 @@
 import android.annotation.UserHandleAware;
 import android.content.Context;
 
+import com.android.internal.util.FrameworkStatsLog;
+
 import java.lang.annotation.Retention;
 import java.lang.annotation.RetentionPolicy;
 import java.util.Collections;
@@ -451,6 +453,62 @@
     @Retention(RetentionPolicy.SOURCE)
     public @interface ReasonCode {}
 
+    private static final int EXEMPTION_REASON_SYSTEM_UID = FrameworkStatsLog
+            .APP_BACKGROUND_RESTRICTIONS_INFO__EXEMPTION_REASON__REASON_SYSTEM_UID;
+    private static final int EXEMPTION_REASON_ALLOWLISTED_PACKAGE = FrameworkStatsLog
+            .APP_BACKGROUND_RESTRICTIONS_INFO__EXEMPTION_REASON__REASON_ALLOWLISTED_PACKAGE;
+    private static final int EXEMPTION_REASON_COMPANION_DEVICE_MANAGER = FrameworkStatsLog
+            .APP_BACKGROUND_RESTRICTIONS_INFO__EXEMPTION_REASON__REASON_COMPANION_DEVICE_MANAGER;
+    private static final int EXEMPTION_REASON_DEVICE_DEMO_MODE = FrameworkStatsLog
+            .APP_BACKGROUND_RESTRICTIONS_INFO__EXEMPTION_REASON__REASON_DEVICE_DEMO_MODE;
+    private static final int EXEMPTION_REASON_DEVICE_OWNER = FrameworkStatsLog
+            .APP_BACKGROUND_RESTRICTIONS_INFO__EXEMPTION_REASON__REASON_DEVICE_OWNER;
+    private static final int EXEMPTION_REASON_PROFILE_OWNER = FrameworkStatsLog
+            .APP_BACKGROUND_RESTRICTIONS_INFO__EXEMPTION_REASON__REASON_PROFILE_OWNER;
+    private static final int EXEMPTION_REASON_PROC_STATE_PERSISTENT = FrameworkStatsLog
+            .APP_BACKGROUND_RESTRICTIONS_INFO__EXEMPTION_REASON__REASON_PROC_STATE_PERSISTENT;
+    private static final int EXEMPTION_REASON_PROC_STATE_PERSISTENT_UI = FrameworkStatsLog
+            .APP_BACKGROUND_RESTRICTIONS_INFO__EXEMPTION_REASON__REASON_PROC_STATE_PERSISTENT_UI;
+    private static final int EXEMPTION_REASON_OP_ACTIVATE_VPN = FrameworkStatsLog
+            .APP_BACKGROUND_RESTRICTIONS_INFO__EXEMPTION_REASON__REASON_OP_ACTIVATE_VPN;
+    private static final int EXEMPTION_REASON_OP_ACTIVATE_PLATFORM_VPN = FrameworkStatsLog
+            .APP_BACKGROUND_RESTRICTIONS_INFO__EXEMPTION_REASON__REASON_OP_ACTIVATE_PLATFORM_VPN;
+    private static final int EXEMPTION_REASON_SYSTEM_MODULE = FrameworkStatsLog
+            .APP_BACKGROUND_RESTRICTIONS_INFO__EXEMPTION_REASON__REASON_SYSTEM_MODULE;
+    private static final int EXEMPTION_REASON_CARRIER_PRIVILEGED_APP = FrameworkStatsLog
+            .APP_BACKGROUND_RESTRICTIONS_INFO__EXEMPTION_REASON__REASON_CARRIER_PRIVILEGED_APP;
+    private static final int EXEMPTION_REASON_SYSTEM_ALLOW_LISTED = FrameworkStatsLog
+            .APP_BACKGROUND_RESTRICTIONS_INFO__EXEMPTION_REASON__REASON_SYSTEM_ALLOW_LISTED;
+    private static final int EXEMPTION_REASON_ROLE_DIALER = FrameworkStatsLog
+            .APP_BACKGROUND_RESTRICTIONS_INFO__EXEMPTION_REASON__REASON_ROLE_DIALER;
+    private static final int EXEMPTION_REASON_ROLE_EMERGENCY = FrameworkStatsLog
+            .APP_BACKGROUND_RESTRICTIONS_INFO__EXEMPTION_REASON__REASON_ROLE_EMERGENCY;
+    private static final int EXEMPTION_REASON_DENIED = FrameworkStatsLog
+            .APP_BACKGROUND_RESTRICTIONS_INFO__EXEMPTION_REASON__REASON_DENIED;
+    /**
+     * List of exemption reason codes used for statsd logging in AppBackgroundRestrictionsInfo atom.
+     * @hide
+     */
+    @IntDef(prefix = { "EXEMPTION_REASON_" }, value = {
+            EXEMPTION_REASON_SYSTEM_UID,
+            EXEMPTION_REASON_ALLOWLISTED_PACKAGE,
+            EXEMPTION_REASON_COMPANION_DEVICE_MANAGER,
+            EXEMPTION_REASON_DEVICE_DEMO_MODE,
+            EXEMPTION_REASON_DEVICE_OWNER,
+            EXEMPTION_REASON_PROFILE_OWNER,
+            EXEMPTION_REASON_PROC_STATE_PERSISTENT,
+            EXEMPTION_REASON_PROC_STATE_PERSISTENT_UI,
+            EXEMPTION_REASON_OP_ACTIVATE_VPN,
+            EXEMPTION_REASON_OP_ACTIVATE_PLATFORM_VPN,
+            EXEMPTION_REASON_SYSTEM_MODULE,
+            EXEMPTION_REASON_CARRIER_PRIVILEGED_APP,
+            EXEMPTION_REASON_SYSTEM_ALLOW_LISTED,
+            EXEMPTION_REASON_ROLE_DIALER,
+            EXEMPTION_REASON_ROLE_EMERGENCY,
+            EXEMPTION_REASON_DENIED,
+    })
+    public @interface ExemptionReason {}
+
     /**
      * @hide
      */
@@ -618,6 +676,47 @@
     }
 
     /**
+     * @hide
+     * @return the reason code mapped to statsd for the AppBackgroundRestrictionsInfo atom.
+     */
+    public static @ExemptionReason int getExemptionReasonForStatsd(@ReasonCode int reasonCode) {
+        switch (reasonCode) {
+            case REASON_SYSTEM_UID:
+                return EXEMPTION_REASON_SYSTEM_UID;
+            case REASON_ALLOWLISTED_PACKAGE:
+                return EXEMPTION_REASON_ALLOWLISTED_PACKAGE;
+            case REASON_COMPANION_DEVICE_MANAGER:
+                return EXEMPTION_REASON_COMPANION_DEVICE_MANAGER;
+            case REASON_DEVICE_DEMO_MODE:
+                return EXEMPTION_REASON_DEVICE_DEMO_MODE;
+            case REASON_DEVICE_OWNER:
+                return EXEMPTION_REASON_DEVICE_OWNER;
+            case REASON_PROFILE_OWNER:
+                return EXEMPTION_REASON_PROFILE_OWNER;
+            case REASON_PROC_STATE_PERSISTENT:
+                return EXEMPTION_REASON_PROC_STATE_PERSISTENT;
+            case REASON_PROC_STATE_PERSISTENT_UI:
+                return EXEMPTION_REASON_PROC_STATE_PERSISTENT_UI;
+            case REASON_OP_ACTIVATE_VPN:
+                return EXEMPTION_REASON_OP_ACTIVATE_VPN;
+            case REASON_OP_ACTIVATE_PLATFORM_VPN:
+                return EXEMPTION_REASON_OP_ACTIVATE_PLATFORM_VPN;
+            case REASON_SYSTEM_MODULE:
+                return EXEMPTION_REASON_SYSTEM_MODULE;
+            case REASON_CARRIER_PRIVILEGED_APP:
+                return EXEMPTION_REASON_CARRIER_PRIVILEGED_APP;
+            case REASON_SYSTEM_ALLOW_LISTED:
+                return EXEMPTION_REASON_SYSTEM_ALLOW_LISTED;
+            case REASON_ROLE_DIALER:
+                return EXEMPTION_REASON_ROLE_DIALER;
+            case REASON_ROLE_EMERGENCY:
+                return EXEMPTION_REASON_ROLE_EMERGENCY;
+            default:
+                return EXEMPTION_REASON_DENIED;
+        }
+    }
+
+    /**
      * Return string name of the integer reason code.
      * @hide
      * @param reasonCode
diff --git a/services/core/java/com/android/server/am/AppBatteryExemptionTracker.java b/services/core/java/com/android/server/am/AppBatteryExemptionTracker.java
index 6f11b00..b07d9a6 100644
--- a/services/core/java/com/android/server/am/AppBatteryExemptionTracker.java
+++ b/services/core/java/com/android/server/am/AppBatteryExemptionTracker.java
@@ -39,6 +39,7 @@
 import com.android.server.am.AppBatteryTracker.AppBatteryPolicy;
 import com.android.server.am.AppBatteryTracker.BatteryUsage;
 import com.android.server.am.AppBatteryTracker.ImmutableBatteryUsage;
+import com.android.server.am.AppRestrictionController.TrackerType;
 import com.android.server.am.BaseAppStateTimeEvents.BaseTimeEvent;
 import com.android.server.am.BaseAppStateTracker.Injector;
 import com.android.server.am.BaseAppStateTracker.StateListener;
@@ -85,6 +86,11 @@
     }
 
     @Override
+    @TrackerType int getType() {
+        return AppRestrictionController.TRACKER_TYPE_BATTERY_EXEMPTION;
+    }
+
+    @Override
     void onSystemReady() {
         super.onSystemReady();
         mAppRestrictionController.forEachTracker(tracker -> {
diff --git a/services/core/java/com/android/server/am/AppBatteryTracker.java b/services/core/java/com/android/server/am/AppBatteryTracker.java
index 3de5e1a..8f98fba 100644
--- a/services/core/java/com/android/server/am/AppBatteryTracker.java
+++ b/services/core/java/com/android/server/am/AppBatteryTracker.java
@@ -73,6 +73,7 @@
 import com.android.internal.annotations.VisibleForTesting;
 import com.android.internal.util.ArrayUtils;
 import com.android.server.am.AppBatteryTracker.AppBatteryPolicy;
+import com.android.server.am.AppRestrictionController.TrackerType;
 import com.android.server.am.AppRestrictionController.UidBatteryUsageProvider;
 import com.android.server.pm.UserManagerInternal;
 
@@ -195,6 +196,11 @@
     }
 
     @Override
+    @TrackerType int getType() {
+        return AppRestrictionController.TRACKER_TYPE_BATTERY;
+    }
+
+    @Override
     void onSystemReady() {
         super.onSystemReady();
         final UserManagerInternal um = mInjector.getUserManagerInternal();
diff --git a/services/core/java/com/android/server/am/AppBindServiceEventsTracker.java b/services/core/java/com/android/server/am/AppBindServiceEventsTracker.java
index 9e3cae6..9bed077 100644
--- a/services/core/java/com/android/server/am/AppBindServiceEventsTracker.java
+++ b/services/core/java/com/android/server/am/AppBindServiceEventsTracker.java
@@ -26,6 +26,7 @@
 import android.content.Context;
 
 import com.android.server.am.AppBindServiceEventsTracker.AppBindServiceEventsPolicy;
+import com.android.server.am.AppRestrictionController.TrackerType;
 import com.android.server.am.BaseAppStateTimeSlotEventsTracker.SimpleAppStateTimeslotEvents;
 import com.android.server.am.BaseAppStateTracker.Injector;
 
@@ -59,6 +60,11 @@
     }
 
     @Override
+    @TrackerType int getType() {
+        return AppRestrictionController.TRACKER_TYPE_BIND_SERVICE_EVENTS;
+    }
+
+    @Override
     void onSystemReady() {
         super.onSystemReady();
         mInjector.getActivityManagerInternal().addBindServiceEventListener(this);
diff --git a/services/core/java/com/android/server/am/AppBroadcastEventsTracker.java b/services/core/java/com/android/server/am/AppBroadcastEventsTracker.java
index cafae40..a9155a1 100644
--- a/services/core/java/com/android/server/am/AppBroadcastEventsTracker.java
+++ b/services/core/java/com/android/server/am/AppBroadcastEventsTracker.java
@@ -26,6 +26,7 @@
 import android.content.Context;
 
 import com.android.server.am.AppBroadcastEventsTracker.AppBroadcastEventsPolicy;
+import com.android.server.am.AppRestrictionController.TrackerType;
 import com.android.server.am.BaseAppStateTimeSlotEventsTracker.SimpleAppStateTimeslotEvents;
 import com.android.server.am.BaseAppStateTracker.Injector;
 
@@ -58,6 +59,11 @@
     }
 
     @Override
+    @TrackerType int getType() {
+        return AppRestrictionController.TRACKER_TYPE_BROADCAST_EVENTS;
+    }
+
+    @Override
     void onSystemReady() {
         super.onSystemReady();
         mInjector.getActivityManagerInternal().addBroadcastEventListener(this);
diff --git a/services/core/java/com/android/server/am/AppFGSTracker.java b/services/core/java/com/android/server/am/AppFGSTracker.java
index 246725e..f8378c3 100644
--- a/services/core/java/com/android/server/am/AppFGSTracker.java
+++ b/services/core/java/com/android/server/am/AppFGSTracker.java
@@ -54,6 +54,7 @@
 import com.android.internal.annotations.VisibleForTesting;
 import com.android.server.am.AppFGSTracker.AppFGSPolicy;
 import com.android.server.am.AppFGSTracker.PackageDurations;
+import com.android.server.am.AppRestrictionController.TrackerType;
 import com.android.server.am.BaseAppStateEventsTracker.BaseAppStateEventsPolicy;
 import com.android.server.am.BaseAppStateTimeEvents.BaseTimeEvent;
 import com.android.server.am.BaseAppStateTracker.Injector;
@@ -176,6 +177,11 @@
     }
 
     @Override
+    @TrackerType int getType() {
+        return AppRestrictionController.TRACKER_TYPE_FGS;
+    }
+
+    @Override
     void onSystemReady() {
         super.onSystemReady();
         mInjector.getActivityManagerInternal().addForegroundServiceStateListener(this);
diff --git a/services/core/java/com/android/server/am/AppMediaSessionTracker.java b/services/core/java/com/android/server/am/AppMediaSessionTracker.java
index 4ce23f7..7daa93e 100644
--- a/services/core/java/com/android/server/am/AppMediaSessionTracker.java
+++ b/services/core/java/com/android/server/am/AppMediaSessionTracker.java
@@ -36,6 +36,7 @@
 
 import com.android.internal.app.ProcessMap;
 import com.android.server.am.AppMediaSessionTracker.AppMediaSessionPolicy;
+import com.android.server.am.AppRestrictionController.TrackerType;
 import com.android.server.am.BaseAppStateDurationsTracker.SimplePackageDurations;
 import com.android.server.am.BaseAppStateEventsTracker.BaseAppStateEventsPolicy;
 
@@ -161,6 +162,11 @@
     }
 
     @Override
+    @TrackerType int getType() {
+        return AppRestrictionController.TRACKER_TYPE_MEDIA_SESSION;
+    }
+
+    @Override
     void dump(PrintWriter pw, String prefix) {
         pw.print(prefix);
         pw.println("APP MEDIA SESSION TRACKER:");
diff --git a/services/core/java/com/android/server/am/AppPermissionTracker.java b/services/core/java/com/android/server/am/AppPermissionTracker.java
index 622d746..722d0d4 100644
--- a/services/core/java/com/android/server/am/AppPermissionTracker.java
+++ b/services/core/java/com/android/server/am/AppPermissionTracker.java
@@ -58,6 +58,7 @@
 import com.android.internal.app.IAppOpsCallback;
 import com.android.internal.app.IAppOpsService;
 import com.android.server.am.AppPermissionTracker.AppPermissionPolicy;
+import com.android.server.am.AppRestrictionController.TrackerType;
 import com.android.server.pm.permission.PermissionManagerServiceInternal;
 
 import java.io.PrintWriter;
@@ -104,6 +105,11 @@
     }
 
     @Override
+    @TrackerType int getType() {
+        return AppRestrictionController.TRACKER_TYPE_PERMISSION;
+    }
+
+    @Override
     public void onPermissionsChanged(int uid) {
         mHandler.obtainMessage(MyHandler.MSG_PERMISSIONS_CHANGED, uid, 0).sendToTarget();
     }
diff --git a/services/core/java/com/android/server/am/AppRestrictionController.java b/services/core/java/com/android/server/am/AppRestrictionController.java
index c15deac..3fc30ee 100644
--- a/services/core/java/com/android/server/am/AppRestrictionController.java
+++ b/services/core/java/com/android/server/am/AppRestrictionController.java
@@ -27,6 +27,7 @@
 import static android.app.ActivityManager.RESTRICTION_LEVEL_MAX;
 import static android.app.ActivityManager.RESTRICTION_LEVEL_RESTRICTED_BUCKET;
 import static android.app.ActivityManager.RESTRICTION_LEVEL_UNKNOWN;
+import static android.app.ActivityManager.RESTRICTION_LEVEL_UNRESTRICTED;
 import static android.app.ActivityManager.UID_OBSERVER_ACTIVE;
 import static android.app.ActivityManager.UID_OBSERVER_GONE;
 import static android.app.ActivityManager.UID_OBSERVER_IDLE;
@@ -70,6 +71,7 @@
 import static android.os.PowerExemptionManager.REASON_SYSTEM_ALLOW_LISTED;
 import static android.os.PowerExemptionManager.REASON_SYSTEM_MODULE;
 import static android.os.PowerExemptionManager.REASON_SYSTEM_UID;
+import static android.os.PowerExemptionManager.getExemptionReasonForStatsd;
 import static android.os.PowerExemptionManager.reasonCodeToString;
 import static android.os.Process.SYSTEM_UID;
 import static android.os.Process.THREAD_PRIORITY_BACKGROUND;
@@ -114,12 +116,14 @@
 import android.database.ContentObserver;
 import android.graphics.drawable.Icon;
 import android.net.Uri;
+import android.os.Build;
 import android.os.Environment;
 import android.os.Handler;
 import android.os.HandlerExecutor;
 import android.os.HandlerThread;
 import android.os.Looper;
 import android.os.Message;
+import android.os.PowerExemptionManager.ExemptionReason;
 import android.os.PowerExemptionManager.ReasonCode;
 import android.os.RemoteException;
 import android.os.SystemClock;
@@ -134,6 +138,7 @@
 import android.text.TextUtils;
 import android.util.ArraySet;
 import android.util.AtomicFile;
+import android.util.Pair;
 import android.util.Slog;
 import android.util.SparseArray;
 import android.util.SparseArrayMap;
@@ -147,6 +152,7 @@
 import com.android.internal.annotations.VisibleForTesting;
 import com.android.internal.messages.nano.SystemMessageProto.SystemMessage;
 import com.android.internal.util.ArrayUtils;
+import com.android.internal.util.FrameworkStatsLog;
 import com.android.internal.util.function.TriConsumer;
 import com.android.server.AppStateTracker;
 import com.android.server.LocalServices;
@@ -297,6 +303,26 @@
 
     final ActivityManagerService mActivityManagerService;
 
+    static final int TRACKER_TYPE_UNKNOWN = 0;
+    static final int TRACKER_TYPE_BATTERY = 1;
+    static final int TRACKER_TYPE_BATTERY_EXEMPTION = 2;
+    static final int TRACKER_TYPE_FGS = 3;
+    static final int TRACKER_TYPE_MEDIA_SESSION = 4;
+    static final int TRACKER_TYPE_PERMISSION = 5;
+    static final int TRACKER_TYPE_BROADCAST_EVENTS = 6;
+    static final int TRACKER_TYPE_BIND_SERVICE_EVENTS = 7;
+    @IntDef(prefix = { "TRACKER_TYPE_" }, value = {
+            TRACKER_TYPE_UNKNOWN,
+            TRACKER_TYPE_BATTERY,
+            TRACKER_TYPE_BATTERY_EXEMPTION,
+            TRACKER_TYPE_FGS,
+            TRACKER_TYPE_MEDIA_SESSION,
+            TRACKER_TYPE_PERMISSION,
+            TRACKER_TYPE_BROADCAST_EVENTS,
+            TRACKER_TYPE_BIND_SERVICE_EVENTS,
+    })
+    @interface TrackerType {}
+
     private final BroadcastReceiver mBroadcastReceiver = new BroadcastReceiver() {
         @Override
         public void onReceive(Context context, Intent intent) {
@@ -883,8 +909,8 @@
                 final int curBucket = mInjector.getAppStandbyInternal().getAppStandbyBucket(
                         packageName, UserHandle.getUserId(uid), now, false);
                 if (applyLevel) {
-                    applyRestrictionLevel(packageName, uid, curLevel, curBucket, true,
-                            reason & REASON_MAIN_MASK, reason & REASON_SUB_MASK);
+                    applyRestrictionLevel(packageName, uid, curLevel, TRACKER_TYPE_UNKNOWN,
+                            curBucket, true, reason & REASON_MAIN_MASK, reason & REASON_SUB_MASK);
                 } else {
                     pkgSettings.update(curLevel,
                             reason & REASON_MAIN_MASK, reason & REASON_SUB_MASK);
@@ -1509,15 +1535,15 @@
                 Slog.e(TAG, "Unable to find " + info.mPackageName + "/u" + userId);
                 continue;
             }
-            final @RestrictionLevel int level = calcAppRestrictionLevel(
+            final Pair<Integer, Integer> levelTypePair = calcAppRestrictionLevel(
                     userId, uid, info.mPackageName, info.mStandbyBucket, false, false);
             if (DEBUG_BG_RESTRICTION_CONTROLLER) {
                 Slog.i(TAG, "Proposed restriction level of " + info.mPackageName + "/"
                         + UserHandle.formatUid(uid) + ": "
-                        + ActivityManager.restrictionLevelToName(level)
+                        + ActivityManager.restrictionLevelToName(levelTypePair.first)
                         + " " + info.mStandbyBucket);
             }
-            applyRestrictionLevel(info.mPackageName, uid, level,
+            applyRestrictionLevel(info.mPackageName, uid, levelTypePair.first, levelTypePair.second,
                     info.mStandbyBucket, true, reason, subReason);
         }
     }
@@ -1533,24 +1559,26 @@
         final long now = SystemClock.elapsedRealtime();
         for (String pkg: packages) {
             final int curBucket = appStandbyInternal.getAppStandbyBucket(pkg, userId, now, false);
-            final @RestrictionLevel int level = calcAppRestrictionLevel(userId, uid, pkg,
+            final Pair<Integer, Integer> levelTypePair = calcAppRestrictionLevel(userId, uid, pkg,
                     curBucket, allowRequestBgRestricted, true);
             if (DEBUG_BG_RESTRICTION_CONTROLLER) {
                 Slog.i(TAG, "Proposed restriction level of " + pkg + "/"
                         + UserHandle.formatUid(uid) + ": "
-                        + ActivityManager.restrictionLevelToName(level));
+                        + ActivityManager.restrictionLevelToName(levelTypePair.first));
             }
-            applyRestrictionLevel(pkg, uid, level, curBucket, true, reason, subReason);
+            applyRestrictionLevel(pkg, uid, levelTypePair.first, levelTypePair.second,
+                    curBucket, true, reason, subReason);
         }
     }
 
-    private @RestrictionLevel int calcAppRestrictionLevel(@UserIdInt int userId, int uid,
+    private Pair<Integer, Integer> calcAppRestrictionLevel(@UserIdInt int userId, int uid,
             String packageName, @UsageStatsManager.StandbyBuckets int standbyBucket,
             boolean allowRequestBgRestricted, boolean calcTrackers) {
         if (mInjector.getAppHibernationInternal().isHibernatingForUser(packageName, userId)) {
-            return RESTRICTION_LEVEL_HIBERNATION;
+            return new Pair<>(RESTRICTION_LEVEL_HIBERNATION, TRACKER_TYPE_UNKNOWN);
         }
         @RestrictionLevel int level;
+        @TrackerType int trackerType = TRACKER_TYPE_UNKNOWN;
         switch (standbyBucket) {
             case STANDBY_BUCKET_EXEMPTED:
                 level = RESTRICTION_LEVEL_EXEMPTED;
@@ -1566,19 +1594,23 @@
             default:
                 if (mInjector.getAppStateTracker()
                         .isAppBackgroundRestricted(uid, packageName)) {
-                    return RESTRICTION_LEVEL_BACKGROUND_RESTRICTED;
+                    return new Pair<>(RESTRICTION_LEVEL_BACKGROUND_RESTRICTED, trackerType);
                 }
                 level = mConstantsObserver.mRestrictedBucketEnabled
                         && standbyBucket == STANDBY_BUCKET_RESTRICTED
                         ? RESTRICTION_LEVEL_RESTRICTED_BUCKET
                         : RESTRICTION_LEVEL_ADAPTIVE_BUCKET;
                 if (calcTrackers) {
-                    @RestrictionLevel int l = calcAppRestrictionLevelFromTackers(uid, packageName,
-                            RESTRICTION_LEVEL_MAX);
+                    Pair<Integer, Integer> levelTypePair = calcAppRestrictionLevelFromTackers(
+                            uid, packageName, RESTRICTION_LEVEL_MAX);
+                    @RestrictionLevel int l = levelTypePair.first;
                     if (l == RESTRICTION_LEVEL_EXEMPTED) {
-                        return RESTRICTION_LEVEL_EXEMPTED;
+                        return new Pair<>(RESTRICTION_LEVEL_EXEMPTED, levelTypePair.second);
                     }
                     level = Math.max(l, level);
+                    if (l == level) {
+                        trackerType = levelTypePair.second;
+                    }
                     if (level == RESTRICTION_LEVEL_BACKGROUND_RESTRICTED) {
                         // This level can't be entered without user consent
                         if (allowRequestBgRestricted) {
@@ -1586,27 +1618,32 @@
                                     uid, 0, packageName).sendToTarget();
                         }
                         // Lower the level.
-                        level = calcAppRestrictionLevelFromTackers(uid, packageName,
+                        levelTypePair = calcAppRestrictionLevelFromTackers(uid, packageName,
                                 RESTRICTION_LEVEL_BACKGROUND_RESTRICTED);
+                        level = levelTypePair.first;
+                        trackerType = levelTypePair.second;
                     }
                 }
                 break;
         }
-        return level;
+        return new Pair<>(level, trackerType);
     }
 
     /**
      * Ask each of the trackers for their proposed restriction levels for the given uid/package,
-     * and return the most restrictive level.
+     * and return the most restrictive level along with the type of tracker which applied this
+     * restriction level as a {@code Pair<@RestrictionLevel, @TrackerType>}.
      *
      * <p>Note, it's different from the {@link #getRestrictionLevel} where it returns the least
      * restrictive level. We're returning the most restrictive level here because each tracker
      * monitors certain dimensions of the app, the abusive behaviors could be detected in one or
      * more of these dimensions, but not necessarily all of them. </p>
      */
-    private @RestrictionLevel int calcAppRestrictionLevelFromTackers(int uid, String packageName,
+    private Pair<Integer, Integer> calcAppRestrictionLevelFromTackers(int uid, String packageName,
             @RestrictionLevel int maxLevel) {
         @RestrictionLevel int level = RESTRICTION_LEVEL_UNKNOWN;
+        @RestrictionLevel int prevLevel = level;
+        @TrackerType int trackerType = TRACKER_TYPE_UNKNOWN;
         final boolean isRestrictedBucketEnabled = mConstantsObserver.mRestrictedBucketEnabled;
         for (int i = mAppStateTrackers.size() - 1; i >= 0; i--) {
             @RestrictionLevel int l = mAppStateTrackers.get(i).getPolicy()
@@ -1615,8 +1652,12 @@
                 l = RESTRICTION_LEVEL_ADAPTIVE_BUCKET;
             }
             level = Math.max(level, l);
+            if (level != prevLevel) {
+                trackerType = mAppStateTrackers.get(i).getType();
+                prevLevel = level;
+            }
         }
-        return level;
+        return new Pair<>(level, trackerType);
     }
 
     private static @RestrictionLevel int standbyBucketToRestrictionLevel(
@@ -1829,7 +1870,126 @@
         }
     }
 
-    private void applyRestrictionLevel(String pkgName, int uid, @RestrictionLevel int level,
+    private int getRestrictionLevelStatsd(@RestrictionLevel int level) {
+        switch (level) {
+            case RESTRICTION_LEVEL_UNKNOWN:
+                return FrameworkStatsLog
+                        .APP_BACKGROUND_RESTRICTIONS_INFO__RESTRICTION_LEVEL__LEVEL_UNKNOWN;
+            case RESTRICTION_LEVEL_UNRESTRICTED:
+                return FrameworkStatsLog
+                        .APP_BACKGROUND_RESTRICTIONS_INFO__RESTRICTION_LEVEL__LEVEL_UNRESTRICTED;
+            case RESTRICTION_LEVEL_EXEMPTED:
+                return FrameworkStatsLog
+                        .APP_BACKGROUND_RESTRICTIONS_INFO__RESTRICTION_LEVEL__LEVEL_EXEMPTED;
+            case RESTRICTION_LEVEL_ADAPTIVE_BUCKET:
+                return FrameworkStatsLog
+                        .APP_BACKGROUND_RESTRICTIONS_INFO__RESTRICTION_LEVEL__LEVEL_ADAPTIVE_BUCKET;
+            case RESTRICTION_LEVEL_RESTRICTED_BUCKET:
+                return FrameworkStatsLog
+                        .APP_BACKGROUND_RESTRICTIONS_INFO__RESTRICTION_LEVEL__LEVEL_RESTRICTED_BUCKET;
+            case RESTRICTION_LEVEL_BACKGROUND_RESTRICTED:
+                return FrameworkStatsLog
+                        .APP_BACKGROUND_RESTRICTIONS_INFO__RESTRICTION_LEVEL__LEVEL_BACKGROUND_RESTRICTED;
+            case RESTRICTION_LEVEL_HIBERNATION:
+                return FrameworkStatsLog
+                        .APP_BACKGROUND_RESTRICTIONS_INFO__RESTRICTION_LEVEL__LEVEL_HIBERNATION;
+            default:
+                return FrameworkStatsLog
+                        .APP_BACKGROUND_RESTRICTIONS_INFO__RESTRICTION_LEVEL__LEVEL_UNKNOWN;
+        }
+    }
+
+    private int getThresholdStatsd(int reason) {
+        switch (reason) {
+            case REASON_MAIN_FORCED_BY_SYSTEM:
+                return FrameworkStatsLog
+                        .APP_BACKGROUND_RESTRICTIONS_INFO__THRESHOLD__THRESHOLD_RESTRICTED;
+            case REASON_MAIN_FORCED_BY_USER:
+                return FrameworkStatsLog
+                        .APP_BACKGROUND_RESTRICTIONS_INFO__THRESHOLD__THRESHOLD_USER;
+            default:
+                return FrameworkStatsLog
+                        .APP_BACKGROUND_RESTRICTIONS_INFO__THRESHOLD__THRESHOLD_UNKNOWN;
+        }
+    }
+
+    private int getTrackerTypeStatsd(@TrackerType int type) {
+        switch (type) {
+            case TRACKER_TYPE_BATTERY:
+                return FrameworkStatsLog.APP_BACKGROUND_RESTRICTIONS_INFO__TRACKER__BATTERY_TRACKER;
+            case TRACKER_TYPE_BATTERY_EXEMPTION:
+                return FrameworkStatsLog
+                        .APP_BACKGROUND_RESTRICTIONS_INFO__TRACKER__BATTERY_EXEMPTION_TRACKER;
+            case TRACKER_TYPE_FGS:
+                return FrameworkStatsLog.APP_BACKGROUND_RESTRICTIONS_INFO__TRACKER__FGS_TRACKER;
+            case TRACKER_TYPE_MEDIA_SESSION:
+                return FrameworkStatsLog
+                        .APP_BACKGROUND_RESTRICTIONS_INFO__TRACKER__MEDIA_SESSION_TRACKER;
+            case TRACKER_TYPE_PERMISSION:
+                return FrameworkStatsLog
+                        .APP_BACKGROUND_RESTRICTIONS_INFO__TRACKER__PERMISSION_TRACKER;
+            case TRACKER_TYPE_BROADCAST_EVENTS:
+                return FrameworkStatsLog
+                        .APP_BACKGROUND_RESTRICTIONS_INFO__TRACKER__BROADCAST_EVENTS_TRACKER;
+            case TRACKER_TYPE_BIND_SERVICE_EVENTS:
+                return FrameworkStatsLog
+                        .APP_BACKGROUND_RESTRICTIONS_INFO__TRACKER__BIND_SERVICE_EVENTS_TRACKER;
+            default:
+                return FrameworkStatsLog.APP_BACKGROUND_RESTRICTIONS_INFO__TRACKER__UNKNOWN_TRACKER;
+        }
+    }
+
+    private @ExemptionReason int getExemptionReasonStatsd(int uid, @RestrictionLevel int level) {
+        if (level != RESTRICTION_LEVEL_EXEMPTED) {
+            return FrameworkStatsLog
+                    .APP_BACKGROUND_RESTRICTIONS_INFO__EXEMPTION_REASON__REASON_DENIED;
+        }
+
+        @ReasonCode final int reasonCode = getBackgroundRestrictionExemptionReason(uid);
+        return getExemptionReasonForStatsd(reasonCode);
+    }
+
+    private int getOptimizationLevelStatsd(@RestrictionLevel int level) {
+        switch (level) {
+            case RESTRICTION_LEVEL_UNKNOWN:
+                return FrameworkStatsLog.APP_BACKGROUND_RESTRICTIONS_INFO__OPT_LEVEL__UNKNOWN;
+            case RESTRICTION_LEVEL_UNRESTRICTED:
+                return FrameworkStatsLog.APP_BACKGROUND_RESTRICTIONS_INFO__OPT_LEVEL__NOT_OPTIMIZED;
+            case RESTRICTION_LEVEL_ADAPTIVE_BUCKET:
+                return FrameworkStatsLog.APP_BACKGROUND_RESTRICTIONS_INFO__OPT_LEVEL__OPTIMIZED;
+            case RESTRICTION_LEVEL_BACKGROUND_RESTRICTED:
+                return FrameworkStatsLog
+                        .APP_BACKGROUND_RESTRICTIONS_INFO__OPT_LEVEL__BACKGROUND_RESTRICTED;
+            default:
+                return FrameworkStatsLog.APP_BACKGROUND_RESTRICTIONS_INFO__OPT_LEVEL__UNKNOWN;
+        }
+    }
+
+    @SuppressWarnings("AndroidFrameworkCompatChange")
+    private int getTargetSdkStatsd(String packageName) {
+        final PackageManager pm = mInjector.getPackageManager();
+        if (pm == null) {
+            return FrameworkStatsLog.APP_BACKGROUND_RESTRICTIONS_INFO__TARGET_SDK__SDK_UNKNOWN;
+        }
+        try {
+            final PackageInfo pkg = pm.getPackageInfo(packageName, 0 /* flags */);
+            final int targetSdk = pkg.applicationInfo.targetSdkVersion;
+            if (targetSdk < Build.VERSION_CODES.S) {
+                return FrameworkStatsLog.APP_BACKGROUND_RESTRICTIONS_INFO__TARGET_SDK__SDK_PRE_S;
+            } else if (targetSdk < Build.VERSION_CODES.TIRAMISU) {
+                return FrameworkStatsLog.APP_BACKGROUND_RESTRICTIONS_INFO__TARGET_SDK__SDK_S;
+            } else if (targetSdk == Build.VERSION_CODES.TIRAMISU) {
+                return FrameworkStatsLog.APP_BACKGROUND_RESTRICTIONS_INFO__TARGET_SDK__SDK_T;
+            } else {
+                return FrameworkStatsLog.APP_BACKGROUND_RESTRICTIONS_INFO__TARGET_SDK__SDK_UNKNOWN;
+            }
+        } catch (PackageManager.NameNotFoundException ignored) {
+        }
+        return FrameworkStatsLog.APP_BACKGROUND_RESTRICTIONS_INFO__TARGET_SDK__SDK_UNKNOWN;
+    }
+
+    private void applyRestrictionLevel(String pkgName, int uid,
+            @RestrictionLevel int level, @TrackerType int trackerType,
             int curBucket, boolean allowUpdateBucket, int reason, int subReason) {
         int curLevel;
         int prevReason;
@@ -1909,6 +2069,19 @@
                     prevReason & REASON_MAIN_MASK, prevReason & REASON_SUB_MASK,
                     reason, subReason);
         }
+
+        FrameworkStatsLog.write(FrameworkStatsLog.APP_BACKGROUND_RESTRICTIONS_INFO, uid,
+                getRestrictionLevelStatsd(level),
+                getThresholdStatsd(reason),
+                getTrackerTypeStatsd(trackerType),
+                null, // FgsTrackerInfo
+                null, // BatteryTrackerInfo
+                null, // BroadcastEventsTrackerInfo
+                null, // BindServiceEventsTrackerInfo
+                getExemptionReasonStatsd(uid, level),
+                getOptimizationLevelStatsd(level),
+                getTargetSdkStatsd(pkgName),
+                ActivityManager.isLowRamDeviceStatic());
     }
 
     private void handleBackgroundRestrictionChanged(int uid, String pkgName, boolean restricted) {
@@ -1926,7 +2099,7 @@
             // The app could fall into the background restricted with user consent only,
             // so set the reason to it.
             applyRestrictionLevel(pkgName, uid, RESTRICTION_LEVEL_BACKGROUND_RESTRICTED,
-                    curBucket, true, REASON_MAIN_FORCED_BY_USER,
+                    TRACKER_TYPE_UNKNOWN, curBucket, true, REASON_MAIN_FORCED_BY_USER,
                     REASON_SUB_FORCED_USER_FLAG_INTERACTION);
             mBgHandler.obtainMessage(BgHandler.MSG_CANCEL_REQUEST_BG_RESTRICTED, uid, 0, pkgName)
                     .sendToTarget();
@@ -1939,11 +2112,11 @@
                     ? STANDBY_BUCKET_EXEMPTED
                     : (lastLevel == RESTRICTION_LEVEL_RESTRICTED_BUCKET
                             ? STANDBY_BUCKET_RESTRICTED : STANDBY_BUCKET_RARE);
-            final @RestrictionLevel int level = calcAppRestrictionLevel(
+            final Pair<Integer, Integer> levelTypePair = calcAppRestrictionLevel(
                     UserHandle.getUserId(uid), uid, pkgName, tentativeBucket, false, true);
 
-            applyRestrictionLevel(pkgName, uid, level, curBucket, true,
-                    REASON_MAIN_USAGE, REASON_SUB_USAGE_USER_INTERACTION);
+            applyRestrictionLevel(pkgName, uid, levelTypePair.first, levelTypePair.second,
+                    curBucket, true, REASON_MAIN_USAGE, REASON_SUB_USAGE_USER_INTERACTION);
         }
     }
 
@@ -1983,10 +2156,10 @@
             @UserIdInt int userId) {
         final int uid = mInjector.getPackageManagerInternal().getPackageUid(
                 packageName, STOCK_PM_FLAGS, userId);
-        final @RestrictionLevel int level = calcAppRestrictionLevel(
+        final Pair<Integer, Integer> levelTypePair = calcAppRestrictionLevel(
                 userId, uid, packageName, bucket, false, false);
-        applyRestrictionLevel(packageName, uid, level, bucket, false,
-                REASON_MAIN_DEFAULT, REASON_SUB_DEFAULT_UNDEFINED);
+        applyRestrictionLevel(packageName, uid, levelTypePair.first, levelTypePair.second,
+                bucket, false, REASON_MAIN_DEFAULT, REASON_SUB_DEFAULT_UNDEFINED);
     }
 
     void handleRequestBgRestricted(String packageName, int uid) {
diff --git a/services/core/java/com/android/server/am/BaseAppStateTracker.java b/services/core/java/com/android/server/am/BaseAppStateTracker.java
index cb21a4b..5afceca 100644
--- a/services/core/java/com/android/server/am/BaseAppStateTracker.java
+++ b/services/core/java/com/android/server/am/BaseAppStateTracker.java
@@ -40,6 +40,7 @@
 import com.android.internal.app.IAppOpsService;
 import com.android.server.DeviceIdleInternal;
 import com.android.server.LocalServices;
+import com.android.server.am.AppRestrictionController.TrackerType;
 import com.android.server.notification.NotificationManagerInternal;
 import com.android.server.pm.UserManagerInternal;
 import com.android.server.pm.permission.PermissionManagerServiceInternal;
@@ -161,6 +162,13 @@
     }
 
     /**
+     * Return the type of tracker (as defined by AppRestrictionController.TrackerType)
+     */
+    @TrackerType int getType() {
+        return AppRestrictionController.TRACKER_TYPE_UNKNOWN;
+    }
+
+    /**
      * Return the policy holder of this tracker.
      */
     T getPolicy() {