Merge "Dump battery history without locking"
diff --git a/core/java/android/os/BatteryStats.java b/core/java/android/os/BatteryStats.java
index 083b4f6..a35b088 100644
--- a/core/java/android/os/BatteryStats.java
+++ b/core/java/android/os/BatteryStats.java
@@ -2425,8 +2425,9 @@
     public abstract int getHistoryTagPoolUid(int index);
 
     /**
-     * Returns a BatteryStatsHistoryIterator. Battery history will remain immutable until the
-     * {@link BatteryStatsHistoryIterator#close()} method is invoked.
+     * Returns a BatteryStatsHistoryIterator. Battery history will continue being writable,
+     * but the iterator will continue iterating over the snapshot taken at the time this method
+     * is called.
      */
     public abstract BatteryStatsHistoryIterator iterateBatteryStatsHistory();
 
@@ -5120,14 +5121,6 @@
         sb.append(formatCharge(power));
     }
 
-    /**
-     * Temporary for settings.
-     */
-    public final void dumpLocked(Context context, PrintWriter pw, String prefix, int which,
-            int reqUid) {
-        dumpLocked(context, pw, prefix, which, reqUid, checkWifiOnly(context));
-    }
-
     @SuppressWarnings("unused")
     public final void dumpLocked(Context context, PrintWriter pw, String prefix, final int which,
             int reqUid, boolean wifiOnly) {
@@ -7483,7 +7476,25 @@
     public static final int DUMP_VERBOSE = 1<<5;
     public static final int DUMP_DEVICE_WIFI_ONLY = 1<<6;
 
-    private void dumpHistoryLocked(PrintWriter pw, int flags, long histStart, boolean checkin) {
+    private void dumpHistory(PrintWriter pw, int flags, long histStart, boolean checkin) {
+        if (!checkin) {
+            synchronized (this) {
+                final long historyTotalSize = getHistoryTotalSize();
+                final long historyUsedSize = getHistoryUsedSize();
+                pw.print("Battery History (");
+                pw.print((100 * historyUsedSize) / historyTotalSize);
+                pw.print("% used, ");
+                printSizeValue(pw, historyUsedSize);
+                pw.print(" used of ");
+                printSizeValue(pw, historyTotalSize);
+                pw.print(", ");
+                pw.print(getHistoryStringPoolSize());
+                pw.print(" strings using ");
+                printSizeValue(pw, getHistoryStringPoolBytes());
+                pw.println("):");
+            }
+        }
+
         final HistoryPrinter hprinter = new HistoryPrinter();
         long lastTime = -1;
         long baseTime = -1;
@@ -7628,27 +7639,14 @@
      * @param pw a Printer to receive the dump output.
      */
     @SuppressWarnings("unused")
-    public void dumpLocked(Context context, PrintWriter pw, int flags, int reqUid, long histStart) {
+    public void dump(Context context, PrintWriter pw, int flags, int reqUid, long histStart) {
         prepareForDumpLocked();
 
         final boolean filtering = (flags
                 & (DUMP_HISTORY_ONLY|DUMP_CHARGED_ONLY|DUMP_DAILY_ONLY)) != 0;
 
         if ((flags&DUMP_HISTORY_ONLY) != 0 || !filtering) {
-            final long historyTotalSize = getHistoryTotalSize();
-            final long historyUsedSize = getHistoryUsedSize();
-            pw.print("Battery History (");
-            pw.print((100 * historyUsedSize) / historyTotalSize);
-            pw.print("% used, ");
-            printSizeValue(pw, historyUsedSize);
-            pw.print(" used of ");
-            printSizeValue(pw, historyTotalSize);
-            pw.print(", ");
-            pw.print(getHistoryStringPoolSize());
-            pw.print(" strings using ");
-            printSizeValue(pw, getHistoryStringPoolBytes());
-            pw.println("):");
-            dumpHistoryLocked(pw, flags, histStart, false);
+            dumpHistory(pw, flags, histStart, false);
             pw.println();
         }
 
@@ -7656,6 +7654,13 @@
             return;
         }
 
+        synchronized (this) {
+            dumpLocked(context, pw, flags, reqUid, filtering);
+        }
+    }
+
+    private void dumpLocked(Context context, PrintWriter pw, int flags, int reqUid,
+            boolean filtering) {
         if (!filtering) {
             SparseArray<? extends Uid> uidStats = getUidStats();
             final int NU = uidStats.size();
@@ -7823,7 +7828,7 @@
                 }
                 pw.print("\"");
                 pw.println();
-                dumpHistoryLocked(pw, flags, histStart, true);
+                dumpHistory(pw, flags, histStart, true);
             }
         }
 
diff --git a/core/java/com/android/internal/os/BatteryStatsHistory.java b/core/java/com/android/internal/os/BatteryStatsHistory.java
index ddc0c0c..9e176e3 100644
--- a/core/java/com/android/internal/os/BatteryStatsHistory.java
+++ b/core/java/com/android/internal/os/BatteryStatsHistory.java
@@ -40,12 +40,12 @@
 import android.util.SparseArray;
 import android.util.TimeUtils;
 
+import com.android.internal.annotations.GuardedBy;
 import com.android.internal.annotations.VisibleForTesting;
 import com.android.internal.util.ParseUtils;
 
 import java.io.File;
 import java.io.FileOutputStream;
-import java.io.FilenameFilter;
 import java.io.IOException;
 import java.util.ArrayList;
 import java.util.Collections;
@@ -198,6 +198,8 @@
     private final VarintParceler mVarintParceler = new VarintParceler();
     private byte mLastHistoryStepLevel = 0;
     private boolean mMutable = true;
+    private final BatteryStatsHistory mWritableHistory;
+    private boolean mCleanupEnabled = true;
 
     /**
      * A delegate responsible for computing additional details for a step in battery history.
@@ -272,10 +274,32 @@
         initHistoryBuffer();
     }
 
+    /**
+     * Creates a read-only wrapper for the supplied writable history.
+     */
+    public BatteryStatsHistory(BatteryStatsHistory writableHistory) {
+        this(Parcel.obtain(), writableHistory.mSystemDir, 0, 0, null, null, null, writableHistory);
+        mMutable = false;
+
+        synchronized (mWritableHistory) {
+            // Make a copy of battery history to avoid concurrent modification.
+            mHistoryBuffer.appendFrom(mWritableHistory.mHistoryBuffer, 0,
+                    mWritableHistory.mHistoryBuffer.dataSize());
+        }
+    }
+
     @VisibleForTesting
     public BatteryStatsHistory(Parcel historyBuffer, File systemDir,
             int maxHistoryFiles, int maxHistoryBufferSize,
             HistoryStepDetailsCalculator stepDetailsCalculator, Clock clock, TraceDelegate tracer) {
+        this(historyBuffer, systemDir, maxHistoryFiles, maxHistoryBufferSize, stepDetailsCalculator,
+                clock, tracer, null);
+    }
+
+    private BatteryStatsHistory(Parcel historyBuffer, File systemDir,
+            int maxHistoryFiles, int maxHistoryBufferSize,
+            HistoryStepDetailsCalculator stepDetailsCalculator, Clock clock, TraceDelegate tracer,
+            BatteryStatsHistory writableHistory) {
         mHistoryBuffer = historyBuffer;
         mSystemDir = systemDir;
         mMaxHistoryFiles = maxHistoryFiles;
@@ -283,6 +307,7 @@
         mStepDetailsCalculator = stepDetailsCalculator;
         mTracer = tracer;
         mClock = clock;
+        mWritableHistory = writableHistory;
 
         mHistoryDir = new File(systemDir, HISTORY_DIR);
         mHistoryDir.mkdirs();
@@ -292,21 +317,17 @@
 
         final Set<Integer> dedup = new ArraySet<>();
         // scan directory, fill mFileNumbers and mActiveFile.
-        mHistoryDir.listFiles(new FilenameFilter() {
-            @Override
-            public boolean accept(File dir, String name) {
-                final int b = name.lastIndexOf(FILE_SUFFIX);
-                if (b <= 0) {
-                    return false;
-                }
-                final Integer c =
-                        ParseUtils.parseInt(name.substring(0, b), -1);
-                if (c != -1) {
-                    dedup.add(c);
-                    return true;
-                } else {
-                    return false;
-                }
+        mHistoryDir.listFiles((dir, name) -> {
+            final int b = name.lastIndexOf(FILE_SUFFIX);
+            if (b <= 0) {
+                return false;
+            }
+            final int c = ParseUtils.parseInt(name.substring(0, b), -1);
+            if (c != -1) {
+                dedup.add(c);
+                return true;
+            } else {
+                return false;
             }
         });
         if (!dedup.isEmpty()) {
@@ -328,21 +349,29 @@
         mHistoryBuffer = Parcel.obtain();
         mSystemDir = null;
         mHistoryDir = null;
+        mWritableHistory = null;
         initHistoryBuffer();
     }
 
     /**
-     * Used when BatteryStatsImpl object is created from deserialization of a parcel,
-     * such as a checkin file.
+     * Used when BatteryStatsHistory object is created from deserialization of a BatteryUsageStats
+     * parcel.
      */
-    private BatteryStatsHistory(Parcel historyBuffer,
-            HistoryStepDetailsCalculator stepDetailsCalculator, Clock clock) {
-        mHistoryBuffer = historyBuffer;
-        mTracer = new TraceDelegate();
-        mClock = clock;
+    private BatteryStatsHistory(Parcel parcel) {
+        mClock = Clock.SYSTEM_CLOCK;
+        mTracer = null;
         mSystemDir = null;
         mHistoryDir = null;
-        mStepDetailsCalculator = stepDetailsCalculator;
+        mStepDetailsCalculator = null;
+        mWritableHistory = null;
+        mMutable = false;
+
+        final byte[] historyBlob = parcel.readBlob();
+
+        mHistoryBuffer = Parcel.obtain();
+        mHistoryBuffer.unmarshall(historyBlob, 0, historyBlob.length);
+
+        readFromParcel(parcel, true /* useBlobs */);
     }
 
     private void initHistoryBuffer() {
@@ -386,10 +415,7 @@
      * in the system directory, so it is not safe while actively writing history.
      */
     public BatteryStatsHistory copy() {
-        // Make a copy of battery history to avoid concurrent modification.
-        Parcel historyBuffer = Parcel.obtain();
-        historyBuffer.appendFrom(mHistoryBuffer, 0, mHistoryBuffer.dataSize());
-        return new BatteryStatsHistory(historyBuffer, mSystemDir, 0, 0, null, null, mTracer);
+        return new BatteryStatsHistory(this);
     }
 
     /**
@@ -448,6 +474,25 @@
             Slog.e(TAG, "Could not create history file: " + mActiveFile.getBaseFile());
         }
 
+        synchronized (this) {
+            cleanupLocked();
+        }
+    }
+
+    @GuardedBy("this")
+    private void setCleanupEnabledLocked(boolean enabled) {
+        mCleanupEnabled = enabled;
+        if (mCleanupEnabled) {
+            cleanupLocked();
+        }
+    }
+
+    @GuardedBy("this")
+    private void cleanupLocked() {
+        if (!mCleanupEnabled || mHistoryDir == null) {
+            return;
+        }
+
         // if free disk space is less than 100MB, delete oldest history file.
         if (!hasFreeDiskSpace()) {
             int oldest = mFileNumbers.remove(0);
@@ -465,6 +510,16 @@
     }
 
     /**
+     * Returns true if it is safe to reset history. It will return false if the history is
+     * currently being read.
+     */
+    public boolean isResetEnabled() {
+        synchronized (this) {
+            return mCleanupEnabled;
+        }
+    }
+
+    /**
      * Clear history buffer and delete all existing history files. Active history file start from
      * number 0 again.
      */
@@ -491,6 +546,11 @@
         mCurrentParcelEnd = 0;
         mParcelIndex = 0;
         mMutable = false;
+        if (mWritableHistory != null) {
+            synchronized (mWritableHistory) {
+                mWritableHistory.setCleanupEnabledLocked(false);
+            }
+        }
         return new BatteryStatsHistoryIterator(this);
     }
 
@@ -500,7 +560,13 @@
     void iteratorFinished() {
         // setDataPosition so mHistoryBuffer Parcel can be written.
         mHistoryBuffer.setDataPosition(mHistoryBuffer.dataSize());
-        mMutable = true;
+        if (mWritableHistory != null) {
+            synchronized (mWritableHistory) {
+                mWritableHistory.setCleanupEnabledLocked(true);
+            }
+        } else {
+            mMutable = true;
+        }
     }
 
     /**
@@ -717,15 +783,7 @@
      * the {@link #writeToBatteryUsageStatsParcel} method.
      */
     public static BatteryStatsHistory createFromBatteryUsageStatsParcel(Parcel in) {
-        final byte[] historyBlob = in.readBlob();
-
-        Parcel historyBuffer = Parcel.obtain();
-        historyBuffer.unmarshall(historyBlob, 0, historyBlob.length);
-
-        BatteryStatsHistory history = new BatteryStatsHistory(historyBuffer, null,
-                Clock.SYSTEM_CLOCK);
-        history.readFromParcel(in, true /* useBlobs */);
-        return history;
+        return new BatteryStatsHistory(in);
     }
 
     /**
diff --git a/services/core/java/com/android/server/am/BatteryStatsService.java b/services/core/java/com/android/server/am/BatteryStatsService.java
index 7719c5a..f09622f 100644
--- a/services/core/java/com/android/server/am/BatteryStatsService.java
+++ b/services/core/java/com/android/server/am/BatteryStatsService.java
@@ -2905,11 +2905,9 @@
             if (DBG) Slog.d(TAG, "begin dumpLocked from UID " + Binder.getCallingUid());
             awaitCompletion();
 
-            synchronized (mStats) {
-                mStats.dumpLocked(mContext, pw, flags, reqUid, historyStart);
-                if (writeData) {
-                    mStats.writeAsyncLocked();
-                }
+            mStats.dump(mContext, pw, flags, reqUid, historyStart);
+            if (writeData) {
+                mStats.writeAsyncLocked();
             }
             pw.println();
             mCpuWakeupStats.dump(new IndentingPrintWriter(pw, "  "), SystemClock.elapsedRealtime());
diff --git a/services/core/java/com/android/server/power/stats/BatteryStatsImpl.java b/services/core/java/com/android/server/power/stats/BatteryStatsImpl.java
index c278550..89c5c9e 100644
--- a/services/core/java/com/android/server/power/stats/BatteryStatsImpl.java
+++ b/services/core/java/com/android/server/power/stats/BatteryStatsImpl.java
@@ -11299,7 +11299,7 @@
      */
     @Override
     public BatteryStatsHistoryIterator iterateBatteryStatsHistory() {
-        return mHistory.iterate();
+        return mHistory.copy().iterate();
     }
 
     @Override
@@ -14054,7 +14054,8 @@
                     && (oldStatus == BatteryManager.BATTERY_STATUS_FULL
                     || level >= 90
                     || (mDischargeCurrentLevel < 20 && level >= 80)
-                    || getHighDischargeAmountSinceCharge() >= 200)) {
+                    || getHighDischargeAmountSinceCharge() >= 200)
+                    && mHistory.isResetEnabled()) {
                 Slog.i(TAG, "Resetting battery stats: level=" + level + " status=" + oldStatus
                         + " dischargeLevel=" + mDischargeCurrentLevel
                         + " lowAmount=" + getLowDischargeAmountSinceCharge()
@@ -16604,7 +16605,7 @@
     }
 
     @GuardedBy("this")
-    public void dumpLocked(Context context, PrintWriter pw, int flags, int reqUid, long histStart) {
+    public void dump(Context context, PrintWriter pw, int flags, int reqUid, long histStart) {
         if (DEBUG) {
             pw.println("mOnBatteryTimeBase:");
             mOnBatteryTimeBase.dump(pw, "  ");
@@ -16676,36 +16677,39 @@
             pr.println("*** Camera timer:");
             mCameraOnTimer.logState(pr, "  ");
         }
-        super.dumpLocked(context, pw, flags, reqUid, histStart);
+        super.dump(context, pw, flags, reqUid, histStart);
 
-        pw.print("Per process state tracking available: ");
-        pw.println(trackPerProcStateCpuTimes());
-        pw.print("Total cpu time reads: ");
-        pw.println(mNumSingleUidCpuTimeReads);
-        pw.print("Batching Duration (min): ");
-        pw.println((mClock.uptimeMillis() - mCpuTimeReadsTrackingStartTimeMs) / (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);
+        synchronized (this) {
+            pw.print("Per process state tracking available: ");
+            pw.println(trackPerProcStateCpuTimes());
+            pw.print("Total cpu time reads: ");
+            pw.println(mNumSingleUidCpuTimeReads);
+            pw.print("Batching Duration (min): ");
+            pw.println((mClock.uptimeMillis() - mCpuTimeReadsTrackingStartTimeMs) / (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);
 
-        pw.println("Currently mapped isolated uids:");
-        final int numIsolatedUids = mIsolatedUids.size();
-        for (int i = 0; i < numIsolatedUids; i++) {
-            final int isolatedUid = mIsolatedUids.keyAt(i);
-            final int ownerUid = mIsolatedUids.valueAt(i);
-            final int refCount = mIsolatedUidRefCounts.get(isolatedUid);
-            pw.println("  " + isolatedUid + "->" + ownerUid + " (ref count = " + refCount + ")");
+            pw.println("Currently mapped isolated uids:");
+            final int numIsolatedUids = mIsolatedUids.size();
+            for (int i = 0; i < numIsolatedUids; i++) {
+                final int isolatedUid = mIsolatedUids.keyAt(i);
+                final int ownerUid = mIsolatedUids.valueAt(i);
+                final int refCount = mIsolatedUidRefCounts.get(isolatedUid);
+                pw.println(
+                        "  " + isolatedUid + "->" + ownerUid + " (ref count = " + refCount + ")");
+            }
+
+            pw.println();
+            dumpConstantsLocked(pw);
+
+            pw.println();
+            dumpCpuPowerBracketsLocked(pw);
+
+            pw.println();
+            dumpEnergyConsumerStatsLocked(pw);
         }
-
-        pw.println();
-        dumpConstantsLocked(pw);
-
-        pw.println();
-        dumpCpuPowerBracketsLocked(pw);
-
-        pw.println();
-        dumpEnergyConsumerStatsLocked(pw);
     }
 
     @Override