1 /*
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
21 #include <inttypes.h>
22
23 #include <utils/Log.h>
24 #include <utils/Trace.h>
25
26 #include <cutils/properties.h>
27 #include <gui/BufferItem.h>
28 #include <gui/BufferItemConsumer.h>
29 #include <gui/Surface.h>
30
31 #include <android/hardware/ICameraService.h>
32 #include <camera/CameraSessionStats.h>
33 #include <camera/StringUtils.h>
34 #include <com_android_window_flags.h>
35 #include <com_android_internal_camera_flags.h>
36
37 #include "common/Camera2ClientBase.h"
38
39 #include "api2/CameraDeviceClient.h"
40
41 #include "device3/Camera3Device.h"
42 #include "device3/aidl/AidlCamera3Device.h"
43 #include "device3/hidl/HidlCamera3Device.h"
44 #include "device3/aidl/AidlCamera3SharedDevice.h"
45
46 namespace android {
47
48 using namespace camera2;
49
50 namespace wm_flags = com::android::window::flags;
51 namespace flags = com::android::internal::camera::flags;
52
53 // Interface used by CameraService
54
55 template <typename TClientBase>
Camera2ClientBase(const sp<CameraService> & cameraService,const sp<TCamCallbacks> & remoteCallback,std::shared_ptr<CameraServiceProxyWrapper> cameraServiceProxyWrapper,std::shared_ptr<AttributionAndPermissionUtils> attributionAndPermissionUtils,const AttributionSourceState & clientAttribution,int callingPid,bool systemNativeClient,const std::string & cameraId,int api1CameraId,int cameraFacing,int sensorOrientation,int servicePid,bool overrideForPerfClass,int rotationOverride,bool sharedMode,bool isVendorClient,bool legacyClient)56 Camera2ClientBase<TClientBase>::Camera2ClientBase(
57 const sp<CameraService>& cameraService, const sp<TCamCallbacks>& remoteCallback,
58 std::shared_ptr<CameraServiceProxyWrapper> cameraServiceProxyWrapper,
59 std::shared_ptr<AttributionAndPermissionUtils> attributionAndPermissionUtils,
60 const AttributionSourceState& clientAttribution, int callingPid, bool systemNativeClient,
61 const std::string& cameraId, int api1CameraId, int cameraFacing, int sensorOrientation,
62 int servicePid, bool overrideForPerfClass, int rotationOverride, bool sharedMode,
63 bool isVendorClient, bool legacyClient)
64 : TClientBase(cameraService, remoteCallback, attributionAndPermissionUtils, clientAttribution,
65 callingPid, systemNativeClient, cameraId, api1CameraId, cameraFacing,
66 sensorOrientation, servicePid, rotationOverride, sharedMode),
67 mSharedCameraCallbacks(remoteCallback),
68 mCameraServiceProxyWrapper(cameraServiceProxyWrapper),
69 mDeviceActive(false),
70 mApi1CameraId(api1CameraId) {
71 ALOGI("Camera %s: Opened. Client: %s (PID %d, UID %d)", cameraId.c_str(),
72 TClientBase::getPackageName().c_str(), TClientBase::mCallingPid,
73 TClientBase::getClientUid());
74
75 mInitialClientPid = TClientBase::mCallingPid;
76 mOverrideForPerfClass = overrideForPerfClass;
77 mLegacyClient = legacyClient;
78 mIsVendorClient = isVendorClient;
79 }
80
81 template <typename TClientBase>
checkPid(const char * checkLocation) const82 status_t Camera2ClientBase<TClientBase>::checkPid(const char* checkLocation)
83 const {
84
85 int callingPid = TClientBase::getCallingPid();
86 if (callingPid == TClientBase::mCallingPid) return NO_ERROR;
87
88 ALOGE("%s: attempt to use a locked camera from a different process"
89 " (old pid %d, new pid %d)", checkLocation, TClientBase::mCallingPid, callingPid);
90 return PERMISSION_DENIED;
91 }
92
93 template <typename TClientBase>
initialize(sp<CameraProviderManager> manager,const std::string & monitorTags)94 status_t Camera2ClientBase<TClientBase>::initialize(sp<CameraProviderManager> manager,
95 const std::string& monitorTags) {
96 return initializeImpl(manager, monitorTags);
97 }
98
99 template <typename TClientBase>
100 template <typename TProviderPtr>
initializeImpl(TProviderPtr providerPtr,const std::string & monitorTags)101 status_t Camera2ClientBase<TClientBase>::initializeImpl(TProviderPtr providerPtr,
102 const std::string& monitorTags) {
103 ATRACE_CALL();
104 ALOGV("%s: Initializing client for camera %s", __FUNCTION__,
105 TClientBase::mCameraIdStr.c_str());
106 status_t res;
107
108 IPCTransport providerTransport = IPCTransport::INVALID;
109 res = providerPtr->getCameraIdIPCTransport(TClientBase::mCameraIdStr,
110 &providerTransport);
111 if (res != OK) {
112 return res;
113 }
114 switch (providerTransport) {
115 case IPCTransport::HIDL:
116 mDevice =
117 new HidlCamera3Device(mCameraServiceProxyWrapper,
118 TClientBase::mAttributionAndPermissionUtils,
119 TClientBase::mCameraIdStr, mOverrideForPerfClass,
120 TClientBase::mRotationOverride, mIsVendorClient,
121 mLegacyClient);
122 break;
123 case IPCTransport::AIDL:
124 if (flags::camera_multi_client() && TClientBase::mSharedMode) {
125 mDevice = AidlCamera3SharedDevice::getInstance(mCameraServiceProxyWrapper,
126 TClientBase::mAttributionAndPermissionUtils,
127 TClientBase::mCameraIdStr, mOverrideForPerfClass,
128 TClientBase::mRotationOverride, mIsVendorClient,
129 mLegacyClient);
130 } else {
131 mDevice =
132 new AidlCamera3Device(mCameraServiceProxyWrapper,
133 TClientBase::mAttributionAndPermissionUtils,
134 TClientBase::mCameraIdStr, mOverrideForPerfClass,
135 TClientBase::mRotationOverride, mIsVendorClient,
136 mLegacyClient);
137 }
138 break;
139 default:
140 ALOGE("%s Invalid transport for camera id %s", __FUNCTION__,
141 TClientBase::mCameraIdStr.c_str());
142 return NO_INIT;
143 }
144 if (mDevice == NULL) {
145 ALOGE("%s: Camera %s: No device connected",
146 __FUNCTION__, TClientBase::mCameraIdStr.c_str());
147 return NO_INIT;
148 }
149
150 // Notify camera opening (check op if check_full_attribution_source_chain flag is off).
151 res = TClientBase::notifyCameraOpening();
152 if (res != OK) {
153 TClientBase::notifyCameraClosing();
154 return res;
155 }
156
157 res = mDevice->initialize(providerPtr, monitorTags);
158 if (res != OK) {
159 ALOGE("%s: Camera %s: unable to initialize device: %s (%d)",
160 __FUNCTION__, TClientBase::mCameraIdStr.c_str(), strerror(-res), res);
161 TClientBase::notifyCameraClosing();
162 return res;
163 }
164
165 wp<NotificationListener> weakThis(this);
166 res = mDevice->setNotifyCallback(weakThis);
167 if (res != OK) {
168 ALOGE("%s: Camera %s: Unable to set notify callback: %s (%d)",
169 __FUNCTION__, TClientBase::mCameraIdStr.c_str(), strerror(-res), res);
170 return res;
171 }
172
173 return OK;
174 }
175
176 template <typename TClientBase>
~Camera2ClientBase()177 Camera2ClientBase<TClientBase>::~Camera2ClientBase() {
178 ATRACE_CALL();
179
180 if (!flags::camera_multi_client() || !TClientBase::mDisconnected) {
181 TClientBase::mDestructionStarted = true;
182 disconnect();
183 }
184
185 ALOGI("%s: Client object's dtor for Camera Id %s completed. Client was: %s (PID %d, UID %u)",
186 __FUNCTION__, TClientBase::mCameraIdStr.c_str(), TClientBase::getPackageName().c_str(),
187 mInitialClientPid, TClientBase::getClientUid());
188 }
189
190 template <typename TClientBase>
dumpClient(int fd,const Vector<String16> & args)191 status_t Camera2ClientBase<TClientBase>::dumpClient(int fd,
192 const Vector<String16>& args) {
193 std::string result;
194 result += fmt::sprintf("Camera2ClientBase[%s] (%p) PID: %d, dump:\n",
195 TClientBase::mCameraIdStr.c_str(),
196 (TClientBase::getRemoteCallback() != NULL ?
197 (void *)IInterface::asBinder(TClientBase::getRemoteCallback()).get() : NULL),
198 TClientBase::mCallingPid);
199 result += " State: ";
200
201 write(fd, result.c_str(), result.size());
202 // TODO: print dynamic/request section from most recent requests
203
204 return dumpDevice(fd, args);
205 }
206
207 template <typename TClientBase>
startWatchingTags(const std::string & tags,int out)208 status_t Camera2ClientBase<TClientBase>::startWatchingTags(const std::string &tags, int out) {
209 sp<CameraDeviceBase> device = mDevice;
210 if (!device) {
211 dprintf(out, " Device is detached");
212 return OK;
213 }
214
215 return device->startWatchingTags(tags);
216 }
217
218 template <typename TClientBase>
stopWatchingTags(int out)219 status_t Camera2ClientBase<TClientBase>::stopWatchingTags(int out) {
220 sp<CameraDeviceBase> device = mDevice;
221 if (!device) {
222 dprintf(out, " Device is detached");
223 return OK;
224 }
225
226 return device->stopWatchingTags();
227 }
228
229 template <typename TClientBase>
dumpWatchedEventsToVector(std::vector<std::string> & out)230 status_t Camera2ClientBase<TClientBase>::dumpWatchedEventsToVector(std::vector<std::string> &out) {
231 sp<CameraDeviceBase> device = mDevice;
232 if (!device) {
233 // Nothing to dump if the device is detached
234 return OK;
235 }
236 return device->dumpWatchedEventsToVector(out);
237 }
238
239 template <typename TClientBase>
dumpDevice(int fd,const Vector<String16> & args)240 status_t Camera2ClientBase<TClientBase>::dumpDevice(
241 int fd,
242 const Vector<String16>& args) {
243 std::string result;
244
245 result = " Device dump:\n";
246 write(fd, result.c_str(), result.size());
247
248 sp<CameraDeviceBase> device = mDevice;
249 if (!device.get()) {
250 result = " *** Device is detached\n";
251 write(fd, result.c_str(), result.size());
252 return NO_ERROR;
253 }
254
255 status_t res = device->dump(fd, args);
256 if (res != OK) {
257 result = fmt::sprintf(" Error dumping device: %s (%d)",
258 strerror(-res), res);
259 write(fd, result.c_str(), result.size());
260 }
261
262 return NO_ERROR;
263 }
264
265 // ICameraClient2BaseUser interface
266
267 template <typename TClientBase>
disconnect()268 binder::Status Camera2ClientBase<TClientBase>::disconnect() {
269
270 if (!flags::camera_multi_client() || !TClientBase::mDisconnected) {
271 return disconnectImpl();
272 }
273 return binder::Status::ok();
274 }
275
276 template <typename TClientBase>
disconnectImpl()277 binder::Status Camera2ClientBase<TClientBase>::disconnectImpl() {
278 ATRACE_CALL();
279 ALOGD("Camera %s: start to disconnect", TClientBase::mCameraIdStr.c_str());
280 Mutex::Autolock icl(mBinderSerializationLock);
281
282 ALOGD("Camera %s: serializationLock acquired", TClientBase::mCameraIdStr.c_str());
283 binder::Status res = binder::Status::ok();
284 // Allow both client and the media server to disconnect at all times
285 int callingPid = TClientBase::getCallingPid();
286 if (callingPid != TClientBase::mCallingPid &&
287 callingPid != TClientBase::mServicePid) return res;
288
289 ALOGD("Camera %s: Shutting down", TClientBase::mCameraIdStr.c_str());
290
291 // Before detaching the device, cache the info from current open session.
292 // The disconnected check avoids duplication of info and also prevents
293 // deadlock while acquiring service lock in cacheDump.
294 if (!TClientBase::mDisconnected) {
295 ALOGD("Camera %s: start to cacheDump", TClientBase::mCameraIdStr.c_str());
296 Camera2ClientBase::getCameraService()->cacheDump();
297 }
298
299 detachDevice();
300
301 CameraService::BasicClient::disconnect();
302
303 ALOGV("Camera %s: Shut down complete", TClientBase::mCameraIdStr.c_str());
304
305 return res;
306 }
307
308 template <typename TClientBase>
detachDevice()309 void Camera2ClientBase<TClientBase>::detachDevice() {
310 if (mDevice == 0) return;
311 if (flags::camera_multi_client() && TClientBase::mSharedMode) {
312 mDevice->disconnectClient(TClientBase::getClientCallingPid());
313 } else {
314 mDevice->disconnect();
315 }
316
317 ALOGV("Camera %s: Detach complete", TClientBase::mCameraIdStr.c_str());
318 }
319
320 template <typename TClientBase>
connect(const sp<TCamCallbacks> & client)321 status_t Camera2ClientBase<TClientBase>::connect(
322 const sp<TCamCallbacks>& client) {
323 ATRACE_CALL();
324 ALOGV("%s: E", __FUNCTION__);
325 Mutex::Autolock icl(mBinderSerializationLock);
326
327 if (TClientBase::mCallingPid != 0 &&
328 TClientBase::getCallingPid() != TClientBase::mCallingPid) {
329
330 ALOGE("%s: Camera %s: Connection attempt from pid %d; "
331 "current locked to pid %d",
332 __FUNCTION__,
333 TClientBase::mCameraIdStr.c_str(),
334 TClientBase::getCallingPid(),
335 TClientBase::mCallingPid);
336 return BAD_VALUE;
337 }
338
339 TClientBase::mCallingPid = TClientBase::getCallingPid();
340
341 TClientBase::mRemoteCallback = client;
342 mSharedCameraCallbacks = client;
343
344 return OK;
345 }
346
347 /** Device-related methods */
348
349 template <typename TClientBase>
notifyError(int32_t errorCode,const CaptureResultExtras & resultExtras)350 void Camera2ClientBase<TClientBase>::notifyError(
351 int32_t errorCode,
352 const CaptureResultExtras& resultExtras) {
353 ALOGE("Error condition %d reported by HAL, requestId %" PRId32, errorCode,
354 resultExtras.requestId);
355 }
356
357 template <typename TClientBase>
notifyClientSharedAccessPriorityChanged(bool primaryClient)358 void Camera2ClientBase<TClientBase>::notifyClientSharedAccessPriorityChanged(bool primaryClient) {
359 ALOGV("%s Camera %s access priorities changed for client %d primaryClient=%d", __FUNCTION__,
360 TClientBase::mCameraIdStr.c_str(), TClientBase::getClientUid(), primaryClient);
361 }
362
363 template <typename TClientBase>
notifyPhysicalCameraChange(const std::string & physicalId)364 void Camera2ClientBase<TClientBase>::notifyPhysicalCameraChange(const std::string &physicalId) {
365 using android::hardware::ICameraService;
366 // We're only interested in this notification if rotationOverride is turned on.
367 if (TClientBase::mRotationOverride == ICameraService::ROTATION_OVERRIDE_NONE) {
368 return;
369 }
370
371 auto physicalCameraMetadata = mDevice->infoPhysical(physicalId);
372 auto orientationEntry = physicalCameraMetadata.find(ANDROID_SENSOR_ORIENTATION);
373
374 if (orientationEntry.count == 1) {
375 int orientation = orientationEntry.data.i32[0];
376 int rotateAndCropMode = ANDROID_SCALER_ROTATE_AND_CROP_NONE;
377 bool landscapeSensor = (orientation == 0 || orientation == 180);
378 if (((TClientBase::mRotationOverride ==
379 ICameraService::ROTATION_OVERRIDE_OVERRIDE_TO_PORTRAIT) && landscapeSensor) ||
380 ((wm_flags::enable_camera_compat_for_desktop_windowing() &&
381 TClientBase::mRotationOverride ==
382 ICameraService::ROTATION_OVERRIDE_ROTATION_ONLY)
383 && !landscapeSensor)) {
384 rotateAndCropMode = ANDROID_SCALER_ROTATE_AND_CROP_90;
385 }
386
387 static_cast<TClientBase *>(this)->setRotateAndCropOverride(rotateAndCropMode,
388 /*fromHal*/ true);
389 }
390 }
391
392 template <typename TClientBase>
notifyActive(float maxPreviewFps)393 status_t Camera2ClientBase<TClientBase>::notifyActive(float maxPreviewFps) {
394 if (!mDeviceActive) {
395 status_t res = TClientBase::startCameraStreamingOps();
396 if (res != OK) {
397 ALOGE("%s: Camera %s: Error starting camera streaming ops: %d", __FUNCTION__,
398 TClientBase::mCameraIdStr.c_str(), res);
399 return res;
400 }
401 mCameraServiceProxyWrapper->logActive(TClientBase::mCameraIdStr, maxPreviewFps);
402 }
403 mDeviceActive = true;
404
405 ALOGV("Camera device is now active");
406 return OK;
407 }
408
409 template <typename TClientBase>
notifyIdleWithUserTag(int64_t requestCount,int64_t resultErrorCount,bool deviceError,std::pair<int32_t,int32_t> mostRequestedFpsRange,const std::vector<hardware::CameraStreamStats> & streamStats,const std::string & userTag,int videoStabilizationMode,bool usedUltraWide,bool usedZoomOverride)410 void Camera2ClientBase<TClientBase>::notifyIdleWithUserTag(
411 int64_t requestCount, int64_t resultErrorCount, bool deviceError,
412 std::pair<int32_t, int32_t> mostRequestedFpsRange,
413 const std::vector<hardware::CameraStreamStats>& streamStats,
414 const std::string& userTag, int videoStabilizationMode, bool usedUltraWide,
415 bool usedZoomOverride) {
416 if (mDeviceActive) {
417 status_t res = TClientBase::finishCameraStreamingOps();
418 if (res != OK) {
419 ALOGE("%s: Camera %s: Error finishing streaming ops: %d", __FUNCTION__,
420 TClientBase::mCameraIdStr.c_str(), res);
421 }
422 mCameraServiceProxyWrapper->logIdle(TClientBase::mCameraIdStr,
423 requestCount, resultErrorCount, deviceError, userTag, videoStabilizationMode,
424 usedUltraWide, usedZoomOverride, mostRequestedFpsRange, streamStats);
425 }
426 mDeviceActive = false;
427
428 ALOGV("Camera device is now idle");
429 }
430
431 template <typename TClientBase>
notifyShutter(const CaptureResultExtras & resultExtras,nsecs_t timestamp)432 void Camera2ClientBase<TClientBase>::notifyShutter(
433 [[maybe_unused]] const CaptureResultExtras& resultExtras,
434 [[maybe_unused]] nsecs_t timestamp) {
435 ALOGV("%s: Shutter notification for request id %" PRId32 " at time %" PRId64,
436 __FUNCTION__, resultExtras.requestId, timestamp);
437 }
438
439 template <typename TClientBase>
notifyAutoFocus(uint8_t newState,int triggerId)440 void Camera2ClientBase<TClientBase>::notifyAutoFocus([[maybe_unused]] uint8_t newState,
441 [[maybe_unused]] int triggerId) {
442 ALOGV("%s: Autofocus state now %d, last trigger %d",
443 __FUNCTION__, newState, triggerId);
444
445 }
446
447 template <typename TClientBase>
notifyAutoExposure(uint8_t newState,int triggerId)448 void Camera2ClientBase<TClientBase>::notifyAutoExposure([[maybe_unused]] uint8_t newState,
449 [[maybe_unused]] int triggerId) {
450 ALOGV("%s: Autoexposure state now %d, last trigger %d",
451 __FUNCTION__, newState, triggerId);
452 }
453
454 template <typename TClientBase>
notifyAutoWhitebalance(uint8_t newState,int triggerId)455 void Camera2ClientBase<TClientBase>::notifyAutoWhitebalance(
456 [[maybe_unused]] uint8_t newState,
457 [[maybe_unused]] int triggerId) {
458 ALOGV("%s: Auto-whitebalance state now %d, last trigger %d",
459 __FUNCTION__, newState, triggerId);
460 }
461
462 template <typename TClientBase>
notifyPrepared(int streamId)463 void Camera2ClientBase<TClientBase>::notifyPrepared([[maybe_unused]] int streamId) {
464 ALOGV("%s: Stream %d now prepared",
465 __FUNCTION__, streamId);
466 }
467
468 template <typename TClientBase>
notifyRequestQueueEmpty()469 void Camera2ClientBase<TClientBase>::notifyRequestQueueEmpty() {
470
471 ALOGV("%s: Request queue now empty", __FUNCTION__);
472 }
473
474 template <typename TClientBase>
notifyRepeatingRequestError(long lastFrameNumber)475 void Camera2ClientBase<TClientBase>::notifyRepeatingRequestError(
476 [[maybe_unused]] long lastFrameNumber) {
477 ALOGV("%s: Repeating request was stopped. Last frame number is %ld",
478 __FUNCTION__, lastFrameNumber);
479 }
480
481 template <typename TClientBase>
getCameraId() const482 int Camera2ClientBase<TClientBase>::getCameraId() const {
483 return mApi1CameraId;
484 }
485
486 template <typename TClientBase>
getCameraDevice()487 const sp<CameraDeviceBase>& Camera2ClientBase<TClientBase>::getCameraDevice() {
488 return mDevice;
489 }
490
491 template <typename TClientBase>
getCameraService()492 const sp<CameraService>& Camera2ClientBase<TClientBase>::getCameraService() {
493 return TClientBase::sCameraService;
494 }
495
496 template <typename TClientBase>
Lock(SharedCameraCallbacks & client)497 Camera2ClientBase<TClientBase>::SharedCameraCallbacks::Lock::Lock(
498 SharedCameraCallbacks &client) :
499
500 mRemoteCallback(client.mRemoteCallback),
501 mSharedClient(client) {
502
503 mSharedClient.mRemoteCallbackLock.lock();
504 }
505
506 template <typename TClientBase>
~Lock()507 Camera2ClientBase<TClientBase>::SharedCameraCallbacks::Lock::~Lock() {
508 mSharedClient.mRemoteCallbackLock.unlock();
509 }
510
511 template <typename TClientBase>
SharedCameraCallbacks(const sp<TCamCallbacks> & client)512 Camera2ClientBase<TClientBase>::SharedCameraCallbacks::SharedCameraCallbacks(
513 const sp<TCamCallbacks>&client) :
514
515 mRemoteCallback(client) {
516 }
517
518 template <typename TClientBase>
519 typename Camera2ClientBase<TClientBase>::SharedCameraCallbacks&
operator =(const sp<TCamCallbacks> & client)520 Camera2ClientBase<TClientBase>::SharedCameraCallbacks::operator=(
521 const sp<TCamCallbacks>&client) {
522
523 Mutex::Autolock l(mRemoteCallbackLock);
524 mRemoteCallback = client;
525 return *this;
526 }
527
528 template <typename TClientBase>
clear()529 void Camera2ClientBase<TClientBase>::SharedCameraCallbacks::clear() {
530 Mutex::Autolock l(mRemoteCallbackLock);
531 mRemoteCallback.clear();
532 }
533
534 template <typename TClientBase>
injectCamera(const std::string & injectedCamId,sp<CameraProviderManager> manager)535 status_t Camera2ClientBase<TClientBase>::injectCamera(const std::string& injectedCamId,
536 sp<CameraProviderManager> manager) {
537 return mDevice->injectCamera(injectedCamId, manager);
538 }
539
540 template <typename TClientBase>
stopInjection()541 status_t Camera2ClientBase<TClientBase>::stopInjection() {
542 return mDevice->stopInjection();
543 }
544
545 template <typename TClientBase>
injectSessionParams(const CameraMetadata & sessionParams)546 status_t Camera2ClientBase<TClientBase>::injectSessionParams(
547 const CameraMetadata& sessionParams) {
548 return mDevice->injectSessionParams(sessionParams);
549 }
550
551 template class Camera2ClientBase<CameraService::Client>;
552 template class Camera2ClientBase<CameraDeviceClientBase>;
553
554 } // namespace android
555