blob: 7907cdb61ffdfe549a1814a9f3b0d248321ce45a [file] [log] [blame]
Jan Sebechlebsky5cb39962023-11-22 17:33:07 +01001/*
2 * Copyright (C) 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
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;
44using ::aidl::android::companion::virtualcamera::SupportedStreamConfiguration;
Jan Sebechlebsky5cb39962023-11-22 17:33:07 +010045using ::aidl::android::companion::virtualcamera::VirtualCameraConfiguration;
46
47namespace {
48
Jan Sebechlebsky3b478c42023-11-23 13:15:56 +010049constexpr int kVgaWidth = 640;
50constexpr int kVgaHeight = 480;
Jan Sebechlebsky5cb39962023-11-22 17:33:07 +010051constexpr char kEnableTestCameraCmd[] = "enable_test_camera";
52constexpr char kDisableTestCameraCmd[] = "disable_test_camera";
53constexpr char kShellCmdHelp[] = R"(
54Available commands:
55 * enable_test_camera
56 * disable_test_camera
57)";
Jan Sebechlebskyde6f16f2023-11-29 09:27:36 +010058constexpr char kCreateVirtualDevicePermission[] =
59 "android.permission.CREATE_VIRTUAL_DEVICE";
Jan Sebechlebsky5cb39962023-11-22 17:33:07 +010060
Jan Sebechlebsky3b478c42023-11-23 13:15:56 +010061ndk::ScopedAStatus validateConfiguration(
62 const VirtualCameraConfiguration& configuration) {
63 if (configuration.supportedStreamConfigs.empty()) {
64 ALOGE("%s: No supported input configuration specified", __func__);
65 return ndk::ScopedAStatus::fromServiceSpecificError(
66 Status::EX_ILLEGAL_ARGUMENT);
67 }
68
69 for (const SupportedStreamConfiguration& config :
70 configuration.supportedStreamConfigs) {
71 if (!isFormatSupportedForInput(config.width, config.height,
72 config.pixelFormat)) {
73 ALOGE("%s: Requested unsupported input format: %d x %d (%d)", __func__,
74 config.width, config.height, static_cast<int>(config.pixelFormat));
75 return ndk::ScopedAStatus::fromServiceSpecificError(
76 Status::EX_ILLEGAL_ARGUMENT);
77 }
78 }
79 return ndk::ScopedAStatus::ok();
80}
81
Jan Sebechlebsky5cb39962023-11-22 17:33:07 +010082} // namespace
83
84VirtualCameraService::VirtualCameraService(
Jan Sebechlebskyde6f16f2023-11-29 09:27:36 +010085 std::shared_ptr<VirtualCameraProvider> virtualCameraProvider,
86 const PermissionsProxy& permissionProxy)
87 : mVirtualCameraProvider(virtualCameraProvider),
88 mPermissionProxy(permissionProxy) {
Jan Sebechlebsky5cb39962023-11-22 17:33:07 +010089}
90
91ndk::ScopedAStatus VirtualCameraService::registerCamera(
92 const ::ndk::SpAIBinder& token,
93 const VirtualCameraConfiguration& configuration, bool* _aidl_return) {
Jan Sebechlebskyde6f16f2023-11-29 09:27:36 +010094 if (!mPermissionProxy.checkCallingPermission(kCreateVirtualDevicePermission)) {
95 ALOGE("%s: caller (pid %d, uid %d) doesn't hold %s permission", __func__,
96 getpid(), getuid(), kCreateVirtualDevicePermission);
97 return ndk::ScopedAStatus::fromExceptionCode(EX_SECURITY);
98 }
99
Jan Sebechlebsky5cb39962023-11-22 17:33:07 +0100100 if (_aidl_return == nullptr) {
101 return ndk::ScopedAStatus::fromServiceSpecificError(
102 Status::EX_ILLEGAL_ARGUMENT);
103 }
Jan Sebechlebskyde6f16f2023-11-29 09:27:36 +0100104
Jan Sebechlebsky5cb39962023-11-22 17:33:07 +0100105 *_aidl_return = true;
106
Jan Sebechlebsky3b478c42023-11-23 13:15:56 +0100107 auto status = validateConfiguration(configuration);
108 if (!status.isOk()) {
109 *_aidl_return = false;
110 return status;
111 }
112
Jan Sebechlebsky5cb39962023-11-22 17:33:07 +0100113 std::lock_guard lock(mLock);
114 if (mTokenToCameraName.find(token) != mTokenToCameraName.end()) {
115 ALOGE(
116 "Attempt to register camera corresponding to already registered binder "
117 "token: "
118 "0x%" PRIxPTR,
119 reinterpret_cast<uintptr_t>(token.get()));
120 *_aidl_return = false;
Jan Sebechlebsky3b478c42023-11-23 13:15:56 +0100121 return ndk::ScopedAStatus::ok();
Jan Sebechlebsky5cb39962023-11-22 17:33:07 +0100122 }
123
124 // TODO(b/301023410) Validate configuration and pass it to the camera.
125 std::shared_ptr<VirtualCameraDevice> camera =
Jan Sebechlebsky3b478c42023-11-23 13:15:56 +0100126 mVirtualCameraProvider->createCamera(configuration.supportedStreamConfigs,
127 configuration.virtualCameraCallback);
Jan Sebechlebsky5cb39962023-11-22 17:33:07 +0100128 if (camera == nullptr) {
129 ALOGE("Failed to create camera for binder token 0x%" PRIxPTR,
130 reinterpret_cast<uintptr_t>(token.get()));
131 *_aidl_return = false;
132 return ndk::ScopedAStatus::fromServiceSpecificError(
133 Status::EX_SERVICE_SPECIFIC);
134 }
135
136 mTokenToCameraName[token] = camera->getCameraName();
137 return ndk::ScopedAStatus::ok();
138}
139
140ndk::ScopedAStatus VirtualCameraService::unregisterCamera(
141 const ::ndk::SpAIBinder& token) {
Jan Sebechlebskyde6f16f2023-11-29 09:27:36 +0100142 if (!mPermissionProxy.checkCallingPermission(kCreateVirtualDevicePermission)) {
143 ALOGE("%s: caller (pid %d, uid %d) doesn't hold %s permission", __func__,
144 getpid(), getuid(), kCreateVirtualDevicePermission);
145 return ndk::ScopedAStatus::fromExceptionCode(EX_SECURITY);
146 }
147
Jan Sebechlebsky5cb39962023-11-22 17:33:07 +0100148 std::lock_guard lock(mLock);
149
150 auto it = mTokenToCameraName.find(token);
151 if (it == mTokenToCameraName.end()) {
152 ALOGE(
153 "Attempt to unregister camera corresponding to unknown binder token: "
154 "0x%" PRIxPTR,
155 reinterpret_cast<uintptr_t>(token.get()));
156 return ndk::ScopedAStatus::ok();
157 }
158
159 mVirtualCameraProvider->removeCamera(it->second);
160
Tony Guo6cbe11b2024-03-17 02:34:23 +0000161 mTokenToCameraName.erase(it);
Jan Sebechlebsky5cb39962023-11-22 17:33:07 +0100162 return ndk::ScopedAStatus::ok();
163}
164
Biswarup Pal68137fc2023-11-24 18:06:54 +0000165ndk::ScopedAStatus VirtualCameraService::getCameraId(
166 const ::ndk::SpAIBinder& token, int32_t* _aidl_return) {
Jan Sebechlebskyde6f16f2023-11-29 09:27:36 +0100167 if (!mPermissionProxy.checkCallingPermission(kCreateVirtualDevicePermission)) {
168 ALOGE("%s: caller (pid %d, uid %d) doesn't hold %s permission", __func__,
169 getpid(), getuid(), kCreateVirtualDevicePermission);
170 return ndk::ScopedAStatus::fromExceptionCode(EX_SECURITY);
171 }
172
Biswarup Pal68137fc2023-11-24 18:06:54 +0000173 if (_aidl_return == nullptr) {
174 return ndk::ScopedAStatus::fromServiceSpecificError(
175 Status::EX_ILLEGAL_ARGUMENT);
176 }
177
178 auto camera = getCamera(token);
179 if (camera == nullptr) {
180 ALOGE(
181 "Attempt to get camera id corresponding to unknown binder token: "
182 "0x%" PRIxPTR,
183 reinterpret_cast<uintptr_t>(token.get()));
184 return ndk::ScopedAStatus::ok();
185 }
186
187 *_aidl_return = camera->getCameraId();
188
189 return ndk::ScopedAStatus::ok();
190}
191
Jan Sebechlebsky5cb39962023-11-22 17:33:07 +0100192std::shared_ptr<VirtualCameraDevice> VirtualCameraService::getCamera(
193 const ::ndk::SpAIBinder& token) {
194 if (token == nullptr) {
195 return nullptr;
196 }
197
198 std::lock_guard lock(mLock);
199 auto it = mTokenToCameraName.find(token);
200 if (it == mTokenToCameraName.end()) {
201 return nullptr;
202 }
203
204 return mVirtualCameraProvider->getCamera(it->second);
205}
206
207binder_status_t VirtualCameraService::handleShellCommand(int in, int out,
208 int err,
209 const char** args,
210 uint32_t numArgs) {
211 if (numArgs <= 0) {
212 dprintf(out, kShellCmdHelp);
Jan Sebechlebsky76d7e212023-11-28 14:10:25 +0100213 fsync(out);
214 return STATUS_OK;
Jan Sebechlebsky5cb39962023-11-22 17:33:07 +0100215 }
216
217 if (args == nullptr || args[0] == nullptr) {
218 return STATUS_BAD_VALUE;
219 }
220 const char* const cmd = args[0];
221 if (strcmp(kEnableTestCameraCmd, cmd) == 0) {
222 enableTestCameraCmd(in, err);
223 } else if (strcmp(kDisableTestCameraCmd, cmd) == 0) {
224 disableTestCameraCmd(in);
225 } else {
226 dprintf(out, kShellCmdHelp);
227 }
228
229 fsync(out);
230 return STATUS_OK;
231}
232
233void VirtualCameraService::enableTestCameraCmd(const int out, const int err) {
234 if (mTestCameraToken != nullptr) {
235 dprintf(out, "Test camera is already enabled (%s).",
236 getCamera(mTestCameraToken)->getCameraName().c_str());
237 return;
238 }
239
240 sp<BBinder> token = sp<BBinder>::make();
241 mTestCameraToken.set(AIBinder_fromPlatformBinder(token));
242
243 bool ret;
Jan Sebechlebsky3b478c42023-11-23 13:15:56 +0100244 VirtualCameraConfiguration configuration;
245 configuration.supportedStreamConfigs.push_back(
246 {.width = kVgaWidth, .height = kVgaHeight, Format::YUV_420_888});
247 registerCamera(mTestCameraToken, configuration, &ret);
Jan Sebechlebsky5cb39962023-11-22 17:33:07 +0100248 if (ret) {
249 dprintf(out, "Successfully registered test camera %s",
250 getCamera(mTestCameraToken)->getCameraName().c_str());
251 } else {
252 dprintf(err, "Failed to create test camera");
253 }
254}
255
256void VirtualCameraService::disableTestCameraCmd(const int out) {
257 if (mTestCameraToken == nullptr) {
258 dprintf(out, "Test camera is not registered.");
259 }
260 unregisterCamera(mTestCameraToken);
261 mTestCameraToken.set(nullptr);
262}
263
264} // namespace virtualcamera
265} // namespace companion
266} // namespace android