1 /*
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
31 namespace android {
32 namespace hardware {
33 namespace camera {
34 namespace provider {
35 namespace implementation {
36
37 using ::aidl::android::hardware::camera::common::Status;
38 using ::android::hardware::camera::device::implementation::ExternalCameraDevice;
39 using ::android::hardware::camera::device::implementation::fromStatus;
40 using ::android::hardware::camera::external::common::ExternalCameraConfig;
41
42 namespace {
43 // "device@<version>/external/<id>"
44 const std::regex kDeviceNameRE("device@([0-9]+\\.[0-9]+)/external/(.+)");
45 const int kMaxDevicePathLen = 256;
46 constexpr char kDevicePath[] = "/dev/";
47 constexpr char kPrefix[] = "video";
48 constexpr int kPrefixLen = sizeof(kPrefix) - 1;
49 constexpr int kDevicePrefixLen = sizeof(kDevicePath) + kPrefixLen - 1;
50
matchDeviceName(int cameraIdOffset,const std::string & deviceName,std::string * deviceVersion,std::string * cameraDevicePath)51 bool 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
ExternalCameraProvider()67 ExternalCameraProvider::ExternalCameraProvider() : mCfg(ExternalCameraConfig::loadFromCfg()) {
68 mHotPlugThread = std::make_shared<HotplugThread>(this);
69 mHotPlugThread->run();
70 }
71
~ExternalCameraProvider()72 ExternalCameraProvider::~ExternalCameraProvider() {
73 mHotPlugThread->requestExitAndWait();
74 }
75
setCallback(const std::shared_ptr<ICameraProviderCallback> & in_callback)76 ndk::ScopedAStatus ExternalCameraProvider::setCallback(
77 const std::shared_ptr<ICameraProviderCallback>& in_callback) {
78 if (in_callback == nullptr) {
79 return fromStatus(Status::ILLEGAL_ARGUMENT);
80 }
81
82 {
83 Mutex::Autolock _l(mLock);
84 mCallback = in_callback;
85 }
86
87 for (const auto& pair : mCameraStatusMap) {
88 mCallback->cameraDeviceStatusChange(pair.first, pair.second);
89 }
90 return fromStatus(Status::OK);
91 }
92
getVendorTags(std::vector<VendorTagSection> * _aidl_return)93 ndk::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
getCameraIdList(std::vector<std::string> * _aidl_return)103 ndk::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
getCameraDeviceInterface(const std::string & in_cameraDeviceName,std::shared_ptr<ICameraDevice> * _aidl_return)113 ndk::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
notifyDeviceStateChange(int64_t)152 ndk::ScopedAStatus ExternalCameraProvider::notifyDeviceStateChange(int64_t) {
153 return fromStatus(Status::OK);
154 }
155
getConcurrentCameraIds(std::vector<ConcurrentCameraIdCombination> * _aidl_return)156 ndk::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
isConcurrentStreamCombinationSupported(const std::vector<CameraIdAndStreamCombination> &,bool * _aidl_return)165 ndk::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
addExternalCamera(const char * devName)175 void 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
deviceAdded(const char * devName)189 void 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
deviceRemoved(const char * devName)221 void 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
updateAttachedCameras()241 void 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
HotplugThread(ExternalCameraProvider * parent)269 ExternalCameraProvider::HotplugThread::HotplugThread(ExternalCameraProvider* parent)
270 : mParent(parent), mInternalDevices(parent->mCfg.mInternalDevices) {}
271
~HotplugThread()272 ExternalCameraProvider::HotplugThread::~HotplugThread() {
273 // Clean up inotify descriptor if needed.
274 if (mINotifyFD >= 0) {
275 close(mINotifyFD);
276 }
277 }
278
initialize()279 bool 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
threadLoop()304 bool 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
383