Merge "Change fingerprint behavior in keyguard to dismiss on notification"
diff --git a/api/current.txt b/api/current.txt
index 0046938..707999b 100644
--- a/api/current.txt
+++ b/api/current.txt
@@ -15404,6 +15404,7 @@
     method public void setOnSeekCompleteListener(android.media.MediaPlayer.OnSeekCompleteListener);
     method public void setOnTimedTextListener(android.media.MediaPlayer.OnTimedTextListener);
     method public void setOnVideoSizeChangedListener(android.media.MediaPlayer.OnVideoSizeChangedListener);
+    method public void setPlaybackRate(float, int);
     method public void setScreenOnWhilePlaying(boolean);
     method public void setSurface(android.view.Surface);
     method public void setVideoScalingMode(int);
@@ -15429,6 +15430,7 @@
     field public static final int MEDIA_INFO_VIDEO_RENDERING_START = 3; // 0x3
     field public static final int MEDIA_INFO_VIDEO_TRACK_LAGGING = 700; // 0x2bc
     field public static final java.lang.String MEDIA_MIMETYPE_TEXT_SUBRIP = "application/x-subrip";
+    field public static final int PLAYBACK_RATE_AUDIO_MODE_RESAMPLE = 0; // 0x0
     field public static final int VIDEO_SCALING_MODE_SCALE_TO_FIT = 1; // 0x1
     field public static final int VIDEO_SCALING_MODE_SCALE_TO_FIT_WITH_CROPPING = 2; // 0x2
   }
diff --git a/api/system-current.txt b/api/system-current.txt
index d48fd0d..2ffc4e18 100644
--- a/api/system-current.txt
+++ b/api/system-current.txt
@@ -16400,6 +16400,7 @@
     method public void setOnSeekCompleteListener(android.media.MediaPlayer.OnSeekCompleteListener);
     method public void setOnTimedTextListener(android.media.MediaPlayer.OnTimedTextListener);
     method public void setOnVideoSizeChangedListener(android.media.MediaPlayer.OnVideoSizeChangedListener);
+    method public void setPlaybackRate(float, int);
     method public void setScreenOnWhilePlaying(boolean);
     method public void setSurface(android.view.Surface);
     method public void setVideoScalingMode(int);
@@ -16425,6 +16426,7 @@
     field public static final int MEDIA_INFO_VIDEO_RENDERING_START = 3; // 0x3
     field public static final int MEDIA_INFO_VIDEO_TRACK_LAGGING = 700; // 0x2bc
     field public static final java.lang.String MEDIA_MIMETYPE_TEXT_SUBRIP = "application/x-subrip";
+    field public static final int PLAYBACK_RATE_AUDIO_MODE_RESAMPLE = 0; // 0x0
     field public static final int VIDEO_SCALING_MODE_SCALE_TO_FIT = 1; // 0x1
     field public static final int VIDEO_SCALING_MODE_SCALE_TO_FIT_WITH_CROPPING = 2; // 0x2
   }
diff --git a/core/java/android/os/BatteryStats.java b/core/java/android/os/BatteryStats.java
index 4dadda2..43d3a71 100644
--- a/core/java/android/os/BatteryStats.java
+++ b/core/java/android/os/BatteryStats.java
@@ -30,6 +30,7 @@
 import android.telephony.SignalStrength;
 import android.text.format.DateFormat;
 import android.util.Printer;
+import android.util.Slog;
 import android.util.SparseArray;
 import android.util.SparseIntArray;
 import android.util.TimeUtils;
@@ -542,6 +543,286 @@
         }
     }
 
+    public static final class LevelStepTracker {
+        public long mLastStepTime = -1;
+        public int mNumStepDurations;
+        public final long[] mStepDurations;
+
+        public LevelStepTracker(int maxLevelSteps) {
+            mStepDurations = new long[maxLevelSteps];
+        }
+
+        public LevelStepTracker(int numSteps, long[] steps) {
+            mNumStepDurations = numSteps;
+            mStepDurations = new long[numSteps];
+            System.arraycopy(steps, 0, mStepDurations, 0, numSteps);
+        }
+
+        public long getDurationAt(int index) {
+            return mStepDurations[index] & STEP_LEVEL_TIME_MASK;
+        }
+
+        public int getLevelAt(int index) {
+            return (int)((mStepDurations[index] & STEP_LEVEL_LEVEL_MASK)
+                    >> STEP_LEVEL_LEVEL_SHIFT);
+        }
+
+        public int getInitModeAt(int index) {
+            return (int)((mStepDurations[index] & STEP_LEVEL_INITIAL_MODE_MASK)
+                    >> STEP_LEVEL_INITIAL_MODE_SHIFT);
+        }
+
+        public int getModModeAt(int index) {
+            return (int)((mStepDurations[index] & STEP_LEVEL_MODIFIED_MODE_MASK)
+                    >> STEP_LEVEL_MODIFIED_MODE_SHIFT);
+        }
+
+        private void appendHex(long val, int topOffset, StringBuilder out) {
+            boolean hasData = false;
+            while (topOffset >= 0) {
+                int digit = (int)( (val>>topOffset) & 0xf );
+                topOffset -= 4;
+                if (!hasData && digit == 0) {
+                    continue;
+                }
+                hasData = true;
+                if (digit >= 0 && digit <= 9) {
+                    out.append((char)('0' + digit));
+                } else {
+                    out.append((char)('a' + digit - 10));
+                }
+            }
+        }
+
+        public void encodeEntryAt(int index, StringBuilder out) {
+            long item = mStepDurations[index];
+            long duration = item & STEP_LEVEL_TIME_MASK;
+            int level = (int)((item & STEP_LEVEL_LEVEL_MASK)
+                    >> STEP_LEVEL_LEVEL_SHIFT);
+            int initMode = (int)((item & STEP_LEVEL_INITIAL_MODE_MASK)
+                    >> STEP_LEVEL_INITIAL_MODE_SHIFT);
+            int modMode = (int)((item & STEP_LEVEL_MODIFIED_MODE_MASK)
+                    >> STEP_LEVEL_MODIFIED_MODE_SHIFT);
+            switch ((initMode&STEP_LEVEL_MODE_SCREEN_STATE) + 1) {
+                case Display.STATE_OFF: out.append('f'); break;
+                case Display.STATE_ON: out.append('o'); break;
+                case Display.STATE_DOZE: out.append('d'); break;
+                case Display.STATE_DOZE_SUSPEND: out.append('z'); break;
+            }
+            if ((initMode&STEP_LEVEL_MODE_POWER_SAVE) != 0) {
+                out.append('p');
+            }
+            switch ((modMode&STEP_LEVEL_MODE_SCREEN_STATE) + 1) {
+                case Display.STATE_OFF: out.append('F'); break;
+                case Display.STATE_ON: out.append('O'); break;
+                case Display.STATE_DOZE: out.append('D'); break;
+                case Display.STATE_DOZE_SUSPEND: out.append('Z'); break;
+            }
+            if ((modMode&STEP_LEVEL_MODE_POWER_SAVE) != 0) {
+                out.append('P');
+            }
+            out.append('-');
+            appendHex(level, 4, out);
+            out.append('-');
+            appendHex(duration, STEP_LEVEL_LEVEL_SHIFT-4, out);
+        }
+
+        public void decodeEntryAt(int index, String value) {
+            final int N = value.length();
+            int i = 0;
+            char c;
+            long out = 0;
+            while (i < N && (c=value.charAt(i)) != '-') {
+                i++;
+                switch (c) {
+                    case 'f': out |= ((Display.STATE_OFF-1)<<STEP_LEVEL_INITIAL_MODE_SHIFT); break;
+                    case 'o': out |= ((Display.STATE_ON-1)<<STEP_LEVEL_INITIAL_MODE_SHIFT); break;
+                    case 'd': out |= ((Display.STATE_DOZE-1)<<STEP_LEVEL_INITIAL_MODE_SHIFT); break;
+                    case 'z': out |= ((Display.STATE_DOZE_SUSPEND-1)<<STEP_LEVEL_INITIAL_MODE_SHIFT);
+                        break;
+                    case 'p': out |= (STEP_LEVEL_MODE_POWER_SAVE<<STEP_LEVEL_INITIAL_MODE_SHIFT);
+                        break;
+                    case 'F': out |= ((Display.STATE_OFF-1)<<STEP_LEVEL_MODIFIED_MODE_SHIFT); break;
+                    case 'O': out |= ((Display.STATE_ON-1)<<STEP_LEVEL_MODIFIED_MODE_SHIFT); break;
+                    case 'D': out |= ((Display.STATE_DOZE-1)<<STEP_LEVEL_MODIFIED_MODE_SHIFT); break;
+                    case 'Z': out |= ((Display.STATE_DOZE_SUSPEND-1)<<STEP_LEVEL_MODIFIED_MODE_SHIFT);
+                        break;
+                    case 'P': out |= (STEP_LEVEL_MODE_POWER_SAVE<<STEP_LEVEL_MODIFIED_MODE_SHIFT);
+                        break;
+                }
+            }
+            i++;
+            int level = 0;
+            while (i < N && (c=value.charAt(i)) != '-') {
+                i++;
+                level <<= 4;
+                if (c >= '0' && c <= '9') {
+                    level += c - '0';
+                } else if (c >= 'a' && c <= 'f') {
+                    level += c - 'a' + 10;
+                } else if (c >= 'A' && c <= 'F') {
+                    level += c - 'A' + 10;
+                }
+            }
+            out |= (level << STEP_LEVEL_LEVEL_SHIFT) & STEP_LEVEL_LEVEL_MASK;
+            long duration = 0;
+            while (i < N && (c=value.charAt(i)) != '-') {
+                i++;
+                duration <<= 4;
+                if (c >= '0' && c <= '9') {
+                    duration += c - '0';
+                } else if (c >= 'a' && c <= 'f') {
+                    duration += c - 'a' + 10;
+                } else if (c >= 'A' && c <= 'F') {
+                    duration += c - 'A' + 10;
+                }
+            }
+            mStepDurations[index] = out | (duration & STEP_LEVEL_TIME_MASK);
+        }
+
+        public void init() {
+            mLastStepTime = -1;
+            mNumStepDurations = 0;
+        }
+
+        public void clearTime() {
+            mLastStepTime = -1;
+        }
+
+        public long computeTimePerLevel() {
+            final long[] steps = mStepDurations;
+            final int numSteps = mNumStepDurations;
+
+            // For now we'll do a simple average across all steps.
+            if (numSteps <= 0) {
+                return -1;
+            }
+            long total = 0;
+            for (int i=0; i<numSteps; i++) {
+                total += steps[i] & STEP_LEVEL_TIME_MASK;
+            }
+            return total / numSteps;
+            /*
+            long[] buckets = new long[numSteps];
+            int numBuckets = 0;
+            int numToAverage = 4;
+            int i = 0;
+            while (i < numSteps) {
+                long totalTime = 0;
+                int num = 0;
+                for (int j=0; j<numToAverage && (i+j)<numSteps; j++) {
+                    totalTime += steps[i+j] & STEP_LEVEL_TIME_MASK;
+                    num++;
+                }
+                buckets[numBuckets] = totalTime / num;
+                numBuckets++;
+                numToAverage *= 2;
+                i += num;
+            }
+            if (numBuckets < 1) {
+                return -1;
+            }
+            long averageTime = buckets[numBuckets-1];
+            for (i=numBuckets-2; i>=0; i--) {
+                averageTime = (averageTime + buckets[i]) / 2;
+            }
+            return averageTime;
+            */
+        }
+
+        public long computeTimeEstimate(long modesOfInterest, long modeValues,
+                int[] outNumOfInterest) {
+            final long[] steps = mStepDurations;
+            final int count = mNumStepDurations;
+            if (count <= 0) {
+                return -1;
+            }
+            long total = 0;
+            int numOfInterest = 0;
+            for (int i=0; i<count; i++) {
+                long initMode = (steps[i] & STEP_LEVEL_INITIAL_MODE_MASK)
+                        >> STEP_LEVEL_INITIAL_MODE_SHIFT;
+                long modMode = (steps[i] & STEP_LEVEL_MODIFIED_MODE_MASK)
+                        >> STEP_LEVEL_MODIFIED_MODE_SHIFT;
+                // If the modes of interest didn't change during this step period...
+                if ((modMode&modesOfInterest) == 0) {
+                    // And the mode values during this period match those we are measuring...
+                    if ((initMode&modesOfInterest) == modeValues) {
+                        // Then this can be used to estimate the total time!
+                        numOfInterest++;
+                        total += steps[i] & STEP_LEVEL_TIME_MASK;
+                    }
+                }
+            }
+            if (numOfInterest <= 0) {
+                return -1;
+            }
+
+            if (outNumOfInterest != null) {
+                outNumOfInterest[0] = numOfInterest;
+            }
+
+            // The estimated time is the average time we spend in each level, multipled
+            // by 100 -- the total number of battery levels
+            return (total / numOfInterest) * 100;
+        }
+
+        public void addLevelSteps(int numStepLevels, long modeBits, long elapsedRealtime) {
+            int stepCount = mNumStepDurations;
+            final long lastStepTime = mLastStepTime;
+            if (lastStepTime >= 0 && numStepLevels > 0) {
+                final long[] steps = mStepDurations;
+                long duration = elapsedRealtime - lastStepTime;
+                for (int i=0; i<numStepLevels; i++) {
+                    System.arraycopy(steps, 0, steps, 1, steps.length-1);
+                    long thisDuration = duration / (numStepLevels-i);
+                    duration -= thisDuration;
+                    if (thisDuration > STEP_LEVEL_TIME_MASK) {
+                        thisDuration = STEP_LEVEL_TIME_MASK;
+                    }
+                    steps[0] = thisDuration | modeBits;
+                }
+                stepCount += numStepLevels;
+                if (stepCount > steps.length) {
+                    stepCount = steps.length;
+                }
+            }
+            mNumStepDurations = stepCount;
+            mLastStepTime = elapsedRealtime;
+        }
+
+        public void readFromParcel(Parcel in) {
+            final int N = in.readInt();
+            mNumStepDurations = N;
+            for (int i=0; i<N; i++) {
+                mStepDurations[i] = in.readLong();
+            }
+        }
+
+        public void writeToParcel(Parcel out) {
+            final int N = mNumStepDurations;
+            out.writeInt(N);
+            for (int i=0; i<N; i++) {
+                out.writeLong(mStepDurations[i]);
+            }
+        }
+    }
+
+    public static final class DailyItem {
+        public long mStartTime;
+        public long mEndTime;
+        public LevelStepTracker mDischargeSteps;
+        public LevelStepTracker mChargeSteps;
+    }
+
+    public abstract DailyItem getDailyItemLocked(int daysAgo);
+
+    public abstract long getCurrentDailyStartTime();
+
+    public abstract long getNextMinDailyDeadline();
+
+    public abstract long getNextMaxDailyDeadline();
+
     public final static class HistoryTag {
         public String string;
         public int uid;
@@ -1724,15 +2005,15 @@
 
     // Bits in a step duration that are the new battery level we are at.
     public static final long STEP_LEVEL_LEVEL_MASK = 0x0000ff0000000000L;
-    public static final long STEP_LEVEL_LEVEL_SHIFT = 40;
+    public static final int STEP_LEVEL_LEVEL_SHIFT = 40;
 
     // Bits in a step duration that are the initial mode we were in at that step.
     public static final long STEP_LEVEL_INITIAL_MODE_MASK = 0x00ff000000000000L;
-    public static final long STEP_LEVEL_INITIAL_MODE_SHIFT = 48;
+    public static final int STEP_LEVEL_INITIAL_MODE_SHIFT = 48;
 
     // Bits in a step duration that indicate which modes changed during that step.
     public static final long STEP_LEVEL_MODIFIED_MODE_MASK = 0xff00000000000000L;
-    public static final long STEP_LEVEL_MODIFIED_MODE_SHIFT = 56;
+    public static final int STEP_LEVEL_MODIFIED_MODE_SHIFT = 56;
 
     // Step duration mode: the screen is on, off, dozed, etc; value is Display.STATE_* - 1.
     public static final int STEP_LEVEL_MODE_SCREEN_STATE = 0x03;
@@ -1740,17 +2021,56 @@
     // Step duration mode: power save is on.
     public static final int STEP_LEVEL_MODE_POWER_SAVE = 0x04;
 
-    /**
-     * Return the historical number of discharge steps we currently have.
-     */
-    public abstract int getNumDischargeStepDurations();
+    public static final int[] STEP_LEVEL_MODES_OF_INTEREST = new int[] {
+            STEP_LEVEL_MODE_SCREEN_STATE|STEP_LEVEL_MODE_POWER_SAVE,
+            STEP_LEVEL_MODE_SCREEN_STATE|STEP_LEVEL_MODE_POWER_SAVE,
+            STEP_LEVEL_MODE_SCREEN_STATE|STEP_LEVEL_MODE_POWER_SAVE,
+            STEP_LEVEL_MODE_SCREEN_STATE|STEP_LEVEL_MODE_POWER_SAVE,
+            STEP_LEVEL_MODE_SCREEN_STATE|STEP_LEVEL_MODE_POWER_SAVE,
+            STEP_LEVEL_MODE_SCREEN_STATE|STEP_LEVEL_MODE_POWER_SAVE,
+            STEP_LEVEL_MODE_SCREEN_STATE|STEP_LEVEL_MODE_POWER_SAVE,
+            STEP_LEVEL_MODE_SCREEN_STATE|STEP_LEVEL_MODE_POWER_SAVE,
+    };
+    public static final int[] STEP_LEVEL_MODE_VALUES = new int[] {
+            (Display.STATE_OFF-1),
+            (Display.STATE_OFF-1)|STEP_LEVEL_MODE_POWER_SAVE,
+            (Display.STATE_ON-1),
+            (Display.STATE_ON-1)|STEP_LEVEL_MODE_POWER_SAVE,
+            (Display.STATE_DOZE-1),
+            (Display.STATE_DOZE-1)|STEP_LEVEL_MODE_POWER_SAVE,
+            (Display.STATE_DOZE_SUSPEND-1),
+            (Display.STATE_DOZE_SUSPEND-1)|STEP_LEVEL_MODE_POWER_SAVE,
+    };
+    public static final String[] STEP_LEVEL_MODE_LABELS = new String[] {
+            "screen off",
+            "screen off power save",
+            "screen on",
+            "screen on power save",
+            "screen doze",
+            "screen doze power save",
+            "screen doze-suspend",
+            "screen doze-suspend power save",
+    };
+    public static final String[] STEP_LEVEL_MODE_TAGS = new String[] {
+            "off",
+            "off-save",
+            "on",
+            "on-save",
+            "doze",
+            "doze-save",
+            "susp",
+            "susp-save",
+    };
 
     /**
-     * Return the array of discharge step durations; the number of valid
-     * items in it is returned by {@link #getNumDischargeStepDurations()}.
-     * These values are in milliseconds.
+     * Return the array of discharge step durations.
      */
-    public abstract long[] getDischargeStepDurationsArray();
+    public abstract LevelStepTracker getDischargeLevelStepTracker();
+
+    /**
+     * Return the array of daily discharge step durations.
+     */
+    public abstract LevelStepTracker getDailyDischargeLevelStepTracker();
 
     /**
      * Compute an approximation for how much time (in microseconds) remains until the battery
@@ -1763,16 +2083,14 @@
     public abstract long computeChargeTimeRemaining(long curTime);
 
     /**
-     * Return the historical number of charge steps we currently have.
+     * Return the array of charge step durations.
      */
-    public abstract int getNumChargeStepDurations();
+    public abstract LevelStepTracker getChargeLevelStepTracker();
 
     /**
-     * Return the array of charge step durations; the number of valid
-     * items in it is returned by {@link #getNumChargeStepDurations()}.
-     * These values are in milliseconds.
+     * Return the array of daily charge step durations.
      */
-    public abstract long[] getChargeStepDurationsArray();
+    public abstract LevelStepTracker getDailyChargeLevelStepTracker();
 
     public abstract Map<String, ? extends Timer> getWakeupReasonStats();
 
@@ -3913,47 +4231,27 @@
         pw.print(suffix);
     }
 
-    private static boolean dumpTimeEstimate(PrintWriter pw, String label, long[] steps,
-            int count, long modesOfInterest, long modeValues) {
-        if (count <= 0) {
+    private static boolean dumpTimeEstimate(PrintWriter pw, String label1, String label2,
+            String label3, long estimatedTime) {
+        if (estimatedTime < 0) {
             return false;
         }
-        long total = 0;
-        int numOfInterest = 0;
-        for (int i=0; i<count; i++) {
-            long initMode = (steps[i] & STEP_LEVEL_INITIAL_MODE_MASK)
-                    >> STEP_LEVEL_INITIAL_MODE_SHIFT;
-            long modMode = (steps[i] & STEP_LEVEL_MODIFIED_MODE_MASK)
-                    >> STEP_LEVEL_MODIFIED_MODE_SHIFT;
-            // If the modes of interest didn't change during this step period...
-            if ((modMode&modesOfInterest) == 0) {
-                // And the mode values during this period match those we are measuring...
-                if ((initMode&modesOfInterest) == modeValues) {
-                    // Then this can be used to estimate the total time!
-                    numOfInterest++;
-                    total += steps[i] & STEP_LEVEL_TIME_MASK;
-                }
-            }
-        }
-        if (numOfInterest <= 0) {
-            return false;
-        }
-
-        // The estimated time is the average time we spend in each level, multipled
-        // by 100 -- the total number of battery levels
-        long estimatedTime = (total / numOfInterest) * 100;
-
-        pw.print(label);
+        pw.print(label1);
+        pw.print(label2);
+        pw.print(label3);
         StringBuilder sb = new StringBuilder(64);
         formatTimeMs(sb, estimatedTime);
         pw.print(sb);
         pw.println();
-
         return true;
     }
 
-    private static boolean dumpDurationSteps(PrintWriter pw, String header, long[] steps,
-            int count, boolean checkin) {
+    private static boolean dumpDurationSteps(PrintWriter pw, String prefix, String header,
+            LevelStepTracker steps, boolean checkin) {
+        if (steps == null) {
+            return false;
+        }
+        int count = steps.mNumStepDurations;
         if (count <= 0) {
             return false;
         }
@@ -3962,13 +4260,10 @@
         }
         String[] lineArgs = new String[4];
         for (int i=0; i<count; i++) {
-            long duration = steps[i] & STEP_LEVEL_TIME_MASK;
-            int level = (int)((steps[i] & STEP_LEVEL_LEVEL_MASK)
-                    >> STEP_LEVEL_LEVEL_SHIFT);
-            long initMode = (steps[i] & STEP_LEVEL_INITIAL_MODE_MASK)
-                    >> STEP_LEVEL_INITIAL_MODE_SHIFT;
-            long modMode = (steps[i] & STEP_LEVEL_MODIFIED_MODE_MASK)
-                    >> STEP_LEVEL_MODIFIED_MODE_SHIFT;
+            long duration = steps.getDurationAt(i);
+            int level = steps.getLevelAt(i);
+            long initMode = steps.getInitModeAt(i);
+            long modMode = steps.getModModeAt(i);
             if (checkin) {
                 lineArgs[0] = Long.toString(duration);
                 lineArgs[1] = Integer.toString(level);
@@ -3990,7 +4285,8 @@
                 }
                 dumpLine(pw, 0 /* uid */, "i" /* category */, header, (Object[])lineArgs);
             } else {
-                pw.print("  #"); pw.print(i); pw.print(": ");
+                pw.print(prefix);
+                pw.print("#"); pw.print(i); pw.print(": ");
                 TimeUtils.formatDuration(duration, pw);
                 pw.print(" to "); pw.print(level);
                 boolean haveModes = false;
@@ -4022,10 +4318,11 @@
 
     public static final int DUMP_UNPLUGGED_ONLY = 1<<0;
     public static final int DUMP_CHARGED_ONLY = 1<<1;
-    public static final int DUMP_HISTORY_ONLY = 1<<2;
-    public static final int DUMP_INCLUDE_HISTORY = 1<<3;
-    public static final int DUMP_VERBOSE = 1<<4;
-    public static final int DUMP_DEVICE_WIFI_ONLY = 1<<5;
+    public static final int DUMP_DAILY_ONLY = 1<<2;
+    public static final int DUMP_HISTORY_ONLY = 1<<3;
+    public static final int DUMP_INCLUDE_HISTORY = 1<<4;
+    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) {
         final HistoryPrinter hprinter = new HistoryPrinter();
@@ -4111,6 +4408,36 @@
         }
     }
 
+    private void dumpDailyLevelStepSummary(PrintWriter pw, String prefix, String label,
+            LevelStepTracker steps, StringBuilder tmpSb, int[] tmpOutInt) {
+        if (steps == null) {
+            return;
+        }
+        long timeRemaining = steps.computeTimeEstimate(0, 0, tmpOutInt);
+        if (timeRemaining >= 0) {
+            pw.print(prefix); pw.print(label); pw.print(" total time: ");
+            tmpSb.setLength(0);
+            formatTimeMs(tmpSb, timeRemaining);
+            pw.print(tmpSb);
+            pw.print(" (from "); pw.print(tmpOutInt[0]);
+            pw.println(" steps)");
+        }
+        for (int i=0; i< STEP_LEVEL_MODES_OF_INTEREST.length; i++) {
+            long estimatedTime = steps.computeTimeEstimate(STEP_LEVEL_MODES_OF_INTEREST[i],
+                    STEP_LEVEL_MODE_VALUES[i], tmpOutInt);
+            if (estimatedTime > 0) {
+                pw.print(prefix); pw.print(label); pw.print(" ");
+                pw.print(STEP_LEVEL_MODE_LABELS[i]);
+                pw.print(" time: ");
+                tmpSb.setLength(0);
+                formatTimeMs(tmpSb, estimatedTime);
+                pw.print(tmpSb);
+                pw.print(" (from "); pw.print(tmpOutInt[0]);
+                pw.println(" steps)");
+            }
+        }
+    }
+
     /**
      * Dumps a human-readable summary of the battery statistics to the given PrintWriter.
      *
@@ -4120,8 +4447,8 @@
     public void dumpLocked(Context context, PrintWriter pw, int flags, int reqUid, long histStart) {
         prepareForDumpLocked();
 
-        final boolean filtering =
-                (flags&(DUMP_HISTORY_ONLY|DUMP_UNPLUGGED_ONLY|DUMP_CHARGED_ONLY)) != 0;
+        final boolean filtering = (flags
+                & (DUMP_HISTORY_ONLY|DUMP_UNPLUGGED_ONLY|DUMP_CHARGED_ONLY|DUMP_DAILY_ONLY)) != 0;
 
         if ((flags&DUMP_HISTORY_ONLY) != 0 || !filtering) {
             final long historyTotalSize = getHistoryTotalSize();
@@ -4165,7 +4492,7 @@
             }
         }
 
-        if (filtering && (flags&(DUMP_UNPLUGGED_ONLY|DUMP_CHARGED_ONLY)) == 0) {
+        if (filtering && (flags&(DUMP_UNPLUGGED_ONLY|DUMP_CHARGED_ONLY|DUMP_DAILY_ONLY)) == 0) {
             return;
         }
 
@@ -4199,50 +4526,24 @@
         }
 
         if (!filtering || (flags&DUMP_CHARGED_ONLY) != 0) {
-            if (dumpDurationSteps(pw, "Discharge step durations:", getDischargeStepDurationsArray(),
-                    getNumDischargeStepDurations(), false)) {
+            if (dumpDurationSteps(pw, "  ", "Discharge step durations:",
+                    getDischargeLevelStepTracker(), false)) {
                 long timeRemaining = computeBatteryTimeRemaining(SystemClock.elapsedRealtime());
                 if (timeRemaining >= 0) {
                     pw.print("  Estimated discharge time remaining: ");
                     TimeUtils.formatDuration(timeRemaining / 1000, pw);
                     pw.println();
                 }
-                dumpTimeEstimate(pw, "  Estimated screen off time: ",
-                        getDischargeStepDurationsArray(), getNumDischargeStepDurations(),
-                        STEP_LEVEL_MODE_SCREEN_STATE|STEP_LEVEL_MODE_POWER_SAVE,
-                        (Display.STATE_OFF-1));
-                dumpTimeEstimate(pw, "  Estimated screen off power save time: ",
-                        getDischargeStepDurationsArray(), getNumDischargeStepDurations(),
-                        STEP_LEVEL_MODE_SCREEN_STATE|STEP_LEVEL_MODE_POWER_SAVE,
-                        (Display.STATE_OFF-1)|STEP_LEVEL_MODE_POWER_SAVE);
-                dumpTimeEstimate(pw, "  Estimated screen on time: ",
-                        getDischargeStepDurationsArray(), getNumDischargeStepDurations(),
-                        STEP_LEVEL_MODE_SCREEN_STATE|STEP_LEVEL_MODE_POWER_SAVE,
-                        (Display.STATE_ON-1));
-                dumpTimeEstimate(pw, "  Estimated screen on power save time: ",
-                        getDischargeStepDurationsArray(), getNumDischargeStepDurations(),
-                        STEP_LEVEL_MODE_SCREEN_STATE|STEP_LEVEL_MODE_POWER_SAVE,
-                        (Display.STATE_ON-1)|STEP_LEVEL_MODE_POWER_SAVE);
-                dumpTimeEstimate(pw, "  Estimated screen doze time: ",
-                        getDischargeStepDurationsArray(), getNumDischargeStepDurations(),
-                        STEP_LEVEL_MODE_SCREEN_STATE|STEP_LEVEL_MODE_POWER_SAVE,
-                        (Display.STATE_DOZE-1));
-                dumpTimeEstimate(pw, "  Estimated screen doze power save time: ",
-                        getDischargeStepDurationsArray(), getNumDischargeStepDurations(),
-                        STEP_LEVEL_MODE_SCREEN_STATE|STEP_LEVEL_MODE_POWER_SAVE,
-                        (Display.STATE_DOZE-1)|STEP_LEVEL_MODE_POWER_SAVE);
-                dumpTimeEstimate(pw, "  Estimated screen doze suspend time: ",
-                        getDischargeStepDurationsArray(), getNumDischargeStepDurations(),
-                        STEP_LEVEL_MODE_SCREEN_STATE|STEP_LEVEL_MODE_POWER_SAVE,
-                        (Display.STATE_DOZE_SUSPEND-1));
-                dumpTimeEstimate(pw, "  Estimated screen doze suspend power save time: ",
-                        getDischargeStepDurationsArray(), getNumDischargeStepDurations(),
-                        STEP_LEVEL_MODE_SCREEN_STATE|STEP_LEVEL_MODE_POWER_SAVE,
-                        (Display.STATE_DOZE_SUSPEND-1)|STEP_LEVEL_MODE_POWER_SAVE);
+                final LevelStepTracker steps = getDischargeLevelStepTracker();
+                for (int i=0; i< STEP_LEVEL_MODES_OF_INTEREST.length; i++) {
+                    dumpTimeEstimate(pw, "  Estimated ", STEP_LEVEL_MODE_LABELS[i], " time: ",
+                            steps.computeTimeEstimate(STEP_LEVEL_MODES_OF_INTEREST[i],
+                                    STEP_LEVEL_MODE_VALUES[i], null));
+                }
                 pw.println();
             }
-            if (dumpDurationSteps(pw, "Charge step durations:", getChargeStepDurationsArray(),
-                    getNumChargeStepDurations(), false)) {
+            if (dumpDurationSteps(pw, "  ", "Charge step durations:",
+                    getChargeLevelStepTracker(), false)) {
                 long timeRemaining = computeChargeTimeRemaining(SystemClock.elapsedRealtime());
                 if (timeRemaining >= 0) {
                     pw.print("  Estimated charge time remaining: ");
@@ -4251,6 +4552,75 @@
                 }
                 pw.println();
             }
+        }
+        if (!filtering || (flags&(DUMP_CHARGED_ONLY|DUMP_DAILY_ONLY)) != 0) {
+            pw.println("Daily stats:");
+            pw.print("  Current start time: ");
+            pw.println(DateFormat.format("yyyy-MM-dd-HH-mm-ss",
+                    getCurrentDailyStartTime()).toString());
+            pw.print("  Next min deadline: ");
+            pw.println(DateFormat.format("yyyy-MM-dd-HH-mm-ss",
+                    getNextMinDailyDeadline()).toString());
+            pw.print("  Next max deadline: ");
+            pw.println(DateFormat.format("yyyy-MM-dd-HH-mm-ss",
+                    getNextMaxDailyDeadline()).toString());
+            StringBuilder sb = new StringBuilder(64);
+            int[] outInt = new int[1];
+            LevelStepTracker dsteps = getDailyDischargeLevelStepTracker();
+            LevelStepTracker csteps = getDailyChargeLevelStepTracker();
+            if (dsteps.mNumStepDurations > 0 || csteps.mNumStepDurations > 0) {
+                if ((flags&DUMP_DAILY_ONLY) != 0) {
+                    if (dumpDurationSteps(pw, "    ", "  Current daily discharge step durations:",
+                            dsteps, false)) {
+                        dumpDailyLevelStepSummary(pw, "      ", "Discharge", dsteps,
+                                sb, outInt);
+                    }
+                    if (dumpDurationSteps(pw, "    ", "  Current daily charge step durations:",
+                            csteps, false)) {
+                        dumpDailyLevelStepSummary(pw, "      ", "Charge", csteps,
+                                sb, outInt);
+                    }
+                } else {
+                    pw.println("  Current daily steps:");
+                    dumpDailyLevelStepSummary(pw, "    ", "Discharge", dsteps,
+                            sb, outInt);
+                    dumpDailyLevelStepSummary(pw, "    ", "Charge", csteps,
+                            sb, outInt);
+                }
+            }
+            DailyItem dit;
+            int curIndex = 0;
+            while ((dit=getDailyItemLocked(curIndex)) != null) {
+                curIndex++;
+                if ((flags&DUMP_DAILY_ONLY) != 0) {
+                    pw.println();
+                }
+                pw.print("  Daily from ");
+                pw.print(DateFormat.format("yyyy-MM-dd-HH-mm-ss", dit.mStartTime).toString());
+                pw.print(" to ");
+                pw.print(DateFormat.format("yyyy-MM-dd-HH-mm-ss", dit.mEndTime).toString());
+                pw.println(":");
+                if ((flags&DUMP_DAILY_ONLY) != 0) {
+                    if (dumpDurationSteps(pw, "      ",
+                            "    Discharge step durations:", dit.mDischargeSteps, false)) {
+                        dumpDailyLevelStepSummary(pw, "        ", "Discharge", dit.mDischargeSteps,
+                                sb, outInt);
+                    }
+                    if (dumpDurationSteps(pw, "      ",
+                            "    Charge step durations:", dit.mChargeSteps, false)) {
+                        dumpDailyLevelStepSummary(pw, "        ", "Charge", dit.mChargeSteps,
+                                sb, outInt);
+                    }
+                } else {
+                    dumpDailyLevelStepSummary(pw, "    ", "Discharge", dit.mDischargeSteps,
+                            sb, outInt);
+                    dumpDailyLevelStepSummary(pw, "    ", "Charge", dit.mChargeSteps,
+                            sb, outInt);
+                }
+            }
+            pw.println();
+        }
+        if (!filtering || (flags&DUMP_CHARGED_ONLY) != 0) {
             pw.println("Statistics since last charge:");
             pw.println("  System starts: " + getStartCount()
                     + ", currently on battery: " + getIsOnBattery());
@@ -4275,8 +4645,8 @@
 
         long now = getHistoryBaseTime() + SystemClock.elapsedRealtime();
 
-        final boolean filtering =
-                (flags&(DUMP_HISTORY_ONLY|DUMP_UNPLUGGED_ONLY|DUMP_CHARGED_ONLY)) != 0;
+        final boolean filtering = (flags &
+                (DUMP_HISTORY_ONLY|DUMP_UNPLUGGED_ONLY|DUMP_CHARGED_ONLY|DUMP_DAILY_ONLY)) != 0;
 
         if ((flags&DUMP_INCLUDE_HISTORY) != 0 || (flags&DUMP_HISTORY_ONLY) != 0) {
             if (startIteratingHistoryLocked()) {
@@ -4302,7 +4672,7 @@
             }
         }
 
-        if (filtering && (flags&(DUMP_UNPLUGGED_ONLY|DUMP_CHARGED_ONLY)) == 0) {
+        if (filtering && (flags&(DUMP_UNPLUGGED_ONLY|DUMP_CHARGED_ONLY|DUMP_DAILY_ONLY)) == 0) {
             return;
         }
 
@@ -4334,8 +4704,7 @@
             }
         }
         if (!filtering || (flags&DUMP_CHARGED_ONLY) != 0) {
-            dumpDurationSteps(pw, DISCHARGE_STEP_DATA, getDischargeStepDurationsArray(),
-                    getNumDischargeStepDurations(), true);
+            dumpDurationSteps(pw, "", DISCHARGE_STEP_DATA, getDischargeLevelStepTracker(), true);
             String[] lineArgs = new String[1];
             long timeRemaining = computeBatteryTimeRemaining(SystemClock.elapsedRealtime());
             if (timeRemaining >= 0) {
@@ -4343,8 +4712,7 @@
                 dumpLine(pw, 0 /* uid */, "i" /* category */, DISCHARGE_TIME_REMAIN_DATA,
                         (Object[])lineArgs);
             }
-            dumpDurationSteps(pw, CHARGE_STEP_DATA, getChargeStepDurationsArray(),
-                    getNumChargeStepDurations(), true);
+            dumpDurationSteps(pw, "", CHARGE_STEP_DATA, getChargeLevelStepTracker(), true);
             timeRemaining = computeChargeTimeRemaining(SystemClock.elapsedRealtime());
             if (timeRemaining >= 0) {
                 lineArgs[0] = Long.toString(timeRemaining);
diff --git a/core/java/android/transition/TransitionManager.java b/core/java/android/transition/TransitionManager.java
index 80245ef..0b70fdb 100644
--- a/core/java/android/transition/TransitionManager.java
+++ b/core/java/android/transition/TransitionManager.java
@@ -181,24 +181,27 @@
     private static void changeScene(Scene scene, Transition transition) {
 
         final ViewGroup sceneRoot = scene.getSceneRoot();
+        if (!sPendingTransitions.contains(sceneRoot)) {
+            sPendingTransitions.add(sceneRoot);
 
-        Transition transitionClone = null;
-        if (transition != null) {
-            transitionClone = transition.clone();
-            transitionClone.setSceneRoot(sceneRoot);
+            Transition transitionClone = null;
+            if (transition != null) {
+                transitionClone = transition.clone();
+                transitionClone.setSceneRoot(sceneRoot);
+            }
+
+            Scene oldScene = Scene.getCurrentScene(sceneRoot);
+            if (oldScene != null && transitionClone != null &&
+                    oldScene.isCreatedFromLayoutResource()) {
+                transitionClone.setCanRemoveViews(true);
+            }
+
+            sceneChangeSetup(sceneRoot, transitionClone);
+
+            scene.enter();
+
+            sceneChangeRunTransition(sceneRoot, transitionClone);
         }
-
-        Scene oldScene = Scene.getCurrentScene(sceneRoot);
-        if (oldScene != null && transitionClone != null &&
-                oldScene.isCreatedFromLayoutResource()) {
-            transitionClone.setCanRemoveViews(true);
-        }
-
-        sceneChangeSetup(sceneRoot, transitionClone);
-
-        scene.enter();
-
-        sceneChangeRunTransition(sceneRoot, transitionClone);
     }
 
     private static ArrayMap<ViewGroup, ArrayList<Transition>> getRunningTransitions() {
diff --git a/core/java/com/android/internal/os/BatteryStatsImpl.java b/core/java/com/android/internal/os/BatteryStatsImpl.java
index d0c7f8c..f6c5dc7 100644
--- a/core/java/com/android/internal/os/BatteryStatsImpl.java
+++ b/core/java/com/android/internal/os/BatteryStatsImpl.java
@@ -56,20 +56,29 @@
 import android.util.SparseArray;
 import android.util.SparseIntArray;
 import android.util.TimeUtils;
+import android.util.Xml;
 import android.view.Display;
 
 import com.android.internal.annotations.GuardedBy;
 import com.android.internal.net.NetworkStatsFactory;
 import com.android.internal.util.ArrayUtils;
 import com.android.internal.util.FastPrintWriter;
+import com.android.internal.util.FastXmlSerializer;
 import com.android.internal.util.JournaledFile;
+import com.android.internal.util.XmlUtils;
+import org.xmlpull.v1.XmlPullParser;
+import org.xmlpull.v1.XmlPullParserException;
+import org.xmlpull.v1.XmlSerializer;
 
+import java.io.ByteArrayOutputStream;
 import java.io.File;
 import java.io.FileInputStream;
+import java.io.FileNotFoundException;
 import java.io.FileOutputStream;
 import java.io.IOException;
 import java.io.PrintWriter;
 import java.util.ArrayList;
+import java.util.Calendar;
 import java.util.HashMap;
 import java.util.Iterator;
 import java.util.List;
@@ -94,7 +103,7 @@
     private static final int MAGIC = 0xBA757475; // 'BATSTATS'
 
     // Current on-disk Parcel version
-    private static final int VERSION = 118 + (USE_OLD_HISTORY ? 1000 : 0);
+    private static final int VERSION = 119 + (USE_OLD_HISTORY ? 1000 : 0);
 
     // Maximum number of items we will record in the history.
     private static final int MAX_HISTORY_ITEMS = 2000;
@@ -111,6 +120,7 @@
 
     private final JournaledFile mFile;
     public final AtomicFile mCheckinFile;
+    public final AtomicFile mDailyFile;
 
     static final int MSG_UPDATE_WAKELOCKS = 1;
     static final int MSG_REPORT_POWER_CHANGE = 2;
@@ -386,16 +396,22 @@
     int mModStepMode = 0;
 
     int mLastDischargeStepLevel;
-    long mLastDischargeStepTime;
     int mMinDischargeStepLevel;
-    int mNumDischargeStepDurations;
-    final long[] mDischargeStepDurations = new long[MAX_LEVEL_STEPS];
+    final LevelStepTracker mDischargeStepTracker = new LevelStepTracker(MAX_LEVEL_STEPS);
+    final LevelStepTracker mDailyDischargeStepTracker = new LevelStepTracker(MAX_LEVEL_STEPS*2);
 
     int mLastChargeStepLevel;
-    long mLastChargeStepTime;
     int mMaxChargeStepLevel;
-    int mNumChargeStepDurations;
-    final long[] mChargeStepDurations = new long[MAX_LEVEL_STEPS];
+    final LevelStepTracker mChargeStepTracker = new LevelStepTracker(MAX_LEVEL_STEPS);
+    final LevelStepTracker mDailyChargeStepTracker = new LevelStepTracker(MAX_LEVEL_STEPS*2);
+
+    static final int MAX_DAILY_ITEMS = 10;
+
+    long mDailyStartTime = 0;
+    long mNextMinDailyDeadline = 0;
+    long mNextMaxDailyDeadline = 0;
+
+    final ArrayList<DailyItem> mDailyItems = new ArrayList<>();
 
     long mLastWriteTime = 0; // Milliseconds
 
@@ -478,6 +494,7 @@
     public BatteryStatsImpl() {
         mFile = null;
         mCheckinFile = null;
+        mDailyFile = null;
         mHandler = null;
         clearHistoryLocked();
     }
@@ -3199,6 +3216,7 @@
 
     public void noteScreenStateLocked(int state) {
         if (mScreenState != state) {
+            recordDailyStatsIfNeededLocked(true);
             final int oldState = mScreenState;
             mScreenState = state;
             if (DEBUG) Slog.v(TAG, "Screen state: oldState=" + Display.stateToString(oldState)
@@ -6594,6 +6612,7 @@
             mFile = null;
         }
         mCheckinFile = new AtomicFile(new File(systemDir, "batterystats-checkin.bin"));
+        mDailyFile = new AtomicFile(new File(systemDir, "batterystats-daily.xml"));
         mHandler = new MyHandler(handler.getLooper());
         mStartCount++;
         mScreenOnTimer = new StopwatchTimer(null, -1, null, mOnBatteryTimeBase);
@@ -6652,11 +6671,13 @@
         mCurrentBatteryLevel = 0;
         initDischarge();
         clearHistoryLocked();
+        updateDailyDeadlineLocked();
     }
 
     public BatteryStatsImpl(Parcel p) {
         mFile = null;
         mCheckinFile = null;
+        mDailyFile = null;
         mHandler = null;
         clearHistoryLocked();
         readFromParcel(p);
@@ -6676,6 +6697,286 @@
         }
     }
 
+    public void updateDailyDeadlineLocked() {
+        // Get the current time.
+        long currentTime = mDailyStartTime = System.currentTimeMillis();
+        Calendar calDeadline = Calendar.getInstance();
+        calDeadline.setTimeInMillis(currentTime);
+
+        // Move time up to the next day, ranging from 1am to 3pm.
+        calDeadline.set(Calendar.DAY_OF_YEAR, calDeadline.get(Calendar.DAY_OF_YEAR) + 1);
+        calDeadline.set(Calendar.MILLISECOND, 0);
+        calDeadline.set(Calendar.SECOND, 0);
+        calDeadline.set(Calendar.MINUTE, 0);
+        calDeadline.set(Calendar.HOUR_OF_DAY, 1);
+        mNextMinDailyDeadline = calDeadline.getTimeInMillis();
+        calDeadline.set(Calendar.HOUR_OF_DAY, 3);
+        mNextMaxDailyDeadline = calDeadline.getTimeInMillis();
+    }
+
+    public void recordDailyStatsIfNeededLocked(boolean settled) {
+        long currentTime = System.currentTimeMillis();
+        if (currentTime >= mNextMaxDailyDeadline) {
+            recordDailyStatsLocked();
+        } else if (settled && currentTime >= mNextMinDailyDeadline) {
+            recordDailyStatsLocked();
+        } else if (currentTime < (mDailyStartTime-(1000*60*60*24))) {
+            recordDailyStatsLocked();
+        }
+    }
+
+    public void recordDailyStatsLocked() {
+        DailyItem item = new DailyItem();
+        item.mStartTime = mDailyStartTime;
+        item.mEndTime = System.currentTimeMillis();
+        boolean hasData = false;
+        if (mDailyDischargeStepTracker.mNumStepDurations > 0) {
+            hasData = true;
+            item.mDischargeSteps = new LevelStepTracker(
+                    mDailyDischargeStepTracker.mNumStepDurations,
+                    mDailyDischargeStepTracker.mStepDurations);
+        }
+        if (mDailyChargeStepTracker.mNumStepDurations > 0) {
+            hasData = true;
+            item.mChargeSteps = new LevelStepTracker(
+                    mDailyChargeStepTracker.mNumStepDurations,
+                    mDailyChargeStepTracker.mStepDurations);
+        }
+        mDailyDischargeStepTracker.init();
+        mDailyChargeStepTracker.init();
+        updateDailyDeadlineLocked();
+
+        if (hasData) {
+            mDailyItems.add(item);
+            while (mDailyItems.size() > MAX_DAILY_ITEMS) {
+                mDailyItems.remove(0);
+            }
+            final ByteArrayOutputStream memStream = new ByteArrayOutputStream();
+            try {
+                XmlSerializer out = new FastXmlSerializer();
+                out.setOutput(memStream, "utf-8");
+                writeDailyItemsLocked(out);
+                BackgroundThread.getHandler().post(new Runnable() {
+                    @Override
+                    public void run() {
+                        synchronized (mCheckinFile) {
+                            FileOutputStream stream = null;
+                            try {
+                                stream = mDailyFile.startWrite();
+                                memStream.writeTo(stream);
+                                stream.flush();
+                                FileUtils.sync(stream);
+                                stream.close();
+                                mDailyFile.finishWrite(stream);
+                            } catch (IOException e) {
+                                Slog.w("BatteryStats",
+                                        "Error writing battery daily items", e);
+                                mDailyFile.failWrite(stream);
+                            }
+                        }
+                    }
+                });
+            } catch (IOException e) {
+            }
+        }
+    }
+
+    private void writeDailyItemsLocked(XmlSerializer out) throws IOException {
+        StringBuilder sb = new StringBuilder(64);
+        out.startDocument(null, true);
+        out.startTag(null, "daily-items");
+        for (int i=0; i<mDailyItems.size(); i++) {
+            final DailyItem dit = mDailyItems.get(i);
+            out.startTag(null, "item");
+            out.attribute(null, "start", Long.toString(dit.mStartTime));
+            out.attribute(null, "end", Long.toString(dit.mEndTime));
+            writeDailyLevelSteps(out, "dis", dit.mDischargeSteps, sb);
+            writeDailyLevelSteps(out, "chg", dit.mChargeSteps, sb);
+            out.endTag(null, "item");
+        }
+        out.endTag(null, "daily-items");
+        out.endDocument();
+    }
+
+    private void writeDailyLevelSteps(XmlSerializer out, String tag, LevelStepTracker steps,
+            StringBuilder tmpBuilder) throws IOException {
+        if (steps != null) {
+            out.startTag(null, tag);
+            out.attribute(null, "n", Integer.toString(steps.mNumStepDurations));
+            for (int i=0; i<steps.mNumStepDurations; i++) {
+                out.startTag(null, "s");
+                tmpBuilder.setLength(0);
+                steps.encodeEntryAt(i, tmpBuilder);
+                out.attribute(null, "v", tmpBuilder.toString());
+                out.endTag(null, "s");
+            }
+            out.endTag(null, tag);
+        }
+    }
+
+    public void readDailyStatsLocked() {
+        Slog.d(TAG, "Reading daily items from " + mDailyFile.getBaseFile());
+        mDailyItems.clear();
+        FileInputStream stream;
+        try {
+            stream = mDailyFile.openRead();
+        } catch (FileNotFoundException e) {
+            return;
+        }
+        try {
+            XmlPullParser parser = Xml.newPullParser();
+            parser.setInput(stream, null);
+            readDailyItemsLocked(parser);
+        } catch (XmlPullParserException e) {
+        } finally {
+            try {
+                stream.close();
+            } catch (IOException e) {
+            }
+        }
+    }
+
+    private void readDailyItemsLocked(XmlPullParser parser) {
+        try {
+            int type;
+            while ((type = parser.next()) != XmlPullParser.START_TAG
+                    && type != XmlPullParser.END_DOCUMENT) {
+                ;
+            }
+
+            if (type != XmlPullParser.START_TAG) {
+                throw new IllegalStateException("no start tag found");
+            }
+
+            int outerDepth = parser.getDepth();
+            while ((type = parser.next()) != XmlPullParser.END_DOCUMENT
+                    && (type != XmlPullParser.END_TAG || parser.getDepth() > outerDepth)) {
+                if (type == XmlPullParser.END_TAG || type == XmlPullParser.TEXT) {
+                    continue;
+                }
+
+                String tagName = parser.getName();
+                if (tagName.equals("item")) {
+                    readDailyItemTagLocked(parser);
+                } else {
+                    Slog.w(TAG, "Unknown element under <daily-items>: "
+                            + parser.getName());
+                    XmlUtils.skipCurrentTag(parser);
+                }
+            }
+
+        } catch (IllegalStateException e) {
+            Slog.w(TAG, "Failed parsing daily " + e);
+        } catch (NullPointerException e) {
+            Slog.w(TAG, "Failed parsing daily " + e);
+        } catch (NumberFormatException e) {
+            Slog.w(TAG, "Failed parsing daily " + e);
+        } catch (XmlPullParserException e) {
+            Slog.w(TAG, "Failed parsing daily " + e);
+        } catch (IOException e) {
+            Slog.w(TAG, "Failed parsing daily " + e);
+        } catch (IndexOutOfBoundsException e) {
+            Slog.w(TAG, "Failed parsing daily " + e);
+        }
+    }
+
+    void readDailyItemTagLocked(XmlPullParser parser) throws NumberFormatException,
+            XmlPullParserException, IOException {
+        DailyItem dit = new DailyItem();
+        String attr = parser.getAttributeValue(null, "start");
+        if (attr != null) {
+            dit.mStartTime = Long.parseLong(attr);
+        }
+        attr = parser.getAttributeValue(null, "end");
+        if (attr != null) {
+            dit.mEndTime = Long.parseLong(attr);
+        }
+        int outerDepth = parser.getDepth();
+        int type;
+        while ((type = parser.next()) != XmlPullParser.END_DOCUMENT
+                && (type != XmlPullParser.END_TAG || parser.getDepth() > outerDepth)) {
+            if (type == XmlPullParser.END_TAG || type == XmlPullParser.TEXT) {
+                continue;
+            }
+
+            String tagName = parser.getName();
+            if (tagName.equals("dis")) {
+                readDailyItemTagDetailsLocked(parser, dit, false, "dis");
+            } else if (tagName.equals("chg")) {
+                readDailyItemTagDetailsLocked(parser, dit, true, "chg");
+            } else {
+                Slog.w(TAG, "Unknown element under <item>: "
+                        + parser.getName());
+                XmlUtils.skipCurrentTag(parser);
+            }
+        }
+        mDailyItems.add(dit);
+    }
+
+    void readDailyItemTagDetailsLocked(XmlPullParser parser, DailyItem dit, boolean isCharge,
+            String tag)
+            throws NumberFormatException, XmlPullParserException, IOException {
+        final String numAttr = parser.getAttributeValue(null, "n");
+        if (numAttr == null) {
+            Slog.w(TAG, "Missing 'n' attribute at " + parser.getPositionDescription());
+            XmlUtils.skipCurrentTag(parser);
+            return;
+        }
+        final int num = Integer.parseInt(numAttr);
+        LevelStepTracker steps = new LevelStepTracker(num);
+        if (isCharge) {
+            dit.mChargeSteps = steps;
+        } else {
+            dit.mDischargeSteps = steps;
+        }
+        int i = 0;
+        int outerDepth = parser.getDepth();
+        int type;
+        while ((type = parser.next()) != XmlPullParser.END_DOCUMENT
+                && (type != XmlPullParser.END_TAG || parser.getDepth() > outerDepth)) {
+            if (type == XmlPullParser.END_TAG || type == XmlPullParser.TEXT) {
+                continue;
+            }
+
+            String tagName = parser.getName();
+            if ("s".equals(tagName)) {
+                if (i < num) {
+                    String valueAttr = parser.getAttributeValue(null, "v");
+                    if (valueAttr != null) {
+                        steps.decodeEntryAt(i, valueAttr);
+                        i++;
+                    }
+                }
+            } else {
+                Slog.w(TAG, "Unknown element under <" + tag + ">: "
+                        + parser.getName());
+                XmlUtils.skipCurrentTag(parser);
+            }
+        }
+        steps.mNumStepDurations = i;
+    }
+
+    @Override
+    public DailyItem getDailyItemLocked(int daysAgo) {
+        int index = mDailyItems.size()-1-daysAgo;
+        return index >= 0 ? mDailyItems.get(index) : null;
+    }
+
+    @Override
+    public long getCurrentDailyStartTime() {
+        return mDailyStartTime;
+    }
+
+    @Override
+    public long getNextMinDailyDeadline() {
+        return mNextMinDailyDeadline;
+    }
+
+    @Override
+    public long getNextMaxDailyDeadline() {
+        return mNextMaxDailyDeadline;
+    }
+
     @Override
     public boolean startIteratingOldHistoryLocked() {
         if (DEBUG_HISTORY) Slog.i(TAG, "ITERATING: buff size=" + mHistoryBuffer.dataSize()
@@ -6846,10 +7147,8 @@
         mDischargeAmountScreenOnSinceCharge = 0;
         mDischargeAmountScreenOff = 0;
         mDischargeAmountScreenOffSinceCharge = 0;
-        mLastDischargeStepTime = -1;
-        mNumDischargeStepDurations = 0;
-        mLastChargeStepTime = -1;
-        mNumChargeStepDurations = 0;
+        mDischargeStepTracker.init();
+        mChargeStepTracker.init();
     }
 
     public void resetAllStatsCmdLocked() {
@@ -7072,12 +7371,13 @@
                 resetAllStatsLocked();
                 mDischargeStartLevel = level;
                 reset = true;
-                mNumDischargeStepDurations = 0;
+                mDischargeStepTracker.init();
             }
             mOnBattery = mOnBatteryInternal = true;
             mLastDischargeStepLevel = level;
             mMinDischargeStepLevel = level;
-            mLastDischargeStepTime = -1;
+            mDischargeStepTracker.clearTime();
+            mDailyDischargeStepTracker.clearTime();
             mInitStepMode = mCurStepMode;
             mModStepMode = 0;
             pullPendingStateUpdatesLocked();
@@ -7116,10 +7416,9 @@
             }
             updateDischargeScreenLevelsLocked(screenOn, screenOn);
             updateTimeBasesLocked(false, !screenOn, uptime, realtime);
-            mNumChargeStepDurations = 0;
+            mChargeStepTracker.init();
             mLastChargeStepLevel = level;
             mMaxChargeStepLevel = level;
-            mLastChargeStepTime = -1;
             mInitStepMode = mCurStepMode;
             mModStepMode = 0;
         }
@@ -7171,34 +7470,12 @@
     // This should probably be exposed in the API, though it's not critical
     private static final int BATTERY_PLUGGED_NONE = 0;
 
-    private static int addLevelSteps(long[] steps, int stepCount, long lastStepTime,
-            int numStepLevels, long modeBits, long elapsedRealtime) {
-        if (lastStepTime >= 0 && numStepLevels > 0) {
-            long duration = elapsedRealtime - lastStepTime;
-            for (int i=0; i<numStepLevels; i++) {
-                System.arraycopy(steps, 0, steps, 1, steps.length-1);
-                long thisDuration = duration / (numStepLevels-i);
-                duration -= thisDuration;
-                if (thisDuration > STEP_LEVEL_TIME_MASK) {
-                    thisDuration = STEP_LEVEL_TIME_MASK;
-                }
-                steps[0] = thisDuration | modeBits;
-            }
-            stepCount += numStepLevels;
-            if (stepCount > steps.length) {
-                stepCount = steps.length;
-            }
-        }
-        return stepCount;
-    }
-
     public void setBatteryState(int status, int health, int plugType, int level,
             int temp, int volt) {
         synchronized(this) {
             final boolean onBattery = plugType == BATTERY_PLUGGED_NONE;
             final long uptime = SystemClock.uptimeMillis();
             final long elapsedRealtime = SystemClock.elapsedRealtime();
-            int oldStatus = mHistoryCur.batteryStatus;
             if (!mHaveBatteryLevel) {
                 mHaveBatteryLevel = true;
                 // We start out assuming that the device is plugged in (not
@@ -7212,8 +7489,14 @@
                         mHistoryCur.states |= HistoryItem.STATE_BATTERY_PLUGGED_FLAG;
                     }
                 }
-                oldStatus = status;
+                mHistoryCur.batteryStatus = (byte)status;
+                mHistoryCur.batteryLevel = (byte)level;
+                mMaxChargeStepLevel = mMinDischargeStepLevel =
+                        mLastChargeStepLevel = mLastDischargeStepLevel = level;
+            } else if (mCurrentBatteryLevel != level || mOnBattery != onBattery) {
+                recordDailyStatsIfNeededLocked(level >= 100 && onBattery);
             }
+            int oldStatus = mHistoryCur.batteryStatus;
             if (onBattery) {
                 mDischargeCurrentLevel = level;
                 if (!mRecordingHistory) {
@@ -7274,23 +7557,23 @@
                         | (((long)(level&0xff)) << STEP_LEVEL_LEVEL_SHIFT);
                 if (onBattery) {
                     if (mLastDischargeStepLevel != level && mMinDischargeStepLevel > level) {
-                        mNumDischargeStepDurations = addLevelSteps(mDischargeStepDurations,
-                                mNumDischargeStepDurations, mLastDischargeStepTime,
-                                mLastDischargeStepLevel - level, modeBits, elapsedRealtime);
+                        mDischargeStepTracker.addLevelSteps(mLastDischargeStepLevel - level,
+                                modeBits, elapsedRealtime);
+                        mDailyDischargeStepTracker.addLevelSteps(mLastDischargeStepLevel - level,
+                                modeBits, elapsedRealtime);
                         mLastDischargeStepLevel = level;
                         mMinDischargeStepLevel = level;
-                        mLastDischargeStepTime = elapsedRealtime;
                         mInitStepMode = mCurStepMode;
                         mModStepMode = 0;
                     }
                 } else {
                     if (mLastChargeStepLevel != level && mMaxChargeStepLevel < level) {
-                        mNumChargeStepDurations = addLevelSteps(mChargeStepDurations,
-                                mNumChargeStepDurations, mLastChargeStepTime,
-                                level - mLastChargeStepLevel, modeBits, elapsedRealtime);
+                        mChargeStepTracker.addLevelSteps(level - mLastChargeStepLevel,
+                                modeBits, elapsedRealtime);
+                        mDailyChargeStepTracker.addLevelSteps(level - mLastChargeStepLevel,
+                                modeBits, elapsedRealtime);
                         mLastChargeStepLevel = level;
                         mMaxChargeStepLevel = level;
-                        mLastChargeStepTime = elapsedRealtime;
                         mInitStepMode = mCurStepMode;
                         mModStepMode = 0;
                     }
@@ -7565,22 +7848,24 @@
         long usPerLevel = duration/discharge;
         return usPerLevel * mCurrentBatteryLevel;
         */
-        if (mNumDischargeStepDurations < 1) {
+        if (mDischargeStepTracker.mNumStepDurations < 1) {
             return -1;
         }
-        long msPerLevel = computeTimePerLevel(mDischargeStepDurations, mNumDischargeStepDurations);
+        long msPerLevel = mDischargeStepTracker.computeTimePerLevel();
         if (msPerLevel <= 0) {
             return -1;
         }
         return (msPerLevel * mCurrentBatteryLevel) * 1000;
     }
 
-    public int getNumDischargeStepDurations() {
-        return mNumDischargeStepDurations;
+    @Override
+    public LevelStepTracker getDischargeLevelStepTracker() {
+        return mDischargeStepTracker;
     }
 
-    public long[] getDischargeStepDurationsArray() {
-        return mDischargeStepDurations;
+    @Override
+    public LevelStepTracker getDailyDischargeLevelStepTracker() {
+        return mDailyDischargeStepTracker;
     }
 
     @Override
@@ -7602,22 +7887,24 @@
         long usPerLevel = duration/(curLevel-plugLevel);
         return usPerLevel * (100-curLevel);
         */
-        if (mNumChargeStepDurations < 1) {
+        if (mChargeStepTracker.mNumStepDurations < 1) {
             return -1;
         }
-        long msPerLevel = computeTimePerLevel(mChargeStepDurations, mNumChargeStepDurations);
+        long msPerLevel = mChargeStepTracker.computeTimePerLevel();
         if (msPerLevel <= 0) {
             return -1;
         }
         return (msPerLevel * (100-mCurrentBatteryLevel)) * 1000;
     }
 
-    public int getNumChargeStepDurations() {
-        return mNumChargeStepDurations;
+    @Override
+    public LevelStepTracker getChargeLevelStepTracker() {
+        return mChargeStepTracker;
     }
 
-    public long[] getChargeStepDurationsArray() {
-        return mChargeStepDurations;
+    @Override
+    public LevelStepTracker getDailyChargeLevelStepTracker() {
+        return mDailyChargeStepTracker;
     }
 
     long getBatteryUptimeLocked() {
@@ -7915,6 +8202,10 @@
     }
 
     public void readLocked() {
+        if (mDailyFile != null) {
+            readDailyStatsLocked();
+        }
+
         if (mFile == null) {
             Slog.w("BatteryStats", "readLocked: no file associated with this instance");
             return;
@@ -7952,6 +8243,8 @@
             addHistoryBufferLocked(elapsedRealtime, uptime, HistoryItem.CMD_START, mHistoryCur);
             startRecordingHistory(elapsedRealtime, uptime, false);
         }
+
+        recordDailyStatsIfNeededLocked(false);
     }
 
     public int describeContents() {
@@ -8110,10 +8403,13 @@
         mHighDischargeAmountSinceCharge = in.readInt();
         mDischargeAmountScreenOnSinceCharge = in.readInt();
         mDischargeAmountScreenOffSinceCharge = in.readInt();
-        mNumDischargeStepDurations = in.readInt();
-        in.readLongArray(mDischargeStepDurations);
-        mNumChargeStepDurations = in.readInt();
-        in.readLongArray(mChargeStepDurations);
+        mDischargeStepTracker.readFromParcel(in);
+        mChargeStepTracker.readFromParcel(in);
+        mDailyDischargeStepTracker.readFromParcel(in);
+        mDailyChargeStepTracker.readFromParcel(in);
+        mDailyStartTime = in.readLong();
+        mNextMinDailyDeadline = in.readLong();
+        mNextMaxDailyDeadline = in.readLong();
 
         mStartCount++;
 
@@ -8404,10 +8700,13 @@
         out.writeInt(getHighDischargeAmountSinceCharge());
         out.writeInt(getDischargeAmountScreenOnSinceCharge());
         out.writeInt(getDischargeAmountScreenOffSinceCharge());
-        out.writeInt(mNumDischargeStepDurations);
-        out.writeLongArray(mDischargeStepDurations);
-        out.writeInt(mNumChargeStepDurations);
-        out.writeLongArray(mChargeStepDurations);
+        mDischargeStepTracker.writeToParcel(out);
+        mChargeStepTracker.writeToParcel(out);
+        mDailyDischargeStepTracker.writeToParcel(out);
+        mDailyChargeStepTracker.writeToParcel(out);
+        out.writeLong(mDailyStartTime);
+        out.writeLong(mNextMinDailyDeadline);
+        out.writeLong(mNextMaxDailyDeadline);
 
         mScreenOnTimer.writeSummaryFromParcelLocked(out, NOWREAL_SYS);
         for (int i=0; i<NUM_SCREEN_BRIGHTNESS_BINS; i++) {
@@ -8770,10 +9069,8 @@
         mDischargeAmountScreenOnSinceCharge = in.readInt();
         mDischargeAmountScreenOff = in.readInt();
         mDischargeAmountScreenOffSinceCharge = in.readInt();
-        mNumDischargeStepDurations = in.readInt();
-        in.readLongArray(mDischargeStepDurations);
-        mNumChargeStepDurations = in.readInt();
-        in.readLongArray(mChargeStepDurations);
+        mDischargeStepTracker.readFromParcel(in);
+        mChargeStepTracker.readFromParcel(in);
         mLastWriteTime = in.readLong();
 
         mBluetoothPingCount = in.readInt();
@@ -8912,10 +9209,8 @@
         out.writeInt(mDischargeAmountScreenOnSinceCharge);
         out.writeInt(mDischargeAmountScreenOff);
         out.writeInt(mDischargeAmountScreenOffSinceCharge);
-        out.writeInt(mNumDischargeStepDurations);
-        out.writeLongArray(mDischargeStepDurations);
-        out.writeInt(mNumChargeStepDurations);
-        out.writeLongArray(mChargeStepDurations);
+        mDischargeStepTracker.writeToParcel(out);
+        mChargeStepTracker.writeToParcel(out);
         out.writeLong(mLastWriteTime);
 
         out.writeInt(getBluetoothPingCount());
diff --git a/core/res/res/layout-television/user_switching_dialog.xml b/core/res/res/layout-television/user_switching_dialog.xml
new file mode 100644
index 0000000..72150e3
--- /dev/null
+++ b/core/res/res/layout-television/user_switching_dialog.xml
@@ -0,0 +1,29 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+     Copyright (C) 2015 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.
+-->
+
+<TextView xmlns:android="http://schemas.android.com/apk/res/android"
+        android:id="@+id/message"
+        style="?attr/textAppearanceListItem"
+        android:layout_width="match_parent"
+        android:background="@color/background_leanback_dark"
+        android:textColor="@color/primary_text_leanback_dark"
+        android:layout_height="match_parent"
+        android:gravity="center"
+        android:paddingStart="?attr/dialogPreferredPadding"
+        android:paddingEnd="?attr/dialogPreferredPadding"
+        android:paddingTop="24dp"
+        android:paddingBottom="24dp" />
diff --git a/media/java/android/media/MediaPlayer.java b/media/java/android/media/MediaPlayer.java
index d61610d..d77fcd8 100644
--- a/media/java/android/media/MediaPlayer.java
+++ b/media/java/android/media/MediaPlayer.java
@@ -16,6 +16,7 @@
 
 package android.media;
 
+import android.annotation.IntDef;
 import android.app.ActivityThread;
 import android.app.AppOpsManager;
 import android.content.ContentResolver;
@@ -61,6 +62,8 @@
 import java.io.IOException;
 import java.io.InputStream;
 import java.lang.Runnable;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
 import java.net.InetSocketAddress;
 import java.util.Map;
 import java.util.Scanner;
@@ -472,6 +475,11 @@
  *     <td>{} </p></td>
  *     <td>This method can be called in any state and calling it does not change
  *         the object state.  </p></td></tr>
+ * <tr><td>setPlaybackRate</p></td>
+ *     <td>any </p></td>
+ *     <td>{} </p></td>
+ *     <td>This method can be called in any state and calling it does not change
+ *         the object state. </p></td></tr>
  * <tr><td>setVolume </p></td>
  *     <td>{Idle, Initialized, Stopped, Prepared, Started, Paused,
  *          PlaybackCompleted}</p></td>
@@ -1319,6 +1327,59 @@
     public native boolean isPlaying();
 
     /**
+     * Specifies resampling as audio mode for variable rate playback, i.e.,
+     * resample the waveform based on the requested playback rate to get
+     * a new waveform, and play back the new waveform at the original sampling
+     * frequency.
+     * When rate is larger than 1.0, pitch becomes higher.
+     * When rate is smaller than 1.0, pitch becomes lower.
+     */
+    public static final int PLAYBACK_RATE_AUDIO_MODE_RESAMPLE = 0;
+
+    /**
+     * Specifies time stretching as audio mode for variable rate playback.
+     * Time stretching changes the duration of the audio samples without
+     * affecting its pitch.
+     * FIXME: implement time strectching.
+     * @hide
+     */
+    public static final int PLAYBACK_RATE_AUDIO_MODE_STRETCH = 1;
+
+    /** @hide */
+    @IntDef(
+        value = {
+            PLAYBACK_RATE_AUDIO_MODE_RESAMPLE,
+            PLAYBACK_RATE_AUDIO_MODE_STRETCH })
+    @Retention(RetentionPolicy.SOURCE)
+    public @interface PlaybackRateAudioMode {}
+
+    /**
+     * Sets playback rate and audio mode.
+     *
+     * <p> The supported audio modes are:
+     * <ul>
+     * <li> {@link #PLAYBACK_RATE_AUDIO_MODE_RESAMPLE}
+     * </ul>
+     *
+     * @param rate the ratio between desired playback rate and normal one.
+     * @param audioMode audio playback mode. Must be one of the supported
+     * audio modes.
+     *
+     * @throws IllegalStateException if the internal player engine has not been
+     * initialized.
+     * @throws IllegalArgumentException if audioMode is not supported.
+     */
+    public void setPlaybackRate(float rate, @PlaybackRateAudioMode int audioMode) {
+        if (!isAudioPlaybackModeSupported(audioMode)) {
+            final String msg = "Audio playback mode " + audioMode + " is not supported";
+            throw new IllegalArgumentException(msg);
+        }
+        _setPlaybackRate(rate);
+    }
+
+    private native void _setPlaybackRate(float rate) throws IllegalStateException;
+
+    /**
      * Seeks to specified time position.
      *
      * @param msec the offset in milliseconds from the start to seek to
@@ -3078,6 +3139,14 @@
                 mode == VIDEO_SCALING_MODE_SCALE_TO_FIT_WITH_CROPPING);
     }
 
+    /*
+     * Test whether a given audio playback mode is supported.
+     * TODO query supported AudioPlaybackMode from player.
+     */
+    private boolean isAudioPlaybackModeSupported(int mode) {
+        return (mode == PLAYBACK_RATE_AUDIO_MODE_RESAMPLE);
+    }
+
     /** @hide */
     static class TimeProvider implements MediaPlayer.OnSeekCompleteListener,
             MediaTimeProvider {
diff --git a/media/jni/android_media_MediaPlayer.cpp b/media/jni/android_media_MediaPlayer.cpp
index 820de5b..55643f7 100644
--- a/media/jni/android_media_MediaPlayer.cpp
+++ b/media/jni/android_media_MediaPlayer.cpp
@@ -402,6 +402,18 @@
 }
 
 static void
+android_media_MediaPlayer_setPlaybackRate(JNIEnv *env, jobject thiz, jfloat rate)
+{
+    sp<MediaPlayer> mp = getMediaPlayer(env, thiz);
+    if (mp == NULL) {
+        jniThrowException(env, "java/lang/IllegalStateException", NULL);
+        return;
+    }
+    ALOGV("setPlaybackRate: %f", rate);
+    process_media_player_call(env, thiz, mp->setPlaybackRate(rate), NULL, NULL);
+}
+
+static void
 android_media_MediaPlayer_seekTo(JNIEnv *env, jobject thiz, jint msec)
 {
     sp<MediaPlayer> mp = getMediaPlayer(env, thiz);
@@ -867,6 +879,7 @@
     {"_stop",               "()V",                              (void *)android_media_MediaPlayer_stop},
     {"getVideoWidth",       "()I",                              (void *)android_media_MediaPlayer_getVideoWidth},
     {"getVideoHeight",      "()I",                              (void *)android_media_MediaPlayer_getVideoHeight},
+    {"_setPlaybackRate",    "(F)V",                             (void *)android_media_MediaPlayer_setPlaybackRate},
     {"seekTo",              "(I)V",                             (void *)android_media_MediaPlayer_seekTo},
     {"_pause",              "()V",                              (void *)android_media_MediaPlayer_pause},
     {"isPlaying",           "()Z",                              (void *)android_media_MediaPlayer_isPlaying},
diff --git a/services/core/java/com/android/server/am/BatteryStatsService.java b/services/core/java/com/android/server/am/BatteryStatsService.java
index a4602a1..6eacfa9 100644
--- a/services/core/java/com/android/server/am/BatteryStatsService.java
+++ b/services/core/java/com/android/server/am/BatteryStatsService.java
@@ -751,14 +751,17 @@
     private void dumpHelp(PrintWriter pw) {
         pw.println("Battery stats (batterystats) dump options:");
         pw.println("  [--checkin] [--history] [--history-start] [--unplugged] [--charged] [-c]");
-        pw.println("  [--reset] [--write] [-h] [<package.name>]");
+        pw.println("  [--daily] [--reset] [--write] [--new-daily] [--read-daily] [-h] [<package.name>]");
         pw.println("  --checkin: format output for a checkin report.");
         pw.println("  --history: show only history data.");
         pw.println("  --history-start <num>: show only history data starting at given time offset.");
         pw.println("  --unplugged: only output data since last unplugged.");
         pw.println("  --charged: only output data since last charged.");
+        pw.println("  --daily: only output full daily data.");
         pw.println("  --reset: reset the stats, clearing all current data.");
         pw.println("  --write: force write current collected stats to disk.");
+        pw.println("  --new-daily: immediately create and write new daily stats record.");
+        pw.println("  --read-daily: read-load last written daily stats.");
         pw.println("  <package.name>: optional name of package to filter output by.");
         pw.println("  -h: print this help text.");
         pw.println("Battery stats (batterystats) commands:");
@@ -835,6 +838,8 @@
                     flags |= BatteryStats.DUMP_UNPLUGGED_ONLY;
                 } else if ("--charged".equals(arg)) {
                     flags |= BatteryStats.DUMP_CHARGED_ONLY;
+                } else if ("--daily".equals(arg)) {
+                    flags |= BatteryStats.DUMP_DAILY_ONLY;
                 } else if ("--reset".equals(arg)) {
                     synchronized (mStats) {
                         mStats.resetAllStatsCmdLocked();
@@ -847,6 +852,18 @@
                         pw.println("Battery stats written.");
                         noOutput = true;
                     }
+                } else if ("--new-daily".equals(arg)) {
+                    synchronized (mStats) {
+                        mStats.recordDailyStatsLocked();
+                        pw.println("New daily stats written.");
+                        noOutput = true;
+                    }
+                } else if ("--read-daily".equals(arg)) {
+                    synchronized (mStats) {
+                        mStats.readDailyStatsLocked();
+                        pw.println("Last daily stats read.");
+                        noOutput = true;
+                    }
                 } else if ("--enable".equals(arg) || "enable".equals(arg)) {
                     i = doEnableOrDisable(pw, i, args, true);
                     if (i < 0) {