From 1fadab5c36445bb9f0997904dbce44f8e234f847 Mon Sep 17 00:00:00 2001 From: Dianne Hackborn Date: Thu, 14 Apr 2011 17:57:33 -0700 Subject: More battery stats improvements. We now write to the parcel using deltas. For common situations, it only takes 4 bytes to write a delta (new command, time delta, significant state changes, flags indicating additional state that follows). Increasing the buffer size to 128K, this give us 32,768 samples if they all fit in the smallest delta. A device that is doing something every minute (like acquiring a wake lock or doing a wifi scan) for our max target battery life of 30 days would generate 43,200 samples. Also some turning to the maximum time between samples at which we decide to completely collapse two samples. Change-Id: I074a698d27ccf9389f9585abfc983af2f5ba7a54 --- core/java/android/os/BatteryStats.java | 215 ++++++++++++++++++--- .../com/android/internal/os/BatteryStatsImpl.java | 31 +-- 2 files changed, 205 insertions(+), 41 deletions(-) diff --git a/core/java/android/os/BatteryStats.java b/core/java/android/os/BatteryStats.java index d7483ba7e2a6..b0f3ac3e23a4 100644 --- a/core/java/android/os/BatteryStats.java +++ b/core/java/android/os/BatteryStats.java @@ -26,6 +26,7 @@ import android.content.pm.ApplicationInfo; import android.telephony.SignalStrength; import android.util.Log; import android.util.Printer; +import android.util.Slog; import android.util.SparseArray; import android.util.TimeUtils; @@ -408,16 +409,19 @@ public abstract class BatteryStats implements Parcelable { } public final static class HistoryItem implements Parcelable { + static final String TAG = "HistoryItem"; + static final boolean DEBUG = false; + public HistoryItem next; public long time; - public static final byte CMD_NULL = -1; - public static final byte CMD_UPDATE = 0; - public static final byte CMD_START = 1; - public static final byte CMD_OVERFLOW = 2; + public static final byte CMD_NULL = 0; + public static final byte CMD_UPDATE = 1; + public static final byte CMD_START = 2; + public static final byte CMD_OVERFLOW = 3; - public byte cmd; + public byte cmd = CMD_NULL; public byte batteryLevel; public byte batteryStatus; @@ -428,33 +432,38 @@ public abstract class BatteryStats implements Parcelable { public char batteryVoltage; // Constants from SCREEN_BRIGHTNESS_* - public static final int STATE_BRIGHTNESS_MASK = 0x000000f; + public static final int STATE_BRIGHTNESS_MASK = 0x0000000f; public static final int STATE_BRIGHTNESS_SHIFT = 0; // Constants from SIGNAL_STRENGTH_* - public static final int STATE_SIGNAL_STRENGTH_MASK = 0x00000f0; + public static final int STATE_SIGNAL_STRENGTH_MASK = 0x000000f0; public static final int STATE_SIGNAL_STRENGTH_SHIFT = 4; // Constants from ServiceState.STATE_* - public static final int STATE_PHONE_STATE_MASK = 0x0000f00; + public static final int STATE_PHONE_STATE_MASK = 0x00000f00; public static final int STATE_PHONE_STATE_SHIFT = 8; // Constants from DATA_CONNECTION_* - public static final int STATE_DATA_CONNECTION_MASK = 0x000f000; + public static final int STATE_DATA_CONNECTION_MASK = 0x0000f000; public static final int STATE_DATA_CONNECTION_SHIFT = 12; - public static final int STATE_BATTERY_PLUGGED_FLAG = 1<<30; - public static final int STATE_SCREEN_ON_FLAG = 1<<29; + // These states always appear directly in the first int token + // of a delta change; they should be ones that change relatively + // frequently. + public static final int STATE_WAKE_LOCK_FLAG = 1<<30; + public static final int STATE_SENSOR_ON_FLAG = 1<<29; public static final int STATE_GPS_ON_FLAG = 1<<28; - public static final int STATE_PHONE_IN_CALL_FLAG = 1<<27; - public static final int STATE_PHONE_SCANNING_FLAG = 1<<26; - public static final int STATE_WIFI_ON_FLAG = 1<<25; - public static final int STATE_WIFI_RUNNING_FLAG = 1<<24; - public static final int STATE_WIFI_FULL_LOCK_FLAG = 1<<23; - public static final int STATE_WIFI_SCAN_LOCK_FLAG = 1<<22; - public static final int STATE_WIFI_MULTICAST_ON_FLAG = 1<<21; - public static final int STATE_BLUETOOTH_ON_FLAG = 1<<20; - public static final int STATE_AUDIO_ON_FLAG = 1<<19; - public static final int STATE_VIDEO_ON_FLAG = 1<<18; - public static final int STATE_WAKE_LOCK_FLAG = 1<<17; - public static final int STATE_SENSOR_ON_FLAG = 1<<16; + public static final int STATE_PHONE_SCANNING_FLAG = 1<<27; + public static final int STATE_WIFI_RUNNING_FLAG = 1<<26; + public static final int STATE_WIFI_FULL_LOCK_FLAG = 1<<25; + public static final int STATE_WIFI_SCAN_LOCK_FLAG = 1<<24; + public static final int STATE_WIFI_MULTICAST_ON_FLAG = 1<<23; + // These are on the lower bits used for the command; if they change + // we need to write another int of data. + public static final int STATE_AUDIO_ON_FLAG = 1<<22; + public static final int STATE_VIDEO_ON_FLAG = 1<<21; + public static final int STATE_SCREEN_ON_FLAG = 1<<20; + public static final int STATE_BATTERY_PLUGGED_FLAG = 1<<19; + public static final int STATE_PHONE_IN_CALL_FLAG = 1<<18; + public static final int STATE_WIFI_ON_FLAG = 1<<17; + public static final int STATE_BLUETOOTH_ON_FLAG = 1<<16; public static final int MOST_INTERESTING_STATES = STATE_BATTERY_PLUGGED_FLAG | STATE_SCREEN_ON_FLAG @@ -487,10 +496,6 @@ public abstract class BatteryStats implements Parcelable { dest.writeInt(bat); dest.writeInt(states); } - - public void writeDelta(Parcel dest, HistoryItem last) { - writeToParcel(dest, 0); - } private void readFromParcel(Parcel src) { int bat = src.readInt(); @@ -505,11 +510,161 @@ public abstract class BatteryStats implements Parcelable { states = src.readInt(); } - public void readDelta(Parcel src, HistoryItem last) { - time = src.readLong(); - readFromParcel(src); + // Part of initial delta int that specifies the time delta. + static final int DELTA_TIME_MASK = 0x3ffff; + static final int DELTA_TIME_ABS = 0x3fffd; // Following is an entire abs update. + static final int DELTA_TIME_INT = 0x3fffe; // The delta is a following int + static final int DELTA_TIME_LONG = 0x3ffff; // The delta is a following long + // Part of initial delta int holding the command code. + static final int DELTA_CMD_MASK = 0x3; + static final int DELTA_CMD_SHIFT = 18; + // Flag in delta int: a new battery level int follows. + static final int DELTA_BATTERY_LEVEL_FLAG = 1<<20; + // Flag in delta int: a new full state and battery status int follows. + static final int DELTA_STATE_FLAG = 1<<21; + static final int DELTA_STATE_MASK = 0xffc00000; + + public void writeDelta(Parcel dest, HistoryItem last) { + if (last == null || last.cmd != CMD_UPDATE) { + dest.writeInt(DELTA_TIME_ABS); + writeToParcel(dest, 0); + return; + } + + final long deltaTime = time - last.time; + final int lastBatteryLevelInt = last.buildBatteryLevelInt(); + final int lastStateInt = last.buildStateInt(); + + int deltaTimeToken; + if (deltaTime < 0 || deltaTime > Integer.MAX_VALUE) { + deltaTimeToken = DELTA_TIME_LONG; + } else if (deltaTime >= DELTA_TIME_ABS) { + deltaTimeToken = DELTA_TIME_INT; + } else { + deltaTimeToken = (int)deltaTime; + } + int firstToken = deltaTimeToken + | (cmd<>DELTA_CMD_SHIFT)&DELTA_CMD_MASK); + if (DEBUG) Slog.i(TAG, "READ DELTA: firstToken=0x" + Integer.toHexString(firstToken) + + " deltaTimeToken=" + deltaTimeToken); + + if (deltaTimeToken < DELTA_TIME_ABS) { + time += deltaTimeToken; + } else if (deltaTimeToken == DELTA_TIME_ABS) { + time = src.readLong(); + readFromParcel(src); + return; + } else if (deltaTimeToken == DELTA_TIME_INT) { + int delta = src.readInt(); + time += delta; + if (DEBUG) Slog.i(TAG, "READ DELTA: time delta=" + delta + " new time=" + time); + } else { + long delta = src.readLong(); + if (DEBUG) Slog.i(TAG, "READ DELTA: time delta=" + delta + " new time=" + time); + time += delta; + } + + if ((firstToken&DELTA_BATTERY_LEVEL_FLAG) != 0) { + int batteryLevelInt = src.readInt(); + batteryLevel = (byte)((batteryLevelInt>>24)&0xff); + batteryTemperature = (char)((batteryLevelInt>>14)&0x3ff); + batteryVoltage = (char)(batteryLevelInt&0x3fff); + if (DEBUG) Slog.i(TAG, "READ DELTA: batteryToken=0x" + + Integer.toHexString(batteryLevelInt) + + " batteryLevel=" + batteryLevel + + " batteryTemp=" + (int)batteryTemperature + + " batteryVolt=" + (int)batteryVoltage); + } + + if ((firstToken&DELTA_STATE_FLAG) != 0) { + int stateInt = src.readInt(); + states = (firstToken&DELTA_STATE_MASK) | (stateInt&(~DELTA_STATE_MASK)); + batteryStatus = (byte)((stateInt>>28)&0xf); + batteryHealth = (byte)((stateInt>>24)&0xf); + batteryPlugType = (byte)((stateInt>>22)&0x3); + if (DEBUG) Slog.i(TAG, "READ DELTA: stateToken=0x" + + Integer.toHexString(stateInt) + + " batteryStatus=" + batteryStatus + + " batteryHealth=" + batteryHealth + + " batteryPlugType=" + batteryPlugType + + " states=0x" + Integer.toHexString(states)); + } else { + states = (firstToken&DELTA_STATE_MASK) | (states&(~DELTA_STATE_MASK)); + } } + public void clear() { + time = 0; + cmd = CMD_NULL; + batteryLevel = 0; + batteryStatus = 0; + batteryHealth = 0; + batteryPlugType = 0; + batteryTemperature = 0; + batteryVoltage = 0; + states = 0; + } + public void setTo(HistoryItem o) { time = o.time; cmd = o.cmd; diff --git a/core/java/com/android/internal/os/BatteryStatsImpl.java b/core/java/com/android/internal/os/BatteryStatsImpl.java index fcda673d13ac..7cf33fcee754 100644 --- a/core/java/com/android/internal/os/BatteryStatsImpl.java +++ b/core/java/com/android/internal/os/BatteryStatsImpl.java @@ -71,7 +71,7 @@ public final class BatteryStatsImpl extends BatteryStats { private static final int MAGIC = 0xBA757475; // 'BATSTATS' // Current on-disk Parcel version - private static final int VERSION = 57; + private static final int VERSION = 60; // Maximum number of items we will record in the history. private static final int MAX_HISTORY_ITEMS = 2000; @@ -156,11 +156,12 @@ public final class BatteryStatsImpl extends BatteryStats { boolean mRecordingHistory = true; int mNumHistoryItems; - static final int MAX_HISTORY_BUFFER = 64*1024; // 64KB - static final int MAX_MAX_HISTORY_BUFFER = 92*1024; // 92KB + static final int MAX_HISTORY_BUFFER = 128*1024; // 128KB + static final int MAX_MAX_HISTORY_BUFFER = 144*1024; // 144KB final Parcel mHistoryBuffer = Parcel.obtain(); final HistoryItem mHistoryLastWritten = new HistoryItem(); final HistoryItem mHistoryLastLastWritten = new HistoryItem(); + final HistoryItem mHistoryReadTmp = new HistoryItem(); int mHistoryBufferLastPos = -1; boolean mHistoryOverflow = false; long mLastHistoryTime = 0; @@ -1212,8 +1213,9 @@ public final class BatteryStatsImpl extends BatteryStats { return; } + final long timeDiff = (mHistoryBaseTime+curTime) - mHistoryLastWritten.time; if (mHistoryBufferLastPos >= 0 && mHistoryLastWritten.cmd == HistoryItem.CMD_UPDATE - && (mHistoryBaseTime+curTime) < (mHistoryLastWritten.time+2000) + && timeDiff < 2000 && ((mHistoryLastWritten.states^mHistoryCur.states)&mChangedBufferStates) == 0) { // If the current is the same as the one before, then we no // longer need the entry. @@ -1221,7 +1223,7 @@ public final class BatteryStatsImpl extends BatteryStats { mHistoryBuffer.setDataPosition(mHistoryBufferLastPos); mHistoryBufferLastPos = -1; if (mHistoryLastLastWritten.cmd == HistoryItem.CMD_UPDATE - && mHistoryLastLastWritten.same(mHistoryCur)) { + && timeDiff < 500 && mHistoryLastLastWritten.same(mHistoryCur)) { // If this results in us returning to the state written // prior to the last one, then we can just delete the last // written one and drop the new one. Nothing more to do. @@ -1231,6 +1233,7 @@ public final class BatteryStatsImpl extends BatteryStats { } mChangedBufferStates |= mHistoryLastWritten.states^mHistoryCur.states; curTime = mHistoryLastWritten.time - mHistoryBaseTime; + mHistoryLastWritten.setTo(mHistoryLastLastWritten); } else { mChangedBufferStates = 0; } @@ -1295,6 +1298,7 @@ public final class BatteryStatsImpl extends BatteryStats { // If the current is the same as the one before, then we no // longer need the entry. if (mHistoryLastEnd != null && mHistoryLastEnd.cmd == HistoryItem.CMD_UPDATE + && (mHistoryBaseTime+curTime) < (mHistoryEnd.time+500) && mHistoryLastEnd.same(mHistoryCur)) { mHistoryLastEnd.next = null; mHistoryEnd.next = mHistoryCache; @@ -4038,6 +4042,7 @@ public final class BatteryStatsImpl extends BatteryStats { if (DEBUG_HISTORY) Slog.i(TAG, "ITERATING: buff size=" + mHistoryBuffer.dataSize() + " pos=" + mHistoryBuffer.dataPosition()); mHistoryBuffer.setDataPosition(0); + mHistoryReadTmp.clear(); mReadOverflow = false; mIteratingHistory = true; return (mHistoryIterator = mHistory) != null; @@ -4047,8 +4052,8 @@ public final class BatteryStatsImpl extends BatteryStats { public boolean getNextOldHistoryLocked(HistoryItem out) { boolean end = mHistoryBuffer.dataPosition() >= mHistoryBuffer.dataSize(); if (!end) { - mHistoryLastWritten.readDelta(mHistoryBuffer, null); - mReadOverflow |= mHistoryLastWritten.cmd == HistoryItem.CMD_OVERFLOW; + mHistoryReadTmp.readDelta(mHistoryBuffer); + mReadOverflow |= mHistoryReadTmp.cmd == HistoryItem.CMD_OVERFLOW; } HistoryItem cur = mHistoryIterator; if (cur == null) { @@ -4062,14 +4067,14 @@ public final class BatteryStatsImpl extends BatteryStats { if (!mReadOverflow) { if (end) { Slog.w(TAG, "New history ends before old history!"); - } else if (!out.same(mHistoryLastWritten)) { + } else if (!out.same(mHistoryReadTmp)) { long now = getHistoryBaseTime() + SystemClock.elapsedRealtime(); PrintWriter pw = new PrintWriter(new LogWriter(android.util.Log.WARN, TAG)); pw.println("Histories differ!"); pw.println("Old history:"); (new HistoryPrinter()).printNextItem(pw, out, now); pw.println("New history:"); - (new HistoryPrinter()).printNextItem(pw, mHistoryLastWritten, now); + (new HistoryPrinter()).printNextItem(pw, mHistoryReadTmp, now); } } return true; @@ -4093,12 +4098,16 @@ public final class BatteryStatsImpl extends BatteryStats { @Override public boolean getNextHistoryLocked(HistoryItem out) { - boolean end = mHistoryBuffer.dataPosition() >= mHistoryBuffer.dataSize(); + final int pos = mHistoryBuffer.dataPosition(); + if (pos == 0) { + out.clear(); + } + boolean end = pos >= mHistoryBuffer.dataSize(); if (end) { return false; } - out.readDelta(mHistoryBuffer, null); + out.readDelta(mHistoryBuffer); return true; } -- cgit v1.2.3-59-g8ed1b