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