Added resource power manager stats to batterystats
Currently Resource Power Management (e.g. VMIN time) is reported in the
batterystats history each time the battery level changes. We need this
information in the batterystats checkin, and need to be able to
differentiate time spent in each power state/voter, total as well as
when the screen is off.
The RPM information is obtained via a JNI call to the appropriate HALs.
Batterystats report version is increased to 26.
Bug: 62549421
Test: manual
Change-Id: I0c384eb3950714d8a0aa1353a4bf965330ebc8c1
diff --git a/core/java/android/os/BatteryStats.java b/core/java/android/os/BatteryStats.java
index cea5715..49afeeb 100644
--- a/core/java/android/os/BatteryStats.java
+++ b/core/java/android/os/BatteryStats.java
@@ -213,8 +213,10 @@
* - Fixed bugs in background timers and BLE scan time
* New in version 25:
* - Package wakeup alarms are now on screen-off timebase
+ * New in version 26:
+ * - Resource power manager (rpm) states
*/
- static final String CHECKIN_VERSION = "25";
+ static final String CHECKIN_VERSION = "26";
/**
* Old version, we hit 9 and ran out of room, need to remove.
@@ -233,6 +235,10 @@
private static final String CPU_DATA = "cpu";
private static final String GLOBAL_CPU_FREQ_DATA = "gcf";
private static final String CPU_TIMES_AT_FREQ_DATA = "ctf";
+ // rpm line is:
+ // BATTERY_STATS_CHECKIN_VERSION, uid, which, "rpm", state/voter name, total time, total count,
+ // screen-off time, screen-off count
+ private static final String RESOURCE_POWER_MANAGER_DATA = "rpm";
private static final String SENSOR_DATA = "sr";
private static final String VIBRATOR_DATA = "vib";
private static final String FOREGROUND_ACTIVITY_DATA = "fg";
@@ -2648,6 +2654,16 @@
public abstract Map<String, ? extends Timer> getKernelWakelockStats();
+ /**
+ * Returns Timers tracking the total time of each Resource Power Manager state and voter.
+ */
+ public abstract Map<String, ? extends Timer> getRpmStats();
+ /**
+ * Returns Timers tracking the screen-off time of each Resource Power Manager state and voter.
+ */
+ public abstract Map<String, ? extends Timer> getScreenOffRpmStats();
+
+
public abstract LongSparseArray<? extends Timer> getKernelMemoryStats();
public abstract void writeToParcelWithoutUids(Parcel out, int flags);
@@ -3309,6 +3325,24 @@
}
}
+ final Map<String, ? extends Timer> rpmStats = getRpmStats();
+ final Map<String, ? extends Timer> screenOffRpmStats = getScreenOffRpmStats();
+ if (rpmStats.size() > 0) {
+ for (Map.Entry<String, ? extends Timer> ent : rpmStats.entrySet()) {
+ sb.setLength(0);
+ Timer totalTimer = ent.getValue();
+ long timeMs = (totalTimer.getTotalTimeLocked(rawRealtime, which) + 500) / 1000;
+ int count = totalTimer.getCountLocked(which);
+ Timer screenOffTimer = screenOffRpmStats.get(ent.getKey());
+ long screenOffTimeMs = screenOffTimer != null
+ ? (screenOffTimer.getTotalTimeLocked(rawRealtime, which) + 500) / 1000 : 0;
+ int screenOffCount = screenOffTimer != null
+ ? screenOffTimer.getCountLocked(which) : 0;
+ dumpLine(pw, 0 /* uid */, category, RESOURCE_POWER_MANAGER_DATA,
+ "\"" + ent.getKey() + "\"", timeMs, count, screenOffTimeMs, screenOffCount);
+ }
+ }
+
final BatteryStatsHelper helper = new BatteryStatsHelper(context, false, wifiOnly);
helper.create(this);
helper.refreshStats(which, UserHandle.USER_ALL);
@@ -4570,24 +4604,54 @@
}
final LongSparseArray<? extends Timer> mMemoryStats = getKernelMemoryStats();
- pw.println("Memory Stats");
- for (int i = 0; i < mMemoryStats.size(); i++) {
- sb.setLength(0);
- sb.append("Bandwidth ");
- sb.append(mMemoryStats.keyAt(i));
- sb.append(" Time ");
- sb.append(mMemoryStats.valueAt(i).getTotalTimeLocked(rawRealtime, which));
- pw.println(sb.toString());
+ if (mMemoryStats.size() > 0) {
+ pw.println(" Memory Stats");
+ for (int i = 0; i < mMemoryStats.size(); i++) {
+ sb.setLength(0);
+ sb.append(" Bandwidth ");
+ sb.append(mMemoryStats.keyAt(i));
+ sb.append(" Time ");
+ sb.append(mMemoryStats.valueAt(i).getTotalTimeLocked(rawRealtime, which));
+ pw.println(sb.toString());
+ }
+ pw.println();
+ }
+
+ final Map<String, ? extends Timer> rpmStats = getRpmStats();
+ if (rpmStats.size() > 0) {
+ pw.print(prefix); pw.println(" Resource Power Manager Stats");
+ if (rpmStats.size() > 0) {
+ for (Map.Entry<String, ? extends Timer> ent : rpmStats.entrySet()) {
+ final String timerName = ent.getKey();
+ final Timer timer = ent.getValue();
+ printTimer(pw, sb, timer, rawRealtime, which, prefix, timerName);
+ }
+ }
+ pw.println();
+ }
+ final Map<String, ? extends Timer> screenOffRpmStats = getScreenOffRpmStats();
+ if (screenOffRpmStats.size() > 0) {
+ pw.print(prefix);
+ pw.println(" Resource Power Manager Stats for when screen was off");
+ if (screenOffRpmStats.size() > 0) {
+ for (Map.Entry<String, ? extends Timer> ent : screenOffRpmStats.entrySet()) {
+ final String timerName = ent.getKey();
+ final Timer timer = ent.getValue();
+ printTimer(pw, sb, timer, rawRealtime, which, prefix, timerName);
+ }
+ }
+ pw.println();
}
final long[] cpuFreqs = getCpuFreqs();
if (cpuFreqs != null) {
sb.setLength(0);
- sb.append("CPU freqs:");
+ sb.append(" CPU freqs:");
for (int i = 0; i < cpuFreqs.length; ++i) {
sb.append(" " + cpuFreqs[i]);
}
pw.println(sb.toString());
+ pw.println();
}
for (int iu=0; iu<NU; iu++) {
diff --git a/core/java/com/android/internal/os/BatteryStatsImpl.java b/core/java/com/android/internal/os/BatteryStatsImpl.java
index d20345b..90455f6 100644
--- a/core/java/com/android/internal/os/BatteryStatsImpl.java
+++ b/core/java/com/android/internal/os/BatteryStatsImpl.java
@@ -119,7 +119,7 @@
private static final int MAGIC = 0xBA757475; // 'BATSTATS'
// Current on-disk Parcel version
- private static final int VERSION = 165 + (USE_OLD_HISTORY ? 1000 : 0);
+ private static final int VERSION = 166 + (USE_OLD_HISTORY ? 1000 : 0);
// Maximum number of items we will record in the history.
private static final int MAX_HISTORY_ITEMS;
@@ -183,6 +183,9 @@
return mKernelMemoryStats;
}
+ /** Temporary container for Resource Power Manager stats. */
+ private final RpmStats mTmpRpmStats = new RpmStats();
+
public interface BatteryCallback {
public void batteryNeedsCpuUpdate();
public void batteryPowerChanged(boolean onBattery);
@@ -190,6 +193,7 @@
}
public interface PlatformIdleStateCallback {
+ public void fillLowPowerStats(RpmStats rpmStats);
public String getPlatformLowPowerStats();
public String getSubsystemLowPowerStats();
}
@@ -265,7 +269,8 @@
int UPDATE_WIFI = 0x02;
int UPDATE_RADIO = 0x04;
int UPDATE_BT = 0x08;
- int UPDATE_ALL = UPDATE_CPU | UPDATE_WIFI | UPDATE_RADIO | UPDATE_BT;
+ int UPDATE_RPM = 0x10; // 16
+ int UPDATE_ALL = UPDATE_CPU | UPDATE_WIFI | UPDATE_RADIO | UPDATE_BT | UPDATE_RPM;
Future<?> scheduleSync(String reason, int flags);
Future<?> scheduleCpuSyncDueToRemovedUid(int uid);
@@ -609,6 +614,24 @@
private PowerProfile mPowerProfile;
/*
+ * Holds a SamplingTimer associated with each Resource Power Manager state and voter,
+ * recording their times when on-battery (regardless of screen state).
+ */
+ private final HashMap<String, SamplingTimer> mRpmStats = new HashMap<>();
+ /** Times for each Resource Power Manager state and voter when screen-off and on-battery. */
+ private final HashMap<String, SamplingTimer> mScreenOffRpmStats = new HashMap<>();
+
+ @Override
+ public Map<String, ? extends Timer> getRpmStats() {
+ return mRpmStats;
+ }
+
+ @Override
+ public Map<String, ? extends Timer> getScreenOffRpmStats() {
+ return mScreenOffRpmStats;
+ }
+
+ /*
* Holds a SamplingTimer associated with each kernel wakelock name being tracked.
*/
private final HashMap<String, SamplingTimer> mKernelWakelockStats = new HashMap<>();
@@ -2628,6 +2651,26 @@
}
}
+ /** Get Resource Power Manager stats. Create a new one if it doesn't already exist. */
+ public SamplingTimer getRpmTimerLocked(String name) {
+ SamplingTimer rpmt = mRpmStats.get(name);
+ if (rpmt == null) {
+ rpmt = new SamplingTimer(mClocks, mOnBatteryTimeBase);
+ mRpmStats.put(name, rpmt);
+ }
+ return rpmt;
+ }
+
+ /** Get Screen-off Resource Power Manager stats. Create new one if it doesn't already exist. */
+ public SamplingTimer getScreenOffRpmTimerLocked(String name) {
+ SamplingTimer rpmt = mScreenOffRpmStats.get(name);
+ if (rpmt == null) {
+ rpmt = new SamplingTimer(mClocks, mOnBatteryScreenOffTimeBase);
+ mScreenOffRpmStats.put(name, rpmt);
+ }
+ return rpmt;
+ }
+
/*
* Get the wakeup reason counter, and create a new one if one
* doesn't already exist.
@@ -3516,6 +3559,7 @@
updateKernelWakelocksLocked();
updateBatteryPropertiesLocked();
}
+ updateRpmStatsLocked(); // if either OnBattery or OnBatteryScreenOff timebase changes.
if (DEBUG_ENERGY_CPU) {
Slog.d(TAG, "Updating cpu time because screen is now " + (screenOff ? "off" : "on")
+ " and battery is " + (unplugged ? "on" : "off"));
@@ -9481,6 +9525,19 @@
}
}
+ if (mRpmStats.size() > 0) {
+ for (SamplingTimer timer : mRpmStats.values()) {
+ mOnBatteryTimeBase.remove(timer);
+ }
+ mRpmStats.clear();
+ }
+ if (mScreenOffRpmStats.size() > 0) {
+ for (SamplingTimer timer : mScreenOffRpmStats.values()) {
+ mOnBatteryScreenOffTimeBase.remove(timer);
+ }
+ mScreenOffRpmStats.clear();
+ }
+
if (mKernelWakelockStats.size() > 0) {
for (SamplingTimer timer : mKernelWakelockStats.values()) {
mOnBatteryScreenOffTimeBase.remove(timer);
@@ -10183,6 +10240,49 @@
}
/**
+ * Read and record Resource Power Manager state and voter times.
+ */
+ public void updateRpmStatsLocked() {
+ if (mPlatformIdleStateCallback == null) return;
+ mPlatformIdleStateCallback.fillLowPowerStats(mTmpRpmStats);
+
+ for (Map.Entry<String, RpmStats.PowerStatePlatformSleepState> pstate
+ : mTmpRpmStats.mPlatformLowPowerStats.entrySet()) {
+
+ // Update values for this platform state.
+ final String pName = pstate.getKey();
+ final long pTimeUs = pstate.getValue().mTimeMs * 1000;
+ final int pCount = pstate.getValue().mCount;
+ getRpmTimerLocked(pName).update(pTimeUs, pCount);
+ getScreenOffRpmTimerLocked(pName).update(pTimeUs, pCount);
+
+ // Update values for each voter of this platform state.
+ for (Map.Entry<String, RpmStats.PowerStateElement> voter
+ : pstate.getValue().mVoters.entrySet()) {
+ final String vName = pName + "." + voter.getKey();
+ final long vTimeUs = voter.getValue().mTimeMs * 1000;
+ final int vCount = voter.getValue().mCount;
+ getRpmTimerLocked(vName).update(vTimeUs, vCount);
+ getScreenOffRpmTimerLocked(vName).update(vTimeUs, vCount);
+ }
+ }
+
+ for (Map.Entry<String, RpmStats.PowerStateSubsystem> subsys
+ : mTmpRpmStats.mSubsystemLowPowerStats.entrySet()) {
+
+ final String subsysName = subsys.getKey();
+ for (Map.Entry<String, RpmStats.PowerStateElement> sstate
+ : subsys.getValue().mStates.entrySet()) {
+ final String name = subsysName + "." + sstate.getKey();
+ final long timeUs = sstate.getValue().mTimeMs * 1000;
+ final int count = sstate.getValue().mCount;
+ getRpmTimerLocked(name).update(timeUs, count);
+ getScreenOffRpmTimerLocked(name).update(timeUs, count);
+ }
+ }
+ }
+
+ /**
* Read and distribute kernel wake lock use across apps.
*/
public void updateKernelWakelocksLocked() {
@@ -11627,6 +11727,27 @@
mBluetoothScanNesting = 0;
mBluetoothScanTimer.readSummaryFromParcelLocked(in);
+ int NRPMS = in.readInt();
+ if (NRPMS > 10000) {
+ throw new ParcelFormatException("File corrupt: too many rpm stats " + NRPMS);
+ }
+ for (int irpm = 0; irpm < NRPMS; irpm++) {
+ if (in.readInt() != 0) {
+ String rpmName = in.readString();
+ getRpmTimerLocked(rpmName).readSummaryFromParcelLocked(in);
+ }
+ }
+ int NSORPMS = in.readInt();
+ if (NSORPMS > 10000) {
+ throw new ParcelFormatException("File corrupt: too many screen-off rpm stats " + NSORPMS);
+ }
+ for (int irpm = 0; irpm < NSORPMS; irpm++) {
+ if (in.readInt() != 0) {
+ String rpmName = in.readString();
+ getScreenOffRpmTimerLocked(rpmName).readSummaryFromParcelLocked(in);
+ }
+ }
+
int NKW = in.readInt();
if (NKW > 10000) {
throw new ParcelFormatException("File corrupt: too many kernel wake locks " + NKW);
@@ -12015,6 +12136,29 @@
mCameraOnTimer.writeSummaryFromParcelLocked(out, NOWREAL_SYS);
mBluetoothScanTimer.writeSummaryFromParcelLocked(out, NOWREAL_SYS);
+ out.writeInt(mRpmStats.size());
+ for (Map.Entry<String, SamplingTimer> ent : mRpmStats.entrySet()) {
+ Timer rpmt = ent.getValue();
+ if (rpmt != null) {
+ out.writeInt(1);
+ out.writeString(ent.getKey());
+ rpmt.writeSummaryFromParcelLocked(out, NOWREAL_SYS);
+ } else {
+ out.writeInt(0);
+ }
+ }
+ out.writeInt(mScreenOffRpmStats.size());
+ for (Map.Entry<String, SamplingTimer> ent : mScreenOffRpmStats.entrySet()) {
+ Timer rpmt = ent.getValue();
+ if (rpmt != null) {
+ out.writeInt(1);
+ out.writeString(ent.getKey());
+ rpmt.writeSummaryFromParcelLocked(out, NOWREAL_SYS);
+ } else {
+ out.writeInt(0);
+ }
+ }
+
out.writeInt(mKernelWakelockStats.size());
for (Map.Entry<String, SamplingTimer> ent : mKernelWakelockStats.entrySet()) {
Timer kwlt = ent.getValue();
@@ -12474,6 +12618,25 @@
mDischargeScreenOffCounter = new LongSamplingCounter(mOnBatteryTimeBase, in);
mLastWriteTime = in.readLong();
+ mRpmStats.clear();
+ int NRPMS = in.readInt();
+ for (int irpm = 0; irpm < NRPMS; irpm++) {
+ if (in.readInt() != 0) {
+ String rpmName = in.readString();
+ SamplingTimer rpmt = new SamplingTimer(mClocks, mOnBatteryTimeBase, in);
+ mRpmStats.put(rpmName, rpmt);
+ }
+ }
+ mScreenOffRpmStats.clear();
+ int NSORPMS = in.readInt();
+ for (int irpm = 0; irpm < NSORPMS; irpm++) {
+ if (in.readInt() != 0) {
+ String rpmName = in.readString();
+ SamplingTimer rpmt = new SamplingTimer(mClocks, mOnBatteryScreenOffTimeBase, in);
+ mScreenOffRpmStats.put(rpmName, rpmt);
+ }
+ }
+
mKernelWakelockStats.clear();
int NKW = in.readInt();
for (int ikw = 0; ikw < NKW; ikw++) {
@@ -12639,6 +12802,29 @@
mDischargeScreenOffCounter.writeToParcel(out);
out.writeLong(mLastWriteTime);
+ out.writeInt(mRpmStats.size());
+ for (Map.Entry<String, SamplingTimer> ent : mRpmStats.entrySet()) {
+ SamplingTimer rpmt = ent.getValue();
+ if (rpmt != null) {
+ out.writeInt(1);
+ out.writeString(ent.getKey());
+ rpmt.writeToParcel(out, uSecRealtime);
+ } else {
+ out.writeInt(0);
+ }
+ }
+ out.writeInt(mScreenOffRpmStats.size());
+ for (Map.Entry<String, SamplingTimer> ent : mScreenOffRpmStats.entrySet()) {
+ SamplingTimer rpmt = ent.getValue();
+ if (rpmt != null) {
+ out.writeInt(1);
+ out.writeString(ent.getKey());
+ rpmt.writeToParcel(out, uSecRealtime);
+ } else {
+ out.writeInt(0);
+ }
+ }
+
if (inclUids) {
out.writeInt(mKernelWakelockStats.size());
for (Map.Entry<String, SamplingTimer> ent : mKernelWakelockStats.entrySet()) {
@@ -12663,6 +12849,7 @@
}
}
} else {
+ // TODO: There should be two 0's printed here, not just one.
out.writeInt(0);
}
diff --git a/core/java/com/android/internal/os/RpmStats.java b/core/java/com/android/internal/os/RpmStats.java
new file mode 100644
index 0000000..befc76e
--- /dev/null
+++ b/core/java/com/android/internal/os/RpmStats.java
@@ -0,0 +1,112 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.internal.os;
+
+import android.util.ArrayMap;
+
+import java.util.Map;
+
+/**
+ * Container for Resource Power Manager states and their data.
+ * Values can be populated by the BatteryStatsService.fillLowPowerStats jni function.
+ */
+public final class RpmStats {
+ public Map<String, PowerStatePlatformSleepState> mPlatformLowPowerStats = new ArrayMap<>();
+ public Map<String, PowerStateSubsystem> mSubsystemLowPowerStats = new ArrayMap<>();
+
+ /**
+ * Finds the PowerStatePlatformSleepState with the given name (creating it if it doesn't exist),
+ * updates its timeMs and count, and returns it.
+ */
+ @SuppressWarnings("unused")
+ public PowerStatePlatformSleepState getAndUpdatePlatformState(
+ String name, long timeMs, int count) {
+
+ PowerStatePlatformSleepState e = mPlatformLowPowerStats.get(name);
+ if (e == null) {
+ e = new PowerStatePlatformSleepState();
+ mPlatformLowPowerStats.put(name, e);
+ }
+ e.mTimeMs = timeMs;
+ e.mCount = count;
+ return e;
+ }
+
+ /**
+ * Returns the PowerStateSubsystem with the given name (creating it if it doesn't exist).
+ */
+ public PowerStateSubsystem getSubsystem(String name) {
+ PowerStateSubsystem e = mSubsystemLowPowerStats.get(name);
+ if (e == null) {
+ e = new PowerStateSubsystem();
+ mSubsystemLowPowerStats.put(name, e);
+ }
+ return e;
+ }
+
+ /** Represents a subsystem state or a platform voter. */
+ public static class PowerStateElement {
+ public long mTimeMs; // totalTimeInMsecVotedForSinceBoot
+ public int mCount; // totalNumberOfTimesVotedSinceBoot
+
+ private PowerStateElement(long timeMs, int count) {
+ this.mTimeMs = timeMs;
+ this.mCount = count;
+ }
+ }
+
+ /** Represents a PowerStatePlatformSleepState, per hardware/interfaces/power/1.0/types.hal */
+ public static class PowerStatePlatformSleepState {
+ public long mTimeMs; // residencyInMsecSinceBoot
+ public int mCount; // totalTransitions
+ public Map<String, PowerStateElement> mVoters = new ArrayMap<>(); // voters for this platform-level sleep state
+
+ /**
+ * Updates (creating if necessary) the voter with the given name, with the given timeMs and
+ * count.
+ */
+ @SuppressWarnings("unused")
+ public void putVoter(String name, long timeMs, int count) {
+ PowerStateElement e = mVoters.get(name);
+ if (e == null) {
+ mVoters.put(name, new PowerStateElement(timeMs, count));
+ } else {
+ e.mTimeMs = timeMs;
+ e.mCount = count;
+ }
+ }
+ }
+
+ /** Represents a PowerStateSubsystem, per hardware/interfaces/power/1.1/types.hal */
+ public static class PowerStateSubsystem {
+ public Map<String, PowerStateElement> mStates = new ArrayMap<>(); // sleep states supported by this susbsystem
+
+ /**
+ * Updates (creating if necessary) the subsystem state with the given name, with the given
+ * timeMs and count.
+ */
+ @SuppressWarnings("unused")
+ public void putState(String name, long timeMs, int count) {
+ PowerStateElement e = mStates.get(name);
+ if (e == null) {
+ mStates.put(name, new PowerStateElement(timeMs, count));
+ } else {
+ e.mTimeMs = timeMs;
+ e.mCount = count;
+ }
+ }
+ }
+}