1 /*
2 * Copyright (C) 2016-2019 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 #include "EvsEnumerator.h"
18 #include "EvsV4lCamera.h"
19 #include "EvsGlDisplay.h"
20
21 #include <dirent.h>
22 #include <hardware_legacy/uevent.h>
23 #include <hwbinder/IPCThreadState.h>
24 #include <cutils/android_filesystem_config.h>
25
26
27 using namespace std::chrono_literals;
28
29 namespace android {
30 namespace hardware {
31 namespace automotive {
32 namespace evs {
33 namespace V1_0 {
34 namespace implementation {
35
36
37 // NOTE: All members values are static so that all clients operate on the same state
38 // That is to say, this is effectively a singleton despite the fact that HIDL
39 // constructs a new instance for each client.
40 std::unordered_map<std::string, EvsEnumerator::CameraRecord> EvsEnumerator::sCameraList;
41 wp<EvsGlDisplay> EvsEnumerator::sActiveDisplay;
42 std::mutex EvsEnumerator::sLock;
43 std::condition_variable EvsEnumerator::sCameraSignal;
44
45 // Constants
46 const auto kEnumerationTimeout = 10s;
47
48
checkPermission()49 bool EvsEnumerator::checkPermission() {
50 hardware::IPCThreadState *ipc = hardware::IPCThreadState::self();
51 if (AID_AUTOMOTIVE_EVS != ipc->getCallingUid()) {
52 ALOGE("EVS access denied: pid = %d, uid = %d", ipc->getCallingPid(), ipc->getCallingUid());
53 return false;
54 }
55
56 return true;
57 }
58
EvsUeventThread(std::atomic<bool> & running)59 void EvsEnumerator::EvsUeventThread(std::atomic<bool>& running) {
60 int status = uevent_init();
61 if (!status) {
62 ALOGE("Failed to initialize uevent handler.");
63 return;
64 }
65
66 char uevent_data[PAGE_SIZE - 2] = {};
67 while (running) {
68 int length = uevent_next_event(uevent_data, static_cast<int32_t>(sizeof(uevent_data)));
69
70 // Ensure double-null termination.
71 uevent_data[length] = uevent_data[length + 1] = '\0';
72
73 const char *action = nullptr;
74 const char *devname = nullptr;
75 const char *subsys = nullptr;
76 char *cp = uevent_data;
77 while (*cp) {
78 // EVS is interested only in ACTION, SUBSYSTEM, and DEVNAME.
79 if (!std::strncmp(cp, "ACTION=", 7)) {
80 action = cp + 7;
81 } else if (!std::strncmp(cp, "SUBSYSTEM=", 10)) {
82 subsys = cp + 10;
83 } else if (!std::strncmp(cp, "DEVNAME=", 8)) {
84 devname = cp + 8;
85 }
86
87 // Advance to after next \0
88 while (*cp++);
89 }
90
91 if (!devname || !subsys || std::strcmp(subsys, "video4linux")) {
92 // EVS expects that the subsystem of enabled video devices is
93 // video4linux.
94 continue;
95 }
96
97 // Update shared list.
98 bool cmd_addition = !std::strcmp(action, "add");
99 bool cmd_removal = !std::strcmp(action, "remove");
100 {
101 std::string devpath = "/dev/";
102 devpath += devname;
103
104 std::lock_guard<std::mutex> lock(sLock);
105 if (cmd_removal) {
106 sCameraList.erase(devpath);
107 ALOGI("%s is removed", devpath.c_str());
108 } else if (cmd_addition) {
109 // NOTE: we are here adding new device without a validation
110 // because it always fails to open, b/132164956.
111 sCameraList.emplace(devpath, devpath.c_str());
112 ALOGI("%s is added", devpath.c_str());
113 } else {
114 // Ignore all other actions including "change".
115 }
116
117 // Notify the change.
118 sCameraSignal.notify_all();
119 }
120 }
121
122 return;
123 }
124
EvsEnumerator()125 EvsEnumerator::EvsEnumerator() {
126 ALOGD("EvsEnumerator created");
127
128 enumerateDevices();
129 }
130
enumerateDevices()131 void EvsEnumerator::enumerateDevices() {
132 // For every video* entry in the dev folder, see if it reports suitable capabilities
133 // WARNING: Depending on the driver implementations this could be slow, especially if
134 // there are timeouts or round trips to hardware required to collect the needed
135 // information. Platform implementers should consider hard coding this list of
136 // known good devices to speed up the startup time of their EVS implementation.
137 // For example, this code might be replaced with nothing more than:
138 // sCameraList.emplace("/dev/video0");
139 // sCameraList.emplace("/dev/video1");
140 ALOGI("%s: Starting dev/video* enumeration", __FUNCTION__);
141 unsigned videoCount = 0;
142 unsigned captureCount = 0;
143 DIR* dir = opendir("/dev");
144 if (!dir) {
145 LOG_FATAL("Failed to open /dev folder\n");
146 }
147 struct dirent* entry;
148 {
149 std::lock_guard<std::mutex> lock(sLock);
150
151 while ((entry = readdir(dir)) != nullptr) {
152 // We're only looking for entries starting with 'video'
153 if (strncmp(entry->d_name, "video", 5) == 0) {
154 std::string deviceName("/dev/");
155 deviceName += entry->d_name;
156 videoCount++;
157 if (sCameraList.find(deviceName) != sCameraList.end()) {
158 ALOGI("%s has been added already.", deviceName.c_str());
159 captureCount++;
160 } else if(qualifyCaptureDevice(deviceName.c_str())) {
161 sCameraList.emplace(deviceName, deviceName.c_str());
162 captureCount++;
163 }
164 }
165 }
166 }
167
168 ALOGI("Found %d qualified video capture devices of %d checked\n", captureCount, videoCount);
169 }
170
171 // Methods from ::android::hardware::automotive::evs::V1_0::IEvsEnumerator follow.
getCameraList(getCameraList_cb _hidl_cb)172 Return<void> EvsEnumerator::getCameraList(getCameraList_cb _hidl_cb) {
173 ALOGD("getCameraList");
174 if (!checkPermission()) {
175 return Void();
176 }
177
178 {
179 std::unique_lock<std::mutex> lock(sLock);
180 if (sCameraList.size() < 1) {
181 // No qualified device has been found. Wait until new device is ready,
182 // for 10 seconds.
183 if (!sCameraSignal.wait_for(lock,
184 kEnumerationTimeout,
185 []{ return sCameraList.size() > 0; })) {
186 ALOGD("Timer expired. No new device has been added.");
187 }
188 }
189 }
190
191 const unsigned numCameras = sCameraList.size();
192
193 // Build up a packed array of CameraDesc for return
194 hidl_vec<CameraDesc> hidlCameras;
195 hidlCameras.resize(numCameras);
196 unsigned i = 0;
197 for (const auto& [key, cam] : sCameraList) {
198 hidlCameras[i++] = cam.desc;
199 }
200
201 // Send back the results
202 ALOGD("reporting %zu cameras available", hidlCameras.size());
203 _hidl_cb(hidlCameras);
204
205 // HIDL convention says we return Void if we sent our result back via callback
206 return Void();
207 }
208
209
openCamera(const hidl_string & cameraId)210 Return<sp<IEvsCamera>> EvsEnumerator::openCamera(const hidl_string& cameraId) {
211 ALOGD("openCamera");
212 if (!checkPermission()) {
213 return nullptr;
214 }
215
216 // Is this a recognized camera id?
217 CameraRecord *pRecord = findCameraById(cameraId);
218
219 // Has this camera already been instantiated by another caller?
220 sp<EvsV4lCamera> pActiveCamera = pRecord->activeInstance.promote();
221 if (pActiveCamera != nullptr) {
222 ALOGW("Killing previous camera because of new caller");
223 closeCamera(pActiveCamera);
224 }
225
226 // Construct a camera instance for the caller
227 pActiveCamera = new EvsV4lCamera(cameraId.c_str());
228 pRecord->activeInstance = pActiveCamera;
229 if (pActiveCamera == nullptr) {
230 ALOGE("Failed to allocate new EvsV4lCamera object for %s\n", cameraId.c_str());
231 }
232
233 return pActiveCamera;
234 }
235
236
closeCamera(const::android::sp<IEvsCamera> & pCamera)237 Return<void> EvsEnumerator::closeCamera(const ::android::sp<IEvsCamera>& pCamera) {
238 ALOGD("closeCamera");
239
240 if (pCamera == nullptr) {
241 ALOGE("Ignoring call to closeCamera with null camera ptr");
242 return Void();
243 }
244
245 // Get the camera id so we can find it in our list
246 std::string cameraId;
247 pCamera->getCameraInfo([&cameraId](CameraDesc desc) {
248 cameraId = desc.cameraId;
249 }
250 );
251
252 // Find the named camera
253 CameraRecord *pRecord = findCameraById(cameraId);
254
255 // Is the display being destroyed actually the one we think is active?
256 if (!pRecord) {
257 ALOGE("Asked to close a camera whose name isn't recognized");
258 } else {
259 sp<EvsV4lCamera> pActiveCamera = pRecord->activeInstance.promote();
260
261 if (pActiveCamera == nullptr) {
262 ALOGE("Somehow a camera is being destroyed when the enumerator didn't know one existed");
263 } else if (pActiveCamera != pCamera) {
264 // This can happen if the camera was aggressively reopened, orphaning this previous instance
265 ALOGW("Ignoring close of previously orphaned camera - why did a client steal?");
266 } else {
267 // Drop the active camera
268 pActiveCamera->shutdown();
269 pRecord->activeInstance = nullptr;
270 }
271 }
272
273 return Void();
274 }
275
276
openDisplay()277 Return<sp<IEvsDisplay>> EvsEnumerator::openDisplay() {
278 ALOGD("openDisplay");
279 if (!checkPermission()) {
280 return nullptr;
281 }
282
283 // If we already have a display active, then we need to shut it down so we can
284 // give exclusive access to the new caller.
285 sp<EvsGlDisplay> pActiveDisplay = sActiveDisplay.promote();
286 if (pActiveDisplay != nullptr) {
287 ALOGW("Killing previous display because of new caller");
288 closeDisplay(pActiveDisplay);
289 }
290
291 // Create a new display interface and return it
292 pActiveDisplay = new EvsGlDisplay();
293 sActiveDisplay = pActiveDisplay;
294
295 ALOGD("Returning new EvsGlDisplay object %p", pActiveDisplay.get());
296 return pActiveDisplay;
297 }
298
299
closeDisplay(const::android::sp<IEvsDisplay> & pDisplay)300 Return<void> EvsEnumerator::closeDisplay(const ::android::sp<IEvsDisplay>& pDisplay) {
301 ALOGD("closeDisplay");
302
303 // Do we still have a display object we think should be active?
304 sp<EvsGlDisplay> pActiveDisplay = sActiveDisplay.promote();
305 if (pActiveDisplay == nullptr) {
306 ALOGE("Somehow a display is being destroyed when the enumerator didn't know one existed");
307 } else if (sActiveDisplay != pDisplay) {
308 ALOGW("Ignoring close of previously orphaned display - why did a client steal?");
309 } else {
310 // Drop the active display
311 pActiveDisplay->forceShutdown();
312 sActiveDisplay = nullptr;
313 }
314
315 return Void();
316 }
317
318
getDisplayState()319 Return<DisplayState> EvsEnumerator::getDisplayState() {
320 ALOGD("getDisplayState");
321 if (!checkPermission()) {
322 return DisplayState::DEAD;
323 }
324
325 // Do we still have a display object we think should be active?
326 sp<IEvsDisplay> pActiveDisplay = sActiveDisplay.promote();
327 if (pActiveDisplay != nullptr) {
328 return pActiveDisplay->getDisplayState();
329 } else {
330 return DisplayState::NOT_OPEN;
331 }
332 }
333
334
qualifyCaptureDevice(const char * deviceName)335 bool EvsEnumerator::qualifyCaptureDevice(const char* deviceName) {
336 class FileHandleWrapper {
337 public:
338 FileHandleWrapper(int fd) { mFd = fd; }
339 ~FileHandleWrapper() { if (mFd > 0) close(mFd); }
340 operator int() const { return mFd; }
341 private:
342 int mFd = -1;
343 };
344
345
346 FileHandleWrapper fd = open(deviceName, O_RDWR, 0);
347 if (fd < 0) {
348 return false;
349 }
350
351 v4l2_capability caps;
352 int result = ioctl(fd, VIDIOC_QUERYCAP, &caps);
353 if (result < 0) {
354 return false;
355 }
356 if (((caps.capabilities & V4L2_CAP_VIDEO_CAPTURE) == 0) ||
357 ((caps.capabilities & V4L2_CAP_STREAMING) == 0)) {
358 return false;
359 }
360
361 // Enumerate the available capture formats (if any)
362 v4l2_fmtdesc formatDescription;
363 formatDescription.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
364 bool found = false;
365 for (int i=0; !found; i++) {
366 formatDescription.index = i;
367 if (ioctl(fd, VIDIOC_ENUM_FMT, &formatDescription) == 0) {
368 ALOGI("FORMAT 0x%X, type 0x%X, desc %s, flags 0x%X",
369 formatDescription.pixelformat, formatDescription.type,
370 formatDescription.description, formatDescription.flags);
371 switch (formatDescription.pixelformat)
372 {
373 case V4L2_PIX_FMT_YUYV: found = true; break;
374 case V4L2_PIX_FMT_NV21: found = true; break;
375 case V4L2_PIX_FMT_NV16: found = true; break;
376 case V4L2_PIX_FMT_YVU420: found = true; break;
377 case V4L2_PIX_FMT_RGB32: found = true; break;
378 #ifdef V4L2_PIX_FMT_ARGB32 // introduced with kernel v3.17
379 case V4L2_PIX_FMT_ARGB32: found = true; break;
380 case V4L2_PIX_FMT_XRGB32: found = true; break;
381 #endif // V4L2_PIX_FMT_ARGB32
382 default:
383 ALOGW("Unsupported, 0x%X", formatDescription.pixelformat);
384 break;
385 }
386 } else {
387 // No more formats available.
388 break;
389 }
390 }
391
392 return found;
393 }
394
395
findCameraById(const std::string & cameraId)396 EvsEnumerator::CameraRecord* EvsEnumerator::findCameraById(const std::string& cameraId) {
397 // Find the named camera
398 auto found = sCameraList.find(cameraId);
399 if (sCameraList.end() != found) {
400 // Found a match!
401 return &found->second;
402 }
403
404 // We didn't find a match
405 return nullptr;
406 }
407
408
409 } // namespace implementation
410 } // namespace V1_0
411 } // namespace evs
412 } // namespace automotive
413 } // namespace hardware
414 } // namespace android
415