blob: 18961c05fcf8f57117d88133cf148290114c8579 [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
21#include <cinttypes>
22#include <cstdint>
23#include <cstdio>
24#include <memory>
25#include <mutex>
26
27#include "VirtualCameraDevice.h"
28#include "VirtualCameraProvider.h"
Jan Sebechlebsky3b478c42023-11-23 13:15:56 +010029#include "aidl/android/companion/virtualcamera/Format.h"
30#include "aidl/android/companion/virtualcamera/VirtualCameraConfiguration.h"
Jan Sebechlebsky5cb39962023-11-22 17:33:07 +010031#include "android/binder_auto_utils.h"
32#include "android/binder_libbinder.h"
33#include "binder/Status.h"
Jan Sebechlebskyde6f16f2023-11-29 09:27:36 +010034#include "util/Permissions.h"
Jan Sebechlebsky3b478c42023-11-23 13:15:56 +010035#include "util/Util.h"
Jan Sebechlebsky5cb39962023-11-22 17:33:07 +010036
37using ::android::binder::Status;
38
39namespace android {
40namespace companion {
41namespace virtualcamera {
42
Jan Sebechlebsky3b478c42023-11-23 13:15:56 +010043using ::aidl::android::companion::virtualcamera::Format;
Biswarup Pal112458f2023-12-28 19:50:17 +000044using ::aidl::android::companion::virtualcamera::LensFacing;
Biswarup Pal6152a302023-12-19 12:44:09 +000045using ::aidl::android::companion::virtualcamera::SensorOrientation;
Jan Sebechlebsky3b478c42023-11-23 13:15:56 +010046using ::aidl::android::companion::virtualcamera::SupportedStreamConfiguration;
Jan Sebechlebsky5cb39962023-11-22 17:33:07 +010047using ::aidl::android::companion::virtualcamera::VirtualCameraConfiguration;
48
Marvin Ramina8196132024-03-15 15:55:22 +000049// TODO(b/301023410) Make camera id range configurable / dynamic
50// based on already registered devices.
51std::atomic_int VirtualCameraService::sNextId{1000};
52
Jan Sebechlebsky5cb39962023-11-22 17:33:07 +010053namespace {
54
Jan Sebechlebsky3b478c42023-11-23 13:15:56 +010055constexpr int kVgaWidth = 640;
56constexpr int kVgaHeight = 480;
Biswarup Pal6152a302023-12-19 12:44:09 +000057constexpr int kMaxFps = 60;
Jan Sebechlebsky5cb39962023-11-22 17:33:07 +010058constexpr char kEnableTestCameraCmd[] = "enable_test_camera";
59constexpr char kDisableTestCameraCmd[] = "disable_test_camera";
60constexpr char kShellCmdHelp[] = R"(
61Available commands:
62 * enable_test_camera
63 * disable_test_camera
64)";
Jan Sebechlebskyde6f16f2023-11-29 09:27:36 +010065constexpr char kCreateVirtualDevicePermission[] =
66 "android.permission.CREATE_VIRTUAL_DEVICE";
Jan Sebechlebsky5cb39962023-11-22 17:33:07 +010067
Jan Sebechlebsky3b478c42023-11-23 13:15:56 +010068ndk::ScopedAStatus validateConfiguration(
69 const VirtualCameraConfiguration& configuration) {
70 if (configuration.supportedStreamConfigs.empty()) {
71 ALOGE("%s: No supported input configuration specified", __func__);
72 return ndk::ScopedAStatus::fromServiceSpecificError(
73 Status::EX_ILLEGAL_ARGUMENT);
74 }
75
76 for (const SupportedStreamConfiguration& config :
77 configuration.supportedStreamConfigs) {
78 if (!isFormatSupportedForInput(config.width, config.height,
Biswarup Pal6152a302023-12-19 12:44:09 +000079 config.pixelFormat, config.maxFps)) {
Jan Sebechlebsky3b478c42023-11-23 13:15:56 +010080 ALOGE("%s: Requested unsupported input format: %d x %d (%d)", __func__,
81 config.width, config.height, static_cast<int>(config.pixelFormat));
82 return ndk::ScopedAStatus::fromServiceSpecificError(
83 Status::EX_ILLEGAL_ARGUMENT);
84 }
85 }
Biswarup Pal6152a302023-12-19 12:44:09 +000086
87 if (configuration.sensorOrientation != SensorOrientation::ORIENTATION_0 &&
88 configuration.sensorOrientation != SensorOrientation::ORIENTATION_90 &&
89 configuration.sensorOrientation != SensorOrientation::ORIENTATION_180 &&
90 configuration.sensorOrientation != SensorOrientation::ORIENTATION_270) {
91 return ndk::ScopedAStatus::fromServiceSpecificError(
92 Status::EX_ILLEGAL_ARGUMENT);
93 }
94
Biswarup Pal112458f2023-12-28 19:50:17 +000095 if (configuration.lensFacing != LensFacing::FRONT &&
96 configuration.lensFacing != LensFacing::BACK &&
97 configuration.lensFacing != LensFacing::EXTERNAL) {
98 return ndk::ScopedAStatus::fromServiceSpecificError(
99 Status::EX_ILLEGAL_ARGUMENT);
100 }
101
Jan Sebechlebsky3b478c42023-11-23 13:15:56 +0100102 return ndk::ScopedAStatus::ok();
103}
104
Jan Sebechlebsky5cb39962023-11-22 17:33:07 +0100105} // namespace
106
107VirtualCameraService::VirtualCameraService(
Jan Sebechlebskyde6f16f2023-11-29 09:27:36 +0100108 std::shared_ptr<VirtualCameraProvider> virtualCameraProvider,
109 const PermissionsProxy& permissionProxy)
110 : mVirtualCameraProvider(virtualCameraProvider),
111 mPermissionProxy(permissionProxy) {
Jan Sebechlebsky5cb39962023-11-22 17:33:07 +0100112}
113
114ndk::ScopedAStatus VirtualCameraService::registerCamera(
115 const ::ndk::SpAIBinder& token,
116 const VirtualCameraConfiguration& configuration, bool* _aidl_return) {
Marvin Ramina8196132024-03-15 15:55:22 +0000117 return registerCamera(token, configuration, sNextId++, _aidl_return);
118}
119
120ndk::ScopedAStatus VirtualCameraService::registerCamera(
121 const ::ndk::SpAIBinder& token,
122 const VirtualCameraConfiguration& configuration, const int cameraId,
123 bool* _aidl_return) {
Jan Sebechlebskyde6f16f2023-11-29 09:27:36 +0100124 if (!mPermissionProxy.checkCallingPermission(kCreateVirtualDevicePermission)) {
125 ALOGE("%s: caller (pid %d, uid %d) doesn't hold %s permission", __func__,
126 getpid(), getuid(), kCreateVirtualDevicePermission);
127 return ndk::ScopedAStatus::fromExceptionCode(EX_SECURITY);
128 }
129
Jan Sebechlebsky5cb39962023-11-22 17:33:07 +0100130 if (_aidl_return == nullptr) {
131 return ndk::ScopedAStatus::fromServiceSpecificError(
132 Status::EX_ILLEGAL_ARGUMENT);
133 }
Jan Sebechlebskyde6f16f2023-11-29 09:27:36 +0100134
Jan Sebechlebsky5cb39962023-11-22 17:33:07 +0100135 *_aidl_return = true;
136
Jan Sebechlebsky3b478c42023-11-23 13:15:56 +0100137 auto status = validateConfiguration(configuration);
138 if (!status.isOk()) {
139 *_aidl_return = false;
140 return status;
141 }
142
Jan Sebechlebsky5cb39962023-11-22 17:33:07 +0100143 std::lock_guard lock(mLock);
144 if (mTokenToCameraName.find(token) != mTokenToCameraName.end()) {
145 ALOGE(
146 "Attempt to register camera corresponding to already registered binder "
147 "token: "
148 "0x%" PRIxPTR,
149 reinterpret_cast<uintptr_t>(token.get()));
150 *_aidl_return = false;
Jan Sebechlebsky3b478c42023-11-23 13:15:56 +0100151 return ndk::ScopedAStatus::ok();
Jan Sebechlebsky5cb39962023-11-22 17:33:07 +0100152 }
153
Jan Sebechlebsky5cb39962023-11-22 17:33:07 +0100154 std::shared_ptr<VirtualCameraDevice> camera =
Marvin Ramina8196132024-03-15 15:55:22 +0000155 mVirtualCameraProvider->createCamera(configuration, cameraId);
Jan Sebechlebsky5cb39962023-11-22 17:33:07 +0100156 if (camera == nullptr) {
157 ALOGE("Failed to create camera for binder token 0x%" PRIxPTR,
158 reinterpret_cast<uintptr_t>(token.get()));
159 *_aidl_return = false;
160 return ndk::ScopedAStatus::fromServiceSpecificError(
161 Status::EX_SERVICE_SPECIFIC);
162 }
163
164 mTokenToCameraName[token] = camera->getCameraName();
165 return ndk::ScopedAStatus::ok();
166}
167
168ndk::ScopedAStatus VirtualCameraService::unregisterCamera(
169 const ::ndk::SpAIBinder& token) {
Jan Sebechlebskyde6f16f2023-11-29 09:27:36 +0100170 if (!mPermissionProxy.checkCallingPermission(kCreateVirtualDevicePermission)) {
171 ALOGE("%s: caller (pid %d, uid %d) doesn't hold %s permission", __func__,
172 getpid(), getuid(), kCreateVirtualDevicePermission);
173 return ndk::ScopedAStatus::fromExceptionCode(EX_SECURITY);
174 }
175
Jan Sebechlebsky5cb39962023-11-22 17:33:07 +0100176 std::lock_guard lock(mLock);
177
178 auto it = mTokenToCameraName.find(token);
179 if (it == mTokenToCameraName.end()) {
180 ALOGE(
181 "Attempt to unregister camera corresponding to unknown binder token: "
182 "0x%" PRIxPTR,
183 reinterpret_cast<uintptr_t>(token.get()));
184 return ndk::ScopedAStatus::ok();
185 }
186
187 mVirtualCameraProvider->removeCamera(it->second);
188
Tony Guo6cbe11b2024-03-17 02:34:23 +0000189 mTokenToCameraName.erase(it);
Jan Sebechlebsky5cb39962023-11-22 17:33:07 +0100190 return ndk::ScopedAStatus::ok();
191}
192
Biswarup Pal68137fc2023-11-24 18:06:54 +0000193ndk::ScopedAStatus VirtualCameraService::getCameraId(
Marvin Ramina8196132024-03-15 15:55:22 +0000194 const ::ndk::SpAIBinder& token, int32_t* _aidl_return) {
Jan Sebechlebskyde6f16f2023-11-29 09:27:36 +0100195 if (!mPermissionProxy.checkCallingPermission(kCreateVirtualDevicePermission)) {
196 ALOGE("%s: caller (pid %d, uid %d) doesn't hold %s permission", __func__,
197 getpid(), getuid(), kCreateVirtualDevicePermission);
198 return ndk::ScopedAStatus::fromExceptionCode(EX_SECURITY);
199 }
200
Biswarup Pal68137fc2023-11-24 18:06:54 +0000201 if (_aidl_return == nullptr) {
202 return ndk::ScopedAStatus::fromServiceSpecificError(
Marvin Ramina8196132024-03-15 15:55:22 +0000203 Status::EX_ILLEGAL_ARGUMENT);
Biswarup Pal68137fc2023-11-24 18:06:54 +0000204 }
205
206 auto camera = getCamera(token);
207 if (camera == nullptr) {
208 ALOGE(
209 "Attempt to get camera id corresponding to unknown binder token: "
210 "0x%" PRIxPTR,
211 reinterpret_cast<uintptr_t>(token.get()));
212 return ndk::ScopedAStatus::ok();
213 }
214
215 *_aidl_return = camera->getCameraId();
216
217 return ndk::ScopedAStatus::ok();
218}
219
Jan Sebechlebsky5cb39962023-11-22 17:33:07 +0100220std::shared_ptr<VirtualCameraDevice> VirtualCameraService::getCamera(
221 const ::ndk::SpAIBinder& token) {
222 if (token == nullptr) {
223 return nullptr;
224 }
225
226 std::lock_guard lock(mLock);
227 auto it = mTokenToCameraName.find(token);
228 if (it == mTokenToCameraName.end()) {
229 return nullptr;
230 }
231
232 return mVirtualCameraProvider->getCamera(it->second);
233}
234
235binder_status_t VirtualCameraService::handleShellCommand(int in, int out,
236 int err,
237 const char** args,
238 uint32_t numArgs) {
239 if (numArgs <= 0) {
240 dprintf(out, kShellCmdHelp);
Jan Sebechlebsky76d7e212023-11-28 14:10:25 +0100241 fsync(out);
242 return STATUS_OK;
Jan Sebechlebsky5cb39962023-11-22 17:33:07 +0100243 }
244
245 if (args == nullptr || args[0] == nullptr) {
246 return STATUS_BAD_VALUE;
247 }
248 const char* const cmd = args[0];
249 if (strcmp(kEnableTestCameraCmd, cmd) == 0) {
Marvin Ramina8196132024-03-15 15:55:22 +0000250 int cameraId = 0;
251 if (numArgs > 1 && args[1] != nullptr) {
252 cameraId = atoi(args[1]);
253 }
254 if (cameraId == 0) {
255 cameraId = sNextId++;
256 }
257
258 enableTestCameraCmd(in, err, cameraId);
Jan Sebechlebsky5cb39962023-11-22 17:33:07 +0100259 } else if (strcmp(kDisableTestCameraCmd, cmd) == 0) {
260 disableTestCameraCmd(in);
261 } else {
262 dprintf(out, kShellCmdHelp);
263 }
264
265 fsync(out);
266 return STATUS_OK;
267}
268
Marvin Ramina8196132024-03-15 15:55:22 +0000269void VirtualCameraService::enableTestCameraCmd(const int out, const int err,
270 const int cameraId) {
Jan Sebechlebsky5cb39962023-11-22 17:33:07 +0100271 if (mTestCameraToken != nullptr) {
272 dprintf(out, "Test camera is already enabled (%s).",
273 getCamera(mTestCameraToken)->getCameraName().c_str());
274 return;
275 }
276
277 sp<BBinder> token = sp<BBinder>::make();
278 mTestCameraToken.set(AIBinder_fromPlatformBinder(token));
279
280 bool ret;
Jan Sebechlebsky3b478c42023-11-23 13:15:56 +0100281 VirtualCameraConfiguration configuration;
Biswarup Pal6152a302023-12-19 12:44:09 +0000282 configuration.supportedStreamConfigs.push_back({.width = kVgaWidth,
283 .height = kVgaHeight,
284 Format::YUV_420_888,
285 .maxFps = kMaxFps});
Biswarup Pal112458f2023-12-28 19:50:17 +0000286 configuration.lensFacing = LensFacing::EXTERNAL;
Marvin Ramina8196132024-03-15 15:55:22 +0000287 registerCamera(mTestCameraToken, configuration, cameraId, &ret);
Jan Sebechlebsky5cb39962023-11-22 17:33:07 +0100288 if (ret) {
289 dprintf(out, "Successfully registered test camera %s",
290 getCamera(mTestCameraToken)->getCameraName().c_str());
291 } else {
292 dprintf(err, "Failed to create test camera");
293 }
294}
295
296void VirtualCameraService::disableTestCameraCmd(const int out) {
297 if (mTestCameraToken == nullptr) {
298 dprintf(out, "Test camera is not registered.");
299 }
300 unregisterCamera(mTestCameraToken);
301 mTestCameraToken.set(nullptr);
302}
303
304} // namespace virtualcamera
305} // namespace companion
306} // namespace android