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

Commit 0ffc988a authored by Dianne Hackborn's avatar Dianne Hackborn
Browse files

Rewrite battery history storage.

We now write battery history directly into a buffer, instead of
creating objects.  This allows for more efficient storage; later
it can be even better because we can only write deltas.

The old code is still there temporarily for validation.

Change-Id: I9707d4d8ff30855be8ebdc93bc078911040d8e0b
parent 5cde33b7
Loading
Loading
Loading
Loading
+171 −127
Original line number Diff line number Diff line
@@ -412,6 +412,7 @@ public abstract class BatteryStats implements Parcelable {
        
        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;
@@ -466,16 +467,7 @@ public abstract class BatteryStats implements Parcelable {
        
        public HistoryItem(long time, Parcel src) {
            this.time = time;
            int bat = src.readInt();
            cmd = (byte)(bat&0xff);
            batteryLevel = (byte)((bat>>8)&0xff);
            batteryStatus = (byte)((bat>>16)&0xf);
            batteryHealth = (byte)((bat>>20)&0xf);
            batteryPlugType = (byte)((bat>>24)&0xf);
            bat = src.readInt();
            batteryTemperature = (char)(bat&0xffff);
            batteryVoltage = (char)((bat>>16)&0xffff);
            states = src.readInt();
            readFromParcel(src);
        }
        
        public int describeContents() {
@@ -496,6 +488,28 @@ public abstract class BatteryStats implements Parcelable {
            dest.writeInt(states);
        }
        
        public void writeDelta(Parcel dest, HistoryItem last) {
            writeToParcel(dest, 0);
        }

        private void readFromParcel(Parcel src) {
            int bat = src.readInt();
            cmd = (byte)(bat&0xff);
            batteryLevel = (byte)((bat>>8)&0xff);
            batteryStatus = (byte)((bat>>16)&0xf);
            batteryHealth = (byte)((bat>>20)&0xf);
            batteryPlugType = (byte)((bat>>24)&0xf);
            bat = src.readInt();
            batteryTemperature = (char)(bat&0xffff);
            batteryVoltage = (char)((bat>>16)&0xffff);
            states = src.readInt();
        }

        public void readDelta(Parcel src, HistoryItem last) {
            time = src.readLong();
            readFromParcel(src);
        }

        public void setTo(HistoryItem o) {
            time = o.time;
            cmd = o.cmd;
@@ -556,10 +570,13 @@ public abstract class BatteryStats implements Parcelable {

    public abstract boolean getNextHistoryLocked(HistoryItem out);

    /**
     * Return the current history of battery state changes.
     */
    public abstract HistoryItem getHistory();
    public abstract void finishIteratingHistoryLocked();

    public abstract boolean startIteratingOldHistoryLocked();

    public abstract boolean getNextOldHistoryLocked(HistoryItem out);

    public abstract void finishIteratingOldHistoryLocked();

    /**
     * Return the base time offset for the battery history.
@@ -1729,7 +1746,7 @@ public abstract class BatteryStats implements Parcelable {
        }
    }

    void printBitDescriptions(PrintWriter pw, int oldval, int newval, BitDescription[] descriptions) {
    static void printBitDescriptions(PrintWriter pw, int oldval, int newval, BitDescription[] descriptions) {
        int diff = oldval ^ newval;
        if (diff == 0) return;
        for (int i=0; i<descriptions.length; i++) {
@@ -1753,24 +1770,18 @@ public abstract class BatteryStats implements Parcelable {
        }
    }
    
    /**
     * Dumps a human-readable summary of the battery statistics to the given PrintWriter.
     *
     * @param pw a Printer to receive the dump output.
     */
    @SuppressWarnings("unused")
    public void dumpLocked(PrintWriter pw) {
        final HistoryItem rec = new HistoryItem();
        if (startIteratingHistoryLocked()) {
            pw.println("Battery History:");
            long now = getHistoryBaseTime() + SystemClock.elapsedRealtime();
    public void prepareForDumpLocked() {
    }

    public static class HistoryPrinter {
        int oldState = 0;
        int oldStatus = -1;
        int oldHealth = -1;
        int oldPlug = -1;
        int oldTemp = -1;
        int oldVolt = -1;
            while (getNextHistoryLocked(rec)) {

        public void printNextItem(PrintWriter pw, HistoryItem rec, long now) {
            pw.print("  ");
            TimeUtils.formatDuration(rec.time-now, pw, TimeUtils.HUNDRED_DAY_FIELD_LEN);
            pw.print(" ");
@@ -1876,6 +1887,37 @@ public abstract class BatteryStats implements Parcelable {
            }
            oldState = rec.states;
        }
    }

    /**
     * Dumps a human-readable summary of the battery statistics to the given PrintWriter.
     *
     * @param pw a Printer to receive the dump output.
     */
    @SuppressWarnings("unused")
    public void dumpLocked(PrintWriter pw) {
        prepareForDumpLocked();

        long now = getHistoryBaseTime() + SystemClock.elapsedRealtime();

        final HistoryItem rec = new HistoryItem();
        if (startIteratingHistoryLocked()) {
            pw.println("Battery History:");
            HistoryPrinter hprinter = new HistoryPrinter();
            while (getNextHistoryLocked(rec)) {
                hprinter.printNextItem(pw, rec, now);
            }
            finishIteratingHistoryLocked();
            pw.println("");
        }

        if (startIteratingOldHistoryLocked()) {
            pw.println("Old battery History:");
            HistoryPrinter hprinter = new HistoryPrinter();
            while (getNextOldHistoryLocked(rec)) {
                hprinter.printNextItem(pw, rec, now);
            }
            finishIteratingOldHistoryLocked();
            pw.println("");
        }
        
@@ -1918,6 +1960,8 @@ public abstract class BatteryStats implements Parcelable {
    
    @SuppressWarnings("unused")
    public void dumpCheckinLocked(PrintWriter pw, String[] args, List<ApplicationInfo> apps) {
        prepareForDumpLocked();

        boolean isUnpluggedOnly = false;
        
        for (String arg : args) {
+214 −20
Original line number Diff line number Diff line
@@ -36,6 +36,7 @@ import android.telephony.ServiceState;
import android.telephony.SignalStrength;
import android.telephony.TelephonyManager;
import android.util.Log;
import android.util.LogWriter;
import android.util.PrintWriterPrinter;
import android.util.Printer;
import android.util.Slog;
@@ -70,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 = 54;
    private static final int VERSION = 57;

    // Maximum number of items we will record in the history.
    private static final int MAX_HISTORY_ITEMS = 2000;
@@ -154,11 +155,26 @@ public final class BatteryStatsImpl extends BatteryStats {
    boolean mHaveBatteryLevel = false;
    boolean mRecordingHistory = true;
    int mNumHistoryItems;

    static final int MAX_HISTORY_BUFFER = 64*1024; // 64KB
    static final int MAX_MAX_HISTORY_BUFFER = 92*1024; // 92KB
    final Parcel mHistoryBuffer = Parcel.obtain();
    final HistoryItem mHistoryLastWritten = new HistoryItem();
    final HistoryItem mHistoryLastLastWritten = new HistoryItem();
    int mHistoryBufferLastPos = -1;
    boolean mHistoryOverflow = false;
    long mLastHistoryTime = 0;

    final HistoryItem mHistoryCur = new HistoryItem();

    HistoryItem mHistory;
    HistoryItem mHistoryEnd;
    HistoryItem mHistoryLastEnd;
    HistoryItem mHistoryCache;
    final HistoryItem mHistoryCur = new HistoryItem();

    private HistoryItem mHistoryIterator;
    private boolean mReadOverflow;
    private boolean mIteratingHistory;

    int mStartCount;

@@ -1189,9 +1205,82 @@ public final class BatteryStatsImpl extends BatteryStats {
        mBtHeadset = headset;
    }

    int mChangedBufferStates = 0;

    void addHistoryBufferLocked(long curTime) {
        if (!mHaveBatteryLevel || !mRecordingHistory) {
            return;
        }

        if (mHistoryBufferLastPos >= 0 && mHistoryLastWritten.cmd == HistoryItem.CMD_UPDATE
                && (mHistoryBaseTime+curTime) < (mHistoryLastWritten.time+2000)
                && ((mHistoryLastWritten.states^mHistoryCur.states)&mChangedBufferStates) == 0) {
            // If the current is the same as the one before, then we no
            // longer need the entry.
            mHistoryBuffer.setDataSize(mHistoryBufferLastPos);
            mHistoryBuffer.setDataPosition(mHistoryBufferLastPos);
            mHistoryBufferLastPos = -1;
            if (mHistoryLastLastWritten.cmd == HistoryItem.CMD_UPDATE
                    && 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.
                mHistoryLastWritten.setTo(mHistoryLastLastWritten);
                mHistoryLastLastWritten.cmd = HistoryItem.CMD_NULL;
                return;
            }
            mChangedBufferStates |= mHistoryLastWritten.states^mHistoryCur.states;
            curTime = mHistoryLastWritten.time - mHistoryBaseTime;
        } else {
            mChangedBufferStates = 0;
        }

        final int dataSize = mHistoryBuffer.dataSize();
        if (dataSize >= MAX_HISTORY_BUFFER) {
            if (!mHistoryOverflow) {
                mHistoryOverflow = true;
                addHistoryBufferLocked(curTime, HistoryItem.CMD_OVERFLOW);
            }

            // Once we've reached the maximum number of items, we only
            // record changes to the battery level and the most interesting states.
            // Once we've reached the maximum maximum number of items, we only
            // record changes to the battery level.
            if (mHistoryLastWritten.batteryLevel == mHistoryCur.batteryLevel &&
                    (dataSize >= MAX_MAX_HISTORY_BUFFER
                            || ((mHistoryEnd.states^mHistoryCur.states)
                                    & HistoryItem.MOST_INTERESTING_STATES) == 0)) {
                return;
            }
        }

        addHistoryBufferLocked(curTime, HistoryItem.CMD_UPDATE);
    }

    void addHistoryBufferLocked(long curTime, byte cmd) {
        int origPos = 0;
        if (mIteratingHistory) {
            origPos = mHistoryBuffer.dataPosition();
            mHistoryBuffer.setDataPosition(mHistoryBuffer.dataSize());
        }
        mHistoryBufferLastPos = mHistoryBuffer.dataPosition();
        mHistoryLastLastWritten.setTo(mHistoryLastWritten);
        mHistoryLastWritten.setTo(mHistoryBaseTime + curTime, cmd, mHistoryCur);
        mHistoryLastWritten.writeDelta(mHistoryBuffer, mHistoryLastLastWritten);
        mLastHistoryTime = curTime;
        if (DEBUG_HISTORY) Slog.i(TAG, "Writing history buffer: was " + mHistoryBufferLastPos
                + " now " + mHistoryBuffer.dataPosition()
                + " size is now " + mHistoryBuffer.dataSize());
        if (mIteratingHistory) {
            mHistoryBuffer.setDataPosition(origPos);
        }
    }

    int mChangedStates = 0;

    void addHistoryRecordLocked(long curTime) {
        addHistoryBufferLocked(curTime);

        if (!mHaveBatteryLevel || !mRecordingHistory) {
            return;
        }
@@ -1268,6 +1357,7 @@ public final class BatteryStatsImpl extends BatteryStats {
    }

    void clearHistoryLocked() {
        if (DEBUG_HISTORY) Slog.i(TAG, "********** CLEARING HISTORY!");
        if (mHistory != null) {
            mHistoryEnd.next = mHistoryCache;
            mHistoryCache = mHistory;
@@ -1275,6 +1365,15 @@ public final class BatteryStatsImpl extends BatteryStats {
        }
        mNumHistoryItems = 0;
        mHistoryBaseTime = 0;
        mLastHistoryTime = 0;

        mHistoryBuffer.setDataSize(0);
        mHistoryBuffer.setDataPosition(0);
        mHistoryBuffer.setDataCapacity(MAX_HISTORY_BUFFER/2);
        mHistoryLastLastWritten.cmd = HistoryItem.CMD_NULL;
        mHistoryLastWritten.cmd = HistoryItem.CMD_NULL;
        mHistoryBufferLastPos = -1;
        mHistoryOverflow = false;
    }

    public void doUnplugLocked(long batteryUptime, long batteryRealtime) {
@@ -3910,11 +4009,13 @@ public final class BatteryStatsImpl extends BatteryStats {
        mDischargeUnplugLevel = 0;
        mDischargeCurrentLevel = 0;
        initDischarge();
        clearHistoryLocked();
    }

    public BatteryStatsImpl(Parcel p) {
        mFile = null;
        mHandler = null;
        clearHistoryLocked();
        readFromParcel(p);
    }

@@ -3932,25 +4033,79 @@ public final class BatteryStatsImpl extends BatteryStats {
        }
    }

    private HistoryItem mHistoryIterator;

    public boolean startIteratingHistoryLocked() {
    @Override
    public boolean startIteratingOldHistoryLocked() {
        if (DEBUG_HISTORY) Slog.i(TAG, "ITERATING: buff size=" + mHistoryBuffer.dataSize()
                + " pos=" + mHistoryBuffer.dataPosition());
        mHistoryBuffer.setDataPosition(0);
        mReadOverflow = false;
        mIteratingHistory = true;
        return (mHistoryIterator = mHistory) != null;
    }

    public boolean getNextHistoryLocked(HistoryItem out) {
    @Override
    public boolean getNextOldHistoryLocked(HistoryItem out) {
        boolean end = mHistoryBuffer.dataPosition() >= mHistoryBuffer.dataSize();
        if (!end) {
            mHistoryLastWritten.readDelta(mHistoryBuffer, null);
            mReadOverflow |= mHistoryLastWritten.cmd == HistoryItem.CMD_OVERFLOW;
        }
        HistoryItem cur = mHistoryIterator;
        if (cur == null) {
            if (!mReadOverflow && !end) {
                Slog.w(TAG, "Old history ends before new history!");
            }
            return false;
        }
        out.setTo(cur);
        mHistoryIterator = cur.next;
        if (!mReadOverflow) {
            if (end) {
                Slog.w(TAG, "New history ends before old history!");
            } else if (!out.same(mHistoryLastWritten)) {
                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);
            }
        }
        return true;
    }

    @Override
    public HistoryItem getHistory() {
        return mHistory;
    public void finishIteratingOldHistoryLocked() {
        mIteratingHistory = false;
        mHistoryBuffer.setDataPosition(mHistoryBuffer.dataSize());
    }

    @Override
    public boolean startIteratingHistoryLocked() {
        if (DEBUG_HISTORY) Slog.i(TAG, "ITERATING: buff size=" + mHistoryBuffer.dataSize()
                + " pos=" + mHistoryBuffer.dataPosition());
        mHistoryBuffer.setDataPosition(0);
        mReadOverflow = false;
        mIteratingHistory = true;
        return mHistoryBuffer.dataSize() > 0;
    }

    @Override
    public boolean getNextHistoryLocked(HistoryItem out) {
        boolean end = mHistoryBuffer.dataPosition() >= mHistoryBuffer.dataSize();
        if (end) {
            return false;
        }

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

    @Override
    public void finishIteratingHistoryLocked() {
        mIteratingHistory = false;
        mHistoryBuffer.setDataPosition(mHistoryBuffer.dataSize());
    }

    @Override
@@ -4697,7 +4852,9 @@ public final class BatteryStatsImpl extends BatteryStats {
            Slog.e("BatteryStats", "Error reading battery statistics", e);
        }

        addHistoryRecordLocked(SystemClock.elapsedRealtime(), HistoryItem.CMD_START);
        long now = SystemClock.elapsedRealtime();
        addHistoryRecordLocked(now, HistoryItem.CMD_START);
        addHistoryBufferLocked(now, HistoryItem.CMD_START);
    }

    public int describeContents() {
@@ -4705,30 +4862,54 @@ public final class BatteryStatsImpl extends BatteryStats {
    }

    void readHistory(Parcel in) {
        mHistory = mHistoryEnd = mHistoryCache = null;
        mHistoryBaseTime = 0;
        long time;
        while ((time=in.readLong()) >= 0) {
            HistoryItem rec = new HistoryItem(time, in);
            addHistoryRecordLocked(rec);
            if (rec.time > mHistoryBaseTime) {
                mHistoryBaseTime = rec.time;
            }
        mHistoryBaseTime = in.readLong();

        mHistoryBuffer.setDataSize(0);
        mHistoryBuffer.setDataPosition(0);

        int bufSize = in.readInt();
        int curPos = in.dataPosition();
        if (bufSize >= (MAX_MAX_HISTORY_BUFFER*3)) {
            Slog.w(TAG, "File corrupt: history data buffer too large " + bufSize);
        } else if ((bufSize&~3) != bufSize) {
            Slog.w(TAG, "File corrupt: history data buffer not aligned " + bufSize);
        } else {
            if (DEBUG_HISTORY) Slog.i(TAG, "***************** READING NEW HISTORY: " + bufSize
                    + " bytes at " + curPos);
            mHistoryBuffer.appendFrom(in, curPos, bufSize);
            in.setDataPosition(curPos + bufSize);
        }

        long oldnow = SystemClock.elapsedRealtime() - (5*60*100);
        long oldnow = SystemClock.elapsedRealtime() - (5*60*1000);
        if (oldnow > 0) {
            // If the system process has restarted, but not the entire
            // system, then the mHistoryBaseTime already accounts for
            // much of the elapsed time.  We thus want to adjust it back,
            // to avoid large gaps in the data.  We determine we are
            // in this case by arbitrarily saying it is so if at this
            // point in boot the elapsed time is already more than 5 seconds.
            // point in boot the elapsed time is already more than 5 minutes.
            mHistoryBaseTime -= oldnow;
        }
    }

    void readOldHistory(Parcel in) {
        mHistory = mHistoryEnd = mHistoryCache = null;
        long time;
        while ((time=in.readLong()) >= 0) {
            HistoryItem rec = new HistoryItem(time, in);
            addHistoryRecordLocked(rec);
        }
    }

    void writeHistory(Parcel out) {
        out.writeLong(mLastHistoryTime);
        out.writeInt(mHistoryBuffer.dataSize());
        if (DEBUG_HISTORY) Slog.i(TAG, "***************** WRITING HISTORY: "
                + mHistoryBuffer.dataSize() + " bytes at " + out.dataPosition());
        out.appendFrom(mHistoryBuffer, 0, mHistoryBuffer.dataSize());
    }

    void writeOldHistory(Parcel out) {
        HistoryItem rec = mHistory;
        while (rec != null) {
            if (rec.time >= 0) rec.writeToParcel(out, 0);
@@ -4746,6 +4927,7 @@ public final class BatteryStatsImpl extends BatteryStats {
        }

        readHistory(in);
        readOldHistory(in);

        mStartCount = in.readInt();
        mBatteryUptime = in.readLong();
@@ -4935,6 +5117,9 @@ public final class BatteryStatsImpl extends BatteryStats {
     * @param out the Parcel to be written to.
     */
    public void writeSummaryToParcel(Parcel out) {
        // Need to update with current kernel wake lock counts.
        updateKernelWakelocksLocked();

        final long NOW_SYS = SystemClock.uptimeMillis() * 1000;
        final long NOWREAL_SYS = SystemClock.elapsedRealtime() * 1000;
        final long NOW = getBatteryUptimeLocked(NOW_SYS);
@@ -4943,6 +5128,7 @@ public final class BatteryStatsImpl extends BatteryStats {
        out.writeInt(VERSION);

        writeHistory(out);
        writeOldHistory(out);

        out.writeInt(mStartCount);
        out.writeLong(computeBatteryUptime(NOW_SYS, STATS_SINCE_CHARGED));
@@ -5256,6 +5442,9 @@ public final class BatteryStatsImpl extends BatteryStats {

    @SuppressWarnings("unused")
    void writeToParcelLocked(Parcel out, boolean inclUids, int flags) {
        // Need to update with current kernel wake lock counts.
        updateKernelWakelocksLocked();

        final long uSecUptime = SystemClock.uptimeMillis() * 1000;
        final long uSecRealtime = SystemClock.elapsedRealtime() * 1000;
        final long batteryUptime = getBatteryUptimeLocked(uSecUptime);
@@ -5358,6 +5547,11 @@ public final class BatteryStatsImpl extends BatteryStats {
        }
    };

    public void prepareForDumpLocked() {
        // Need to retrieve current kernel wake lock stats before printing.
        updateKernelWakelocksLocked();
    }

    public void dumpLocked(PrintWriter pw) {
        if (DEBUG) {
            Printer pr = new PrintWriterPrinter(pw);
+11 −7
Original line number Diff line number Diff line
@@ -338,7 +338,7 @@ void Parcel::setDataPosition(size_t pos) const

status_t Parcel::setDataCapacity(size_t size)
{
    if (size > mDataSize) return continueWrite(size);
    if (size > mDataCapacity) return continueWrite(size);
    return NO_ERROR;
}

@@ -386,11 +386,13 @@ status_t Parcel::appendFrom(Parcel *parcel, size_t offset, size_t len)
    }
    int numObjects = lastIndex - firstIndex + 1;

    if ((mDataSize+len) > mDataCapacity) {
        // grow data
        err = growData(len);
        if (err != NO_ERROR) {
            return err;
        }
    }

    // append data
    memcpy(mData + mDataPos, data + offset, len);
@@ -1384,8 +1386,10 @@ status_t Parcel::continueWrite(size_t desired)
                return NO_MEMORY;
            }
        } else {
            if (mDataSize > desired) {
                mDataSize = desired;
                LOGV("continueWrite Setting data size of %p to %d\n", this, mDataSize);
            }
            if (mDataPos > desired) {
                mDataPos = desired;
                LOGV("continueWrite Setting data pos of %p to %d\n", this, mDataPos);
+13 −0
Original line number Diff line number Diff line
@@ -449,6 +449,7 @@ public final class BatteryStatsService extends IBatteryStats.Stub {
    @Override
    protected void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
        boolean isCheckin = false;
        boolean noOutput = false;
        if (args != null) {
            for (String arg : args) {
                if ("--checkin".equals(arg)) {
@@ -457,10 +458,22 @@ public final class BatteryStatsService extends IBatteryStats.Stub {
                    synchronized (mStats) {
                        mStats.resetAllStatsLocked();
                        pw.println("Battery stats reset.");
                        noOutput = true;
                    }
                } else if ("--write".equals(arg)) {
                    synchronized (mStats) {
                        mStats.writeSyncLocked();
                        pw.println("Battery stats written.");
                        noOutput = true;
                    }
                } else {
                    pw.println("Unknown option: " + arg);
                }
            }
        }
        if (noOutput) {
            return;
        }
        if (isCheckin) {
            List<ApplicationInfo> apps = mContext.getPackageManager().getInstalledApplications(0);
            synchronized (mStats) {