Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 1fadab5c authored by Dianne Hackborn's avatar Dianne Hackborn
Browse files

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
parent 44329ad5
Loading
Loading
Loading
Loading
+185 −30
Original line number Original line Diff line number Diff line
@@ -26,6 +26,7 @@ import android.content.pm.ApplicationInfo;
import android.telephony.SignalStrength;
import android.telephony.SignalStrength;
import android.util.Log;
import android.util.Log;
import android.util.Printer;
import android.util.Printer;
import android.util.Slog;
import android.util.SparseArray;
import android.util.SparseArray;
import android.util.TimeUtils;
import android.util.TimeUtils;


@@ -408,16 +409,19 @@ public abstract class BatteryStats implements Parcelable {
    }
    }


    public final static class HistoryItem implements Parcelable {
    public final static class HistoryItem implements Parcelable {
        static final String TAG = "HistoryItem";
        static final boolean DEBUG = false;
        
        public HistoryItem next;
        public HistoryItem next;
        
        
        public long time;
        public long time;
        
        
        public static final byte CMD_NULL = -1;
        public static final byte CMD_NULL = 0;
        public static final byte CMD_UPDATE = 0;
        public static final byte CMD_UPDATE = 1;
        public static final byte CMD_START = 1;
        public static final byte CMD_START = 2;
        public static final byte CMD_OVERFLOW = 2;
        public static final byte CMD_OVERFLOW = 3;
        
        
        public byte cmd;
        public byte cmd = CMD_NULL;
        
        
        public byte batteryLevel;
        public byte batteryLevel;
        public byte batteryStatus;
        public byte batteryStatus;
@@ -428,33 +432,38 @@ public abstract class BatteryStats implements Parcelable {
        public char batteryVoltage;
        public char batteryVoltage;
        
        
        // Constants from SCREEN_BRIGHTNESS_*
        // 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;
        public static final int STATE_BRIGHTNESS_SHIFT = 0;
        // Constants from SIGNAL_STRENGTH_*
        // 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;
        public static final int STATE_SIGNAL_STRENGTH_SHIFT = 4;
        // Constants from ServiceState.STATE_*
        // 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;
        public static final int STATE_PHONE_STATE_SHIFT = 8;
        // Constants from DATA_CONNECTION_*
        // 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_DATA_CONNECTION_SHIFT = 12;
        
        
        public static final int STATE_BATTERY_PLUGGED_FLAG = 1<<30;
        // These states always appear directly in the first int token
        public static final int STATE_SCREEN_ON_FLAG = 1<<29;
        // 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_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<<27;
        public static final int STATE_PHONE_SCANNING_FLAG = 1<<26;
        public static final int STATE_WIFI_RUNNING_FLAG = 1<<26;
        public static final int STATE_WIFI_ON_FLAG = 1<<25;
        public static final int STATE_WIFI_FULL_LOCK_FLAG = 1<<25;
        public static final int STATE_WIFI_RUNNING_FLAG = 1<<24;
        public static final int STATE_WIFI_SCAN_LOCK_FLAG = 1<<24;
        public static final int STATE_WIFI_FULL_LOCK_FLAG = 1<<23;
        public static final int STATE_WIFI_MULTICAST_ON_FLAG = 1<<23;
        public static final int STATE_WIFI_SCAN_LOCK_FLAG = 1<<22;
        // These are on the lower bits used for the command; if they change
        public static final int STATE_WIFI_MULTICAST_ON_FLAG = 1<<21;
        // we need to write another int of data.
        public static final int STATE_BLUETOOTH_ON_FLAG = 1<<20;
        public static final int STATE_AUDIO_ON_FLAG = 1<<22;
        public static final int STATE_AUDIO_ON_FLAG = 1<<19;
        public static final int STATE_VIDEO_ON_FLAG = 1<<21;
        public static final int STATE_VIDEO_ON_FLAG = 1<<18;
        public static final int STATE_SCREEN_ON_FLAG = 1<<20;
        public static final int STATE_WAKE_LOCK_FLAG = 1<<17;
        public static final int STATE_BATTERY_PLUGGED_FLAG = 1<<19;
        public static final int STATE_SENSOR_ON_FLAG = 1<<16;
        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 =
        public static final int MOST_INTERESTING_STATES =
            STATE_BATTERY_PLUGGED_FLAG | STATE_SCREEN_ON_FLAG
            STATE_BATTERY_PLUGGED_FLAG | STATE_SCREEN_ON_FLAG
@@ -488,10 +497,6 @@ public abstract class BatteryStats implements Parcelable {
            dest.writeInt(states);
            dest.writeInt(states);
        }
        }


        public void writeDelta(Parcel dest, HistoryItem last) {
            writeToParcel(dest, 0);
        }

        private void readFromParcel(Parcel src) {
        private void readFromParcel(Parcel src) {
            int bat = src.readInt();
            int bat = src.readInt();
            cmd = (byte)(bat&0xff);
            cmd = (byte)(bat&0xff);
@@ -505,9 +510,159 @@ public abstract class BatteryStats implements Parcelable {
            states = src.readInt();
            states = src.readInt();
        }
        }


        public void readDelta(Parcel src, HistoryItem last) {
        // 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)
                    | (states&DELTA_STATE_MASK);
            final int batteryLevelInt = buildBatteryLevelInt();
            final boolean batteryLevelIntChanged = batteryLevelInt != lastBatteryLevelInt;
            if (batteryLevelIntChanged) {
                firstToken |= DELTA_BATTERY_LEVEL_FLAG;
            }
            final int stateInt = buildStateInt();
            final boolean stateIntChanged = stateInt != lastStateInt;
            if (stateIntChanged) {
                firstToken |= DELTA_STATE_FLAG;
            }
            dest.writeInt(firstToken);
            if (DEBUG) Slog.i(TAG, "WRITE DELTA: firstToken=0x" + Integer.toHexString(firstToken)
                    + " deltaTime=" + deltaTime);
            
            if (deltaTimeToken >= DELTA_TIME_INT) {
                if (deltaTimeToken == DELTA_TIME_INT) {
                    if (DEBUG) Slog.i(TAG, "WRITE DELTA: int deltaTime=" + (int)deltaTime);
                    dest.writeInt((int)deltaTime);
                } else {
                    if (DEBUG) Slog.i(TAG, "WRITE DELTA: long deltaTime=" + deltaTime);
                    dest.writeLong(deltaTime);
                }
            }
            if (batteryLevelIntChanged) {
                dest.writeInt(batteryLevelInt);
                if (DEBUG) Slog.i(TAG, "WRITE DELTA: batteryToken=0x"
                        + Integer.toHexString(batteryLevelInt)
                        + " batteryLevel=" + batteryLevel
                        + " batteryTemp=" + (int)batteryTemperature
                        + " batteryVolt=" + (int)batteryVoltage);
            }
            if (stateIntChanged) {
                dest.writeInt(stateInt);
                if (DEBUG) Slog.i(TAG, "WRITE DELTA: stateToken=0x"
                        + Integer.toHexString(stateInt)
                        + " batteryStatus=" + batteryStatus
                        + " batteryHealth=" + batteryHealth
                        + " batteryPlugType=" + batteryPlugType
                        + " states=0x" + Integer.toHexString(states));
            }
        }
        
        private int buildBatteryLevelInt() {
            return ((((int)batteryLevel)<<24)&0xff000000)
                    | ((((int)batteryTemperature)<<14)&0x00ffc000)
                    | (((int)batteryVoltage)&0x00003fff);
        }
        
        private int buildStateInt() {
            return ((((int)batteryStatus)<<28)&0xf0000000)
                    | ((((int)batteryHealth)<<24)&0x0f000000)
                    | ((((int)batteryPlugType)<<22)&0x00c00000)
                    | (states&(~DELTA_STATE_MASK));
        }
        
        public void readDelta(Parcel src) {
            int firstToken = src.readInt();
            int deltaTimeToken = firstToken&DELTA_TIME_MASK;
            cmd = (byte)((firstToken>>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();
                time = src.readLong();
                readFromParcel(src);
                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) {
        public void setTo(HistoryItem o) {
+20 −11
Original line number Original line Diff line number Diff line
@@ -71,7 +71,7 @@ public final class BatteryStatsImpl extends BatteryStats {
    private static final int MAGIC = 0xBA757475; // 'BATSTATS'
    private static final int MAGIC = 0xBA757475; // 'BATSTATS'


    // Current on-disk Parcel version
    // 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.
    // Maximum number of items we will record in the history.
    private static final int MAX_HISTORY_ITEMS = 2000;
    private static final int MAX_HISTORY_ITEMS = 2000;
@@ -156,11 +156,12 @@ public final class BatteryStatsImpl extends BatteryStats {
    boolean mRecordingHistory = true;
    boolean mRecordingHistory = true;
    int mNumHistoryItems;
    int mNumHistoryItems;


    static final int MAX_HISTORY_BUFFER = 64*1024; // 64KB
    static final int MAX_HISTORY_BUFFER = 128*1024; // 128KB
    static final int MAX_MAX_HISTORY_BUFFER = 92*1024; // 92KB
    static final int MAX_MAX_HISTORY_BUFFER = 144*1024; // 144KB
    final Parcel mHistoryBuffer = Parcel.obtain();
    final Parcel mHistoryBuffer = Parcel.obtain();
    final HistoryItem mHistoryLastWritten = new HistoryItem();
    final HistoryItem mHistoryLastWritten = new HistoryItem();
    final HistoryItem mHistoryLastLastWritten = new HistoryItem();
    final HistoryItem mHistoryLastLastWritten = new HistoryItem();
    final HistoryItem mHistoryReadTmp = new HistoryItem();
    int mHistoryBufferLastPos = -1;
    int mHistoryBufferLastPos = -1;
    boolean mHistoryOverflow = false;
    boolean mHistoryOverflow = false;
    long mLastHistoryTime = 0;
    long mLastHistoryTime = 0;
@@ -1212,8 +1213,9 @@ public final class BatteryStatsImpl extends BatteryStats {
            return;
            return;
        }
        }


        final long timeDiff = (mHistoryBaseTime+curTime) - mHistoryLastWritten.time;
        if (mHistoryBufferLastPos >= 0 && mHistoryLastWritten.cmd == HistoryItem.CMD_UPDATE
        if (mHistoryBufferLastPos >= 0 && mHistoryLastWritten.cmd == HistoryItem.CMD_UPDATE
                && (mHistoryBaseTime+curTime) < (mHistoryLastWritten.time+2000)
                && timeDiff < 2000
                && ((mHistoryLastWritten.states^mHistoryCur.states)&mChangedBufferStates) == 0) {
                && ((mHistoryLastWritten.states^mHistoryCur.states)&mChangedBufferStates) == 0) {
            // If the current is the same as the one before, then we no
            // If the current is the same as the one before, then we no
            // longer need the entry.
            // longer need the entry.
@@ -1221,7 +1223,7 @@ public final class BatteryStatsImpl extends BatteryStats {
            mHistoryBuffer.setDataPosition(mHistoryBufferLastPos);
            mHistoryBuffer.setDataPosition(mHistoryBufferLastPos);
            mHistoryBufferLastPos = -1;
            mHistoryBufferLastPos = -1;
            if (mHistoryLastLastWritten.cmd == HistoryItem.CMD_UPDATE
            if (mHistoryLastLastWritten.cmd == HistoryItem.CMD_UPDATE
                    && mHistoryLastLastWritten.same(mHistoryCur)) {
                    && timeDiff < 500 && mHistoryLastLastWritten.same(mHistoryCur)) {
                // If this results in us returning to the state written
                // If this results in us returning to the state written
                // prior to the last one, then we can just delete the last
                // prior to the last one, then we can just delete the last
                // written one and drop the new one.  Nothing more to do.
                // 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;
            mChangedBufferStates |= mHistoryLastWritten.states^mHistoryCur.states;
            curTime = mHistoryLastWritten.time - mHistoryBaseTime;
            curTime = mHistoryLastWritten.time - mHistoryBaseTime;
            mHistoryLastWritten.setTo(mHistoryLastLastWritten);
        } else {
        } else {
            mChangedBufferStates = 0;
            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
            // If the current is the same as the one before, then we no
            // longer need the entry.
            // longer need the entry.
            if (mHistoryLastEnd != null && mHistoryLastEnd.cmd == HistoryItem.CMD_UPDATE
            if (mHistoryLastEnd != null && mHistoryLastEnd.cmd == HistoryItem.CMD_UPDATE
                    && (mHistoryBaseTime+curTime) < (mHistoryEnd.time+500)
                    && mHistoryLastEnd.same(mHistoryCur)) {
                    && mHistoryLastEnd.same(mHistoryCur)) {
                mHistoryLastEnd.next = null;
                mHistoryLastEnd.next = null;
                mHistoryEnd.next = mHistoryCache;
                mHistoryEnd.next = mHistoryCache;
@@ -4038,6 +4042,7 @@ public final class BatteryStatsImpl extends BatteryStats {
        if (DEBUG_HISTORY) Slog.i(TAG, "ITERATING: buff size=" + mHistoryBuffer.dataSize()
        if (DEBUG_HISTORY) Slog.i(TAG, "ITERATING: buff size=" + mHistoryBuffer.dataSize()
                + " pos=" + mHistoryBuffer.dataPosition());
                + " pos=" + mHistoryBuffer.dataPosition());
        mHistoryBuffer.setDataPosition(0);
        mHistoryBuffer.setDataPosition(0);
        mHistoryReadTmp.clear();
        mReadOverflow = false;
        mReadOverflow = false;
        mIteratingHistory = true;
        mIteratingHistory = true;
        return (mHistoryIterator = mHistory) != null;
        return (mHistoryIterator = mHistory) != null;
@@ -4047,8 +4052,8 @@ public final class BatteryStatsImpl extends BatteryStats {
    public boolean getNextOldHistoryLocked(HistoryItem out) {
    public boolean getNextOldHistoryLocked(HistoryItem out) {
        boolean end = mHistoryBuffer.dataPosition() >= mHistoryBuffer.dataSize();
        boolean end = mHistoryBuffer.dataPosition() >= mHistoryBuffer.dataSize();
        if (!end) {
        if (!end) {
            mHistoryLastWritten.readDelta(mHistoryBuffer, null);
            mHistoryReadTmp.readDelta(mHistoryBuffer);
            mReadOverflow |= mHistoryLastWritten.cmd == HistoryItem.CMD_OVERFLOW;
            mReadOverflow |= mHistoryReadTmp.cmd == HistoryItem.CMD_OVERFLOW;
        }
        }
        HistoryItem cur = mHistoryIterator;
        HistoryItem cur = mHistoryIterator;
        if (cur == null) {
        if (cur == null) {
@@ -4062,14 +4067,14 @@ public final class BatteryStatsImpl extends BatteryStats {
        if (!mReadOverflow) {
        if (!mReadOverflow) {
            if (end) {
            if (end) {
                Slog.w(TAG, "New history ends before old history!");
                Slog.w(TAG, "New history ends before old history!");
            } else if (!out.same(mHistoryLastWritten)) {
            } else if (!out.same(mHistoryReadTmp)) {
                long now = getHistoryBaseTime() + SystemClock.elapsedRealtime();
                long now = getHistoryBaseTime() + SystemClock.elapsedRealtime();
                PrintWriter pw = new PrintWriter(new LogWriter(android.util.Log.WARN, TAG));
                PrintWriter pw = new PrintWriter(new LogWriter(android.util.Log.WARN, TAG));
                pw.println("Histories differ!");
                pw.println("Histories differ!");
                pw.println("Old history:");
                pw.println("Old history:");
                (new HistoryPrinter()).printNextItem(pw, out, now);
                (new HistoryPrinter()).printNextItem(pw, out, now);
                pw.println("New history:");
                pw.println("New history:");
                (new HistoryPrinter()).printNextItem(pw, mHistoryLastWritten, now);
                (new HistoryPrinter()).printNextItem(pw, mHistoryReadTmp, now);
            }
            }
        }
        }
        return true;
        return true;
@@ -4093,12 +4098,16 @@ public final class BatteryStatsImpl extends BatteryStats {


    @Override
    @Override
    public boolean getNextHistoryLocked(HistoryItem out) {
    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) {
        if (end) {
            return false;
            return false;
        }
        }


        out.readDelta(mHistoryBuffer, null);
        out.readDelta(mHistoryBuffer);
        return true;
        return true;
    }
    }