summaryrefslogtreecommitdiff
path: root/v3/EmulatedCameraHotplugThread.cpp (plain)
blob: ecdcb0d98bbec3b77aaae8a2f3810f1a0b777876
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
35namespace android {
36
37EmulatedCameraHotplugThread::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#if 0
48 if (createFileIfNotExists(id)) {
49 mSubscribedCameraIds.push_back(id);
50 }
51#endif
52 }
53}
54
55EmulatedCameraHotplugThread::~EmulatedCameraHotplugThread() {
56}
57
58status_t EmulatedCameraHotplugThread::requestExitAndWait() {
59 ALOGE("%s: Not implemented. Use requestExit + join instead",
60 __FUNCTION__);
61 return INVALID_OPERATION;
62}
63
64void EmulatedCameraHotplugThread::requestExit() {
65 Mutex::Autolock al(mMutex);
66
67 ALOGV("%s: Requesting thread exit", __FUNCTION__);
68 mRunning = false;
69
70 bool rmWatchFailed = false;
71 Vector<SubscriberInfo>::iterator it;
72 for (it = mSubscribers.begin(); it != mSubscribers.end(); ++it) {
73
74#if 0
75 if (inotify_rm_watch(mInotifyFd, it->WatchID) == -1) {
76
77 ALOGE("%s: Could not remove watch for camID '%d',"
78 " error: '%s' (%d)",
79 __FUNCTION__, it->CameraID, strerror(errno),
80 errno);
81
82 rmWatchFailed = true ;
83 } else {
84 ALOGV("%s: Removed watch for camID '%d'",
85 __FUNCTION__, it->CameraID);
86 }
87#endif
88 }
89
90 if (rmWatchFailed) { // unlikely
91 // Give the thread a fighting chance to error out on the next
92 // read
93 if (TEMP_FAILURE_RETRY(close(mInotifyFd)) == -1) {
94 ALOGE("%s: close failure error: '%s' (%d)",
95 __FUNCTION__, strerror(errno), errno);
96 }
97 }
98 if (shutdown(mSocketFd, SHUT_RD) < 0) {
99 CAMHAL_LOGDB("shutdown socket failed errno=%s", strerror(errno));
100 }
101 if (close(mSocketFd) < 0) {
102 CAMHAL_LOGDB("close socket failed errno=%s", strerror(errno));
103 }
104
105 ALOGV("%s: Request exit complete.", __FUNCTION__);
106}
107
108status_t EmulatedCameraHotplugThread::readyToRun() {
109 Mutex::Autolock al(mMutex);
110
111 mInotifyFd = -1;
112
113 do {
114 ALOGV("%s: Initializing inotify", __FUNCTION__);
115
116#if 0
117 mInotifyFd = inotify_init();
118 if (mInotifyFd == -1) {
119 ALOGE("%s: inotify_init failure error: '%s' (%d)",
120 __FUNCTION__, strerror(errno), errno);
121 mRunning = false;
122 break;
123 }
124#endif
125 memset(&sa,0,sizeof(sa));
126 sa.nl_family = AF_NETLINK;
127 sa.nl_groups = NETLINK_KOBJECT_UEVENT;
128 sa.nl_pid = 0;//getpid(); both is ok
129
130 mSocketFd = socket(AF_NETLINK,SOCK_RAW,NETLINK_KOBJECT_UEVENT);
131 if (mSocketFd == -1) {
132 mRunning = false;
133 CAMHAL_LOGEB("socket creating failed:%s, disable the hotplug thread\n",strerror(errno));
134 }
135
136 if (bind(mSocketFd,(struct sockaddr *)&sa,sizeof(sa)) == -1) {
137 mRunning = false;
138 CAMHAL_LOGEB("bind error:%s, disable the hotplug thread\n",strerror(errno));
139 }
140
141 /**
142 * For each fake camera file, add a watch for when
143 * the file is closed (if it was written to)
144 */
145 Vector<int>::const_iterator it, end;
146 it = mSubscribedCameraIds.begin();
147 end = mSubscribedCameraIds.end();
148 for (; it != end; ++it) {
149 int cameraId = *it;
150 if (!addWatch(cameraId)) {
151 mRunning = false;
152 break;
153 }
154 }
155 } while(false);
156
157 if (!mRunning) {
158 status_t err = -errno;
159
160#if 0
161 if (mInotifyFd != -1) {
162 TEMP_FAILURE_RETRY(close(mInotifyFd));
163 }
164#endif
165
166 return err;
167 }
168
169 return OK;
170}
171
172bool EmulatedCameraHotplugThread::threadLoop() {
173
174 // If requestExit was already called, mRunning will be false
175 int len;
176 char buf[4096];
177 struct iovec iov;
178 struct msghdr msg;
179 char *video4linux_string;
180 char *action_string;
181 int i;
182 int cameraId;
183 int halStatus;
184
185 while (mRunning) {
186 memset(&msg,0,sizeof(msg));
187 iov.iov_base=(void *)buf;
188 iov.iov_len=sizeof(buf);
189 msg.msg_name=(void *)&sa;
190 msg.msg_namelen=sizeof(sa);
191 msg.msg_iov=&iov;
192 msg.msg_iovlen=1;
193
194 len = recvmsg(mSocketFd, &msg, 0);
195 if (len < 0) {
196 break;
197 } else if ((len<32) || (len > (int)sizeof(buf))) {
198 CAMHAL_LOGDA("invalid message");
199 break;
200 }
201 buf[len] = '\0';
202
203 CAMHAL_LOGDB("buf=%s\n", buf);
204 video4linux_string = strstr(buf, "video4linux");
205 CAMHAL_LOGVB("video4linux=%s\n", video4linux_string);
206 if (video4linux_string == NULL) {
207 CAMHAL_LOGDA("not video event\n");
208 break;
209 }
210
211 CAMHAL_LOGVB("video=%s\n", video4linux_string);
212 action_string = strchr(video4linux_string, '\0');
213 action_string ++;
214 CAMHAL_LOGDB("action string=%s\n", action_string);
215
216 if (strstr(action_string, "ACTION=add") != NULL) {
217 halStatus = CAMERA_DEVICE_STATUS_PRESENT;
218 } else if (strstr(action_string, "ACTION=remove") != NULL) {
219 halStatus = CAMERA_DEVICE_STATUS_NOT_PRESENT;
220 } else {
221 CAMHAL_LOGDA("no find add or remove\n");
222 break;
223 }
224
225 //string like that: add@/devices/lm1/usb1/1-1/1-1.3/1-1.3:1.0/video4linux/video0
226 video4linux_string += 17;
227 cameraId = strtol(video4linux_string, NULL, 10);
228
229 gEmulatedCameraFactory.onStatusChanged(cameraId,
230 halStatus);
231
232 }
233
234 if (!mRunning) {
235 //TEMP_FAILURE_RETRY(close(mInotifyFd));
236 return false;
237 }
238
239 return true;
240}
241
242String8 EmulatedCameraHotplugThread::getFilePath(int cameraId) const {
243 return String8::format(FAKE_HOTPLUG_FILE ".%d", cameraId);
244}
245
246bool EmulatedCameraHotplugThread::createFileIfNotExists(int cameraId) const
247{
248 String8 filePath = getFilePath(cameraId);
249 // make sure this file exists and we have access to it
250 int fd = TEMP_FAILURE_RETRY(
251 open(filePath.string(), O_WRONLY | O_CREAT | O_TRUNC,
252 /* mode = ug+rwx */ S_IRWXU | S_IRWXG ));
253 if (fd == -1) {
254 ALOGE("%s: Could not create file '%s', error: '%s' (%d)",
255 __FUNCTION__, filePath.string(), strerror(errno), errno);
256 return false;
257 }
258
259 // File has '1' by default since we are plugged in by default
260 if (TEMP_FAILURE_RETRY(write(fd, "1\n", /*count*/2)) == -1) {
261 ALOGE("%s: Could not write '1' to file '%s', error: '%s' (%d)",
262 __FUNCTION__, filePath.string(), strerror(errno), errno);
263 return false;
264 }
265
266 TEMP_FAILURE_RETRY(close(fd));
267 return true;
268}
269
270int EmulatedCameraHotplugThread::getCameraId(String8 filePath) const {
271 Vector<int>::const_iterator it, end;
272 it = mSubscribedCameraIds.begin();
273 end = mSubscribedCameraIds.end();
274 for (; it != end; ++it) {
275 String8 camPath = getFilePath(*it);
276
277 if (camPath == filePath) {
278 return *it;
279 }
280 }
281
282 return NAME_NOT_FOUND;
283}
284
285int EmulatedCameraHotplugThread::getCameraId(int wd) const {
286 for (size_t i = 0; i < mSubscribers.size(); ++i) {
287 if (mSubscribers[i].WatchID == wd) {
288 return mSubscribers[i].CameraID;
289 }
290 }
291
292 return NAME_NOT_FOUND;
293}
294
295SubscriberInfo* EmulatedCameraHotplugThread::getSubscriberInfo(int cameraId)
296{
297 for (size_t i = 0; i < mSubscribers.size(); ++i) {
298 if (mSubscribers[i].CameraID == cameraId) {
299 return (SubscriberInfo*)&mSubscribers[i];
300 }
301 }
302
303 return NULL;
304}
305
306bool EmulatedCameraHotplugThread::addWatch(int cameraId) {
307 String8 camPath = getFilePath(cameraId);
308 int wd = 0;
309#if 0
310 int wd = inotify_add_watch(mInotifyFd,
311 camPath.string(),
312 IN_CLOSE_WRITE);
313
314 if (wd == -1) {
315 ALOGE("%s: Could not add watch for '%s', error: '%s' (%d)",
316 __FUNCTION__, camPath.string(), strerror(errno),
317 errno);
318
319 mRunning = false;
320 return false;
321 }
322#endif
323
324 ALOGV("%s: Watch added for camID='%d', wd='%d'",
325 __FUNCTION__, cameraId, wd);
326
327 SubscriberInfo si = { cameraId, wd };
328 mSubscribers.push_back(si);
329
330 return true;
331}
332
333bool EmulatedCameraHotplugThread::removeWatch(int cameraId) {
334 SubscriberInfo* si = getSubscriberInfo(cameraId);
335
336 if (!si) return false;
337
338#if 0
339 if (inotify_rm_watch(mInotifyFd, si->WatchID) == -1) {
340
341 ALOGE("%s: Could not remove watch for camID '%d', error: '%s' (%d)",
342 __FUNCTION__, cameraId, strerror(errno),
343 errno);
344
345 return false;
346 }
347#endif
348
349 Vector<SubscriberInfo>::iterator it;
350 for (it = mSubscribers.begin(); it != mSubscribers.end(); ++it) {
351 if (it->CameraID == cameraId) {
352 break;
353 }
354 }
355
356 if (it != mSubscribers.end()) {
357 mSubscribers.erase(it);
358 }
359
360 return true;
361}
362
363int EmulatedCameraHotplugThread::readFile(String8 filePath) const {
364
365 int fd = TEMP_FAILURE_RETRY(
366 open(filePath.string(), O_RDONLY, /*mode*/0));
367 if (fd == -1) {
368 ALOGE("%s: Could not open file '%s', error: '%s' (%d)",
369 __FUNCTION__, filePath.string(), strerror(errno), errno);
370 return -1;
371 }
372
373 char buffer[1];
374 int length;
375
376 length = TEMP_FAILURE_RETRY(
377 read(fd, buffer, sizeof(buffer)));
378
379 int retval;
380
381 ALOGV("%s: Read file '%s', length='%d', buffer='%c'",
382 __FUNCTION__, filePath.string(), length, buffer[0]);
383
384 if (length == 0) { // EOF
385 retval = 0; // empty file is the same thing as 0
386 } else if (buffer[0] == '0') {
387 retval = 0;
388 } else { // anything non-empty that's not beginning with '0'
389 retval = 1;
390 }
391
392 TEMP_FAILURE_RETRY(close(fd));
393
394 return retval;
395}
396
397} //namespace android
398