| Chien-Yu Chen | 3068d73 | 2015-02-09 13:29:57 -0800 | [diff] [blame] | 1 | /* | 
 | 2 |  * Copyright (C) 2015 The Android Open Source Project | 
 | 3 |  * | 
 | 4 |  * Licensed under the Apache License, Version 2.0 (the "License"); | 
 | 5 |  * you may not use this file except in compliance with the License. | 
 | 6 |  * You may obtain a copy of the License at | 
 | 7 |  * | 
 | 8 |  *      http://www.apache.org/licenses/LICENSE-2.0 | 
 | 9 |  * | 
 | 10 |  * Unless required by applicable law or agreed to in writing, software | 
 | 11 |  * distributed under the License is distributed on an "AS IS" BASIS, | 
 | 12 |  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | 
 | 13 |  * See the License for the specific language governing permissions and | 
 | 14 |  * limitations under the License. | 
 | 15 |  */ | 
 | 16 |  | 
 | 17 | #define LOG_TAG "CameraFlashlight" | 
 | 18 | #define ATRACE_TAG ATRACE_TAG_CAMERA | 
| Chien-Yu Chen | 88da526 | 2015-02-17 13:56:46 -0800 | [diff] [blame] | 19 | // #define LOG_NDEBUG 0 | 
| Chien-Yu Chen | 3068d73 | 2015-02-09 13:29:57 -0800 | [diff] [blame] | 20 |  | 
 | 21 | #include <utils/Log.h> | 
 | 22 | #include <utils/Trace.h> | 
 | 23 | #include <cutils/properties.h> | 
 | 24 |  | 
 | 25 | #include "camera/CameraMetadata.h" | 
 | 26 | #include "CameraFlashlight.h" | 
 | 27 | #include "gui/IGraphicBufferConsumer.h" | 
 | 28 | #include "gui/BufferQueue.h" | 
 | 29 | #include "camera/camera2/CaptureRequest.h" | 
| Eino-Ville Talvala | d309fb9 | 2015-11-25 12:12:45 -0800 | [diff] [blame] | 30 | #include "device3/Camera3Device.h" | 
| Chien-Yu Chen | 3068d73 | 2015-02-09 13:29:57 -0800 | [diff] [blame] | 31 |  | 
 | 32 |  | 
 | 33 | namespace android { | 
 | 34 |  | 
| Yin-Chia Yeh | c3e9d6f | 2018-02-06 10:56:32 -0800 | [diff] [blame] | 35 | using hardware::camera::common::V1_0::TorchModeStatus; | 
 | 36 |  | 
| Chien-Yu Chen | 88da526 | 2015-02-17 13:56:46 -0800 | [diff] [blame] | 37 | ///////////////////////////////////////////////////////////////////// | 
 | 38 | // CameraFlashlight implementation begins | 
 | 39 | // used by camera service to control flashflight. | 
 | 40 | ///////////////////////////////////////////////////////////////////// | 
| Eino-Ville Talvala | 2f09bac | 2016-12-13 11:29:54 -0800 | [diff] [blame] | 41 |  | 
 | 42 | CameraFlashlight::CameraFlashlight(sp<CameraProviderManager> providerManager, | 
| Yin-Chia Yeh | c3e9d6f | 2018-02-06 10:56:32 -0800 | [diff] [blame] | 43 |         CameraProviderManager::StatusListener* callbacks) : | 
| Eino-Ville Talvala | 2f09bac | 2016-12-13 11:29:54 -0800 | [diff] [blame] | 44 |         mProviderManager(providerManager), | 
 | 45 |         mCallbacks(callbacks), | 
| Chien-Yu Chen | 88da526 | 2015-02-17 13:56:46 -0800 | [diff] [blame] | 46 |         mFlashlightMapInitialized(false) { | 
| Chien-Yu Chen | 3068d73 | 2015-02-09 13:29:57 -0800 | [diff] [blame] | 47 | } | 
 | 48 |  | 
 | 49 | CameraFlashlight::~CameraFlashlight() { | 
 | 50 | } | 
 | 51 |  | 
| Chien-Yu Chen | 88da526 | 2015-02-17 13:56:46 -0800 | [diff] [blame] | 52 | status_t CameraFlashlight::createFlashlightControl(const String8& cameraId) { | 
| Chien-Yu Chen | 3068d73 | 2015-02-09 13:29:57 -0800 | [diff] [blame] | 53 |     ALOGV("%s: creating a flash light control for camera %s", __FUNCTION__, | 
 | 54 |             cameraId.string()); | 
 | 55 |     if (mFlashControl != NULL) { | 
 | 56 |         return INVALID_OPERATION; | 
 | 57 |     } | 
 | 58 |  | 
| Emilian Peev | f53f66e | 2017-04-11 14:29:43 +0100 | [diff] [blame] | 59 |     if (mProviderManager->supportSetTorchMode(cameraId.string())) { | 
 | 60 |         mFlashControl = new ProviderFlashControl(mProviderManager); | 
| Chien-Yu Chen | 3068d73 | 2015-02-09 13:29:57 -0800 | [diff] [blame] | 61 |     } else { | 
| Eino-Ville Talvala | a976df8 | 2019-06-13 18:01:58 -0700 | [diff] [blame] | 62 |         ALOGE("Flashlight control not supported by this device!"); | 
 | 63 |         return NO_INIT; | 
| Chien-Yu Chen | 3068d73 | 2015-02-09 13:29:57 -0800 | [diff] [blame] | 64 |     } | 
 | 65 |  | 
 | 66 |     return OK; | 
 | 67 | } | 
 | 68 |  | 
| Chien-Yu Chen | 88da526 | 2015-02-17 13:56:46 -0800 | [diff] [blame] | 69 | status_t CameraFlashlight::setTorchMode(const String8& cameraId, bool enabled) { | 
 | 70 |     if (!mFlashlightMapInitialized) { | 
| Ranjith Kagathi Ananda | 32ab9fd | 2015-10-08 16:41:09 -0700 | [diff] [blame] | 71 |         ALOGE("%s: findFlashUnits() must be called before this method.", | 
 | 72 |                __FUNCTION__); | 
| Chien-Yu Chen | 3068d73 | 2015-02-09 13:29:57 -0800 | [diff] [blame] | 73 |         return NO_INIT; | 
 | 74 |     } | 
 | 75 |  | 
 | 76 |     ALOGV("%s: set torch mode of camera %s to %d", __FUNCTION__, | 
 | 77 |             cameraId.string(), enabled); | 
 | 78 |  | 
 | 79 |     status_t res = OK; | 
 | 80 |     Mutex::Autolock l(mLock); | 
 | 81 |  | 
| Ruben Brunk | cc77671 | 2015-02-17 20:18:47 -0800 | [diff] [blame] | 82 |     if (mOpenedCameraIds.indexOf(cameraId) != NAME_NOT_FOUND) { | 
 | 83 |         // This case is needed to avoid state corruption during the following call sequence: | 
 | 84 |         // CameraService::setTorchMode for camera ID 0 begins, does torch status checks | 
 | 85 |         // CameraService::connect for camera ID 0 begins, calls prepareDeviceOpen, ends | 
 | 86 |         // CameraService::setTorchMode for camera ID 0 continues, calls | 
 | 87 |         //        CameraFlashlight::setTorchMode | 
 | 88 |  | 
 | 89 |         // TODO: Move torch status checks and state updates behind this CameraFlashlight lock | 
 | 90 |         // to avoid other similar race conditions. | 
 | 91 |         ALOGE("%s: Camera device %s is in use, cannot set torch mode.", | 
 | 92 |                 __FUNCTION__, cameraId.string()); | 
 | 93 |         return -EBUSY; | 
 | 94 |     } | 
 | 95 |  | 
| Chien-Yu Chen | 3068d73 | 2015-02-09 13:29:57 -0800 | [diff] [blame] | 96 |     if (mFlashControl == NULL) { | 
 | 97 |         res = createFlashlightControl(cameraId); | 
 | 98 |         if (res) { | 
 | 99 |             return res; | 
 | 100 |         } | 
 | 101 |         res =  mFlashControl->setTorchMode(cameraId, enabled); | 
 | 102 |         return res; | 
 | 103 |     } | 
 | 104 |  | 
 | 105 |     // if flash control already exists, turning on torch mode may fail if it's | 
 | 106 |     // tied to another camera device for module v2.3 and below. | 
 | 107 |     res = mFlashControl->setTorchMode(cameraId, enabled); | 
 | 108 |     if (res == BAD_INDEX) { | 
 | 109 |         // flash control is tied to another camera device, need to close it and | 
 | 110 |         // try again. | 
 | 111 |         mFlashControl.clear(); | 
 | 112 |         res = createFlashlightControl(cameraId); | 
 | 113 |         if (res) { | 
 | 114 |             return res; | 
 | 115 |         } | 
 | 116 |         res = mFlashControl->setTorchMode(cameraId, enabled); | 
 | 117 |     } | 
 | 118 |  | 
 | 119 |     return res; | 
 | 120 | } | 
 | 121 |  | 
| Chien-Yu Chen | 88da526 | 2015-02-17 13:56:46 -0800 | [diff] [blame] | 122 | status_t CameraFlashlight::findFlashUnits() { | 
 | 123 |     Mutex::Autolock l(mLock); | 
 | 124 |     status_t res; | 
| Eino-Ville Talvala | 2f09bac | 2016-12-13 11:29:54 -0800 | [diff] [blame] | 125 |  | 
 | 126 |     std::vector<String8> cameraIds; | 
| Yin-Chia Yeh | 98c72a8 | 2018-08-28 11:20:16 -0700 | [diff] [blame] | 127 |     std::vector<std::string> ids = mProviderManager->getCameraDeviceIds(); | 
| Yin-Chia Yeh | 6b1f611 | 2018-02-28 13:05:59 -0800 | [diff] [blame] | 128 |     int numberOfCameras = static_cast<int>(ids.size()); | 
| Yin-Chia Yeh | dc3134e | 2017-03-23 15:26:59 -0700 | [diff] [blame] | 129 |     cameraIds.resize(numberOfCameras); | 
| Emilian Peev | f53f66e | 2017-04-11 14:29:43 +0100 | [diff] [blame] | 130 |     // No module, must be provider | 
| Emilian Peev | f53f66e | 2017-04-11 14:29:43 +0100 | [diff] [blame] | 131 |     for (size_t i = 0; i < cameraIds.size(); i++) { | 
 | 132 |         cameraIds[i] = String8(ids[i].c_str()); | 
| Eino-Ville Talvala | 2f09bac | 2016-12-13 11:29:54 -0800 | [diff] [blame] | 133 |     } | 
| Chien-Yu Chen | 88da526 | 2015-02-17 13:56:46 -0800 | [diff] [blame] | 134 |  | 
| Emilian Peev | aee727d | 2017-05-04 16:35:48 +0100 | [diff] [blame] | 135 |     mFlashControl.clear(); | 
| Chien-Yu Chen | 88da526 | 2015-02-17 13:56:46 -0800 | [diff] [blame] | 136 |  | 
| Eino-Ville Talvala | 2f09bac | 2016-12-13 11:29:54 -0800 | [diff] [blame] | 137 |     for (auto &id : cameraIds) { | 
| Emilian Peev | aee727d | 2017-05-04 16:35:48 +0100 | [diff] [blame] | 138 |         ssize_t index = mHasFlashlightMap.indexOfKey(id); | 
 | 139 |         if (0 <= index) { | 
 | 140 |             continue; | 
 | 141 |         } | 
 | 142 |  | 
| Chien-Yu Chen | 88da526 | 2015-02-17 13:56:46 -0800 | [diff] [blame] | 143 |         bool hasFlash = false; | 
| Chien-Yu Chen | 88da526 | 2015-02-17 13:56:46 -0800 | [diff] [blame] | 144 |         res = createFlashlightControl(id); | 
 | 145 |         if (res) { | 
 | 146 |             ALOGE("%s: failed to create flash control for %s", __FUNCTION__, | 
 | 147 |                     id.string()); | 
 | 148 |         } else { | 
 | 149 |             res = mFlashControl->hasFlashUnit(id, &hasFlash); | 
 | 150 |             if (res == -EUSERS || res == -EBUSY) { | 
 | 151 |                 ALOGE("%s: failed to check if camera %s has a flash unit. Some " | 
 | 152 |                         "camera devices may be opened", __FUNCTION__, | 
 | 153 |                         id.string()); | 
 | 154 |                 return res; | 
 | 155 |             } else if (res) { | 
 | 156 |                 ALOGE("%s: failed to check if camera %s has a flash unit. %s" | 
 | 157 |                         " (%d)", __FUNCTION__, id.string(), strerror(-res), | 
 | 158 |                         res); | 
 | 159 |             } | 
 | 160 |  | 
 | 161 |             mFlashControl.clear(); | 
 | 162 |         } | 
 | 163 |         mHasFlashlightMap.add(id, hasFlash); | 
 | 164 |     } | 
 | 165 |  | 
 | 166 |     mFlashlightMapInitialized = true; | 
 | 167 |     return OK; | 
 | 168 | } | 
 | 169 |  | 
 | 170 | bool CameraFlashlight::hasFlashUnit(const String8& cameraId) { | 
| Chien-Yu Chen | 3068d73 | 2015-02-09 13:29:57 -0800 | [diff] [blame] | 171 |     Mutex::Autolock l(mLock); | 
| Chien-Yu Chen | 88da526 | 2015-02-17 13:56:46 -0800 | [diff] [blame] | 172 |     return hasFlashUnitLocked(cameraId); | 
| Chien-Yu Chen | 3068d73 | 2015-02-09 13:29:57 -0800 | [diff] [blame] | 173 | } | 
 | 174 |  | 
| Chien-Yu Chen | 88da526 | 2015-02-17 13:56:46 -0800 | [diff] [blame] | 175 | bool CameraFlashlight::hasFlashUnitLocked(const String8& cameraId) { | 
 | 176 |     if (!mFlashlightMapInitialized) { | 
| Ranjith Kagathi Ananda | 32ab9fd | 2015-10-08 16:41:09 -0700 | [diff] [blame] | 177 |         ALOGE("%s: findFlashUnits() must be called before this method.", | 
 | 178 |                __FUNCTION__); | 
| Chien-Yu Chen | 88da526 | 2015-02-17 13:56:46 -0800 | [diff] [blame] | 179 |         return false; | 
 | 180 |     } | 
 | 181 |  | 
 | 182 |     ssize_t index = mHasFlashlightMap.indexOfKey(cameraId); | 
 | 183 |     if (index == NAME_NOT_FOUND) { | 
| Yin-Chia Yeh | 6b1f611 | 2018-02-28 13:05:59 -0800 | [diff] [blame] | 184 |         // Might be external camera | 
 | 185 |         ALOGW("%s: camera %s not present when findFlashUnits() was called", | 
| Chien-Yu Chen | 88da526 | 2015-02-17 13:56:46 -0800 | [diff] [blame] | 186 |                 __FUNCTION__, cameraId.string()); | 
 | 187 |         return false; | 
 | 188 |     } | 
 | 189 |  | 
 | 190 |     return mHasFlashlightMap.valueAt(index); | 
 | 191 | } | 
 | 192 |  | 
| Yin-Chia Yeh | dc3134e | 2017-03-23 15:26:59 -0700 | [diff] [blame] | 193 | bool CameraFlashlight::isBackwardCompatibleMode(const String8& cameraId) { | 
 | 194 |     bool backwardCompatibleMode = false; | 
| Emilian Peev | f53f66e | 2017-04-11 14:29:43 +0100 | [diff] [blame] | 195 |     if (mProviderManager != nullptr && | 
| Yin-Chia Yeh | dc3134e | 2017-03-23 15:26:59 -0700 | [diff] [blame] | 196 |             !mProviderManager->supportSetTorchMode(cameraId.string())) { | 
 | 197 |         backwardCompatibleMode = true; | 
 | 198 |     } | 
 | 199 |     return backwardCompatibleMode; | 
 | 200 | } | 
 | 201 |  | 
| Chien-Yu Chen | 88da526 | 2015-02-17 13:56:46 -0800 | [diff] [blame] | 202 | status_t CameraFlashlight::prepareDeviceOpen(const String8& cameraId) { | 
| Chien-Yu Chen | 3068d73 | 2015-02-09 13:29:57 -0800 | [diff] [blame] | 203 |     ALOGV("%s: prepare for device open", __FUNCTION__); | 
 | 204 |  | 
 | 205 |     Mutex::Autolock l(mLock); | 
| Chien-Yu Chen | 88da526 | 2015-02-17 13:56:46 -0800 | [diff] [blame] | 206 |     if (!mFlashlightMapInitialized) { | 
| Ranjith Kagathi Ananda | 32ab9fd | 2015-10-08 16:41:09 -0700 | [diff] [blame] | 207 |         ALOGE("%s: findFlashUnits() must be called before this method.", | 
 | 208 |                __FUNCTION__); | 
| Chien-Yu Chen | 88da526 | 2015-02-17 13:56:46 -0800 | [diff] [blame] | 209 |         return NO_INIT; | 
 | 210 |     } | 
| Chien-Yu Chen | 3068d73 | 2015-02-09 13:29:57 -0800 | [diff] [blame] | 211 |  | 
| Yin-Chia Yeh | dc3134e | 2017-03-23 15:26:59 -0700 | [diff] [blame] | 212 |     if (isBackwardCompatibleMode(cameraId)) { | 
| Chien-Yu Chen | 3068d73 | 2015-02-09 13:29:57 -0800 | [diff] [blame] | 213 |         // framework is going to open a camera device, all flash light control | 
 | 214 |         // should be closed for backward compatible support. | 
| Chien-Yu Chen | 88da526 | 2015-02-17 13:56:46 -0800 | [diff] [blame] | 215 |         mFlashControl.clear(); | 
 | 216 |  | 
 | 217 |         if (mOpenedCameraIds.size() == 0) { | 
 | 218 |             // notify torch unavailable for all cameras with a flash | 
| Yin-Chia Yeh | 98c72a8 | 2018-08-28 11:20:16 -0700 | [diff] [blame] | 219 |             std::vector<std::string> ids = mProviderManager->getCameraDeviceIds(); | 
| Yin-Chia Yeh | 6b1f611 | 2018-02-28 13:05:59 -0800 | [diff] [blame] | 220 |             int numCameras = static_cast<int>(ids.size()); | 
| Chien-Yu Chen | 88da526 | 2015-02-17 13:56:46 -0800 | [diff] [blame] | 221 |             for (int i = 0; i < numCameras; i++) { | 
| Yin-Chia Yeh | 6b1f611 | 2018-02-28 13:05:59 -0800 | [diff] [blame] | 222 |                 String8 id8(ids[i].c_str()); | 
 | 223 |                 if (hasFlashUnitLocked(id8)) { | 
| Yin-Chia Yeh | c3e9d6f | 2018-02-06 10:56:32 -0800 | [diff] [blame] | 224 |                     mCallbacks->onTorchStatusChanged( | 
| Yin-Chia Yeh | 6b1f611 | 2018-02-28 13:05:59 -0800 | [diff] [blame] | 225 |                             id8, TorchModeStatus::NOT_AVAILABLE); | 
| Chien-Yu Chen | 88da526 | 2015-02-17 13:56:46 -0800 | [diff] [blame] | 226 |                 } | 
 | 227 |             } | 
| Chien-Yu Chen | 3068d73 | 2015-02-09 13:29:57 -0800 | [diff] [blame] | 228 |         } | 
| Chien-Yu Chen | 88da526 | 2015-02-17 13:56:46 -0800 | [diff] [blame] | 229 |  | 
 | 230 |         // close flash control that may be opened by calling hasFlashUnitLocked. | 
 | 231 |         mFlashControl.clear(); | 
 | 232 |     } | 
 | 233 |  | 
 | 234 |     if (mOpenedCameraIds.indexOf(cameraId) == NAME_NOT_FOUND) { | 
 | 235 |         mOpenedCameraIds.add(cameraId); | 
| Chien-Yu Chen | 3068d73 | 2015-02-09 13:29:57 -0800 | [diff] [blame] | 236 |     } | 
 | 237 |  | 
 | 238 |     return OK; | 
 | 239 | } | 
 | 240 |  | 
| Chien-Yu Chen | 88da526 | 2015-02-17 13:56:46 -0800 | [diff] [blame] | 241 | status_t CameraFlashlight::deviceClosed(const String8& cameraId) { | 
 | 242 |     ALOGV("%s: device %s is closed", __FUNCTION__, cameraId.string()); | 
 | 243 |  | 
 | 244 |     Mutex::Autolock l(mLock); | 
 | 245 |     if (!mFlashlightMapInitialized) { | 
| Ranjith Kagathi Ananda | 32ab9fd | 2015-10-08 16:41:09 -0700 | [diff] [blame] | 246 |         ALOGE("%s: findFlashUnits() must be called before this method.", | 
 | 247 |                __FUNCTION__); | 
| Chien-Yu Chen | 88da526 | 2015-02-17 13:56:46 -0800 | [diff] [blame] | 248 |         return NO_INIT; | 
 | 249 |     } | 
 | 250 |  | 
 | 251 |     ssize_t index = mOpenedCameraIds.indexOf(cameraId); | 
 | 252 |     if (index == NAME_NOT_FOUND) { | 
 | 253 |         ALOGE("%s: couldn't find camera %s in the opened list", __FUNCTION__, | 
 | 254 |                 cameraId.string()); | 
 | 255 |     } else { | 
 | 256 |         mOpenedCameraIds.removeAt(index); | 
 | 257 |     } | 
 | 258 |  | 
 | 259 |     // Cannot do anything until all cameras are closed. | 
 | 260 |     if (mOpenedCameraIds.size() != 0) | 
 | 261 |         return OK; | 
 | 262 |  | 
| Yin-Chia Yeh | dc3134e | 2017-03-23 15:26:59 -0700 | [diff] [blame] | 263 |     if (isBackwardCompatibleMode(cameraId)) { | 
| Chien-Yu Chen | 88da526 | 2015-02-17 13:56:46 -0800 | [diff] [blame] | 264 |         // notify torch available for all cameras with a flash | 
| Yin-Chia Yeh | 98c72a8 | 2018-08-28 11:20:16 -0700 | [diff] [blame] | 265 |         std::vector<std::string> ids = mProviderManager->getCameraDeviceIds(); | 
| Yin-Chia Yeh | 6b1f611 | 2018-02-28 13:05:59 -0800 | [diff] [blame] | 266 |         int numCameras = static_cast<int>(ids.size()); | 
| Chien-Yu Chen | 88da526 | 2015-02-17 13:56:46 -0800 | [diff] [blame] | 267 |         for (int i = 0; i < numCameras; i++) { | 
| Yin-Chia Yeh | 6b1f611 | 2018-02-28 13:05:59 -0800 | [diff] [blame] | 268 |             String8 id8(ids[i].c_str()); | 
 | 269 |             if (hasFlashUnitLocked(id8)) { | 
| Yin-Chia Yeh | c3e9d6f | 2018-02-06 10:56:32 -0800 | [diff] [blame] | 270 |                 mCallbacks->onTorchStatusChanged( | 
| Yin-Chia Yeh | 6b1f611 | 2018-02-28 13:05:59 -0800 | [diff] [blame] | 271 |                         id8, TorchModeStatus::AVAILABLE_OFF); | 
| Chien-Yu Chen | 88da526 | 2015-02-17 13:56:46 -0800 | [diff] [blame] | 272 |             } | 
 | 273 |         } | 
 | 274 |     } | 
 | 275 |  | 
 | 276 |     return OK; | 
 | 277 | } | 
 | 278 | // CameraFlashlight implementation ends | 
 | 279 |  | 
| Chien-Yu Chen | 3068d73 | 2015-02-09 13:29:57 -0800 | [diff] [blame] | 280 |  | 
 | 281 | FlashControlBase::~FlashControlBase() { | 
 | 282 | } | 
 | 283 |  | 
| Chien-Yu Chen | 88da526 | 2015-02-17 13:56:46 -0800 | [diff] [blame] | 284 | ///////////////////////////////////////////////////////////////////// | 
 | 285 | // ModuleFlashControl implementation begins | 
 | 286 | // Flash control for camera module v2.4 and above. | 
 | 287 | ///////////////////////////////////////////////////////////////////// | 
| Eino-Ville Talvala | 2f09bac | 2016-12-13 11:29:54 -0800 | [diff] [blame] | 288 | ProviderFlashControl::ProviderFlashControl(sp<CameraProviderManager> providerManager) : | 
 | 289 |         mProviderManager(providerManager) { | 
 | 290 | } | 
 | 291 |  | 
 | 292 | ProviderFlashControl::~ProviderFlashControl() { | 
 | 293 | } | 
 | 294 |  | 
 | 295 | status_t ProviderFlashControl::hasFlashUnit(const String8& cameraId, bool *hasFlash) { | 
 | 296 |     if (!hasFlash) { | 
 | 297 |         return BAD_VALUE; | 
 | 298 |     } | 
 | 299 |     *hasFlash = mProviderManager->hasFlashUnit(cameraId.string()); | 
 | 300 |     return OK; | 
 | 301 | } | 
 | 302 |  | 
 | 303 | status_t ProviderFlashControl::setTorchMode(const String8& cameraId, bool enabled) { | 
 | 304 |     ALOGV("%s: set camera %s torch mode to %d", __FUNCTION__, | 
 | 305 |             cameraId.string(), enabled); | 
 | 306 |  | 
 | 307 |     return mProviderManager->setTorchMode(cameraId.string(), enabled); | 
 | 308 | } | 
 | 309 | // ProviderFlashControl implementation ends | 
 | 310 |  | 
| Chien-Yu Chen | 3068d73 | 2015-02-09 13:29:57 -0800 | [diff] [blame] | 311 | } |