1 /*
2 * Copyright 2023 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_NDEBUG 0
18 #define LOG_TAG "VirtualCameraService"
19 #include "VirtualCameraService.h"
20
21 #include <algorithm>
22 #include <array>
23 #include <cinttypes>
24 #include <cstdint>
25 #include <memory>
26 #include <mutex>
27 #include <optional>
28 #include <regex>
29 #include <variant>
30
31 #include "VirtualCameraDevice.h"
32 #include "VirtualCameraProvider.h"
33 #include "VirtualCameraTestInstance.h"
34 #include "aidl/android/companion/virtualcamera/Format.h"
35 #include "aidl/android/companion/virtualcamera/LensFacing.h"
36 #include "aidl/android/companion/virtualcamera/VirtualCameraConfiguration.h"
37 #include "android/binder_auto_utils.h"
38 #include "android/binder_interface_utils.h"
39 #include "android/binder_libbinder.h"
40 #include "android/binder_status.h"
41 #include "android/hardware_buffer.h"
42 #include "binder/Status.h"
43 #include "fmt/format.h"
44 #include "util/EglDisplayContext.h"
45 #include "util/EglUtil.h"
46 #include "util/Permissions.h"
47 #include "util/Util.h"
48
49 using ::android::binder::Status;
50
51 namespace android {
52 namespace companion {
53 namespace virtualcamera {
54
55 using ::aidl::android::companion::virtualcamera::Format;
56 using ::aidl::android::companion::virtualcamera::LensFacing;
57 using ::aidl::android::companion::virtualcamera::SensorOrientation;
58 using ::aidl::android::companion::virtualcamera::SupportedStreamConfiguration;
59 using ::aidl::android::companion::virtualcamera::VirtualCameraConfiguration;
60
61 namespace {
62
63 constexpr char kCameraIdPrefix[] = "v";
64 constexpr int kVgaWidth = 640;
65 constexpr int kVgaHeight = 480;
66 constexpr int kMaxFps = 60;
67 constexpr int kTestCameraDefaultInputFps = 30;
68 constexpr char kEnableTestCameraCmd[] = "enable_test_camera";
69 constexpr char kDisableTestCameraCmd[] = "disable_test_camera";
70 constexpr char kHelp[] = "help";
71 constexpr char kShellCmdHelp[] = R"(
72 Usage:
73 cmd virtual_camera command [--option=value]
74 Available commands:
75 * enable_test_camera
76 Options:
77 --camera_id=(ID) - override numerical ID for test camera instance
78 --lens_facing=(front|back|external) - specifies lens facing for test camera instance
79 --input_fps=(fps) - specify input fps for test camera, valid values are from 1 to 1000
80 --sensor_orientation=(0|90|180|270) - Clockwise angle through which the output image
81 needs to be rotated to be upright on the device screen in its native orientation
82 * disable_test_camera
83 )";
84 constexpr char kCreateVirtualDevicePermission[] =
85 "android.permission.CREATE_VIRTUAL_DEVICE";
86
87 constexpr std::array<const char*, 3> kRequiredEglExtensions = {
88 "GL_OES_EGL_image_external",
89 "GL_OES_EGL_image_external_essl3",
90 "GL_EXT_YUV_target",
91 };
92
93 // Numerical portion for id to assign to next created camera.
94 static std::atomic_int sNextIdNumericalPortion{1000};
95
validateConfiguration(const VirtualCameraConfiguration & configuration)96 ndk::ScopedAStatus validateConfiguration(
97 const VirtualCameraConfiguration& configuration) {
98 if (configuration.supportedStreamConfigs.empty()) {
99 ALOGE("%s: No supported input configuration specified", __func__);
100 return ndk::ScopedAStatus::fromServiceSpecificError(
101 Status::EX_ILLEGAL_ARGUMENT);
102 }
103
104 if (configuration.virtualCameraCallback == nullptr) {
105 ALOGE("%s: Input configuration is missing virtual camera callback",
106 __func__);
107 return ndk::ScopedAStatus::fromServiceSpecificError(
108 Status::EX_ILLEGAL_ARGUMENT);
109 }
110
111 for (const SupportedStreamConfiguration& config :
112 configuration.supportedStreamConfigs) {
113 if (!isFormatSupportedForInput(config.width, config.height,
114 config.pixelFormat, config.maxFps)) {
115 ALOGE("%s: Requested unsupported input format: %d x %d (%d)", __func__,
116 config.width, config.height, static_cast<int>(config.pixelFormat));
117 return ndk::ScopedAStatus::fromServiceSpecificError(
118 Status::EX_ILLEGAL_ARGUMENT);
119 }
120 }
121
122 if (configuration.sensorOrientation != SensorOrientation::ORIENTATION_0 &&
123 configuration.sensorOrientation != SensorOrientation::ORIENTATION_90 &&
124 configuration.sensorOrientation != SensorOrientation::ORIENTATION_180 &&
125 configuration.sensorOrientation != SensorOrientation::ORIENTATION_270) {
126 return ndk::ScopedAStatus::fromServiceSpecificError(
127 Status::EX_ILLEGAL_ARGUMENT);
128 }
129
130 if (configuration.lensFacing != LensFacing::FRONT &&
131 configuration.lensFacing != LensFacing::BACK &&
132 configuration.lensFacing != LensFacing::EXTERNAL) {
133 return ndk::ScopedAStatus::fromServiceSpecificError(
134 Status::EX_ILLEGAL_ARGUMENT);
135 }
136
137 return ndk::ScopedAStatus::ok();
138 }
139
140 enum class Command {
141 ENABLE_TEST_CAMERA,
142 DISABLE_TEST_CAMERA,
143 HELP,
144 };
145
146 struct CommandWithOptions {
147 Command command;
148 std::map<std::string, std::string> optionToValueMap;
149 };
150
parseInt(const std::string & s)151 std::optional<int> parseInt(const std::string& s) {
152 if (!std::all_of(s.begin(), s.end(), [](char c) { return std::isdigit(c); })) {
153 return std::nullopt;
154 }
155 int ret = atoi(s.c_str());
156 return ret > 0 ? std::optional(ret) : std::nullopt;
157 }
158
parseLensFacing(const std::string & s)159 std::optional<LensFacing> parseLensFacing(const std::string& s) {
160 static const std::map<std::string, LensFacing> strToLensFacing{
161 {"front", LensFacing::FRONT},
162 {"back", LensFacing::BACK},
163 {"external", LensFacing::EXTERNAL}};
164 auto it = strToLensFacing.find(s);
165 return it == strToLensFacing.end() ? std::nullopt : std::optional(it->second);
166 }
167
parseCommand(const char ** args,const uint32_t numArgs)168 std::variant<CommandWithOptions, std::string> parseCommand(
169 const char** args, const uint32_t numArgs) {
170 static const std::regex optionRegex("^--(\\w+)(?:=(.+))?$");
171 static const std::map<std::string, Command> strToCommand{
172 {kHelp, Command::HELP},
173 {kEnableTestCameraCmd, Command::ENABLE_TEST_CAMERA},
174 {kDisableTestCameraCmd, Command::DISABLE_TEST_CAMERA}};
175
176 if (numArgs < 1) {
177 return CommandWithOptions{.command = Command::HELP};
178 }
179
180 // We interpret the first argument as command;
181 auto it = strToCommand.find(args[0]);
182 if (it == strToCommand.end()) {
183 return "Unknown command: " + std::string(args[0]);
184 }
185
186 CommandWithOptions cmd{.command = it->second};
187
188 for (int i = 1; i < numArgs; i++) {
189 std::cmatch cm;
190 if (!std::regex_match(args[i], cm, optionRegex)) {
191 return "Not an option: " + std::string(args[i]);
192 }
193
194 cmd.optionToValueMap[cm[1]] = cm[2];
195 }
196
197 return cmd;
198 }
199
verifyRequiredEglExtensions()200 ndk::ScopedAStatus verifyRequiredEglExtensions() {
201 EglDisplayContext context;
202 for (const char* eglExtension : kRequiredEglExtensions) {
203 if (!isGlExtensionSupported(eglExtension)) {
204 ALOGE("%s not supported", eglExtension);
205 return ndk::ScopedAStatus::fromExceptionCodeWithMessage(
206 EX_UNSUPPORTED_OPERATION,
207 fmt::format(
208 "Cannot create virtual camera, because required EGL extension {} "
209 "is not supported on this system",
210 eglExtension)
211 .c_str());
212 }
213 }
214 return ndk::ScopedAStatus::ok();
215 }
216
verifyHardwareBufferSupport()217 ndk::ScopedAStatus verifyHardwareBufferSupport() {
218 static constexpr AHardwareBuffer_Desc desc{
219 .width = static_cast<uint32_t>(kVgaWidth),
220 .height = static_cast<uint32_t>(kVgaHeight),
221 .layers = 1,
222 .format = kHardwareBufferFormat,
223 .usage = kHardwareBufferUsage,
224 .rfu0 = 0,
225 .rfu1 = 0};
226 if (AHardwareBuffer_isSupported(&desc)) {
227 return ndk::ScopedAStatus::ok();
228 }
229 ALOGE("%s: Hardware buffer allocation is unsupported for required formats",
230 __func__);
231 return ndk::ScopedAStatus::fromExceptionCodeWithMessage(
232 EX_UNSUPPORTED_OPERATION,
233 fmt::format("Cannot create virtual camera, because hardware buffer "
234 "allocation is unsupported")
235 .c_str());
236 }
237
createCameraId(const int32_t deviceId)238 std::string createCameraId(const int32_t deviceId) {
239 return kCameraIdPrefix + std::to_string(deviceId) + "_" +
240 std::to_string(sNextIdNumericalPortion++);
241 }
242
243 } // namespace
244
VirtualCameraService(std::shared_ptr<VirtualCameraProvider> virtualCameraProvider,const PermissionsProxy & permissionProxy)245 VirtualCameraService::VirtualCameraService(
246 std::shared_ptr<VirtualCameraProvider> virtualCameraProvider,
247 const PermissionsProxy& permissionProxy)
248 : mVirtualCameraProvider(virtualCameraProvider),
249 mPermissionProxy(permissionProxy) {
250 }
251
registerCamera(const::ndk::SpAIBinder & token,const VirtualCameraConfiguration & configuration,const int32_t deviceId,bool * _aidl_return)252 ndk::ScopedAStatus VirtualCameraService::registerCamera(
253 const ::ndk::SpAIBinder& token,
254 const VirtualCameraConfiguration& configuration, const int32_t deviceId,
255 bool* _aidl_return) {
256 return registerCamera(token, configuration, createCameraId(deviceId),
257 deviceId, _aidl_return);
258 }
259
registerCamera(const::ndk::SpAIBinder & token,const VirtualCameraConfiguration & configuration,const std::string & cameraId,const int32_t deviceId,bool * _aidl_return)260 ndk::ScopedAStatus VirtualCameraService::registerCamera(
261 const ::ndk::SpAIBinder& token,
262 const VirtualCameraConfiguration& configuration,
263 const std::string& cameraId, const int32_t deviceId, bool* _aidl_return) {
264 if (!mPermissionProxy.checkCallingPermission(kCreateVirtualDevicePermission)) {
265 return ndk::ScopedAStatus::fromExceptionCode(EX_SECURITY);
266 }
267 return registerCameraNoCheck(token, configuration, cameraId, deviceId,
268 _aidl_return);
269 }
270
registerCameraNoCheck(const::ndk::SpAIBinder & token,const VirtualCameraConfiguration & configuration,const std::string & cameraId,const int32_t deviceId,bool * _aidl_return)271 ndk::ScopedAStatus VirtualCameraService::registerCameraNoCheck(
272 const ::ndk::SpAIBinder& token,
273 const VirtualCameraConfiguration& configuration,
274 const std::string& cameraId, const int32_t deviceId, bool* _aidl_return) {
275 if (_aidl_return == nullptr) {
276 return ndk::ScopedAStatus::fromServiceSpecificError(
277 Status::EX_ILLEGAL_ARGUMENT);
278 }
279
280 if (mCheckHardwareRequirements) {
281 auto status = verifyRequiredEglExtensions();
282 if (!status.isOk()) {
283 *_aidl_return = false;
284 return status;
285 }
286
287 status = verifyHardwareBufferSupport();
288 if (!status.isOk()) {
289 *_aidl_return = false;
290 return status;
291 }
292 }
293
294 auto status = validateConfiguration(configuration);
295 if (!status.isOk()) {
296 *_aidl_return = false;
297 return status;
298 }
299
300 std::lock_guard lock(mLock);
301 if (mTokenToCameraName.find(token) != mTokenToCameraName.end()) {
302 ALOGE(
303 "Attempt to register camera corresponding to already registered binder "
304 "token: "
305 "0x%" PRIxPTR,
306 reinterpret_cast<uintptr_t>(token.get()));
307 *_aidl_return = false;
308 return ndk::ScopedAStatus::ok();
309 }
310
311 std::shared_ptr<VirtualCameraDevice> camera =
312 mVirtualCameraProvider->createCamera(configuration, cameraId, deviceId);
313 if (camera == nullptr) {
314 ALOGE("Failed to create camera for binder token 0x%" PRIxPTR,
315 reinterpret_cast<uintptr_t>(token.get()));
316 *_aidl_return = false;
317 return ndk::ScopedAStatus::fromServiceSpecificError(
318 Status::EX_SERVICE_SPECIFIC);
319 }
320
321 mTokenToCameraName[token] = camera->getCameraName();
322 *_aidl_return = true;
323 return ndk::ScopedAStatus::ok();
324 }
325
unregisterCamera(const::ndk::SpAIBinder & token)326 ndk::ScopedAStatus VirtualCameraService::unregisterCamera(
327 const ::ndk::SpAIBinder& token) {
328 if (!mPermissionProxy.checkCallingPermission(kCreateVirtualDevicePermission)) {
329 ALOGE("%s: caller (pid %d, uid %d) doesn't hold %s permission", __func__,
330 getpid(), getuid(), kCreateVirtualDevicePermission);
331 return ndk::ScopedAStatus::fromExceptionCode(EX_SECURITY);
332 }
333
334 std::lock_guard lock(mLock);
335
336 auto it = mTokenToCameraName.find(token);
337 if (it == mTokenToCameraName.end()) {
338 ALOGE(
339 "Attempt to unregister camera corresponding to unknown binder token: "
340 "0x%" PRIxPTR,
341 reinterpret_cast<uintptr_t>(token.get()));
342 return ndk::ScopedAStatus::ok();
343 }
344
345 mVirtualCameraProvider->removeCamera(it->second);
346
347 mTokenToCameraName.erase(it);
348 return ndk::ScopedAStatus::ok();
349 }
350
getCameraId(const::ndk::SpAIBinder & token,std::string * _aidl_return)351 ndk::ScopedAStatus VirtualCameraService::getCameraId(
352 const ::ndk::SpAIBinder& token, std::string* _aidl_return) {
353 if (!mPermissionProxy.checkCallingPermission(kCreateVirtualDevicePermission)) {
354 ALOGE("%s: caller (pid %d, uid %d) doesn't hold %s permission", __func__,
355 getpid(), getuid(), kCreateVirtualDevicePermission);
356 return ndk::ScopedAStatus::fromExceptionCode(EX_SECURITY);
357 }
358
359 if (_aidl_return == nullptr) {
360 return ndk::ScopedAStatus::fromServiceSpecificError(
361 Status::EX_ILLEGAL_ARGUMENT);
362 }
363
364 auto camera = getCamera(token);
365 if (camera == nullptr) {
366 ALOGE(
367 "Attempt to get camera id corresponding to unknown binder token: "
368 "0x%" PRIxPTR,
369 reinterpret_cast<uintptr_t>(token.get()));
370 return ndk::ScopedAStatus::ok();
371 }
372
373 *_aidl_return = camera->getCameraId();
374
375 return ndk::ScopedAStatus::ok();
376 }
377
getCamera(const::ndk::SpAIBinder & token)378 std::shared_ptr<VirtualCameraDevice> VirtualCameraService::getCamera(
379 const ::ndk::SpAIBinder& token) {
380 if (token == nullptr) {
381 return nullptr;
382 }
383
384 std::lock_guard lock(mLock);
385 auto it = mTokenToCameraName.find(token);
386 if (it == mTokenToCameraName.end()) {
387 return nullptr;
388 }
389
390 return mVirtualCameraProvider->getCamera(it->second);
391 }
392
handleShellCommand(int,int out,int err,const char ** args,uint32_t numArgs)393 binder_status_t VirtualCameraService::handleShellCommand(int, int out, int err,
394 const char** args,
395 uint32_t numArgs) {
396 if (numArgs <= 0) {
397 dprintf(out, kShellCmdHelp);
398 fsync(out);
399 return STATUS_OK;
400 }
401
402 auto isNullptr = [](const char* ptr) { return ptr == nullptr; };
403 if (args == nullptr || std::any_of(args, args + numArgs, isNullptr)) {
404 return STATUS_BAD_VALUE;
405 }
406
407 std::variant<CommandWithOptions, std::string> cmdOrErrorMessage =
408 parseCommand(args, numArgs);
409 if (std::holds_alternative<std::string>(cmdOrErrorMessage)) {
410 dprintf(err, "Error: %s\n",
411 std::get<std::string>(cmdOrErrorMessage).c_str());
412 return STATUS_BAD_VALUE;
413 }
414
415 const CommandWithOptions& cmd =
416 std::get<CommandWithOptions>(cmdOrErrorMessage);
417 binder_status_t status = STATUS_OK;
418 switch (cmd.command) {
419 case Command::HELP:
420 dprintf(out, kShellCmdHelp);
421 break;
422 case Command::ENABLE_TEST_CAMERA:
423 status = enableTestCameraCmd(out, err, cmd.optionToValueMap);
424 break;
425 case Command::DISABLE_TEST_CAMERA:
426 status = disableTestCameraCmd(out);
427 break;
428 }
429
430 fsync(err);
431 fsync(out);
432 return status;
433 }
434
enableTestCameraCmd(const int out,const int err,const std::map<std::string,std::string> & options)435 binder_status_t VirtualCameraService::enableTestCameraCmd(
436 const int out, const int err,
437 const std::map<std::string, std::string>& options) {
438 if (mTestCameraToken != nullptr) {
439 dprintf(out, "Test camera is already enabled (%s).\n",
440 getCamera(mTestCameraToken)->getCameraName().c_str());
441 return STATUS_OK;
442 }
443
444 std::optional<std::string> cameraId;
445 auto it = options.find("camera_id");
446 if (it != options.end()) {
447 cameraId = it->second;
448 if (!cameraId.has_value()) {
449 dprintf(err, "Invalid camera_id: %s", it->second.c_str());
450 return STATUS_BAD_VALUE;
451 }
452 }
453
454 std::optional<LensFacing> lensFacing;
455 it = options.find("lens_facing");
456 if (it != options.end()) {
457 lensFacing = parseLensFacing(it->second);
458 if (!lensFacing.has_value()) {
459 dprintf(err, "Invalid lens_facing: %s\n, must be front|back|external",
460 it->second.c_str());
461 return STATUS_BAD_VALUE;
462 }
463 }
464
465 std::optional<int> inputFps;
466 it = options.find("input_fps");
467 if (it != options.end()) {
468 inputFps = parseInt(it->second);
469 if (!inputFps.has_value() || inputFps.value() < 1 ||
470 inputFps.value() > 1000) {
471 dprintf(err, "Invalid input fps: %s\n, must be integer in <1,1000> range.",
472 it->second.c_str());
473 return STATUS_BAD_VALUE;
474 }
475 }
476
477 std::optional<SensorOrientation> sensorOrientation;
478 std::optional<int> sensorOrientationInt;
479 it = options.find("sensor_orientation");
480 if (it != options.end()) {
481 sensorOrientationInt = parseInt(it->second);
482 switch (sensorOrientationInt.value_or(0)) {
483 case 0:
484 sensorOrientation = SensorOrientation::ORIENTATION_0;
485 break;
486 case 90:
487 sensorOrientation = SensorOrientation::ORIENTATION_90;
488 break;
489 case 180:
490 sensorOrientation = SensorOrientation::ORIENTATION_180;
491 break;
492 case 270:
493 sensorOrientation = SensorOrientation::ORIENTATION_270;
494 break;
495 default:
496 dprintf(err, "Invalid sensor rotation: %s\n, must be 0, 90, 180 or 270.",
497 it->second.c_str());
498 return STATUS_BAD_VALUE;
499 }
500 }
501
502 sp<BBinder> token = sp<BBinder>::make();
503 mTestCameraToken.set(AIBinder_fromPlatformBinder(token));
504
505 bool ret;
506 VirtualCameraConfiguration configuration;
507 configuration.supportedStreamConfigs.push_back({.width = kVgaWidth,
508 .height = kVgaHeight,
509 Format::RGBA_8888,
510 .maxFps = kMaxFps});
511 configuration.lensFacing = lensFacing.value_or(LensFacing::EXTERNAL);
512 configuration.sensorOrientation =
513 sensorOrientation.value_or(SensorOrientation::ORIENTATION_0);
514 configuration.virtualCameraCallback =
515 ndk::SharedRefBase::make<VirtualCameraTestInstance>(
516 inputFps.value_or(kTestCameraDefaultInputFps));
517 registerCameraNoCheck(
518 mTestCameraToken, configuration,
519 cameraId.value_or(std::to_string(sNextIdNumericalPortion++)),
520 kDefaultDeviceId, &ret);
521 if (!ret) {
522 dprintf(err, "Failed to create test camera (error %d)\n", ret);
523 mTestCameraToken.set(nullptr);
524 return EOPNOTSUPP;
525 }
526
527 dprintf(out, "Successfully registered test camera %s\n",
528 getCamera(mTestCameraToken)->getCameraName().c_str());
529 return STATUS_OK;
530 }
531
disableTestCameraCmd(const int out)532 binder_status_t VirtualCameraService::disableTestCameraCmd(const int out) {
533 if (mTestCameraToken == nullptr) {
534 dprintf(out, "Test camera is not registered.");
535 }
536 binder_status_t ret = unregisterCamera(mTestCameraToken).getStatus();
537 mTestCameraToken.set(nullptr);
538 return ret;
539 }
540
541 } // namespace virtualcamera
542 } // namespace companion
543 } // namespace android
544