• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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