Home | History | Annotate | Download | only in camera
      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 (TEMP_FAILURE_RETRY(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             TEMP_FAILURE_RETRY(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         TEMP_FAILURE_RETRY(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     TEMP_FAILURE_RETRY(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     TEMP_FAILURE_RETRY(close(fd));
    368 
    369     return retval;
    370 }
    371 
    372 } //namespace android
    373