blob: 0283d01c3ea1d91f462410176d01e2a14d90ab90 [file] [log] [blame]
Igor Murashkin44cfcf02013-03-01 16:22:28 -08001/*
2 * Copyright (C) 2013 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 "Camera2ClientBase"
18#define ATRACE_TAG ATRACE_TAG_CAMERA
19//#define LOG_NDEBUG 0
20
Colin Crosse5729fa2014-03-21 15:04:25 -070021#include <inttypes.h>
22
Igor Murashkin44cfcf02013-03-01 16:22:28 -080023#include <utils/Log.h>
24#include <utils/Trace.h>
25
26#include <cutils/properties.h>
27#include <gui/Surface.h>
28#include <gui/Surface.h>
Igor Murashkin44cfcf02013-03-01 16:22:28 -080029
Shuzhen Wang316781a2020-08-18 18:11:01 -070030#include <camera/CameraSessionStats.h>
31
Eino-Ville Talvala7b82efe2013-07-25 17:12:35 -070032#include "common/Camera2ClientBase.h"
Igor Murashkine7ee7632013-06-11 18:10:18 -070033
Eino-Ville Talvala7b82efe2013-07-25 17:12:35 -070034#include "api2/CameraDeviceClient.h"
35
Eino-Ville Talvalad309fb92015-11-25 12:12:45 -080036#include "device3/Camera3Device.h"
Jayant Chowdhary12361932018-08-27 14:46:13 -070037#include "utils/CameraThreadState.h"
Shuzhen Wang316781a2020-08-18 18:11:01 -070038#include "utils/CameraServiceProxyWrapper.h"
Igor Murashkin44cfcf02013-03-01 16:22:28 -080039
40namespace android {
41using namespace camera2;
42
Igor Murashkin44cfcf02013-03-01 16:22:28 -080043// Interface used by CameraService
44
45template <typename TClientBase>
46Camera2ClientBase<TClientBase>::Camera2ClientBase(
47 const sp<CameraService>& cameraService,
48 const sp<TCamCallbacks>& remoteCallback,
49 const String16& clientPackageName,
Jooyung Han3f9a3b42020-01-23 12:27:18 +090050 const std::optional<String16>& clientFeatureId,
Eino-Ville Talvala2f09bac2016-12-13 11:29:54 -080051 const String8& cameraId,
Yin-Chia Yehc3e9d6f2018-02-06 10:56:32 -080052 int api1CameraId,
Igor Murashkin44cfcf02013-03-01 16:22:28 -080053 int cameraFacing,
Emilian Peev8b64f282021-03-25 16:49:57 -070054 int sensorOrientation,
Igor Murashkin44cfcf02013-03-01 16:22:28 -080055 int clientPid,
56 uid_t clientUid,
Shuzhen Wangd4abdf72021-05-28 11:22:50 -070057 int servicePid,
58 bool overrideForPerfClass):
Philip P. Moltmann9e648f62019-11-04 12:52:45 -080059 TClientBase(cameraService, remoteCallback, clientPackageName, clientFeatureId,
Emilian Peev8b64f282021-03-25 16:49:57 -070060 cameraId, api1CameraId, cameraFacing, sensorOrientation, clientPid, clientUid,
61 servicePid),
Yin-Chia Yehcd8fce82014-06-18 10:51:34 -070062 mSharedCameraCallbacks(remoteCallback),
Eino-Ville Talvala2f09bac2016-12-13 11:29:54 -080063 mDeviceVersion(cameraService->getDeviceVersion(TClientBase::mCameraIdStr)),
Shuzhen Wangd4abdf72021-05-28 11:22:50 -070064 mDevice(new Camera3Device(cameraId, overrideForPerfClass)),
Yin-Chia Yehc3e9d6f2018-02-06 10:56:32 -080065 mDeviceActive(false), mApi1CameraId(api1CameraId)
Igor Murashkin44cfcf02013-03-01 16:22:28 -080066{
Eino-Ville Talvala2f09bac2016-12-13 11:29:54 -080067 ALOGI("Camera %s: Opened. Client: %s (PID %d, UID %d)", cameraId.string(),
Eino-Ville Talvalab9d2f332014-09-18 17:24:22 -070068 String8(clientPackageName).string(), clientPid, clientUid);
Igor Murashkin98e24722013-06-19 19:51:04 -070069
Eino-Ville Talvalab9d2f332014-09-18 17:24:22 -070070 mInitialClientPid = clientPid;
Igor Murashkin98e24722013-06-19 19:51:04 -070071 LOG_ALWAYS_FATAL_IF(mDevice == 0, "Device should never be NULL here.");
Igor Murashkin44cfcf02013-03-01 16:22:28 -080072}
73
74template <typename TClientBase>
75status_t Camera2ClientBase<TClientBase>::checkPid(const char* checkLocation)
76 const {
77
Jayant Chowdhary12361932018-08-27 14:46:13 -070078 int callingPid = CameraThreadState::getCallingPid();
Igor Murashkin44cfcf02013-03-01 16:22:28 -080079 if (callingPid == TClientBase::mClientPid) return NO_ERROR;
80
81 ALOGE("%s: attempt to use a locked camera from a different process"
82 " (old pid %d, new pid %d)", checkLocation, TClientBase::mClientPid, callingPid);
83 return PERMISSION_DENIED;
84}
85
86template <typename TClientBase>
Emilian Peevbd8c5032018-02-14 23:05:40 +000087status_t Camera2ClientBase<TClientBase>::initialize(sp<CameraProviderManager> manager,
88 const String8& monitorTags) {
89 return initializeImpl(manager, monitorTags);
Eino-Ville Talvala2f09bac2016-12-13 11:29:54 -080090}
91
92template <typename TClientBase>
93template <typename TProviderPtr>
Emilian Peevbd8c5032018-02-14 23:05:40 +000094status_t Camera2ClientBase<TClientBase>::initializeImpl(TProviderPtr providerPtr,
95 const String8& monitorTags) {
Igor Murashkin44cfcf02013-03-01 16:22:28 -080096 ATRACE_CALL();
Eino-Ville Talvala2f09bac2016-12-13 11:29:54 -080097 ALOGV("%s: Initializing client for camera %s", __FUNCTION__,
98 TClientBase::mCameraIdStr.string());
Igor Murashkin44cfcf02013-03-01 16:22:28 -080099 status_t res;
100
Igor Murashkine6800ce2013-03-04 17:25:57 -0800101 // Verify ops permissions
102 res = TClientBase::startCameraOps();
103 if (res != OK) {
104 return res;
105 }
106
107 if (mDevice == NULL) {
Eino-Ville Talvala2f09bac2016-12-13 11:29:54 -0800108 ALOGE("%s: Camera %s: No device connected",
109 __FUNCTION__, TClientBase::mCameraIdStr.string());
Igor Murashkine6800ce2013-03-04 17:25:57 -0800110 return NO_INIT;
111 }
112
Emilian Peevbd8c5032018-02-14 23:05:40 +0000113 res = mDevice->initialize(providerPtr, monitorTags);
Igor Murashkin44cfcf02013-03-01 16:22:28 -0800114 if (res != OK) {
Eino-Ville Talvala2f09bac2016-12-13 11:29:54 -0800115 ALOGE("%s: Camera %s: unable to initialize device: %s (%d)",
116 __FUNCTION__, TClientBase::mCameraIdStr.string(), strerror(-res), res);
Zhijun He66281c32013-09-13 17:59:59 -0700117 return res;
Igor Murashkin44cfcf02013-03-01 16:22:28 -0800118 }
119
Yin-Chia Yeh5fd603e2019-11-20 11:22:27 -0800120 wp<NotificationListener> weakThis(this);
Yin-Chia Yehe1c80632016-08-08 14:48:05 -0700121 res = mDevice->setNotifyCallback(weakThis);
Igor Murashkin44cfcf02013-03-01 16:22:28 -0800122
123 return OK;
124}
125
126template <typename TClientBase>
127Camera2ClientBase<TClientBase>::~Camera2ClientBase() {
128 ATRACE_CALL();
129
130 TClientBase::mDestructionStarted = true;
131
132 disconnect();
133
Eino-Ville Talvala2f09bac2016-12-13 11:29:54 -0800134 ALOGI("Closed Camera %s. Client was: %s (PID %d, UID %u)",
135 TClientBase::mCameraIdStr.string(),
Svetoslav Ganov280405a2015-05-12 02:19:27 +0000136 String8(TClientBase::mClientPackageName).string(),
Eino-Ville Talvalab9d2f332014-09-18 17:24:22 -0700137 mInitialClientPid, TClientBase::mClientUid);
Igor Murashkin44cfcf02013-03-01 16:22:28 -0800138}
139
140template <typename TClientBase>
Eino-Ville Talvalac4003962016-01-13 10:07:04 -0800141status_t Camera2ClientBase<TClientBase>::dumpClient(int fd,
Igor Murashkin44cfcf02013-03-01 16:22:28 -0800142 const Vector<String16>& args) {
143 String8 result;
Eino-Ville Talvala2f09bac2016-12-13 11:29:54 -0800144 result.appendFormat("Camera2ClientBase[%s] (%p) PID: %d, dump:\n",
145 TClientBase::mCameraIdStr.string(),
Eino-Ville Talvalae992e752014-11-07 16:17:48 -0800146 (TClientBase::getRemoteCallback() != NULL ?
Marco Nelissen06b46062014-11-14 07:58:25 -0800147 IInterface::asBinder(TClientBase::getRemoteCallback()).get() : NULL),
Igor Murashkin44cfcf02013-03-01 16:22:28 -0800148 TClientBase::mClientPid);
149 result.append(" State: ");
150
151 write(fd, result.string(), result.size());
152 // TODO: print dynamic/request section from most recent requests
153
154 return dumpDevice(fd, args);
155}
156
157template <typename TClientBase>
Avichal Rakesh7e53cad2021-10-05 13:46:30 -0700158status_t Camera2ClientBase<TClientBase>::startWatchingTags(const String8 &tags, int out) {
159 sp<CameraDeviceBase> device = mDevice;
160 if (!device) {
161 dprintf(out, " Device is detached");
162 return OK;
163 }
164
165 return device->startWatchingTags(tags);
166}
167
168template <typename TClientBase>
169status_t Camera2ClientBase<TClientBase>::stopWatchingTags(int out) {
170 sp<CameraDeviceBase> device = mDevice;
171 if (!device) {
172 dprintf(out, " Device is detached");
173 return OK;
174 }
175
176 return device->stopWatchingTags();
177}
178
179template <typename TClientBase>
180status_t Camera2ClientBase<TClientBase>::dumpWatchedEventsToVector(std::vector<std::string> &out) {
181 sp<CameraDeviceBase> device = mDevice;
182 if (!device) {
183 // Nothing to dump if the device is detached
184 return OK;
185 }
186 return device->dumpWatchedEventsToVector(out);
187}
188
189template <typename TClientBase>
Igor Murashkin44cfcf02013-03-01 16:22:28 -0800190status_t Camera2ClientBase<TClientBase>::dumpDevice(
191 int fd,
192 const Vector<String16>& args) {
193 String8 result;
194
195 result = " Device dump:\n";
196 write(fd, result.string(), result.size());
197
Yin-Chia Yehe5138f12017-11-10 14:10:05 -0800198 sp<CameraDeviceBase> device = mDevice;
199 if (!device.get()) {
Igor Murashkin44cfcf02013-03-01 16:22:28 -0800200 result = " *** Device is detached\n";
201 write(fd, result.string(), result.size());
202 return NO_ERROR;
203 }
204
Yin-Chia Yehe5138f12017-11-10 14:10:05 -0800205 status_t res = device->dump(fd, args);
Igor Murashkin44cfcf02013-03-01 16:22:28 -0800206 if (res != OK) {
207 result = String8::format(" Error dumping device: %s (%d)",
208 strerror(-res), res);
209 write(fd, result.string(), result.size());
210 }
211
212 return NO_ERROR;
213}
214
215// ICameraClient2BaseUser interface
216
217
218template <typename TClientBase>
Eino-Ville Talvalad56db1d2015-12-17 16:50:35 -0800219binder::Status Camera2ClientBase<TClientBase>::disconnect() {
Igor Murashkin44cfcf02013-03-01 16:22:28 -0800220 ATRACE_CALL();
221 Mutex::Autolock icl(mBinderSerializationLock);
222
Eino-Ville Talvalad56db1d2015-12-17 16:50:35 -0800223 binder::Status res = binder::Status::ok();
Igor Murashkin44cfcf02013-03-01 16:22:28 -0800224 // Allow both client and the media server to disconnect at all times
Jayant Chowdhary12361932018-08-27 14:46:13 -0700225 int callingPid = CameraThreadState::getCallingPid();
Igor Murashkin44cfcf02013-03-01 16:22:28 -0800226 if (callingPid != TClientBase::mClientPid &&
Eino-Ville Talvalad56db1d2015-12-17 16:50:35 -0800227 callingPid != TClientBase::mServicePid) return res;
Igor Murashkin44cfcf02013-03-01 16:22:28 -0800228
Eino-Ville Talvala2f09bac2016-12-13 11:29:54 -0800229 ALOGV("Camera %s: Shutting down", TClientBase::mCameraIdStr.string());
Igor Murashkin44cfcf02013-03-01 16:22:28 -0800230
Rucha Katakwardf223072021-06-15 10:21:00 -0700231 // Before detaching the device, cache the info from current open session.
232 // The disconnected check avoids duplication of info and also prevents
233 // deadlock while acquiring service lock in cacheDump.
234 if (!TClientBase::mDisconnected) {
235 Camera2ClientBase::getCameraService()->cacheDump();
236 }
237
Igor Murashkin44cfcf02013-03-01 16:22:28 -0800238 detachDevice();
239
Igor Murashkine6800ce2013-03-04 17:25:57 -0800240 CameraService::BasicClient::disconnect();
Igor Murashkin44cfcf02013-03-01 16:22:28 -0800241
Shuzhen Wang316781a2020-08-18 18:11:01 -0700242 ALOGV("Camera %s: Shut down complete", TClientBase::mCameraIdStr.string());
Eino-Ville Talvalad56db1d2015-12-17 16:50:35 -0800243
244 return res;
Igor Murashkin44cfcf02013-03-01 16:22:28 -0800245}
246
247template <typename TClientBase>
248void Camera2ClientBase<TClientBase>::detachDevice() {
249 if (mDevice == 0) return;
250 mDevice->disconnect();
251
Eino-Ville Talvala2f09bac2016-12-13 11:29:54 -0800252 ALOGV("Camera %s: Detach complete", TClientBase::mCameraIdStr.string());
Igor Murashkin44cfcf02013-03-01 16:22:28 -0800253}
254
255template <typename TClientBase>
256status_t Camera2ClientBase<TClientBase>::connect(
257 const sp<TCamCallbacks>& client) {
258 ATRACE_CALL();
259 ALOGV("%s: E", __FUNCTION__);
260 Mutex::Autolock icl(mBinderSerializationLock);
261
262 if (TClientBase::mClientPid != 0 &&
Jayant Chowdhary12361932018-08-27 14:46:13 -0700263 CameraThreadState::getCallingPid() != TClientBase::mClientPid) {
Igor Murashkin44cfcf02013-03-01 16:22:28 -0800264
Eino-Ville Talvala2f09bac2016-12-13 11:29:54 -0800265 ALOGE("%s: Camera %s: Connection attempt from pid %d; "
Igor Murashkin44cfcf02013-03-01 16:22:28 -0800266 "current locked to pid %d",
267 __FUNCTION__,
Eino-Ville Talvala2f09bac2016-12-13 11:29:54 -0800268 TClientBase::mCameraIdStr.string(),
Jayant Chowdhary12361932018-08-27 14:46:13 -0700269 CameraThreadState::getCallingPid(),
Igor Murashkin44cfcf02013-03-01 16:22:28 -0800270 TClientBase::mClientPid);
271 return BAD_VALUE;
272 }
273
Jayant Chowdhary12361932018-08-27 14:46:13 -0700274 TClientBase::mClientPid = CameraThreadState::getCallingPid();
Igor Murashkin44cfcf02013-03-01 16:22:28 -0800275
276 TClientBase::mRemoteCallback = client;
277 mSharedCameraCallbacks = client;
278
279 return OK;
280}
281
282/** Device-related methods */
283
284template <typename TClientBase>
Jianing Weicb0652e2014-03-12 18:29:36 -0700285void Camera2ClientBase<TClientBase>::notifyError(
Eino-Ville Talvalad56db1d2015-12-17 16:50:35 -0800286 int32_t errorCode,
Jianing Weicb0652e2014-03-12 18:29:36 -0700287 const CaptureResultExtras& resultExtras) {
288 ALOGE("Error condition %d reported by HAL, requestId %" PRId32, errorCode,
289 resultExtras.requestId);
Igor Murashkin44cfcf02013-03-01 16:22:28 -0800290}
291
292template <typename TClientBase>
Eino-Ville Talvala178e8232021-04-16 18:41:39 -0700293status_t Camera2ClientBase<TClientBase>::notifyActive() {
294 if (!mDeviceActive) {
295 status_t res = TClientBase::startCameraStreamingOps();
296 if (res != OK) {
297 ALOGE("%s: Camera %s: Error starting camera streaming ops: %d", __FUNCTION__,
298 TClientBase::mCameraIdStr.string(), res);
299 return res;
300 }
301 CameraServiceProxyWrapper::logActive(TClientBase::mCameraIdStr);
302 }
303 mDeviceActive = true;
304
305 ALOGV("Camera device is now active");
306 return OK;
307}
308
309template <typename TClientBase>
Shuzhen Wang316781a2020-08-18 18:11:01 -0700310void Camera2ClientBase<TClientBase>::notifyIdle(
311 int64_t requestCount, int64_t resultErrorCount, bool deviceError,
312 const std::vector<hardware::CameraStreamStats>& streamStats) {
Eino-Ville Talvala412fe562015-08-20 17:08:32 -0700313 if (mDeviceActive) {
Eino-Ville Talvala178e8232021-04-16 18:41:39 -0700314 status_t res = TClientBase::finishCameraStreamingOps();
315 if (res != OK) {
316 ALOGE("%s: Camera %s: Error finishing streaming ops: %d", __FUNCTION__,
317 TClientBase::mCameraIdStr.string(), res);
318 }
Shuzhen Wang316781a2020-08-18 18:11:01 -0700319 CameraServiceProxyWrapper::logIdle(TClientBase::mCameraIdStr,
320 requestCount, resultErrorCount, deviceError, streamStats);
Eino-Ville Talvala412fe562015-08-20 17:08:32 -0700321 }
322 mDeviceActive = false;
323
Eino-Ville Talvalaf1e98d82013-09-06 09:32:43 -0700324 ALOGV("Camera device is now idle");
325}
326
327template <typename TClientBase>
Jianing Weicb0652e2014-03-12 18:29:36 -0700328void Camera2ClientBase<TClientBase>::notifyShutter(const CaptureResultExtras& resultExtras,
Igor Murashkin44cfcf02013-03-01 16:22:28 -0800329 nsecs_t timestamp) {
Jianing Weicb0652e2014-03-12 18:29:36 -0700330 (void)resultExtras;
Igor Murashkin44cfcf02013-03-01 16:22:28 -0800331 (void)timestamp;
332
Jianing Weicb0652e2014-03-12 18:29:36 -0700333 ALOGV("%s: Shutter notification for request id %" PRId32 " at time %" PRId64,
334 __FUNCTION__, resultExtras.requestId, timestamp);
Igor Murashkin44cfcf02013-03-01 16:22:28 -0800335}
336
337template <typename TClientBase>
338void Camera2ClientBase<TClientBase>::notifyAutoFocus(uint8_t newState,
339 int triggerId) {
340 (void)newState;
341 (void)triggerId;
342
343 ALOGV("%s: Autofocus state now %d, last trigger %d",
344 __FUNCTION__, newState, triggerId);
345
Igor Murashkin44cfcf02013-03-01 16:22:28 -0800346}
347
348template <typename TClientBase>
349void Camera2ClientBase<TClientBase>::notifyAutoExposure(uint8_t newState,
350 int triggerId) {
351 (void)newState;
352 (void)triggerId;
353
354 ALOGV("%s: Autoexposure state now %d, last trigger %d",
355 __FUNCTION__, newState, triggerId);
356}
357
358template <typename TClientBase>
359void Camera2ClientBase<TClientBase>::notifyAutoWhitebalance(uint8_t newState,
360 int triggerId) {
361 (void)newState;
362 (void)triggerId;
363
364 ALOGV("%s: Auto-whitebalance state now %d, last trigger %d",
365 __FUNCTION__, newState, triggerId);
366}
367
368template <typename TClientBase>
Eino-Ville Talvala4d44cad2015-04-11 13:15:45 -0700369void Camera2ClientBase<TClientBase>::notifyPrepared(int streamId) {
370 (void)streamId;
371
372 ALOGV("%s: Stream %d now prepared",
373 __FUNCTION__, streamId);
374}
375
376template <typename TClientBase>
Shuzhen Wang9d066012016-09-30 11:30:20 -0700377void Camera2ClientBase<TClientBase>::notifyRequestQueueEmpty() {
378
379 ALOGV("%s: Request queue now empty", __FUNCTION__);
380}
381
382template <typename TClientBase>
Chien-Yu Chene8c535e2016-04-14 12:18:26 -0700383void Camera2ClientBase<TClientBase>::notifyRepeatingRequestError(long lastFrameNumber) {
384 (void)lastFrameNumber;
385
386 ALOGV("%s: Repeating request was stopped. Last frame number is %ld",
387 __FUNCTION__, lastFrameNumber);
388}
389
390template <typename TClientBase>
Igor Murashkin44cfcf02013-03-01 16:22:28 -0800391int Camera2ClientBase<TClientBase>::getCameraId() const {
Yin-Chia Yehc3e9d6f2018-02-06 10:56:32 -0800392 return mApi1CameraId;
Igor Murashkin44cfcf02013-03-01 16:22:28 -0800393}
394
395template <typename TClientBase>
Yin-Chia Yehcd8fce82014-06-18 10:51:34 -0700396int Camera2ClientBase<TClientBase>::getCameraDeviceVersion() const {
397 return mDeviceVersion;
398}
399
400template <typename TClientBase>
Igor Murashkin44cfcf02013-03-01 16:22:28 -0800401const sp<CameraDeviceBase>& Camera2ClientBase<TClientBase>::getCameraDevice() {
402 return mDevice;
403}
404
405template <typename TClientBase>
406const sp<CameraService>& Camera2ClientBase<TClientBase>::getCameraService() {
Eino-Ville Talvala2f09bac2016-12-13 11:29:54 -0800407 return TClientBase::sCameraService;
Igor Murashkin44cfcf02013-03-01 16:22:28 -0800408}
409
410template <typename TClientBase>
411Camera2ClientBase<TClientBase>::SharedCameraCallbacks::Lock::Lock(
412 SharedCameraCallbacks &client) :
413
414 mRemoteCallback(client.mRemoteCallback),
415 mSharedClient(client) {
416
417 mSharedClient.mRemoteCallbackLock.lock();
418}
419
420template <typename TClientBase>
421Camera2ClientBase<TClientBase>::SharedCameraCallbacks::Lock::~Lock() {
422 mSharedClient.mRemoteCallbackLock.unlock();
423}
424
425template <typename TClientBase>
426Camera2ClientBase<TClientBase>::SharedCameraCallbacks::SharedCameraCallbacks(
427 const sp<TCamCallbacks>&client) :
428
429 mRemoteCallback(client) {
430}
431
432template <typename TClientBase>
433typename Camera2ClientBase<TClientBase>::SharedCameraCallbacks&
434Camera2ClientBase<TClientBase>::SharedCameraCallbacks::operator=(
435 const sp<TCamCallbacks>&client) {
436
437 Mutex::Autolock l(mRemoteCallbackLock);
438 mRemoteCallback = client;
439 return *this;
440}
441
442template <typename TClientBase>
443void Camera2ClientBase<TClientBase>::SharedCameraCallbacks::clear() {
444 Mutex::Autolock l(mRemoteCallbackLock);
445 mRemoteCallback.clear();
446}
447
Cliff Wud3a05312021-04-26 23:07:31 +0800448template <typename TClientBase>
449status_t Camera2ClientBase<TClientBase>::injectCamera(const String8& injectedCamId,
450 sp<CameraProviderManager> manager) {
451 return mDevice->injectCamera(injectedCamId, manager);
452}
453
454template <typename TClientBase>
455status_t Camera2ClientBase<TClientBase>::stopInjection() {
456 return mDevice->stopInjection();
457}
458
Igor Murashkin44cfcf02013-03-01 16:22:28 -0800459template class Camera2ClientBase<CameraService::Client>;
Igor Murashkine7ee7632013-06-11 18:10:18 -0700460template class Camera2ClientBase<CameraDeviceClientBase>;
Igor Murashkin44cfcf02013-03-01 16:22:28 -0800461
462} // namespace android