blob: f9d81b987b1d037e726efe80324de3d29baa85c6 [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 Pal6152a302023-12-19 12:44:09 +000044using ::aidl::android::companion::virtualcamera::SensorOrientation;
Jan Sebechlebsky3b478c42023-11-23 13:15:56 +010045using ::aidl::android::companion::virtualcamera::SupportedStreamConfiguration;
Jan Sebechlebsky5cb39962023-11-22 17:33:07 +010046using ::aidl::android::companion::virtualcamera::VirtualCameraConfiguration;
47
48namespace {
49
Jan Sebechlebsky3b478c42023-11-23 13:15:56 +010050constexpr int kVgaWidth = 640;
51constexpr int kVgaHeight = 480;
Biswarup Pal6152a302023-12-19 12:44:09 +000052constexpr int kMaxFps = 60;
Jan Sebechlebsky5cb39962023-11-22 17:33:07 +010053constexpr char kEnableTestCameraCmd[] = "enable_test_camera";
54constexpr char kDisableTestCameraCmd[] = "disable_test_camera";
55constexpr char kShellCmdHelp[] = R"(
56Available commands:
57 * enable_test_camera
58 * disable_test_camera
59)";
Jan Sebechlebskyde6f16f2023-11-29 09:27:36 +010060constexpr char kCreateVirtualDevicePermission[] =
61 "android.permission.CREATE_VIRTUAL_DEVICE";
Jan Sebechlebsky5cb39962023-11-22 17:33:07 +010062
Jan Sebechlebsky3b478c42023-11-23 13:15:56 +010063ndk::ScopedAStatus validateConfiguration(
64 const VirtualCameraConfiguration& configuration) {
65 if (configuration.supportedStreamConfigs.empty()) {
66 ALOGE("%s: No supported input configuration specified", __func__);
67 return ndk::ScopedAStatus::fromServiceSpecificError(
68 Status::EX_ILLEGAL_ARGUMENT);
69 }
70
71 for (const SupportedStreamConfiguration& config :
72 configuration.supportedStreamConfigs) {
73 if (!isFormatSupportedForInput(config.width, config.height,
Biswarup Pal6152a302023-12-19 12:44:09 +000074 config.pixelFormat, config.maxFps)) {
Jan Sebechlebsky3b478c42023-11-23 13:15:56 +010075 ALOGE("%s: Requested unsupported input format: %d x %d (%d)", __func__,
76 config.width, config.height, static_cast<int>(config.pixelFormat));
77 return ndk::ScopedAStatus::fromServiceSpecificError(
78 Status::EX_ILLEGAL_ARGUMENT);
79 }
80 }
Biswarup Pal6152a302023-12-19 12:44:09 +000081
82 if (configuration.sensorOrientation != SensorOrientation::ORIENTATION_0 &&
83 configuration.sensorOrientation != SensorOrientation::ORIENTATION_90 &&
84 configuration.sensorOrientation != SensorOrientation::ORIENTATION_180 &&
85 configuration.sensorOrientation != SensorOrientation::ORIENTATION_270) {
86 return ndk::ScopedAStatus::fromServiceSpecificError(
87 Status::EX_ILLEGAL_ARGUMENT);
88 }
89
Jan Sebechlebsky3b478c42023-11-23 13:15:56 +010090 return ndk::ScopedAStatus::ok();
91}
92
Jan Sebechlebsky5cb39962023-11-22 17:33:07 +010093} // namespace
94
95VirtualCameraService::VirtualCameraService(
Jan Sebechlebskyde6f16f2023-11-29 09:27:36 +010096 std::shared_ptr<VirtualCameraProvider> virtualCameraProvider,
97 const PermissionsProxy& permissionProxy)
98 : mVirtualCameraProvider(virtualCameraProvider),
99 mPermissionProxy(permissionProxy) {
Jan Sebechlebsky5cb39962023-11-22 17:33:07 +0100100}
101
102ndk::ScopedAStatus VirtualCameraService::registerCamera(
103 const ::ndk::SpAIBinder& token,
104 const VirtualCameraConfiguration& configuration, bool* _aidl_return) {
Jan Sebechlebskyde6f16f2023-11-29 09:27:36 +0100105 if (!mPermissionProxy.checkCallingPermission(kCreateVirtualDevicePermission)) {
106 ALOGE("%s: caller (pid %d, uid %d) doesn't hold %s permission", __func__,
107 getpid(), getuid(), kCreateVirtualDevicePermission);
108 return ndk::ScopedAStatus::fromExceptionCode(EX_SECURITY);
109 }
110
Jan Sebechlebsky5cb39962023-11-22 17:33:07 +0100111 if (_aidl_return == nullptr) {
112 return ndk::ScopedAStatus::fromServiceSpecificError(
113 Status::EX_ILLEGAL_ARGUMENT);
114 }
Jan Sebechlebskyde6f16f2023-11-29 09:27:36 +0100115
Jan Sebechlebsky5cb39962023-11-22 17:33:07 +0100116 *_aidl_return = true;
117
Jan Sebechlebsky3b478c42023-11-23 13:15:56 +0100118 auto status = validateConfiguration(configuration);
119 if (!status.isOk()) {
120 *_aidl_return = false;
121 return status;
122 }
123
Jan Sebechlebsky5cb39962023-11-22 17:33:07 +0100124 std::lock_guard lock(mLock);
125 if (mTokenToCameraName.find(token) != mTokenToCameraName.end()) {
126 ALOGE(
127 "Attempt to register camera corresponding to already registered binder "
128 "token: "
129 "0x%" PRIxPTR,
130 reinterpret_cast<uintptr_t>(token.get()));
131 *_aidl_return = false;
Jan Sebechlebsky3b478c42023-11-23 13:15:56 +0100132 return ndk::ScopedAStatus::ok();
Jan Sebechlebsky5cb39962023-11-22 17:33:07 +0100133 }
134
Jan Sebechlebsky5cb39962023-11-22 17:33:07 +0100135 std::shared_ptr<VirtualCameraDevice> camera =
Biswarup Pal6152a302023-12-19 12:44:09 +0000136 mVirtualCameraProvider->createCamera(configuration);
Jan Sebechlebsky5cb39962023-11-22 17:33:07 +0100137 if (camera == nullptr) {
138 ALOGE("Failed to create camera for binder token 0x%" PRIxPTR,
139 reinterpret_cast<uintptr_t>(token.get()));
140 *_aidl_return = false;
141 return ndk::ScopedAStatus::fromServiceSpecificError(
142 Status::EX_SERVICE_SPECIFIC);
143 }
144
145 mTokenToCameraName[token] = camera->getCameraName();
146 return ndk::ScopedAStatus::ok();
147}
148
149ndk::ScopedAStatus VirtualCameraService::unregisterCamera(
150 const ::ndk::SpAIBinder& token) {
Jan Sebechlebskyde6f16f2023-11-29 09:27:36 +0100151 if (!mPermissionProxy.checkCallingPermission(kCreateVirtualDevicePermission)) {
152 ALOGE("%s: caller (pid %d, uid %d) doesn't hold %s permission", __func__,
153 getpid(), getuid(), kCreateVirtualDevicePermission);
154 return ndk::ScopedAStatus::fromExceptionCode(EX_SECURITY);
155 }
156
Jan Sebechlebsky5cb39962023-11-22 17:33:07 +0100157 std::lock_guard lock(mLock);
158
159 auto it = mTokenToCameraName.find(token);
160 if (it == mTokenToCameraName.end()) {
161 ALOGE(
162 "Attempt to unregister camera corresponding to unknown binder token: "
163 "0x%" PRIxPTR,
164 reinterpret_cast<uintptr_t>(token.get()));
165 return ndk::ScopedAStatus::ok();
166 }
167
168 mVirtualCameraProvider->removeCamera(it->second);
169
170 return ndk::ScopedAStatus::ok();
171}
172
Biswarup Pal68137fc2023-11-24 18:06:54 +0000173ndk::ScopedAStatus VirtualCameraService::getCameraId(
174 const ::ndk::SpAIBinder& token, int32_t* _aidl_return) {
Jan Sebechlebskyde6f16f2023-11-29 09:27:36 +0100175 if (!mPermissionProxy.checkCallingPermission(kCreateVirtualDevicePermission)) {
176 ALOGE("%s: caller (pid %d, uid %d) doesn't hold %s permission", __func__,
177 getpid(), getuid(), kCreateVirtualDevicePermission);
178 return ndk::ScopedAStatus::fromExceptionCode(EX_SECURITY);
179 }
180
Biswarup Pal68137fc2023-11-24 18:06:54 +0000181 if (_aidl_return == nullptr) {
182 return ndk::ScopedAStatus::fromServiceSpecificError(
183 Status::EX_ILLEGAL_ARGUMENT);
184 }
185
186 auto camera = getCamera(token);
187 if (camera == nullptr) {
188 ALOGE(
189 "Attempt to get camera id corresponding to unknown binder token: "
190 "0x%" PRIxPTR,
191 reinterpret_cast<uintptr_t>(token.get()));
192 return ndk::ScopedAStatus::ok();
193 }
194
195 *_aidl_return = camera->getCameraId();
196
197 return ndk::ScopedAStatus::ok();
198}
199
Jan Sebechlebsky5cb39962023-11-22 17:33:07 +0100200std::shared_ptr<VirtualCameraDevice> VirtualCameraService::getCamera(
201 const ::ndk::SpAIBinder& token) {
202 if (token == nullptr) {
203 return nullptr;
204 }
205
206 std::lock_guard lock(mLock);
207 auto it = mTokenToCameraName.find(token);
208 if (it == mTokenToCameraName.end()) {
209 return nullptr;
210 }
211
212 return mVirtualCameraProvider->getCamera(it->second);
213}
214
215binder_status_t VirtualCameraService::handleShellCommand(int in, int out,
216 int err,
217 const char** args,
218 uint32_t numArgs) {
219 if (numArgs <= 0) {
220 dprintf(out, kShellCmdHelp);
Jan Sebechlebsky76d7e212023-11-28 14:10:25 +0100221 fsync(out);
222 return STATUS_OK;
Jan Sebechlebsky5cb39962023-11-22 17:33:07 +0100223 }
224
225 if (args == nullptr || args[0] == nullptr) {
226 return STATUS_BAD_VALUE;
227 }
228 const char* const cmd = args[0];
229 if (strcmp(kEnableTestCameraCmd, cmd) == 0) {
230 enableTestCameraCmd(in, err);
231 } else if (strcmp(kDisableTestCameraCmd, cmd) == 0) {
232 disableTestCameraCmd(in);
233 } else {
234 dprintf(out, kShellCmdHelp);
235 }
236
237 fsync(out);
238 return STATUS_OK;
239}
240
241void VirtualCameraService::enableTestCameraCmd(const int out, const int err) {
242 if (mTestCameraToken != nullptr) {
243 dprintf(out, "Test camera is already enabled (%s).",
244 getCamera(mTestCameraToken)->getCameraName().c_str());
245 return;
246 }
247
248 sp<BBinder> token = sp<BBinder>::make();
249 mTestCameraToken.set(AIBinder_fromPlatformBinder(token));
250
251 bool ret;
Jan Sebechlebsky3b478c42023-11-23 13:15:56 +0100252 VirtualCameraConfiguration configuration;
Biswarup Pal6152a302023-12-19 12:44:09 +0000253 configuration.supportedStreamConfigs.push_back({.width = kVgaWidth,
254 .height = kVgaHeight,
255 Format::YUV_420_888,
256 .maxFps = kMaxFps});
Jan Sebechlebsky3b478c42023-11-23 13:15:56 +0100257 registerCamera(mTestCameraToken, configuration, &ret);
Jan Sebechlebsky5cb39962023-11-22 17:33:07 +0100258 if (ret) {
259 dprintf(out, "Successfully registered test camera %s",
260 getCamera(mTestCameraToken)->getCameraName().c_str());
261 } else {
262 dprintf(err, "Failed to create test camera");
263 }
264}
265
266void VirtualCameraService::disableTestCameraCmd(const int out) {
267 if (mTestCameraToken == nullptr) {
268 dprintf(out, "Test camera is not registered.");
269 }
270 unregisterCamera(mTestCameraToken);
271 mTestCameraToken.set(nullptr);
272}
273
274} // namespace virtualcamera
275} // namespace companion
276} // namespace android