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