camera: implement flashlight control

Implement flashlight API for module v2.4 by calling module APIs and
by for hal v2 and v3 by using CameraDeviceBase.

Bug: 2682206
Change-Id: Ib8b77f6fd462489d672f27e14fe37801d35b7544
diff --git a/camera/ICameraService.cpp b/camera/ICameraService.cpp
index fc3e437..a75cb48 100644
--- a/camera/ICameraService.cpp
+++ b/camera/ICameraService.cpp
@@ -209,6 +209,20 @@
         return status;
     }
 
+    virtual status_t setTorchMode(const String16& cameraId, bool enabled,
+            const sp<IBinder>& clientBinder)
+    {
+        Parcel data, reply;
+        data.writeInterfaceToken(ICameraService::getInterfaceDescriptor());
+        data.writeString16(cameraId);
+        data.writeInt32(enabled ? 1 : 0);
+        data.writeStrongBinder(clientBinder);
+        remote()->transact(BnCameraService::SET_TORCH_MODE, data, &reply);
+
+        if (readExceptionCode(reply)) return -EPROTO;
+        return reply.readInt32();
+    }
+
     // connect to camera service (pro client)
     virtual status_t connectPro(const sp<IProCameraCallbacks>& cameraCb, int cameraId,
                                 const String16 &clientPackageName, int clientUid,
@@ -490,6 +504,16 @@
             }
             return NO_ERROR;
         } break;
+        case SET_TORCH_MODE: {
+            CHECK_INTERFACE(ICameraService, data, reply);
+            String16 cameraId = data.readString16();
+            bool enabled = data.readInt32() != 0 ? true : false;
+            const sp<IBinder> clientBinder = data.readStrongBinder();
+            status_t status = setTorchMode(cameraId, enabled, clientBinder);
+            reply->writeNoException();
+            reply->writeInt32(status);
+            return NO_ERROR;
+        } break;
         default:
             return BBinder::onTransact(code, data, reply, flags);
     }
diff --git a/camera/ICameraServiceListener.cpp b/camera/ICameraServiceListener.cpp
index b2f1729..90a8bc2 100644
--- a/camera/ICameraServiceListener.cpp
+++ b/camera/ICameraServiceListener.cpp
@@ -29,6 +29,7 @@
 namespace {
     enum {
         STATUS_CHANGED = IBinder::FIRST_CALL_TRANSACTION,
+        TORCH_STATUS_CHANGED,
     };
 }; // namespace anonymous
 
@@ -54,8 +55,21 @@
                            data,
                            &reply,
                            IBinder::FLAG_ONEWAY);
+    }
 
-        reply.readExceptionCode();
+    virtual void onTorchStatusChanged(TorchStatus status, const String16 &cameraId)
+    {
+        Parcel data, reply;
+        data.writeInterfaceToken(
+                              ICameraServiceListener::getInterfaceDescriptor());
+
+        data.writeInt32(static_cast<int32_t>(status));
+        data.writeString16(cameraId);
+
+        remote()->transact(TORCH_STATUS_CHANGED,
+                           data,
+                           &reply,
+                           IBinder::FLAG_ONEWAY);
     }
 };
 
@@ -75,7 +89,16 @@
             int32_t cameraId = data.readInt32();
 
             onStatusChanged(status, cameraId);
-            reply->writeNoException();
+
+            return NO_ERROR;
+        } break;
+        case TORCH_STATUS_CHANGED: {
+            CHECK_INTERFACE(ICameraServiceListener, data, reply);
+
+            TorchStatus status = static_cast<TorchStatus>(data.readInt32());
+            String16 cameraId = data.readString16();
+
+            onTorchStatusChanged(status, cameraId);
 
             return NO_ERROR;
         } break;
diff --git a/camera/tests/ProCameraTests.cpp b/camera/tests/ProCameraTests.cpp
index 1f5867a..6212678 100644
--- a/camera/tests/ProCameraTests.cpp
+++ b/camera/tests/ProCameraTests.cpp
@@ -89,6 +89,12 @@
         mCondition.broadcast();
     }
 
+    void onTorchStatusChanged(TorchStatus status, const String16& cameraId) {
+        dout << "On torch status changed: 0x" << std::hex
+             << (unsigned int) status << " cameraId " << cameraId.string()
+             << std::endl;
+    }
+
     status_t waitForStatusChange(Status& newStatus) {
         Mutex::Autolock al(mMutex);
 
diff --git a/include/camera/ICameraService.h b/include/camera/ICameraService.h
index f7f06bb..cc41efe 100644
--- a/include/camera/ICameraService.h
+++ b/include/camera/ICameraService.h
@@ -53,6 +53,7 @@
         GET_LEGACY_PARAMETERS,
         SUPPORTS_CAMERA_API,
         CONNECT_LEGACY,
+        SET_TORCH_MODE,
     };
 
     enum {
@@ -142,6 +143,12 @@
             int clientUid,
             /*out*/
             sp<ICamera>& device) = 0;
+
+    /**
+     * Turn on or off a camera's torch mode.
+     */
+    virtual status_t setTorchMode(const String16& cameraId, bool enabled,
+            const sp<IBinder>& clientBinder) = 0;
 };
 
 // ----------------------------------------------------------------------------
diff --git a/include/camera/ICameraServiceListener.h b/include/camera/ICameraServiceListener.h
index 0a0e43a..9e8b912 100644
--- a/include/camera/ICameraServiceListener.h
+++ b/include/camera/ICameraServiceListener.h
@@ -66,9 +66,33 @@
         STATUS_UNKNOWN          = 0xFFFFFFFF,
     };
 
+    /**
+     * The torch mode status of a camera.
+     *
+     * Initial status will be transmitted with onTorchStatusChanged immediately
+     * after this listener is added to the service listener list.
+     */
+    enum TorchStatus {
+        // The camera's torch mode has become available to use via
+        // setTorchMode().
+        TORCH_STATUS_AVAILABLE      = TORCH_MODE_STATUS_AVAILABLE,
+        // The camera's torch mode has become not available to use via
+        // setTorchMode().
+        TORCH_STATUS_NOT_AVAILABLE  = TORCH_MODE_STATUS_RESOURCE_BUSY,
+        // The camera's torch mode has been turned off by setTorchMode().
+        TORCH_STATUS_OFF            = TORCH_MODE_STATUS_OFF,
+        // The camera's torch mode has been turned on by setTorchMode().
+        TORCH_STATUS_ON             = 0x80000000,
+
+        // Use to initialize variables only
+        TORCH_STATUS_UNKNOWN        = 0xFFFFFFFF,
+    };
+
     DECLARE_META_INTERFACE(CameraServiceListener);
 
     virtual void onStatusChanged(Status status, int32_t cameraId) = 0;
+
+    virtual void onTorchStatusChanged(TorchStatus status, const String16& cameraId) = 0;
 };
 
 // ----------------------------------------------------------------------------
diff --git a/services/camera/libcameraservice/Android.mk b/services/camera/libcameraservice/Android.mk
index de9551d..5d6423a 100644
--- a/services/camera/libcameraservice/Android.mk
+++ b/services/camera/libcameraservice/Android.mk
@@ -23,6 +23,7 @@
 LOCAL_SRC_FILES:=               \
     CameraService.cpp \
     CameraDeviceFactory.cpp \
+    CameraFlashlight.cpp \
     common/Camera2ClientBase.cpp \
     common/CameraDeviceBase.cpp \
     common/CameraModule.cpp \
diff --git a/services/camera/libcameraservice/CameraFlashlight.cpp b/services/camera/libcameraservice/CameraFlashlight.cpp
new file mode 100644
index 0000000..00a70eb
--- /dev/null
+++ b/services/camera/libcameraservice/CameraFlashlight.cpp
@@ -0,0 +1,520 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#define LOG_TAG "CameraFlashlight"
+#define ATRACE_TAG ATRACE_TAG_CAMERA
+#define LOG_NDEBUG 0
+
+#include <utils/Log.h>
+#include <utils/Trace.h>
+#include <cutils/properties.h>
+
+#include "camera/CameraMetadata.h"
+#include "CameraFlashlight.h"
+#include "gui/IGraphicBufferConsumer.h"
+#include "gui/BufferQueue.h"
+#include "camera/camera2/CaptureRequest.h"
+#include "CameraDeviceFactory.h"
+
+
+namespace android {
+
+CameraFlashlight::CameraFlashlight(CameraModule& cameraModule,
+        const camera_module_callbacks_t& callbacks) :
+        mCameraModule(&cameraModule),
+        mCallbacks(&callbacks) {
+}
+
+CameraFlashlight::~CameraFlashlight() {
+}
+
+status_t CameraFlashlight::createFlashlightControl(const String16& cameraId) {
+    ALOGV("%s: creating a flash light control for camera %s", __FUNCTION__,
+            cameraId.string());
+    if (mFlashControl != NULL) {
+        return INVALID_OPERATION;
+    }
+
+    status_t res = OK;
+
+    if (mCameraModule->getRawModule()->module_api_version >=
+            CAMERA_MODULE_API_VERSION_2_4) {
+        mFlashControl = new FlashControl(*mCameraModule, *mCallbacks);
+        if (mFlashControl == NULL) {
+            ALOGV("%s: cannot create flash control for module api v2.4+",
+                     __FUNCTION__);
+            return NO_MEMORY;
+        }
+    } else {
+        uint32_t deviceVersion = CAMERA_DEVICE_API_VERSION_1_0;
+
+        if (mCameraModule->getRawModule()->module_api_version >=
+                CAMERA_MODULE_API_VERSION_2_0) {
+            camera_info info;
+            res = mCameraModule->getCameraInfo(
+                    atoi(String8(cameraId).string()), &info);
+            if (res) {
+                ALOGV("%s: failed to get camera info for camera %s",
+                        __FUNCTION__, cameraId.string());
+                return res;
+            }
+            deviceVersion = info.device_version;
+        }
+
+        if (deviceVersion >= CAMERA_DEVICE_API_VERSION_2_0) {
+            CameraDeviceClientFlashControl *flashControl =
+                    new CameraDeviceClientFlashControl(*mCameraModule,
+                                                       *mCallbacks);
+            if (!flashControl) {
+                return NO_MEMORY;
+            }
+
+            mFlashControl = flashControl;
+        }
+        else {
+            // todo: implement for device api 1
+            return INVALID_OPERATION;
+        }
+    }
+
+    return OK;
+}
+
+status_t CameraFlashlight::setTorchMode(const String16& cameraId, bool enabled) {
+    if (!mCameraModule) {
+        return NO_INIT;
+    }
+
+    ALOGV("%s: set torch mode of camera %s to %d", __FUNCTION__,
+            cameraId.string(), enabled);
+
+    status_t res = OK;
+    Mutex::Autolock l(mLock);
+
+    if (mFlashControl == NULL) {
+        res = createFlashlightControl(cameraId);
+        if (res) {
+            return res;
+        }
+        res =  mFlashControl->setTorchMode(cameraId, enabled);
+        return res;
+    }
+
+    // if flash control already exists, turning on torch mode may fail if it's
+    // tied to another camera device for module v2.3 and below.
+    res = mFlashControl->setTorchMode(cameraId, enabled);
+    if (res == BAD_INDEX) {
+        // flash control is tied to another camera device, need to close it and
+        // try again.
+        mFlashControl.clear();
+        res = createFlashlightControl(cameraId);
+        if (res) {
+            return res;
+        }
+        res = mFlashControl->setTorchMode(cameraId, enabled);
+    }
+
+    return res;
+}
+
+bool CameraFlashlight::hasFlashUnit(const String16& 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;
+}
+
+status_t CameraFlashlight::prepareDeviceOpen() {
+    ALOGV("%s: prepare for device open", __FUNCTION__);
+
+    Mutex::Autolock l(mLock);
+
+    if (mCameraModule && 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();
+        }
+    }
+
+    return OK;
+}
+
+
+FlashControlBase::~FlashControlBase() {
+}
+
+
+FlashControl::FlashControl(CameraModule& cameraModule,
+        const camera_module_callbacks_t& callbacks) :
+    mCameraModule(&cameraModule) {
+}
+
+FlashControl::~FlashControl() {
+}
+
+status_t FlashControl::hasFlashUnit(const String16& 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()),
+            &info);
+    if (res != 0) {
+        return res;
+    }
+
+    CameraMetadata metadata;
+    metadata = info.static_camera_characteristics;
+    camera_metadata_entry flashAvailable =
+            metadata.find(ANDROID_FLASH_INFO_AVAILABLE);
+    if (flashAvailable.count == 1 && flashAvailable.data.u8[0] == 1) {
+        *hasFlash = true;
+    }
+
+    return OK;
+}
+
+status_t FlashControl::setTorchMode(const String16& 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);
+}
+
+CameraDeviceClientFlashControl::CameraDeviceClientFlashControl(
+        CameraModule& cameraModule,
+        const camera_module_callbacks_t& callbacks) :
+        mCameraModule(&cameraModule),
+        mCallbacks(&callbacks),
+        mTorchEnabled(false),
+        mMetadata(NULL) {
+}
+
+CameraDeviceClientFlashControl::~CameraDeviceClientFlashControl() {
+    if (mDevice != NULL) {
+        mDevice->flush();
+        mDevice->deleteStream(mStreamId);
+        mDevice.clear();
+    }
+    if (mMetadata) {
+        delete mMetadata;
+    }
+
+    mAnw.clear();
+    mSurfaceTexture.clear();
+    mProducer.clear();
+    mConsumer.clear();
+
+    if (mTorchEnabled) {
+        if (mCallbacks) {
+            ALOGV("%s: notify the framework that torch was turned off",
+                    __FUNCTION__);
+            mCallbacks->torch_mode_status_change(mCallbacks,
+                    String8(mCameraId).string(), TORCH_MODE_STATUS_OFF);
+        }
+    }
+}
+
+status_t CameraDeviceClientFlashControl::initializeSurface(int32_t width,
+        int32_t height) {
+    status_t res;
+    BufferQueue::createBufferQueue(&mProducer, &mConsumer);
+
+    mSurfaceTexture = new GLConsumer(mConsumer, 0, GLConsumer::TEXTURE_EXTERNAL,
+            true, true);
+    if (mSurfaceTexture == NULL) {
+        return NO_MEMORY;
+    }
+
+    int32_t format = HAL_PIXEL_FORMAT_IMPLEMENTATION_DEFINED;
+    res = mSurfaceTexture->setDefaultBufferSize(width, height);
+    if (res) {
+        return res;
+    }
+    res = mSurfaceTexture->setDefaultBufferFormat(format);
+    if (res) {
+        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);
+    if (mAnw == NULL) {
+        return NO_MEMORY;
+    }
+    res = mDevice->createStream(mAnw, width, height, format, &mStreamId);
+    if (res) {
+        return res;
+    }
+
+    res = mDevice->configureStreams();
+    if (res) {
+        return res;
+    }
+
+    return res;
+}
+
+status_t CameraDeviceClientFlashControl::getSmallestSurfaceSize(
+        const camera_info& info, int32_t *width, int32_t *height) {
+    if (!width || !height) {
+        return BAD_VALUE;
+    }
+
+    int32_t w = INT32_MAX;
+    int32_t h = 1;
+
+    CameraMetadata metadata;
+    metadata = info.static_camera_characteristics;
+    camera_metadata_entry streamConfigs =
+            metadata.find(ANDROID_SCALER_AVAILABLE_STREAM_CONFIGURATIONS);
+    for (size_t i = 0; i < streamConfigs.count; i += 4) {
+        int32_t fmt = streamConfigs.data.i32[i];
+        if (fmt == ANDROID_SCALER_AVAILABLE_FORMATS_IMPLEMENTATION_DEFINED) {
+            int32_t ww = streamConfigs.data.i32[i + 1];
+            int32_t hh = streamConfigs.data.i32[i + 2];
+
+            if (w* h > ww * hh) {
+                w = ww;
+                h = hh;
+            }
+        }
+    }
+
+    if (w == INT32_MAX) {
+        return NAME_NOT_FOUND;
+    }
+
+    *width = w;
+    *height = h;
+
+    return OK;
+}
+
+status_t CameraDeviceClientFlashControl::connectCameraDevice(
+        const String16& cameraId) {
+    String8 id = String8(cameraId);
+    camera_info info;
+    status_t res = mCameraModule->getCameraInfo(atoi(id.string()), &info);
+    if (res != 0) {
+        ALOGE("%s: failed to get camera info for camera %s", __FUNCTION__,
+                mCameraId.string());
+        return res;
+    }
+
+    mDevice = CameraDeviceFactory::createDevice(atoi(id.string()));
+    if (mDevice == NULL) {
+        return NO_MEMORY;
+    }
+
+    res = mDevice->initialize(mCameraModule);
+    if (res) {
+        goto fail;
+    }
+
+    int32_t width, height;
+    res = getSmallestSurfaceSize(info, &width, &height);
+    if (res) {
+        return res;
+    }
+    res = initializeSurface(width, height);
+    if (res) {
+        goto fail;
+    }
+
+    mCameraId = cameraId;
+
+    return OK;
+
+fail:
+    mDevice.clear();
+    return res;
+}
+
+
+status_t CameraDeviceClientFlashControl::hasFlashUnit(const String16& cameraId,
+        bool *hasFlash) {
+    ALOGV("%s: checking if camera %s has a flash unit", __FUNCTION__,
+            cameraId.string());
+
+    Mutex::Autolock l(mLock);
+    return hasFlashUnitLocked(cameraId, hasFlash);
+
+}
+
+status_t CameraDeviceClientFlashControl::hasFlashUnitLocked(
+        const String16& cameraId, bool *hasFlash) {
+    if (!mCameraModule) {
+        ALOGE("%s: camera module is NULL", __FUNCTION__);
+        return NO_INIT;
+    }
+
+    if (!hasFlash) {
+        return BAD_VALUE;
+    }
+
+    camera_info info;
+    status_t res = mCameraModule->getCameraInfo(
+            atoi(String8(cameraId).string()), &info);
+    if (res != 0) {
+        ALOGE("%s: failed to get camera info for camera %s", __FUNCTION__,
+                cameraId.string());
+        return res;
+    }
+
+    CameraMetadata metadata;
+    metadata = info.static_camera_characteristics;
+    camera_metadata_entry flashAvailable =
+            metadata.find(ANDROID_FLASH_INFO_AVAILABLE);
+    if (flashAvailable.count == 1 && flashAvailable.data.u8[0] == 1) {
+        *hasFlash = true;
+    }
+
+    return OK;
+}
+
+status_t CameraDeviceClientFlashControl::submitTorchRequest(bool enabled) {
+    status_t res;
+
+    if (mMetadata == NULL) {
+        mMetadata = new CameraMetadata();
+        if (mMetadata == NULL) {
+            return NO_MEMORY;
+        }
+        res = mDevice->createDefaultRequest(
+                CAMERA3_TEMPLATE_PREVIEW, mMetadata);
+        if (res) {
+            return res;
+        }
+    }
+
+    uint8_t torchOn = enabled ? ANDROID_FLASH_MODE_TORCH :
+                                ANDROID_FLASH_MODE_OFF;
+
+    mMetadata->update(ANDROID_FLASH_MODE, &torchOn, 1);
+    mMetadata->update(ANDROID_REQUEST_OUTPUT_STREAMS, &mStreamId, 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);
+
+    return res;
+}
+
+
+status_t CameraDeviceClientFlashControl::setTorchMode(
+        const String16& cameraId, bool enabled) {
+    bool hasFlash = false;
+
+    Mutex::Autolock l(mLock);
+    status_t res = hasFlashUnitLocked(cameraId, &hasFlash);
+
+    // pre-check
+    if (enabled) {
+        // invalid camera?
+        if (res) {
+            return -EINVAL;
+        }
+        // no flash unit?
+        if (!hasFlash) {
+            return -ENOSYS;
+        }
+        // already opened for a different device?
+        if (mDevice != NULL && cameraId != mCameraId) {
+            return BAD_INDEX;
+        }
+    } else if (mDevice == NULL || cameraId != mCameraId) {
+        // disabling the torch mode of an un-opened or different device.
+        return OK;
+    }
+
+    if (mDevice == NULL) {
+        res = connectCameraDevice(cameraId);
+        if (res) {
+            return res;
+        }
+    }
+
+    res = submitTorchRequest(enabled);
+    if (res) {
+        return res;
+    }
+
+    mTorchEnabled = enabled;
+    return OK;
+}
+
+}
diff --git a/services/camera/libcameraservice/CameraFlashlight.h b/services/camera/libcameraservice/CameraFlashlight.h
new file mode 100644
index 0000000..a0de0b0
--- /dev/null
+++ b/services/camera/libcameraservice/CameraFlashlight.h
@@ -0,0 +1,149 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef ANDROID_SERVERS_CAMERA_CAMERAFLASHLIGHT_H
+#define ANDROID_SERVERS_CAMERA_CAMERAFLASHLIGHT_H
+
+#include "hardware/camera_common.h"
+#include "utils/KeyedVector.h"
+#include "gui/GLConsumer.h"
+#include "gui/Surface.h"
+#include "common/CameraDeviceBase.h"
+
+namespace android {
+
+/**
+ * FlashControlBase is a base class for flash control. It defines the functions
+ * that a flash control for each camera module/device version should implement.
+ */
+class FlashControlBase : public virtual VirtualLightRefBase {
+    public:
+        virtual ~FlashControlBase();
+
+        // Whether a camera device has a flash unit. Calling this function may
+        // 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,
+                    bool *hasFlash) = 0;
+
+        // set the torch mode to on or off.
+        virtual status_t setTorchMode(const String16& cameraId,
+                    bool enabled) = 0;
+};
+
+/**
+ * CameraFlashlight can be used by camera service to control flashflight.
+ */
+class CameraFlashlight : public virtual VirtualLightRefBase {
+    public:
+        CameraFlashlight(CameraModule& cameraModule,
+                const camera_module_callbacks_t& callbacks);
+        virtual ~CameraFlashlight();
+
+        // set the torch mode to on or off.
+        status_t setTorchMode(const String16& cameraId, bool enabled);
+
+        // 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);
+
+        // 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();
+
+    private:
+        // create flashlight control based on camera module API and camera
+        // device API versions.
+        status_t createFlashlightControl(const String16& cameraId);
+
+        sp<FlashControlBase> mFlashControl;
+        CameraModule *mCameraModule;
+        const camera_module_callbacks_t *mCallbacks;
+
+        Mutex mLock;
+};
+
+/**
+ * Flash control for camera module v2.4 and above.
+ */
+class FlashControl : public FlashControlBase {
+    public:
+        FlashControl(CameraModule& cameraModule,
+                const camera_module_callbacks_t& callbacks);
+        virtual ~FlashControl();
+
+        // FlashControlBase
+        status_t hasFlashUnit(const String16& cameraId, bool *hasFlash);
+        status_t setTorchMode(const String16& cameraId, bool enabled);
+
+    private:
+        CameraModule *mCameraModule;
+
+        Mutex mLock;
+};
+
+/**
+ * Flash control for camera module <= v2.3 and camera HAL v2-v3
+ */
+class CameraDeviceClientFlashControl : public FlashControlBase {
+    public:
+        CameraDeviceClientFlashControl(CameraModule& cameraModule,
+                const camera_module_callbacks_t& callbacks);
+        virtual ~CameraDeviceClientFlashControl();
+
+        // FlashControlBase
+        status_t setTorchMode(const String16& cameraId, bool enabled);
+        status_t hasFlashUnit(const String16& cameraId, bool *hasFlash);
+
+    private:
+        // connect to a camera device
+        status_t connectCameraDevice(const String16& cameraId);
+
+        // initialize a surface
+        status_t initializeSurface(int32_t width, int32_t height);
+
+        // submit a request with the given torch mode
+        status_t submitTorchRequest(bool enabled);
+
+        // 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);
+
+        CameraModule *mCameraModule;
+        const camera_module_callbacks_t *mCallbacks;
+        String16 mCameraId;
+        bool mTorchEnabled;
+        CameraMetadata *mMetadata;
+
+        sp<CameraDeviceBase> mDevice;
+
+        sp<IGraphicBufferProducer> mProducer;
+        sp<IGraphicBufferConsumer>  mConsumer;
+        sp<GLConsumer> mSurfaceTexture;
+        sp<ANativeWindow> mAnw;
+        int32_t mStreamId;
+
+        Mutex mLock;
+};
+
+} // namespace android
+
+#endif
diff --git a/services/camera/libcameraservice/CameraService.cpp b/services/camera/libcameraservice/CameraService.cpp
index 485b979..488fc42 100644
--- a/services/camera/libcameraservice/CameraService.cpp
+++ b/services/camera/libcameraservice/CameraService.cpp
@@ -86,6 +86,38 @@
         camera_id,
         new_status);
 }
+
+static void torch_mode_status_change(
+        const struct camera_module_callbacks* callbacks,
+        const char* camera_id,
+        int new_status) {
+    if (!callbacks || !camera_id) {
+        ALOGE("%s invalid parameters. callbacks %p, camera_id %p", __FUNCTION__,
+                callbacks, camera_id);
+    }
+    sp<CameraService> cs = const_cast<CameraService*>(
+                                static_cast<const CameraService*>(callbacks));
+
+    ICameraServiceListener::TorchStatus status;
+    switch (new_status) {
+        case TORCH_MODE_STATUS_AVAILABLE:
+            status = ICameraServiceListener::TORCH_STATUS_AVAILABLE;
+            break;
+        case TORCH_MODE_STATUS_RESOURCE_BUSY:
+            status = ICameraServiceListener::TORCH_STATUS_NOT_AVAILABLE;
+            break;
+        case TORCH_MODE_STATUS_OFF:
+            status = ICameraServiceListener::TORCH_STATUS_OFF;
+            break;
+        default:
+            ALOGE("Unknown torch status %d", new_status);
+            return;
+    }
+
+    cs->onTorchStatusChanged(
+        String16(camera_id),
+        status);
+}
 } // extern "C"
 
 // ----------------------------------------------------------------------------
@@ -95,7 +127,7 @@
 static CameraService *gCameraService;
 
 CameraService::CameraService()
-    :mSoundRef(0), mModule(0)
+    :mSoundRef(0), mModule(0), mFlashlight(0)
 {
     ALOGI("CameraService started (pid=%d)", getpid());
     gCameraService = this;
@@ -105,6 +137,8 @@
     }
 
     this->camera_device_status_change = android::camera_device_status_change;
+    this->torch_mode_status_change = android::torch_mode_status_change;
+
 }
 
 void CameraService::onFirstRef()
@@ -121,6 +155,8 @@
     }
     else {
         mModule = new CameraModule(rawModule);
+        mFlashlight = new CameraFlashlight(*mModule, *this);
+
         const hw_module_t *common = mModule->getRawModule();
         ALOGI("Loaded \"%s\" camera module", common->name);
         mNumberOfCameras = mModule->getNumberOfCameras();
@@ -131,6 +167,12 @@
         }
         for (int i = 0; i < mNumberOfCameras; i++) {
             setCameraFree(i);
+
+            String16 cameraName = String16(String8::format("%d", i));
+            if (mFlashlight->hasFlashUnit(cameraName)) {
+                mTorchStatusMap.add(cameraName,
+                        ICameraServiceListener::TORCH_STATUS_AVAILABLE);
+            }
         }
 
         if (common->module_api_version >= CAMERA_MODULE_API_VERSION_2_1) {
@@ -225,6 +267,37 @@
 
 }
 
+void CameraService::onTorchStatusChanged(const String16& cameraId,
+        ICameraServiceListener::TorchStatus newStatus) {
+    Mutex::Autolock al(mTorchStatusMutex);
+    onTorchStatusChangedLocked(cameraId, newStatus);
+}
+
+void CameraService::onTorchStatusChangedLocked(const String16& cameraId,
+        ICameraServiceListener::TorchStatus newStatus) {
+    ALOGI("%s: Torch status changed for cameraId=%s, newStatus=%d",
+            __FUNCTION__, cameraId.string(), newStatus);
+
+    if (getTorchStatusLocked(cameraId) == 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);
+    if (res) {
+        ALOGE("%s: Failed to set the torch status", __FUNCTION__,
+                (uint32_t)newStatus);
+        return;
+    }
+
+    Vector<sp<ICameraServiceListener> >::const_iterator it;
+    for (it = mListenerList.begin(); it != mListenerList.end(); ++it) {
+        (*it)->onTorchStatusChanged(newStatus, cameraId);
+    }
+}
+
+
 int32_t CameraService::getNumberOfCameras() {
     return mNumberOfCameras;
 }
@@ -676,6 +749,9 @@
         int halVersion,
         bool legacyMode) {
 
+    // give flashlight a chance to close devices if necessary.
+    mFlashlight->prepareDeviceOpen();
+
     int facing = -1;
     int deviceVersion = getDeviceVersion(cameraId, &facing);
 
@@ -852,6 +928,47 @@
     return OK;
 }
 
+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;
+    }
+
+    Mutex::Autolock al(mTorchStatusMutex);
+    status_t res = mFlashlight->setTorchMode(cameraId, enabled);
+    if (res) {
+        ALOGE("%s: setting torch mode of camera %s to %d failed", __FUNCTION__,
+                cameraId.string(), enabled);
+        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);
+            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;
+}
+
 status_t CameraService::connectFinishUnsafe(const sp<BasicClient>& client,
                                             const sp<IBinder>& remoteCallback) {
     status_t status = client->initialize(mModule);
@@ -977,6 +1094,9 @@
         int facing = -1;
         int deviceVersion = getDeviceVersion(cameraId, &facing);
 
+        // give flashlight a chance to close devices if necessary.
+        mFlashlight->prepareDeviceOpen();
+
         switch(deviceVersion) {
           case CAMERA_DEVICE_API_VERSION_1_0:
             ALOGW("Camera using old HAL version: %d", deviceVersion);
@@ -1048,6 +1168,16 @@
         }
     }
 
+    /* Immediately signal current torch status to this listener only */
+    {
+        Mutex::Autolock al(mTorchStatusMutex);
+        for (size_t i = 0; i < mTorchStatusMap.size(); i++ ) {
+            listener->onTorchStatusChanged(mTorchStatusMap.valueAt(i),
+                    mTorchStatusMap.keyAt(i));
+        }
+
+    }
+
     return OK;
 }
 status_t CameraService::removeListener(
@@ -1727,6 +1857,23 @@
     return NO_ERROR;
 }
 
+void CameraService::handleTorchClientBinderDied(const wp<IBinder> &who) {
+    Mutex::Autolock al(mTorchStatusMutex);
+    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);
+            mTorchClientMap.removeItemsAt(i);
+
+            // notify torch mode was turned off
+            onTorchStatusChangedLocked(cameraId,
+                    ICameraServiceListener::TORCH_STATUS_OFF);
+            break;
+        }
+    }
+}
+
 /*virtual*/void CameraService::binderDied(
     const wp<IBinder> &who) {
 
@@ -1737,6 +1884,10 @@
 
     ALOGV("java clients' binder died");
 
+    // check torch client
+    handleTorchClientBinderDied(who);
+
+    // check camera device client
     sp<BasicClient> cameraClient = getClientByRemote(who);
 
     if (cameraClient == 0) {
@@ -1830,4 +1981,27 @@
     return mStatusList[cameraId];
 }
 
+ICameraServiceListener::TorchStatus CameraService::getTorchStatusLocked(
+        const String16& cameraId) const {
+    ssize_t index = mTorchStatusMap.indexOfKey(cameraId);
+    if (index == NAME_NOT_FOUND) {
+        return ICameraServiceListener::TORCH_STATUS_NOT_AVAILABLE;
+    }
+
+    return mTorchStatusMap.valueAt(index);
+}
+
+status_t CameraService::setTorchStatusLocked(const String16& cameraId,
+        ICameraServiceListener::TorchStatus status) {
+    ssize_t index = mTorchStatusMap.indexOfKey(cameraId);
+    if (index == NAME_NOT_FOUND) {
+        return BAD_VALUE;
+    }
+    ICameraServiceListener::TorchStatus& item =
+            mTorchStatusMap.editValueAt(index);
+    item = status;
+
+    return OK;
+}
+
 }; // namespace android
diff --git a/services/camera/libcameraservice/CameraService.h b/services/camera/libcameraservice/CameraService.h
index 7d0df3a..84bcdb8 100644
--- a/services/camera/libcameraservice/CameraService.h
+++ b/services/camera/libcameraservice/CameraService.h
@@ -36,6 +36,8 @@
 #include <camera/CameraParameters.h>
 
 #include <camera/ICameraServiceListener.h>
+#include "CameraFlashlight.h"
+
 
 #include "common/CameraModule.h"
 
@@ -70,6 +72,9 @@
     // HAL Callbacks
     virtual void        onDeviceStatusChanged(int cameraId,
                                               int newStatus);
+    virtual void        onTorchStatusChanged(const String16& cameraId,
+                                             ICameraServiceListener::TorchStatus
+                                                   newStatus);
 
     /////////////////////////////////////////////////////////////////////
     // ICameraService
@@ -112,6 +117,9 @@
             /*out*/
             String16* parameters);
 
+    virtual status_t    setTorchMode(const String16& cameraId, bool enabled,
+            const sp<IBinder>& clientBinder);
+
     // OK = supports api of that version, -EOPNOTSUPP = does not support
     virtual status_t    supportsCameraApi(
             int cameraId, int apiVersion);
@@ -408,6 +416,32 @@
                             int32_t cameraId,
                             const StatusVector *rejectSourceStates = NULL);
 
+    // flashlight control
+    sp<CameraFlashlight> mFlashlight;
+    // guard mTorchStatusMap and mTorchClientMap
+    Mutex                mTorchStatusMutex;
+    // camera id -> torch status
+    KeyedVector<String16, 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;
+
+    // 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,
+            ICameraServiceListener::TorchStatus newStatus);
+
+    // get a camera's torch status. mTorchStatusMutex should be locked.
+    ICameraServiceListener::TorchStatus getTorchStatusLocked(
+            const String16 &cameraId) const;
+
+    // set a camera's torch status. mTorchStatusMutex should be locked.
+    status_t setTorchStatusLocked(const String16 &cameraId,
+            ICameraServiceListener::TorchStatus status);
+
     // IBinder::DeathRecipient implementation
     virtual void        binderDied(const wp<IBinder> &who);