blob: 705af86ec01385cfa45f4b0583fe95ce34e8e6d5 [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
Jan Sebechlebsky5cb39962023-11-22 17:33:07 +010079 * disable_test_camera
80)";
Jan Sebechlebskyde6f16f2023-11-29 09:27:36 +010081constexpr char kCreateVirtualDevicePermission[] =
82 "android.permission.CREATE_VIRTUAL_DEVICE";
Jan Sebechlebsky5cb39962023-11-22 17:33:07 +010083
Jan Sebechlebskyb36090e2024-04-11 09:19:33 +020084constexpr std::array<const char*, 3> kRequiredEglExtensions = {
85 "GL_OES_EGL_image_external",
86 "GL_OES_EGL_image_external_essl3",
87 "GL_EXT_YUV_target",
88};
89
Biswarup Pal15dcd132024-06-02 14:02:03 +000090// Numerical portion for id to assign to next created camera.
91static std::atomic_int sNextIdNumericalPortion{1000};
92
Jan Sebechlebsky3b478c42023-11-23 13:15:56 +010093ndk::ScopedAStatus validateConfiguration(
94 const VirtualCameraConfiguration& configuration) {
95 if (configuration.supportedStreamConfigs.empty()) {
96 ALOGE("%s: No supported input configuration specified", __func__);
97 return ndk::ScopedAStatus::fromServiceSpecificError(
98 Status::EX_ILLEGAL_ARGUMENT);
99 }
100
Jan Sebechlebsky288900f2024-05-24 14:47:54 +0200101 if (configuration.virtualCameraCallback == nullptr) {
102 ALOGE("%s: Input configuration is missing virtual camera callback",
103 __func__);
104 return ndk::ScopedAStatus::fromServiceSpecificError(
105 Status::EX_ILLEGAL_ARGUMENT);
106 }
107
Jan Sebechlebsky3b478c42023-11-23 13:15:56 +0100108 for (const SupportedStreamConfiguration& config :
109 configuration.supportedStreamConfigs) {
110 if (!isFormatSupportedForInput(config.width, config.height,
Biswarup Pal6152a302023-12-19 12:44:09 +0000111 config.pixelFormat, config.maxFps)) {
Jan Sebechlebsky3b478c42023-11-23 13:15:56 +0100112 ALOGE("%s: Requested unsupported input format: %d x %d (%d)", __func__,
113 config.width, config.height, static_cast<int>(config.pixelFormat));
114 return ndk::ScopedAStatus::fromServiceSpecificError(
115 Status::EX_ILLEGAL_ARGUMENT);
116 }
117 }
Biswarup Pal6152a302023-12-19 12:44:09 +0000118
119 if (configuration.sensorOrientation != SensorOrientation::ORIENTATION_0 &&
120 configuration.sensorOrientation != SensorOrientation::ORIENTATION_90 &&
121 configuration.sensorOrientation != SensorOrientation::ORIENTATION_180 &&
122 configuration.sensorOrientation != SensorOrientation::ORIENTATION_270) {
123 return ndk::ScopedAStatus::fromServiceSpecificError(
124 Status::EX_ILLEGAL_ARGUMENT);
125 }
126
Biswarup Pal112458f2023-12-28 19:50:17 +0000127 if (configuration.lensFacing != LensFacing::FRONT &&
128 configuration.lensFacing != LensFacing::BACK &&
129 configuration.lensFacing != LensFacing::EXTERNAL) {
130 return ndk::ScopedAStatus::fromServiceSpecificError(
131 Status::EX_ILLEGAL_ARGUMENT);
132 }
133
Jan Sebechlebsky3b478c42023-11-23 13:15:56 +0100134 return ndk::ScopedAStatus::ok();
135}
136
Jan Sebechlebsky773c0142024-03-25 12:17:05 +0100137enum class Command {
138 ENABLE_TEST_CAMERA,
139 DISABLE_TEST_CAMERA,
140 HELP,
141};
142
143struct CommandWithOptions {
144 Command command;
145 std::map<std::string, std::string> optionToValueMap;
146};
147
148std::optional<int> parseInt(const std::string& s) {
149 if (!std::all_of(s.begin(), s.end(), [](char c) { return std::isdigit(c); })) {
150 return std::nullopt;
151 }
152 int ret = atoi(s.c_str());
153 return ret > 0 ? std::optional(ret) : std::nullopt;
154}
155
156std::optional<LensFacing> parseLensFacing(const std::string& s) {
157 static const std::map<std::string, LensFacing> strToLensFacing{
158 {"front", LensFacing::FRONT},
159 {"back", LensFacing::BACK},
160 {"external", LensFacing::EXTERNAL}};
161 auto it = strToLensFacing.find(s);
162 return it == strToLensFacing.end() ? std::nullopt : std::optional(it->second);
163}
164
165std::variant<CommandWithOptions, std::string> parseCommand(
166 const char** args, const uint32_t numArgs) {
167 static const std::regex optionRegex("^--(\\w+)(?:=(.+))?$");
168 static const std::map<std::string, Command> strToCommand{
169 {kHelp, Command::HELP},
170 {kEnableTestCameraCmd, Command::ENABLE_TEST_CAMERA},
171 {kDisableTestCameraCmd, Command::DISABLE_TEST_CAMERA}};
172
173 if (numArgs < 1) {
174 return CommandWithOptions{.command = Command::HELP};
175 }
176
177 // We interpret the first argument as command;
178 auto it = strToCommand.find(args[0]);
179 if (it == strToCommand.end()) {
180 return "Unknown command: " + std::string(args[0]);
181 }
182
183 CommandWithOptions cmd{.command = it->second};
184
185 for (int i = 1; i < numArgs; i++) {
186 std::cmatch cm;
187 if (!std::regex_match(args[i], cm, optionRegex)) {
188 return "Not an option: " + std::string(args[i]);
189 }
190
191 cmd.optionToValueMap[cm[1]] = cm[2];
192 }
193
194 return cmd;
Biswarup Pal15dcd132024-06-02 14:02:03 +0000195}
Jan Sebechlebsky773c0142024-03-25 12:17:05 +0100196
Jan Sebechlebskyb36090e2024-04-11 09:19:33 +0200197ndk::ScopedAStatus verifyRequiredEglExtensions() {
198 EglDisplayContext context;
199 for (const char* eglExtension : kRequiredEglExtensions) {
200 if (!isGlExtensionSupported(eglExtension)) {
201 ALOGE("%s not supported", eglExtension);
202 return ndk::ScopedAStatus::fromExceptionCodeWithMessage(
203 EX_UNSUPPORTED_OPERATION,
204 fmt::format(
205 "Cannot create virtual camera, because required EGL extension {} "
206 "is not supported on this system",
207 eglExtension)
208 .c_str());
209 }
210 }
211 return ndk::ScopedAStatus::ok();
212}
213
Biswarup Pal15dcd132024-06-02 14:02:03 +0000214std::string createCameraId(const int32_t deviceId) {
215 return kCameraIdPrefix + std::to_string(deviceId) + "_" +
216 std::to_string(sNextIdNumericalPortion++);
217}
218
Jan Sebechlebsky5cb39962023-11-22 17:33:07 +0100219} // namespace
220
221VirtualCameraService::VirtualCameraService(
Jan Sebechlebskyde6f16f2023-11-29 09:27:36 +0100222 std::shared_ptr<VirtualCameraProvider> virtualCameraProvider,
223 const PermissionsProxy& permissionProxy)
224 : mVirtualCameraProvider(virtualCameraProvider),
225 mPermissionProxy(permissionProxy) {
Jan Sebechlebsky5cb39962023-11-22 17:33:07 +0100226}
227
228ndk::ScopedAStatus VirtualCameraService::registerCamera(
229 const ::ndk::SpAIBinder& token,
Biswarup Pal37a75182024-01-16 15:53:35 +0000230 const VirtualCameraConfiguration& configuration, const int32_t deviceId,
231 bool* _aidl_return) {
Biswarup Pal15dcd132024-06-02 14:02:03 +0000232 return registerCamera(token, configuration, createCameraId(deviceId),
233 deviceId, _aidl_return);
Marvin Ramina8196132024-03-15 15:55:22 +0000234}
235
236ndk::ScopedAStatus VirtualCameraService::registerCamera(
237 const ::ndk::SpAIBinder& token,
Biswarup Pal15dcd132024-06-02 14:02:03 +0000238 const VirtualCameraConfiguration& configuration,
239 const std::string& cameraId, const int32_t deviceId, bool* _aidl_return) {
Jan Sebechlebskyde6f16f2023-11-29 09:27:36 +0100240 if (!mPermissionProxy.checkCallingPermission(kCreateVirtualDevicePermission)) {
241 ALOGE("%s: caller (pid %d, uid %d) doesn't hold %s permission", __func__,
242 getpid(), getuid(), kCreateVirtualDevicePermission);
243 return ndk::ScopedAStatus::fromExceptionCode(EX_SECURITY);
244 }
245
Jan Sebechlebsky5cb39962023-11-22 17:33:07 +0100246 if (_aidl_return == nullptr) {
247 return ndk::ScopedAStatus::fromServiceSpecificError(
248 Status::EX_ILLEGAL_ARGUMENT);
249 }
Jan Sebechlebskyde6f16f2023-11-29 09:27:36 +0100250
Jan Sebechlebskyb36090e2024-04-11 09:19:33 +0200251 if (mVerifyEglExtensions) {
252 auto status = verifyRequiredEglExtensions();
253 if (!status.isOk()) {
254 *_aidl_return = false;
255 return status;
256 }
257 }
Jan Sebechlebsky5cb39962023-11-22 17:33:07 +0100258
Jan Sebechlebsky3b478c42023-11-23 13:15:56 +0100259 auto status = validateConfiguration(configuration);
260 if (!status.isOk()) {
261 *_aidl_return = false;
262 return status;
263 }
264
Jan Sebechlebsky5cb39962023-11-22 17:33:07 +0100265 std::lock_guard lock(mLock);
266 if (mTokenToCameraName.find(token) != mTokenToCameraName.end()) {
267 ALOGE(
268 "Attempt to register camera corresponding to already registered binder "
269 "token: "
270 "0x%" PRIxPTR,
271 reinterpret_cast<uintptr_t>(token.get()));
272 *_aidl_return = false;
Jan Sebechlebsky3b478c42023-11-23 13:15:56 +0100273 return ndk::ScopedAStatus::ok();
Jan Sebechlebsky5cb39962023-11-22 17:33:07 +0100274 }
275
Jan Sebechlebsky5cb39962023-11-22 17:33:07 +0100276 std::shared_ptr<VirtualCameraDevice> camera =
Biswarup Pal37a75182024-01-16 15:53:35 +0000277 mVirtualCameraProvider->createCamera(configuration, cameraId, deviceId);
Jan Sebechlebsky5cb39962023-11-22 17:33:07 +0100278 if (camera == nullptr) {
279 ALOGE("Failed to create camera for binder token 0x%" PRIxPTR,
280 reinterpret_cast<uintptr_t>(token.get()));
281 *_aidl_return = false;
282 return ndk::ScopedAStatus::fromServiceSpecificError(
283 Status::EX_SERVICE_SPECIFIC);
284 }
285
286 mTokenToCameraName[token] = camera->getCameraName();
Jan Sebechlebskyb36090e2024-04-11 09:19:33 +0200287 *_aidl_return = true;
Jan Sebechlebsky5cb39962023-11-22 17:33:07 +0100288 return ndk::ScopedAStatus::ok();
289}
290
291ndk::ScopedAStatus VirtualCameraService::unregisterCamera(
292 const ::ndk::SpAIBinder& token) {
Jan Sebechlebskyde6f16f2023-11-29 09:27:36 +0100293 if (!mPermissionProxy.checkCallingPermission(kCreateVirtualDevicePermission)) {
294 ALOGE("%s: caller (pid %d, uid %d) doesn't hold %s permission", __func__,
295 getpid(), getuid(), kCreateVirtualDevicePermission);
296 return ndk::ScopedAStatus::fromExceptionCode(EX_SECURITY);
297 }
298
Jan Sebechlebsky5cb39962023-11-22 17:33:07 +0100299 std::lock_guard lock(mLock);
300
301 auto it = mTokenToCameraName.find(token);
302 if (it == mTokenToCameraName.end()) {
303 ALOGE(
304 "Attempt to unregister camera corresponding to unknown binder token: "
305 "0x%" PRIxPTR,
306 reinterpret_cast<uintptr_t>(token.get()));
307 return ndk::ScopedAStatus::ok();
308 }
309
310 mVirtualCameraProvider->removeCamera(it->second);
311
Tony Guo6cbe11b2024-03-17 02:34:23 +0000312 mTokenToCameraName.erase(it);
Jan Sebechlebsky5cb39962023-11-22 17:33:07 +0100313 return ndk::ScopedAStatus::ok();
314}
315
Biswarup Pal68137fc2023-11-24 18:06:54 +0000316ndk::ScopedAStatus VirtualCameraService::getCameraId(
Biswarup Pal15dcd132024-06-02 14:02:03 +0000317 const ::ndk::SpAIBinder& token, std::string* _aidl_return) {
Jan Sebechlebskyde6f16f2023-11-29 09:27:36 +0100318 if (!mPermissionProxy.checkCallingPermission(kCreateVirtualDevicePermission)) {
319 ALOGE("%s: caller (pid %d, uid %d) doesn't hold %s permission", __func__,
320 getpid(), getuid(), kCreateVirtualDevicePermission);
321 return ndk::ScopedAStatus::fromExceptionCode(EX_SECURITY);
322 }
323
Biswarup Pal68137fc2023-11-24 18:06:54 +0000324 if (_aidl_return == nullptr) {
325 return ndk::ScopedAStatus::fromServiceSpecificError(
Marvin Ramina8196132024-03-15 15:55:22 +0000326 Status::EX_ILLEGAL_ARGUMENT);
Biswarup Pal68137fc2023-11-24 18:06:54 +0000327 }
328
329 auto camera = getCamera(token);
330 if (camera == nullptr) {
331 ALOGE(
332 "Attempt to get camera id corresponding to unknown binder token: "
333 "0x%" PRIxPTR,
334 reinterpret_cast<uintptr_t>(token.get()));
335 return ndk::ScopedAStatus::ok();
336 }
337
338 *_aidl_return = camera->getCameraId();
339
340 return ndk::ScopedAStatus::ok();
341}
342
Jan Sebechlebsky5cb39962023-11-22 17:33:07 +0100343std::shared_ptr<VirtualCameraDevice> VirtualCameraService::getCamera(
344 const ::ndk::SpAIBinder& token) {
345 if (token == nullptr) {
346 return nullptr;
347 }
348
349 std::lock_guard lock(mLock);
350 auto it = mTokenToCameraName.find(token);
351 if (it == mTokenToCameraName.end()) {
352 return nullptr;
353 }
354
355 return mVirtualCameraProvider->getCamera(it->second);
356}
357
Jan Sebechlebsky773c0142024-03-25 12:17:05 +0100358binder_status_t VirtualCameraService::handleShellCommand(int, int out, int err,
Jan Sebechlebsky5cb39962023-11-22 17:33:07 +0100359 const char** args,
360 uint32_t numArgs) {
361 if (numArgs <= 0) {
362 dprintf(out, kShellCmdHelp);
Jan Sebechlebsky76d7e212023-11-28 14:10:25 +0100363 fsync(out);
364 return STATUS_OK;
Jan Sebechlebsky5cb39962023-11-22 17:33:07 +0100365 }
366
Jan Sebechlebsky773c0142024-03-25 12:17:05 +0100367 auto isNullptr = [](const char* ptr) { return ptr == nullptr; };
368 if (args == nullptr || std::any_of(args, args + numArgs, isNullptr)) {
Jan Sebechlebsky5cb39962023-11-22 17:33:07 +0100369 return STATUS_BAD_VALUE;
370 }
Marvin Ramina8196132024-03-15 15:55:22 +0000371
Jan Sebechlebsky773c0142024-03-25 12:17:05 +0100372 std::variant<CommandWithOptions, std::string> cmdOrErrorMessage =
373 parseCommand(args, numArgs);
374 if (std::holds_alternative<std::string>(cmdOrErrorMessage)) {
375 dprintf(err, "Error: %s\n",
376 std::get<std::string>(cmdOrErrorMessage).c_str());
377 return STATUS_BAD_VALUE;
Jan Sebechlebsky5cb39962023-11-22 17:33:07 +0100378 }
379
Jan Sebechlebsky773c0142024-03-25 12:17:05 +0100380 const CommandWithOptions& cmd =
381 std::get<CommandWithOptions>(cmdOrErrorMessage);
382 binder_status_t status = STATUS_OK;
383 switch (cmd.command) {
384 case Command::HELP:
385 dprintf(out, kShellCmdHelp);
386 break;
387 case Command::ENABLE_TEST_CAMERA:
388 status = enableTestCameraCmd(out, err, cmd.optionToValueMap);
389 break;
390 case Command::DISABLE_TEST_CAMERA:
391 disableTestCameraCmd(out);
392 break;
393 }
394
395 fsync(err);
Jan Sebechlebsky5cb39962023-11-22 17:33:07 +0100396 fsync(out);
Jan Sebechlebsky773c0142024-03-25 12:17:05 +0100397 return status;
Jan Sebechlebsky5cb39962023-11-22 17:33:07 +0100398}
399
Jan Sebechlebsky773c0142024-03-25 12:17:05 +0100400binder_status_t VirtualCameraService::enableTestCameraCmd(
401 const int out, const int err,
402 const std::map<std::string, std::string>& options) {
Jan Sebechlebsky5cb39962023-11-22 17:33:07 +0100403 if (mTestCameraToken != nullptr) {
Jan Sebechlebsky773c0142024-03-25 12:17:05 +0100404 dprintf(out, "Test camera is already enabled (%s).\n",
Jan Sebechlebsky5cb39962023-11-22 17:33:07 +0100405 getCamera(mTestCameraToken)->getCameraName().c_str());
Jan Sebechlebsky773c0142024-03-25 12:17:05 +0100406 return STATUS_OK;
407 }
408
Biswarup Pal15dcd132024-06-02 14:02:03 +0000409 std::optional<std::string> cameraId;
Jan Sebechlebsky773c0142024-03-25 12:17:05 +0100410 auto it = options.find("camera_id");
411 if (it != options.end()) {
Biswarup Pal15dcd132024-06-02 14:02:03 +0000412 cameraId = it->second;
Jan Sebechlebsky773c0142024-03-25 12:17:05 +0100413 if (!cameraId.has_value()) {
Biswarup Pal15dcd132024-06-02 14:02:03 +0000414 dprintf(err, "Invalid camera_id: %s", it->second.c_str());
Jan Sebechlebsky773c0142024-03-25 12:17:05 +0100415 return STATUS_BAD_VALUE;
416 }
417 }
418
419 std::optional<LensFacing> lensFacing;
420 it = options.find("lens_facing");
421 if (it != options.end()) {
422 lensFacing = parseLensFacing(it->second);
423 if (!lensFacing.has_value()) {
424 dprintf(err, "Invalid lens_facing: %s\n, must be front|back|external",
425 it->second.c_str());
426 return STATUS_BAD_VALUE;
427 }
Jan Sebechlebsky5cb39962023-11-22 17:33:07 +0100428 }
429
Jan Sebechlebsky34469e42024-05-29 10:56:12 +0200430 std::optional<int> inputFps;
431 it = options.find("input_fps");
432 if (it != options.end()) {
433 inputFps = parseInt(it->second);
434 if (!inputFps.has_value() || inputFps.value() < 1 ||
435 inputFps.value() > 1000) {
436 dprintf(err, "Invalid input fps: %s\n, must be integer in <1,1000> range.",
437 it->second.c_str());
438 return STATUS_BAD_VALUE;
439 }
440 }
441
Jan Sebechlebsky5cb39962023-11-22 17:33:07 +0100442 sp<BBinder> token = sp<BBinder>::make();
443 mTestCameraToken.set(AIBinder_fromPlatformBinder(token));
444
445 bool ret;
Jan Sebechlebsky3b478c42023-11-23 13:15:56 +0100446 VirtualCameraConfiguration configuration;
Biswarup Pal6152a302023-12-19 12:44:09 +0000447 configuration.supportedStreamConfigs.push_back({.width = kVgaWidth,
448 .height = kVgaHeight,
Jan Sebechlebsky288900f2024-05-24 14:47:54 +0200449 Format::RGBA_8888,
Biswarup Pal6152a302023-12-19 12:44:09 +0000450 .maxFps = kMaxFps});
Jan Sebechlebsky773c0142024-03-25 12:17:05 +0100451 configuration.lensFacing = lensFacing.value_or(LensFacing::EXTERNAL);
Jan Sebechlebsky288900f2024-05-24 14:47:54 +0200452 configuration.virtualCameraCallback =
Jan Sebechlebsky34469e42024-05-29 10:56:12 +0200453 ndk::SharedRefBase::make<VirtualCameraTestInstance>(
454 inputFps.value_or(kTestCameraDefaultInputFps));
Biswarup Pal15dcd132024-06-02 14:02:03 +0000455 registerCamera(mTestCameraToken, configuration,
456 cameraId.value_or(std::to_string(sNextIdNumericalPortion++)),
Biswarup Pal37a75182024-01-16 15:53:35 +0000457 kDefaultDeviceId, &ret);
Jan Sebechlebsky5cb39962023-11-22 17:33:07 +0100458 if (ret) {
Jan Sebechlebsky773c0142024-03-25 12:17:05 +0100459 dprintf(out, "Successfully registered test camera %s\n",
Jan Sebechlebsky5cb39962023-11-22 17:33:07 +0100460 getCamera(mTestCameraToken)->getCameraName().c_str());
461 } else {
Jan Sebechlebsky773c0142024-03-25 12:17:05 +0100462 dprintf(err, "Failed to create test camera\n");
Jan Sebechlebsky5cb39962023-11-22 17:33:07 +0100463 }
Jan Sebechlebsky773c0142024-03-25 12:17:05 +0100464 return STATUS_OK;
Jan Sebechlebsky5cb39962023-11-22 17:33:07 +0100465}
466
467void VirtualCameraService::disableTestCameraCmd(const int out) {
468 if (mTestCameraToken == nullptr) {
469 dprintf(out, "Test camera is not registered.");
470 }
471 unregisterCamera(mTestCameraToken);
472 mTestCameraToken.set(nullptr);
473}
474
475} // namespace virtualcamera
476} // namespace companion
477} // namespace android