Merge "Resolve STATSD and batterystats race condition" into pi-dev
diff --git a/core/java/com/android/internal/os/BatteryStatsImpl.java b/core/java/com/android/internal/os/BatteryStatsImpl.java
index 4ab2fec..89f8ae6 100644
--- a/core/java/com/android/internal/os/BatteryStatsImpl.java
+++ b/core/java/com/android/internal/os/BatteryStatsImpl.java
@@ -33,9 +33,6 @@
 import android.os.BatteryManager;
 import android.os.BatteryStats;
 import android.os.Build;
-import android.os.connectivity.CellularBatteryStats;
-import android.os.connectivity.WifiBatteryStats;
-import android.os.connectivity.GpsBatteryStats;
 import android.os.FileUtils;
 import android.os.Handler;
 import android.os.IBatteryPropertiesRegistrar;
@@ -53,6 +50,9 @@
 import android.os.UserHandle;
 import android.os.WorkSource;
 import android.os.WorkSource.WorkChain;
+import android.os.connectivity.CellularBatteryStats;
+import android.os.connectivity.GpsBatteryStats;
+import android.os.connectivity.WifiBatteryStats;
 import android.provider.Settings;
 import android.telephony.DataConnectionRealTimeInfo;
 import android.telephony.ModemActivityInfo;
@@ -90,8 +90,8 @@
 import com.android.internal.util.JournaledFile;
 import com.android.internal.util.XmlUtils;
 
-import java.util.List;
 import libcore.util.EmptyArray;
+
 import org.xmlpull.v1.XmlPullParser;
 import org.xmlpull.v1.XmlPullParserException;
 import org.xmlpull.v1.XmlSerializer;
@@ -109,11 +109,11 @@
 import java.util.Calendar;
 import java.util.HashMap;
 import java.util.Iterator;
+import java.util.LinkedList;
+import java.util.List;
 import java.util.Map;
-import java.util.concurrent.ExecutorService;
-import java.util.concurrent.Executors;
+import java.util.Queue;
 import java.util.concurrent.Future;
-import java.util.concurrent.ThreadFactory;
 import java.util.concurrent.atomic.AtomicInteger;
 import java.util.concurrent.locks.ReentrantLock;
 
@@ -234,11 +234,15 @@
     protected final SparseIntArray mPendingUids = new SparseIntArray();
 
     @GuardedBy("this")
-    private long mNumCpuTimeReads;
+    private long mNumSingleUidCpuTimeReads;
     @GuardedBy("this")
-    private long mNumBatchedCpuTimeReads;
+    private long mNumBatchedSingleUidCpuTimeReads;
     @GuardedBy("this")
     private long mCpuTimeReadsTrackingStartTime = SystemClock.uptimeMillis();
+    @GuardedBy("this")
+    private int mNumUidsRemoved;
+    @GuardedBy("this")
+    private int mNumAllUidCpuTimeReads;
 
     /** Container for Resource Power Manager stats. Updated by updateRpmStatsLocked. */
     private final RpmStats mTmpRpmStats = new RpmStats();
@@ -246,6 +250,67 @@
     private static final long RPM_STATS_UPDATE_FREQ_MS = 1000;
     /** Last time that RPM stats were updated by updateRpmStatsLocked. */
     private long mLastRpmStatsUpdateTimeMs = -RPM_STATS_UPDATE_FREQ_MS;
+    /**
+     * Use a queue to delay removing UIDs from {@link KernelUidCpuTimeReader},
+     * {@link KernelUidCpuActiveTimeReader}, {@link KernelUidCpuClusterTimeReader},
+     * {@link KernelUidCpuFreqTimeReader} and from the Kernel.
+     *
+     * Isolated and invalid UID info must be removed to conserve memory. However, STATSD and
+     * Batterystats both need to access UID cpu time. To resolve this race condition, only
+     * Batterystats shall remove UIDs, and a delay {@link Constants#UID_REMOVE_DELAY_MS} is
+     * implemented so that STATSD can capture those UID times before they are deleted.
+     */
+    @GuardedBy("this")
+    @VisibleForTesting(visibility = VisibleForTesting.Visibility.PACKAGE)
+    protected Queue<UidToRemove> mPendingRemovedUids = new LinkedList<>();
+
+    @VisibleForTesting
+    public final class UidToRemove {
+        int startUid;
+        int endUid;
+        long timeAddedInQueue;
+
+        /** Remove just one UID */
+        public UidToRemove(int uid, long timestamp) {
+            this(uid, uid, timestamp);
+        }
+
+        /** Remove a range of UIDs, startUid must be smaller than endUid. */
+        public UidToRemove(int startUid, int endUid, long timestamp) {
+            this.startUid = startUid;
+            this.endUid = endUid;
+            timeAddedInQueue = timestamp;
+        }
+
+        void remove() {
+            if (startUid == endUid) {
+                mKernelUidCpuTimeReader.removeUid(startUid);
+                mKernelUidCpuFreqTimeReader.removeUid(startUid);
+                if (mConstants.TRACK_CPU_ACTIVE_CLUSTER_TIME) {
+                    mKernelUidCpuActiveTimeReader.removeUid(startUid);
+                    mKernelUidCpuClusterTimeReader.removeUid(startUid);
+                }
+                if (mKernelSingleUidTimeReader != null) {
+                    mKernelSingleUidTimeReader.removeUid(startUid);
+                }
+                mNumUidsRemoved++;
+            } else if (startUid < endUid) {
+                mKernelUidCpuFreqTimeReader.removeUidsInRange(startUid, endUid);
+                mKernelUidCpuTimeReader.removeUidsInRange(startUid, endUid);
+                if (mConstants.TRACK_CPU_ACTIVE_CLUSTER_TIME) {
+                    mKernelUidCpuActiveTimeReader.removeUidsInRange(startUid, endUid);
+                    mKernelUidCpuClusterTimeReader.removeUidsInRange(startUid, endUid);
+                }
+                if (mKernelSingleUidTimeReader != null) {
+                    mKernelSingleUidTimeReader.removeUidsInRange(startUid, endUid);
+                }
+                // Treat as one. We don't know how many uids there are in between.
+                mNumUidsRemoved++;
+            } else {
+                Slog.w(TAG, "End UID " + endUid + " is smaller than start UID " + startUid);
+            }
+        }
+    }
 
     public interface BatteryCallback {
         public void batteryNeedsCpuUpdate();
@@ -376,6 +441,14 @@
         }
     }
 
+    public void clearPendingRemovedUids() {
+        long cutOffTime = mClocks.elapsedRealtime() - mConstants.UID_REMOVE_DELAY_MS;
+        while (!mPendingRemovedUids.isEmpty()
+                && mPendingRemovedUids.peek().timeAddedInQueue < cutOffTime) {
+            mPendingRemovedUids.poll().remove();
+        }
+    }
+
     public void copyFromAllUidsCpuTimes() {
         synchronized (BatteryStatsImpl.this) {
             copyFromAllUidsCpuTimes(
@@ -3961,12 +4034,7 @@
             u.removeIsolatedUid(isolatedUid);
             mIsolatedUids.removeAt(idx);
         }
-        mKernelUidCpuTimeReader.removeUid(isolatedUid);
-        mKernelUidCpuFreqTimeReader.removeUid(isolatedUid);
-        if (mConstants.TRACK_CPU_ACTIVE_CLUSTER_TIME) {
-            mKernelUidCpuActiveTimeReader.removeUid(isolatedUid);
-            mKernelUidCpuClusterTimeReader.removeUid(isolatedUid);
-        }
+        mPendingRemovedUids.add(new UidToRemove(isolatedUid, mClocks.elapsedRealtime()));
     }
 
     public int mapUid(int uid) {
@@ -9860,9 +9928,9 @@
                                     mBsi.mOnBatteryTimeBase.isRunning(),
                                     mBsi.mOnBatteryScreenOffTimeBase.isRunning(),
                                     mBsi.mConstants.PROC_STATE_CPU_TIMES_READ_DELAY_MS);
-                            mBsi.mNumCpuTimeReads++;
+                            mBsi.mNumSingleUidCpuTimeReads++;
                         } else {
-                            mBsi.mNumBatchedCpuTimeReads++;
+                            mBsi.mNumBatchedSingleUidCpuTimeReads++;
                         }
                         if (mBsi.mPendingUids.indexOfKey(mUid) < 0
                                 || ArrayUtils.contains(CRITICAL_PROC_STATES, mProcessState)) {
@@ -11024,6 +11092,9 @@
         mLastStepStatSoftIrqTime = mCurStepStatSoftIrqTime = 0;
         mLastStepStatIdleTime = mCurStepStatIdleTime = 0;
 
+        mNumAllUidCpuTimeReads = 0;
+        mNumUidsRemoved = 0;
+
         initDischarge();
 
         clearHistoryLocked();
@@ -12009,9 +12080,11 @@
         if (!onBattery) {
             mKernelUidCpuTimeReader.readDelta(null);
             mKernelUidCpuFreqTimeReader.readDelta(null);
+            mNumAllUidCpuTimeReads += 2;
             if (mConstants.TRACK_CPU_ACTIVE_CLUSTER_TIME) {
                 mKernelUidCpuActiveTimeReader.readDelta(null);
                 mKernelUidCpuClusterTimeReader.readDelta(null);
+                mNumAllUidCpuTimeReads += 2;
             }
             for (int cluster = mKernelCpuSpeedReaders.length - 1; cluster >= 0; --cluster) {
                 mKernelCpuSpeedReaders[cluster].readDelta();
@@ -12029,9 +12102,11 @@
             updateClusterSpeedTimes(updatedUids, onBattery);
         }
         readKernelUidCpuFreqTimesLocked(partialTimersToConsider, onBattery, onBatteryScreenOff);
+        mNumAllUidCpuTimeReads += 2;
         if (mConstants.TRACK_CPU_ACTIVE_CLUSTER_TIME) {
             readKernelUidCpuActiveTimesLocked(onBattery);
             readKernelUidCpuClusterTimesLocked(onBattery);
+            mNumAllUidCpuTimeReads += 2;
         }
     }
 
@@ -13256,11 +13331,8 @@
     public void onCleanupUserLocked(int userId) {
         final int firstUidForUser = UserHandle.getUid(userId, 0);
         final int lastUidForUser = UserHandle.getUid(userId, UserHandle.PER_USER_RANGE - 1);
-        mKernelUidCpuFreqTimeReader.removeUidsInRange(firstUidForUser, lastUidForUser);
-        mKernelUidCpuTimeReader.removeUidsInRange(firstUidForUser, lastUidForUser);
-        if (mKernelSingleUidTimeReader != null) {
-            mKernelSingleUidTimeReader.removeUidsInRange(firstUidForUser, lastUidForUser);
-        }
+        mPendingRemovedUids.add(
+                new UidToRemove(firstUidForUser, lastUidForUser, mClocks.elapsedRealtime()));
     }
 
     public void onUserRemovedLocked(int userId) {
@@ -13277,12 +13349,8 @@
      * Remove the statistics object for a particular uid.
      */
     public void removeUidStatsLocked(int uid) {
-        mKernelUidCpuTimeReader.removeUid(uid);
-        mKernelUidCpuFreqTimeReader.removeUid(uid);
-        if (mKernelSingleUidTimeReader != null) {
-            mKernelSingleUidTimeReader.removeUid(uid);
-        }
         mUidStats.remove(uid);
+        mPendingRemovedUids.add(new UidToRemove(uid, mClocks.elapsedRealtime()));
     }
 
     /**
@@ -13335,24 +13403,24 @@
                 = "track_cpu_times_by_proc_state";
         public static final String KEY_TRACK_CPU_ACTIVE_CLUSTER_TIME
                 = "track_cpu_active_cluster_time";
-        public static final String KEY_READ_BINARY_CPU_TIME
-                = "read_binary_cpu_time";
         public static final String KEY_PROC_STATE_CPU_TIMES_READ_DELAY_MS
                 = "proc_state_cpu_times_read_delay_ms";
         public static final String KEY_KERNEL_UID_READERS_THROTTLE_TIME
                 = "kernel_uid_readers_throttle_time";
+        public static final String KEY_UID_REMOVE_DELAY_MS
+                = "uid_remove_delay_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 boolean DEFAULT_READ_BINARY_CPU_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;
 
         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 boolean READ_BINARY_CPU_TIME = DEFAULT_READ_BINARY_CPU_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;
 
         private ContentResolver mResolver;
         private final KeyValueListParser mParser = new KeyValueListParser(',');
@@ -13390,14 +13458,14 @@
                                 DEFAULT_TRACK_CPU_TIMES_BY_PROC_STATE));
                 TRACK_CPU_ACTIVE_CLUSTER_TIME = mParser.getBoolean(
                         KEY_TRACK_CPU_ACTIVE_CLUSTER_TIME, DEFAULT_TRACK_CPU_ACTIVE_CLUSTER_TIME);
-                updateReadBinaryCpuTime(READ_BINARY_CPU_TIME,
-                        mParser.getBoolean(KEY_READ_BINARY_CPU_TIME, DEFAULT_READ_BINARY_CPU_TIME));
                 updateProcStateCpuTimesReadDelayMs(PROC_STATE_CPU_TIMES_READ_DELAY_MS,
                         mParser.getLong(KEY_PROC_STATE_CPU_TIMES_READ_DELAY_MS,
                                 DEFAULT_PROC_STATE_CPU_TIMES_READ_DELAY_MS));
                 updateKernelUidReadersThrottleTime(KERNEL_UID_READERS_THROTTLE_TIME,
                         mParser.getLong(KEY_KERNEL_UID_READERS_THROTTLE_TIME,
                                 DEFAULT_KERNEL_UID_READERS_THROTTLE_TIME));
+                updateUidRemoveDelay(
+                        mParser.getLong(KEY_UID_REMOVE_DELAY_MS, DEFAULT_UID_REMOVE_DELAY_MS));
             }
         }
 
@@ -13407,24 +13475,17 @@
                 mKernelSingleUidTimeReader.markDataAsStale(true);
                 mExternalSync.scheduleCpuSyncDueToSettingChange();
 
-                mNumCpuTimeReads = 0;
-                mNumBatchedCpuTimeReads = 0;
+                mNumSingleUidCpuTimeReads = 0;
+                mNumBatchedSingleUidCpuTimeReads = 0;
                 mCpuTimeReadsTrackingStartTime = mClocks.uptimeMillis();
             }
         }
 
-        private void updateReadBinaryCpuTime(boolean oldEnabled, boolean isEnabled) {
-            READ_BINARY_CPU_TIME = isEnabled;
-            if (oldEnabled != isEnabled) {
-                mKernelUidCpuFreqTimeReader.setReadBinary(isEnabled);
-            }
-        }
-
         private void updateProcStateCpuTimesReadDelayMs(long oldDelayMillis, long newDelayMillis) {
             PROC_STATE_CPU_TIMES_READ_DELAY_MS = newDelayMillis;
             if (oldDelayMillis != newDelayMillis) {
-                mNumCpuTimeReads = 0;
-                mNumBatchedCpuTimeReads = 0;
+                mNumSingleUidCpuTimeReads = 0;
+                mNumBatchedSingleUidCpuTimeReads = 0;
                 mCpuTimeReadsTrackingStartTime = mClocks.uptimeMillis();
             }
         }
@@ -13440,13 +13501,16 @@
             }
         }
 
+        private void updateUidRemoveDelay(long newTimeMs) {
+            UID_REMOVE_DELAY_MS = newTimeMs;
+            clearPendingRemovedUids();
+        }
+
         public void dumpLocked(PrintWriter pw) {
             pw.print(KEY_TRACK_CPU_TIMES_BY_PROC_STATE); pw.print("=");
             pw.println(TRACK_CPU_TIMES_BY_PROC_STATE);
             pw.print(KEY_TRACK_CPU_ACTIVE_CLUSTER_TIME); pw.print("=");
             pw.println(TRACK_CPU_ACTIVE_CLUSTER_TIME);
-            pw.print(KEY_READ_BINARY_CPU_TIME); pw.print("=");
-            pw.println(READ_BINARY_CPU_TIME);
             pw.print(KEY_PROC_STATE_CPU_TIMES_READ_DELAY_MS); pw.print("=");
             pw.println(PROC_STATE_CPU_TIMES_READ_DELAY_MS);
             pw.print(KEY_KERNEL_UID_READERS_THROTTLE_TIME); pw.print("=");
@@ -13459,6 +13523,43 @@
         mConstants.dumpLocked(pw);
     }
 
+    @GuardedBy("this")
+    public void dumpCpuStatsLocked(PrintWriter pw) {
+        int size = mUidStats.size();
+        pw.println("Per UID CPU user & system time in ms:");
+        for (int i = 0; i < size; i++) {
+            int u = mUidStats.keyAt(i);
+            Uid uid = mUidStats.get(u);
+            pw.print("  "); pw.print(u); pw.print(": ");
+            pw.print(uid.getUserCpuTimeUs(STATS_SINCE_CHARGED) / 1000); pw.print(" ");
+            pw.println(uid.getSystemCpuTimeUs(STATS_SINCE_CHARGED) / 1000);
+        }
+        pw.println("Per UID CPU active time in ms:");
+        for (int i = 0; i < size; i++) {
+            int u = mUidStats.keyAt(i);
+            Uid uid = mUidStats.get(u);
+            if (uid.getCpuActiveTime() > 0) {
+                pw.print("  "); pw.print(u); pw.print(": "); pw.println(uid.getCpuActiveTime());
+            }
+        }
+        pw.println("Per UID CPU cluster time in ms:");
+        for (int i = 0; i < size; i++) {
+            int u = mUidStats.keyAt(i);
+            long[] times = mUidStats.get(u).getCpuClusterTimes();
+            if (times != null) {
+                pw.print("  "); pw.print(u); pw.print(": "); pw.println(Arrays.toString(times));
+            }
+        }
+        pw.println("Per UID CPU frequency time in ms:");
+        for (int i = 0; i < size; i++) {
+            int u = mUidStats.keyAt(i);
+            long[] times = mUidStats.get(u).getCpuFreqTimes(STATS_SINCE_CHARGED);
+            if (times != null) {
+                pw.print("  "); pw.print(u); pw.print(": "); pw.println(Arrays.toString(times));
+            }
+        }
+    }
+
     Parcel mPendingWrite = null;
     final ReentrantLock mWriteLock = new ReentrantLock();
 
@@ -15183,10 +15284,14 @@
         }
         super.dumpLocked(context, pw, flags, reqUid, histStart);
         pw.print("Total cpu time reads: ");
-        pw.println(mNumCpuTimeReads);
+        pw.println(mNumSingleUidCpuTimeReads);
         pw.print("Batched cpu time reads: ");
-        pw.println(mNumBatchedCpuTimeReads);
+        pw.println(mNumBatchedSingleUidCpuTimeReads);
         pw.print("Batching Duration (min): ");
         pw.println((mClocks.uptimeMillis() - mCpuTimeReadsTrackingStartTime) / (60 * 1000));
+        pw.print("All UID cpu time reads since the later of device start or stats reset: ");
+        pw.println(mNumAllUidCpuTimeReads);
+        pw.print("UIDs removed since the later of device start or stats reset: ");
+        pw.println(mNumUidsRemoved);
     }
 }
diff --git a/core/java/com/android/internal/os/KernelUidCpuActiveTimeReader.java b/core/java/com/android/internal/os/KernelUidCpuActiveTimeReader.java
index e790e08..bd8a67a 100644
--- a/core/java/com/android/internal/os/KernelUidCpuActiveTimeReader.java
+++ b/core/java/com/android/internal/os/KernelUidCpuActiveTimeReader.java
@@ -24,6 +24,7 @@
 
 import java.nio.ByteBuffer;
 import java.nio.IntBuffer;
+import java.util.function.Consumer;
 
 /**
  * Reads binary proc file /proc/uid_cpupower/concurrent_active_time and reports CPU active time to
@@ -54,6 +55,7 @@
 
     private final KernelCpuProcReader mProcReader;
     private SparseArray<Double> mLastUidCpuActiveTimeMs = new SparseArray<>();
+    private int mCores;
 
     public interface Callback extends KernelUidCpuTimeReaderBase.Callback {
         /**
@@ -75,7 +77,60 @@
     }
 
     @Override
-    protected void readDeltaImpl(@Nullable Callback cb) {
+    protected void readDeltaImpl(@Nullable Callback callback) {
+        readImpl((buf) -> {
+            int uid = buf.get();
+            double activeTime = sumActiveTime(buf);
+            if (activeTime > 0) {
+                double delta = activeTime - mLastUidCpuActiveTimeMs.get(uid, 0.0);
+                if (delta > 0) {
+                    mLastUidCpuActiveTimeMs.put(uid, activeTime);
+                    if (callback != null) {
+                        callback.onUidCpuActiveTime(uid, (long) delta);
+                    }
+                } else if (delta < 0) {
+                    Slog.e(TAG, "Negative delta from active time proc: " + delta);
+                }
+            }
+        });
+    }
+
+    public void readAbsolute(Callback callback) {
+        readImpl((buf) -> {
+            int uid = buf.get();
+            double activeTime = sumActiveTime(buf);
+            if (activeTime > 0) {
+                callback.onUidCpuActiveTime(uid, (long) activeTime);
+            }
+        });
+    }
+
+    private double sumActiveTime(IntBuffer buffer) {
+        double sum = 0;
+        boolean corrupted = false;
+        for (int j = 1; j <= mCores; j++) {
+            int time = buffer.get();
+            if (time < 0) {
+                // Even if error happens, we still need to continue reading.
+                // Buffer cannot be skipped.
+                Slog.e(TAG, "Negative time from active time proc: " + time);
+                corrupted = true;
+            } else {
+                sum += (double) time * 10 / j; // Unit is 10ms.
+            }
+        }
+        return corrupted ? -1 : sum;
+    }
+
+    /**
+     * readImpl accepts a callback to process the uid entry. readDeltaImpl needs to store the last
+     * seen results while processing the buffer, while readAbsolute returns the absolute value read
+     * from the buffer without storing. So readImpl contains the common logic of the two, leaving
+     * the difference to a processUid function.
+     *
+     * @param processUid the callback function to process the uid entry in the buffer.
+     */
+    private void readImpl(Consumer<IntBuffer> processUid) {
         synchronized (mProcReader) {
             final ByteBuffer bytes = mProcReader.readBytes();
             if (bytes == null || bytes.remaining() <= 4) {
@@ -89,6 +144,11 @@
             }
             final IntBuffer buf = bytes.asIntBuffer();
             final int cores = buf.get();
+            if (mCores != 0 && cores != mCores) {
+                Slog.wtf(TAG, "Cpu active time wrong # cores: " + cores);
+                return;
+            }
+            mCores = cores;
             if (cores <= 0 || buf.remaining() % (cores + 1) != 0) {
                 Slog.wtf(TAG,
                         "Cpu active time format error: " + buf.remaining() + " / " + (cores
@@ -97,25 +157,7 @@
             }
             int numUids = buf.remaining() / (cores + 1);
             for (int i = 0; i < numUids; i++) {
-                int uid = buf.get();
-                boolean corrupted = false;
-                double curTime = 0;
-                for (int j = 1; j <= cores; j++) {
-                    int time = buf.get();
-                    if (time < 0) {
-                        Slog.e(TAG, "Corrupted data from active time proc: " + time);
-                        corrupted = true;
-                    } else {
-                        curTime += (double) time * 10 / j; // Unit is 10ms.
-                    }
-                }
-                double delta = curTime - mLastUidCpuActiveTimeMs.get(uid, 0.0);
-                if (delta > 0 && !corrupted) {
-                    mLastUidCpuActiveTimeMs.put(uid, curTime);
-                    if (cb != null) {
-                        cb.onUidCpuActiveTime(uid, (long) delta);
-                    }
-                }
+                processUid.accept(buf);
             }
             if (DEBUG) {
                 Slog.d(TAG, "Read uids: " + numUids);
@@ -123,26 +165,11 @@
         }
     }
 
-    public void readAbsolute(Callback cb) {
-        synchronized (mProcReader) {
-            readDelta(null);
-            int total = mLastUidCpuActiveTimeMs.size();
-            for (int i = 0; i < total; i ++){
-                int uid = mLastUidCpuActiveTimeMs.keyAt(i);
-                cb.onUidCpuActiveTime(uid, mLastUidCpuActiveTimeMs.get(uid).longValue());
-            }
-        }
-    }
-
     public void removeUid(int uid) {
         mLastUidCpuActiveTimeMs.delete(uid);
     }
 
     public void removeUidsInRange(int startUid, int endUid) {
-        if (endUid < startUid) {
-            Slog.w(TAG, "End UID " + endUid + " is smaller than start UID " + startUid);
-            return;
-        }
         mLastUidCpuActiveTimeMs.put(startUid, null);
         mLastUidCpuActiveTimeMs.put(endUid, null);
         final int firstIndex = mLastUidCpuActiveTimeMs.indexOfKey(startUid);
diff --git a/core/java/com/android/internal/os/KernelUidCpuClusterTimeReader.java b/core/java/com/android/internal/os/KernelUidCpuClusterTimeReader.java
index bf5b520..3cbfaea 100644
--- a/core/java/com/android/internal/os/KernelUidCpuClusterTimeReader.java
+++ b/core/java/com/android/internal/os/KernelUidCpuClusterTimeReader.java
@@ -24,6 +24,7 @@
 
 import java.nio.ByteBuffer;
 import java.nio.IntBuffer;
+import java.util.function.Consumer;
 
 /**
  * Reads binary proc file /proc/uid_cpupower/concurrent_policy_time and reports CPU cluster times
@@ -89,6 +90,72 @@
 
     @Override
     protected void readDeltaImpl(@Nullable Callback cb) {
+        readImpl((buf) -> {
+            int uid = buf.get();
+            double[] lastTimes = mLastUidPolicyTimeMs.get(uid);
+            if (lastTimes == null) {
+                lastTimes = new double[mNumClusters];
+                mLastUidPolicyTimeMs.put(uid, lastTimes);
+            }
+            if (!sumClusterTime(buf, mCurTime)) {
+                return;
+            }
+            boolean valid = true;
+            boolean notify = false;
+            for (int i = 0; i < mNumClusters; i++) {
+                mDeltaTime[i] = (long) (mCurTime[i] - lastTimes[i]);
+                if (mDeltaTime[i] < 0) {
+                    Slog.e(TAG, "Negative delta from cluster time proc: " + mDeltaTime[i]);
+                    valid = false;
+                }
+                notify |= mDeltaTime[i] > 0;
+            }
+            if (notify && valid) {
+                System.arraycopy(mCurTime, 0, lastTimes, 0, mNumClusters);
+                if (cb != null) {
+                    cb.onUidCpuPolicyTime(uid, mDeltaTime);
+                }
+            }
+        });
+    }
+
+    public void readAbsolute(Callback callback) {
+        readImpl((buf) -> {
+            int uid = buf.get();
+            if (sumClusterTime(buf, mCurTime)) {
+                for (int i = 0; i < mNumClusters; i++) {
+                    mCurTimeRounded[i] = (long) mCurTime[i];
+                }
+                callback.onUidCpuPolicyTime(uid, mCurTimeRounded);
+            }
+        });
+    }
+
+    private boolean sumClusterTime(IntBuffer buffer, double[] clusterTime) {
+        boolean valid = true;
+        for (int i = 0; i < mNumClusters; i++) {
+            clusterTime[i] = 0;
+            for (int j = 1; j <= mNumCoresOnCluster[i]; j++) {
+                int time = buffer.get();
+                if (time < 0) {
+                    Slog.e(TAG, "Negative time from cluster time proc: " + time);
+                    valid = false;
+                }
+                clusterTime[i] += (double) time * 10 / j; // Unit is 10ms.
+            }
+        }
+        return valid;
+    }
+
+    /**
+     * readImpl accepts a callback to process the uid entry. readDeltaImpl needs to store the last
+     * seen results while processing the buffer, while readAbsolute returns the absolute value read
+     * from the buffer without storing. So readImpl contains the common logic of the two, leaving
+     * the difference to a processUid function.
+     *
+     * @param processUid the callback function to process the uid entry in the buffer.
+     */
+    private void readImpl(Consumer<IntBuffer> processUid) {
         synchronized (mProcReader) {
             ByteBuffer bytes = mProcReader.readBytes();
             if (bytes == null || bytes.remaining() <= 4) {
@@ -130,7 +197,7 @@
             int numUids = buf.remaining() / (mNumCores + 1);
 
             for (int i = 0; i < numUids; i++) {
-                processUid(buf, cb);
+                processUid.accept(buf);
             }
             if (DEBUG) {
                 Slog.d(TAG, "Read uids: " + numUids);
@@ -138,57 +205,6 @@
         }
     }
 
-    public void readAbsolute(Callback cb) {
-        synchronized (mProcReader) {
-            readDelta(null);
-            int total = mLastUidPolicyTimeMs.size();
-            for (int i = 0; i < total; i ++){
-                int uid = mLastUidPolicyTimeMs.keyAt(i);
-                double[] lastTimes = mLastUidPolicyTimeMs.get(uid);
-                for (int j = 0; j < mNumClusters; j++) {
-                    mCurTimeRounded[j] = (long) lastTimes[j];
-                }
-                cb.onUidCpuPolicyTime(uid, mCurTimeRounded);
-            }
-        }
-    }
-
-    private void processUid(IntBuffer buf, @Nullable Callback cb) {
-        int uid = buf.get();
-        double[] lastTimes = mLastUidPolicyTimeMs.get(uid);
-        if (lastTimes == null) {
-            lastTimes = new double[mNumClusters];
-            mLastUidPolicyTimeMs.put(uid, lastTimes);
-        }
-
-        boolean notify = false;
-        boolean corrupted = false;
-
-        for (int j = 0; j < mNumClusters; j++) {
-            mCurTime[j] = 0;
-            for (int k = 1; k <= mNumCoresOnCluster[j]; k++) {
-                int time = buf.get();
-                if (time < 0) {
-                    Slog.e(TAG, "Corrupted data from cluster time proc uid: " + uid);
-                    corrupted = true;
-                }
-                mCurTime[j] += (double) time * 10 / k; // Unit is 10ms.
-            }
-            mDeltaTime[j] = (long) (mCurTime[j] - lastTimes[j]);
-            if (mDeltaTime[j] < 0) {
-                Slog.e(TAG, "Unexpected delta from cluster time proc uid: " + uid);
-                corrupted = true;
-            }
-            notify |= mDeltaTime[j] > 0;
-        }
-        if (notify && !corrupted) {
-            System.arraycopy(mCurTime, 0, lastTimes, 0, mNumClusters);
-            if (cb != null) {
-                cb.onUidCpuPolicyTime(uid, mDeltaTime);
-            }
-        }
-    }
-
     // Returns if it has read valid info.
     private boolean readCoreInfo(IntBuffer buf, int numClusters) {
         int numCores = 0;
@@ -214,10 +230,6 @@
     }
 
     public void removeUidsInRange(int startUid, int endUid) {
-        if (endUid < startUid) {
-            Slog.w(TAG, "End UID " + endUid + " is smaller than start UID " + startUid);
-            return;
-        }
         mLastUidPolicyTimeMs.put(startUid, null);
         mLastUidPolicyTimeMs.put(endUid, null);
         final int firstIndex = mLastUidPolicyTimeMs.indexOfKey(startUid);
diff --git a/core/java/com/android/internal/os/KernelUidCpuFreqTimeReader.java b/core/java/com/android/internal/os/KernelUidCpuFreqTimeReader.java
index f65074f..5b46d0f 100644
--- a/core/java/com/android/internal/os/KernelUidCpuFreqTimeReader.java
+++ b/core/java/com/android/internal/os/KernelUidCpuFreqTimeReader.java
@@ -21,11 +21,9 @@
 import android.annotation.NonNull;
 import android.annotation.Nullable;
 import android.os.StrictMode;
-import android.os.SystemClock;
 import android.util.IntArray;
 import android.util.Slog;
 import android.util.SparseArray;
-import android.util.TimeUtils;
 
 import com.android.internal.annotations.VisibleForTesting;
 
@@ -34,6 +32,7 @@
 import java.io.IOException;
 import java.nio.ByteBuffer;
 import java.nio.IntBuffer;
+import java.util.function.Consumer;
 
 /**
  * Reads /proc/uid_time_in_state which has the format:
@@ -75,9 +74,6 @@
     private long[] mCurTimes; // Reuse to prevent GC.
     private long[] mDeltaTimes; // Reuse to prevent GC.
     private int mCpuFreqsCount;
-    private long mLastTimeReadMs = Long.MIN_VALUE;
-    private long mNowTimeMs;
-    private boolean mReadBinary = true;
     private final KernelCpuProcReader mProcReader;
 
     private SparseArray<long[]> mLastUidCpuFreqTimeMs = new SparseArray<>();
@@ -140,180 +136,6 @@
         if (line == null) {
             return null;
         }
-        return readCpuFreqs(line, powerProfile);
-    }
-
-    public void setReadBinary(boolean readBinary) {
-        mReadBinary = readBinary;
-    }
-
-    @Override
-    protected void readDeltaImpl(@Nullable Callback callback) {
-        if (mCpuFreqs == null) {
-            return;
-        }
-        if (mReadBinary) {
-            readDeltaBinary(callback);
-        } else {
-            readDeltaString(callback);
-        }
-    }
-
-    private void readDeltaString(@Nullable Callback callback) {
-        mNowTimeMs = SystemClock.elapsedRealtime();
-        final int oldMask = StrictMode.allowThreadDiskReadsMask();
-        try (BufferedReader reader = new BufferedReader(new FileReader(UID_TIMES_PROC_FILE))) {
-            readDelta(reader, callback);
-        } catch (IOException e) {
-            Slog.e(TAG, "Failed to read " + UID_TIMES_PROC_FILE + ": " + e);
-        } finally {
-            StrictMode.setThreadPolicyMask(oldMask);
-        }
-        mLastTimeReadMs = mNowTimeMs;
-    }
-
-    @VisibleForTesting
-    public void readDeltaBinary(@Nullable Callback callback) {
-        synchronized (mProcReader) {
-            ByteBuffer bytes = mProcReader.readBytes();
-            if (bytes == null || bytes.remaining() <= 4) {
-                // Error already logged in mProcReader.
-                return;
-            }
-            if ((bytes.remaining() & 3) != 0) {
-                Slog.wtf(TAG, "Cannot parse cluster time proc bytes to int: " + bytes.remaining());
-                return;
-            }
-            IntBuffer buf = bytes.asIntBuffer();
-            final int freqs = buf.get();
-            if (freqs != mCpuFreqsCount) {
-                Slog.wtf(TAG, "Cpu freqs expect " + mCpuFreqsCount + " , got " + freqs);
-                return;
-            }
-            if (buf.remaining() % (freqs + 1) != 0) {
-                Slog.wtf(TAG, "Freq time format error: " + buf.remaining() + " / " + (freqs + 1));
-                return;
-            }
-            int numUids = buf.remaining() / (freqs + 1);
-            for (int i = 0; i < numUids; i++) {
-                int uid = buf.get();
-                long[] lastTimes = mLastUidCpuFreqTimeMs.get(uid);
-                if (lastTimes == null) {
-                    lastTimes = new long[mCpuFreqsCount];
-                    mLastUidCpuFreqTimeMs.put(uid, lastTimes);
-                }
-                boolean notify = false;
-                boolean corrupted = false;
-                for (int j = 0; j < freqs; j++) {
-                    mCurTimes[j] = (long) buf.get() * 10; // Unit is 10ms.
-                    mDeltaTimes[j] = mCurTimes[j] - lastTimes[j];
-                    if (mCurTimes[j] < 0 || mDeltaTimes[j] < 0) {
-                        Slog.e(TAG, "Unexpected data from freq time proc: " + mCurTimes[j]);
-                        corrupted = true;
-                    }
-                    notify |= mDeltaTimes[j] > 0;
-                }
-                if (notify && !corrupted) {
-                    System.arraycopy(mCurTimes, 0, lastTimes, 0, freqs);
-                    if (callback != null) {
-                        callback.onUidCpuFreqTime(uid, mDeltaTimes);
-                    }
-                }
-            }
-            if (DEBUG) {
-                Slog.d(TAG, "Read uids: " + numUids);
-            }
-        }
-    }
-
-    public void readAbsolute(Callback cb) {
-        synchronized (mProcReader) {
-            readDelta(null);
-            int total = mLastUidCpuFreqTimeMs.size();
-            for (int i = 0; i < total; i ++){
-                int uid = mLastUidCpuFreqTimeMs.keyAt(i);
-                cb.onUidCpuFreqTime(uid, mLastUidCpuFreqTimeMs.get(uid));
-            }
-        }
-    }
-
-    public void removeUid(int uid) {
-        mLastUidCpuFreqTimeMs.delete(uid);
-    }
-
-    public void removeUidsInRange(int startUid, int endUid) {
-        if (endUid < startUid) {
-            return;
-        }
-        mLastUidCpuFreqTimeMs.put(startUid, null);
-        mLastUidCpuFreqTimeMs.put(endUid, null);
-        final int firstIndex = mLastUidCpuFreqTimeMs.indexOfKey(startUid);
-        final int lastIndex = mLastUidCpuFreqTimeMs.indexOfKey(endUid);
-        mLastUidCpuFreqTimeMs.removeAtRange(firstIndex, lastIndex - firstIndex + 1);
-    }
-
-    @VisibleForTesting
-    public void readDelta(BufferedReader reader, @Nullable Callback callback) throws IOException {
-        String line = reader.readLine();
-        if (line == null) {
-            return;
-        }
-        while ((line = reader.readLine()) != null) {
-            final int index = line.indexOf(' ');
-            final int uid = Integer.parseInt(line.substring(0, index - 1), 10);
-            readTimesForUid(uid, line.substring(index + 1, line.length()), callback);
-        }
-    }
-
-    private void readTimesForUid(int uid, String line, Callback callback) {
-        long[] uidTimeMs = mLastUidCpuFreqTimeMs.get(uid);
-        if (uidTimeMs == null) {
-            uidTimeMs = new long[mCpuFreqsCount];
-            mLastUidCpuFreqTimeMs.put(uid, uidTimeMs);
-        }
-        final String[] timesStr = line.split(" ");
-        final int size = timesStr.length;
-        if (size != uidTimeMs.length) {
-            Slog.e(TAG, "No. of readings don't match cpu freqs, readings: " + size
-                    + " cpuFreqsCount: " + uidTimeMs.length);
-            return;
-        }
-        final long[] deltaUidTimeMs = new long[size];
-        final long[] curUidTimeMs = new long[size];
-        boolean notify = false;
-        for (int i = 0; i < size; ++i) {
-            // Times read will be in units of 10ms
-            final long totalTimeMs = Long.parseLong(timesStr[i], 10) * 10;
-            deltaUidTimeMs[i] = totalTimeMs - uidTimeMs[i];
-            // If there is malformed data for any uid, then we just log about it and ignore
-            // the data for that uid.
-            if (deltaUidTimeMs[i] < 0 || totalTimeMs < 0) {
-                if (DEBUG) {
-                    final StringBuilder sb = new StringBuilder("Malformed cpu freq data for UID=")
-                            .append(uid).append("\n");
-                    sb.append("data=").append("(").append(uidTimeMs[i]).append(",")
-                            .append(totalTimeMs).append(")").append("\n");
-                    sb.append("times=").append("(");
-                    TimeUtils.formatDuration(mLastTimeReadMs, sb);
-                    sb.append(",");
-                    TimeUtils.formatDuration(mNowTimeMs, sb);
-                    sb.append(")");
-                    Slog.e(TAG, sb.toString());
-                }
-                return;
-            }
-            curUidTimeMs[i] = totalTimeMs;
-            notify = notify || (deltaUidTimeMs[i] > 0);
-        }
-        if (notify) {
-            System.arraycopy(curUidTimeMs, 0, uidTimeMs, 0, size);
-            if (callback != null) {
-                callback.onUidCpuFreqTime(uid, deltaUidTimeMs);
-            }
-        }
-    }
-
-    private long[] readCpuFreqs(String line, PowerProfile powerProfile) {
         final String[] freqStr = line.split(" ");
         // First item would be "uid: " which needs to be ignored.
         mCpuFreqsCount = freqStr.length - 1;
@@ -339,10 +161,116 @@
             mPerClusterTimesAvailable = false;
         }
         Slog.i(TAG, "mPerClusterTimesAvailable=" + mPerClusterTimesAvailable);
-
         return mCpuFreqs;
     }
 
+    @Override
+    @VisibleForTesting
+    public void readDeltaImpl(@Nullable Callback callback) {
+        if (mCpuFreqs == null) {
+            return;
+        }
+        readImpl((buf) -> {
+            int uid = buf.get();
+            long[] lastTimes = mLastUidCpuFreqTimeMs.get(uid);
+            if (lastTimes == null) {
+                lastTimes = new long[mCpuFreqsCount];
+                mLastUidCpuFreqTimeMs.put(uid, lastTimes);
+            }
+            if (!getFreqTimeForUid(buf, mCurTimes)) {
+                return;
+            }
+            boolean notify = false;
+            boolean valid = true;
+            for (int i = 0; i < mCpuFreqsCount; i++) {
+                mDeltaTimes[i] = mCurTimes[i] - lastTimes[i];
+                if (mDeltaTimes[i] < 0) {
+                    Slog.e(TAG, "Negative delta from freq time proc: " + mDeltaTimes[i]);
+                    valid = false;
+                }
+                notify |= mDeltaTimes[i] > 0;
+            }
+            if (notify && valid) {
+                System.arraycopy(mCurTimes, 0, lastTimes, 0, mCpuFreqsCount);
+                if (callback != null) {
+                    callback.onUidCpuFreqTime(uid, mDeltaTimes);
+                }
+            }
+        });
+    }
+
+    public void readAbsolute(Callback callback) {
+        readImpl((buf) -> {
+            int uid = buf.get();
+            if (getFreqTimeForUid(buf, mCurTimes)) {
+                callback.onUidCpuFreqTime(uid, mCurTimes);
+            }
+        });
+    }
+
+    private boolean getFreqTimeForUid(IntBuffer buffer, long[] freqTime) {
+        boolean valid = true;
+        for (int i = 0; i < mCpuFreqsCount; i++) {
+            freqTime[i] = (long) buffer.get() * 10; // Unit is 10ms.
+            if (freqTime[i] < 0) {
+                Slog.e(TAG, "Negative time from freq time proc: " + freqTime[i]);
+                valid = false;
+            }
+        }
+        return valid;
+    }
+
+    /**
+     * readImpl accepts a callback to process the uid entry. readDeltaImpl needs to store the last
+     * seen results while processing the buffer, while readAbsolute returns the absolute value read
+     * from the buffer without storing. So readImpl contains the common logic of the two, leaving
+     * the difference to a processUid function.
+     *
+     * @param processUid the callback function to process the uid entry in the buffer.
+     */
+    private void readImpl(Consumer<IntBuffer> processUid) {
+        synchronized (mProcReader) {
+            ByteBuffer bytes = mProcReader.readBytes();
+            if (bytes == null || bytes.remaining() <= 4) {
+                // Error already logged in mProcReader.
+                return;
+            }
+            if ((bytes.remaining() & 3) != 0) {
+                Slog.wtf(TAG, "Cannot parse freq time proc bytes to int: " + bytes.remaining());
+                return;
+            }
+            IntBuffer buf = bytes.asIntBuffer();
+            final int freqs = buf.get();
+            if (freqs != mCpuFreqsCount) {
+                Slog.wtf(TAG, "Cpu freqs expect " + mCpuFreqsCount + " , got " + freqs);
+                return;
+            }
+            if (buf.remaining() % (freqs + 1) != 0) {
+                Slog.wtf(TAG, "Freq time format error: " + buf.remaining() + " / " + (freqs + 1));
+                return;
+            }
+            int numUids = buf.remaining() / (freqs + 1);
+            for (int i = 0; i < numUids; i++) {
+                processUid.accept(buf);
+            }
+            if (DEBUG) {
+                Slog.d(TAG, "Read uids: #" + numUids);
+            }
+        }
+    }
+
+    public void removeUid(int uid) {
+        mLastUidCpuFreqTimeMs.delete(uid);
+    }
+
+    public void removeUidsInRange(int startUid, int endUid) {
+        mLastUidCpuFreqTimeMs.put(startUid, null);
+        mLastUidCpuFreqTimeMs.put(endUid, null);
+        final int firstIndex = mLastUidCpuFreqTimeMs.indexOfKey(startUid);
+        final int lastIndex = mLastUidCpuFreqTimeMs.indexOfKey(endUid);
+        mLastUidCpuFreqTimeMs.removeAtRange(firstIndex, lastIndex - firstIndex + 1);
+    }
+
     /**
      * Extracts no. of cpu clusters and no. of freqs in each of these clusters from the freqs
      * read from the proc file.
diff --git a/core/tests/coretests/src/com/android/internal/os/BatteryStatsCpuTimesTest.java b/core/tests/coretests/src/com/android/internal/os/BatteryStatsCpuTimesTest.java
index cb049b7..ee8d508 100644
--- a/core/tests/coretests/src/com/android/internal/os/BatteryStatsCpuTimesTest.java
+++ b/core/tests/coretests/src/com/android/internal/os/BatteryStatsCpuTimesTest.java
@@ -1085,6 +1085,49 @@
     }
 
     @Test
+    public void testReadKernelUidCpuActiveTimesLocked_invalidUid() {
+        // PRECONDITIONS
+        updateTimeBasesLocked(true, Display.STATE_ON, 0, 0);
+
+        final int testUserId = 11;
+        final int invalidUserId = 15;
+        final int invalidUid = UserHandle.getUid(invalidUserId, FIRST_APPLICATION_UID + 99);
+        when(mUserInfoProvider.exists(testUserId)).thenReturn(true);
+        when(mUserInfoProvider.exists(invalidUserId)).thenReturn(false);
+        final int[] testUids = getUids(testUserId, new int[]{
+                FIRST_APPLICATION_UID + 22,
+                FIRST_APPLICATION_UID + 27,
+                FIRST_APPLICATION_UID + 33
+        });
+        final long[] uidTimesMs = {8000, 25000, 3000, 0, 42000};
+        doAnswer(invocation -> {
+            final KernelUidCpuActiveTimeReader.Callback callback =
+                    (KernelUidCpuActiveTimeReader.Callback) invocation.getArguments()[0];
+            for (int i = 0; i < testUids.length; ++i) {
+                callback.onUidCpuActiveTime(testUids[i], uidTimesMs[i]);
+            }
+            // And one for the invalid uid
+            callback.onUidCpuActiveTime(invalidUid, 1200L);
+            return null;
+        }).when(mKernelUidCpuActiveTimeReader).readDelta(
+                any(KernelUidCpuActiveTimeReader.Callback.class));
+
+        // RUN
+        mBatteryStatsImpl.readKernelUidCpuActiveTimesLocked(true);
+
+        // VERIFY
+        for (int i = 0; i < testUids.length; ++i) {
+            final BatteryStats.Uid u = mBatteryStatsImpl.getUidStats().get(testUids[i]);
+            assertNotNull("No entry for uid=" + testUids[i], u);
+            assertEquals("Unexpected cpu active time for uid=" + testUids[i], uidTimesMs[i],
+                    u.getCpuActiveTime());
+        }
+        assertNull("There shouldn't be an entry for invalid uid=" + invalidUid,
+                mBatteryStatsImpl.getUidStats().get(invalidUid));
+        verify(mKernelUidCpuActiveTimeReader).removeUid(invalidUid);
+    }
+
+    @Test
     public void testReadKernelUidCpuClusterTimesLocked() {
         // PRECONDITIONS
         updateTimeBasesLocked(true, Display.STATE_ON, 0, 0);
@@ -1153,6 +1196,97 @@
         }
     }
 
+    @Test
+    public void testReadKernelUidCpuClusterTimesLocked_invalidUid() {
+        // PRECONDITIONS
+        updateTimeBasesLocked(true, Display.STATE_ON, 0, 0);
+
+        final int testUserId = 11;
+        final int invalidUserId = 15;
+        final int invalidUid = UserHandle.getUid(invalidUserId, FIRST_APPLICATION_UID + 99);
+        when(mUserInfoProvider.exists(testUserId)).thenReturn(true);
+        when(mUserInfoProvider.exists(invalidUserId)).thenReturn(false);
+        final int[] testUids = getUids(testUserId, new int[]{
+                FIRST_APPLICATION_UID + 22,
+                FIRST_APPLICATION_UID + 27,
+                FIRST_APPLICATION_UID + 33
+        });
+        final long[][] uidTimesMs = {
+                {4000, 10000},
+                {5000, 1000},
+                {8000, 0}
+        };
+        doAnswer(invocation -> {
+            final KernelUidCpuClusterTimeReader.Callback callback =
+                    (KernelUidCpuClusterTimeReader.Callback) invocation.getArguments()[0];
+            for (int i = 0; i < testUids.length; ++i) {
+                callback.onUidCpuPolicyTime(testUids[i], uidTimesMs[i]);
+            }
+            // And one for the invalid uid
+            callback.onUidCpuPolicyTime(invalidUid, new long[] {400, 1000});
+            return null;
+        }).when(mKernelUidCpuClusterTimeReader).readDelta(
+                any(KernelUidCpuClusterTimeReader.Callback.class));
+
+        // RUN
+        mBatteryStatsImpl.readKernelUidCpuClusterTimesLocked(true);
+
+        // VERIFY
+        for (int i = 0; i < testUids.length; ++i) {
+            final BatteryStats.Uid u = mBatteryStatsImpl.getUidStats().get(testUids[i]);
+            assertNotNull("No entry for uid=" + testUids[i], u);
+            assertArrayEquals("Unexpected cpu cluster time for uid=" + testUids[i], uidTimesMs[i],
+                    u.getCpuClusterTimes());
+        }
+        assertNull("There shouldn't be an entry for invalid uid=" + invalidUid,
+                mBatteryStatsImpl.getUidStats().get(invalidUid));
+        verify(mKernelUidCpuClusterTimeReader).removeUid(invalidUid);
+    }
+
+    @Test
+    public void testRemoveUidCpuTimes() {
+        mClocks.realtime = mClocks.uptime = 0;
+        mBatteryStatsImpl.getPendingRemovedUids().add(
+                mBatteryStatsImpl.new UidToRemove(1, mClocks.elapsedRealtime()));
+        mBatteryStatsImpl.getPendingRemovedUids().add(
+                mBatteryStatsImpl.new UidToRemove(5, 10, mClocks.elapsedRealtime()));
+        mBatteryStatsImpl.clearPendingRemovedUids();
+        assertEquals(2, mBatteryStatsImpl.getPendingRemovedUids().size());
+
+        mClocks.realtime = mClocks.uptime = 100_000;
+        mBatteryStatsImpl.clearPendingRemovedUids();
+        assertEquals(2, mBatteryStatsImpl.getPendingRemovedUids().size());
+
+        mClocks.realtime = mClocks.uptime = 200_000;
+        mBatteryStatsImpl.getPendingRemovedUids().add(
+                mBatteryStatsImpl.new UidToRemove(100, mClocks.elapsedRealtime()));
+        mBatteryStatsImpl.clearPendingRemovedUids();
+        assertEquals(3, mBatteryStatsImpl.getPendingRemovedUids().size());
+
+        mClocks.realtime = mClocks.uptime = 400_000;
+        mBatteryStatsImpl.clearPendingRemovedUids();
+        assertEquals(1, mBatteryStatsImpl.getPendingRemovedUids().size());
+        verify(mKernelUidCpuActiveTimeReader).removeUid(1);
+        verify(mKernelUidCpuActiveTimeReader).removeUidsInRange(5, 10);
+        verify(mKernelUidCpuClusterTimeReader).removeUid(1);
+        verify(mKernelUidCpuClusterTimeReader).removeUidsInRange(5, 10);
+        verify(mKernelUidCpuFreqTimeReader).removeUid(1);
+        verify(mKernelUidCpuFreqTimeReader).removeUidsInRange(5, 10);
+        verify(mKernelUidCpuTimeReader).removeUid(1);
+        verify(mKernelUidCpuTimeReader).removeUidsInRange(5, 10);
+
+        mClocks.realtime = mClocks.uptime = 800_000;
+        mBatteryStatsImpl.clearPendingRemovedUids();
+        assertEquals(0, mBatteryStatsImpl.getPendingRemovedUids().size());
+        verify(mKernelUidCpuActiveTimeReader).removeUid(100);
+        verify(mKernelUidCpuClusterTimeReader).removeUid(100);
+        verify(mKernelUidCpuFreqTimeReader).removeUid(100);
+        verify(mKernelUidCpuTimeReader).removeUid(100);
+
+        verifyNoMoreInteractions(mKernelUidCpuActiveTimeReader, mKernelUidCpuClusterTimeReader,
+                mKernelUidCpuFreqTimeReader, mKernelUidCpuTimeReader);
+    }
+
     private void updateTimeBasesLocked(boolean unplugged, int screenState,
             long upTime, long realTime) {
         // Set PowerProfile=null before calling updateTimeBasesLocked to avoid execution of
diff --git a/core/tests/coretests/src/com/android/internal/os/BatteryStatsNoteTest.java b/core/tests/coretests/src/com/android/internal/os/BatteryStatsNoteTest.java
index 01ddc15..8cbe5d6 100644
--- a/core/tests/coretests/src/com/android/internal/os/BatteryStatsNoteTest.java
+++ b/core/tests/coretests/src/com/android/internal/os/BatteryStatsNoteTest.java
@@ -244,13 +244,16 @@
         assertEquals(bi.getScreenState(), Display.STATE_OFF);
     }
 
-    /**
+    /*
      * Test BatteryStatsImpl.noteScreenStateLocked updates timers correctly.
      *
      * Unknown and doze should both be subset of off state
      *
-     * Timeline 0----100----200----310----400------------1000 Unknown         ------- On ------- Off
-     * -------       ---------------------- Doze ----------------
+     * Timeline 0----100----200----310----400------------1000
+     * Unknown         -------
+     * On                     -------
+     * Off             -------       ----------------------
+     * Doze                                ----------------
      */
     @SmallTest
     public void testNoteScreenStateTimersLocked() throws Exception {
diff --git a/core/tests/coretests/src/com/android/internal/os/KernelUidCpuActiveTimeReaderTest.java b/core/tests/coretests/src/com/android/internal/os/KernelUidCpuActiveTimeReaderTest.java
index 06f51c9..28570e8 100644
--- a/core/tests/coretests/src/com/android/internal/os/KernelUidCpuActiveTimeReaderTest.java
+++ b/core/tests/coretests/src/com/android/internal/os/KernelUidCpuActiveTimeReaderTest.java
@@ -57,7 +57,7 @@
     }
 
     @Test
-    public void testReadDelta() throws Exception {
+    public void testReadDelta() {
         final int cores = 8;
         final int[] uids = {1, 22, 333, 4444, 5555};
 
@@ -104,7 +104,7 @@
     }
 
     @Test
-    public void testReadAbsolute() throws Exception {
+    public void testReadAbsolute() {
         final int cores = 8;
         final int[] uids = {1, 22, 333, 4444, 5555};
 
@@ -128,7 +128,7 @@
     }
 
     @Test
-    public void testReadDelta_malformedData() throws Exception {
+    public void testReadDelta_malformedData() {
         final int cores = 8;
         final int[] uids = {1, 22, 333, 4444, 5555};
         final long[][] times = increaseTime(new long[uids.length][cores]);
diff --git a/core/tests/coretests/src/com/android/internal/os/KernelUidCpuFreqTimeReaderTest.java b/core/tests/coretests/src/com/android/internal/os/KernelUidCpuFreqTimeReaderTest.java
index a6b99c3..67c4e61 100644
--- a/core/tests/coretests/src/com/android/internal/os/KernelUidCpuFreqTimeReaderTest.java
+++ b/core/tests/coretests/src/com/android/internal/os/KernelUidCpuFreqTimeReaderTest.java
@@ -21,7 +21,6 @@
 import static org.junit.Assert.assertFalse;
 import static org.junit.Assert.assertNotNull;
 import static org.junit.Assert.assertTrue;
-import static org.mockito.Mockito.verifyNoMoreInteractions;
 import static org.mockito.Mockito.verifyZeroInteractions;
 import static org.mockito.Mockito.when;
 
@@ -145,82 +144,6 @@
     }
 
     @Test
-    public void testReadDelta() throws Exception {
-        final long[] freqs = {1, 12, 123, 1234, 12345, 123456};
-        final int[] uids = {1, 22, 333, 4444, 5555};
-        final long[][] times = new long[uids.length][freqs.length];
-        for (int i = 0; i < uids.length; ++i) {
-            for (int j = 0; j < freqs.length; ++j) {
-                times[i][j] = uids[i] * freqs[j] * 10;
-            }
-        }
-        when(mBufferedReader.readLine()).thenReturn(getFreqsLine(freqs));
-        long[] actualFreqs = mKernelUidCpuFreqTimeReader.readFreqs(mBufferedReader, mPowerProfile);
-
-        assertArrayEquals(freqs, actualFreqs);
-        when(mBufferedReader.readLine())
-                .thenReturn(getFreqsLine(freqs), getUidTimesLines(uids, times));
-        mKernelUidCpuFreqTimeReader.readDelta(mBufferedReader, mCallback);
-        for (int i = 0; i < uids.length; ++i) {
-            Mockito.verify(mCallback).onUidCpuFreqTime(uids[i], times[i]);
-        }
-        verifyNoMoreInteractions(mCallback);
-
-        // Verify that a second call will only return deltas.
-        Mockito.reset(mCallback, mBufferedReader);
-        final long[][] newTimes1 = new long[uids.length][freqs.length];
-        for (int i = 0; i < uids.length; ++i) {
-            for (int j = 0; j < freqs.length; ++j) {
-                newTimes1[i][j] = (times[i][j] + uids[i] + freqs[j]) * 10;
-            }
-        }
-        when(mBufferedReader.readLine())
-                .thenReturn(getFreqsLine(freqs), getUidTimesLines(uids, newTimes1));
-        mKernelUidCpuFreqTimeReader.readDelta(mBufferedReader, mCallback);
-        for (int i = 0; i < uids.length; ++i) {
-            Mockito.verify(mCallback).onUidCpuFreqTime(uids[i], subtract(newTimes1[i], times[i]));
-        }
-        verifyNoMoreInteractions(mCallback);
-
-        // Verify that there won't be a callback if the proc file values didn't change.
-        Mockito.reset(mCallback, mBufferedReader);
-        when(mBufferedReader.readLine())
-                .thenReturn(getFreqsLine(freqs), getUidTimesLines(uids, newTimes1));
-        mKernelUidCpuFreqTimeReader.readDelta(mBufferedReader, mCallback);
-        verifyNoMoreInteractions(mCallback);
-
-        // Verify that calling with a null callback doesn't result in any crashes
-        Mockito.reset(mCallback, mBufferedReader);
-        final long[][] newTimes2 = new long[uids.length][freqs.length];
-        for (int i = 0; i < uids.length; ++i) {
-            for (int j = 0; j < freqs.length; ++j) {
-                newTimes2[i][j] = (newTimes1[i][j] + uids[i] * freqs[j]) * 10;
-            }
-        }
-        when(mBufferedReader.readLine())
-                .thenReturn(getFreqsLine(freqs), getUidTimesLines(uids, newTimes2));
-        mKernelUidCpuFreqTimeReader.readDelta(mBufferedReader, null);
-
-        // Verify that the readDelta call will only return deltas when
-        // the previous call had null callback.
-        Mockito.reset(mCallback, mBufferedReader);
-        final long[][] newTimes3 = new long[uids.length][freqs.length];
-        for (int i = 0; i < uids.length; ++i) {
-            for (int j = 0; j < freqs.length; ++j) {
-                newTimes3[i][j] = (newTimes2[i][j] * (uids[i] + freqs[j])) * 10;
-            }
-        }
-        when(mBufferedReader.readLine())
-                .thenReturn(getFreqsLine(freqs), getUidTimesLines(uids, newTimes3));
-        mKernelUidCpuFreqTimeReader.readDelta(mBufferedReader, mCallback);
-        for (int i = 0; i < uids.length; ++i) {
-            Mockito.verify(mCallback).onUidCpuFreqTime(uids[i],
-                    subtract(newTimes3[i], newTimes2[i]));
-        }
-        verifyNoMoreInteractions(mCallback);
-    }
-
-    @Test
     public void testReadDelta_Binary() throws Exception {
         VerifiableCallback cb = new VerifiableCallback();
         final long[] freqs = {110, 123, 145, 167, 289, 997};
@@ -236,7 +159,7 @@
 
         assertArrayEquals(freqs, actualFreqs);
         when(mProcReader.readBytes()).thenReturn(getUidTimesBytes(uids, times));
-        mKernelUidCpuFreqTimeReader.readDeltaBinary(cb);
+        mKernelUidCpuFreqTimeReader.readDeltaImpl(cb);
         for (int i = 0; i < uids.length; ++i) {
             cb.verify(uids[i], times[i]);
         }
@@ -252,7 +175,7 @@
             }
         }
         when(mProcReader.readBytes()).thenReturn(getUidTimesBytes(uids, newTimes1));
-        mKernelUidCpuFreqTimeReader.readDeltaBinary(cb);
+        mKernelUidCpuFreqTimeReader.readDeltaImpl(cb);
         for (int i = 0; i < uids.length; ++i) {
             cb.verify(uids[i], subtract(newTimes1[i], times[i]));
         }
@@ -262,7 +185,7 @@
         cb.clear();
         Mockito.reset(mProcReader);
         when(mProcReader.readBytes()).thenReturn(getUidTimesBytes(uids, newTimes1));
-        mKernelUidCpuFreqTimeReader.readDeltaBinary(cb);
+        mKernelUidCpuFreqTimeReader.readDeltaImpl(cb);
         cb.verifyNoMoreInteractions();
 
         // Verify that calling with a null callback doesn't result in any crashes
@@ -275,7 +198,7 @@
             }
         }
         when(mProcReader.readBytes()).thenReturn(getUidTimesBytes(uids, newTimes2));
-        mKernelUidCpuFreqTimeReader.readDeltaBinary(null);
+        mKernelUidCpuFreqTimeReader.readDeltaImpl(null);
         cb.verifyNoMoreInteractions();
 
         // Verify that the readDelta call will only return deltas when
@@ -289,7 +212,7 @@
             }
         }
         when(mProcReader.readBytes()).thenReturn(getUidTimesBytes(uids, newTimes3));
-        mKernelUidCpuFreqTimeReader.readDeltaBinary(cb);
+        mKernelUidCpuFreqTimeReader.readDeltaImpl(cb);
         for (int i = 0; i < uids.length; ++i) {
             cb.verify(uids[i], subtract(newTimes3[i], newTimes2[i]));
         }
@@ -336,91 +259,6 @@
         cb.verifyNoMoreInteractions();
     }
 
-    @Test
-    public void testReadDelta_malformedData() throws Exception {
-        final long[] freqs = {1, 12, 123, 1234, 12345, 123456};
-        final int[] uids = {1, 22, 333, 4444, 5555};
-        final long[][] times = new long[uids.length][freqs.length];
-        for (int i = 0; i < uids.length; ++i) {
-            for (int j = 0; j < freqs.length; ++j) {
-                times[i][j] = uids[i] * freqs[j] * 10;
-            }
-        }
-        when(mBufferedReader.readLine()).thenReturn(getFreqsLine(freqs));
-        long[] actualFreqs = mKernelUidCpuFreqTimeReader.readFreqs(mBufferedReader, mPowerProfile);
-
-        assertArrayEquals(freqs, actualFreqs);
-        when(mBufferedReader.readLine())
-                .thenReturn(getFreqsLine(freqs), getUidTimesLines(uids, times));
-        mKernelUidCpuFreqTimeReader.readDelta(mBufferedReader, mCallback);
-        for (int i = 0; i < uids.length; ++i) {
-            Mockito.verify(mCallback).onUidCpuFreqTime(uids[i], times[i]);
-        }
-        verifyNoMoreInteractions(mCallback);
-
-        // Verify that there is no callback if any value in the proc file is -ve.
-        Mockito.reset(mCallback, mBufferedReader);
-        final long[][] newTimes1 = new long[uids.length][freqs.length];
-        for (int i = 0; i < uids.length; ++i) {
-            for (int j = 0; j < freqs.length; ++j) {
-                newTimes1[i][j] = (times[i][j] + uids[i] + freqs[j]) * 10;
-            }
-        }
-        newTimes1[uids.length - 1][freqs.length - 1] *= -1;
-        when(mBufferedReader.readLine())
-                .thenReturn(getFreqsLine(freqs), getUidTimesLines(uids, newTimes1));
-        mKernelUidCpuFreqTimeReader.readDelta(mBufferedReader, mCallback);
-        for (int i = 0; i < uids.length; ++i) {
-            if (i == uids.length - 1) {
-                continue;
-            }
-            Mockito.verify(mCallback).onUidCpuFreqTime(uids[i], subtract(newTimes1[i], times[i]));
-        }
-        verifyNoMoreInteractions(mCallback);
-
-        // Verify that the internal state was not modified when the proc file had -ve value.
-        Mockito.reset(mCallback, mBufferedReader);
-        for (int i = 0; i < freqs.length; ++i) {
-            newTimes1[uids.length - 1][i] = times[uids.length - 1][i];
-        }
-        when(mBufferedReader.readLine())
-                .thenReturn(getFreqsLine(freqs), getUidTimesLines(uids, newTimes1));
-        mKernelUidCpuFreqTimeReader.readDelta(mBufferedReader, mCallback);
-        verifyNoMoreInteractions(mCallback);
-
-        // Verify that there is no callback if the values in the proc file are decreased.
-        Mockito.reset(mCallback, mBufferedReader);
-        final long[][] newTimes2 = new long[uids.length][freqs.length];
-        for (int i = 0; i < uids.length; ++i) {
-            for (int j = 0; j < freqs.length; ++j) {
-                newTimes2[i][j] = (newTimes1[i][j] + uids[i] * freqs[j]) * 10;
-            }
-        }
-        newTimes2[uids.length - 1][freqs.length - 1] =
-                newTimes1[uids.length - 1][freqs.length - 1] - 222;
-        when(mBufferedReader.readLine())
-                .thenReturn(getFreqsLine(freqs), getUidTimesLines(uids, newTimes2));
-        mKernelUidCpuFreqTimeReader.readDelta(mBufferedReader, mCallback);
-        for (int i = 0; i < uids.length; ++i) {
-            if (i == uids.length - 1) {
-                continue;
-            }
-            Mockito.verify(mCallback).onUidCpuFreqTime(uids[i],
-                    subtract(newTimes2[i], newTimes1[i]));
-        }
-        verifyNoMoreInteractions(mCallback);
-
-        // Verify that the internal state was not modified when the proc file had decreasing values.
-        Mockito.reset(mCallback, mBufferedReader);
-        for (int i = 0; i < freqs.length; ++i) {
-            newTimes2[uids.length - 1][i] = newTimes1[uids.length - 1][i];
-        }
-        when(mBufferedReader.readLine())
-                .thenReturn(getFreqsLine(freqs), getUidTimesLines(uids, newTimes2));
-        mKernelUidCpuFreqTimeReader.readDelta(mBufferedReader, mCallback);
-        verifyNoMoreInteractions(mCallback);
-    }
-
     private long[] subtract(long[] a1, long[] a2) {
         long[] val = new long[a1.length];
         for (int i = 0; i < val.length; ++i) {
@@ -438,21 +276,6 @@
         return sb.toString();
     }
 
-    private String[] getUidTimesLines(int[] uids, long[][] times) {
-        final String[] lines = new String[uids.length + 1];
-        final StringBuilder sb = new StringBuilder();
-        for (int i = 0; i < uids.length; ++i) {
-            sb.setLength(0);
-            sb.append(uids[i] + ":");
-            for (int j = 0; j < times[i].length; ++j) {
-                sb.append(" " + times[i][j] / 10);
-            }
-            lines[i] = sb.toString();
-        }
-        lines[uids.length] = null;
-        return lines;
-    }
-
     private ByteBuffer getUidTimesBytes(int[] uids, long[][] times) {
         int size = (1 + uids.length + uids.length * times[0].length) * 4;
         ByteBuffer buf = ByteBuffer.allocate(size);
diff --git a/core/tests/coretests/src/com/android/internal/os/MockBatteryStatsImpl.java b/core/tests/coretests/src/com/android/internal/os/MockBatteryStatsImpl.java
index 6d5c7e774..36e54ad 100644
--- a/core/tests/coretests/src/com/android/internal/os/MockBatteryStatsImpl.java
+++ b/core/tests/coretests/src/com/android/internal/os/MockBatteryStatsImpl.java
@@ -22,6 +22,7 @@
 
 import com.android.internal.location.gnssmetrics.GnssMetrics;
 import java.util.ArrayList;
+import java.util.Queue;
 import java.util.concurrent.Future;
 
 /**
@@ -66,6 +67,10 @@
         return mScreenState;
     }
 
+    public Queue<UidToRemove> getPendingRemovedUids() {
+        return mPendingRemovedUids;
+    }
+
     public boolean isOnBattery() {
         return mForceOnBattery ? true : super.isOnBattery();
     }
diff --git a/services/core/java/com/android/server/am/BatteryExternalStatsWorker.java b/services/core/java/com/android/server/am/BatteryExternalStatsWorker.java
index 1f10181..f5e1a31 100644
--- a/services/core/java/com/android/server/am/BatteryExternalStatsWorker.java
+++ b/services/core/java/com/android/server/am/BatteryExternalStatsWorker.java
@@ -306,6 +306,7 @@
                 for (int uid : uidsToRemove) {
                     mStats.removeIsolatedUidLocked(uid);
                 }
+                mStats.clearPendingRemovedUids();
             }
         }
     };
diff --git a/services/core/java/com/android/server/am/BatteryStatsService.java b/services/core/java/com/android/server/am/BatteryStatsService.java
index 3db1da5..3c49ece 100644
--- a/services/core/java/com/android/server/am/BatteryStatsService.java
+++ b/services/core/java/com/android/server/am/BatteryStatsService.java
@@ -1193,6 +1193,7 @@
         pw.println("  --new-daily: immediately create and write new daily stats record.");
         pw.println("  --read-daily: read-load last written daily stats.");
         pw.println("  --settings: dump the settings key/values related to batterystats");
+        pw.println("  --cpu: dump cpu stats for debugging purpose");
         pw.println("  <package.name>: optional name of package to filter output by.");
         pw.println("  -h: print this help text.");
         pw.println("Battery stats (batterystats) commands:");
@@ -1211,6 +1212,12 @@
         }
     }
 
+    private void dumpCpuStats(PrintWriter pw) {
+        synchronized (mStats) {
+            mStats.dumpCpuStatsLocked(pw);
+        }
+    }
+
     private int doEnableOrDisable(PrintWriter pw, int i, String[] args, boolean enable) {
         i++;
         if (i >= args.length) {
@@ -1324,6 +1331,9 @@
                 } else if ("--settings".equals(arg)) {
                     dumpSettings(pw);
                     return;
+                } else if ("--cpu".equals(arg)) {
+                    dumpCpuStats(pw);
+                    return;
                 } else if ("-a".equals(arg)) {
                     flags |= BatteryStats.DUMP_VERBOSE;
                 } else if (arg.length() > 0 && arg.charAt(0) == '-'){
diff --git a/services/core/java/com/android/server/stats/StatsCompanionService.java b/services/core/java/com/android/server/stats/StatsCompanionService.java
index afcedcc..74c5ee9 100644
--- a/services/core/java/com/android/server/stats/StatsCompanionService.java
+++ b/services/core/java/com/android/server/stats/StatsCompanionService.java
@@ -177,7 +177,6 @@
         // frameworks/base/core/java/com/android/internal/os/KernelCpuProcReader
         mKernelUidCpuFreqTimeReader.setThrottleInterval(0);
         long[] freqs = mKernelUidCpuFreqTimeReader.readFreqs(powerProfile);
-        mKernelUidCpuFreqTimeReader.setReadBinary(true);
         mKernelUidCpuClusterTimeReader.setThrottleInterval(0);
         mKernelUidCpuActiveTimeReader.setThrottleInterval(0);
     }