Track camera and flashlight usage in battery stats.

Bug: 15986092

Change-Id: I9dc6828332e4091fd93bf2d82839e8e3862a2fc2
diff --git a/include/media/stagefright/MediaCodec.h b/include/media/stagefright/MediaCodec.h
index 6e14fc5..d62b35d 100644
--- a/include/media/stagefright/MediaCodec.h
+++ b/include/media/stagefright/MediaCodec.h
@@ -60,8 +60,6 @@
         CB_RESOURCE_RECLAIMED = 5,
     };
 
-    struct BatteryNotifier;
-
     static sp<MediaCodec> CreateByType(
             const sp<ALooper> &looper, const char *mime, bool encoder, status_t *err = NULL);
 
diff --git a/media/libmediaplayerservice/Android.mk b/media/libmediaplayerservice/Android.mk
index 2c4e719..ce04505 100644
--- a/media/libmediaplayerservice/Android.mk
+++ b/media/libmediaplayerservice/Android.mk
@@ -33,6 +33,7 @@
     libdl                       \
     libgui                      \
     libmedia                    \
+    libmediautils               \
     libsonivox                  \
     libstagefright              \
     libstagefright_foundation   \
diff --git a/media/libmediaplayerservice/MediaPlayerService.cpp b/media/libmediaplayerservice/MediaPlayerService.cpp
index 891a9e9..3e2d967 100644
--- a/media/libmediaplayerservice/MediaPlayerService.cpp
+++ b/media/libmediaplayerservice/MediaPlayerService.cpp
@@ -34,7 +34,6 @@
 
 #include <utils/misc.h>
 
-#include <binder/IBatteryStats.h>
 #include <binder/IPCThreadState.h>
 #include <binder/IServiceManager.h>
 #include <binder/MemoryHeapBase.h>
@@ -60,6 +59,7 @@
 #include <media/stagefright/AudioPlayer.h>
 #include <media/stagefright/foundation/ADebug.h>
 #include <media/stagefright/foundation/ALooperRoster.h>
+#include <mediautils/BatteryNotifier.h>
 
 #include <system/audio.h>
 
@@ -287,17 +287,9 @@
     // reset battery stats
     // if the mediaserver has crashed, battery stats could be left
     // in bad state, reset the state upon service start.
-    const sp<IServiceManager> sm(defaultServiceManager());
-    if (sm != NULL) {
-        const String16 name("batterystats");
-        // use checkService() to avoid blocking if service is not up yet
-        sp<IBatteryStats> batteryStats =
-                interface_cast<IBatteryStats>(sm->checkService(name));
-        if (batteryStats != NULL) {
-            batteryStats->noteResetVideo();
-            batteryStats->noteResetAudio();
-        }
-    }
+    BatteryNotifier& notifier(BatteryNotifier::getInstance());
+    notifier.noteResetVideo();
+    notifier.noteResetAudio();
 
     MediaPlayerFactory::registerBuiltinFactories();
 }
diff --git a/media/libstagefright/Android.mk b/media/libstagefright/Android.mk
index 6010558..99f9d94 100644
--- a/media/libstagefright/Android.mk
+++ b/media/libstagefright/Android.mk
@@ -90,6 +90,7 @@
         libicuuc \
         liblog \
         libmedia \
+        libmediautils \
         libnetd_client \
         libopus \
         libsonivox \
diff --git a/media/libstagefright/MediaCodec.cpp b/media/libstagefright/MediaCodec.cpp
index 46c154d..c8dcef1 100644
--- a/media/libstagefright/MediaCodec.cpp
+++ b/media/libstagefright/MediaCodec.cpp
@@ -21,7 +21,6 @@
 #include "include/avc_utils.h"
 #include "include/SoftwareRenderer.h"
 
-#include <binder/IBatteryStats.h>
 #include <binder/IMemory.h>
 #include <binder/IPCThreadState.h>
 #include <binder/IServiceManager.h>
@@ -47,6 +46,7 @@
 #include <media/stagefright/OMXCodec.h>
 #include <media/stagefright/PersistentSurface.h>
 #include <media/stagefright/SurfaceUtils.h>
+#include <mediautils/BatteryNotifier.h>
 #include <private/android_filesystem_config.h>
 #include <utils/Log.h>
 #include <utils/Singleton.h>
@@ -107,134 +107,6 @@
     DISALLOW_EVIL_CONSTRUCTORS(ResourceManagerClient);
 };
 
-struct MediaCodec::BatteryNotifier : public Singleton<BatteryNotifier> {
-    BatteryNotifier();
-    virtual ~BatteryNotifier();
-
-    void noteStartVideo();
-    void noteStopVideo();
-    void noteStartAudio();
-    void noteStopAudio();
-    void onBatteryStatServiceDied();
-
-private:
-    struct DeathNotifier : public IBinder::DeathRecipient {
-        DeathNotifier() {}
-        virtual void binderDied(const wp<IBinder>& /*who*/) {
-            BatteryNotifier::getInstance().onBatteryStatServiceDied();
-        }
-    };
-
-    Mutex mLock;
-    int32_t mVideoRefCount;
-    int32_t mAudioRefCount;
-    sp<IBatteryStats> mBatteryStatService;
-    sp<DeathNotifier> mDeathNotifier;
-
-    sp<IBatteryStats> getBatteryService_l();
-
-    DISALLOW_EVIL_CONSTRUCTORS(BatteryNotifier);
-};
-
-ANDROID_SINGLETON_STATIC_INSTANCE(MediaCodec::BatteryNotifier)
-
-MediaCodec::BatteryNotifier::BatteryNotifier() :
-    mVideoRefCount(0),
-    mAudioRefCount(0) {
-}
-
-sp<IBatteryStats> MediaCodec::BatteryNotifier::getBatteryService_l() {
-    if (mBatteryStatService != NULL) {
-        return mBatteryStatService;
-    }
-    // get battery service from service manager
-    const sp<IServiceManager> sm(defaultServiceManager());
-    if (sm != NULL) {
-        const String16 name("batterystats");
-        mBatteryStatService =
-                interface_cast<IBatteryStats>(sm->getService(name));
-        if (mBatteryStatService == NULL) {
-            ALOGE("batterystats service unavailable!");
-            return NULL;
-        }
-        mDeathNotifier = new DeathNotifier();
-        IInterface::asBinder(mBatteryStatService)->linkToDeath(mDeathNotifier);
-        // notify start now if media already started
-        if (mVideoRefCount > 0) {
-            mBatteryStatService->noteStartVideo(AID_MEDIA);
-        }
-        if (mAudioRefCount > 0) {
-            mBatteryStatService->noteStartAudio(AID_MEDIA);
-        }
-    }
-    return mBatteryStatService;
-}
-
-MediaCodec::BatteryNotifier::~BatteryNotifier() {
-    if (mDeathNotifier != NULL) {
-        IInterface::asBinder(mBatteryStatService)->
-                unlinkToDeath(mDeathNotifier);
-    }
-}
-
-void MediaCodec::BatteryNotifier::noteStartVideo() {
-    Mutex::Autolock _l(mLock);
-    sp<IBatteryStats> batteryService = getBatteryService_l();
-    if (mVideoRefCount == 0 && batteryService != NULL) {
-        batteryService->noteStartVideo(AID_MEDIA);
-    }
-    mVideoRefCount++;
-}
-
-void MediaCodec::BatteryNotifier::noteStopVideo() {
-    Mutex::Autolock _l(mLock);
-    if (mVideoRefCount == 0) {
-        ALOGW("BatteryNotifier::noteStop(): video refcount is broken!");
-        return;
-    }
-
-    sp<IBatteryStats> batteryService = getBatteryService_l();
-
-    mVideoRefCount--;
-    if (mVideoRefCount == 0 && batteryService != NULL) {
-        batteryService->noteStopVideo(AID_MEDIA);
-    }
-}
-
-void MediaCodec::BatteryNotifier::noteStartAudio() {
-    Mutex::Autolock _l(mLock);
-    sp<IBatteryStats> batteryService = getBatteryService_l();
-    if (mAudioRefCount == 0 && batteryService != NULL) {
-        batteryService->noteStartAudio(AID_MEDIA);
-    }
-    mAudioRefCount++;
-}
-
-void MediaCodec::BatteryNotifier::noteStopAudio() {
-    Mutex::Autolock _l(mLock);
-    if (mAudioRefCount == 0) {
-        ALOGW("BatteryNotifier::noteStop(): audio refcount is broken!");
-        return;
-    }
-
-    sp<IBatteryStats> batteryService = getBatteryService_l();
-
-    mAudioRefCount--;
-    if (mAudioRefCount == 0 && batteryService != NULL) {
-        batteryService->noteStopAudio(AID_MEDIA);
-    }
-}
-
-void MediaCodec::BatteryNotifier::onBatteryStatServiceDied() {
-    Mutex::Autolock _l(mLock);
-    mBatteryStatService.clear();
-    mDeathNotifier.clear();
-    // Do not reset mVideoRefCount and mAudioRefCount here. The ref
-    // counting is independent of the battery service availability.
-    // We need this if battery service becomes available after media
-    // started.
-}
-
 MediaCodec::ResourceManagerServiceProxy::ResourceManagerServiceProxy() {
 }
 
diff --git a/media/utils/Android.mk b/media/utils/Android.mk
new file mode 100644
index 0000000..dfadbc80
--- /dev/null
+++ b/media/utils/Android.mk
@@ -0,0 +1,39 @@
+# Copyright 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.
+
+LOCAL_PATH := $(call my-dir)
+
+include $(CLEAR_VARS)
+
+LOCAL_SRC_FILES := \
+  BatteryNotifier.cpp \
+
+LOCAL_SHARED_LIBRARIES := \
+  libbinder \
+  libcutils \
+  liblog \
+  libutils \
+
+LOCAL_C_INCLUDES := $(LOCAL_PATH)/include
+
+LOCAL_CFLAGS += \
+  -Wall \
+  -Wextra \
+  -Werror \
+
+LOCAL_EXPORT_C_INCLUDE_DIRS := $(LOCAL_PATH)/include
+
+LOCAL_MODULE := libmediautils
+
+include $(BUILD_SHARED_LIBRARY)
diff --git a/media/utils/BatteryNotifier.cpp b/media/utils/BatteryNotifier.cpp
new file mode 100644
index 0000000..7f9cd7a
--- /dev/null
+++ b/media/utils/BatteryNotifier.cpp
@@ -0,0 +1,213 @@
+/*
+ * Copyright 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.
+ */
+
+#include "include/mediautils/BatteryNotifier.h"
+
+#include <binder/IServiceManager.h>
+#include <utils/Log.h>
+#include <private/android_filesystem_config.h>
+
+namespace android {
+
+void BatteryNotifier::DeathNotifier::binderDied(const wp<IBinder>& /*who*/) {
+    BatteryNotifier::getInstance().onBatteryStatServiceDied();
+}
+
+BatteryNotifier::BatteryNotifier() : mVideoRefCount(0), mAudioRefCount(0) {}
+
+BatteryNotifier::~BatteryNotifier() {
+    Mutex::Autolock _l(mLock);
+    if (mDeathNotifier != nullptr) {
+        IInterface::asBinder(mBatteryStatService)->unlinkToDeath(mDeathNotifier);
+    }
+}
+
+void BatteryNotifier::noteStartVideo() {
+    Mutex::Autolock _l(mLock);
+    sp<IBatteryStats> batteryService = getBatteryService_l();
+    if (mVideoRefCount == 0 && batteryService != nullptr) {
+        batteryService->noteStartVideo(AID_MEDIA);
+    }
+    mVideoRefCount++;
+}
+
+void BatteryNotifier::noteStopVideo() {
+    Mutex::Autolock _l(mLock);
+    if (mVideoRefCount == 0) {
+        ALOGW("%s: video refcount is broken.", __FUNCTION__);
+        return;
+    }
+
+    sp<IBatteryStats> batteryService = getBatteryService_l();
+
+    mVideoRefCount--;
+    if (mVideoRefCount == 0 && batteryService != nullptr) {
+        batteryService->noteStopVideo(AID_MEDIA);
+    }
+}
+
+void BatteryNotifier::noteResetVideo() {
+    Mutex::Autolock _l(mLock);
+    sp<IBatteryStats> batteryService = getBatteryService_l();
+    mVideoRefCount = 0;
+    if (batteryService != nullptr) {
+        batteryService->noteResetAudio();
+    }
+}
+
+void BatteryNotifier::noteStartAudio() {
+    Mutex::Autolock _l(mLock);
+    sp<IBatteryStats> batteryService = getBatteryService_l();
+    if (mAudioRefCount == 0 && batteryService != nullptr) {
+        batteryService->noteStartAudio(AID_MEDIA);
+    }
+    mAudioRefCount++;
+}
+
+void BatteryNotifier::noteStopAudio() {
+    Mutex::Autolock _l(mLock);
+    if (mAudioRefCount == 0) {
+        ALOGW("%s: audio refcount is broken.", __FUNCTION__);
+        return;
+    }
+
+    sp<IBatteryStats> batteryService = getBatteryService_l();
+
+    mAudioRefCount--;
+    if (mAudioRefCount == 0 && batteryService != nullptr) {
+        batteryService->noteStopAudio(AID_MEDIA);
+    }
+}
+
+void BatteryNotifier::noteResetAudio() {
+    Mutex::Autolock _l(mLock);
+    sp<IBatteryStats> batteryService = getBatteryService_l();
+    mAudioRefCount = 0;
+    if (batteryService != nullptr) {
+        batteryService->noteResetAudio();
+    }
+}
+
+void BatteryNotifier::noteFlashlightOn(const String8& id, int uid) {
+    Mutex::Autolock _l(mLock);
+    sp<IBatteryStats> batteryService = getBatteryService_l();
+
+    std::pair<String8, int> k = std::make_pair(id, uid);
+    if (!mFlashlightState[k]) {
+        mFlashlightState[k] = true;
+        if (batteryService != nullptr) {
+            batteryService->noteFlashlightOn(uid);
+        }
+    }
+}
+
+void BatteryNotifier::noteFlashlightOff(const String8& id, int uid) {
+    Mutex::Autolock _l(mLock);
+    sp<IBatteryStats> batteryService = getBatteryService_l();
+
+    std::pair<String8, int> k = std::make_pair(id, uid);
+    if (mFlashlightState[k]) {
+        mFlashlightState[k] = false;
+        if (batteryService != nullptr) {
+            batteryService->noteFlashlightOff(uid);
+        }
+    }
+}
+
+void BatteryNotifier::noteResetFlashlight() {
+    Mutex::Autolock _l(mLock);
+    sp<IBatteryStats> batteryService = getBatteryService_l();
+    mFlashlightState.clear();
+    if (batteryService != nullptr) {
+        batteryService->noteResetFlashlight();
+    }
+}
+
+void BatteryNotifier::noteStartCamera(const String8& id, int uid) {
+    Mutex::Autolock _l(mLock);
+    sp<IBatteryStats> batteryService = getBatteryService_l();
+    std::pair<String8, int> k = std::make_pair(id, uid);
+    if (!mCameraState[k]) {
+        mCameraState[k] = true;
+        if (batteryService != nullptr) {
+            batteryService->noteStartCamera(uid);
+        }
+    }
+}
+
+void BatteryNotifier::noteStopCamera(const String8& id, int uid) {
+    Mutex::Autolock _l(mLock);
+    sp<IBatteryStats> batteryService = getBatteryService_l();
+    std::pair<String8, int> k = std::make_pair(id, uid);
+    if (mCameraState[k]) {
+        mCameraState[k] = false;
+        if (batteryService != nullptr) {
+            batteryService->noteStopCamera(uid);
+        }
+    }
+}
+
+void BatteryNotifier::noteResetCamera() {
+    Mutex::Autolock _l(mLock);
+    sp<IBatteryStats> batteryService = getBatteryService_l();
+    mCameraState.clear();
+    if (batteryService != nullptr) {
+        batteryService->noteResetCamera();
+    }
+}
+
+void BatteryNotifier::onBatteryStatServiceDied() {
+    Mutex::Autolock _l(mLock);
+    mBatteryStatService.clear();
+    mDeathNotifier.clear();
+    // Do not reset mVideoRefCount and mAudioRefCount here. The ref
+    // counting is independent of the battery service availability.
+    // We need this if battery service becomes available after media
+    // started.
+
+}
+
+sp<IBatteryStats> BatteryNotifier::getBatteryService_l() {
+    if (mBatteryStatService != nullptr) {
+        return mBatteryStatService;
+    }
+    // Get battery service from service manager
+    const sp<IServiceManager> sm(defaultServiceManager());
+    if (sm != nullptr) {
+        const String16 name("batterystats");
+        mBatteryStatService = interface_cast<IBatteryStats>(sm->checkService(name));
+        if (mBatteryStatService == nullptr) {
+            ALOGE("batterystats service unavailable!");
+            return nullptr;
+        }
+
+        mDeathNotifier = new DeathNotifier();
+        IInterface::asBinder(mBatteryStatService)->linkToDeath(mDeathNotifier);
+
+        // Notify start now if media already started
+        if (mVideoRefCount > 0) {
+            mBatteryStatService->noteStartVideo(AID_MEDIA);
+        }
+        if (mAudioRefCount > 0) {
+            mBatteryStatService->noteStartAudio(AID_MEDIA);
+        }
+    }
+    return mBatteryStatService;
+}
+
+ANDROID_SINGLETON_STATIC_INSTANCE(BatteryNotifier);
+
+}  // namespace android
diff --git a/media/utils/README b/media/utils/README
new file mode 100644
index 0000000..65ab0b8
--- /dev/null
+++ b/media/utils/README
@@ -0,0 +1,4 @@
+This is a common shared library for media utility classes.
+
+Consider adding your utility class/function here if it will
+be used across several of the media libraries.
diff --git a/media/utils/include/mediautils/BatteryNotifier.h b/media/utils/include/mediautils/BatteryNotifier.h
new file mode 100644
index 0000000..49048042
--- /dev/null
+++ b/media/utils/include/mediautils/BatteryNotifier.h
@@ -0,0 +1,73 @@
+/*
+ * Copyright 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.
+ */
+
+#ifndef MEDIA_BATTERY_NOTIFIER_H
+#define MEDIA_BATTERY_NOTIFIER_H
+
+#include <binder/IBatteryStats.h>
+#include <utils/Singleton.h>
+#include <utils/String8.h>
+
+#include <map>
+#include <utility>
+
+namespace android {
+
+/**
+ * Class used for logging battery life events in mediaserver.
+ */
+class BatteryNotifier : public Singleton<BatteryNotifier> {
+
+    friend class Singleton<BatteryNotifier>;
+    BatteryNotifier();
+
+public:
+    ~BatteryNotifier();
+
+    void noteStartVideo();
+    void noteStopVideo();
+    void noteResetVideo();
+    void noteStartAudio();
+    void noteStopAudio();
+    void noteResetAudio();
+    void noteFlashlightOn(const String8& id, int uid);
+    void noteFlashlightOff(const String8& id, int uid);
+    void noteResetFlashlight();
+    void noteStartCamera(const String8& id, int uid);
+    void noteStopCamera(const String8& id, int uid);
+    void noteResetCamera();
+
+private:
+    void onBatteryStatServiceDied();
+
+    class DeathNotifier : public IBinder::DeathRecipient {
+        virtual void binderDied(const wp<IBinder>& /*who*/);
+    };
+
+    Mutex mLock;
+    int mVideoRefCount;
+    int mAudioRefCount;
+    std::map<std::pair<String8, int>, bool> mFlashlightState;
+    std::map<std::pair<String8, int>, bool> mCameraState;
+    sp<IBatteryStats> mBatteryStatService;
+    sp<DeathNotifier> mDeathNotifier;
+
+    sp<IBatteryStats> getBatteryService_l();
+};
+
+}  // namespace android
+
+#endif // MEDIA_BATTERY_NOTIFIER_H
diff --git a/services/camera/libcameraservice/Android.mk b/services/camera/libcameraservice/Android.mk
index 9c60911..cbead32 100644
--- a/services/camera/libcameraservice/Android.mk
+++ b/services/camera/libcameraservice/Android.mk
@@ -62,6 +62,7 @@
     libbinder \
     libcutils \
     libmedia \
+    libmediautils \
     libcamera_client \
     libgui \
     libhardware \
diff --git a/services/camera/libcameraservice/CameraService.cpp b/services/camera/libcameraservice/CameraService.cpp
index fc9a332..527e80b 100644
--- a/services/camera/libcameraservice/CameraService.cpp
+++ b/services/camera/libcameraservice/CameraService.cpp
@@ -40,6 +40,7 @@
 #include <media/AudioSystem.h>
 #include <media/IMediaHTTPService.h>
 #include <media/mediaplayer.h>
+#include <mediautils/BatteryNotifier.h>
 #include <utils/Errors.h>
 #include <utils/Log.h>
 #include <utils/String16.h>
@@ -139,6 +140,11 @@
 
     BnCameraService::onFirstRef();
 
+    // Update battery life tracking if service is restarting
+    BatteryNotifier& notifier(BatteryNotifier::getInstance());
+    notifier.noteResetCamera();
+    notifier.noteResetFlashlight();
+
     camera_module_t *rawModule;
     int err = hw_get_module(CAMERA_HARDWARE_MODULE_ID,
             (const hw_module_t **)&rawModule);
@@ -323,12 +329,39 @@
 
     res = setTorchStatusLocked(cameraId, newStatus);
     if (res) {
-        ALOGE("%s: Failed to set the torch status", __FUNCTION__,
-                (uint32_t)newStatus);
+        ALOGE("%s: Failed to set the torch status", __FUNCTION__, (uint32_t)newStatus);
         return;
     }
 
     {
+        // Update battery life logging for flashlight
+        Mutex::Autolock al(mTorchClientMapMutex);
+        auto iter = mTorchUidMap.find(cameraId);
+        if (iter != mTorchUidMap.end()) {
+            int oldUid = iter->second.second;
+            int newUid = iter->second.first;
+            BatteryNotifier& notifier(BatteryNotifier::getInstance());
+            if (oldUid != newUid) {
+                // If the UID has changed, log the status and update current UID in mTorchUidMap
+                if (status == ICameraServiceListener::TORCH_STATUS_AVAILABLE_ON) {
+                    notifier.noteFlashlightOff(cameraId, oldUid);
+                }
+                if (newStatus == ICameraServiceListener::TORCH_STATUS_AVAILABLE_ON) {
+                    notifier.noteFlashlightOn(cameraId, newUid);
+                }
+                iter->second.second = newUid;
+            } else {
+                // If the UID has not changed, log the status
+                if (newStatus == ICameraServiceListener::TORCH_STATUS_AVAILABLE_ON) {
+                    notifier.noteFlashlightOn(cameraId, oldUid);
+                } else {
+                    notifier.noteFlashlightOff(cameraId, oldUid);
+                }
+            }
+        }
+    }
+
+    {
         Mutex::Autolock lock(mStatusListenerLock);
         for (auto& i : mListenerList) {
             i->onTorchStatusChanged(newStatus, String16{cameraId});
@@ -1137,12 +1170,13 @@
 
 status_t CameraService::setTorchMode(const String16& cameraId, bool enabled,
         const sp<IBinder>& clientBinder) {
-    if (enabled && clientBinder == NULL) {
+    if (enabled && clientBinder == nullptr) {
         ALOGE("%s: torch client binder is NULL", __FUNCTION__);
         return -EINVAL;
     }
 
     String8 id = String8(cameraId.string());
+    int uid = getCallingUid();
 
     // verify id is valid.
     auto state = getCameraState(id);
@@ -1181,7 +1215,21 @@
         }
     }
 
+    {
+        // Update UID map - this is used in the torch status changed callbacks, so must be done
+        // before setTorchMode
+        Mutex::Autolock al(mTorchClientMapMutex);
+        if (mTorchUidMap.find(id) == mTorchUidMap.end()) {
+            mTorchUidMap[id].first = uid;
+            mTorchUidMap[id].second = uid;
+        } else {
+            // Set the pending UID
+            mTorchUidMap[id].first = uid;
+        }
+    }
+
     status_t res = mFlashlight->setTorchMode(id, enabled);
+
     if (res) {
         ALOGE("%s: setting torch mode of camera %s to %d failed. %s (%d)",
                 __FUNCTION__, id.string(), enabled, strerror(-res), res);
@@ -1192,19 +1240,17 @@
         // update the link to client's death
         Mutex::Autolock al(mTorchClientMapMutex);
         ssize_t index = mTorchClientMap.indexOfKey(id);
+        BatteryNotifier& notifier(BatteryNotifier::getInstance());
         if (enabled) {
             if (index == NAME_NOT_FOUND) {
                 mTorchClientMap.add(id, clientBinder);
             } else {
-                const sp<IBinder> oldBinder = mTorchClientMap.valueAt(index);
-                oldBinder->unlinkToDeath(this);
-
+                mTorchClientMap.valueAt(index)->unlinkToDeath(this);
                 mTorchClientMap.replaceValueAt(index, clientBinder);
             }
             clientBinder->linkToDeath(this);
         } else if (index != NAME_NOT_FOUND) {
-            sp<IBinder> oldBinder = mTorchClientMap.valueAt(index);
-            oldBinder->unlinkToDeath(this);
+            mTorchClientMap.valueAt(index)->unlinkToDeath(this);
         }
     }
 
@@ -1226,8 +1272,7 @@
     }
 }
 
-status_t CameraService::addListener(
-                                const sp<ICameraServiceListener>& listener) {
+status_t CameraService::addListener(const sp<ICameraServiceListener>& listener) {
     ALOGV("%s: Add listener %p", __FUNCTION__, listener.get());
 
     if (listener == 0) {
@@ -1948,9 +1993,40 @@
 }
 
 // ----------------------------------------------------------------------------
+//                  ClientEventListener
+// ----------------------------------------------------------------------------
+
+void CameraService::ClientEventListener::onClientAdded(
+        const resource_policy::ClientDescriptor<String8,
+        sp<CameraService::BasicClient>>& descriptor) {
+    auto basicClient = descriptor.getValue();
+    if (basicClient.get() != nullptr) {
+        BatteryNotifier& notifier(BatteryNotifier::getInstance());
+        notifier.noteStartCamera(descriptor.getKey(),
+                static_cast<int>(basicClient->getClientUid()));
+    }
+}
+
+void CameraService::ClientEventListener::onClientRemoved(
+        const resource_policy::ClientDescriptor<String8,
+        sp<CameraService::BasicClient>>& descriptor) {
+    auto basicClient = descriptor.getValue();
+    if (basicClient.get() != nullptr) {
+        BatteryNotifier& notifier(BatteryNotifier::getInstance());
+        notifier.noteStopCamera(descriptor.getKey(),
+                static_cast<int>(basicClient->getClientUid()));
+    }
+}
+
+
+// ----------------------------------------------------------------------------
 //                  CameraClientManager
 // ----------------------------------------------------------------------------
 
+CameraService::CameraClientManager::CameraClientManager() {
+    setListener(std::make_shared<ClientEventListener>());
+}
+
 CameraService::CameraClientManager::~CameraClientManager() {}
 
 sp<CameraService::BasicClient> CameraService::CameraClientManager::getCameraClient(
diff --git a/services/camera/libcameraservice/CameraService.h b/services/camera/libcameraservice/CameraService.h
index 9b7163a..2e4743f 100644
--- a/services/camera/libcameraservice/CameraService.h
+++ b/services/camera/libcameraservice/CameraService.h
@@ -46,6 +46,7 @@
 #include <string>
 #include <map>
 #include <memory>
+#include <utility>
 
 namespace android {
 
@@ -327,6 +328,20 @@
 
     }; // class Client
 
+    /**
+     * A listener class that implements the LISTENER interface for use with a ClientManager, and
+     * implements the following methods:
+     *    void onClientRemoved(const ClientDescriptor<KEY, VALUE>& descriptor);
+     *    void onClientAdded(const ClientDescriptor<KEY, VALUE>& descriptor);
+     */
+    class ClientEventListener {
+    public:
+        void onClientAdded(const resource_policy::ClientDescriptor<String8,
+                sp<CameraService::BasicClient>>& descriptor);
+        void onClientRemoved(const resource_policy::ClientDescriptor<String8,
+                sp<CameraService::BasicClient>>& descriptor);
+    }; // class ClientEventListener
+
     typedef std::shared_ptr<resource_policy::ClientDescriptor<String8,
             sp<CameraService::BasicClient>>> DescriptorPtr;
 
@@ -338,9 +353,10 @@
      * This class manages the eviction behavior for the camera clients.  See the parent class
      * implementation in utils/ClientManager for the specifics of this behavior.
      */
-    class CameraClientManager :
-            public resource_policy::ClientManager<String8, sp<CameraService::BasicClient>> {
+    class CameraClientManager : public resource_policy::ClientManager<String8,
+            sp<CameraService::BasicClient>, ClientEventListener> {
     public:
+        CameraClientManager();
         virtual ~CameraClientManager();
 
         /**
@@ -624,13 +640,15 @@
     sp<CameraFlashlight> mFlashlight;
     // guard mTorchStatusMap
     Mutex                mTorchStatusMutex;
-    // guard mTorchClientMap
+    // guard mTorchClientMap, mTorchUidMap
     Mutex                mTorchClientMapMutex;
     // camera id -> torch status
     KeyedVector<String8, ICameraServiceListener::TorchStatus> mTorchStatusMap;
     // camera id -> torch client binder
     // only store the last client that turns on each camera's torch mode
-    KeyedVector<String8, sp<IBinder> > mTorchClientMap;
+    KeyedVector<String8, sp<IBinder>> mTorchClientMap;
+    // camera id -> [incoming uid, current uid] pair
+    std::map<String8, std::pair<int, int>> mTorchUidMap;
 
     // check and handle if torch client's process has died
     void handleTorchClientBinderDied(const wp<IBinder> &who);
diff --git a/services/camera/libcameraservice/utils/ClientManager.h b/services/camera/libcameraservice/utils/ClientManager.h
index 5afb7a3..7ae58d5 100644
--- a/services/camera/libcameraservice/utils/ClientManager.h
+++ b/services/camera/libcameraservice/utils/ClientManager.h
@@ -172,6 +172,26 @@
 // --------------------------------------------------------------------------------
 
 /**
+ * A default class implementing the LISTENER interface used by ClientManager.
+ */
+template<class KEY, class VALUE>
+class DefaultEventListener {
+public:
+    void onClientAdded(const ClientDescriptor<KEY, VALUE>& descriptor);
+    void onClientRemoved(const ClientDescriptor<KEY, VALUE>& descriptor);
+};
+
+template<class KEY, class VALUE>
+void DefaultEventListener<KEY, VALUE>::onClientAdded(
+        const ClientDescriptor<KEY, VALUE>& /*descriptor*/) {}
+
+template<class KEY, class VALUE>
+void DefaultEventListener<KEY, VALUE>::onClientRemoved(
+        const ClientDescriptor<KEY, VALUE>& /*descriptor*/) {}
+
+// --------------------------------------------------------------------------------
+
+/**
  * The ClientManager class wraps an LRU-ordered list of active clients and implements eviction
  * behavior for handling shared resource access.
  *
@@ -189,7 +209,7 @@
  *     incoming descriptor has the highest priority.  Otherwise, the incoming descriptor is
  *     removed instead.
  */
-template<class KEY, class VALUE>
+template<class KEY, class VALUE, class LISTENER=DefaultEventListener<KEY, VALUE>>
 class ClientManager {
 public:
     // The default maximum "cost" allowed before evicting
@@ -275,6 +295,24 @@
     status_t waitUntilRemoved(const std::shared_ptr<ClientDescriptor<KEY, VALUE>> client,
             nsecs_t timeout) const;
 
+    /**
+     * Set the current listener for client add/remove events.
+     *
+     * The listener instance must inherit from the LISTENER class and implement the following
+     * methods:
+     *    void onClientRemoved(const ClientDescriptor<KEY, VALUE>& descriptor);
+     *    void onClientAdded(const ClientDescriptor<KEY, VALUE>& descriptor);
+     *
+     * These callback methods will be called with the ClientManager's lock held, and should
+     * not call any further ClientManager methods.
+     *
+     * The onClientRemoved method will be called when the client has been removed or evicted
+     * from the ClientManager that this event listener has been added to. The onClientAdded
+     * method will be called when the client has been added to the ClientManager that this
+     * event listener has been added to.
+     */
+    void setListener(const std::shared_ptr<LISTENER>& listener);
+
 protected:
     ~ClientManager();
 
@@ -300,36 +338,38 @@
     int32_t mMaxCost;
     // LRU ordered, most recent at end
     std::vector<std::shared_ptr<ClientDescriptor<KEY, VALUE>>> mClients;
+    std::shared_ptr<LISTENER> mListener;
 }; // class ClientManager
 
-template<class KEY, class VALUE>
-ClientManager<KEY, VALUE>::ClientManager() :
+template<class KEY, class VALUE, class LISTENER>
+ClientManager<KEY, VALUE, LISTENER>::ClientManager() :
         ClientManager(DEFAULT_MAX_COST) {}
 
-template<class KEY, class VALUE>
-ClientManager<KEY, VALUE>::ClientManager(int32_t totalCost) : mMaxCost(totalCost) {}
+template<class KEY, class VALUE, class LISTENER>
+ClientManager<KEY, VALUE, LISTENER>::ClientManager(int32_t totalCost) : mMaxCost(totalCost) {}
 
-template<class KEY, class VALUE>
-ClientManager<KEY, VALUE>::~ClientManager() {}
+template<class KEY, class VALUE, class LISTENER>
+ClientManager<KEY, VALUE, LISTENER>::~ClientManager() {}
 
-template<class KEY, class VALUE>
-std::vector<std::shared_ptr<ClientDescriptor<KEY, VALUE>>> ClientManager<KEY, VALUE>::wouldEvict(
+template<class KEY, class VALUE, class LISTENER>
+std::vector<std::shared_ptr<ClientDescriptor<KEY, VALUE>>>
+ClientManager<KEY, VALUE, LISTENER>::wouldEvict(
         const std::shared_ptr<ClientDescriptor<KEY, VALUE>>& client) const {
     Mutex::Autolock lock(mLock);
     return wouldEvictLocked(client);
 }
 
-template<class KEY, class VALUE>
+template<class KEY, class VALUE, class LISTENER>
 std::vector<std::shared_ptr<ClientDescriptor<KEY, VALUE>>>
-ClientManager<KEY, VALUE>::getIncompatibleClients(
+ClientManager<KEY, VALUE, LISTENER>::getIncompatibleClients(
         const std::shared_ptr<ClientDescriptor<KEY, VALUE>>& client) const {
     Mutex::Autolock lock(mLock);
     return wouldEvictLocked(client, /*returnIncompatibleClients*/true);
 }
 
-template<class KEY, class VALUE>
+template<class KEY, class VALUE, class LISTENER>
 std::vector<std::shared_ptr<ClientDescriptor<KEY, VALUE>>>
-ClientManager<KEY, VALUE>::wouldEvictLocked(
+ClientManager<KEY, VALUE, LISTENER>::wouldEvictLocked(
         const std::shared_ptr<ClientDescriptor<KEY, VALUE>>& client,
         bool returnIncompatibleClients) const {
 
@@ -420,8 +460,9 @@
 
 }
 
-template<class KEY, class VALUE>
-std::vector<std::shared_ptr<ClientDescriptor<KEY, VALUE>>> ClientManager<KEY, VALUE>::addAndEvict(
+template<class KEY, class VALUE, class LISTENER>
+std::vector<std::shared_ptr<ClientDescriptor<KEY, VALUE>>>
+ClientManager<KEY, VALUE, LISTENER>::addAndEvict(
         const std::shared_ptr<ClientDescriptor<KEY, VALUE>>& client) {
     Mutex::Autolock lock(mLock);
     auto evicted = wouldEvictLocked(client);
@@ -433,6 +474,9 @@
     auto iter = evicted.cbegin();
 
     if (iter != evicted.cend()) {
+
+        if (mListener != nullptr) mListener->onClientRemoved(**iter);
+
         // Remove evicted clients from list
         mClients.erase(std::remove_if(mClients.begin(), mClients.end(),
             [&iter] (std::shared_ptr<ClientDescriptor<KEY, VALUE>>& curClientPtr) {
@@ -444,21 +488,22 @@
             }), mClients.end());
     }
 
+    if (mListener != nullptr) mListener->onClientAdded(*client);
     mClients.push_back(client);
     mRemovedCondition.broadcast();
 
     return evicted;
 }
 
-template<class KEY, class VALUE>
+template<class KEY, class VALUE, class LISTENER>
 std::vector<std::shared_ptr<ClientDescriptor<KEY, VALUE>>>
-ClientManager<KEY, VALUE>::getAll() const {
+ClientManager<KEY, VALUE, LISTENER>::getAll() const {
     Mutex::Autolock lock(mLock);
     return mClients;
 }
 
-template<class KEY, class VALUE>
-std::vector<KEY> ClientManager<KEY, VALUE>::getAllKeys() const {
+template<class KEY, class VALUE, class LISTENER>
+std::vector<KEY> ClientManager<KEY, VALUE, LISTENER>::getAllKeys() const {
     Mutex::Autolock lock(mLock);
     std::vector<KEY> keys(mClients.size());
     for (const auto& i : mClients) {
@@ -467,8 +512,8 @@
     return keys;
 }
 
-template<class KEY, class VALUE>
-std::vector<int32_t> ClientManager<KEY, VALUE>::getAllOwners() const {
+template<class KEY, class VALUE, class LISTENER>
+std::vector<int32_t> ClientManager<KEY, VALUE, LISTENER>::getAllOwners() const {
     Mutex::Autolock lock(mLock);
     std::set<int32_t> owners;
     for (const auto& i : mClients) {
@@ -477,8 +522,8 @@
     return std::vector<int32_t>(owners.begin(), owners.end());
 }
 
-template<class KEY, class VALUE>
-void ClientManager<KEY, VALUE>::updatePriorities(
+template<class KEY, class VALUE, class LISTENER>
+void ClientManager<KEY, VALUE, LISTENER>::updatePriorities(
         const std::map<int32_t,int32_t>& ownerPriorityList) {
     Mutex::Autolock lock(mLock);
     for (auto& i : mClients) {
@@ -489,8 +534,8 @@
     }
 }
 
-template<class KEY, class VALUE>
-std::shared_ptr<ClientDescriptor<KEY, VALUE>> ClientManager<KEY, VALUE>::get(
+template<class KEY, class VALUE, class LISTENER>
+std::shared_ptr<ClientDescriptor<KEY, VALUE>> ClientManager<KEY, VALUE, LISTENER>::get(
         const KEY& key) const {
     Mutex::Autolock lock(mLock);
     for (const auto& i : mClients) {
@@ -499,23 +544,30 @@
     return std::shared_ptr<ClientDescriptor<KEY, VALUE>>(nullptr);
 }
 
-template<class KEY, class VALUE>
-void ClientManager<KEY, VALUE>::removeAll() {
+template<class KEY, class VALUE, class LISTENER>
+void ClientManager<KEY, VALUE, LISTENER>::removeAll() {
     Mutex::Autolock lock(mLock);
+    if (mListener != nullptr) {
+        for (const auto& i : mClients) {
+            mListener->onClientRemoved(*i);
+        }
+    }
     mClients.clear();
     mRemovedCondition.broadcast();
 }
 
-template<class KEY, class VALUE>
-std::shared_ptr<ClientDescriptor<KEY, VALUE>> ClientManager<KEY, VALUE>::remove(const KEY& key) {
+template<class KEY, class VALUE, class LISTENER>
+std::shared_ptr<ClientDescriptor<KEY, VALUE>> ClientManager<KEY, VALUE, LISTENER>::remove(
+    const KEY& key) {
     Mutex::Autolock lock(mLock);
 
     std::shared_ptr<ClientDescriptor<KEY, VALUE>> ret;
 
     // Remove evicted clients from list
     mClients.erase(std::remove_if(mClients.begin(), mClients.end(),
-        [&key, &ret] (std::shared_ptr<ClientDescriptor<KEY, VALUE>>& curClientPtr) {
+        [this, &key, &ret] (std::shared_ptr<ClientDescriptor<KEY, VALUE>>& curClientPtr) {
             if (curClientPtr->getKey() == key) {
+                if (mListener != nullptr) mListener->onClientRemoved(*curClientPtr);
                 ret = curClientPtr;
                 return true;
             }
@@ -526,8 +578,8 @@
     return ret;
 }
 
-template<class KEY, class VALUE>
-status_t ClientManager<KEY, VALUE>::waitUntilRemoved(
+template<class KEY, class VALUE, class LISTENER>
+status_t ClientManager<KEY, VALUE, LISTENER>::waitUntilRemoved(
         const std::shared_ptr<ClientDescriptor<KEY, VALUE>> client,
         nsecs_t timeout) const {
     status_t ret = NO_ERROR;
@@ -558,14 +610,21 @@
     return ret;
 }
 
-template<class KEY, class VALUE>
-void ClientManager<KEY, VALUE>::remove(
+template<class KEY, class VALUE, class LISTENER>
+void ClientManager<KEY, VALUE, LISTENER>::setListener(const std::shared_ptr<LISTENER>& listener) {
+    Mutex::Autolock lock(mLock);
+    mListener = listener;
+}
+
+template<class KEY, class VALUE, class LISTENER>
+void ClientManager<KEY, VALUE, LISTENER>::remove(
         const std::shared_ptr<ClientDescriptor<KEY, VALUE>>& value) {
     Mutex::Autolock lock(mLock);
     // Remove evicted clients from list
     mClients.erase(std::remove_if(mClients.begin(), mClients.end(),
-        [&value] (std::shared_ptr<ClientDescriptor<KEY, VALUE>>& curClientPtr) {
+        [this, &value] (std::shared_ptr<ClientDescriptor<KEY, VALUE>>& curClientPtr) {
             if (curClientPtr == value) {
+                if (mListener != nullptr) mListener->onClientRemoved(*curClientPtr);
                 return true;
             }
             return false;
@@ -573,8 +632,8 @@
     mRemovedCondition.broadcast();
 }
 
-template<class KEY, class VALUE>
-int64_t ClientManager<KEY, VALUE>::getCurrentCostLocked() const {
+template<class KEY, class VALUE, class LISTENER>
+int64_t ClientManager<KEY, VALUE, LISTENER>::getCurrentCostLocked() const {
     int64_t totalCost = 0;
     for (const auto& x : mClients) {
             totalCost += x->getCost();