blob: b78f1206c323158069a622f129366ffd4acf7328 [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
Jan Sebechlebsky5cb39962023-11-22 17:33:07 +010018#define LOG_TAG "VirtualCameraService"
19#include "VirtualCameraService.h"
20
Jan Sebechlebsky773c0142024-03-25 12:17:05 +010021#include <algorithm>
Jan Sebechlebskyb36090e2024-04-11 09:19:33 +020022#include <array>
Jan Sebechlebsky5cb39962023-11-22 17:33:07 +010023#include <cinttypes>
24#include <cstdint>
Jan Sebechlebsky5cb39962023-11-22 17:33:07 +010025#include <memory>
26#include <mutex>
Jan Sebechlebsky773c0142024-03-25 12:17:05 +010027#include <optional>
28#include <regex>
29#include <variant>
Jan Sebechlebsky5cb39962023-11-22 17:33:07 +010030
31#include "VirtualCameraDevice.h"
32#include "VirtualCameraProvider.h"
Jan Sebechlebsky288900f2024-05-24 14:47:54 +020033#include "VirtualCameraTestInstance.h"
Jan Sebechlebsky3b478c42023-11-23 13:15:56 +010034#include "aidl/android/companion/virtualcamera/Format.h"
Jan Sebechlebsky773c0142024-03-25 12:17:05 +010035#include "aidl/android/companion/virtualcamera/LensFacing.h"
Jan Sebechlebsky3b478c42023-11-23 13:15:56 +010036#include "aidl/android/companion/virtualcamera/VirtualCameraConfiguration.h"
Jan Sebechlebsky5cb39962023-11-22 17:33:07 +010037#include "android/binder_auto_utils.h"
Jan Sebechlebsky288900f2024-05-24 14:47:54 +020038#include "android/binder_interface_utils.h"
Jan Sebechlebsky5cb39962023-11-22 17:33:07 +010039#include "android/binder_libbinder.h"
Jan Sebechlebsky773c0142024-03-25 12:17:05 +010040#include "android/binder_status.h"
Jan Sebechlebsky5cb39962023-11-22 17:33:07 +010041#include "binder/Status.h"
Jan Sebechlebskyb36090e2024-04-11 09:19:33 +020042#include "fmt/format.h"
43#include "util/EglDisplayContext.h"
44#include "util/EglUtil.h"
Jan Sebechlebskyde6f16f2023-11-29 09:27:36 +010045#include "util/Permissions.h"
Jan Sebechlebsky3b478c42023-11-23 13:15:56 +010046#include "util/Util.h"
Jan Sebechlebsky5cb39962023-11-22 17:33:07 +010047
48using ::android::binder::Status;
49
50namespace android {
51namespace companion {
52namespace virtualcamera {
53
Jan Sebechlebsky3b478c42023-11-23 13:15:56 +010054using ::aidl::android::companion::virtualcamera::Format;
Biswarup Pal112458f2023-12-28 19:50:17 +000055using ::aidl::android::companion::virtualcamera::LensFacing;
Biswarup Pal6152a302023-12-19 12:44:09 +000056using ::aidl::android::companion::virtualcamera::SensorOrientation;
Jan Sebechlebsky3b478c42023-11-23 13:15:56 +010057using ::aidl::android::companion::virtualcamera::SupportedStreamConfiguration;
Jan Sebechlebsky5cb39962023-11-22 17:33:07 +010058using ::aidl::android::companion::virtualcamera::VirtualCameraConfiguration;
59
Marvin Ramina8196132024-03-15 15:55:22 +000060// TODO(b/301023410) Make camera id range configurable / dynamic
61// based on already registered devices.
62std::atomic_int VirtualCameraService::sNextId{1000};
63
Jan Sebechlebsky5cb39962023-11-22 17:33:07 +010064namespace {
65
Jan Sebechlebsky3b478c42023-11-23 13:15:56 +010066constexpr int kVgaWidth = 640;
67constexpr int kVgaHeight = 480;
Biswarup Pal6152a302023-12-19 12:44:09 +000068constexpr int kMaxFps = 60;
Jan Sebechlebsky288900f2024-05-24 14:47:54 +020069constexpr int kTestCameraInputFps = 30;
Jan Sebechlebsky5cb39962023-11-22 17:33:07 +010070constexpr char kEnableTestCameraCmd[] = "enable_test_camera";
71constexpr char kDisableTestCameraCmd[] = "disable_test_camera";
Jan Sebechlebsky773c0142024-03-25 12:17:05 +010072constexpr char kHelp[] = "help";
Jan Sebechlebsky5cb39962023-11-22 17:33:07 +010073constexpr char kShellCmdHelp[] = R"(
Jan Sebechlebsky773c0142024-03-25 12:17:05 +010074Usage:
75 cmd virtual_camera command [--option=value]
Jan Sebechlebsky5cb39962023-11-22 17:33:07 +010076Available commands:
77 * enable_test_camera
Jan Sebechlebsky773c0142024-03-25 12:17:05 +010078 Options:
79 --camera_id=(ID) - override numerical ID for test camera instance
80 --lens_facing=(front|back|external) - specifies lens facing for test camera instance
Jan Sebechlebsky5cb39962023-11-22 17:33:07 +010081 * disable_test_camera
82)";
Jan Sebechlebskyde6f16f2023-11-29 09:27:36 +010083constexpr char kCreateVirtualDevicePermission[] =
84 "android.permission.CREATE_VIRTUAL_DEVICE";
Jan Sebechlebsky5cb39962023-11-22 17:33:07 +010085
Jan Sebechlebskyb36090e2024-04-11 09:19:33 +020086constexpr std::array<const char*, 3> kRequiredEglExtensions = {
87 "GL_OES_EGL_image_external",
88 "GL_OES_EGL_image_external_essl3",
89 "GL_EXT_YUV_target",
90};
91
Jan Sebechlebsky3b478c42023-11-23 13:15:56 +010092ndk::ScopedAStatus validateConfiguration(
93 const VirtualCameraConfiguration& configuration) {
94 if (configuration.supportedStreamConfigs.empty()) {
95 ALOGE("%s: No supported input configuration specified", __func__);
96 return ndk::ScopedAStatus::fromServiceSpecificError(
97 Status::EX_ILLEGAL_ARGUMENT);
98 }
99
Jan Sebechlebsky288900f2024-05-24 14:47:54 +0200100 if (configuration.virtualCameraCallback == nullptr) {
101 ALOGE("%s: Input configuration is missing virtual camera callback",
102 __func__);
103 return ndk::ScopedAStatus::fromServiceSpecificError(
104 Status::EX_ILLEGAL_ARGUMENT);
105 }
106
Jan Sebechlebsky3b478c42023-11-23 13:15:56 +0100107 for (const SupportedStreamConfiguration& config :
108 configuration.supportedStreamConfigs) {
109 if (!isFormatSupportedForInput(config.width, config.height,
Biswarup Pal6152a302023-12-19 12:44:09 +0000110 config.pixelFormat, config.maxFps)) {
Jan Sebechlebsky3b478c42023-11-23 13:15:56 +0100111 ALOGE("%s: Requested unsupported input format: %d x %d (%d)", __func__,
112 config.width, config.height, static_cast<int>(config.pixelFormat));
113 return ndk::ScopedAStatus::fromServiceSpecificError(
114 Status::EX_ILLEGAL_ARGUMENT);
115 }
116 }
Biswarup Pal6152a302023-12-19 12:44:09 +0000117
118 if (configuration.sensorOrientation != SensorOrientation::ORIENTATION_0 &&
119 configuration.sensorOrientation != SensorOrientation::ORIENTATION_90 &&
120 configuration.sensorOrientation != SensorOrientation::ORIENTATION_180 &&
121 configuration.sensorOrientation != SensorOrientation::ORIENTATION_270) {
122 return ndk::ScopedAStatus::fromServiceSpecificError(
123 Status::EX_ILLEGAL_ARGUMENT);
124 }
125
Biswarup Pal112458f2023-12-28 19:50:17 +0000126 if (configuration.lensFacing != LensFacing::FRONT &&
127 configuration.lensFacing != LensFacing::BACK &&
128 configuration.lensFacing != LensFacing::EXTERNAL) {
129 return ndk::ScopedAStatus::fromServiceSpecificError(
130 Status::EX_ILLEGAL_ARGUMENT);
131 }
132
Jan Sebechlebsky3b478c42023-11-23 13:15:56 +0100133 return ndk::ScopedAStatus::ok();
134}
135
Jan Sebechlebsky773c0142024-03-25 12:17:05 +0100136enum class Command {
137 ENABLE_TEST_CAMERA,
138 DISABLE_TEST_CAMERA,
139 HELP,
140};
141
142struct CommandWithOptions {
143 Command command;
144 std::map<std::string, std::string> optionToValueMap;
145};
146
147std::optional<int> parseInt(const std::string& s) {
148 if (!std::all_of(s.begin(), s.end(), [](char c) { return std::isdigit(c); })) {
149 return std::nullopt;
150 }
151 int ret = atoi(s.c_str());
152 return ret > 0 ? std::optional(ret) : std::nullopt;
153}
154
155std::optional<LensFacing> parseLensFacing(const std::string& s) {
156 static const std::map<std::string, LensFacing> strToLensFacing{
157 {"front", LensFacing::FRONT},
158 {"back", LensFacing::BACK},
159 {"external", LensFacing::EXTERNAL}};
160 auto it = strToLensFacing.find(s);
161 return it == strToLensFacing.end() ? std::nullopt : std::optional(it->second);
162}
163
164std::variant<CommandWithOptions, std::string> parseCommand(
165 const char** args, const uint32_t numArgs) {
166 static const std::regex optionRegex("^--(\\w+)(?:=(.+))?$");
167 static const std::map<std::string, Command> strToCommand{
168 {kHelp, Command::HELP},
169 {kEnableTestCameraCmd, Command::ENABLE_TEST_CAMERA},
170 {kDisableTestCameraCmd, Command::DISABLE_TEST_CAMERA}};
171
172 if (numArgs < 1) {
173 return CommandWithOptions{.command = Command::HELP};
174 }
175
176 // We interpret the first argument as command;
177 auto it = strToCommand.find(args[0]);
178 if (it == strToCommand.end()) {
179 return "Unknown command: " + std::string(args[0]);
180 }
181
182 CommandWithOptions cmd{.command = it->second};
183
184 for (int i = 1; i < numArgs; i++) {
185 std::cmatch cm;
186 if (!std::regex_match(args[i], cm, optionRegex)) {
187 return "Not an option: " + std::string(args[i]);
188 }
189
190 cmd.optionToValueMap[cm[1]] = cm[2];
191 }
192
193 return cmd;
194};
195
Jan Sebechlebskyb36090e2024-04-11 09:19:33 +0200196ndk::ScopedAStatus verifyRequiredEglExtensions() {
197 EglDisplayContext context;
198 for (const char* eglExtension : kRequiredEglExtensions) {
199 if (!isGlExtensionSupported(eglExtension)) {
200 ALOGE("%s not supported", eglExtension);
201 return ndk::ScopedAStatus::fromExceptionCodeWithMessage(
202 EX_UNSUPPORTED_OPERATION,
203 fmt::format(
204 "Cannot create virtual camera, because required EGL extension {} "
205 "is not supported on this system",
206 eglExtension)
207 .c_str());
208 }
209 }
210 return ndk::ScopedAStatus::ok();
211}
212
Jan Sebechlebsky5cb39962023-11-22 17:33:07 +0100213} // namespace
214
215VirtualCameraService::VirtualCameraService(
Jan Sebechlebskyde6f16f2023-11-29 09:27:36 +0100216 std::shared_ptr<VirtualCameraProvider> virtualCameraProvider,
217 const PermissionsProxy& permissionProxy)
218 : mVirtualCameraProvider(virtualCameraProvider),
219 mPermissionProxy(permissionProxy) {
Jan Sebechlebsky5cb39962023-11-22 17:33:07 +0100220}
221
222ndk::ScopedAStatus VirtualCameraService::registerCamera(
223 const ::ndk::SpAIBinder& token,
Biswarup Pal37a75182024-01-16 15:53:35 +0000224 const VirtualCameraConfiguration& configuration, const int32_t deviceId,
225 bool* _aidl_return) {
226 return registerCamera(token, configuration, sNextId++, deviceId, _aidl_return);
Marvin Ramina8196132024-03-15 15:55:22 +0000227}
228
229ndk::ScopedAStatus VirtualCameraService::registerCamera(
230 const ::ndk::SpAIBinder& token,
231 const VirtualCameraConfiguration& configuration, const int cameraId,
Biswarup Pal37a75182024-01-16 15:53:35 +0000232 const int32_t deviceId, bool* _aidl_return) {
Jan Sebechlebskyde6f16f2023-11-29 09:27:36 +0100233 if (!mPermissionProxy.checkCallingPermission(kCreateVirtualDevicePermission)) {
234 ALOGE("%s: caller (pid %d, uid %d) doesn't hold %s permission", __func__,
235 getpid(), getuid(), kCreateVirtualDevicePermission);
236 return ndk::ScopedAStatus::fromExceptionCode(EX_SECURITY);
237 }
238
Jan Sebechlebsky5cb39962023-11-22 17:33:07 +0100239 if (_aidl_return == nullptr) {
240 return ndk::ScopedAStatus::fromServiceSpecificError(
241 Status::EX_ILLEGAL_ARGUMENT);
242 }
Jan Sebechlebskyde6f16f2023-11-29 09:27:36 +0100243
Jan Sebechlebskyb36090e2024-04-11 09:19:33 +0200244 if (mVerifyEglExtensions) {
245 auto status = verifyRequiredEglExtensions();
246 if (!status.isOk()) {
247 *_aidl_return = false;
248 return status;
249 }
250 }
Jan Sebechlebsky5cb39962023-11-22 17:33:07 +0100251
Jan Sebechlebsky3b478c42023-11-23 13:15:56 +0100252 auto status = validateConfiguration(configuration);
253 if (!status.isOk()) {
254 *_aidl_return = false;
255 return status;
256 }
257
Jan Sebechlebsky5cb39962023-11-22 17:33:07 +0100258 std::lock_guard lock(mLock);
259 if (mTokenToCameraName.find(token) != mTokenToCameraName.end()) {
260 ALOGE(
261 "Attempt to register camera corresponding to already registered binder "
262 "token: "
263 "0x%" PRIxPTR,
264 reinterpret_cast<uintptr_t>(token.get()));
265 *_aidl_return = false;
Jan Sebechlebsky3b478c42023-11-23 13:15:56 +0100266 return ndk::ScopedAStatus::ok();
Jan Sebechlebsky5cb39962023-11-22 17:33:07 +0100267 }
268
Jan Sebechlebsky5cb39962023-11-22 17:33:07 +0100269 std::shared_ptr<VirtualCameraDevice> camera =
Biswarup Pal37a75182024-01-16 15:53:35 +0000270 mVirtualCameraProvider->createCamera(configuration, cameraId, deviceId);
Jan Sebechlebsky5cb39962023-11-22 17:33:07 +0100271 if (camera == nullptr) {
272 ALOGE("Failed to create camera for binder token 0x%" PRIxPTR,
273 reinterpret_cast<uintptr_t>(token.get()));
274 *_aidl_return = false;
275 return ndk::ScopedAStatus::fromServiceSpecificError(
276 Status::EX_SERVICE_SPECIFIC);
277 }
278
279 mTokenToCameraName[token] = camera->getCameraName();
Jan Sebechlebskyb36090e2024-04-11 09:19:33 +0200280 *_aidl_return = true;
Jan Sebechlebsky5cb39962023-11-22 17:33:07 +0100281 return ndk::ScopedAStatus::ok();
282}
283
284ndk::ScopedAStatus VirtualCameraService::unregisterCamera(
285 const ::ndk::SpAIBinder& token) {
Jan Sebechlebskyde6f16f2023-11-29 09:27:36 +0100286 if (!mPermissionProxy.checkCallingPermission(kCreateVirtualDevicePermission)) {
287 ALOGE("%s: caller (pid %d, uid %d) doesn't hold %s permission", __func__,
288 getpid(), getuid(), kCreateVirtualDevicePermission);
289 return ndk::ScopedAStatus::fromExceptionCode(EX_SECURITY);
290 }
291
Jan Sebechlebsky5cb39962023-11-22 17:33:07 +0100292 std::lock_guard lock(mLock);
293
294 auto it = mTokenToCameraName.find(token);
295 if (it == mTokenToCameraName.end()) {
296 ALOGE(
297 "Attempt to unregister camera corresponding to unknown binder token: "
298 "0x%" PRIxPTR,
299 reinterpret_cast<uintptr_t>(token.get()));
300 return ndk::ScopedAStatus::ok();
301 }
302
303 mVirtualCameraProvider->removeCamera(it->second);
304
Tony Guo6cbe11b2024-03-17 02:34:23 +0000305 mTokenToCameraName.erase(it);
Jan Sebechlebsky5cb39962023-11-22 17:33:07 +0100306 return ndk::ScopedAStatus::ok();
307}
308
Biswarup Pal68137fc2023-11-24 18:06:54 +0000309ndk::ScopedAStatus VirtualCameraService::getCameraId(
Marvin Ramina8196132024-03-15 15:55:22 +0000310 const ::ndk::SpAIBinder& token, int32_t* _aidl_return) {
Jan Sebechlebskyde6f16f2023-11-29 09:27:36 +0100311 if (!mPermissionProxy.checkCallingPermission(kCreateVirtualDevicePermission)) {
312 ALOGE("%s: caller (pid %d, uid %d) doesn't hold %s permission", __func__,
313 getpid(), getuid(), kCreateVirtualDevicePermission);
314 return ndk::ScopedAStatus::fromExceptionCode(EX_SECURITY);
315 }
316
Biswarup Pal68137fc2023-11-24 18:06:54 +0000317 if (_aidl_return == nullptr) {
318 return ndk::ScopedAStatus::fromServiceSpecificError(
Marvin Ramina8196132024-03-15 15:55:22 +0000319 Status::EX_ILLEGAL_ARGUMENT);
Biswarup Pal68137fc2023-11-24 18:06:54 +0000320 }
321
322 auto camera = getCamera(token);
323 if (camera == nullptr) {
324 ALOGE(
325 "Attempt to get camera id corresponding to unknown binder token: "
326 "0x%" PRIxPTR,
327 reinterpret_cast<uintptr_t>(token.get()));
328 return ndk::ScopedAStatus::ok();
329 }
330
331 *_aidl_return = camera->getCameraId();
332
333 return ndk::ScopedAStatus::ok();
334}
335
Jan Sebechlebsky5cb39962023-11-22 17:33:07 +0100336std::shared_ptr<VirtualCameraDevice> VirtualCameraService::getCamera(
337 const ::ndk::SpAIBinder& token) {
338 if (token == nullptr) {
339 return nullptr;
340 }
341
342 std::lock_guard lock(mLock);
343 auto it = mTokenToCameraName.find(token);
344 if (it == mTokenToCameraName.end()) {
345 return nullptr;
346 }
347
348 return mVirtualCameraProvider->getCamera(it->second);
349}
350
Jan Sebechlebsky773c0142024-03-25 12:17:05 +0100351binder_status_t VirtualCameraService::handleShellCommand(int, int out, int err,
Jan Sebechlebsky5cb39962023-11-22 17:33:07 +0100352 const char** args,
353 uint32_t numArgs) {
354 if (numArgs <= 0) {
355 dprintf(out, kShellCmdHelp);
Jan Sebechlebsky76d7e212023-11-28 14:10:25 +0100356 fsync(out);
357 return STATUS_OK;
Jan Sebechlebsky5cb39962023-11-22 17:33:07 +0100358 }
359
Jan Sebechlebsky773c0142024-03-25 12:17:05 +0100360 auto isNullptr = [](const char* ptr) { return ptr == nullptr; };
361 if (args == nullptr || std::any_of(args, args + numArgs, isNullptr)) {
Jan Sebechlebsky5cb39962023-11-22 17:33:07 +0100362 return STATUS_BAD_VALUE;
363 }
Marvin Ramina8196132024-03-15 15:55:22 +0000364
Jan Sebechlebsky773c0142024-03-25 12:17:05 +0100365 std::variant<CommandWithOptions, std::string> cmdOrErrorMessage =
366 parseCommand(args, numArgs);
367 if (std::holds_alternative<std::string>(cmdOrErrorMessage)) {
368 dprintf(err, "Error: %s\n",
369 std::get<std::string>(cmdOrErrorMessage).c_str());
370 return STATUS_BAD_VALUE;
Jan Sebechlebsky5cb39962023-11-22 17:33:07 +0100371 }
372
Jan Sebechlebsky773c0142024-03-25 12:17:05 +0100373 const CommandWithOptions& cmd =
374 std::get<CommandWithOptions>(cmdOrErrorMessage);
375 binder_status_t status = STATUS_OK;
376 switch (cmd.command) {
377 case Command::HELP:
378 dprintf(out, kShellCmdHelp);
379 break;
380 case Command::ENABLE_TEST_CAMERA:
381 status = enableTestCameraCmd(out, err, cmd.optionToValueMap);
382 break;
383 case Command::DISABLE_TEST_CAMERA:
384 disableTestCameraCmd(out);
385 break;
386 }
387
388 fsync(err);
Jan Sebechlebsky5cb39962023-11-22 17:33:07 +0100389 fsync(out);
Jan Sebechlebsky773c0142024-03-25 12:17:05 +0100390 return status;
Jan Sebechlebsky5cb39962023-11-22 17:33:07 +0100391}
392
Jan Sebechlebsky773c0142024-03-25 12:17:05 +0100393binder_status_t VirtualCameraService::enableTestCameraCmd(
394 const int out, const int err,
395 const std::map<std::string, std::string>& options) {
Jan Sebechlebsky5cb39962023-11-22 17:33:07 +0100396 if (mTestCameraToken != nullptr) {
Jan Sebechlebsky773c0142024-03-25 12:17:05 +0100397 dprintf(out, "Test camera is already enabled (%s).\n",
Jan Sebechlebsky5cb39962023-11-22 17:33:07 +0100398 getCamera(mTestCameraToken)->getCameraName().c_str());
Jan Sebechlebsky773c0142024-03-25 12:17:05 +0100399 return STATUS_OK;
400 }
401
402 std::optional<int> cameraId;
403 auto it = options.find("camera_id");
404 if (it != options.end()) {
405 cameraId = parseInt(it->second);
406 if (!cameraId.has_value()) {
407 dprintf(err, "Invalid camera_id: %s\n, must be number > 0",
408 it->second.c_str());
409 return STATUS_BAD_VALUE;
410 }
411 }
412
413 std::optional<LensFacing> lensFacing;
414 it = options.find("lens_facing");
415 if (it != options.end()) {
416 lensFacing = parseLensFacing(it->second);
417 if (!lensFacing.has_value()) {
418 dprintf(err, "Invalid lens_facing: %s\n, must be front|back|external",
419 it->second.c_str());
420 return STATUS_BAD_VALUE;
421 }
Jan Sebechlebsky5cb39962023-11-22 17:33:07 +0100422 }
423
424 sp<BBinder> token = sp<BBinder>::make();
425 mTestCameraToken.set(AIBinder_fromPlatformBinder(token));
426
427 bool ret;
Jan Sebechlebsky3b478c42023-11-23 13:15:56 +0100428 VirtualCameraConfiguration configuration;
Biswarup Pal6152a302023-12-19 12:44:09 +0000429 configuration.supportedStreamConfigs.push_back({.width = kVgaWidth,
430 .height = kVgaHeight,
Jan Sebechlebsky288900f2024-05-24 14:47:54 +0200431 Format::RGBA_8888,
Biswarup Pal6152a302023-12-19 12:44:09 +0000432 .maxFps = kMaxFps});
Jan Sebechlebsky773c0142024-03-25 12:17:05 +0100433 configuration.lensFacing = lensFacing.value_or(LensFacing::EXTERNAL);
Jan Sebechlebsky288900f2024-05-24 14:47:54 +0200434 configuration.virtualCameraCallback =
435 ndk::SharedRefBase::make<VirtualCameraTestInstance>(kTestCameraInputFps);
Jan Sebechlebsky773c0142024-03-25 12:17:05 +0100436 registerCamera(mTestCameraToken, configuration, cameraId.value_or(sNextId++),
Biswarup Pal37a75182024-01-16 15:53:35 +0000437 kDefaultDeviceId, &ret);
Jan Sebechlebsky5cb39962023-11-22 17:33:07 +0100438 if (ret) {
Jan Sebechlebsky773c0142024-03-25 12:17:05 +0100439 dprintf(out, "Successfully registered test camera %s\n",
Jan Sebechlebsky5cb39962023-11-22 17:33:07 +0100440 getCamera(mTestCameraToken)->getCameraName().c_str());
441 } else {
Jan Sebechlebsky773c0142024-03-25 12:17:05 +0100442 dprintf(err, "Failed to create test camera\n");
Jan Sebechlebsky5cb39962023-11-22 17:33:07 +0100443 }
Jan Sebechlebsky773c0142024-03-25 12:17:05 +0100444 return STATUS_OK;
Jan Sebechlebsky5cb39962023-11-22 17:33:07 +0100445}
446
447void VirtualCameraService::disableTestCameraCmd(const int out) {
448 if (mTestCameraToken == nullptr) {
449 dprintf(out, "Test camera is not registered.");
450 }
451 unregisterCamera(mTestCameraToken);
452 mTestCameraToken.set(nullptr);
453}
454
455} // namespace virtualcamera
456} // namespace companion
457} // namespace android