bc629c1e3cc57bad38c0b0cdeaf8d61c9a4494a4
[iec.git] / src / type3_AndroidCloud / anbox-master / android / camera / EmulatedCameraHotplugThread.cpp
1 /*
2  * Copyright (C) 2013 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 //#define LOG_NDEBUG 0
17 #define LOG_TAG "EmulatedCamera_HotplugThread"
18 #include <cutils/log.h>
19
20 #include <sys/types.h>
21 #include <sys/stat.h>
22 #include <fcntl.h>
23 #include <sys/inotify.h>
24
25 #include "EmulatedCameraHotplugThread.h"
26 #include "EmulatedCameraFactory.h"
27
28 #define FAKE_HOTPLUG_FILE "/data/misc/media/emulator.camera.hotplug"
29
30 #define EVENT_SIZE (sizeof(struct inotify_event))
31 #define EVENT_BUF_LEN (1024*(EVENT_SIZE+16))
32
33 #define SubscriberInfo EmulatedCameraHotplugThread::SubscriberInfo
34
35 namespace android {
36
37 EmulatedCameraHotplugThread::EmulatedCameraHotplugThread(
38     const int* cameraIdArray,
39     size_t size) :
40         Thread(/*canCallJava*/false) {
41
42     mRunning = true;
43     mInotifyFd = 0;
44
45     for (size_t i = 0; i < size; ++i) {
46         int id = cameraIdArray[i];
47
48         if (createFileIfNotExists(id)) {
49             mSubscribedCameraIds.push_back(id);
50         }
51     }
52 }
53
54 EmulatedCameraHotplugThread::~EmulatedCameraHotplugThread() {
55 }
56
57 status_t EmulatedCameraHotplugThread::requestExitAndWait() {
58     ALOGE("%s: Not implemented. Use requestExit + join instead",
59           __FUNCTION__);
60     return INVALID_OPERATION;
61 }
62
63 void EmulatedCameraHotplugThread::requestExit() {
64     Mutex::Autolock al(mMutex);
65
66     ALOGV("%s: Requesting thread exit", __FUNCTION__);
67     mRunning = false;
68
69     bool rmWatchFailed = false;
70     Vector<SubscriberInfo>::iterator it;
71     for (it = mSubscribers.begin(); it != mSubscribers.end(); ++it) {
72
73         if (inotify_rm_watch(mInotifyFd, it->WatchID) == -1) {
74
75             ALOGE("%s: Could not remove watch for camID '%d',"
76                   " error: '%s' (%d)",
77                  __FUNCTION__, it->CameraID, strerror(errno),
78                  errno);
79
80             rmWatchFailed = true ;
81         } else {
82             ALOGV("%s: Removed watch for camID '%d'",
83                 __FUNCTION__, it->CameraID);
84         }
85     }
86
87     if (rmWatchFailed) { // unlikely
88         // Give the thread a fighting chance to error out on the next
89         // read
90         if (close(mInotifyFd) == -1) {
91             ALOGE("%s: close failure error: '%s' (%d)",
92                  __FUNCTION__, strerror(errno), errno);
93         }
94     }
95
96     ALOGV("%s: Request exit complete.", __FUNCTION__);
97 }
98
99 status_t EmulatedCameraHotplugThread::readyToRun() {
100     Mutex::Autolock al(mMutex);
101
102     mInotifyFd = -1;
103
104     do {
105         ALOGV("%s: Initializing inotify", __FUNCTION__);
106
107         mInotifyFd = inotify_init();
108         if (mInotifyFd == -1) {
109             ALOGE("%s: inotify_init failure error: '%s' (%d)",
110                  __FUNCTION__, strerror(errno), errno);
111             mRunning = false;
112             break;
113         }
114
115         /**
116          * For each fake camera file, add a watch for when
117          * the file is closed (if it was written to)
118          */
119         Vector<int>::const_iterator it, end;
120         it = mSubscribedCameraIds.begin();
121         end = mSubscribedCameraIds.end();
122         for (; it != end; ++it) {
123             int cameraId = *it;
124             if (!addWatch(cameraId)) {
125                 mRunning = false;
126                 break;
127             }
128         }
129     } while(false);
130
131     if (!mRunning) {
132         status_t err = -errno;
133
134         if (mInotifyFd != -1) {
135             close(mInotifyFd);
136         }
137
138         return err;
139     }
140
141     return OK;
142 }
143
144 bool EmulatedCameraHotplugThread::threadLoop() {
145
146     // If requestExit was already called, mRunning will be false
147     while (mRunning) {
148         char buffer[EVENT_BUF_LEN];
149         int length = TEMP_FAILURE_RETRY(
150                         read(mInotifyFd, buffer, EVENT_BUF_LEN));
151
152         if (length < 0) {
153             ALOGE("%s: Error reading from inotify FD, error: '%s' (%d)",
154                  __FUNCTION__, strerror(errno),
155                  errno);
156             mRunning = false;
157             break;
158         }
159
160         ALOGV("%s: Read %d bytes from inotify FD", __FUNCTION__, length);
161
162         int i = 0;
163         while (i < length) {
164             inotify_event* event = (inotify_event*) &buffer[i];
165
166             if (event->mask & IN_IGNORED) {
167                 Mutex::Autolock al(mMutex);
168                 if (!mRunning) {
169                     ALOGV("%s: Shutting down thread", __FUNCTION__);
170                     break;
171                 } else {
172                     ALOGE("%s: File was deleted, aborting",
173                           __FUNCTION__);
174                     mRunning = false;
175                     break;
176                 }
177             } else if (event->mask & IN_CLOSE_WRITE) {
178                 int cameraId = getCameraId(event->wd);
179
180                 if (cameraId < 0) {
181                     ALOGE("%s: Got bad camera ID from WD '%d",
182                           __FUNCTION__, event->wd);
183                 } else {
184                     // Check the file for the new hotplug event
185                     String8 filePath = getFilePath(cameraId);
186                     /**
187                      * NOTE: we carefully avoid getting an inotify
188                      * for the same exact file because it's opened for
189                      * read-only, but our inotify is for write-only
190                      */
191                     int newStatus = readFile(filePath);
192
193                     if (newStatus < 0) {
194                         mRunning = false;
195                         break;
196                     }
197
198                     int halStatus = newStatus ?
199                         CAMERA_DEVICE_STATUS_PRESENT :
200                         CAMERA_DEVICE_STATUS_NOT_PRESENT;
201                     gEmulatedCameraFactory.onStatusChanged(cameraId,
202                                                            halStatus);
203                 }
204
205             } else {
206                 ALOGW("%s: Unknown mask 0x%x",
207                       __FUNCTION__, event->mask);
208             }
209
210             i += EVENT_SIZE + event->len;
211         }
212     }
213
214     if (!mRunning) {
215         close(mInotifyFd);
216         return false;
217     }
218
219     return true;
220 }
221
222 String8 EmulatedCameraHotplugThread::getFilePath(int cameraId) const {
223     return String8::format(FAKE_HOTPLUG_FILE ".%d", cameraId);
224 }
225
226 bool EmulatedCameraHotplugThread::createFileIfNotExists(int cameraId) const
227 {
228     String8 filePath = getFilePath(cameraId);
229     // make sure this file exists and we have access to it
230     int fd = TEMP_FAILURE_RETRY(
231                 open(filePath.string(), O_WRONLY | O_CREAT | O_TRUNC,
232                      /* mode = ug+rwx */ S_IRWXU | S_IRWXG ));
233     if (fd == -1) {
234         ALOGE("%s: Could not create file '%s', error: '%s' (%d)",
235              __FUNCTION__, filePath.string(), strerror(errno), errno);
236         return false;
237     }
238
239     // File has '1' by default since we are plugged in by default
240     if (TEMP_FAILURE_RETRY(write(fd, "1\n", /*count*/2)) == -1) {
241         ALOGE("%s: Could not write '1' to file '%s', error: '%s' (%d)",
242              __FUNCTION__, filePath.string(), strerror(errno), errno);
243         return false;
244     }
245
246     close(fd);
247     return true;
248 }
249
250 int EmulatedCameraHotplugThread::getCameraId(String8 filePath) const {
251     Vector<int>::const_iterator it, end;
252     it = mSubscribedCameraIds.begin();
253     end = mSubscribedCameraIds.end();
254     for (; it != end; ++it) {
255         String8 camPath = getFilePath(*it);
256
257         if (camPath == filePath) {
258             return *it;
259         }
260     }
261
262     return NAME_NOT_FOUND;
263 }
264
265 int EmulatedCameraHotplugThread::getCameraId(int wd) const {
266     for (size_t i = 0; i < mSubscribers.size(); ++i) {
267         if (mSubscribers[i].WatchID == wd) {
268             return mSubscribers[i].CameraID;
269         }
270     }
271
272     return NAME_NOT_FOUND;
273 }
274
275 SubscriberInfo* EmulatedCameraHotplugThread::getSubscriberInfo(int cameraId)
276 {
277     for (size_t i = 0; i < mSubscribers.size(); ++i) {
278         if (mSubscribers[i].CameraID == cameraId) {
279             return (SubscriberInfo*)&mSubscribers[i];
280         }
281     }
282
283     return NULL;
284 }
285
286 bool EmulatedCameraHotplugThread::addWatch(int cameraId) {
287     String8 camPath = getFilePath(cameraId);
288     int wd = inotify_add_watch(mInotifyFd,
289                                camPath.string(),
290                                IN_CLOSE_WRITE);
291
292     if (wd == -1) {
293         ALOGE("%s: Could not add watch for '%s', error: '%s' (%d)",
294              __FUNCTION__, camPath.string(), strerror(errno),
295              errno);
296
297         mRunning = false;
298         return false;
299     }
300
301     ALOGV("%s: Watch added for camID='%d', wd='%d'",
302           __FUNCTION__, cameraId, wd);
303
304     SubscriberInfo si = { cameraId, wd };
305     mSubscribers.push_back(si);
306
307     return true;
308 }
309
310 bool EmulatedCameraHotplugThread::removeWatch(int cameraId) {
311     SubscriberInfo* si = getSubscriberInfo(cameraId);
312
313     if (!si) return false;
314
315     if (inotify_rm_watch(mInotifyFd, si->WatchID) == -1) {
316
317         ALOGE("%s: Could not remove watch for camID '%d', error: '%s' (%d)",
318              __FUNCTION__, cameraId, strerror(errno),
319              errno);
320
321         return false;
322     }
323
324     Vector<SubscriberInfo>::iterator it;
325     for (it = mSubscribers.begin(); it != mSubscribers.end(); ++it) {
326         if (it->CameraID == cameraId) {
327             break;
328         }
329     }
330
331     if (it != mSubscribers.end()) {
332         mSubscribers.erase(it);
333     }
334
335     return true;
336 }
337
338 int EmulatedCameraHotplugThread::readFile(String8 filePath) const {
339
340     int fd = TEMP_FAILURE_RETRY(
341                 open(filePath.string(), O_RDONLY, /*mode*/0));
342     if (fd == -1) {
343         ALOGE("%s: Could not open file '%s', error: '%s' (%d)",
344              __FUNCTION__, filePath.string(), strerror(errno), errno);
345         return -1;
346     }
347
348     char buffer[1];
349     int length;
350
351     length = TEMP_FAILURE_RETRY(
352                     read(fd, buffer, sizeof(buffer)));
353
354     int retval;
355
356     ALOGV("%s: Read file '%s', length='%d', buffer='%c'",
357          __FUNCTION__, filePath.string(), length, buffer[0]);
358
359     if (length == 0) { // EOF
360         retval = 0; // empty file is the same thing as 0
361     } else if (buffer[0] == '0') {
362         retval = 0;
363     } else { // anything non-empty that's not beginning with '0'
364         retval = 1;
365     }
366
367     close(fd);
368
369     return retval;
370 }
371
372 } //namespace android