blob: 7466089e896a385b29fae86ddc8f7959b1de20f1 [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
60namespace {
61
Biswarup Pal15dcd132024-06-02 14:02:03 +000062constexpr char kCameraIdPrefix[] = "v";
Jan Sebechlebsky3b478c42023-11-23 13:15:56 +010063constexpr int kVgaWidth = 640;
64constexpr int kVgaHeight = 480;
Biswarup Pal6152a302023-12-19 12:44:09 +000065constexpr int kMaxFps = 60;
Jan Sebechlebsky34469e42024-05-29 10:56:12 +020066constexpr int kTestCameraDefaultInputFps = 30;
Jan Sebechlebsky5cb39962023-11-22 17:33:07 +010067constexpr char kEnableTestCameraCmd[] = "enable_test_camera";
68constexpr char kDisableTestCameraCmd[] = "disable_test_camera";
Jan Sebechlebsky773c0142024-03-25 12:17:05 +010069constexpr char kHelp[] = "help";
Jan Sebechlebsky5cb39962023-11-22 17:33:07 +010070constexpr char kShellCmdHelp[] = R"(
Jan Sebechlebsky773c0142024-03-25 12:17:05 +010071Usage:
72 cmd virtual_camera command [--option=value]
Jan Sebechlebsky5cb39962023-11-22 17:33:07 +010073Available commands:
74 * enable_test_camera
Jan Sebechlebsky773c0142024-03-25 12:17:05 +010075 Options:
76 --camera_id=(ID) - override numerical ID for test camera instance
77 --lens_facing=(front|back|external) - specifies lens facing for test camera instance
Jan Sebechlebsky34469e42024-05-29 10:56:12 +020078 --input_fps=(fps) - specify input fps for test camera, valid values are from 1 to 1000
Vadim Caen39100152024-06-11 15:24:15 +020079 --sensor_orientation=(0|90|180|270) - Clockwise angle through which the output image
80 needs to be rotated to be upright on the device screen in its native orientation
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
Biswarup Pal15dcd132024-06-02 14:02:03 +000092// Numerical portion for id to assign to next created camera.
93static std::atomic_int sNextIdNumericalPortion{1000};
94
Jan Sebechlebsky3b478c42023-11-23 13:15:56 +010095ndk::ScopedAStatus validateConfiguration(
96 const VirtualCameraConfiguration& configuration) {
97 if (configuration.supportedStreamConfigs.empty()) {
98 ALOGE("%s: No supported input configuration specified", __func__);
99 return ndk::ScopedAStatus::fromServiceSpecificError(
100 Status::EX_ILLEGAL_ARGUMENT);
101 }
102
Jan Sebechlebsky288900f2024-05-24 14:47:54 +0200103 if (configuration.virtualCameraCallback == nullptr) {
104 ALOGE("%s: Input configuration is missing virtual camera callback",
105 __func__);
106 return ndk::ScopedAStatus::fromServiceSpecificError(
107 Status::EX_ILLEGAL_ARGUMENT);
108 }
109
Jan Sebechlebsky3b478c42023-11-23 13:15:56 +0100110 for (const SupportedStreamConfiguration& config :
111 configuration.supportedStreamConfigs) {
112 if (!isFormatSupportedForInput(config.width, config.height,
Biswarup Pal6152a302023-12-19 12:44:09 +0000113 config.pixelFormat, config.maxFps)) {
Jan Sebechlebsky3b478c42023-11-23 13:15:56 +0100114 ALOGE("%s: Requested unsupported input format: %d x %d (%d)", __func__,
115 config.width, config.height, static_cast<int>(config.pixelFormat));
116 return ndk::ScopedAStatus::fromServiceSpecificError(
117 Status::EX_ILLEGAL_ARGUMENT);
118 }
119 }
Biswarup Pal6152a302023-12-19 12:44:09 +0000120
121 if (configuration.sensorOrientation != SensorOrientation::ORIENTATION_0 &&
122 configuration.sensorOrientation != SensorOrientation::ORIENTATION_90 &&
123 configuration.sensorOrientation != SensorOrientation::ORIENTATION_180 &&
124 configuration.sensorOrientation != SensorOrientation::ORIENTATION_270) {
125 return ndk::ScopedAStatus::fromServiceSpecificError(
126 Status::EX_ILLEGAL_ARGUMENT);
127 }
128
Biswarup Pal112458f2023-12-28 19:50:17 +0000129 if (configuration.lensFacing != LensFacing::FRONT &&
130 configuration.lensFacing != LensFacing::BACK &&
131 configuration.lensFacing != LensFacing::EXTERNAL) {
132 return ndk::ScopedAStatus::fromServiceSpecificError(
133 Status::EX_ILLEGAL_ARGUMENT);
134 }
135
Jan Sebechlebsky3b478c42023-11-23 13:15:56 +0100136 return ndk::ScopedAStatus::ok();
137}
138
Jan Sebechlebsky773c0142024-03-25 12:17:05 +0100139enum class Command {
140 ENABLE_TEST_CAMERA,
141 DISABLE_TEST_CAMERA,
142 HELP,
143};
144
145struct CommandWithOptions {
146 Command command;
147 std::map<std::string, std::string> optionToValueMap;
148};
149
150std::optional<int> parseInt(const std::string& s) {
151 if (!std::all_of(s.begin(), s.end(), [](char c) { return std::isdigit(c); })) {
152 return std::nullopt;
153 }
154 int ret = atoi(s.c_str());
155 return ret > 0 ? std::optional(ret) : std::nullopt;
156}
157
158std::optional<LensFacing> parseLensFacing(const std::string& s) {
159 static const std::map<std::string, LensFacing> strToLensFacing{
160 {"front", LensFacing::FRONT},
161 {"back", LensFacing::BACK},
162 {"external", LensFacing::EXTERNAL}};
163 auto it = strToLensFacing.find(s);
164 return it == strToLensFacing.end() ? std::nullopt : std::optional(it->second);
165}
166
167std::variant<CommandWithOptions, std::string> parseCommand(
168 const char** args, const uint32_t numArgs) {
169 static const std::regex optionRegex("^--(\\w+)(?:=(.+))?$");
170 static const std::map<std::string, Command> strToCommand{
171 {kHelp, Command::HELP},
172 {kEnableTestCameraCmd, Command::ENABLE_TEST_CAMERA},
173 {kDisableTestCameraCmd, Command::DISABLE_TEST_CAMERA}};
174
175 if (numArgs < 1) {
176 return CommandWithOptions{.command = Command::HELP};
177 }
178
179 // We interpret the first argument as command;
180 auto it = strToCommand.find(args[0]);
181 if (it == strToCommand.end()) {
182 return "Unknown command: " + std::string(args[0]);
183 }
184
185 CommandWithOptions cmd{.command = it->second};
186
187 for (int i = 1; i < numArgs; i++) {
188 std::cmatch cm;
189 if (!std::regex_match(args[i], cm, optionRegex)) {
190 return "Not an option: " + std::string(args[i]);
191 }
192
193 cmd.optionToValueMap[cm[1]] = cm[2];
194 }
195
196 return cmd;
Biswarup Pal15dcd132024-06-02 14:02:03 +0000197}
Jan Sebechlebsky773c0142024-03-25 12:17:05 +0100198
Jan Sebechlebskyb36090e2024-04-11 09:19:33 +0200199ndk::ScopedAStatus verifyRequiredEglExtensions() {
200 EglDisplayContext context;
201 for (const char* eglExtension : kRequiredEglExtensions) {
202 if (!isGlExtensionSupported(eglExtension)) {
203 ALOGE("%s not supported", eglExtension);
204 return ndk::ScopedAStatus::fromExceptionCodeWithMessage(
205 EX_UNSUPPORTED_OPERATION,
206 fmt::format(
207 "Cannot create virtual camera, because required EGL extension {} "
208 "is not supported on this system",
209 eglExtension)
210 .c_str());
211 }
212 }
213 return ndk::ScopedAStatus::ok();
214}
215
Biswarup Pal15dcd132024-06-02 14:02:03 +0000216std::string createCameraId(const int32_t deviceId) {
217 return kCameraIdPrefix + std::to_string(deviceId) + "_" +
218 std::to_string(sNextIdNumericalPortion++);
219}
220
Jan Sebechlebsky5cb39962023-11-22 17:33:07 +0100221} // namespace
222
223VirtualCameraService::VirtualCameraService(
Jan Sebechlebskyde6f16f2023-11-29 09:27:36 +0100224 std::shared_ptr<VirtualCameraProvider> virtualCameraProvider,
225 const PermissionsProxy& permissionProxy)
226 : mVirtualCameraProvider(virtualCameraProvider),
227 mPermissionProxy(permissionProxy) {
Jan Sebechlebsky5cb39962023-11-22 17:33:07 +0100228}
229
230ndk::ScopedAStatus VirtualCameraService::registerCamera(
231 const ::ndk::SpAIBinder& token,
Biswarup Pal37a75182024-01-16 15:53:35 +0000232 const VirtualCameraConfiguration& configuration, const int32_t deviceId,
233 bool* _aidl_return) {
Biswarup Pal15dcd132024-06-02 14:02:03 +0000234 return registerCamera(token, configuration, createCameraId(deviceId),
235 deviceId, _aidl_return);
Marvin Ramina8196132024-03-15 15:55:22 +0000236}
237
238ndk::ScopedAStatus VirtualCameraService::registerCamera(
239 const ::ndk::SpAIBinder& token,
Biswarup Pal15dcd132024-06-02 14:02:03 +0000240 const VirtualCameraConfiguration& configuration,
241 const std::string& cameraId, const int32_t deviceId, bool* _aidl_return) {
Jan Sebechlebskyde6f16f2023-11-29 09:27:36 +0100242 if (!mPermissionProxy.checkCallingPermission(kCreateVirtualDevicePermission)) {
243 ALOGE("%s: caller (pid %d, uid %d) doesn't hold %s permission", __func__,
244 getpid(), getuid(), kCreateVirtualDevicePermission);
245 return ndk::ScopedAStatus::fromExceptionCode(EX_SECURITY);
246 }
247
Jan Sebechlebsky5cb39962023-11-22 17:33:07 +0100248 if (_aidl_return == nullptr) {
249 return ndk::ScopedAStatus::fromServiceSpecificError(
250 Status::EX_ILLEGAL_ARGUMENT);
251 }
Jan Sebechlebskyde6f16f2023-11-29 09:27:36 +0100252
Jan Sebechlebskyb36090e2024-04-11 09:19:33 +0200253 if (mVerifyEglExtensions) {
254 auto status = verifyRequiredEglExtensions();
255 if (!status.isOk()) {
256 *_aidl_return = false;
257 return status;
258 }
259 }
Jan Sebechlebsky5cb39962023-11-22 17:33:07 +0100260
Jan Sebechlebsky3b478c42023-11-23 13:15:56 +0100261 auto status = validateConfiguration(configuration);
262 if (!status.isOk()) {
263 *_aidl_return = false;
264 return status;
265 }
266
Jan Sebechlebsky5cb39962023-11-22 17:33:07 +0100267 std::lock_guard lock(mLock);
268 if (mTokenToCameraName.find(token) != mTokenToCameraName.end()) {
269 ALOGE(
270 "Attempt to register camera corresponding to already registered binder "
271 "token: "
272 "0x%" PRIxPTR,
273 reinterpret_cast<uintptr_t>(token.get()));
274 *_aidl_return = false;
Jan Sebechlebsky3b478c42023-11-23 13:15:56 +0100275 return ndk::ScopedAStatus::ok();
Jan Sebechlebsky5cb39962023-11-22 17:33:07 +0100276 }
277
Jan Sebechlebsky5cb39962023-11-22 17:33:07 +0100278 std::shared_ptr<VirtualCameraDevice> camera =
Biswarup Pal37a75182024-01-16 15:53:35 +0000279 mVirtualCameraProvider->createCamera(configuration, cameraId, deviceId);
Jan Sebechlebsky5cb39962023-11-22 17:33:07 +0100280 if (camera == nullptr) {
281 ALOGE("Failed to create camera for binder token 0x%" PRIxPTR,
282 reinterpret_cast<uintptr_t>(token.get()));
283 *_aidl_return = false;
284 return ndk::ScopedAStatus::fromServiceSpecificError(
285 Status::EX_SERVICE_SPECIFIC);
286 }
287
288 mTokenToCameraName[token] = camera->getCameraName();
Jan Sebechlebskyb36090e2024-04-11 09:19:33 +0200289 *_aidl_return = true;
Jan Sebechlebsky5cb39962023-11-22 17:33:07 +0100290 return ndk::ScopedAStatus::ok();
291}
292
293ndk::ScopedAStatus VirtualCameraService::unregisterCamera(
294 const ::ndk::SpAIBinder& token) {
Jan Sebechlebskyde6f16f2023-11-29 09:27:36 +0100295 if (!mPermissionProxy.checkCallingPermission(kCreateVirtualDevicePermission)) {
296 ALOGE("%s: caller (pid %d, uid %d) doesn't hold %s permission", __func__,
297 getpid(), getuid(), kCreateVirtualDevicePermission);
298 return ndk::ScopedAStatus::fromExceptionCode(EX_SECURITY);
299 }
300
Jan Sebechlebsky5cb39962023-11-22 17:33:07 +0100301 std::lock_guard lock(mLock);
302
303 auto it = mTokenToCameraName.find(token);
304 if (it == mTokenToCameraName.end()) {
305 ALOGE(
306 "Attempt to unregister camera corresponding to unknown binder token: "
307 "0x%" PRIxPTR,
308 reinterpret_cast<uintptr_t>(token.get()));
309 return ndk::ScopedAStatus::ok();
310 }
311
312 mVirtualCameraProvider->removeCamera(it->second);
313
Tony Guo6cbe11b2024-03-17 02:34:23 +0000314 mTokenToCameraName.erase(it);
Jan Sebechlebsky5cb39962023-11-22 17:33:07 +0100315 return ndk::ScopedAStatus::ok();
316}
317
Biswarup Pal68137fc2023-11-24 18:06:54 +0000318ndk::ScopedAStatus VirtualCameraService::getCameraId(
Biswarup Pal15dcd132024-06-02 14:02:03 +0000319 const ::ndk::SpAIBinder& token, std::string* _aidl_return) {
Jan Sebechlebskyde6f16f2023-11-29 09:27:36 +0100320 if (!mPermissionProxy.checkCallingPermission(kCreateVirtualDevicePermission)) {
321 ALOGE("%s: caller (pid %d, uid %d) doesn't hold %s permission", __func__,
322 getpid(), getuid(), kCreateVirtualDevicePermission);
323 return ndk::ScopedAStatus::fromExceptionCode(EX_SECURITY);
324 }
325
Biswarup Pal68137fc2023-11-24 18:06:54 +0000326 if (_aidl_return == nullptr) {
327 return ndk::ScopedAStatus::fromServiceSpecificError(
Marvin Ramina8196132024-03-15 15:55:22 +0000328 Status::EX_ILLEGAL_ARGUMENT);
Biswarup Pal68137fc2023-11-24 18:06:54 +0000329 }
330
331 auto camera = getCamera(token);
332 if (camera == nullptr) {
333 ALOGE(
334 "Attempt to get camera id corresponding to unknown binder token: "
335 "0x%" PRIxPTR,
336 reinterpret_cast<uintptr_t>(token.get()));
337 return ndk::ScopedAStatus::ok();
338 }
339
340 *_aidl_return = camera->getCameraId();
341
342 return ndk::ScopedAStatus::ok();
343}
344
Jan Sebechlebsky5cb39962023-11-22 17:33:07 +0100345std::shared_ptr<VirtualCameraDevice> VirtualCameraService::getCamera(
346 const ::ndk::SpAIBinder& token) {
347 if (token == nullptr) {
348 return nullptr;
349 }
350
351 std::lock_guard lock(mLock);
352 auto it = mTokenToCameraName.find(token);
353 if (it == mTokenToCameraName.end()) {
354 return nullptr;
355 }
356
357 return mVirtualCameraProvider->getCamera(it->second);
358}
359
Jan Sebechlebsky773c0142024-03-25 12:17:05 +0100360binder_status_t VirtualCameraService::handleShellCommand(int, int out, int err,
Jan Sebechlebsky5cb39962023-11-22 17:33:07 +0100361 const char** args,
362 uint32_t numArgs) {
363 if (numArgs <= 0) {
364 dprintf(out, kShellCmdHelp);
Jan Sebechlebsky76d7e212023-11-28 14:10:25 +0100365 fsync(out);
366 return STATUS_OK;
Jan Sebechlebsky5cb39962023-11-22 17:33:07 +0100367 }
368
Jan Sebechlebsky773c0142024-03-25 12:17:05 +0100369 auto isNullptr = [](const char* ptr) { return ptr == nullptr; };
370 if (args == nullptr || std::any_of(args, args + numArgs, isNullptr)) {
Jan Sebechlebsky5cb39962023-11-22 17:33:07 +0100371 return STATUS_BAD_VALUE;
372 }
Marvin Ramina8196132024-03-15 15:55:22 +0000373
Jan Sebechlebsky773c0142024-03-25 12:17:05 +0100374 std::variant<CommandWithOptions, std::string> cmdOrErrorMessage =
375 parseCommand(args, numArgs);
376 if (std::holds_alternative<std::string>(cmdOrErrorMessage)) {
377 dprintf(err, "Error: %s\n",
378 std::get<std::string>(cmdOrErrorMessage).c_str());
379 return STATUS_BAD_VALUE;
Jan Sebechlebsky5cb39962023-11-22 17:33:07 +0100380 }
381
Jan Sebechlebsky773c0142024-03-25 12:17:05 +0100382 const CommandWithOptions& cmd =
383 std::get<CommandWithOptions>(cmdOrErrorMessage);
384 binder_status_t status = STATUS_OK;
385 switch (cmd.command) {
386 case Command::HELP:
387 dprintf(out, kShellCmdHelp);
388 break;
389 case Command::ENABLE_TEST_CAMERA:
390 status = enableTestCameraCmd(out, err, cmd.optionToValueMap);
391 break;
392 case Command::DISABLE_TEST_CAMERA:
393 disableTestCameraCmd(out);
394 break;
395 }
396
397 fsync(err);
Jan Sebechlebsky5cb39962023-11-22 17:33:07 +0100398 fsync(out);
Jan Sebechlebsky773c0142024-03-25 12:17:05 +0100399 return status;
Jan Sebechlebsky5cb39962023-11-22 17:33:07 +0100400}
401
Jan Sebechlebsky773c0142024-03-25 12:17:05 +0100402binder_status_t VirtualCameraService::enableTestCameraCmd(
403 const int out, const int err,
404 const std::map<std::string, std::string>& options) {
Jan Sebechlebsky5cb39962023-11-22 17:33:07 +0100405 if (mTestCameraToken != nullptr) {
Jan Sebechlebsky773c0142024-03-25 12:17:05 +0100406 dprintf(out, "Test camera is already enabled (%s).\n",
Jan Sebechlebsky5cb39962023-11-22 17:33:07 +0100407 getCamera(mTestCameraToken)->getCameraName().c_str());
Jan Sebechlebsky773c0142024-03-25 12:17:05 +0100408 return STATUS_OK;
409 }
410
Biswarup Pal15dcd132024-06-02 14:02:03 +0000411 std::optional<std::string> cameraId;
Jan Sebechlebsky773c0142024-03-25 12:17:05 +0100412 auto it = options.find("camera_id");
413 if (it != options.end()) {
Biswarup Pal15dcd132024-06-02 14:02:03 +0000414 cameraId = it->second;
Jan Sebechlebsky773c0142024-03-25 12:17:05 +0100415 if (!cameraId.has_value()) {
Biswarup Pal15dcd132024-06-02 14:02:03 +0000416 dprintf(err, "Invalid camera_id: %s", it->second.c_str());
Jan Sebechlebsky773c0142024-03-25 12:17:05 +0100417 return STATUS_BAD_VALUE;
418 }
419 }
420
421 std::optional<LensFacing> lensFacing;
422 it = options.find("lens_facing");
423 if (it != options.end()) {
424 lensFacing = parseLensFacing(it->second);
425 if (!lensFacing.has_value()) {
426 dprintf(err, "Invalid lens_facing: %s\n, must be front|back|external",
427 it->second.c_str());
428 return STATUS_BAD_VALUE;
429 }
Jan Sebechlebsky5cb39962023-11-22 17:33:07 +0100430 }
431
Jan Sebechlebsky34469e42024-05-29 10:56:12 +0200432 std::optional<int> inputFps;
433 it = options.find("input_fps");
434 if (it != options.end()) {
435 inputFps = parseInt(it->second);
436 if (!inputFps.has_value() || inputFps.value() < 1 ||
437 inputFps.value() > 1000) {
438 dprintf(err, "Invalid input fps: %s\n, must be integer in <1,1000> range.",
439 it->second.c_str());
440 return STATUS_BAD_VALUE;
441 }
442 }
443
Vadim Caen39100152024-06-11 15:24:15 +0200444 std::optional<SensorOrientation> sensorOrientation;
445 std::optional<int> sensorOrientationInt;
446 it = options.find("sensor_orientation");
447 if (it != options.end()) {
448 sensorOrientationInt = parseInt(it->second);
449 switch (sensorOrientationInt.value_or(0)) {
450 case 0:
451 sensorOrientation = SensorOrientation::ORIENTATION_0;
452 break;
453 case 90:
454 sensorOrientation = SensorOrientation::ORIENTATION_90;
455 break;
456 case 180:
457 sensorOrientation = SensorOrientation::ORIENTATION_180;
458 break;
459 case 270:
460 sensorOrientation = SensorOrientation::ORIENTATION_270;
461 break;
462 default:
463 dprintf(err, "Invalid sensor rotation: %s\n, must be 0, 90, 180 or 270.",
464 it->second.c_str());
465 return STATUS_BAD_VALUE;
466 }
467 }
468
Jan Sebechlebsky5cb39962023-11-22 17:33:07 +0100469 sp<BBinder> token = sp<BBinder>::make();
470 mTestCameraToken.set(AIBinder_fromPlatformBinder(token));
471
472 bool ret;
Jan Sebechlebsky3b478c42023-11-23 13:15:56 +0100473 VirtualCameraConfiguration configuration;
Biswarup Pal6152a302023-12-19 12:44:09 +0000474 configuration.supportedStreamConfigs.push_back({.width = kVgaWidth,
475 .height = kVgaHeight,
Jan Sebechlebsky288900f2024-05-24 14:47:54 +0200476 Format::RGBA_8888,
Biswarup Pal6152a302023-12-19 12:44:09 +0000477 .maxFps = kMaxFps});
Jan Sebechlebsky773c0142024-03-25 12:17:05 +0100478 configuration.lensFacing = lensFacing.value_or(LensFacing::EXTERNAL);
Vadim Caen39100152024-06-11 15:24:15 +0200479 configuration.sensorOrientation =
480 sensorOrientation.value_or(SensorOrientation::ORIENTATION_0);
Jan Sebechlebsky288900f2024-05-24 14:47:54 +0200481 configuration.virtualCameraCallback =
Jan Sebechlebsky34469e42024-05-29 10:56:12 +0200482 ndk::SharedRefBase::make<VirtualCameraTestInstance>(
483 inputFps.value_or(kTestCameraDefaultInputFps));
Biswarup Pal15dcd132024-06-02 14:02:03 +0000484 registerCamera(mTestCameraToken, configuration,
485 cameraId.value_or(std::to_string(sNextIdNumericalPortion++)),
Biswarup Pal37a75182024-01-16 15:53:35 +0000486 kDefaultDeviceId, &ret);
Jan Sebechlebsky5cb39962023-11-22 17:33:07 +0100487 if (ret) {
Jan Sebechlebsky773c0142024-03-25 12:17:05 +0100488 dprintf(out, "Successfully registered test camera %s\n",
Jan Sebechlebsky5cb39962023-11-22 17:33:07 +0100489 getCamera(mTestCameraToken)->getCameraName().c_str());
490 } else {
Jan Sebechlebsky773c0142024-03-25 12:17:05 +0100491 dprintf(err, "Failed to create test camera\n");
Jan Sebechlebsky5cb39962023-11-22 17:33:07 +0100492 }
Jan Sebechlebsky773c0142024-03-25 12:17:05 +0100493 return STATUS_OK;
Jan Sebechlebsky5cb39962023-11-22 17:33:07 +0100494}
495
496void VirtualCameraService::disableTestCameraCmd(const int out) {
497 if (mTestCameraToken == nullptr) {
498 dprintf(out, "Test camera is not registered.");
499 }
500 unregisterCamera(mTestCameraToken);
501 mTestCameraToken.set(nullptr);
502}
503
504} // namespace virtualcamera
505} // namespace companion
506} // namespace android