camera: fix flashlight implementation for HAL v2

Update torch availability when the camera device availability changes.

For device HAL v2 and v3 implementation, notify torch unavailable for
all camera devices with a flash unit when a camera device is opened.
Notify torch available for all camera devices with flash unit when
all camera devices are closed.

Don't invoke torch status callback in camera service. Invoke torch
status callback in HAL or FlashControlBase implementations to avoid
race condition.

Clean up previous CL.

Bug: 2682206
Change-Id: I24f5478f467b2c680565fe98f112eef33e2547a1
diff --git a/services/camera/libcameraservice/CameraFlashlight.cpp b/services/camera/libcameraservice/CameraFlashlight.cpp
index 00a70eb..a00a49f 100644
--- a/services/camera/libcameraservice/CameraFlashlight.cpp
+++ b/services/camera/libcameraservice/CameraFlashlight.cpp
@@ -16,7 +16,7 @@
 
 #define LOG_TAG "CameraFlashlight"
 #define ATRACE_TAG ATRACE_TAG_CAMERA
-#define LOG_NDEBUG 0
+// #define LOG_NDEBUG 0
 
 #include <utils/Log.h>
 #include <utils/Trace.h>
@@ -32,16 +32,21 @@
 
 namespace android {
 
+/////////////////////////////////////////////////////////////////////
+// CameraFlashlight implementation begins
+// used by camera service to control flashflight.
+/////////////////////////////////////////////////////////////////////
 CameraFlashlight::CameraFlashlight(CameraModule& cameraModule,
         const camera_module_callbacks_t& callbacks) :
         mCameraModule(&cameraModule),
-        mCallbacks(&callbacks) {
+        mCallbacks(&callbacks),
+        mFlashlightMapInitialized(false) {
 }
 
 CameraFlashlight::~CameraFlashlight() {
 }
 
-status_t CameraFlashlight::createFlashlightControl(const String16& cameraId) {
+status_t CameraFlashlight::createFlashlightControl(const String8& cameraId) {
     ALOGV("%s: creating a flash light control for camera %s", __FUNCTION__,
             cameraId.string());
     if (mFlashControl != NULL) {
@@ -52,7 +57,7 @@
 
     if (mCameraModule->getRawModule()->module_api_version >=
             CAMERA_MODULE_API_VERSION_2_4) {
-        mFlashControl = new FlashControl(*mCameraModule, *mCallbacks);
+        mFlashControl = new ModuleFlashControl(*mCameraModule, *mCallbacks);
         if (mFlashControl == NULL) {
             ALOGV("%s: cannot create flash control for module api v2.4+",
                      __FUNCTION__);
@@ -67,7 +72,7 @@
             res = mCameraModule->getCameraInfo(
                     atoi(String8(cameraId).string()), &info);
             if (res) {
-                ALOGV("%s: failed to get camera info for camera %s",
+                ALOGE("%s: failed to get camera info for camera %s",
                         __FUNCTION__, cameraId.string());
                 return res;
             }
@@ -83,8 +88,7 @@
             }
 
             mFlashControl = flashControl;
-        }
-        else {
+        } else {
             // todo: implement for device api 1
             return INVALID_OPERATION;
         }
@@ -93,8 +97,9 @@
     return OK;
 }
 
-status_t CameraFlashlight::setTorchMode(const String16& cameraId, bool enabled) {
-    if (!mCameraModule) {
+status_t CameraFlashlight::setTorchMode(const String8& cameraId, bool enabled) {
+    if (!mFlashlightMapInitialized) {
+        ALOGE("%s: findFlashUnits() must be called before this method.");
         return NO_INIT;
     }
 
@@ -105,6 +110,10 @@
     Mutex::Autolock l(mLock);
 
     if (mFlashControl == NULL) {
+        if (enabled == false) {
+            return OK;
+        }
+
         res = createFlashlightControl(cameraId);
         if (res) {
             return res;
@@ -130,88 +139,169 @@
     return res;
 }
 
-bool CameraFlashlight::hasFlashUnit(const String16& cameraId) {
+status_t CameraFlashlight::findFlashUnits() {
+    Mutex::Autolock l(mLock);
+    status_t res;
+    int32_t numCameras = mCameraModule->getNumberOfCameras();
+
+    mHasFlashlightMap.clear();
+    mFlashlightMapInitialized = false;
+
+    for (int32_t i = 0; i < numCameras; i++) {
+        bool hasFlash = false;
+        String8 id = String8::format("%d", i);
+
+        res = createFlashlightControl(id);
+        if (res) {
+            ALOGE("%s: failed to create flash control for %s", __FUNCTION__,
+                    id.string());
+        } else {
+            res = mFlashControl->hasFlashUnit(id, &hasFlash);
+            if (res == -EUSERS || res == -EBUSY) {
+                ALOGE("%s: failed to check if camera %s has a flash unit. Some "
+                        "camera devices may be opened", __FUNCTION__,
+                        id.string());
+                return res;
+            } else if (res) {
+                ALOGE("%s: failed to check if camera %s has a flash unit. %s"
+                        " (%d)", __FUNCTION__, id.string(), strerror(-res),
+                        res);
+            }
+
+            mFlashControl.clear();
+        }
+        mHasFlashlightMap.add(id, hasFlash);
+    }
+
+    mFlashlightMapInitialized = true;
+    return OK;
+}
+
+bool CameraFlashlight::hasFlashUnit(const String8& cameraId) {
     status_t res;
 
     Mutex::Autolock l(mLock);
-
-    if (mFlashControl == NULL) {
-        res = createFlashlightControl(cameraId);
-        if (res) {
-            ALOGE("%s: failed to create flash control for %s ",
-                    __FUNCTION__, cameraId.string());
-            return false;
-        }
-    }
-
-    bool flashUnit = false;
-
-    // if flash control already exists, querying if a camera device has a flash
-    // unit may fail if it's module v1
-    res = mFlashControl->hasFlashUnit(cameraId, &flashUnit);
-    if (res == BAD_INDEX) {
-        // need to close the flash control before query.
-        mFlashControl.clear();
-        res = createFlashlightControl(cameraId);
-        if (res) {
-            ALOGE("%s: failed to create flash control for %s ", __FUNCTION__,
-                    cameraId.string());
-            return false;
-        }
-        res = mFlashControl->hasFlashUnit(cameraId, &flashUnit);
-        if (res) {
-            flashUnit = false;
-        }
-    }
-
-    return flashUnit;
+    return hasFlashUnitLocked(cameraId);
 }
 
-status_t CameraFlashlight::prepareDeviceOpen() {
+bool CameraFlashlight::hasFlashUnitLocked(const String8& cameraId) {
+    if (!mFlashlightMapInitialized) {
+        ALOGE("%s: findFlashUnits() must be called before this method.");
+        return false;
+    }
+
+    ssize_t index = mHasFlashlightMap.indexOfKey(cameraId);
+    if (index == NAME_NOT_FOUND) {
+        ALOGE("%s: camera %s not present when findFlashUnits() was called",
+                __FUNCTION__, cameraId.string());
+        return false;
+    }
+
+    return mHasFlashlightMap.valueAt(index);
+}
+
+status_t CameraFlashlight::prepareDeviceOpen(const String8& cameraId) {
     ALOGV("%s: prepare for device open", __FUNCTION__);
 
     Mutex::Autolock l(mLock);
+    if (!mFlashlightMapInitialized) {
+        ALOGE("%s: findFlashUnits() must be called before this method.");
+        return NO_INIT;
+    }
 
-    if (mCameraModule && mCameraModule->getRawModule()->module_api_version <
+    if (mCameraModule->getRawModule()->module_api_version <
             CAMERA_MODULE_API_VERSION_2_4) {
         // framework is going to open a camera device, all flash light control
         // should be closed for backward compatible support.
-        if (mFlashControl != NULL) {
-            mFlashControl.clear();
+        mFlashControl.clear();
+
+        if (mOpenedCameraIds.size() == 0) {
+            // notify torch unavailable for all cameras with a flash
+            int numCameras = mCameraModule->getNumberOfCameras();
+            for (int i = 0; i < numCameras; i++) {
+                if (hasFlashUnitLocked(String8::format("%d", i))) {
+                    mCallbacks->torch_mode_status_change(mCallbacks,
+                            String8::format("%d", i).string(),
+                            TORCH_MODE_STATUS_NOT_AVAILABLE);
+                }
+            }
         }
+
+        // close flash control that may be opened by calling hasFlashUnitLocked.
+        mFlashControl.clear();
+    }
+
+    if (mOpenedCameraIds.indexOf(cameraId) == NAME_NOT_FOUND) {
+        mOpenedCameraIds.add(cameraId);
     }
 
     return OK;
 }
 
+status_t CameraFlashlight::deviceClosed(const String8& cameraId) {
+    ALOGV("%s: device %s is closed", __FUNCTION__, cameraId.string());
+
+    Mutex::Autolock l(mLock);
+    if (!mFlashlightMapInitialized) {
+        ALOGE("%s: findFlashUnits() must be called before this method.");
+        return NO_INIT;
+    }
+
+    ssize_t index = mOpenedCameraIds.indexOf(cameraId);
+    if (index == NAME_NOT_FOUND) {
+        ALOGE("%s: couldn't find camera %s in the opened list", __FUNCTION__,
+                cameraId.string());
+    } else {
+        mOpenedCameraIds.removeAt(index);
+    }
+
+    // Cannot do anything until all cameras are closed.
+    if (mOpenedCameraIds.size() != 0)
+        return OK;
+
+    if (mCameraModule->getRawModule()->module_api_version <
+            CAMERA_MODULE_API_VERSION_2_4) {
+        // notify torch available for all cameras with a flash
+        int numCameras = mCameraModule->getNumberOfCameras();
+        for (int i = 0; i < numCameras; i++) {
+            if (hasFlashUnitLocked(String8::format("%d", i))) {
+                mCallbacks->torch_mode_status_change(mCallbacks,
+                        String8::format("%d", i).string(),
+                        TORCH_MODE_STATUS_AVAILABLE_OFF);
+            }
+        }
+    }
+
+    return OK;
+}
+// CameraFlashlight implementation ends
+
 
 FlashControlBase::~FlashControlBase() {
 }
 
-
-FlashControl::FlashControl(CameraModule& cameraModule,
+/////////////////////////////////////////////////////////////////////
+// ModuleFlashControl implementation begins
+// Flash control for camera module v2.4 and above.
+/////////////////////////////////////////////////////////////////////
+ModuleFlashControl::ModuleFlashControl(CameraModule& cameraModule,
         const camera_module_callbacks_t& callbacks) :
     mCameraModule(&cameraModule) {
 }
 
-FlashControl::~FlashControl() {
+ModuleFlashControl::~ModuleFlashControl() {
 }
 
-status_t FlashControl::hasFlashUnit(const String16& cameraId, bool *hasFlash) {
+status_t ModuleFlashControl::hasFlashUnit(const String8& cameraId, bool *hasFlash) {
     if (!hasFlash) {
         return BAD_VALUE;
     }
 
     *hasFlash = false;
-
     Mutex::Autolock l(mLock);
 
-    if (!mCameraModule) {
-        return NO_INIT;
-    }
-
     camera_info info;
-    status_t res = mCameraModule->getCameraInfo(atoi(String8(cameraId).string()),
+    status_t res = mCameraModule->getCameraInfo(atoi(cameraId.string()),
             &info);
     if (res != 0) {
         return res;
@@ -228,33 +318,31 @@
     return OK;
 }
 
-status_t FlashControl::setTorchMode(const String16& cameraId, bool enabled) {
+status_t ModuleFlashControl::setTorchMode(const String8& cameraId, bool enabled) {
     ALOGV("%s: set camera %s torch mode to %d", __FUNCTION__,
             cameraId.string(), enabled);
 
     Mutex::Autolock l(mLock);
-    if (!mCameraModule) {
-        return NO_INIT;
-    }
-
-    return mCameraModule->setTorchMode(String8(cameraId).string(), enabled);
+    return mCameraModule->setTorchMode(cameraId.string(), enabled);
 }
+// ModuleFlashControl implementation ends
 
+/////////////////////////////////////////////////////////////////////
+// CameraDeviceClientFlashControl implementation begins
+// Flash control for camera module <= v2.3 and camera HAL v2-v3
+/////////////////////////////////////////////////////////////////////
 CameraDeviceClientFlashControl::CameraDeviceClientFlashControl(
         CameraModule& cameraModule,
         const camera_module_callbacks_t& callbacks) :
         mCameraModule(&cameraModule),
         mCallbacks(&callbacks),
         mTorchEnabled(false),
-        mMetadata(NULL) {
+        mMetadata(NULL),
+        mStreaming(false) {
 }
 
 CameraDeviceClientFlashControl::~CameraDeviceClientFlashControl() {
-    if (mDevice != NULL) {
-        mDevice->flush();
-        mDevice->deleteStream(mStreamId);
-        mDevice.clear();
-    }
+    disconnectCameraDevice();
     if (mMetadata) {
         delete mMetadata;
     }
@@ -269,13 +357,13 @@
             ALOGV("%s: notify the framework that torch was turned off",
                     __FUNCTION__);
             mCallbacks->torch_mode_status_change(mCallbacks,
-                    String8(mCameraId).string(), TORCH_MODE_STATUS_OFF);
+                    mCameraId.string(), TORCH_MODE_STATUS_AVAILABLE_OFF);
         }
     }
 }
 
-status_t CameraDeviceClientFlashControl::initializeSurface(int32_t width,
-        int32_t height) {
+status_t CameraDeviceClientFlashControl::initializeSurface(
+        sp<CameraDeviceBase> &device, int32_t width, int32_t height) {
     status_t res;
     BufferQueue::createBufferQueue(&mProducer, &mConsumer);
 
@@ -295,27 +383,16 @@
         return res;
     }
 
-    bool useAsync = false;
-    int32_t consumerUsage;
-    res = mProducer->query(NATIVE_WINDOW_CONSUMER_USAGE_BITS, &consumerUsage);
-    if (res) {
-        return res;
-    }
-
-    if (consumerUsage & GraphicBuffer::USAGE_HW_TEXTURE) {
-        useAsync = true;
-    }
-
-    mAnw = new Surface(mProducer, useAsync);
+    mAnw = new Surface(mProducer, /*useAsync*/ true);
     if (mAnw == NULL) {
         return NO_MEMORY;
     }
-    res = mDevice->createStream(mAnw, width, height, format, &mStreamId);
+    res = device->createStream(mAnw, width, height, format, &mStreamId);
     if (res) {
         return res;
     }
 
-    res = mDevice->configureStreams();
+    res = device->configureStreams();
     if (res) {
         return res;
     }
@@ -342,7 +419,21 @@
             int32_t ww = streamConfigs.data.i32[i + 1];
             int32_t hh = streamConfigs.data.i32[i + 2];
 
-            if (w* h > ww * hh) {
+            if (w * h > ww * hh) {
+                w = ww;
+                h = hh;
+            }
+        }
+    }
+
+    // if stream configuration is not found, try available processed sizes.
+    if (streamConfigs.count == 0) {
+        camera_metadata_entry availableProcessedSizes =
+            metadata.find(ANDROID_SCALER_AVAILABLE_PROCESSED_SIZES);
+        for (size_t i = 0; i < availableProcessedSizes.count; i += 2) {
+            int32_t ww = availableProcessedSizes.data.i32[i];
+            int32_t hh = availableProcessedSizes.data.i32[i + 1];
+            if (w * h > ww * hh) {
                 w = ww;
                 h = hh;
             }
@@ -360,24 +451,24 @@
 }
 
 status_t CameraDeviceClientFlashControl::connectCameraDevice(
-        const String16& cameraId) {
-    String8 id = String8(cameraId);
+        const String8& cameraId) {
     camera_info info;
-    status_t res = mCameraModule->getCameraInfo(atoi(id.string()), &info);
+    status_t res = mCameraModule->getCameraInfo(atoi(cameraId.string()), &info);
     if (res != 0) {
         ALOGE("%s: failed to get camera info for camera %s", __FUNCTION__,
-                mCameraId.string());
+                cameraId.string());
         return res;
     }
 
-    mDevice = CameraDeviceFactory::createDevice(atoi(id.string()));
-    if (mDevice == NULL) {
+    sp<CameraDeviceBase> device =
+            CameraDeviceFactory::createDevice(atoi(cameraId.string()));
+    if (device == NULL) {
         return NO_MEMORY;
     }
 
-    res = mDevice->initialize(mCameraModule);
+    res = device->initialize(mCameraModule);
     if (res) {
-        goto fail;
+        return res;
     }
 
     int32_t width, height;
@@ -385,22 +476,30 @@
     if (res) {
         return res;
     }
-    res = initializeSurface(width, height);
+    res = initializeSurface(device, width, height);
     if (res) {
-        goto fail;
+        return res;
     }
 
     mCameraId = cameraId;
+    mStreaming = (info.device_version <= CAMERA_DEVICE_API_VERSION_3_1);
+    mDevice = device;
 
     return OK;
+}
 
-fail:
-    mDevice.clear();
-    return res;
+status_t CameraDeviceClientFlashControl::disconnectCameraDevice() {
+    if (mDevice != NULL) {
+        mDevice->disconnect();
+        mDevice.clear();
+    }
+
+    return OK;
 }
 
 
-status_t CameraDeviceClientFlashControl::hasFlashUnit(const String16& cameraId,
+
+status_t CameraDeviceClientFlashControl::hasFlashUnit(const String8& cameraId,
         bool *hasFlash) {
     ALOGV("%s: checking if camera %s has a flash unit", __FUNCTION__,
             cameraId.string());
@@ -411,19 +510,14 @@
 }
 
 status_t CameraDeviceClientFlashControl::hasFlashUnitLocked(
-        const String16& cameraId, bool *hasFlash) {
-    if (!mCameraModule) {
-        ALOGE("%s: camera module is NULL", __FUNCTION__);
-        return NO_INIT;
-    }
-
+        const String8& cameraId, bool *hasFlash) {
     if (!hasFlash) {
         return BAD_VALUE;
     }
 
     camera_info info;
     status_t res = mCameraModule->getCameraInfo(
-            atoi(String8(cameraId).string()), &info);
+            atoi(cameraId.string()), &info);
     if (res != 0) {
         ALOGE("%s: failed to get camera info for camera %s", __FUNCTION__,
                 cameraId.string());
@@ -441,7 +535,7 @@
     return OK;
 }
 
-status_t CameraDeviceClientFlashControl::submitTorchRequest(bool enabled) {
+status_t CameraDeviceClientFlashControl::submitTorchEnabledRequest() {
     status_t res;
 
     if (mMetadata == NULL) {
@@ -456,27 +550,29 @@
         }
     }
 
-    uint8_t torchOn = enabled ? ANDROID_FLASH_MODE_TORCH :
-                                ANDROID_FLASH_MODE_OFF;
-
+    uint8_t torchOn = ANDROID_FLASH_MODE_TORCH;
     mMetadata->update(ANDROID_FLASH_MODE, &torchOn, 1);
     mMetadata->update(ANDROID_REQUEST_OUTPUT_STREAMS, &mStreamId, 1);
 
+    uint8_t aeMode = ANDROID_CONTROL_AE_MODE_ON;
+    mMetadata->update(ANDROID_CONTROL_AE_MODE, &aeMode, 1);
+
     int32_t requestId = 0;
     mMetadata->update(ANDROID_REQUEST_ID, &requestId, 1);
 
-    List<const CameraMetadata> metadataRequestList;
-    metadataRequestList.push_back(*mMetadata);
-
-    int64_t lastFrameNumber = 0;
-    res = mDevice->captureList(metadataRequestList, &lastFrameNumber);
-
+    if (mStreaming) {
+        res = mDevice->setStreamingRequest(*mMetadata);
+    } else {
+        res = mDevice->capture(*mMetadata);
+    }
     return res;
 }
 
 
+
+
 status_t CameraDeviceClientFlashControl::setTorchMode(
-        const String16& cameraId, bool enabled) {
+        const String8& cameraId, bool enabled) {
     bool hasFlash = false;
 
     Mutex::Autolock l(mLock);
@@ -499,6 +595,13 @@
     } else if (mDevice == NULL || cameraId != mCameraId) {
         // disabling the torch mode of an un-opened or different device.
         return OK;
+    } else {
+        // disabling the torch mode of currently opened device
+        disconnectCameraDevice();
+        mTorchEnabled = false;
+        mCallbacks->torch_mode_status_change(mCallbacks,
+            cameraId.string(), TORCH_MODE_STATUS_AVAILABLE_OFF);
+        return OK;
     }
 
     if (mDevice == NULL) {
@@ -508,13 +611,16 @@
         }
     }
 
-    res = submitTorchRequest(enabled);
+    res = submitTorchEnabledRequest();
     if (res) {
         return res;
     }
 
-    mTorchEnabled = enabled;
+    mTorchEnabled = true;
+    mCallbacks->torch_mode_status_change(mCallbacks,
+            cameraId.string(), TORCH_MODE_STATUS_AVAILABLE_ON);
     return OK;
 }
+// CameraDeviceClientFlashControl implementation ends
 
 }
diff --git a/services/camera/libcameraservice/CameraFlashlight.h b/services/camera/libcameraservice/CameraFlashlight.h
index a0de0b0..ae502b9 100644
--- a/services/camera/libcameraservice/CameraFlashlight.h
+++ b/services/camera/libcameraservice/CameraFlashlight.h
@@ -19,6 +19,7 @@
 
 #include "hardware/camera_common.h"
 #include "utils/KeyedVector.h"
+#include "utils/SortedVector.h"
 #include "gui/GLConsumer.h"
 #include "gui/Surface.h"
 #include "common/CameraDeviceBase.h"
@@ -37,11 +38,11 @@
         // cause the torch mode to be turned off in HAL v1 devices. If
         // previously-on torch mode is turned off,
         // callbacks.torch_mode_status_change() should be invoked.
-        virtual status_t hasFlashUnit(const String16& cameraId,
+        virtual status_t hasFlashUnit(const String8& cameraId,
                     bool *hasFlash) = 0;
 
         // set the torch mode to on or off.
-        virtual status_t setTorchMode(const String16& cameraId,
+        virtual status_t setTorchMode(const String8& cameraId,
                     bool enabled) = 0;
 };
 
@@ -54,43 +55,61 @@
                 const camera_module_callbacks_t& callbacks);
         virtual ~CameraFlashlight();
 
-        // set the torch mode to on or off.
-        status_t setTorchMode(const String16& cameraId, bool enabled);
+        // Find all flash units. This must be called before other methods. All
+        // camera devices must be closed when it's called because HAL v1 devices
+        // need to be opened to query available flash modes.
+        status_t findFlashUnits();
 
-        // Whether a camera device has a flash unit. Calling this function may
-        // cause the torch mode to be turned off in HAL v1 devices.
-        bool hasFlashUnit(const String16& cameraId);
+        // Whether a camera device has a flash unit. Before findFlashUnits() is
+        // called, this function always returns false.
+        bool hasFlashUnit(const String8& cameraId);
+
+        // set the torch mode to on or off.
+        status_t setTorchMode(const String8& cameraId, bool enabled);
 
         // Notify CameraFlashlight that camera service is going to open a camera
         // device. CameraFlashlight will free the resources that may cause the
         // camera open to fail. Camera service must call this function before
         // opening a camera device.
-        status_t prepareDeviceOpen();
+        status_t prepareDeviceOpen(const String8& cameraId);
+
+        // Notify CameraFlashlight that camera service has closed a camera
+        // device. CameraFlashlight may invoke callbacks for torch mode
+        // available depending on the implementation.
+        status_t deviceClosed(const String8& cameraId);
 
     private:
         // create flashlight control based on camera module API and camera
         // device API versions.
-        status_t createFlashlightControl(const String16& cameraId);
+        status_t createFlashlightControl(const String8& cameraId);
+
+        // mLock should be locked.
+        bool hasFlashUnitLocked(const String8& cameraId);
 
         sp<FlashControlBase> mFlashControl;
         CameraModule *mCameraModule;
         const camera_module_callbacks_t *mCallbacks;
+        SortedVector<String8> mOpenedCameraIds;
 
-        Mutex mLock;
+        // camera id -> if it has a flash unit
+        KeyedVector<String8, bool> mHasFlashlightMap;
+        bool mFlashlightMapInitialized;
+
+        Mutex mLock; // protect CameraFlashlight API
 };
 
 /**
  * Flash control for camera module v2.4 and above.
  */
-class FlashControl : public FlashControlBase {
+class ModuleFlashControl : public FlashControlBase {
     public:
-        FlashControl(CameraModule& cameraModule,
+        ModuleFlashControl(CameraModule& cameraModule,
                 const camera_module_callbacks_t& callbacks);
-        virtual ~FlashControl();
+        virtual ~ModuleFlashControl();
 
         // FlashControlBase
-        status_t hasFlashUnit(const String16& cameraId, bool *hasFlash);
-        status_t setTorchMode(const String16& cameraId, bool enabled);
+        status_t hasFlashUnit(const String8& cameraId, bool *hasFlash);
+        status_t setTorchMode(const String8& cameraId, bool enabled);
 
     private:
         CameraModule *mCameraModule;
@@ -108,30 +127,37 @@
         virtual ~CameraDeviceClientFlashControl();
 
         // FlashControlBase
-        status_t setTorchMode(const String16& cameraId, bool enabled);
-        status_t hasFlashUnit(const String16& cameraId, bool *hasFlash);
+        status_t setTorchMode(const String8& cameraId, bool enabled);
+        status_t hasFlashUnit(const String8& cameraId, bool *hasFlash);
 
     private:
         // connect to a camera device
-        status_t connectCameraDevice(const String16& cameraId);
+        status_t connectCameraDevice(const String8& cameraId);
+        // disconnect and free mDevice
+        status_t disconnectCameraDevice();
 
         // initialize a surface
-        status_t initializeSurface(int32_t width, int32_t height);
+        status_t initializeSurface(sp<CameraDeviceBase>& device, int32_t width,
+                int32_t height);
 
-        // submit a request with the given torch mode
-        status_t submitTorchRequest(bool enabled);
+        // submit a request to enable the torch mode
+        status_t submitTorchEnabledRequest();
 
         // get the smallest surface size of IMPLEMENTATION_DEFINED
         status_t getSmallestSurfaceSize(const camera_info& info, int32_t *width,
                     int32_t *height);
 
-        status_t hasFlashUnitLocked(const String16& cameraId, bool *hasFlash);
+        status_t hasFlashUnitLocked(const String8& cameraId, bool *hasFlash);
 
         CameraModule *mCameraModule;
         const camera_module_callbacks_t *mCallbacks;
-        String16 mCameraId;
+        String8 mCameraId;
         bool mTorchEnabled;
         CameraMetadata *mMetadata;
+        // WORKAROUND: will be set to true for HAL v2 devices where
+        // setStreamingRequest() needs to be call for torch mode settings to
+        // take effect.
+        bool mStreaming;
 
         sp<CameraDeviceBase> mDevice;
 
diff --git a/services/camera/libcameraservice/CameraService.cpp b/services/camera/libcameraservice/CameraService.cpp
index d65ac21..8fdfaa8 100644
--- a/services/camera/libcameraservice/CameraService.cpp
+++ b/services/camera/libcameraservice/CameraService.cpp
@@ -101,14 +101,14 @@
 
     ICameraServiceListener::TorchStatus status;
     switch (new_status) {
-        case TORCH_MODE_STATUS_AVAILABLE:
-            status = ICameraServiceListener::TORCH_STATUS_AVAILABLE;
-            break;
-        case TORCH_MODE_STATUS_RESOURCE_BUSY:
+        case TORCH_MODE_STATUS_NOT_AVAILABLE:
             status = ICameraServiceListener::TORCH_STATUS_NOT_AVAILABLE;
             break;
-        case TORCH_MODE_STATUS_OFF:
-            status = ICameraServiceListener::TORCH_STATUS_OFF;
+        case TORCH_MODE_STATUS_AVAILABLE_OFF:
+            status = ICameraServiceListener::TORCH_STATUS_AVAILABLE_OFF;
+            break;
+        case TORCH_MODE_STATUS_AVAILABLE_ON:
+            status = ICameraServiceListener::TORCH_STATUS_AVAILABLE_ON;
             break;
         default:
             ALOGE("Unknown torch status %d", new_status);
@@ -116,7 +116,7 @@
     }
 
     cs->onTorchStatusChanged(
-        String16(camera_id),
+        String8(camera_id),
         status);
 }
 } // extern "C"
@@ -156,23 +156,29 @@
     }
     else {
         mModule = new CameraModule(rawModule);
-        mFlashlight = new CameraFlashlight(*mModule, *this);
-
         const hw_module_t *common = mModule->getRawModule();
-        ALOGI("Loaded \"%s\" camera module", common->name);
+        ALOGI("Loaded \"%s\" cameraCa module", common->name);
         mNumberOfCameras = mModule->getNumberOfCameras();
         if (mNumberOfCameras > MAX_CAMERAS) {
             ALOGE("Number of cameras(%d) > MAX_CAMERAS(%d).",
                     mNumberOfCameras, MAX_CAMERAS);
             mNumberOfCameras = MAX_CAMERAS;
         }
+
+        mFlashlight = new CameraFlashlight(*mModule, *this);
+        status_t res = mFlashlight->findFlashUnits();
+        if (res) {
+            // impossible because we haven't open any camera devices.
+            ALOGE("failed to find flash units.");
+        }
+
         for (int i = 0; i < mNumberOfCameras; i++) {
             setCameraFree(i);
 
-            String16 cameraName = String16(String8::format("%d", i));
+            String8 cameraName = String8::format("%d", i);
             if (mFlashlight->hasFlashUnit(cameraName)) {
                 mTorchStatusMap.add(cameraName,
-                        ICameraServiceListener::TORCH_STATUS_AVAILABLE);
+                        ICameraServiceListener::TORCH_STATUS_AVAILABLE_OFF);
             }
         }
 
@@ -207,7 +213,7 @@
 void CameraService::onDeviceStatusChanged(int cameraId,
                                           int newStatus)
 {
-    ALOGI("%s: Status changed for cameraId=%d, newStatus=%d", __FUNCTION__,
+    ALOGV("%s: Status changed for cameraId=%d, newStatus=%d", __FUNCTION__,
           cameraId, newStatus);
 
     if (cameraId < 0 || cameraId >= MAX_CAMERAS) {
@@ -268,24 +274,30 @@
 
 }
 
-void CameraService::onTorchStatusChanged(const String16& cameraId,
+void CameraService::onTorchStatusChanged(const String8& cameraId,
         ICameraServiceListener::TorchStatus newStatus) {
     Mutex::Autolock al(mTorchStatusMutex);
     onTorchStatusChangedLocked(cameraId, newStatus);
 }
 
-void CameraService::onTorchStatusChangedLocked(const String16& cameraId,
+void CameraService::onTorchStatusChangedLocked(const String8& cameraId,
         ICameraServiceListener::TorchStatus newStatus) {
     ALOGI("%s: Torch status changed for cameraId=%s, newStatus=%d",
             __FUNCTION__, cameraId.string(), newStatus);
 
-    if (getTorchStatusLocked(cameraId) == newStatus) {
+    ICameraServiceListener::TorchStatus status;
+    status_t res = getTorchStatusLocked(cameraId, &status);
+    if (res) {
+        ALOGE("%s: cannot get torch status of camera %s", cameraId.string());
+        return;
+    }
+    if (status == newStatus) {
         ALOGE("%s: Torch state transition to the same status 0x%x not allowed",
               __FUNCTION__, (uint32_t)newStatus);
         return;
     }
 
-    status_t res = setTorchStatusLocked(cameraId, newStatus);
+    res = setTorchStatusLocked(cameraId, newStatus);
     if (res) {
         ALOGE("%s: Failed to set the torch status", __FUNCTION__,
                 (uint32_t)newStatus);
@@ -294,7 +306,7 @@
 
     Vector<sp<ICameraServiceListener> >::const_iterator it;
     for (it = mListenerList.begin(); it != mListenerList.end(); ++it) {
-        (*it)->onTorchStatusChanged(newStatus, cameraId);
+        (*it)->onTorchStatusChanged(newStatus, String16(cameraId.string()));
     }
 }
 
@@ -754,7 +766,7 @@
         bool legacyMode) {
 
     // give flashlight a chance to close devices if necessary.
-    mFlashlight->prepareDeviceOpen();
+    mFlashlight->prepareDeviceOpen(String8::format("%d", cameraId));
 
     int facing = -1;
     int deviceVersion = getDeviceVersion(cameraId, &facing);
@@ -932,44 +944,94 @@
     return OK;
 }
 
+bool CameraService::validCameraIdForSetTorchMode(const String8& cameraId) {
+    // invalid string for int
+    if (cameraId.string() == NULL) {
+        return false;
+    }
+    errno = 0;
+    char *endptr;
+    long id = strtol(cameraId.string(), &endptr, 10); // base 10
+    if (errno || id > INT_MAX || id < INT_MIN || *endptr != 0) {
+        return false;
+    }
+
+    // id matches one of the plugged-in devices?
+    ICameraServiceListener::Status deviceStatus = getStatus(id);
+    if (deviceStatus != ICameraServiceListener::STATUS_PRESENT &&
+            deviceStatus != ICameraServiceListener::STATUS_NOT_AVAILABLE) {
+        return false;
+    }
+
+    return true;
+}
+
 status_t CameraService::setTorchMode(const String16& cameraId, bool enabled,
         const sp<IBinder>& clientBinder) {
     if (enabled && clientBinder == NULL) {
         ALOGE("%s: torch client binder is NULL", __FUNCTION__);
-        return -ENOSYS;
+        return -EINVAL;
     }
 
-    Mutex::Autolock al(mTorchStatusMutex);
-    status_t res = mFlashlight->setTorchMode(cameraId, enabled);
+    String8 id = String8(cameraId.string());
+
+    // verify id is valid.
+    if (validCameraIdForSetTorchMode(id) == false) {
+        ALOGE("%s: camera id is invalid %s", id.string());
+        return -EINVAL;
+    }
+
+    {
+        Mutex::Autolock al(mTorchStatusMutex);
+        ICameraServiceListener::TorchStatus status;
+        status_t res = getTorchStatusLocked(id, &status);
+        if (res) {
+            ALOGE("%s: getting current torch status failed for camera %s",
+                    __FUNCTION__, id.string());
+            return -EINVAL;
+        }
+
+        if (status == ICameraServiceListener::TORCH_STATUS_NOT_AVAILABLE) {
+            if (getStatus(atoi(id.string())) ==
+                        ICameraServiceListener::STATUS_NOT_AVAILABLE) {
+                ALOGE("%s: torch mode of camera %s is not available because "
+                        "camera is in use", __FUNCTION__, id.string());
+                return -EBUSY;
+            } else {
+                ALOGE("%s: torch mode of camera %s is not available due to "
+                        "insufficient resources", __FUNCTION__, id.string());
+                return -EUSERS;
+            }
+        }
+    }
+
+    status_t res = mFlashlight->setTorchMode(id, enabled);
     if (res) {
-        ALOGE("%s: setting torch mode of camera %s to %d failed", __FUNCTION__,
-                cameraId.string(), enabled);
+        ALOGE("%s: setting torch mode of camera %s to %d failed. %s (%d)",
+                __FUNCTION__, id.string(), enabled, strerror(-res), res);
         return res;
     }
 
-    // update the link to client's death
-    ssize_t index = mTorchClientMap.indexOfKey(cameraId);
-    if (enabled) {
-        if (index == NAME_NOT_FOUND) {
-            mTorchClientMap.add(cameraId, clientBinder);
-        } else {
-            const sp<IBinder> oldBinder = mTorchClientMap.valueAt(index);
+    {
+        // update the link to client's death
+        Mutex::Autolock al(mTorchClientMapMutex);
+        ssize_t index = mTorchClientMap.indexOfKey(id);
+        if (enabled) {
+            if (index == NAME_NOT_FOUND) {
+                mTorchClientMap.add(id, clientBinder);
+            } else {
+                const sp<IBinder> oldBinder = mTorchClientMap.valueAt(index);
+                oldBinder->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.replaceValueAt(index, clientBinder);
         }
-        clientBinder->linkToDeath(this);
-    } else if (index != NAME_NOT_FOUND) {
-        sp<IBinder> oldBinder = mTorchClientMap.valueAt(index);
-        oldBinder->unlinkToDeath(this);
     }
 
-    // notify the listeners the change.
-    ICameraServiceListener::TorchStatus status = enabled ?
-            ICameraServiceListener::TORCH_STATUS_ON :
-            ICameraServiceListener::TORCH_STATUS_OFF;
-    onTorchStatusChangedLocked(cameraId, status);
-
     return OK;
 }
 
@@ -1099,7 +1161,7 @@
         int deviceVersion = getDeviceVersion(cameraId, &facing);
 
         // give flashlight a chance to close devices if necessary.
-        mFlashlight->prepareDeviceOpen();
+        mFlashlight->prepareDeviceOpen(String8::format("%d", cameraId));
 
         switch(deviceVersion) {
           case CAMERA_DEVICE_API_VERSION_1_0:
@@ -1176,8 +1238,8 @@
     {
         Mutex::Autolock al(mTorchStatusMutex);
         for (size_t i = 0; i < mTorchStatusMap.size(); i++ ) {
-            listener->onTorchStatusChanged(mTorchStatusMap.valueAt(i),
-                    mTorchStatusMap.keyAt(i));
+            String16 id = String16(mTorchStatusMap.keyAt(i).string());
+            listener->onTorchStatusChanged(mTorchStatusMap.valueAt(i), id);
         }
 
     }
@@ -1616,6 +1678,9 @@
                 mCameraId,
                 &rejectSourceStates);
 
+        // Notify flashlight that a camera device is closed.
+        mCameraService->mFlashlight->deviceClosed(
+                String8::format("%d", mCameraId));
     }
     // Always stop watching, even if no camera op is active
     if (mOpsCallback != NULL) {
@@ -1862,17 +1927,18 @@
 }
 
 void CameraService::handleTorchClientBinderDied(const wp<IBinder> &who) {
-    Mutex::Autolock al(mTorchStatusMutex);
+    Mutex::Autolock al(mTorchClientMapMutex);
     for (size_t i = 0; i < mTorchClientMap.size(); i++) {
         if (mTorchClientMap[i] == who) {
             // turn off the torch mode that was turned on by dead client
-            String16 cameraId = mTorchClientMap.keyAt(i);
-            mFlashlight->setTorchMode(cameraId, false);
+            String8 cameraId = mTorchClientMap.keyAt(i);
+            status_t res = mFlashlight->setTorchMode(cameraId, false);
+            if (res) {
+                ALOGE("%s: torch client died but couldn't turn off torch: "
+                    "%s (%d)", __FUNCTION__, strerror(-res), res);
+                return;
+            }
             mTorchClientMap.removeItemsAt(i);
-
-            // notify torch mode was turned off
-            onTorchStatusChangedLocked(cameraId,
-                    ICameraServiceListener::TORCH_STATUS_OFF);
             break;
         }
     }
@@ -1968,6 +2034,19 @@
             }
         }
 
+        if (status == ICameraServiceListener::STATUS_NOT_PRESENT ||
+            status == ICameraServiceListener::STATUS_NOT_AVAILABLE) {
+            // update torch status to not available when the camera device
+            // becomes not present or not available.
+            onTorchStatusChanged(String8::format("%d", cameraId),
+                    ICameraServiceListener::TORCH_STATUS_NOT_AVAILABLE);
+        } else if (status == ICameraServiceListener::STATUS_PRESENT) {
+            // update torch status to available when the camera device becomes
+            // present or available
+            onTorchStatusChanged(String8::format("%d", cameraId),
+                    ICameraServiceListener::TORCH_STATUS_AVAILABLE_OFF);
+        }
+
         Vector<sp<ICameraServiceListener> >::const_iterator it;
         for (it = mListenerList.begin(); it != mListenerList.end(); ++it) {
             (*it)->onStatusChanged(status, cameraId);
@@ -1985,17 +2064,23 @@
     return mStatusList[cameraId];
 }
 
-ICameraServiceListener::TorchStatus CameraService::getTorchStatusLocked(
-        const String16& cameraId) const {
+status_t CameraService::getTorchStatusLocked(
+        const String8& cameraId,
+        ICameraServiceListener::TorchStatus *status) const {
+    if (!status) {
+        return BAD_VALUE;
+    }
     ssize_t index = mTorchStatusMap.indexOfKey(cameraId);
     if (index == NAME_NOT_FOUND) {
-        return ICameraServiceListener::TORCH_STATUS_NOT_AVAILABLE;
+        // invalid camera ID or the camera doesn't have a flash unit
+        return NAME_NOT_FOUND;
     }
 
-    return mTorchStatusMap.valueAt(index);
+    *status = mTorchStatusMap.valueAt(index);
+    return OK;
 }
 
-status_t CameraService::setTorchStatusLocked(const String16& cameraId,
+status_t CameraService::setTorchStatusLocked(const String8& cameraId,
         ICameraServiceListener::TorchStatus status) {
     ssize_t index = mTorchStatusMap.indexOfKey(cameraId);
     if (index == NAME_NOT_FOUND) {
diff --git a/services/camera/libcameraservice/CameraService.h b/services/camera/libcameraservice/CameraService.h
index 84bcdb8..74b3623 100644
--- a/services/camera/libcameraservice/CameraService.h
+++ b/services/camera/libcameraservice/CameraService.h
@@ -72,7 +72,7 @@
     // HAL Callbacks
     virtual void        onDeviceStatusChanged(int cameraId,
                                               int newStatus);
-    virtual void        onTorchStatusChanged(const String16& cameraId,
+    virtual void        onTorchStatusChanged(const String8& cameraId,
                                              ICameraServiceListener::TorchStatus
                                                    newStatus);
 
@@ -418,28 +418,33 @@
 
     // flashlight control
     sp<CameraFlashlight> mFlashlight;
-    // guard mTorchStatusMap and mTorchClientMap
+    // guard mTorchStatusMap
     Mutex                mTorchStatusMutex;
+    // guard mTorchClientMap
+    Mutex                mTorchClientMapMutex;
     // camera id -> torch status
-    KeyedVector<String16, ICameraServiceListener::TorchStatus> mTorchStatusMap;
+    KeyedVector<String8, ICameraServiceListener::TorchStatus> mTorchStatusMap;
     // camera id -> torch client binder
     // only store the last client that turns on each camera's torch mode
-    KeyedVector<String16, sp<IBinder> > mTorchClientMap;
+    KeyedVector<String8, sp<IBinder> > mTorchClientMap;
 
     // check and handle if torch client's process has died
     void handleTorchClientBinderDied(const wp<IBinder> &who);
 
     // handle torch mode status change and invoke callbacks. mTorchStatusMutex
     // should be locked.
-    void onTorchStatusChangedLocked(const String16& cameraId,
+    void onTorchStatusChangedLocked(const String8& cameraId,
             ICameraServiceListener::TorchStatus newStatus);
 
+    // validate the camera id for use of setting a torch mode.
+    bool validCameraIdForSetTorchMode(const String8& cameraId);
+
     // get a camera's torch status. mTorchStatusMutex should be locked.
-    ICameraServiceListener::TorchStatus getTorchStatusLocked(
-            const String16 &cameraId) const;
+    status_t getTorchStatusLocked(const String8 &cameraId,
+            ICameraServiceListener::TorchStatus *status) const;
 
     // set a camera's torch status. mTorchStatusMutex should be locked.
-    status_t setTorchStatusLocked(const String16 &cameraId,
+    status_t setTorchStatusLocked(const String8 &cameraId,
             ICameraServiceListener::TorchStatus status);
 
     // IBinder::DeathRecipient implementation