Rate limit battery external stats collection in takeUidSnapshot(s).

Bug: 74146897
Test: atest cts/tests/tests/os/src/android/os/health/cts/SystemHealthManagerTest.java
Test: atest core/tests/coretests/src/com/android/internal/os/BatteryStatsTests.java
Change-Id: I2e01146718c3ce65d230308097622b958cb2897a
diff --git a/core/java/android/provider/Settings.java b/core/java/android/provider/Settings.java
index c5fc067..6b6e14f 100644
--- a/core/java/android/provider/Settings.java
+++ b/core/java/android/provider/Settings.java
@@ -10775,6 +10775,7 @@
          * track_cpu_active_cluster_time (boolean)
          * read_binary_cpu_time          (boolean)
          * proc_state_cpu_times_read_delay_ms (long)
+         * external_stats_collection_rate_limit_ms (long)
          * </pre>
          *
          * <p>
diff --git a/core/java/com/android/internal/os/BatteryStatsImpl.java b/core/java/com/android/internal/os/BatteryStatsImpl.java
index 5da3874..d18faa7 100644
--- a/core/java/com/android/internal/os/BatteryStatsImpl.java
+++ b/core/java/com/android/internal/os/BatteryStatsImpl.java
@@ -13261,18 +13261,23 @@
                 = "kernel_uid_readers_throttle_time";
         public static final String KEY_UID_REMOVE_DELAY_MS
                 = "uid_remove_delay_ms";
+        public static final String KEY_EXTERNAL_STATS_COLLECTION_RATE_LIMIT_MS
+                = "external_stats_collection_rate_limit_ms";
 
         private static final boolean DEFAULT_TRACK_CPU_TIMES_BY_PROC_STATE = true;
         private static final boolean DEFAULT_TRACK_CPU_ACTIVE_CLUSTER_TIME = true;
         private static final long DEFAULT_PROC_STATE_CPU_TIMES_READ_DELAY_MS = 5_000;
         private static final long DEFAULT_KERNEL_UID_READERS_THROTTLE_TIME = 10_000;
         private static final long DEFAULT_UID_REMOVE_DELAY_MS = 5L * 60L * 1000L;
+        private static final long DEFAULT_EXTERNAL_STATS_COLLECTION_RATE_LIMIT_MS = 600_000;
 
         public boolean TRACK_CPU_TIMES_BY_PROC_STATE = DEFAULT_TRACK_CPU_TIMES_BY_PROC_STATE;
         public boolean TRACK_CPU_ACTIVE_CLUSTER_TIME = DEFAULT_TRACK_CPU_ACTIVE_CLUSTER_TIME;
         public long PROC_STATE_CPU_TIMES_READ_DELAY_MS = DEFAULT_PROC_STATE_CPU_TIMES_READ_DELAY_MS;
         public long KERNEL_UID_READERS_THROTTLE_TIME = DEFAULT_KERNEL_UID_READERS_THROTTLE_TIME;
         public long UID_REMOVE_DELAY_MS = DEFAULT_UID_REMOVE_DELAY_MS;
+        public long EXTERNAL_STATS_COLLECTION_RATE_LIMIT_MS
+                = DEFAULT_EXTERNAL_STATS_COLLECTION_RATE_LIMIT_MS;
 
         private ContentResolver mResolver;
         private final KeyValueListParser mParser = new KeyValueListParser(',');
@@ -13318,6 +13323,9 @@
                                 DEFAULT_KERNEL_UID_READERS_THROTTLE_TIME));
                 updateUidRemoveDelay(
                         mParser.getLong(KEY_UID_REMOVE_DELAY_MS, DEFAULT_UID_REMOVE_DELAY_MS));
+                EXTERNAL_STATS_COLLECTION_RATE_LIMIT_MS = mParser.getLong(
+                        KEY_EXTERNAL_STATS_COLLECTION_RATE_LIMIT_MS,
+                        DEFAULT_EXTERNAL_STATS_COLLECTION_RATE_LIMIT_MS);
             }
         }
 
@@ -13367,6 +13375,14 @@
             pw.println(PROC_STATE_CPU_TIMES_READ_DELAY_MS);
             pw.print(KEY_KERNEL_UID_READERS_THROTTLE_TIME); pw.print("=");
             pw.println(KERNEL_UID_READERS_THROTTLE_TIME);
+            pw.print(KEY_EXTERNAL_STATS_COLLECTION_RATE_LIMIT_MS); pw.print("=");
+            pw.println(EXTERNAL_STATS_COLLECTION_RATE_LIMIT_MS);
+        }
+    }
+
+    public long getExternalStatsCollectionRateLimitMs() {
+        synchronized (this) {
+            return mConstants.EXTERNAL_STATS_COLLECTION_RATE_LIMIT_MS;
         }
     }
 
diff --git a/services/core/java/com/android/server/am/BatteryExternalStatsWorker.java b/services/core/java/com/android/server/am/BatteryExternalStatsWorker.java
index 2291e44..87194bc 100644
--- a/services/core/java/com/android/server/am/BatteryExternalStatsWorker.java
+++ b/services/core/java/com/android/server/am/BatteryExternalStatsWorker.java
@@ -117,6 +117,13 @@
     private WifiActivityEnergyInfo mLastInfo =
             new WifiActivityEnergyInfo(0, 0, 0, new long[]{0}, 0, 0, 0, 0);
 
+    /**
+     * Timestamp at which all external stats were last collected in
+     * {@link SystemClock#elapsedRealtime()} time base.
+     */
+    @GuardedBy("this")
+    private long mLastCollectionTimeStamp;
+
     BatteryExternalStatsWorker(Context context, BatteryStatsImpl stats) {
         mContext = context;
         mStats = stats;
@@ -259,6 +266,12 @@
         return mCurrentFuture;
     }
 
+    long getLastCollectionTimeStamp() {
+        synchronized (this) {
+            return mLastCollectionTimeStamp;
+        }
+    }
+
     private final Runnable mSyncTask = new Runnable() {
         @Override
         public void run() {
@@ -312,6 +325,10 @@
             } catch (Exception e) {
                 Slog.wtf(TAG, "Error updating external stats: ", e);
             }
+
+            synchronized (BatteryExternalStatsWorker.this) {
+                mLastCollectionTimeStamp = SystemClock.elapsedRealtime();
+            }
         }
     };
 
diff --git a/services/core/java/com/android/server/am/BatteryStatsService.java b/services/core/java/com/android/server/am/BatteryStatsService.java
index 9c2b1a5..0c328a8 100644
--- a/services/core/java/com/android/server/am/BatteryStatsService.java
+++ b/services/core/java/com/android/server/am/BatteryStatsService.java
@@ -1541,7 +1541,9 @@
         }
         long ident = Binder.clearCallingIdentity();
         try {
-            syncStats("get-health-stats-for-uids", BatteryExternalStatsWorker.UPDATE_ALL);
+            if (shouldCollectExternalStats()) {
+                syncStats("get-health-stats-for-uids", BatteryExternalStatsWorker.UPDATE_ALL);
+            }
             synchronized (mStats) {
                 return getHealthStatsForUidLocked(requestUid);
             }
@@ -1565,7 +1567,9 @@
         long ident = Binder.clearCallingIdentity();
         int i=-1;
         try {
-            syncStats("get-health-stats-for-uids", BatteryExternalStatsWorker.UPDATE_ALL);
+            if (shouldCollectExternalStats()) {
+                syncStats("get-health-stats-for-uids", BatteryExternalStatsWorker.UPDATE_ALL);
+            }
             synchronized (mStats) {
                 final int N = requestUids.length;
                 final HealthStatsParceler[] results = new HealthStatsParceler[N];
@@ -1583,6 +1587,11 @@
         }
     }
 
+    private boolean shouldCollectExternalStats() {
+        return (SystemClock.elapsedRealtime() - mWorker.getLastCollectionTimeStamp())
+                > mStats.getExternalStatsCollectionRateLimitMs();
+    }
+
     /**
      * Returns whether the Binder.getCallingUid is the only thing in requestUids.
      */