blob: d47ddbfef733b75d12bbbcf80261442331bfa137 [file] [log] [blame]
Avichal Rakeshe1857f82022-06-08 17:47:23 -07001/*
2 * Copyright (C) 2022 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_TAG "ExtCamPrvdr"
18// #define LOG_NDEBUG 0
19
20#include "ExternalCameraProvider.h"
21
22#include <ExternalCameraDevice.h>
23#include <aidl/android/hardware/camera/common/Status.h>
24#include <convert.h>
25#include <cutils/properties.h>
26#include <linux/videodev2.h>
27#include <log/log.h>
28#include <sys/inotify.h>
29#include <regex>
30
31namespace android {
32namespace hardware {
33namespace camera {
34namespace provider {
35namespace implementation {
36
37using ::aidl::android::hardware::camera::common::Status;
38using ::android::hardware::camera::device::implementation::ExternalCameraDevice;
39using ::android::hardware::camera::device::implementation::fromStatus;
40using ::android::hardware::camera::external::common::ExternalCameraConfig;
41
42namespace {
43// "device@<version>/external/<id>"
44const std::regex kDeviceNameRE("device@([0-9]+\\.[0-9]+)/external/(.+)");
45const int kMaxDevicePathLen = 256;
46constexpr char kDevicePath[] = "/dev/";
47constexpr char kPrefix[] = "video";
48constexpr int kPrefixLen = sizeof(kPrefix) - 1;
49constexpr int kDevicePrefixLen = sizeof(kDevicePath) + kPrefixLen + 1;
50
51bool matchDeviceName(int cameraIdOffset, const std::string& deviceName, std::string* deviceVersion,
52 std::string* cameraDevicePath) {
53 std::smatch sm;
54 if (std::regex_match(deviceName, sm, kDeviceNameRE)) {
55 if (deviceVersion != nullptr) {
56 *deviceVersion = sm[1];
57 }
58 if (cameraDevicePath != nullptr) {
59 *cameraDevicePath = "/dev/video" + std::to_string(std::stoi(sm[2]) - cameraIdOffset);
60 }
61 return true;
62 }
63 return false;
64}
65} // namespace
66
67ExternalCameraProvider::ExternalCameraProvider() : mCfg(ExternalCameraConfig::loadFromCfg()) {
68 mHotPlugThread = std::make_shared<HotplugThread>(this);
69 mHotPlugThread->run();
70}
71
72ExternalCameraProvider::~ExternalCameraProvider() {
73 mHotPlugThread->requestExitAndWait();
74}
75
76ndk::ScopedAStatus ExternalCameraProvider::setCallback(
77 const std::shared_ptr<ICameraProviderCallback>& in_callback) {
78 {
79 Mutex::Autolock _l(mLock);
80 mCallback = in_callback;
81 }
82
83 if (mCallback == nullptr) {
84 return fromStatus(Status::OK);
85 }
86
87 for (const auto& pair : mCameraStatusMap) {
88 mCallback->cameraDeviceStatusChange(pair.first, pair.second);
89 }
90 return fromStatus(Status::OK);
91}
92
93ndk::ScopedAStatus ExternalCameraProvider::getVendorTags(
94 std::vector<VendorTagSection>* _aidl_return) {
95 if (_aidl_return == nullptr) {
96 return fromStatus(Status::ILLEGAL_ARGUMENT);
97 }
98 // No vendor tag support for USB camera
99 *_aidl_return = {};
100 return fromStatus(Status::OK);
101}
102
103ndk::ScopedAStatus ExternalCameraProvider::getCameraIdList(std::vector<std::string>* _aidl_return) {
104 if (_aidl_return == nullptr) {
105 return fromStatus(Status::ILLEGAL_ARGUMENT);
106 }
107 // External camera HAL always report 0 camera, and extra cameras
108 // are just reported via cameraDeviceStatusChange callbacks
109 *_aidl_return = {};
110 return fromStatus(Status::OK);
111}
112
113ndk::ScopedAStatus ExternalCameraProvider::getCameraDeviceInterface(
114 const std::string& in_cameraDeviceName, std::shared_ptr<ICameraDevice>* _aidl_return) {
115 if (_aidl_return == nullptr) {
116 return fromStatus(Status::ILLEGAL_ARGUMENT);
117 }
118 std::string cameraDevicePath, deviceVersion;
119 bool match = matchDeviceName(mCfg.cameraIdOffset, in_cameraDeviceName, &deviceVersion,
120 &cameraDevicePath);
121
122 if (!match) {
123 *_aidl_return = nullptr;
124 return fromStatus(Status::ILLEGAL_ARGUMENT);
125 }
126
127 if (mCameraStatusMap.count(in_cameraDeviceName) == 0 ||
128 mCameraStatusMap[in_cameraDeviceName] != CameraDeviceStatus::PRESENT) {
129 *_aidl_return = nullptr;
130 return fromStatus(Status::ILLEGAL_ARGUMENT);
131 }
132
133 ALOGV("Constructing external camera device");
134 std::shared_ptr<ExternalCameraDevice> deviceImpl =
135 ndk::SharedRefBase::make<ExternalCameraDevice>(cameraDevicePath, mCfg);
136 if (deviceImpl == nullptr || deviceImpl->isInitFailed()) {
137 ALOGE("%s: camera device %s init failed!", __FUNCTION__, cameraDevicePath.c_str());
138 *_aidl_return = nullptr;
139 return fromStatus(Status::INTERNAL_ERROR);
140 }
141
142 IF_ALOGV() {
143 int interfaceVersion;
144 deviceImpl->getInterfaceVersion(&interfaceVersion);
145 ALOGV("%s: device interface version: %d", __FUNCTION__, interfaceVersion);
146 }
147
148 *_aidl_return = deviceImpl;
149 return fromStatus(Status::OK);
150}
151
152ndk::ScopedAStatus ExternalCameraProvider::notifyDeviceStateChange(int64_t) {
153 return fromStatus(Status::OK);
154}
155
156ndk::ScopedAStatus ExternalCameraProvider::getConcurrentCameraIds(
157 std::vector<ConcurrentCameraIdCombination>* _aidl_return) {
158 if (_aidl_return == nullptr) {
159 return fromStatus(Status::ILLEGAL_ARGUMENT);
160 }
161 *_aidl_return = {};
162 return fromStatus(Status::OK);
163}
164
165ndk::ScopedAStatus ExternalCameraProvider::isConcurrentStreamCombinationSupported(
166 const std::vector<CameraIdAndStreamCombination>&, bool* _aidl_return) {
167 if (_aidl_return == nullptr) {
168 return fromStatus(Status::ILLEGAL_ARGUMENT);
169 }
170 // No concurrent stream combinations are supported
171 *_aidl_return = false;
172 return fromStatus(Status::OK);
173}
174
175void ExternalCameraProvider::addExternalCamera(const char* devName) {
176 ALOGV("%s: ExtCam: adding %s to External Camera HAL!", __FUNCTION__, devName);
177 Mutex::Autolock _l(mLock);
178 std::string deviceName;
179 std::string cameraId =
180 std::to_string(mCfg.cameraIdOffset + std::atoi(devName + kDevicePrefixLen));
181 deviceName =
182 std::string("device@") + ExternalCameraDevice::kDeviceVersion + "/external/" + cameraId;
183 mCameraStatusMap[deviceName] = CameraDeviceStatus::PRESENT;
184 if (mCallback != nullptr) {
185 mCallback->cameraDeviceStatusChange(deviceName, CameraDeviceStatus::PRESENT);
186 }
187}
188
189void ExternalCameraProvider::deviceAdded(const char* devName) {
190 {
191 base::unique_fd fd(::open(devName, O_RDWR));
192 if (fd.get() < 0) {
193 ALOGE("%s open v4l2 device %s failed:%s", __FUNCTION__, devName, strerror(errno));
194 return;
195 }
196
197 struct v4l2_capability capability;
198 int ret = ioctl(fd.get(), VIDIOC_QUERYCAP, &capability);
199 if (ret < 0) {
200 ALOGE("%s v4l2 QUERYCAP %s failed", __FUNCTION__, devName);
201 return;
202 }
203
204 if (!(capability.device_caps & V4L2_CAP_VIDEO_CAPTURE)) {
205 ALOGW("%s device %s does not support VIDEO_CAPTURE", __FUNCTION__, devName);
206 return;
207 }
208 }
209
210 // See if we can initialize ExternalCameraDevice correctly
211 std::shared_ptr<ExternalCameraDevice> deviceImpl =
212 ndk::SharedRefBase::make<ExternalCameraDevice>(devName, mCfg);
213 if (deviceImpl == nullptr || deviceImpl->isInitFailed()) {
214 ALOGW("%s: Attempt to init camera device %s failed!", __FUNCTION__, devName);
215 return;
216 }
217 deviceImpl.reset();
218 addExternalCamera(devName);
219}
220
221void ExternalCameraProvider::deviceRemoved(const char* devName) {
222 Mutex::Autolock _l(mLock);
223 std::string deviceName;
224 std::string cameraId =
225 std::to_string(mCfg.cameraIdOffset + std::atoi(devName + kDevicePrefixLen));
226
227 deviceName =
228 std::string("device@") + ExternalCameraDevice::kDeviceVersion + "/external/" + cameraId;
229
230 if (mCameraStatusMap.erase(deviceName) == 0) {
231 // Unknown device, do not fire callback
232 ALOGE("%s: cannot find camera device to remove %s", __FUNCTION__, devName);
233 return;
234 }
235
236 if (mCallback != nullptr) {
237 mCallback->cameraDeviceStatusChange(deviceName, CameraDeviceStatus::NOT_PRESENT);
238 }
239}
240
241void ExternalCameraProvider::updateAttachedCameras() {
242 ALOGV("%s start scanning for existing V4L2 devices", __FUNCTION__);
243
244 // Find existing /dev/video* devices
245 DIR* devdir = opendir(kDevicePath);
246 if (devdir == nullptr) {
247 ALOGE("%s: cannot open %s! Exiting threadloop", __FUNCTION__, kDevicePath);
248 return;
249 }
250
251 struct dirent* de;
252 while ((de = readdir(devdir)) != nullptr) {
253 // Find external v4l devices that's existing before we start watching and add them
254 if (!strncmp(kPrefix, de->d_name, kPrefixLen)) {
255 std::string deviceId(de->d_name + kPrefixLen);
256 if (mCfg.mInternalDevices.count(deviceId) == 0) {
257 ALOGV("Non-internal v4l device %s found", de->d_name);
258 char v4l2DevicePath[kMaxDevicePathLen];
259 snprintf(v4l2DevicePath, kMaxDevicePathLen, "%s%s", kDevicePath, de->d_name);
260 deviceAdded(v4l2DevicePath);
261 }
262 }
263 }
264 closedir(devdir);
265}
266
267// Start ExternalCameraProvider::HotplugThread functions
268
269ExternalCameraProvider::HotplugThread::HotplugThread(ExternalCameraProvider* parent)
270 : mParent(parent), mInternalDevices(parent->mCfg.mInternalDevices) {}
271
272ExternalCameraProvider::HotplugThread::~HotplugThread() {
273 // Clean up inotify descriptor if needed.
274 if (mINotifyFD >= 0) {
275 close(mINotifyFD);
276 }
277}
278
279bool ExternalCameraProvider::HotplugThread::initialize() {
280 // Update existing cameras
281 mParent->updateAttachedCameras();
282
283 // Set up non-blocking fd. The threadLoop will be responsible for polling read at the
284 // desired frequency
285 mINotifyFD = inotify_init();
286 if (mINotifyFD < 0) {
287 ALOGE("%s: inotify init failed! Exiting threadloop", __FUNCTION__);
288 return false;
289 }
290
291 // Start watching /dev/ directory for created and deleted files
292 mWd = inotify_add_watch(mINotifyFD, kDevicePath, IN_CREATE | IN_DELETE);
293 if (mWd < 0) {
294 ALOGE("%s: inotify add watch failed! Exiting threadloop", __FUNCTION__);
295 return false;
296 }
297
298 mPollFd = {.fd = mINotifyFD, .events = POLLIN};
299
300 mIsInitialized = true;
301 return true;
302}
303
304bool ExternalCameraProvider::HotplugThread::threadLoop() {
305 // Initialize inotify descriptors if needed.
306 if (!mIsInitialized && !initialize()) {
307 return true;
308 }
309
310 // poll /dev/* and handle timeouts and error
311 int pollRet = poll(&mPollFd, /* fd_count= */ 1, /* timeout= */ 250);
312 if (pollRet == 0) {
313 // no read event in 100ms
314 mPollFd.revents = 0;
315 return true;
316 } else if (pollRet < 0) {
317 ALOGE("%s: error while polling for /dev/*: %d", __FUNCTION__, errno);
318 mPollFd.revents = 0;
319 return true;
320 } else if (mPollFd.revents & POLLERR) {
321 ALOGE("%s: polling /dev/ returned POLLERR", __FUNCTION__);
322 mPollFd.revents = 0;
323 return true;
324 } else if (mPollFd.revents & POLLHUP) {
325 ALOGE("%s: polling /dev/ returned POLLHUP", __FUNCTION__);
326 mPollFd.revents = 0;
327 return true;
328 } else if (mPollFd.revents & POLLNVAL) {
329 ALOGE("%s: polling /dev/ returned POLLNVAL", __FUNCTION__);
330 mPollFd.revents = 0;
331 return true;
332 }
333 // mPollFd.revents must contain POLLIN, so safe to reset it before reading
334 mPollFd.revents = 0;
335
336 uint64_t offset = 0;
337 ssize_t ret = read(mINotifyFD, mEventBuf, sizeof(mEventBuf));
338 if (ret < sizeof(struct inotify_event)) {
339 // invalid event. skip
340 return true;
341 }
342
343 while (offset < ret) {
344 struct inotify_event* event = (struct inotify_event*)&mEventBuf[offset];
345 offset += sizeof(struct inotify_event) + event->len;
346
347 if (event->wd != mWd) {
348 // event for an unrelated descriptor. ignore.
349 continue;
350 }
351
352 ALOGV("%s inotify_event %s", __FUNCTION__, event->name);
353 if (strncmp(kPrefix, event->name, kPrefixLen) != 0) {
354 // event not for /dev/video*. ignore.
355 continue;
356 }
357
358 std::string deviceId = event->name + kPrefixLen;
359 if (mInternalDevices.count(deviceId) != 0) {
360 // update to an internal device. ignore.
361 continue;
362 }
363
364 char v4l2DevicePath[kMaxDevicePathLen];
365 snprintf(v4l2DevicePath, kMaxDevicePathLen, "%s%s", kDevicePath, event->name);
366
367 if (event->mask & IN_CREATE) {
368 mParent->deviceAdded(v4l2DevicePath);
369 } else if (event->mask & IN_DELETE) {
370 mParent->deviceRemoved(v4l2DevicePath);
371 }
372 }
373 return true;
374}
375
376// End ExternalCameraProvider::HotplugThread functions
377
378} // namespace implementation
379} // namespace provider
380} // namespace camera
381} // namespace hardware
382} // namespace android