blob: 52fe2725d115125f8a667e799fce34991cd4752c [file] [log] [blame]
Jan Sebechlebsky5cb39962023-11-22 17:33:07 +01001/*
Biswarup Pal6152a302023-12-19 12:44:09 +00002 * Copyright 2023 The Android Open Source Project
Jan Sebechlebsky5cb39962023-11-22 17:33:07 +01003 *
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_NDEBUG 0
18#define LOG_TAG "VirtualCameraDevice"
19#include "VirtualCameraDevice.h"
20
Jan Sebechlebsky3b478c42023-11-23 13:15:56 +010021#include <algorithm>
22#include <array>
Jan Sebechlebsky5cb39962023-11-22 17:33:07 +010023#include <chrono>
24#include <cstdint>
Jan Sebechlebsky3b478c42023-11-23 13:15:56 +010025#include <iterator>
26#include <optional>
Jan Sebechlebsky5cb39962023-11-22 17:33:07 +010027#include <string>
28
29#include "VirtualCameraSession.h"
Jan Sebechlebsky3b478c42023-11-23 13:15:56 +010030#include "aidl/android/companion/virtualcamera/SupportedStreamConfiguration.h"
Biswarup Pal6152a302023-12-19 12:44:09 +000031#include "aidl/android/companion/virtualcamera/VirtualCameraConfiguration.h"
Jan Sebechlebsky5cb39962023-11-22 17:33:07 +010032#include "aidl/android/hardware/camera/common/Status.h"
33#include "aidl/android/hardware/camera/device/CameraMetadata.h"
Jan Sebechlebsky3b478c42023-11-23 13:15:56 +010034#include "aidl/android/hardware/camera/device/StreamConfiguration.h"
Jan Sebechlebsky5cb39962023-11-22 17:33:07 +010035#include "android/binder_auto_utils.h"
36#include "android/binder_status.h"
37#include "log/log.h"
38#include "system/camera_metadata.h"
39#include "util/MetadataBuilder.h"
40#include "util/Util.h"
41
42namespace android {
43namespace companion {
44namespace virtualcamera {
45
Jan Sebechlebsky3b478c42023-11-23 13:15:56 +010046using ::aidl::android::companion::virtualcamera::Format;
Jan Sebechlebsky5cb39962023-11-22 17:33:07 +010047using ::aidl::android::companion::virtualcamera::IVirtualCameraCallback;
Biswarup Pal112458f2023-12-28 19:50:17 +000048using ::aidl::android::companion::virtualcamera::LensFacing;
Biswarup Pal6152a302023-12-19 12:44:09 +000049using ::aidl::android::companion::virtualcamera::SensorOrientation;
Jan Sebechlebsky3b478c42023-11-23 13:15:56 +010050using ::aidl::android::companion::virtualcamera::SupportedStreamConfiguration;
Biswarup Pal6152a302023-12-19 12:44:09 +000051using ::aidl::android::companion::virtualcamera::VirtualCameraConfiguration;
Jan Sebechlebsky5cb39962023-11-22 17:33:07 +010052using ::aidl::android::hardware::camera::common::CameraResourceCost;
53using ::aidl::android::hardware::camera::common::Status;
54using ::aidl::android::hardware::camera::device::CameraMetadata;
55using ::aidl::android::hardware::camera::device::ICameraDeviceCallback;
56using ::aidl::android::hardware::camera::device::ICameraDeviceSession;
57using ::aidl::android::hardware::camera::device::ICameraInjectionSession;
Jan Sebechlebsky3b478c42023-11-23 13:15:56 +010058using ::aidl::android::hardware::camera::device::Stream;
Jan Sebechlebsky5cb39962023-11-22 17:33:07 +010059using ::aidl::android::hardware::camera::device::StreamConfiguration;
60using ::aidl::android::hardware::camera::device::StreamRotation;
61using ::aidl::android::hardware::camera::device::StreamType;
62using ::aidl::android::hardware::graphics::common::PixelFormat;
63
64namespace {
65
66using namespace std::chrono_literals;
67
68// Prefix of camera name - "device@1.1/virtual/{numerical_id}"
69const char* kDevicePathPrefix = "device@1.1/virtual/";
70
Jan Sebechlebsky5cb39962023-11-22 17:33:07 +010071constexpr int32_t kMaxJpegSize = 3 * 1024 * 1024 /*3MiB*/;
72
73constexpr MetadataBuilder::ControlRegion kDefaultEmptyControlRegion{};
74
Jan Sebechlebsky8ae23592024-02-02 16:08:18 +010075const std::array<PixelFormat, 3> kOutputFormats{
76 PixelFormat::IMPLEMENTATION_DEFINED, PixelFormat::YCBCR_420_888,
77 PixelFormat::BLOB};
Biswarup Pal6152a302023-12-19 12:44:09 +000078
Jan Sebechlebsky3b478c42023-11-23 13:15:56 +010079struct Resolution {
80 Resolution(const int w, const int h) : width(w), height(h) {
81 }
82
83 bool operator<(const Resolution& other) const {
84 return width * height < other.width * other.height;
85 }
86
87 bool operator==(const Resolution& other) const {
88 return width == other.width && height == other.height;
89 }
90
91 const int width;
92 const int height;
93};
94
Jan Sebechlebsky8ae23592024-02-02 16:08:18 +010095bool isSupportedOutputFormat(const PixelFormat pixelFormat) {
96 return std::find(kOutputFormats.begin(), kOutputFormats.end(), pixelFormat) !=
97 kOutputFormats.end();
98}
99
Jan Sebechlebsky3b478c42023-11-23 13:15:56 +0100100std::optional<Resolution> getMaxResolution(
101 const std::vector<SupportedStreamConfiguration>& configs) {
102 auto itMax = std::max_element(configs.begin(), configs.end(),
103 [](const SupportedStreamConfiguration& a,
104 const SupportedStreamConfiguration& b) {
105 return a.width * b.height < a.width * b.height;
106 });
107 if (itMax == configs.end()) {
108 ALOGE(
109 "%s: empty vector of supported configurations, cannot find largest "
110 "resolution.",
111 __func__);
112 return std::nullopt;
113 }
114
115 return Resolution(itMax->width, itMax->height);
116}
117
Biswarup Pal6152a302023-12-19 12:44:09 +0000118// Returns a map of unique resolution to maximum maxFps for all streams with
119// that resolution.
120std::map<Resolution, int> getResolutionToMaxFpsMap(
Jan Sebechlebsky3b478c42023-11-23 13:15:56 +0100121 const std::vector<SupportedStreamConfiguration>& configs) {
Biswarup Pal6152a302023-12-19 12:44:09 +0000122 std::map<Resolution, int> resolutionToMaxFpsMap;
123
124 for (const SupportedStreamConfiguration& config : configs) {
125 Resolution resolution(config.width, config.height);
126 if (resolutionToMaxFpsMap.find(resolution) == resolutionToMaxFpsMap.end()) {
127 resolutionToMaxFpsMap[resolution] = config.maxFps;
128 } else {
129 int currentMaxFps = resolutionToMaxFpsMap[resolution];
130 resolutionToMaxFpsMap[resolution] = std::max(currentMaxFps, config.maxFps);
131 }
132 }
133
134 return resolutionToMaxFpsMap;
Jan Sebechlebsky3b478c42023-11-23 13:15:56 +0100135}
136
Jan Sebechlebsky5cb39962023-11-22 17:33:07 +0100137// TODO(b/301023410) - Populate camera characteristics according to camera configuration.
Jan Sebechlebsky3b478c42023-11-23 13:15:56 +0100138std::optional<CameraMetadata> initCameraCharacteristics(
Biswarup Pal6152a302023-12-19 12:44:09 +0000139 const std::vector<SupportedStreamConfiguration>& supportedInputConfig,
Biswarup Pal112458f2023-12-28 19:50:17 +0000140 const SensorOrientation sensorOrientation, const LensFacing lensFacing) {
Jan Sebechlebsky3b478c42023-11-23 13:15:56 +0100141 if (!std::all_of(supportedInputConfig.begin(), supportedInputConfig.end(),
142 [](const SupportedStreamConfiguration& config) {
Jan Sebechlebsky042d1fb2023-12-12 16:37:00 +0100143 return isFormatSupportedForInput(
Biswarup Pal6152a302023-12-19 12:44:09 +0000144 config.width, config.height, config.pixelFormat,
145 config.maxFps);
Jan Sebechlebsky3b478c42023-11-23 13:15:56 +0100146 })) {
Jan Sebechlebsky042d1fb2023-12-12 16:37:00 +0100147 ALOGE("%s: input configuration contains unsupported format", __func__);
Jan Sebechlebsky3b478c42023-11-23 13:15:56 +0100148 return std::nullopt;
149 }
150
151 MetadataBuilder builder =
Jan Sebechlebsky5cb39962023-11-22 17:33:07 +0100152 MetadataBuilder()
153 .setSupportedHardwareLevel(
154 ANDROID_INFO_SUPPORTED_HARDWARE_LEVEL_EXTERNAL)
155 .setFlashAvailable(false)
Biswarup Pal112458f2023-12-28 19:50:17 +0000156 .setLensFacing(
157 static_cast<camera_metadata_enum_android_lens_facing>(lensFacing))
Biswarup Pald9be04d2024-01-31 14:35:15 +0000158 .setFocalLength(43.0)
Biswarup Pal6152a302023-12-19 12:44:09 +0000159 .setSensorOrientation(static_cast<int32_t>(sensorOrientation))
Jan Sebechlebskya984ffb2024-02-01 09:12:37 +0100160 .setSensorReadoutTimestamp(
161 ANDROID_SENSOR_READOUT_TIMESTAMP_NOT_SUPPORTED)
Biswarup Pald9be04d2024-01-31 14:35:15 +0000162 .setSensorPhysicalSize(36.0, 24.0)
Jan Sebechlebsky5cb39962023-11-22 17:33:07 +0100163 .setAvailableFaceDetectModes({ANDROID_STATISTICS_FACE_DETECT_MODE_OFF})
Jan Sebechlebskyb0119fa2023-12-04 10:29:06 +0100164 .setAvailableMaxDigitalZoom(1.0)
165 .setControlAvailableModes({ANDROID_CONTROL_MODE_AUTO})
Jan Sebechlebsky5cb39962023-11-22 17:33:07 +0100166 .setControlAfAvailableModes({ANDROID_CONTROL_AF_MODE_OFF})
Jan Sebechlebsky4425a732024-01-31 11:31:54 +0100167 .setControlAvailableSceneModes({ANDROID_CONTROL_SCENE_MODE_DISABLED})
168 .setControlAvailableEffects({ANDROID_CONTROL_EFFECT_MODE_OFF})
Jan Sebechlebsky5cb39962023-11-22 17:33:07 +0100169 .setControlAeAvailableFpsRange(10, 30)
170 .setControlMaxRegions(0, 0, 0)
Jan Sebechlebsky5cb39962023-11-22 17:33:07 +0100171 .setControlAfRegions({kDefaultEmptyControlRegion})
172 .setControlAeRegions({kDefaultEmptyControlRegion})
173 .setControlAwbRegions({kDefaultEmptyControlRegion})
174 .setControlAeCompensationRange(0, 1)
175 .setControlAeCompensationStep(camera_metadata_rational_t{0, 1})
Jan Sebechlebsky4425a732024-01-31 11:31:54 +0100176 .setControlAwbLockAvailable(false)
177 .setControlAeLockAvailable(false)
178 .setControlAvailableAwbModes({ANDROID_CONTROL_AWB_MODE_AUTO})
Jan Sebechlebsky6ab07fe2023-12-05 15:23:34 +0100179 .setControlZoomRatioRange(/*min=*/1.0, /*max=*/1.0)
Jan Sebechlebsky5cb39962023-11-22 17:33:07 +0100180 .setMaxJpegSize(kMaxJpegSize)
Jan Sebechlebsky8ae23592024-02-02 16:08:18 +0100181 .setMaxNumberOutputStreams(
182 VirtualCameraDevice::kMaxNumberOfRawStreams,
183 VirtualCameraDevice::kMaxNumberOfProcessedStreams,
184 VirtualCameraDevice::kMaxNumberOfStallStreams)
Jan Sebechlebsky4425a732024-01-31 11:31:54 +0100185 .setSyncMaxLatency(ANDROID_SYNC_MAX_LATENCY_UNKNOWN)
186 .setAvailableRequestKeys({})
Jan Sebechlebsky5cb39962023-11-22 17:33:07 +0100187 .setAvailableRequestKeys({ANDROID_CONTROL_AF_MODE})
188 .setAvailableResultKeys({ANDROID_CONTROL_AF_MODE})
189 .setAvailableCapabilities(
190 {ANDROID_REQUEST_AVAILABLE_CAPABILITIES_BACKWARD_COMPATIBLE})
Jan Sebechlebsky3b478c42023-11-23 13:15:56 +0100191 .setAvailableCharacteristicKeys();
Jan Sebechlebsky5cb39962023-11-22 17:33:07 +0100192
Jan Sebechlebsky3b478c42023-11-23 13:15:56 +0100193 // Active array size must correspond to largest supported input resolution.
194 std::optional<Resolution> maxResolution =
195 getMaxResolution(supportedInputConfig);
196 if (!maxResolution.has_value()) {
197 return std::nullopt;
198 }
199 builder.setSensorActiveArraySize(0, 0, maxResolution->width,
200 maxResolution->height);
Biswarup Pald9be04d2024-01-31 14:35:15 +0000201 builder.setSensorPixelArraySize(maxResolution->width, maxResolution->height);
Jan Sebechlebsky3b478c42023-11-23 13:15:56 +0100202
203 std::vector<MetadataBuilder::StreamConfiguration> outputConfigurations;
204
205 // TODO(b/301023410) Add also all "standard" resolutions we can rescale the
206 // streams to (all standard resolutions with same aspect ratio).
207
Biswarup Pal6152a302023-12-19 12:44:09 +0000208 std::map<Resolution, int> resolutionToMaxFpsMap =
209 getResolutionToMaxFpsMap(supportedInputConfig);
Jan Sebechlebsky3b478c42023-11-23 13:15:56 +0100210
Biswarup Pal6152a302023-12-19 12:44:09 +0000211 // Add configurations for all unique input resolutions and output formats.
Jan Sebechlebsky8ae23592024-02-02 16:08:18 +0100212 for (const PixelFormat format : kOutputFormats) {
Biswarup Pal6152a302023-12-19 12:44:09 +0000213 std::transform(
214 resolutionToMaxFpsMap.begin(), resolutionToMaxFpsMap.end(),
215 std::back_inserter(outputConfigurations), [format](const auto& entry) {
216 Resolution resolution = entry.first;
217 int maxFps = entry.second;
218 return MetadataBuilder::StreamConfiguration{
219 .width = resolution.width,
220 .height = resolution.height,
Jan Sebechlebsky8ae23592024-02-02 16:08:18 +0100221 .format = static_cast<int32_t>(format),
Biswarup Pal6152a302023-12-19 12:44:09 +0000222 .minFrameDuration = std::chrono::nanoseconds(1s) / maxFps,
223 .minStallDuration = 0s};
224 });
225 }
Jan Sebechlebsky3b478c42023-11-23 13:15:56 +0100226
227 ALOGV("Adding %zu output configurations", outputConfigurations.size());
228 builder.setAvailableOutputStreamConfigurations(outputConfigurations);
229
230 auto metadata = builder.build();
Jan Sebechlebsky5cb39962023-11-22 17:33:07 +0100231 if (metadata == nullptr) {
232 ALOGE("Failed to build metadata!");
233 return CameraMetadata();
234 }
235
236 return std::move(*metadata);
237}
238
239} // namespace
240
241VirtualCameraDevice::VirtualCameraDevice(
Biswarup Pal6152a302023-12-19 12:44:09 +0000242 const uint32_t cameraId, const VirtualCameraConfiguration& configuration)
Jan Sebechlebsky5cb39962023-11-22 17:33:07 +0100243 : mCameraId(cameraId),
Biswarup Pal6152a302023-12-19 12:44:09 +0000244 mVirtualCameraClientCallback(configuration.virtualCameraCallback),
245 mSupportedInputConfigurations(configuration.supportedStreamConfigs) {
246 std::optional<CameraMetadata> metadata = initCameraCharacteristics(
Biswarup Pal112458f2023-12-28 19:50:17 +0000247 mSupportedInputConfigurations, configuration.sensorOrientation,
248 configuration.lensFacing);
Jan Sebechlebsky3b478c42023-11-23 13:15:56 +0100249 if (metadata.has_value()) {
250 mCameraCharacteristics = *metadata;
251 } else {
252 ALOGE(
253 "%s: Failed to initialize camera characteristic based on provided "
254 "configuration.",
255 __func__);
256 }
Jan Sebechlebsky5cb39962023-11-22 17:33:07 +0100257}
258
259ndk::ScopedAStatus VirtualCameraDevice::getCameraCharacteristics(
260 CameraMetadata* _aidl_return) {
261 ALOGV("%s", __func__);
262 if (_aidl_return == nullptr) {
263 return cameraStatus(Status::ILLEGAL_ARGUMENT);
264 }
265
266 *_aidl_return = mCameraCharacteristics;
267 return ndk::ScopedAStatus::ok();
268}
269
270ndk::ScopedAStatus VirtualCameraDevice::getPhysicalCameraCharacteristics(
271 const std::string& in_physicalCameraId, CameraMetadata* _aidl_return) {
272 ALOGV("%s: physicalCameraId %s", __func__, in_physicalCameraId.c_str());
273 (void)_aidl_return;
274
275 // VTS tests expect this call to fail with illegal argument status for
276 // all publicly advertised camera ids.
277 // Because we don't support physical camera ids, we just always
278 // fail with illegal argument (there's no valid argument to provide).
279 return cameraStatus(Status::ILLEGAL_ARGUMENT);
280}
281
282ndk::ScopedAStatus VirtualCameraDevice::getResourceCost(
283 CameraResourceCost* _aidl_return) {
284 ALOGV("%s", __func__);
285 if (_aidl_return == nullptr) {
286 return cameraStatus(Status::ILLEGAL_ARGUMENT);
287 }
288 _aidl_return->resourceCost = 100; // ¯\_(ツ)_/¯
289 return ndk::ScopedAStatus::ok();
290}
291
292ndk::ScopedAStatus VirtualCameraDevice::isStreamCombinationSupported(
293 const StreamConfiguration& in_streams, bool* _aidl_return) {
294 ALOGV("%s", __func__);
295
296 if (_aidl_return == nullptr) {
297 return cameraStatus(Status::ILLEGAL_ARGUMENT);
298 }
299
Jan Sebechlebsky3b478c42023-11-23 13:15:56 +0100300 *_aidl_return = isStreamCombinationSupported(in_streams);
301 return ndk::ScopedAStatus::ok();
302};
303
304bool VirtualCameraDevice::isStreamCombinationSupported(
305 const StreamConfiguration& streamConfiguration) const {
Jan Sebechlebsky39129f82024-01-19 16:42:11 +0100306 if (streamConfiguration.streams.empty()) {
307 ALOGE("%s: Querying empty configuration", __func__);
308 return false;
309 }
310
Jan Sebechlebsky8ae23592024-02-02 16:08:18 +0100311 int numberOfProcessedStreams = 0;
312 int numberOfStallStreams = 0;
Jan Sebechlebsky3b478c42023-11-23 13:15:56 +0100313 for (const Stream& stream : streamConfiguration.streams) {
Jan Sebechlebsky5cb39962023-11-22 17:33:07 +0100314 ALOGV("%s: Configuration queried: %s", __func__, stream.toString().c_str());
315
316 if (stream.streamType == StreamType::INPUT) {
317 ALOGW("%s: Input stream type is not supported", __func__);
Jan Sebechlebsky3b478c42023-11-23 13:15:56 +0100318 return false;
Jan Sebechlebsky5cb39962023-11-22 17:33:07 +0100319 }
320
Jan Sebechlebsky3b478c42023-11-23 13:15:56 +0100321 if (stream.rotation != StreamRotation::ROTATION_0 ||
Jan Sebechlebsky8ae23592024-02-02 16:08:18 +0100322 !isSupportedOutputFormat(stream.format)) {
Jan Sebechlebsky3b478c42023-11-23 13:15:56 +0100323 ALOGV("Unsupported output stream type");
324 return false;
325 }
326
Jan Sebechlebsky8ae23592024-02-02 16:08:18 +0100327 if (stream.format == PixelFormat::BLOB) {
328 numberOfStallStreams++;
329 } else {
330 numberOfProcessedStreams++;
331 }
332
Jan Sebechlebsky3b478c42023-11-23 13:15:56 +0100333 auto matchesSupportedInputConfig =
334 [&stream](const SupportedStreamConfiguration& config) {
335 return stream.width == config.width && stream.height == config.height;
336 };
337 if (std::none_of(mSupportedInputConfigurations.begin(),
338 mSupportedInputConfigurations.end(),
339 matchesSupportedInputConfig)) {
340 ALOGV("Requested config doesn't match any supported input config");
341 return false;
Jan Sebechlebsky5cb39962023-11-22 17:33:07 +0100342 }
343 }
Jan Sebechlebsky8ae23592024-02-02 16:08:18 +0100344
345 if (numberOfProcessedStreams > kMaxNumberOfProcessedStreams) {
346 ALOGE("%s: %d processed streams exceeds the supported maximum of %d",
347 __func__, numberOfProcessedStreams, kMaxNumberOfProcessedStreams);
348 return false;
349 }
350
351 if (numberOfStallStreams > kMaxNumberOfStallStreams) {
352 ALOGE("%s: %d stall streams exceeds the supported maximum of %d", __func__,
353 numberOfStallStreams, kMaxNumberOfStallStreams);
354 return false;
355 }
356
Jan Sebechlebsky3b478c42023-11-23 13:15:56 +0100357 return true;
358}
Jan Sebechlebsky5cb39962023-11-22 17:33:07 +0100359
360ndk::ScopedAStatus VirtualCameraDevice::open(
361 const std::shared_ptr<ICameraDeviceCallback>& in_callback,
362 std::shared_ptr<ICameraDeviceSession>* _aidl_return) {
363 ALOGV("%s", __func__);
364
365 *_aidl_return = ndk::SharedRefBase::make<VirtualCameraSession>(
Jan Sebechlebsky0bb5e092023-12-08 16:17:54 +0100366 sharedFromThis(), in_callback, mVirtualCameraClientCallback);
Jan Sebechlebsky5cb39962023-11-22 17:33:07 +0100367
368 return ndk::ScopedAStatus::ok();
369};
370
371ndk::ScopedAStatus VirtualCameraDevice::openInjectionSession(
372 const std::shared_ptr<ICameraDeviceCallback>& in_callback,
373 std::shared_ptr<ICameraInjectionSession>* _aidl_return) {
374 ALOGV("%s", __func__);
375
376 (void)in_callback;
377 (void)_aidl_return;
378 return cameraStatus(Status::OPERATION_NOT_SUPPORTED);
379}
380
381ndk::ScopedAStatus VirtualCameraDevice::setTorchMode(bool in_on) {
382 ALOGV("%s: on = %s", __func__, in_on ? "on" : "off");
383 return cameraStatus(Status::OPERATION_NOT_SUPPORTED);
384}
385
386ndk::ScopedAStatus VirtualCameraDevice::turnOnTorchWithStrengthLevel(
387 int32_t in_torchStrength) {
388 ALOGV("%s: torchStrength = %d", __func__, in_torchStrength);
389 return cameraStatus(Status::OPERATION_NOT_SUPPORTED);
390}
391
392ndk::ScopedAStatus VirtualCameraDevice::getTorchStrengthLevel(
393 int32_t* _aidl_return) {
394 (void)_aidl_return;
395 return cameraStatus(Status::OPERATION_NOT_SUPPORTED);
396}
397
398binder_status_t VirtualCameraDevice::dump(int fd, const char** args,
399 uint32_t numArgs) {
400 // TODO(b/301023410) Implement.
401 (void)fd;
402 (void)args;
403 (void)numArgs;
404 return STATUS_OK;
405}
406
407std::string VirtualCameraDevice::getCameraName() const {
408 return std::string(kDevicePathPrefix) + std::to_string(mCameraId);
409}
410
Jan Sebechlebsky0bb5e092023-12-08 16:17:54 +0100411std::shared_ptr<VirtualCameraDevice> VirtualCameraDevice::sharedFromThis() {
412 // SharedRefBase which BnCameraDevice inherits from breaks
413 // std::enable_shared_from_this. This is recommended replacement for
414 // shared_from_this() per documentation in binder_interface_utils.h.
415 return ref<VirtualCameraDevice>();
416}
417
Jan Sebechlebsky5cb39962023-11-22 17:33:07 +0100418} // namespace virtualcamera
419} // namespace companion
420} // namespace android