blob: 11449971e6a8b81a6b66028ba2760cde15db8d71 [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
49namespace {
50
Jan Sebechlebsky3b478c42023-11-23 13:15:56 +010051constexpr int kVgaWidth = 640;
52constexpr int kVgaHeight = 480;
Biswarup Pal6152a302023-12-19 12:44:09 +000053constexpr int kMaxFps = 60;
Jan Sebechlebsky5cb39962023-11-22 17:33:07 +010054constexpr char kEnableTestCameraCmd[] = "enable_test_camera";
55constexpr char kDisableTestCameraCmd[] = "disable_test_camera";
56constexpr char kShellCmdHelp[] = R"(
57Available commands:
58 * enable_test_camera
59 * disable_test_camera
60)";
Jan Sebechlebskyde6f16f2023-11-29 09:27:36 +010061constexpr char kCreateVirtualDevicePermission[] =
62 "android.permission.CREATE_VIRTUAL_DEVICE";
Jan Sebechlebsky5cb39962023-11-22 17:33:07 +010063
Jan Sebechlebsky3b478c42023-11-23 13:15:56 +010064ndk::ScopedAStatus validateConfiguration(
65 const VirtualCameraConfiguration& configuration) {
66 if (configuration.supportedStreamConfigs.empty()) {
67 ALOGE("%s: No supported input configuration specified", __func__);
68 return ndk::ScopedAStatus::fromServiceSpecificError(
69 Status::EX_ILLEGAL_ARGUMENT);
70 }
71
72 for (const SupportedStreamConfiguration& config :
73 configuration.supportedStreamConfigs) {
74 if (!isFormatSupportedForInput(config.width, config.height,
Biswarup Pal6152a302023-12-19 12:44:09 +000075 config.pixelFormat, config.maxFps)) {
Jan Sebechlebsky3b478c42023-11-23 13:15:56 +010076 ALOGE("%s: Requested unsupported input format: %d x %d (%d)", __func__,
77 config.width, config.height, static_cast<int>(config.pixelFormat));
78 return ndk::ScopedAStatus::fromServiceSpecificError(
79 Status::EX_ILLEGAL_ARGUMENT);
80 }
81 }
Biswarup Pal6152a302023-12-19 12:44:09 +000082
83 if (configuration.sensorOrientation != SensorOrientation::ORIENTATION_0 &&
84 configuration.sensorOrientation != SensorOrientation::ORIENTATION_90 &&
85 configuration.sensorOrientation != SensorOrientation::ORIENTATION_180 &&
86 configuration.sensorOrientation != SensorOrientation::ORIENTATION_270) {
87 return ndk::ScopedAStatus::fromServiceSpecificError(
88 Status::EX_ILLEGAL_ARGUMENT);
89 }
90
Biswarup Pal112458f2023-12-28 19:50:17 +000091 if (configuration.lensFacing != LensFacing::FRONT &&
92 configuration.lensFacing != LensFacing::BACK &&
93 configuration.lensFacing != LensFacing::EXTERNAL) {
94 return ndk::ScopedAStatus::fromServiceSpecificError(
95 Status::EX_ILLEGAL_ARGUMENT);
96 }
97
Jan Sebechlebsky3b478c42023-11-23 13:15:56 +010098 return ndk::ScopedAStatus::ok();
99}
100
Jan Sebechlebsky5cb39962023-11-22 17:33:07 +0100101} // namespace
102
103VirtualCameraService::VirtualCameraService(
Jan Sebechlebskyde6f16f2023-11-29 09:27:36 +0100104 std::shared_ptr<VirtualCameraProvider> virtualCameraProvider,
105 const PermissionsProxy& permissionProxy)
106 : mVirtualCameraProvider(virtualCameraProvider),
107 mPermissionProxy(permissionProxy) {
Jan Sebechlebsky5cb39962023-11-22 17:33:07 +0100108}
109
110ndk::ScopedAStatus VirtualCameraService::registerCamera(
111 const ::ndk::SpAIBinder& token,
112 const VirtualCameraConfiguration& configuration, bool* _aidl_return) {
Jan Sebechlebskyde6f16f2023-11-29 09:27:36 +0100113 if (!mPermissionProxy.checkCallingPermission(kCreateVirtualDevicePermission)) {
114 ALOGE("%s: caller (pid %d, uid %d) doesn't hold %s permission", __func__,
115 getpid(), getuid(), kCreateVirtualDevicePermission);
116 return ndk::ScopedAStatus::fromExceptionCode(EX_SECURITY);
117 }
118
Jan Sebechlebsky5cb39962023-11-22 17:33:07 +0100119 if (_aidl_return == nullptr) {
120 return ndk::ScopedAStatus::fromServiceSpecificError(
121 Status::EX_ILLEGAL_ARGUMENT);
122 }
Jan Sebechlebskyde6f16f2023-11-29 09:27:36 +0100123
Jan Sebechlebsky5cb39962023-11-22 17:33:07 +0100124 *_aidl_return = true;
125
Jan Sebechlebsky3b478c42023-11-23 13:15:56 +0100126 auto status = validateConfiguration(configuration);
127 if (!status.isOk()) {
128 *_aidl_return = false;
129 return status;
130 }
131
Jan Sebechlebsky5cb39962023-11-22 17:33:07 +0100132 std::lock_guard lock(mLock);
133 if (mTokenToCameraName.find(token) != mTokenToCameraName.end()) {
134 ALOGE(
135 "Attempt to register camera corresponding to already registered binder "
136 "token: "
137 "0x%" PRIxPTR,
138 reinterpret_cast<uintptr_t>(token.get()));
139 *_aidl_return = false;
Jan Sebechlebsky3b478c42023-11-23 13:15:56 +0100140 return ndk::ScopedAStatus::ok();
Jan Sebechlebsky5cb39962023-11-22 17:33:07 +0100141 }
142
Jan Sebechlebsky5cb39962023-11-22 17:33:07 +0100143 std::shared_ptr<VirtualCameraDevice> camera =
Biswarup Pal6152a302023-12-19 12:44:09 +0000144 mVirtualCameraProvider->createCamera(configuration);
Jan Sebechlebsky5cb39962023-11-22 17:33:07 +0100145 if (camera == nullptr) {
146 ALOGE("Failed to create camera for binder token 0x%" PRIxPTR,
147 reinterpret_cast<uintptr_t>(token.get()));
148 *_aidl_return = false;
149 return ndk::ScopedAStatus::fromServiceSpecificError(
150 Status::EX_SERVICE_SPECIFIC);
151 }
152
153 mTokenToCameraName[token] = camera->getCameraName();
154 return ndk::ScopedAStatus::ok();
155}
156
157ndk::ScopedAStatus VirtualCameraService::unregisterCamera(
158 const ::ndk::SpAIBinder& token) {
Jan Sebechlebskyde6f16f2023-11-29 09:27:36 +0100159 if (!mPermissionProxy.checkCallingPermission(kCreateVirtualDevicePermission)) {
160 ALOGE("%s: caller (pid %d, uid %d) doesn't hold %s permission", __func__,
161 getpid(), getuid(), kCreateVirtualDevicePermission);
162 return ndk::ScopedAStatus::fromExceptionCode(EX_SECURITY);
163 }
164
Jan Sebechlebsky5cb39962023-11-22 17:33:07 +0100165 std::lock_guard lock(mLock);
166
167 auto it = mTokenToCameraName.find(token);
168 if (it == mTokenToCameraName.end()) {
169 ALOGE(
170 "Attempt to unregister camera corresponding to unknown binder token: "
171 "0x%" PRIxPTR,
172 reinterpret_cast<uintptr_t>(token.get()));
173 return ndk::ScopedAStatus::ok();
174 }
175
176 mVirtualCameraProvider->removeCamera(it->second);
177
178 return ndk::ScopedAStatus::ok();
179}
180
Biswarup Pal68137fc2023-11-24 18:06:54 +0000181ndk::ScopedAStatus VirtualCameraService::getCameraId(
182 const ::ndk::SpAIBinder& token, int32_t* _aidl_return) {
Jan Sebechlebskyde6f16f2023-11-29 09:27:36 +0100183 if (!mPermissionProxy.checkCallingPermission(kCreateVirtualDevicePermission)) {
184 ALOGE("%s: caller (pid %d, uid %d) doesn't hold %s permission", __func__,
185 getpid(), getuid(), kCreateVirtualDevicePermission);
186 return ndk::ScopedAStatus::fromExceptionCode(EX_SECURITY);
187 }
188
Biswarup Pal68137fc2023-11-24 18:06:54 +0000189 if (_aidl_return == nullptr) {
190 return ndk::ScopedAStatus::fromServiceSpecificError(
191 Status::EX_ILLEGAL_ARGUMENT);
192 }
193
194 auto camera = getCamera(token);
195 if (camera == nullptr) {
196 ALOGE(
197 "Attempt to get camera id corresponding to unknown binder token: "
198 "0x%" PRIxPTR,
199 reinterpret_cast<uintptr_t>(token.get()));
200 return ndk::ScopedAStatus::ok();
201 }
202
203 *_aidl_return = camera->getCameraId();
204
205 return ndk::ScopedAStatus::ok();
206}
207
Jan Sebechlebsky5cb39962023-11-22 17:33:07 +0100208std::shared_ptr<VirtualCameraDevice> VirtualCameraService::getCamera(
209 const ::ndk::SpAIBinder& token) {
210 if (token == nullptr) {
211 return nullptr;
212 }
213
214 std::lock_guard lock(mLock);
215 auto it = mTokenToCameraName.find(token);
216 if (it == mTokenToCameraName.end()) {
217 return nullptr;
218 }
219
220 return mVirtualCameraProvider->getCamera(it->second);
221}
222
223binder_status_t VirtualCameraService::handleShellCommand(int in, int out,
224 int err,
225 const char** args,
226 uint32_t numArgs) {
227 if (numArgs <= 0) {
228 dprintf(out, kShellCmdHelp);
Jan Sebechlebsky76d7e212023-11-28 14:10:25 +0100229 fsync(out);
230 return STATUS_OK;
Jan Sebechlebsky5cb39962023-11-22 17:33:07 +0100231 }
232
233 if (args == nullptr || args[0] == nullptr) {
234 return STATUS_BAD_VALUE;
235 }
236 const char* const cmd = args[0];
237 if (strcmp(kEnableTestCameraCmd, cmd) == 0) {
238 enableTestCameraCmd(in, err);
239 } else if (strcmp(kDisableTestCameraCmd, cmd) == 0) {
240 disableTestCameraCmd(in);
241 } else {
242 dprintf(out, kShellCmdHelp);
243 }
244
245 fsync(out);
246 return STATUS_OK;
247}
248
249void VirtualCameraService::enableTestCameraCmd(const int out, const int err) {
250 if (mTestCameraToken != nullptr) {
251 dprintf(out, "Test camera is already enabled (%s).",
252 getCamera(mTestCameraToken)->getCameraName().c_str());
253 return;
254 }
255
256 sp<BBinder> token = sp<BBinder>::make();
257 mTestCameraToken.set(AIBinder_fromPlatformBinder(token));
258
259 bool ret;
Jan Sebechlebsky3b478c42023-11-23 13:15:56 +0100260 VirtualCameraConfiguration configuration;
Biswarup Pal6152a302023-12-19 12:44:09 +0000261 configuration.supportedStreamConfigs.push_back({.width = kVgaWidth,
262 .height = kVgaHeight,
263 Format::YUV_420_888,
264 .maxFps = kMaxFps});
Biswarup Pal112458f2023-12-28 19:50:17 +0000265 configuration.lensFacing = LensFacing::EXTERNAL;
Jan Sebechlebsky3b478c42023-11-23 13:15:56 +0100266 registerCamera(mTestCameraToken, configuration, &ret);
Jan Sebechlebsky5cb39962023-11-22 17:33:07 +0100267 if (ret) {
268 dprintf(out, "Successfully registered test camera %s",
269 getCamera(mTestCameraToken)->getCameraName().c_str());
270 } else {
271 dprintf(err, "Failed to create test camera");
272 }
273}
274
275void VirtualCameraService::disableTestCameraCmd(const int out) {
276 if (mTestCameraToken == nullptr) {
277 dprintf(out, "Test camera is not registered.");
278 }
279 unregisterCamera(mTestCameraToken);
280 mTestCameraToken.set(nullptr);
281}
282
283} // namespace virtualcamera
284} // namespace companion
285} // namespace android