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);
}