| Igor Murashkin | c073ba5 | 2013-02-26 14:32:34 -0800 | [diff] [blame] | 1 | /* | 
|  | 2 | ** | 
|  | 3 | ** Copyright (C) 2013, The Android Open Source Project | 
|  | 4 | ** | 
|  | 5 | ** Licensed under the Apache License, Version 2.0 (the "License"); | 
|  | 6 | ** you may not use this file except in compliance with the License. | 
|  | 7 | ** You may obtain a copy of the License at | 
|  | 8 | ** | 
|  | 9 | **     http://www.apache.org/licenses/LICENSE-2.0 | 
|  | 10 | ** | 
|  | 11 | ** Unless required by applicable law or agreed to in writing, software | 
|  | 12 | ** distributed under the License is distributed on an "AS IS" BASIS, | 
|  | 13 | ** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | 
|  | 14 | ** See the License for the specific language governing permissions and | 
|  | 15 | ** limitations under the License. | 
|  | 16 | */ | 
|  | 17 |  | 
|  | 18 | //#define LOG_NDEBUG 0 | 
|  | 19 | #define LOG_TAG "CameraBase" | 
|  | 20 | #include <utils/Log.h> | 
|  | 21 | #include <utils/threads.h> | 
|  | 22 | #include <utils/Mutex.h> | 
| Ivan Podogov | ee844a8 | 2016-09-15 11:32:41 +0100 | [diff] [blame] | 23 | #include <cutils/properties.h> | 
| Igor Murashkin | c073ba5 | 2013-02-26 14:32:34 -0800 | [diff] [blame] | 24 |  | 
| Eino-Ville Talvala | d56db1d | 2015-12-17 16:50:35 -0800 | [diff] [blame] | 25 | #include <android/hardware/ICameraService.h> | 
| Austin Borger | 3560b7e | 2022-10-27 12:20:29 -0700 | [diff] [blame] | 26 | #include <com/android/internal/compat/IPlatformCompatNative.h> | 
| Eino-Ville Talvala | d56db1d | 2015-12-17 16:50:35 -0800 | [diff] [blame] | 27 |  | 
| Igor Murashkin | c073ba5 | 2013-02-26 14:32:34 -0800 | [diff] [blame] | 28 | #include <binder/IPCThreadState.h> | 
|  | 29 | #include <binder/IServiceManager.h> | 
|  | 30 | #include <binder/IMemory.h> | 
|  | 31 |  | 
|  | 32 | #include <camera/CameraBase.h> | 
| Jayant Chowdhary | f5b9cc6 | 2020-09-08 16:25:17 -0700 | [diff] [blame] | 33 | #include <camera/CameraUtils.h> | 
| Austin Borger | 0fb3ad9 | 2023-06-01 16:51:35 -0700 | [diff] [blame] | 34 | #include <camera/StringUtils.h> | 
| Igor Murashkin | c073ba5 | 2013-02-26 14:32:34 -0800 | [diff] [blame] | 35 |  | 
|  | 36 | // needed to instantiate | 
| Igor Murashkin | c073ba5 | 2013-02-26 14:32:34 -0800 | [diff] [blame] | 37 | #include <camera/Camera.h> | 
|  | 38 |  | 
|  | 39 | #include <system/camera_metadata.h> | 
|  | 40 |  | 
|  | 41 | namespace android { | 
|  | 42 |  | 
| Eino-Ville Talvala | d56db1d | 2015-12-17 16:50:35 -0800 | [diff] [blame] | 43 | namespace hardware { | 
|  | 44 |  | 
| Eino-Ville Talvala | f51fca2 | 2016-12-13 11:25:55 -0800 | [diff] [blame] | 45 | status_t CameraInfo::writeToParcel(android::Parcel* parcel) const { | 
| Eino-Ville Talvala | d56db1d | 2015-12-17 16:50:35 -0800 | [diff] [blame] | 46 | status_t res; | 
|  | 47 | res = parcel->writeInt32(facing); | 
|  | 48 | if (res != OK) return res; | 
|  | 49 | res = parcel->writeInt32(orientation); | 
|  | 50 | return res; | 
|  | 51 | } | 
|  | 52 |  | 
| Eino-Ville Talvala | f51fca2 | 2016-12-13 11:25:55 -0800 | [diff] [blame] | 53 | status_t CameraInfo::readFromParcel(const android::Parcel* parcel) { | 
| Eino-Ville Talvala | d56db1d | 2015-12-17 16:50:35 -0800 | [diff] [blame] | 54 | status_t res; | 
|  | 55 | res = parcel->readInt32(&facing); | 
|  | 56 | if (res != OK) return res; | 
|  | 57 | res = parcel->readInt32(&orientation); | 
|  | 58 | return res; | 
|  | 59 | } | 
|  | 60 |  | 
| Eino-Ville Talvala | f51fca2 | 2016-12-13 11:25:55 -0800 | [diff] [blame] | 61 | status_t CameraStatus::writeToParcel(android::Parcel* parcel) const { | 
| Austin Borger | 0fb3ad9 | 2023-06-01 16:51:35 -0700 | [diff] [blame] | 62 | auto res = parcel->writeString16(toString16(cameraId)); | 
| Eino-Ville Talvala | f51fca2 | 2016-12-13 11:25:55 -0800 | [diff] [blame] | 63 | if (res != OK) return res; | 
| Oleksiy Vyalov | ead8472 | 2017-03-24 14:06:03 -0700 | [diff] [blame] | 64 |  | 
| Eino-Ville Talvala | f51fca2 | 2016-12-13 11:25:55 -0800 | [diff] [blame] | 65 | res = parcel->writeInt32(status); | 
| Shuzhen Wang | 4385816 | 2020-01-10 13:42:15 -0800 | [diff] [blame] | 66 | if (res != OK) return res; | 
|  | 67 |  | 
|  | 68 | std::vector<String16> unavailablePhysicalIds16; | 
|  | 69 | for (auto& id8 : unavailablePhysicalIds) { | 
| Austin Borger | 0fb3ad9 | 2023-06-01 16:51:35 -0700 | [diff] [blame] | 70 | unavailablePhysicalIds16.push_back(toString16(id8)); | 
| Shuzhen Wang | 4385816 | 2020-01-10 13:42:15 -0800 | [diff] [blame] | 71 | } | 
|  | 72 | res = parcel->writeString16Vector(unavailablePhysicalIds16); | 
| Shuzhen Wang | e7aa034 | 2021-08-03 11:29:47 -0700 | [diff] [blame] | 73 | if (res != OK) return res; | 
|  | 74 |  | 
| Austin Borger | 0fb3ad9 | 2023-06-01 16:51:35 -0700 | [diff] [blame] | 75 | res = parcel->writeString16(toString16(clientPackage)); | 
| Eino-Ville Talvala | f51fca2 | 2016-12-13 11:25:55 -0800 | [diff] [blame] | 76 | return res; | 
| Eino-Ville Talvala | d56db1d | 2015-12-17 16:50:35 -0800 | [diff] [blame] | 77 | } | 
|  | 78 |  | 
| Eino-Ville Talvala | f51fca2 | 2016-12-13 11:25:55 -0800 | [diff] [blame] | 79 | status_t CameraStatus::readFromParcel(const android::Parcel* parcel) { | 
| Oleksiy Vyalov | ead8472 | 2017-03-24 14:06:03 -0700 | [diff] [blame] | 80 | String16 tempCameraId; | 
|  | 81 | auto res = parcel->readString16(&tempCameraId); | 
| Eino-Ville Talvala | f51fca2 | 2016-12-13 11:25:55 -0800 | [diff] [blame] | 82 | if (res != OK) return res; | 
| Austin Borger | 0fb3ad9 | 2023-06-01 16:51:35 -0700 | [diff] [blame] | 83 | cameraId = toString8(tempCameraId); | 
| Oleksiy Vyalov | ead8472 | 2017-03-24 14:06:03 -0700 | [diff] [blame] | 84 |  | 
| Eino-Ville Talvala | f51fca2 | 2016-12-13 11:25:55 -0800 | [diff] [blame] | 85 | res = parcel->readInt32(&status); | 
| Shuzhen Wang | 4385816 | 2020-01-10 13:42:15 -0800 | [diff] [blame] | 86 | if (res != OK) return res; | 
|  | 87 |  | 
|  | 88 | std::vector<String16> unavailablePhysicalIds16; | 
|  | 89 | res = parcel->readString16Vector(&unavailablePhysicalIds16); | 
|  | 90 | if (res != OK) return res; | 
|  | 91 | for (auto& id16 : unavailablePhysicalIds16) { | 
| Austin Borger | 0fb3ad9 | 2023-06-01 16:51:35 -0700 | [diff] [blame] | 92 | unavailablePhysicalIds.push_back(toStdString(id16)); | 
| Shuzhen Wang | 4385816 | 2020-01-10 13:42:15 -0800 | [diff] [blame] | 93 | } | 
| Shuzhen Wang | e7aa034 | 2021-08-03 11:29:47 -0700 | [diff] [blame] | 94 |  | 
|  | 95 | String16 tempClientPackage; | 
|  | 96 | res = parcel->readString16(&tempClientPackage); | 
|  | 97 | if (res != OK) return res; | 
| Austin Borger | 0fb3ad9 | 2023-06-01 16:51:35 -0700 | [diff] [blame] | 98 | clientPackage = toStdString(tempClientPackage); | 
| Shuzhen Wang | e7aa034 | 2021-08-03 11:29:47 -0700 | [diff] [blame] | 99 |  | 
| Eino-Ville Talvala | f51fca2 | 2016-12-13 11:25:55 -0800 | [diff] [blame] | 100 | return res; | 
|  | 101 | } | 
|  | 102 |  | 
|  | 103 | } // namespace hardware | 
|  | 104 |  | 
| Igor Murashkin | c073ba5 | 2013-02-26 14:32:34 -0800 | [diff] [blame] | 105 | namespace { | 
| Eino-Ville Talvala | d56db1d | 2015-12-17 16:50:35 -0800 | [diff] [blame] | 106 | sp<::android::hardware::ICameraService> gCameraService; | 
| Igor Murashkin | c073ba5 | 2013-02-26 14:32:34 -0800 | [diff] [blame] | 107 | const int                 kCameraServicePollDelay = 500000; // 0.5s | 
|  | 108 | const char*               kCameraServiceName      = "media.camera"; | 
|  | 109 |  | 
|  | 110 | Mutex                     gLock; | 
|  | 111 |  | 
|  | 112 | class DeathNotifier : public IBinder::DeathRecipient | 
|  | 113 | { | 
|  | 114 | public: | 
|  | 115 | DeathNotifier() { | 
|  | 116 | } | 
|  | 117 |  | 
| Mark Salyzyn | 7b73e71 | 2014-06-09 16:28:21 -0700 | [diff] [blame] | 118 | virtual void binderDied(const wp<IBinder>& /*who*/) { | 
| Igor Murashkin | c073ba5 | 2013-02-26 14:32:34 -0800 | [diff] [blame] | 119 | ALOGV("binderDied"); | 
|  | 120 | Mutex::Autolock _l(gLock); | 
|  | 121 | gCameraService.clear(); | 
|  | 122 | ALOGW("Camera service died!"); | 
|  | 123 | } | 
|  | 124 | }; | 
|  | 125 |  | 
|  | 126 | sp<DeathNotifier>         gDeathNotifier; | 
|  | 127 | }; // namespace anonymous | 
|  | 128 |  | 
|  | 129 | /////////////////////////////////////////////////////////// | 
|  | 130 | // CameraBase definition | 
|  | 131 | /////////////////////////////////////////////////////////// | 
|  | 132 |  | 
|  | 133 | // establish binder interface to camera service | 
|  | 134 | template <typename TCam, typename TCamTraits> | 
| Eino-Ville Talvala | f86177d | 2017-02-01 15:27:41 -0800 | [diff] [blame] | 135 | const sp<::android::hardware::ICameraService> CameraBase<TCam, TCamTraits>::getCameraService() | 
| Igor Murashkin | c073ba5 | 2013-02-26 14:32:34 -0800 | [diff] [blame] | 136 | { | 
|  | 137 | Mutex::Autolock _l(gLock); | 
|  | 138 | if (gCameraService.get() == 0) { | 
| Jayant Chowdhary | f5b9cc6 | 2020-09-08 16:25:17 -0700 | [diff] [blame] | 139 | if (CameraUtils::isCameraServiceDisabled()) { | 
| Ivan Podogov | ee844a8 | 2016-09-15 11:32:41 +0100 | [diff] [blame] | 140 | return gCameraService; | 
|  | 141 | } | 
|  | 142 |  | 
| Igor Murashkin | c073ba5 | 2013-02-26 14:32:34 -0800 | [diff] [blame] | 143 | sp<IServiceManager> sm = defaultServiceManager(); | 
|  | 144 | sp<IBinder> binder; | 
|  | 145 | do { | 
| Austin Borger | 0fb3ad9 | 2023-06-01 16:51:35 -0700 | [diff] [blame] | 146 | binder = sm->getService(toString16(kCameraServiceName)); | 
| Igor Murashkin | c073ba5 | 2013-02-26 14:32:34 -0800 | [diff] [blame] | 147 | if (binder != 0) { | 
|  | 148 | break; | 
|  | 149 | } | 
|  | 150 | ALOGW("CameraService not published, waiting..."); | 
|  | 151 | usleep(kCameraServicePollDelay); | 
|  | 152 | } while(true); | 
|  | 153 | if (gDeathNotifier == NULL) { | 
|  | 154 | gDeathNotifier = new DeathNotifier(); | 
|  | 155 | } | 
|  | 156 | binder->linkToDeath(gDeathNotifier); | 
| Eino-Ville Talvala | d56db1d | 2015-12-17 16:50:35 -0800 | [diff] [blame] | 157 | gCameraService = interface_cast<::android::hardware::ICameraService>(binder); | 
| Igor Murashkin | c073ba5 | 2013-02-26 14:32:34 -0800 | [diff] [blame] | 158 | } | 
|  | 159 | ALOGE_IF(gCameraService == 0, "no CameraService!?"); | 
|  | 160 | return gCameraService; | 
|  | 161 | } | 
|  | 162 |  | 
|  | 163 | template <typename TCam, typename TCamTraits> | 
|  | 164 | sp<TCam> CameraBase<TCam, TCamTraits>::connect(int cameraId, | 
| Austin Borger | 0fb3ad9 | 2023-06-01 16:51:35 -0700 | [diff] [blame] | 165 | const std::string& clientPackageName, | 
| Austin Borger | 3560b7e | 2022-10-27 12:20:29 -0700 | [diff] [blame] | 166 | int clientUid, int clientPid, int targetSdkVersion, | 
| Chengfei Tao | 4094e1f | 2023-01-31 18:52:49 +0000 | [diff] [blame] | 167 | bool overrideToPortrait, bool forceSlowJpegMode) | 
| Igor Murashkin | c073ba5 | 2013-02-26 14:32:34 -0800 | [diff] [blame] | 168 | { | 
|  | 169 | ALOGV("%s: connect", __FUNCTION__); | 
|  | 170 | sp<TCam> c = new TCam(cameraId); | 
|  | 171 | sp<TCamCallbacks> cl = c; | 
| Eino-Ville Talvala | f86177d | 2017-02-01 15:27:41 -0800 | [diff] [blame] | 172 | const sp<::android::hardware::ICameraService> cs = getCameraService(); | 
| Ruben Brunk | 0f61d8f | 2013-08-08 13:07:18 -0700 | [diff] [blame] | 173 |  | 
| Eino-Ville Talvala | d56db1d | 2015-12-17 16:50:35 -0800 | [diff] [blame] | 174 | binder::Status ret; | 
|  | 175 | if (cs != nullptr) { | 
| Ruben Brunk | 0f61d8f | 2013-08-08 13:07:18 -0700 | [diff] [blame] | 176 | TCamConnectService fnConnectService = TCamTraits::fnConnectService; | 
| Chengfei Tao | 4094e1f | 2023-01-31 18:52:49 +0000 | [diff] [blame] | 177 | ALOGI("Connect camera (legacy API) - overrideToPortrait %d, forceSlowJpegMode %d", | 
|  | 178 | overrideToPortrait, forceSlowJpegMode); | 
| Eino-Ville Talvala | d56db1d | 2015-12-17 16:50:35 -0800 | [diff] [blame] | 179 | ret = (cs.get()->*fnConnectService)(cl, cameraId, clientPackageName, clientUid, | 
| Chengfei Tao | 4094e1f | 2023-01-31 18:52:49 +0000 | [diff] [blame] | 180 | clientPid, targetSdkVersion, overrideToPortrait, forceSlowJpegMode, | 
|  | 181 | /*out*/ &c->mCamera); | 
| Igor Murashkin | c073ba5 | 2013-02-26 14:32:34 -0800 | [diff] [blame] | 182 | } | 
| Eino-Ville Talvala | d56db1d | 2015-12-17 16:50:35 -0800 | [diff] [blame] | 183 | if (ret.isOk() && c->mCamera != nullptr) { | 
| Marco Nelissen | 06b4606 | 2014-11-14 07:58:25 -0800 | [diff] [blame] | 184 | IInterface::asBinder(c->mCamera)->linkToDeath(c); | 
| Igor Murashkin | c073ba5 | 2013-02-26 14:32:34 -0800 | [diff] [blame] | 185 | c->mStatus = NO_ERROR; | 
|  | 186 | } else { | 
| Eino-Ville Talvala | d56db1d | 2015-12-17 16:50:35 -0800 | [diff] [blame] | 187 | ALOGW("An error occurred while connecting to camera %d: %s", cameraId, | 
| Tomasz Wasilczyk | 12b04a5 | 2023-08-11 15:52:22 +0000 | [diff] [blame] | 188 | (cs == nullptr) ? "Service not available" : ret.toString8().c_str()); | 
| Igor Murashkin | c073ba5 | 2013-02-26 14:32:34 -0800 | [diff] [blame] | 189 | c.clear(); | 
|  | 190 | } | 
|  | 191 | return c; | 
|  | 192 | } | 
|  | 193 |  | 
|  | 194 | template <typename TCam, typename TCamTraits> | 
|  | 195 | void CameraBase<TCam, TCamTraits>::disconnect() | 
|  | 196 | { | 
|  | 197 | ALOGV("%s: disconnect", __FUNCTION__); | 
|  | 198 | if (mCamera != 0) { | 
|  | 199 | mCamera->disconnect(); | 
| Marco Nelissen | 06b4606 | 2014-11-14 07:58:25 -0800 | [diff] [blame] | 200 | IInterface::asBinder(mCamera)->unlinkToDeath(this); | 
| Igor Murashkin | c073ba5 | 2013-02-26 14:32:34 -0800 | [diff] [blame] | 201 | mCamera = 0; | 
|  | 202 | } | 
|  | 203 | ALOGV("%s: disconnect (done)", __FUNCTION__); | 
|  | 204 | } | 
|  | 205 |  | 
|  | 206 | template <typename TCam, typename TCamTraits> | 
|  | 207 | CameraBase<TCam, TCamTraits>::CameraBase(int cameraId) : | 
|  | 208 | mStatus(UNKNOWN_ERROR), | 
|  | 209 | mCameraId(cameraId) | 
|  | 210 | { | 
|  | 211 | } | 
|  | 212 |  | 
|  | 213 | template <typename TCam, typename TCamTraits> | 
|  | 214 | CameraBase<TCam, TCamTraits>::~CameraBase() | 
|  | 215 | { | 
|  | 216 | } | 
|  | 217 |  | 
|  | 218 | template <typename TCam, typename TCamTraits> | 
|  | 219 | sp<typename TCamTraits::TCamUser> CameraBase<TCam, TCamTraits>::remote() | 
|  | 220 | { | 
|  | 221 | return mCamera; | 
|  | 222 | } | 
|  | 223 |  | 
|  | 224 | template <typename TCam, typename TCamTraits> | 
|  | 225 | status_t CameraBase<TCam, TCamTraits>::getStatus() | 
|  | 226 | { | 
|  | 227 | return mStatus; | 
|  | 228 | } | 
|  | 229 |  | 
|  | 230 | template <typename TCam, typename TCamTraits> | 
| Mark Salyzyn | 7b73e71 | 2014-06-09 16:28:21 -0700 | [diff] [blame] | 231 | void CameraBase<TCam, TCamTraits>::binderDied(const wp<IBinder>& /*who*/) { | 
| Igor Murashkin | c073ba5 | 2013-02-26 14:32:34 -0800 | [diff] [blame] | 232 | ALOGW("mediaserver's remote binder Camera object died"); | 
|  | 233 | notifyCallback(CAMERA_MSG_ERROR, CAMERA_ERROR_SERVER_DIED, /*ext2*/0); | 
|  | 234 | } | 
|  | 235 |  | 
|  | 236 | template <typename TCam, typename TCamTraits> | 
|  | 237 | void CameraBase<TCam, TCamTraits>::setListener(const sp<TCamListener>& listener) | 
|  | 238 | { | 
|  | 239 | Mutex::Autolock _l(mLock); | 
|  | 240 | mListener = listener; | 
|  | 241 | } | 
|  | 242 |  | 
|  | 243 | // callback from camera service | 
|  | 244 | template <typename TCam, typename TCamTraits> | 
|  | 245 | void CameraBase<TCam, TCamTraits>::notifyCallback(int32_t msgType, | 
|  | 246 | int32_t ext1, | 
|  | 247 | int32_t ext2) | 
|  | 248 | { | 
|  | 249 | sp<TCamListener> listener; | 
|  | 250 | { | 
|  | 251 | Mutex::Autolock _l(mLock); | 
|  | 252 | listener = mListener; | 
|  | 253 | } | 
|  | 254 | if (listener != NULL) { | 
|  | 255 | listener->notify(msgType, ext1, ext2); | 
|  | 256 | } | 
|  | 257 | } | 
|  | 258 |  | 
| Igor Murashkin | c073ba5 | 2013-02-26 14:32:34 -0800 | [diff] [blame] | 259 | template <typename TCam, typename TCamTraits> | 
|  | 260 | int CameraBase<TCam, TCamTraits>::getNumberOfCameras() { | 
| Eino-Ville Talvala | d56db1d | 2015-12-17 16:50:35 -0800 | [diff] [blame] | 261 | const sp<::android::hardware::ICameraService> cs = getCameraService(); | 
| Igor Murashkin | c073ba5 | 2013-02-26 14:32:34 -0800 | [diff] [blame] | 262 |  | 
|  | 263 | if (!cs.get()) { | 
|  | 264 | // as required by the public Java APIs | 
|  | 265 | return 0; | 
|  | 266 | } | 
| Eino-Ville Talvala | d56db1d | 2015-12-17 16:50:35 -0800 | [diff] [blame] | 267 | int32_t count; | 
|  | 268 | binder::Status res = cs->getNumberOfCameras( | 
|  | 269 | ::android::hardware::ICameraService::CAMERA_TYPE_BACKWARD_COMPATIBLE, | 
|  | 270 | &count); | 
|  | 271 | if (!res.isOk()) { | 
|  | 272 | ALOGE("Error reading number of cameras: %s", | 
| Tomasz Wasilczyk | 12b04a5 | 2023-08-11 15:52:22 +0000 | [diff] [blame] | 273 | res.toString8().c_str()); | 
| Eino-Ville Talvala | d56db1d | 2015-12-17 16:50:35 -0800 | [diff] [blame] | 274 | count = 0; | 
|  | 275 | } | 
|  | 276 | return count; | 
| Igor Murashkin | c073ba5 | 2013-02-26 14:32:34 -0800 | [diff] [blame] | 277 | } | 
|  | 278 |  | 
|  | 279 | // this can be in BaseCamera but it should be an instance method | 
|  | 280 | template <typename TCam, typename TCamTraits> | 
|  | 281 | status_t CameraBase<TCam, TCamTraits>::getCameraInfo(int cameraId, | 
| Austin Borger | 3560b7e | 2022-10-27 12:20:29 -0700 | [diff] [blame] | 282 | bool overrideToPortrait, | 
| Eino-Ville Talvala | d56db1d | 2015-12-17 16:50:35 -0800 | [diff] [blame] | 283 | struct hardware::CameraInfo* cameraInfo) { | 
| Eino-Ville Talvala | f86177d | 2017-02-01 15:27:41 -0800 | [diff] [blame] | 284 | const sp<::android::hardware::ICameraService> cs = getCameraService(); | 
| Igor Murashkin | c073ba5 | 2013-02-26 14:32:34 -0800 | [diff] [blame] | 285 | if (cs == 0) return UNKNOWN_ERROR; | 
| Austin Borger | 3560b7e | 2022-10-27 12:20:29 -0700 | [diff] [blame] | 286 | binder::Status res = cs->getCameraInfo(cameraId, overrideToPortrait, cameraInfo); | 
| Eino-Ville Talvala | d56db1d | 2015-12-17 16:50:35 -0800 | [diff] [blame] | 287 | return res.isOk() ? OK : res.serviceSpecificErrorCode(); | 
| Igor Murashkin | c073ba5 | 2013-02-26 14:32:34 -0800 | [diff] [blame] | 288 | } | 
|  | 289 |  | 
| Igor Murashkin | c073ba5 | 2013-02-26 14:32:34 -0800 | [diff] [blame] | 290 | template class CameraBase<Camera>; | 
|  | 291 |  | 
|  | 292 | } // namespace android |