Track camera and flashlight usage in battery stats.

Bug: 15986092

Change-Id: Idf03981271721c44fd43327d5d90cf6f3fecfacb
diff --git a/core/java/android/os/BatteryStats.java b/core/java/android/os/BatteryStats.java
index 5f515eb..1f2ad5f 100644
--- a/core/java/android/os/BatteryStats.java
+++ b/core/java/android/os/BatteryStats.java
@@ -132,6 +132,16 @@
     public static final int AUDIO_TURNED_ON = 15;
 
     /**
+     * A constant indicating a flashlight turn on timer
+     */
+    public static final int FLASHLIGHT_TURNED_ON = 16;
+
+    /**
+     * A constant indicating a camera turn on timer
+     */
+    public static final int CAMERA_TURNED_ON = 17;
+
+    /**
      * Include all of the data in the stats, including previously saved data.
      */
     public static final int STATS_SINCE_CHARGED = 0;
@@ -208,6 +218,10 @@
     private static final String CHARGE_STEP_DATA = "csd";
     private static final String DISCHARGE_TIME_REMAIN_DATA = "dtr";
     private static final String CHARGE_TIME_REMAIN_DATA = "ctr";
+    private static final String FLASHLIGHT_DATA = "fla";
+    private static final String CAMERA_DATA = "cam";
+    private static final String VIDEO_DATA = "vid";
+    private static final String AUDIO_DATA = "aud";
 
     private final StringBuilder mFormatBuilder = new StringBuilder(32);
     private final Formatter mFormatter = new Formatter(mFormatBuilder);
@@ -381,8 +395,10 @@
         public abstract long getWifiBatchedScanTime(int csphBin, long elapsedRealtimeUs, int which);
         public abstract int getWifiBatchedScanCount(int csphBin, int which);
         public abstract long getWifiMulticastTime(long elapsedRealtimeUs, int which);
-        public abstract long getAudioTurnedOnTime(long elapsedRealtimeUs, int which);
-        public abstract long getVideoTurnedOnTime(long elapsedRealtimeUs, int which);
+        public abstract Timer getAudioTurnedOnTimer();
+        public abstract Timer getVideoTurnedOnTimer();
+        public abstract Timer getFlashlightTurnedOnTimer();
+        public abstract Timer getCameraTurnedOnTimer();
         public abstract Timer getForegroundActivityTimer();
 
         // Time this uid has any processes in foreground state.
@@ -1106,6 +1122,7 @@
         public static final int STATE2_CHARGING_FLAG = 1<<25;
         public static final int STATE2_PHONE_IN_CALL_FLAG = 1<<24;
         public static final int STATE2_BLUETOOTH_ON_FLAG = 1<<23;
+        public static final int STATE2_CAMERA_FLAG = 1<<22;
 
         public static final int MOST_INTERESTING_STATES2 =
             STATE2_POWER_SAVE_FLAG | STATE2_WIFI_ON_FLAG | STATE2_DEVICE_IDLE_FLAG
@@ -1813,6 +1830,7 @@
         new BitDescription(HistoryItem.STATE2_WIFI_SUPPL_STATE_MASK,
                 HistoryItem.STATE2_WIFI_SUPPL_STATE_SHIFT, "wifi_suppl", "Wsp",
                 WIFI_SUPPL_STATE_NAMES, WIFI_SUPPL_STATE_SHORT_NAMES),
+        new BitDescription(HistoryItem.STATE2_CAMERA_FLAG, "camera", "ca"),
     };
 
     public static final String[] HISTORY_EVENT_NAMES = new String[] {
@@ -2317,10 +2335,10 @@
      */
     private static final String printWakeLock(StringBuilder sb, Timer timer,
             long elapsedRealtimeUs, String name, int which, String linePrefix) {
-        
+
         if (timer != null) {
             long totalTimeMillis = computeWakeLock(timer, elapsedRealtimeUs, which);
-            
+
             int count = timer.getCountLocked(which);
             if (totalTimeMillis != 0) {
                 sb.append(linePrefix);
@@ -2337,6 +2355,40 @@
         }
         return linePrefix;
     }
+
+    /**
+     *
+     * @param pw a PrintWriter object to print to.
+     * @param sb a StringBuilder object.
+     * @param timer a Timer object contining the wakelock times.
+     * @param rawRealtime the current on-battery time in microseconds.
+     * @param which which one of STATS_SINCE_CHARGED, STATS_SINCE_UNPLUGGED, or STATS_CURRENT.
+     * @param prefix a String to be prepended to each line of output.
+     * @param type the name of the timer.
+     */
+    private static final boolean printTimer(PrintWriter pw, StringBuilder sb, Timer timer,
+            long rawRealtime, int which, String prefix, String type) {
+        if (timer != null) {
+            // Convert from microseconds to milliseconds with rounding
+            final long totalTime = (timer.getTotalTimeLocked(
+                    rawRealtime, which) + 500) / 1000;
+            final int count = timer.getCountLocked(which);
+            if (totalTime != 0) {
+                sb.setLength(0);
+                sb.append(prefix);
+                sb.append("    ");
+                sb.append(type);
+                sb.append(": ");
+                formatTimeMs(sb, totalTime);
+                sb.append("realtime (");
+                sb.append(count);
+                sb.append(" times)");
+                pw.println(sb.toString());
+                return true;
+            }
+        }
+        return false;
+    }
     
     /**
      * Checkin version of wakelock printer. Prints simple comma-separated list.
@@ -2375,12 +2427,15 @@
      */
     private static final void dumpLine(PrintWriter pw, int uid, String category, String type, 
            Object... args ) {
-        pw.print(BATTERY_STATS_CHECKIN_VERSION); pw.print(',');
-        pw.print(uid); pw.print(',');
-        pw.print(category); pw.print(',');
+        pw.print(BATTERY_STATS_CHECKIN_VERSION);
+        pw.print(',');
+        pw.print(uid);
+        pw.print(',');
+        pw.print(category);
+        pw.print(',');
         pw.print(type);
-        
-        for (Object arg : args) {  
+
+        for (Object arg : args) {
             pw.print(',');
             pw.print(arg);
         }
@@ -2388,6 +2443,30 @@
     }
 
     /**
+     * Dump a given timer stat for terse checkin mode.
+     *
+     * @param pw the PageWriter to dump log to
+     * @param uid the UID to log
+     * @param category category of data (e.g. "total", "last", "unplugged", "current" )
+     * @param type type of data (e.g. "wakelock", "sensor", "process", "apk" ,  "process", "network")
+     * @param timer a {@link Timer} to dump stats for
+     * @param rawRealtime the current elapsed realtime of the system in microseconds
+     * @param which one of STATS_SINCE_CHARGED, STATS_SINCE_UNPLUGGED, or STATS_CURRENT
+     */
+    private static final void dumpTimer(PrintWriter pw, int uid, String category, String type,
+                                        Timer timer, long rawRealtime, int which) {
+        if (timer != null) {
+            // Convert from microseconds to milliseconds with rounding
+            final long totalTime = (timer.getTotalTimeLocked(rawRealtime, which) + 500)
+                    / 1000;
+            final int count = timer.getCountLocked(which);
+            if (totalTime != 0) {
+                dumpLine(pw, uid, category, type, totalTime, count);
+            }
+        }
+    }
+
+    /**
      * Temporary for settings.
      */
     public final void dumpCheckinLocked(Context context, PrintWriter pw, int which, int reqUid) {
@@ -2764,6 +2843,15 @@
                 }
             }
 
+            dumpTimer(pw, uid, category, FLASHLIGHT_DATA, u.getFlashlightTurnedOnTimer(),
+                    rawRealtime, which);
+            dumpTimer(pw, uid, category, CAMERA_DATA, u.getCameraTurnedOnTimer(),
+                    rawRealtime, which);
+            dumpTimer(pw, uid, category, VIDEO_DATA, u.getVideoTurnedOnTimer(),
+                    rawRealtime, which);
+            dumpTimer(pw, uid, category, AUDIO_DATA, u.getAudioTurnedOnTimer(),
+                    rawRealtime, which);
+
             final SparseArray<? extends BatteryStats.Uid.Sensor> sensors = u.getSensorStats();
             final int NSE = sensors.size();
             for (int ise=0; ise<NSE; ise++) {
@@ -2781,27 +2869,11 @@
                 }
             }
 
-            final Timer vibTimer = u.getVibratorOnTimer();
-            if (vibTimer != null) {
-                // Convert from microseconds to milliseconds with rounding
-                final long totalTime = (vibTimer.getTotalTimeLocked(rawRealtime, which) + 500)
-                        / 1000;
-                final int count = vibTimer.getCountLocked(which);
-                if (totalTime != 0) {
-                    dumpLine(pw, uid, category, VIBRATOR_DATA, totalTime, count);
-                }
-            }
+            dumpTimer(pw, uid, category, VIBRATOR_DATA, u.getVibratorOnTimer(),
+                    rawRealtime, which);
 
-            final Timer fgTimer = u.getForegroundActivityTimer();
-            if (fgTimer != null) {
-                // Convert from microseconds to milliseconds with rounding
-                final long totalTime = (fgTimer.getTotalTimeLocked(rawRealtime, which) + 500)
-                        / 1000;
-                final int count = fgTimer.getCountLocked(which);
-                if (totalTime != 0) {
-                    dumpLine(pw, uid, category, FOREGROUND_DATA, totalTime, count);
-                }
-            }
+            dumpTimer(pw, uid, category, FOREGROUND_DATA, u.getForegroundActivityTimer(),
+                    rawRealtime, which);
 
             final Object[] stateTimes = new Object[Uid.NUM_PROCESS_STATE];
             long totalStateTime = 0;
@@ -3788,6 +3860,15 @@
                 uidActivity = true;
             }
 
+            uidActivity |= printTimer(pw, sb, u.getFlashlightTurnedOnTimer(), rawRealtime, which,
+                    prefix, "Flashlight");
+            uidActivity |= printTimer(pw, sb, u.getCameraTurnedOnTimer(), rawRealtime, which,
+                    prefix, "Camera");
+            uidActivity |= printTimer(pw, sb, u.getVideoTurnedOnTimer(), rawRealtime, which,
+                    prefix, "Video");
+            uidActivity |= printTimer(pw, sb, u.getAudioTurnedOnTimer(), rawRealtime, which,
+                    prefix, "Audio");
+
             final SparseArray<? extends BatteryStats.Uid.Sensor> sensors = u.getSensorStats();
             final int NSE = sensors.size();
             for (int ise=0; ise<NSE; ise++) {
@@ -3827,44 +3908,10 @@
                 uidActivity = true;
             }
 
-            final Timer vibTimer = u.getVibratorOnTimer();
-            if (vibTimer != null) {
-                // Convert from microseconds to milliseconds with rounding
-                final long totalTime = (vibTimer.getTotalTimeLocked(
-                        rawRealtime, which) + 500) / 1000;
-                final int count = vibTimer.getCountLocked(which);
-                //timer.logState();
-                if (totalTime != 0) {
-                    sb.setLength(0);
-                    sb.append(prefix);
-                    sb.append("    Vibrator: ");
-                    formatTimeMs(sb, totalTime);
-                    sb.append("realtime (");
-                    sb.append(count);
-                    sb.append(" times)");
-                    pw.println(sb.toString());
-                    uidActivity = true;
-                }
-            }
-
-            final Timer fgTimer = u.getForegroundActivityTimer();
-            if (fgTimer != null) {
-                // Convert from microseconds to milliseconds with rounding
-                final long totalTime = (fgTimer.getTotalTimeLocked(rawRealtime, which) + 500)
-                        / 1000;
-                final int count = fgTimer.getCountLocked(which);
-                if (totalTime != 0) {
-                    sb.setLength(0);
-                    sb.append(prefix);
-                    sb.append("    Foreground activities: ");
-                    formatTimeMs(sb, totalTime);
-                    sb.append("realtime (");
-                    sb.append(count);
-                    sb.append(" times)");
-                    pw.println(sb.toString());
-                    uidActivity = true;
-                }
-            }
+            uidActivity |= printTimer(pw, sb, u.getVibratorOnTimer(), rawRealtime, which, prefix,
+                    "Vibrator");
+            uidActivity |= printTimer(pw, sb, u.getForegroundActivityTimer(), rawRealtime, which,
+                    prefix, "Foreground activities");
 
             long totalStateTime = 0;
             for (int ips=0; ips<Uid.NUM_PROCESS_STATE; ips++) {
diff --git a/core/java/com/android/internal/app/IBatteryStats.aidl b/core/java/com/android/internal/app/IBatteryStats.aidl
index 7c5c565..929cacd 100644
--- a/core/java/com/android/internal/app/IBatteryStats.aidl
+++ b/core/java/com/android/internal/app/IBatteryStats.aidl
@@ -34,6 +34,12 @@
     void noteStopAudio(int uid);
     void noteResetVideo();
     void noteResetAudio();
+    void noteFlashlightOn(int uid);
+    void noteFlashlightOff(int uid);
+    void noteStartCamera(int uid);
+    void noteStopCamera(int uid);
+    void noteResetCamera();
+    void noteResetFlashlight();
 
     // Remaining methods are only used in Java.
     byte[] getStatistics();
@@ -72,8 +78,6 @@
 
     void noteVibratorOn(int uid, long durationMillis);
     void noteVibratorOff(int uid);
-    void noteFlashlightOn();
-    void noteFlashlightOff();
     void noteStartGps(int uid);
     void noteStopGps(int uid);
     void noteScreenState(int state);
diff --git a/core/java/com/android/internal/os/BatteryStatsImpl.java b/core/java/com/android/internal/os/BatteryStatsImpl.java
index eaca43b..62745d4 100644
--- a/core/java/com/android/internal/os/BatteryStatsImpl.java
+++ b/core/java/com/android/internal/os/BatteryStatsImpl.java
@@ -211,6 +211,8 @@
     final SparseArray<ArrayList<StopwatchTimer>> mWifiBatchedScanTimers = new SparseArray<>();
     final ArrayList<StopwatchTimer> mAudioTurnedOnTimers = new ArrayList<>();
     final ArrayList<StopwatchTimer> mVideoTurnedOnTimers = new ArrayList<>();
+    final ArrayList<StopwatchTimer> mFlashlightTurnedOnTimers = new ArrayList<>();
+    final ArrayList<StopwatchTimer> mCameraTurnedOnTimers = new ArrayList<>();
 
     // Last partial timers we use for distributing CPU usage.
     final ArrayList<StopwatchTimer> mLastPartialTimers = new ArrayList<>();
@@ -343,9 +345,12 @@
     int mVideoOnNesting;
     StopwatchTimer mVideoOnTimer;
 
-    boolean mFlashlightOn;
+    int mFlashlightOnNesting;
     StopwatchTimer mFlashlightOnTimer;
 
+    int mCameraOnNesting;
+    StopwatchTimer mCameraOnTimer;
+
     int mPhoneSignalStrengthBin = -1;
     int mPhoneSignalStrengthBinRaw = -1;
     final StopwatchTimer[] mPhoneSignalStrengthsTimer =
@@ -3710,29 +3715,99 @@
         getUidStatsLocked(uid).noteVibratorOffLocked();
     }
 
-    public void noteFlashlightOnLocked() {
-        if (!mFlashlightOn) {
-            final long elapsedRealtime = SystemClock.elapsedRealtime();
-            final long uptime = SystemClock.uptimeMillis();
+    public void noteFlashlightOnLocked(int uid) {
+        uid = mapUid(uid);
+        final long elapsedRealtime = SystemClock.elapsedRealtime();
+        final long uptime = SystemClock.uptimeMillis();
+        if (mFlashlightOnNesting++ == 0) {
             mHistoryCur.states2 |= HistoryItem.STATE2_FLASHLIGHT_FLAG;
             if (DEBUG_HISTORY) Slog.v(TAG, "Flashlight on to: "
-                    + Integer.toHexString(mHistoryCur.states));
+                    + Integer.toHexString(mHistoryCur.states2));
             addHistoryRecordLocked(elapsedRealtime, uptime);
-            mFlashlightOn = true;
             mFlashlightOnTimer.startRunningLocked(elapsedRealtime);
         }
+        getUidStatsLocked(uid).noteFlashlightTurnedOnLocked(elapsedRealtime);
+    }
+
+    public void noteFlashlightOffLocked(int uid) {
+        if (mFlashlightOnNesting == 0) {
+            return;
+        }
+        uid = mapUid(uid);
+        final long elapsedRealtime = SystemClock.elapsedRealtime();
+        final long uptime = SystemClock.uptimeMillis();
+        if (--mFlashlightOnNesting == 0) {
+            mHistoryCur.states2 &= ~HistoryItem.STATE2_FLASHLIGHT_FLAG;
+            if (DEBUG_HISTORY) Slog.v(TAG, "Flashlight off to: "
+                    + Integer.toHexString(mHistoryCur.states2));
+            addHistoryRecordLocked(elapsedRealtime, uptime);
+            mFlashlightOnTimer.stopRunningLocked(elapsedRealtime);
+        }
+        getUidStatsLocked(uid).noteFlashlightTurnedOffLocked(elapsedRealtime);
+    }
+
+    public void noteCameraOnLocked(int uid) {
+        uid = mapUid(uid);
+        final long elapsedRealtime = SystemClock.elapsedRealtime();
+        final long uptime = SystemClock.uptimeMillis();
+        if (mCameraOnNesting++ == 0) {
+            mHistoryCur.states2 |= HistoryItem.STATE2_CAMERA_FLAG;
+            if (DEBUG_HISTORY) Slog.v(TAG, "Camera on to: "
+                    + Integer.toHexString(mHistoryCur.states2));
+            addHistoryRecordLocked(elapsedRealtime, uptime);
+            mCameraOnTimer.startRunningLocked(elapsedRealtime);
+        }
+        getUidStatsLocked(uid).noteCameraTurnedOnLocked(elapsedRealtime);
+    }
+
+    public void noteCameraOffLocked(int uid) {
+        if (mCameraOnNesting == 0) {
+            return;
+        }
+        uid = mapUid(uid);
+        final long elapsedRealtime = SystemClock.elapsedRealtime();
+        final long uptime = SystemClock.uptimeMillis();
+        if (--mCameraOnNesting == 0) {
+            mHistoryCur.states2 &= ~HistoryItem.STATE2_CAMERA_FLAG;
+            if (DEBUG_HISTORY) Slog.v(TAG, "Camera off to: "
+                    + Integer.toHexString(mHistoryCur.states2));
+            addHistoryRecordLocked(elapsedRealtime, uptime);
+            mCameraOnTimer.stopRunningLocked(elapsedRealtime);
+        }
+        getUidStatsLocked(uid).noteCameraTurnedOffLocked(elapsedRealtime);
+    }
+
+    public void noteResetCameraLocked() {
+        if (mCameraOnNesting > 0) {
+            final long elapsedRealtime = SystemClock.elapsedRealtime();
+            final long uptime = SystemClock.uptimeMillis();
+            mCameraOnNesting = 0;
+            mHistoryCur.states2 &= ~HistoryItem.STATE2_CAMERA_FLAG;
+            if (DEBUG_HISTORY) Slog.v(TAG, "Camera off to: "
+                    + Integer.toHexString(mHistoryCur.states2));
+            addHistoryRecordLocked(elapsedRealtime, uptime);
+            mCameraOnTimer.stopAllRunningLocked(elapsedRealtime);
+            for (int i=0; i<mUidStats.size(); i++) {
+                BatteryStatsImpl.Uid uid = mUidStats.valueAt(i);
+                uid.noteResetCameraLocked(elapsedRealtime);
+            }
+        }
     }
 
-    public void noteFlashlightOffLocked() {
-        final long elapsedRealtime = SystemClock.elapsedRealtime();
-        final long uptime = SystemClock.uptimeMillis();
-        if (mFlashlightOn) {
+    public void noteResetFlashlightLocked() {
+        if (mFlashlightOnNesting > 0) {
+            final long elapsedRealtime = SystemClock.elapsedRealtime();
+            final long uptime = SystemClock.uptimeMillis();
+            mFlashlightOnNesting = 0;
             mHistoryCur.states2 &= ~HistoryItem.STATE2_FLASHLIGHT_FLAG;
             if (DEBUG_HISTORY) Slog.v(TAG, "Flashlight off to: "
-                    + Integer.toHexString(mHistoryCur.states));
+                    + Integer.toHexString(mHistoryCur.states2));
             addHistoryRecordLocked(elapsedRealtime, uptime);
-            mFlashlightOn = false;
-            mFlashlightOnTimer.stopRunningLocked(elapsedRealtime);
+            mFlashlightOnTimer.stopAllRunningLocked(elapsedRealtime);
+            for (int i=0; i<mUidStats.size(); i++) {
+                BatteryStatsImpl.Uid uid = mUidStats.valueAt(i);
+                uid.noteResetFlashlightLocked(elapsedRealtime);
+            }
         }
     }
 
@@ -4350,6 +4425,9 @@
 
         StopwatchTimer mAudioTurnedOnTimer;
         StopwatchTimer mVideoTurnedOnTimer;
+        StopwatchTimer mFlashlightTurnedOnTimer;
+        StopwatchTimer mCameraTurnedOnTimer;
+
 
         StopwatchTimer mForegroundActivityTimer;
 
@@ -4650,6 +4728,54 @@
             }
         }
 
+        public StopwatchTimer createFlashlightTurnedOnTimerLocked() {
+            if (mFlashlightTurnedOnTimer == null) {
+                mFlashlightTurnedOnTimer = new StopwatchTimer(Uid.this, FLASHLIGHT_TURNED_ON,
+                        mFlashlightTurnedOnTimers, mOnBatteryTimeBase);
+            }
+            return mFlashlightTurnedOnTimer;
+        }
+
+        public void noteFlashlightTurnedOnLocked(long elapsedRealtimeMs) {
+            createFlashlightTurnedOnTimerLocked().startRunningLocked(elapsedRealtimeMs);
+        }
+
+        public void noteFlashlightTurnedOffLocked(long elapsedRealtimeMs) {
+            if (mFlashlightTurnedOnTimer != null) {
+                mFlashlightTurnedOnTimer.stopRunningLocked(elapsedRealtimeMs);
+            }
+        }
+
+        public void noteResetFlashlightLocked(long elapsedRealtimeMs) {
+            if (mFlashlightTurnedOnTimer != null) {
+                mFlashlightTurnedOnTimer.stopAllRunningLocked(elapsedRealtimeMs);
+            }
+        }
+
+        public StopwatchTimer createCameraTurnedOnTimerLocked() {
+            if (mCameraTurnedOnTimer == null) {
+                mCameraTurnedOnTimer = new StopwatchTimer(Uid.this, CAMERA_TURNED_ON,
+                        mCameraTurnedOnTimers, mOnBatteryTimeBase);
+            }
+            return mCameraTurnedOnTimer;
+        }
+
+        public void noteCameraTurnedOnLocked(long elapsedRealtimeMs) {
+            createCameraTurnedOnTimerLocked().startRunningLocked(elapsedRealtimeMs);
+        }
+
+        public void noteCameraTurnedOffLocked(long elapsedRealtimeMs) {
+            if (mCameraTurnedOnTimer != null) {
+                mCameraTurnedOnTimer.stopRunningLocked(elapsedRealtimeMs);
+            }
+        }
+
+        public void noteResetCameraLocked(long elapsedRealtimeMs) {
+            if (mCameraTurnedOnTimer != null) {
+                mCameraTurnedOnTimer.stopAllRunningLocked(elapsedRealtimeMs);
+            }
+        }
+
         public StopwatchTimer createForegroundActivityTimerLocked() {
             if (mForegroundActivityTimer == null) {
                 mForegroundActivityTimer = new StopwatchTimer(
@@ -4762,19 +4888,23 @@
         }
 
         @Override
-        public long getAudioTurnedOnTime(long elapsedRealtimeUs, int which) {
-            if (mAudioTurnedOnTimer == null) {
-                return 0;
-            }
-            return mAudioTurnedOnTimer.getTotalTimeLocked(elapsedRealtimeUs, which);
+        public Timer getAudioTurnedOnTimer() {
+            return mAudioTurnedOnTimer;
         }
 
         @Override
-        public long getVideoTurnedOnTime(long elapsedRealtimeUs, int which) {
-            if (mVideoTurnedOnTimer == null) {
-                return 0;
-            }
-            return mVideoTurnedOnTimer.getTotalTimeLocked(elapsedRealtimeUs, which);
+        public Timer getVideoTurnedOnTimer() {
+            return mVideoTurnedOnTimer;
+        }
+
+        @Override
+        public Timer getFlashlightTurnedOnTimer() {
+            return mFlashlightTurnedOnTimer;
+        }
+
+        @Override
+        public Timer getCameraTurnedOnTimer() {
+            return mCameraTurnedOnTimer;
         }
 
         @Override
@@ -4994,6 +5124,12 @@
             if (mVideoTurnedOnTimer != null) {
                 active |= !mVideoTurnedOnTimer.reset(false);
             }
+            if (mFlashlightTurnedOnTimer != null) {
+                active |= !mFlashlightTurnedOnTimer.reset(false);
+            }
+            if (mCameraTurnedOnTimer != null) {
+                active |= !mCameraTurnedOnTimer.reset(false);
+            }
             if (mForegroundActivityTimer != null) {
                 active |= !mForegroundActivityTimer.reset(false);
             }
@@ -5155,6 +5291,14 @@
                     mVideoTurnedOnTimer.detach();
                     mVideoTurnedOnTimer = null;
                 }
+                if (mFlashlightTurnedOnTimer != null) {
+                    mFlashlightTurnedOnTimer.detach();
+                    mFlashlightTurnedOnTimer = null;
+                }
+                if (mCameraTurnedOnTimer != null) {
+                    mCameraTurnedOnTimer.detach();
+                    mCameraTurnedOnTimer = null;
+                }
                 if (mForegroundActivityTimer != null) {
                     mForegroundActivityTimer.detach();
                     mForegroundActivityTimer = null;
@@ -5291,6 +5435,18 @@
             } else {
                 out.writeInt(0);
             }
+            if (mFlashlightTurnedOnTimer != null) {
+                out.writeInt(1);
+                mFlashlightTurnedOnTimer.writeToParcel(out, elapsedRealtimeUs);
+            } else {
+                out.writeInt(0);
+            }
+            if (mCameraTurnedOnTimer != null) {
+                out.writeInt(1);
+                mCameraTurnedOnTimer.writeToParcel(out, elapsedRealtimeUs);
+            } else {
+                out.writeInt(0);
+            }
             if (mForegroundActivityTimer != null) {
                 out.writeInt(1);
                 mForegroundActivityTimer.writeToParcel(out, elapsedRealtimeUs);
@@ -5469,6 +5625,18 @@
                 mVideoTurnedOnTimer = null;
             }
             if (in.readInt() != 0) {
+                mFlashlightTurnedOnTimer = new StopwatchTimer(Uid.this, FLASHLIGHT_TURNED_ON,
+                        mFlashlightTurnedOnTimers, mOnBatteryTimeBase, in);
+            } else {
+                mFlashlightTurnedOnTimer = null;
+            }
+            if (in.readInt() != 0) {
+                mCameraTurnedOnTimer = new StopwatchTimer(Uid.this, CAMERA_TURNED_ON,
+                        mCameraTurnedOnTimers, mOnBatteryTimeBase, in);
+            } else {
+                mCameraTurnedOnTimer = null;
+            }
+            if (in.readInt() != 0) {
                 mForegroundActivityTimer = new StopwatchTimer(
                         Uid.this, FOREGROUND_ACTIVITY, null, mOnBatteryTimeBase, in);
             } else {
@@ -6700,6 +6868,7 @@
         mAudioOnTimer = new StopwatchTimer(null, -7, null, mOnBatteryTimeBase);
         mVideoOnTimer = new StopwatchTimer(null, -8, null, mOnBatteryTimeBase);
         mFlashlightOnTimer = new StopwatchTimer(null, -9, null, mOnBatteryTimeBase);
+        mCameraOnTimer = new StopwatchTimer(null, -13, null, mOnBatteryTimeBase);
         mOnBattery = mOnBatteryInternal = false;
         long uptime = SystemClock.uptimeMillis() * 1000;
         long realtime = SystemClock.elapsedRealtime() * 1000;
@@ -7285,6 +7454,7 @@
         mAudioOnTimer.reset(false);
         mVideoOnTimer.reset(false);
         mFlashlightOnTimer.reset(false);
+        mCameraOnTimer.reset(false);
         for (int i=0; i<SignalStrength.NUM_SIGNAL_STRENGTH_BINS; i++) {
             mPhoneSignalStrengthsTimer[i].reset(false);
         }
@@ -8811,8 +8981,10 @@
         }
 
         mNumConnectivityChange = mLoadedNumConnectivityChange = in.readInt();
-        mFlashlightOn = false;
+        mFlashlightOnNesting = 0;
         mFlashlightOnTimer.readSummaryFromParcelLocked(in);
+        mCameraOnNesting = 0;
+        mCameraOnTimer.readSummaryFromParcelLocked(in);
 
         int NKW = in.readInt();
         if (NKW > 10000) {
@@ -8883,6 +9055,12 @@
                 u.createVideoTurnedOnTimerLocked().readSummaryFromParcelLocked(in);
             }
             if (in.readInt() != 0) {
+                u.createFlashlightTurnedOnTimerLocked().readSummaryFromParcelLocked(in);
+            }
+            if (in.readInt() != 0) {
+                u.createCameraTurnedOnTimerLocked().readSummaryFromParcelLocked(in);
+            }
+            if (in.readInt() != 0) {
                 u.createForegroundActivityTimerLocked().readSummaryFromParcelLocked(in);
             }
             u.mProcessState = Uid.PROCESS_STATE_NONE;
@@ -9132,6 +9310,7 @@
         }
         out.writeInt(mNumConnectivityChange);
         mFlashlightOnTimer.writeSummaryFromParcelLocked(out, NOWREAL_SYS);
+        mCameraOnTimer.writeSummaryFromParcelLocked(out, NOWREAL_SYS);
 
         out.writeInt(mKernelWakelockStats.size());
         for (Map.Entry<String, SamplingTimer> ent : mKernelWakelockStats.entrySet()) {
@@ -9208,6 +9387,18 @@
             } else {
                 out.writeInt(0);
             }
+            if (u.mFlashlightTurnedOnTimer != null) {
+                out.writeInt(1);
+                u.mFlashlightTurnedOnTimer.writeSummaryFromParcelLocked(out, NOWREAL_SYS);
+            } else {
+                out.writeInt(0);
+            }
+            if (u.mCameraTurnedOnTimer != null) {
+                out.writeInt(1);
+                u.mCameraTurnedOnTimer.writeSummaryFromParcelLocked(out, NOWREAL_SYS);
+            } else {
+                out.writeInt(0);
+            }
             if (u.mForegroundActivityTimer != null) {
                 out.writeInt(1);
                 u.mForegroundActivityTimer.writeSummaryFromParcelLocked(out, NOWREAL_SYS);
@@ -9453,8 +9644,10 @@
         mAudioOnTimer = new StopwatchTimer(null, -7, null, mOnBatteryTimeBase);
         mVideoOnNesting = 0;
         mVideoOnTimer = new StopwatchTimer(null, -8, null, mOnBatteryTimeBase);
-        mFlashlightOn = false;
+        mFlashlightOnNesting = 0;
         mFlashlightOnTimer = new StopwatchTimer(null, -9, null, mOnBatteryTimeBase, in);
+        mCameraOnNesting = 0;
+        mCameraOnTimer = new StopwatchTimer(null, -13, null, mOnBatteryTimeBase, in);
         mDischargeUnplugLevel = in.readInt();
         mDischargePlugLevel = in.readInt();
         mDischargeCurrentLevel = in.readInt();
@@ -9499,6 +9692,8 @@
         mWifiMulticastTimers.clear();
         mAudioTurnedOnTimers.clear();
         mVideoTurnedOnTimers.clear();
+        mFlashlightTurnedOnTimers.clear();
+        mCameraTurnedOnTimers.clear();
 
         sNumSpeedSteps = in.readInt();
 
@@ -9598,6 +9793,7 @@
         out.writeInt(mLoadedNumConnectivityChange);
         out.writeInt(mUnpluggedNumConnectivityChange);
         mFlashlightOnTimer.writeToParcel(out, uSecRealtime);
+        mCameraOnTimer.writeToParcel(out, uSecRealtime);
         out.writeInt(mDischargeUnplugLevel);
         out.writeInt(mDischargePlugLevel);
         out.writeInt(mDischargeCurrentLevel);
@@ -9732,6 +9928,8 @@
             }
             pr.println("*** Flashlight timer:");
             mFlashlightOnTimer.logState(pr, "  ");
+            pr.println("*** Camera timer:");
+            mCameraOnTimer.logState(pr, "  ");
         }
         super.dumpLocked(context, pw, flags, reqUid, histStart);
     }