Merge "Remove PCM16 assumption from FastCapture"
diff --git a/include/camera/ICameraService.h b/include/camera/ICameraService.h
index cc41efe..194a646 100644
--- a/include/camera/ICameraService.h
+++ b/include/camera/ICameraService.h
@@ -145,7 +145,16 @@
             sp<ICamera>& device) = 0;
 
     /**
-     * Turn on or off a camera's torch mode.
+     * Turn on or off a camera's torch mode. Torch mode will be turned off by
+     * camera service if the lastest client binder that turns it on dies.
+     *
+     * return values:
+     * 0:       on a successful operation.
+     * -ENOSYS: the camera device doesn't support this operation. It it returned
+     *          if and only if android.flash.into.available is false.
+     * -EBUSY:  the camera device is opened.
+     * -EINVAL: camera_id is invalid or clientBinder is NULL when enabling a
+     *          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 9e8b912..709ff31 100644
--- a/include/camera/ICameraServiceListener.h
+++ b/include/camera/ICameraServiceListener.h
@@ -71,18 +71,20 @@
      *
      * Initial status will be transmitted with onTorchStatusChanged immediately
      * after this listener is added to the service listener list.
+     *
+     * The enums should be set to values matching
+     * include/hardware/camera_common.h
      */
     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,
+        TORCH_STATUS_NOT_AVAILABLE  = TORCH_MODE_STATUS_NOT_AVAILABLE,
+        // The camera's torch mode is off and available to be turned on via
+        // setTorchMode().
+        TORCH_STATUS_AVAILABLE_OFF  = TORCH_MODE_STATUS_AVAILABLE_OFF,
+        // The camera's torch mode is on and available to be turned off via
+        // setTorchMode().
+        TORCH_STATUS_AVAILABLE_ON   = TORCH_MODE_STATUS_AVAILABLE_ON,
 
         // Use to initialize variables only
         TORCH_STATUS_UNKNOWN        = 0xFFFFFFFF,
diff --git a/include/media/MediaPlayerInterface.h b/include/media/MediaPlayerInterface.h
index 482b85f..d6fe390 100644
--- a/include/media/MediaPlayerInterface.h
+++ b/include/media/MediaPlayerInterface.h
@@ -113,7 +113,19 @@
                 const audio_offload_info_t *offloadInfo = NULL) = 0;
 
         virtual status_t    start() = 0;
-        virtual ssize_t     write(const void* buffer, size_t size) = 0;
+
+        /* Input parameter |size| is in byte units stored in |buffer|.
+         * Data is copied over and actual number of bytes written (>= 0)
+         * is returned, or no data is copied and a negative status code
+         * is returned (even when |blocking| is true).
+         * When |blocking| is false, AudioSink will immediately return after
+         * part of or full |buffer| is copied over.
+         * When |blocking| is true, AudioSink will wait to copy the entire
+         * buffer, unless an error occurs or the copy operation is
+         * prematurely stopped.
+         */
+        virtual ssize_t     write(const void* buffer, size_t size, bool blocking = true) = 0;
+
         virtual void        stop() = 0;
         virtual void        flush() = 0;
         virtual void        pause() = 0;
diff --git a/media/libmediaplayerservice/MediaPlayerService.cpp b/media/libmediaplayerservice/MediaPlayerService.cpp
index 0b18ae0..5e5d099 100644
--- a/media/libmediaplayerservice/MediaPlayerService.cpp
+++ b/media/libmediaplayerservice/MediaPlayerService.cpp
@@ -1672,13 +1672,13 @@
     }
 }
 
-ssize_t MediaPlayerService::AudioOutput::write(const void* buffer, size_t size)
+ssize_t MediaPlayerService::AudioOutput::write(const void* buffer, size_t size, bool blocking)
 {
     LOG_ALWAYS_FATAL_IF(mCallback != NULL, "Don't call write if supplying a callback.");
 
     //ALOGV("write(%p, %u)", buffer, size);
     if (mTrack != 0) {
-        ssize_t ret = mTrack->write(buffer, size);
+        ssize_t ret = mTrack->write(buffer, size, blocking);
         if (ret >= 0) {
             mBytesWritten += ret;
         }
diff --git a/media/libmediaplayerservice/MediaPlayerService.h b/media/libmediaplayerservice/MediaPlayerService.h
index 7320311..4ce4b81 100644
--- a/media/libmediaplayerservice/MediaPlayerService.h
+++ b/media/libmediaplayerservice/MediaPlayerService.h
@@ -97,7 +97,7 @@
                 const audio_offload_info_t *offloadInfo = NULL);
 
         virtual status_t        start();
-        virtual ssize_t         write(const void* buffer, size_t size);
+        virtual ssize_t         write(const void* buffer, size_t size, bool blocking = true);
         virtual void            stop();
         virtual void            flush();
         virtual void            pause();
diff --git a/media/libmediaplayerservice/nuplayer/NuPlayerRenderer.cpp b/media/libmediaplayerservice/nuplayer/NuPlayerRenderer.cpp
index d21884b..e491a31 100644
--- a/media/libmediaplayerservice/nuplayer/NuPlayerRenderer.cpp
+++ b/media/libmediaplayerservice/nuplayer/NuPlayerRenderer.cpp
@@ -567,6 +567,7 @@
 }
 
 bool NuPlayer::Renderer::onDrainAudioQueue() {
+#if 0
     uint32_t numFramesPlayed;
     if (mAudioSink->getPosition(&numFramesPlayed) != OK) {
         return false;
@@ -575,7 +576,6 @@
     ssize_t numFramesAvailableToWrite =
         mAudioSink->frameCount() - (mNumFramesWritten - numFramesPlayed);
 
-#if 0
     if (numFramesAvailableToWrite == mAudioSink->frameCount()) {
         ALOGI("audio sink underrun");
     } else {
@@ -584,10 +584,7 @@
     }
 #endif
 
-    size_t numBytesAvailableToWrite =
-        numFramesAvailableToWrite * mAudioSink->frameSize();
-
-    while (numBytesAvailableToWrite > 0 && !mAudioQueue.empty()) {
+    while (!mAudioQueue.empty()) {
         QueueEntry *entry = &*mAudioQueue.begin();
 
         mLastAudioBufferDrained = entry->mBufferOrdinal;
@@ -620,14 +617,16 @@
         }
 
         size_t copy = entry->mBuffer->size() - entry->mOffset;
-        if (copy > numBytesAvailableToWrite) {
-            copy = numBytesAvailableToWrite;
-        }
 
-        ssize_t written = mAudioSink->write(entry->mBuffer->data() + entry->mOffset, copy);
+        ssize_t written = mAudioSink->write(entry->mBuffer->data() + entry->mOffset,
+                                            copy, false /* blocking */);
         if (written < 0) {
             // An error in AudioSink write. Perhaps the AudioSink was not properly opened.
-            ALOGE("AudioSink write error(%zd) when writing %zu bytes", written, copy);
+            if (written == WOULD_BLOCK) {
+                ALOGW("AudioSink write would block when writing %zu bytes", copy);
+            } else {
+                ALOGE("AudioSink write error(%zd) when writing %zu bytes", written, copy);
+            }
             break;
         }
 
@@ -639,7 +638,6 @@
             entry = NULL;
         }
 
-        numBytesAvailableToWrite -= written;
         size_t copiedFrames = written / mAudioSink->frameSize();
         mNumFramesWritten += copiedFrames;
 
@@ -651,21 +649,23 @@
         if (written != (ssize_t)copy) {
             // A short count was received from AudioSink::write()
             //
-            // AudioSink write should block until exactly the number of bytes are delivered.
-            // But it may return with a short count (without an error) when:
+            // AudioSink write is called in non-blocking mode.
+            // It may return with a short count when:
             //
             // 1) Size to be copied is not a multiple of the frame size. We consider this fatal.
-            // 2) AudioSink is an AudioCache for data retrieval, and the AudioCache is exceeded.
+            // 2) The data to be copied exceeds the available buffer in AudioSink.
+            // 3) An error occurs and data has been partially copied to the buffer in AudioSink.
+            // 4) AudioSink is an AudioCache for data retrieval, and the AudioCache is exceeded.
 
             // (Case 1)
             // Must be a multiple of the frame size.  If it is not a multiple of a frame size, it
             // needs to fail, as we should not carry over fractional frames between calls.
             CHECK_EQ(copy % mAudioSink->frameSize(), 0);
 
-            // (Case 2)
+            // (Case 2, 3, 4)
             // Return early to the caller.
             // Beware of calling immediately again as this may busy-loop if you are not careful.
-            ALOGW("AudioSink write short frame count %zd < %zu", written, copy);
+            ALOGV("AudioSink write short frame count %zd < %zu", written, copy);
             break;
         }
     }
diff --git a/media/libstagefright/mpeg2ts/ATSParser.cpp b/media/libstagefright/mpeg2ts/ATSParser.cpp
index 482ccff..934e2e5 100644
--- a/media/libstagefright/mpeg2ts/ATSParser.cpp
+++ b/media/libstagefright/mpeg2ts/ATSParser.cpp
@@ -92,8 +92,10 @@
     KeyedVector<unsigned, sp<Stream> > mStreams;
     bool mFirstPTSValid;
     uint64_t mFirstPTS;
+    int64_t mLastRecoveredPTS;
 
     status_t parseProgramMap(ABitReader *br);
+    int64_t recoverPTS(uint64_t PTS_33bit);
 
     DISALLOW_EVIL_CONSTRUCTORS(Program);
 };
@@ -182,7 +184,8 @@
       mProgramNumber(programNumber),
       mProgramMapPID(programMapPID),
       mFirstPTSValid(false),
-      mFirstPTS(0) {
+      mFirstPTS(0),
+      mLastRecoveredPTS(0) {
     ALOGV("new program number %u", programNumber);
 }
 
@@ -425,6 +428,21 @@
     return OK;
 }
 
+int64_t ATSParser::Program::recoverPTS(uint64_t PTS_33bit) {
+    // We only have the lower 33-bit of the PTS. It could overflow within a
+    // reasonable amount of time. To handle the wrap-around, use fancy math
+    // to get an extended PTS that is within [-0xffffffff, 0xffffffff]
+    // of the latest recovered PTS.
+    mLastRecoveredPTS = static_cast<int64_t>(
+            ((mLastRecoveredPTS - PTS_33bit + 0x100000000ll)
+            & 0xfffffffe00000000ull) | PTS_33bit);
+
+    // We start from 0, but recovered PTS could be slightly below 0.
+    // Clamp it to 0 as rest of the pipeline doesn't take negative pts.
+    // (eg. video is read first and starts at 0, but audio starts at 0xfffffff0)
+    return mLastRecoveredPTS < 0ll ? 0ll : mLastRecoveredPTS;
+}
+
 sp<MediaSource> ATSParser::Program::getSource(SourceType type) {
     size_t index = (type == AUDIO) ? 0 : 0;
 
@@ -455,6 +473,8 @@
 }
 
 int64_t ATSParser::Program::convertPTSToTimestamp(uint64_t PTS) {
+    PTS = recoverPTS(PTS);
+
     if (!(mParser->mFlags & TS_TIMESTAMPS_ARE_ABSOLUTE)) {
         if (!mFirstPTSValid) {
             mFirstPTSValid = true;
diff --git a/services/camera/libcameraservice/CameraFlashlight.cpp b/services/camera/libcameraservice/CameraFlashlight.cpp
index 00a70eb..8e894cd 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,18 +88,19 @@
             }
 
             mFlashControl = flashControl;
-        }
-        else {
-            // todo: implement for device api 1
-            return INVALID_OPERATION;
+        } else {
+            mFlashControl =
+                    new CameraHardwareInterfaceFlashControl(*mCameraModule,
+                                                            *mCallbacks);
         }
     }
 
     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 +111,10 @@
     Mutex::Autolock l(mLock);
 
     if (mFlashControl == NULL) {
+        if (enabled == false) {
+            return OK;
+        }
+
         res = createFlashlightControl(cameraId);
         if (res) {
             return res;
@@ -130,88 +140,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 +319,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 +358,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 +384,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 +420,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 +452,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 +477,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 +511,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 +536,7 @@
     return OK;
 }
 
-status_t CameraDeviceClientFlashControl::submitTorchRequest(bool enabled) {
+status_t CameraDeviceClientFlashControl::submitTorchEnabledRequest() {
     status_t res;
 
     if (mMetadata == NULL) {
@@ -456,27 +551,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 +596,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 +612,263 @@
         }
     }
 
-    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
+
+
+/////////////////////////////////////////////////////////////////////
+// CameraHardwareInterfaceFlashControl implementation begins
+// Flash control for camera module <= v2.3 and camera HAL v1
+/////////////////////////////////////////////////////////////////////
+CameraHardwareInterfaceFlashControl::CameraHardwareInterfaceFlashControl(
+        CameraModule& cameraModule,
+        const camera_module_callbacks_t& callbacks) :
+        mCameraModule(&cameraModule),
+        mCallbacks(&callbacks),
+        mTorchEnabled(false) {
+
+}
+
+CameraHardwareInterfaceFlashControl::~CameraHardwareInterfaceFlashControl() {
+    disconnectCameraDevice();
+
+    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,
+                    mCameraId.string(), TORCH_MODE_STATUS_AVAILABLE_OFF);
+        }
+    }
+}
+
+status_t CameraHardwareInterfaceFlashControl::setTorchMode(
+        const String8& cameraId, bool enabled) {
+    Mutex::Autolock l(mLock);
+
+    // pre-check
+    status_t res;
+    if (enabled) {
+        bool hasFlash = false;
+        res = hasFlashUnitLocked(cameraId, &hasFlash);
+        // invalid camera?
+        if (res) {
+            // hasFlashUnitLocked() returns BAD_INDEX if mDevice is connected to
+            // another camera device.
+            return res == BAD_INDEX ? BAD_INDEX : -EINVAL;
+        }
+        // no flash unit?
+        if (!hasFlash) {
+            return -ENOSYS;
+        }
+    } 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;
+    }
+
+    res = startPreviewAndTorch();
+    if (res) {
+        return res;
+    }
+
+    mTorchEnabled = true;
+    mCallbacks->torch_mode_status_change(mCallbacks,
+            cameraId.string(), TORCH_MODE_STATUS_AVAILABLE_ON);
+    return OK;
+}
+
+status_t CameraHardwareInterfaceFlashControl::hasFlashUnit(
+        const String8& cameraId, bool *hasFlash) {
+    Mutex::Autolock l(mLock);
+    return hasFlashUnitLocked(cameraId, hasFlash);
+}
+
+status_t CameraHardwareInterfaceFlashControl::hasFlashUnitLocked(
+        const String8& cameraId, bool *hasFlash) {
+    if (!hasFlash) {
+        return BAD_VALUE;
+    }
+
+    status_t res;
+    if (mDevice == NULL) {
+        res = connectCameraDevice(cameraId);
+        if (res) {
+            return res;
+        }
+    }
+
+    if (cameraId != mCameraId) {
+        return BAD_INDEX;
+    }
+
+    const char *flashMode =
+            mParameters.get(CameraParameters::KEY_SUPPORTED_FLASH_MODES);
+    if (flashMode && strstr(flashMode, CameraParameters::FLASH_MODE_TORCH)) {
+        *hasFlash = true;
+    } else {
+        *hasFlash = false;
+    }
+
+    return OK;
+}
+
+status_t CameraHardwareInterfaceFlashControl::startPreviewAndTorch() {
+    status_t res = OK;
+    res = mDevice->startPreview();
+    if (res) {
+        ALOGE("%s: start preview failed. %s (%d)", __FUNCTION__,
+                strerror(-res), res);
+        return res;
+    }
+
+    mParameters.set(CameraParameters::KEY_FLASH_MODE,
+            CameraParameters::FLASH_MODE_TORCH);
+
+    return mDevice->setParameters(mParameters);
+}
+
+status_t CameraHardwareInterfaceFlashControl::getSmallestSurfaceSize(
+        int32_t *width, int32_t *height) {
+    if (!width || !height) {
+        return BAD_VALUE;
+    }
+
+    int32_t w = INT32_MAX;
+    int32_t h = 1;
+    Vector<Size> sizes;
+
+    mParameters.getSupportedPreviewSizes(sizes);
+    for (size_t i = 0; i < sizes.size(); i++) {
+        Size s = sizes[i];
+        if (w * h > s.width * s.height) {
+            w = s.width;
+            h = s.height;
+        }
+    }
+
+    if (w == INT32_MAX) {
+        return NAME_NOT_FOUND;
+    }
+
+    *width = w;
+    *height = h;
+
+    return OK;
+}
+
+status_t CameraHardwareInterfaceFlashControl::initializePreviewWindow(
+        sp<CameraHardwareInterface> device, 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;
+    }
+
+    mAnw = new Surface(mProducer, /*useAsync*/ true);
+    if (mAnw == NULL) {
+        return NO_MEMORY;
+    }
+
+    res = native_window_api_connect(mAnw.get(), NATIVE_WINDOW_API_CAMERA);
+    if (res) {
+        ALOGE("%s: Unable to connect to native window", __FUNCTION__);
+        return res;
+    }
+
+    return device->setPreviewWindow(mAnw);
+}
+
+status_t CameraHardwareInterfaceFlashControl::connectCameraDevice(
+        const String8& cameraId) {
+    sp<CameraHardwareInterface> device =
+            new CameraHardwareInterface(cameraId.string());
+
+    status_t res = device->initialize(mCameraModule);
+    if (res) {
+        ALOGE("%s: initializing camera %s failed", __FUNCTION__,
+                cameraId.string());
+        return res;
+    }
+
+    // need to set __get_memory in set_callbacks().
+    device->setCallbacks(NULL, NULL, NULL, NULL);
+
+    mParameters = device->getParameters();
+
+    int32_t width, height;
+    res = getSmallestSurfaceSize(&width, &height);
+    if (res) {
+        ALOGE("%s: failed to get smallest surface size for camera %s",
+                __FUNCTION__, cameraId.string());
+        return res;
+    }
+
+    res = initializePreviewWindow(device, width, height);
+    if (res) {
+        ALOGE("%s: failed to initialize preview window for camera %s",
+                __FUNCTION__, cameraId.string());
+        return res;
+    }
+
+    mCameraId = cameraId;
+    mDevice = device;
+    return OK;
+}
+
+status_t CameraHardwareInterfaceFlashControl::disconnectCameraDevice() {
+    if (mDevice == NULL) {
+        return OK;
+    }
+
+    mParameters.set(CameraParameters::KEY_FLASH_MODE,
+            CameraParameters::FLASH_MODE_OFF);
+    mDevice->setParameters(mParameters);
+    mDevice->stopPreview();
+    status_t res = native_window_api_disconnect(mAnw.get(),
+            NATIVE_WINDOW_API_CAMERA);
+    if (res) {
+        ALOGW("%s: native_window_api_disconnect failed: %s (%d)",
+                __FUNCTION__, strerror(-res), res);
+    }
+    mDevice->setPreviewWindow(NULL);
+    mDevice->release();
+
+    return OK;
+}
+// CameraHardwareInterfaceFlashControl implementation ends
 
 }
diff --git a/services/camera/libcameraservice/CameraFlashlight.h b/services/camera/libcameraservice/CameraFlashlight.h
index a0de0b0..30f01f0 100644
--- a/services/camera/libcameraservice/CameraFlashlight.h
+++ b/services/camera/libcameraservice/CameraFlashlight.h
@@ -19,9 +19,11 @@
 
 #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"
+#include "device1/CameraHardwareInterface.h"
 
 namespace android {
 
@@ -37,11 +39,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 +56,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 +128,38 @@
         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);
+        // protected by mLock
+        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;
 
@@ -144,6 +172,54 @@
         Mutex mLock;
 };
 
+/**
+ * Flash control for camera module <= v2.3 and camera HAL v1
+ */
+class CameraHardwareInterfaceFlashControl : public FlashControlBase {
+    public:
+        CameraHardwareInterfaceFlashControl(CameraModule& cameraModule,
+                const camera_module_callbacks_t& callbacks);
+        virtual ~CameraHardwareInterfaceFlashControl();
+
+        // FlashControlBase
+        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 String8& cameraId);
+
+        // disconnect and free mDevice
+        status_t disconnectCameraDevice();
+
+        // initialize the preview window
+        status_t initializePreviewWindow(sp<CameraHardwareInterface> device,
+                int32_t width, int32_t height);
+
+        // start preview and enable torch
+        status_t startPreviewAndTorch();
+
+        // get the smallest surface
+        status_t getSmallestSurfaceSize(int32_t *width, int32_t *height);
+
+        // protected by mLock
+        status_t hasFlashUnitLocked(const String8& cameraId, bool *hasFlash);
+
+        CameraModule *mCameraModule;
+        const camera_module_callbacks_t *mCallbacks;
+        sp<CameraHardwareInterface> mDevice;
+        String8 mCameraId;
+        CameraParameters mParameters;
+        bool mTorchEnabled;
+
+        sp<IGraphicBufferProducer> mProducer;
+        sp<IGraphicBufferConsumer>  mConsumer;
+        sp<GLConsumer> mSurfaceTexture;
+        sp<ANativeWindow> mAnw;
+
+        Mutex mLock;
+};
+
 } // namespace android
 
 #endif
diff --git a/services/camera/libcameraservice/CameraService.cpp b/services/camera/libcameraservice/CameraService.cpp
index d65ac21..6f37f16 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()));
     }
 }
 
@@ -484,19 +496,6 @@
     return deviceVersion;
 }
 
-status_t CameraService::filterOpenErrorCode(status_t err) {
-    switch(err) {
-        case NO_ERROR:
-        case -EBUSY:
-        case -EINVAL:
-        case -EUSERS:
-            return err;
-        default:
-            break;
-    }
-    return -ENODEV;
-}
-
 status_t CameraService::filterGetInfoErrorCode(status_t err) {
     switch(err) {
         case NO_ERROR:
@@ -754,7 +753,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 +931,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 +1148,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 +1225,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 +1665,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 +1914,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 +2021,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 +2051,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..22afc8c 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);
 
@@ -150,7 +150,6 @@
 
     /////////////////////////////////////////////////////////////////////
     // Shared utilities
-    static status_t     filterOpenErrorCode(status_t err);
     static status_t     filterGetInfoErrorCode(status_t err);
 
     /////////////////////////////////////////////////////////////////////
@@ -418,28 +417,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
diff --git a/services/camera/libcameraservice/common/CameraModule.cpp b/services/camera/libcameraservice/common/CameraModule.cpp
index 85a4df9..5f767ad 100644
--- a/services/camera/libcameraservice/common/CameraModule.cpp
+++ b/services/camera/libcameraservice/common/CameraModule.cpp
@@ -91,7 +91,7 @@
 }
 
 int CameraModule::open(const char* id, struct hw_device_t** device) {
-    return mModule->common.methods->open(&mModule->common, id, device);
+    return filterOpenErrorCode(mModule->common.methods->open(&mModule->common, id, device));
 }
 
 int CameraModule::openLegacy(
@@ -125,5 +125,20 @@
     return mModule->set_torch_mode(camera_id, enable);
 }
 
+
+status_t CameraModule::filterOpenErrorCode(status_t err) {
+    switch(err) {
+        case NO_ERROR:
+        case -EBUSY:
+        case -EINVAL:
+        case -EUSERS:
+            return err;
+        default:
+            break;
+    }
+    return -ENODEV;
+}
+
+
 }; // namespace android
 
diff --git a/services/camera/libcameraservice/common/CameraModule.h b/services/camera/libcameraservice/common/CameraModule.h
index 31b9ae2..16207aa 100644
--- a/services/camera/libcameraservice/common/CameraModule.h
+++ b/services/camera/libcameraservice/common/CameraModule.h
@@ -50,6 +50,8 @@
 private:
     // Derive camera characteristics keys defined after HAL device version
     static void deriveCameraCharacteristicsKeys(uint32_t deviceVersion, CameraMetadata &chars);
+    status_t filterOpenErrorCode(status_t err);
+
     camera_module_t *mModule;
     CameraMetadata mCameraCharacteristics[MAX_CAMERAS_PER_MODULE];
     camera_info mCameraInfo[MAX_CAMERAS_PER_MODULE];
diff --git a/services/camera/libcameraservice/device1/CameraHardwareInterface.h b/services/camera/libcameraservice/device1/CameraHardwareInterface.h
index 9e1cdc9..f5ebbf8 100644
--- a/services/camera/libcameraservice/device1/CameraHardwareInterface.h
+++ b/services/camera/libcameraservice/device1/CameraHardwareInterface.h
@@ -104,8 +104,7 @@
                                      CAMERA_DEVICE_API_VERSION_1_0,
                                      (hw_device_t **)&mDevice);
         } else {
-            rc = CameraService::filterOpenErrorCode(module->open(
-                    mName.string(), (hw_device_t **)&mDevice));
+            rc = module->open(mName.string(), (hw_device_t **)&mDevice);
         }
         if (rc != OK) {
             ALOGE("Could not open camera %s: %d", mName.string(), rc);
diff --git a/services/camera/libcameraservice/device2/Camera2Device.cpp b/services/camera/libcameraservice/device2/Camera2Device.cpp
index be66c4d..43c8307 100644
--- a/services/camera/libcameraservice/device2/Camera2Device.cpp
+++ b/services/camera/libcameraservice/device2/Camera2Device.cpp
@@ -68,8 +68,7 @@
 
     camera2_device_t *device;
 
-    res = CameraService::filterOpenErrorCode(module->open(
-            name, reinterpret_cast<hw_device_t**>(&device)));
+    res = module->open(name, reinterpret_cast<hw_device_t**>(&device));
 
     if (res != OK) {
         ALOGE("%s: Could not open camera %d: %s (%d)", __FUNCTION__,
diff --git a/services/camera/libcameraservice/device3/Camera3Device.cpp b/services/camera/libcameraservice/device3/Camera3Device.cpp
index 9a4e5ac..bca9bfd 100644
--- a/services/camera/libcameraservice/device3/Camera3Device.cpp
+++ b/services/camera/libcameraservice/device3/Camera3Device.cpp
@@ -106,8 +106,8 @@
     camera3_device_t *device;
 
     ATRACE_BEGIN("camera3->open");
-    res = CameraService::filterOpenErrorCode(module->open(
-            deviceName.string(), reinterpret_cast<hw_device_t**>(&device)));
+    res = module->open(deviceName.string(),
+            reinterpret_cast<hw_device_t**>(&device));
     ATRACE_END();
 
     if (res != OK) {