Home | History | Annotate | Download | only in camera
      1 /*
      2  * Copyright (C) 2011 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 /*
     18  * Contains implementation of classes that encapsulate connection to camera
     19  * services in the emulator via qemu pipe.
     20  */
     21 
     22 #define LOG_NDEBUG 0
     23 #define LOG_TAG "EmulatedCamera_QemuClient"
     24 #include "QemuClient.h"
     25 #include <cutils/log.h>
     26 #include "EmulatedCamera.h"
     27 
     28 #define LOG_QUERIES 0
     29 #if LOG_QUERIES
     30 #define LOGQ(...) ALOGD(__VA_ARGS__)
     31 #else
     32 #define LOGQ(...) (void(0))
     33 
     34 #endif  // LOG_QUERIES
     35 namespace android {
     36 
     37 /****************************************************************************
     38  * Qemu query
     39  ***************************************************************************/
     40 
     41 QemuQuery::QemuQuery()
     42     : mQuery(mQueryPrealloc),
     43       mQueryDeliveryStatus(NO_ERROR),
     44       mReplyBuffer(NULL),
     45       mReplyData(NULL),
     46       mReplySize(0),
     47       mReplyDataSize(0),
     48       mReplyStatus(0) {
     49   *mQuery = '\0';
     50 }
     51 
     52 QemuQuery::QemuQuery(const char* query_string)
     53     : mQuery(mQueryPrealloc),
     54       mQueryDeliveryStatus(NO_ERROR),
     55       mReplyBuffer(NULL),
     56       mReplyData(NULL),
     57       mReplySize(0),
     58       mReplyDataSize(0),
     59       mReplyStatus(0) {
     60   mQueryDeliveryStatus = QemuQuery::createQuery(query_string, NULL);
     61 }
     62 
     63 QemuQuery::QemuQuery(const char* query_name, const char* query_param)
     64     : mQuery(mQueryPrealloc),
     65       mQueryDeliveryStatus(NO_ERROR),
     66       mReplyBuffer(NULL),
     67       mReplyData(NULL),
     68       mReplySize(0),
     69       mReplyDataSize(0),
     70       mReplyStatus(0) {
     71   mQueryDeliveryStatus = QemuQuery::createQuery(query_name, query_param);
     72 }
     73 
     74 QemuQuery::~QemuQuery() { QemuQuery::resetQuery(); }
     75 
     76 status_t QemuQuery::createQuery(const char* name, const char* param) {
     77   /* Reset from the previous use. */
     78   resetQuery();
     79 
     80   /* Query name cannot be NULL or an empty string. */
     81   if (name == NULL || *name == '\0') {
     82     ALOGE("%s: NULL or an empty string is passed as query name.", __FUNCTION__);
     83     mQueryDeliveryStatus = EINVAL;
     84     return EINVAL;
     85   }
     86 
     87   const size_t name_len = strlen(name);
     88   const size_t param_len = (param != NULL) ? strlen(param) : 0;
     89   const size_t required = strlen(name) + (param_len ? (param_len + 2) : 1);
     90 
     91   if (required > sizeof(mQueryPrealloc)) {
     92     /* Preallocated buffer was too small. Allocate a bigger query buffer. */
     93     mQuery = new char[required];
     94     if (mQuery == NULL) {
     95       ALOGE("%s: Unable to allocate %zu bytes for query buffer", __FUNCTION__,
     96             required);
     97       mQueryDeliveryStatus = ENOMEM;
     98       return ENOMEM;
     99     }
    100   }
    101 
    102   /* At this point mQuery buffer is big enough for the query. */
    103   if (param_len) {
    104     sprintf(mQuery, "%s %s", name, param);
    105   } else {
    106     memcpy(mQuery, name, name_len + 1);
    107   }
    108 
    109   return NO_ERROR;
    110 }
    111 
    112 status_t QemuQuery::completeQuery(status_t status) {
    113   /* Save query completion status. */
    114   mQueryDeliveryStatus = status;
    115   if (mQueryDeliveryStatus != NO_ERROR) {
    116     return mQueryDeliveryStatus;
    117   }
    118 
    119   /* Make sure reply buffer contains at least 'ok', or 'ko'.
    120    * Note that 'ok', or 'ko' prefixes are always 3 characters long: in case
    121    * there are more data in the reply, that data will be separated from
    122    * 'ok'/'ko' with a ':'. If there is no more data in the reply, the prefix
    123    * will be
    124    * zero-terminated, and the terminator will be inculded in the reply. */
    125   if (mReplyBuffer == NULL || mReplySize < 3) {
    126     ALOGE("%s: Invalid reply to the query", __FUNCTION__);
    127     mQueryDeliveryStatus = EINVAL;
    128     return EINVAL;
    129   }
    130 
    131   /* Lets see the reply status. */
    132   if (!memcmp(mReplyBuffer, "ok", 2)) {
    133     mReplyStatus = 1;
    134   } else if (!memcmp(mReplyBuffer, "ko", 2)) {
    135     mReplyStatus = 0;
    136   } else {
    137     ALOGE("%s: Invalid query reply: '%s'", __FUNCTION__, mReplyBuffer);
    138     mQueryDeliveryStatus = EINVAL;
    139     return EINVAL;
    140   }
    141 
    142   /* Lets see if there are reply data that follow. */
    143   if (mReplySize > 3) {
    144     /* There are extra data. Make sure they are separated from the status
    145      * with a ':' */
    146     if (mReplyBuffer[2] != ':') {
    147       ALOGE("%s: Invalid query reply: '%s'", __FUNCTION__, mReplyBuffer);
    148       mQueryDeliveryStatus = EINVAL;
    149       return EINVAL;
    150     }
    151     mReplyData = mReplyBuffer + 3;
    152     mReplyDataSize = mReplySize - 3;
    153   } else {
    154     /* Make sure reply buffer containing just 'ok'/'ko' ends with
    155      * zero-terminator. */
    156     if (mReplyBuffer[2] != '\0') {
    157       ALOGE("%s: Invalid query reply: '%s'", __FUNCTION__, mReplyBuffer);
    158       mQueryDeliveryStatus = EINVAL;
    159       return EINVAL;
    160     }
    161   }
    162 
    163   return NO_ERROR;
    164 }
    165 
    166 void QemuQuery::resetQuery() {
    167   if (mQuery != NULL && mQuery != mQueryPrealloc) {
    168     delete[] mQuery;
    169   }
    170   mQuery = mQueryPrealloc;
    171   mQueryDeliveryStatus = NO_ERROR;
    172   if (mReplyBuffer != NULL) {
    173     free(mReplyBuffer);
    174     mReplyBuffer = NULL;
    175   }
    176   mReplyData = NULL;
    177   mReplySize = mReplyDataSize = 0;
    178   mReplyStatus = 0;
    179 }
    180 
    181 /****************************************************************************
    182  * Qemu client base
    183  ***************************************************************************/
    184 
    185 /* Camera service name. */
    186 const char QemuClient::mCameraServiceName[] = "camera";
    187 
    188 QemuClient::QemuClient() : mPipeFD(-1) {}
    189 
    190 QemuClient::~QemuClient() {
    191   if (mPipeFD >= 0) {
    192     close(mPipeFD);
    193   }
    194 }
    195 
    196 /****************************************************************************
    197  * Qemu client API
    198  ***************************************************************************/
    199 
    200 status_t QemuClient::connectClient(const char* param) {
    201   ALOGV("%s: '%s'", __FUNCTION__, param ? param : "");
    202 
    203   /* Make sure that client is not connected already. */
    204   if (mPipeFD >= 0) {
    205     ALOGE("%s: Qemu client is already connected", __FUNCTION__);
    206     return EINVAL;
    207   }
    208 
    209   /* Select one of the two: 'factory', or 'emulated camera' service */
    210   if (param == NULL || *param == '\0') {
    211     /* No parameters: connect to the factory service. */
    212     char pipe_name[512];
    213     snprintf(pipe_name, sizeof(pipe_name), "qemud:%s", mCameraServiceName);
    214     mPipeFD = qemu_pipe_open(pipe_name);
    215   } else {
    216     /* One extra char ':' that separates service name and parameters + six
    217      * characters for 'qemud:'. This is required by qemu pipe protocol. */
    218     char* connection_str =
    219         new char[strlen(mCameraServiceName) + strlen(param) + 8];
    220     sprintf(connection_str, "qemud:%s:%s", mCameraServiceName, param);
    221 
    222     mPipeFD = qemu_pipe_open(connection_str);
    223     delete[] connection_str;
    224   }
    225   if (mPipeFD < 0) {
    226     ALOGE("%s: Unable to connect to the camera service '%s': %s", __FUNCTION__,
    227           param ? param : "Factory", strerror(errno));
    228     return errno ? errno : EINVAL;
    229   }
    230 
    231   return NO_ERROR;
    232 }
    233 
    234 void QemuClient::disconnectClient() {
    235   ALOGV("%s", __FUNCTION__);
    236 
    237   if (mPipeFD >= 0) {
    238     close(mPipeFD);
    239     mPipeFD = -1;
    240   }
    241 }
    242 
    243 status_t QemuClient::sendMessage(const void* data, size_t data_size) {
    244   if (mPipeFD < 0) {
    245     ALOGE("%s: Qemu client is not connected", __FUNCTION__);
    246     return EINVAL;
    247   }
    248 
    249   /* Note that we don't use here qemud_client_send, since with qemu pipes we
    250    * don't need to provide payload size prior to payload when we're writing to
    251    * the pipe. So, we can use simple write, and qemu pipe will take care of the
    252    * rest, calling the receiving end with the number of bytes transferred. */
    253   const size_t written = qemud_fd_write(mPipeFD, data, data_size);
    254   if (written == data_size) {
    255     return NO_ERROR;
    256   } else {
    257     ALOGE("%s: Error sending data via qemu pipe: '%s'", __FUNCTION__,
    258           strerror(errno));
    259     return errno ? errno : EIO;
    260   }
    261 }
    262 
    263 status_t QemuClient::receiveMessage(void** data, size_t* data_size) {
    264   *data = NULL;
    265   *data_size = 0;
    266 
    267   if (mPipeFD < 0) {
    268     ALOGE("%s: Qemu client is not connected", __FUNCTION__);
    269     return EINVAL;
    270   }
    271 
    272   /* The way the service replies to a query, it sends payload size first, and
    273    * then it sends the payload itself. Note that payload size is sent as a
    274    * string, containing 8 characters representing a hexadecimal payload size
    275    * value. Note also, that the string doesn't contain zero-terminator. */
    276   size_t payload_size;
    277   char payload_size_str[9];
    278   int rd_res = qemud_fd_read(mPipeFD, payload_size_str, 8);
    279   if (rd_res != 8) {
    280     ALOGE("%s: Unable to obtain payload size: %s", __FUNCTION__,
    281           strerror(errno));
    282     return errno ? errno : EIO;
    283   }
    284 
    285   /* Convert payload size. */
    286   errno = 0;
    287   payload_size_str[8] = '\0';
    288   payload_size = strtol(payload_size_str, NULL, 16);
    289   if (errno) {
    290     ALOGE("%s: Invalid payload size '%s'", __FUNCTION__, payload_size_str);
    291     return EIO;
    292   }
    293 
    294   /* Allocate payload data buffer, and read the payload there. */
    295   *data = malloc(payload_size);
    296   if (*data == NULL) {
    297     ALOGE("%s: Unable to allocate %zu bytes payload buffer", __FUNCTION__,
    298           payload_size);
    299     return ENOMEM;
    300   }
    301   rd_res = qemud_fd_read(mPipeFD, *data, payload_size);
    302   if (static_cast<size_t>(rd_res) == payload_size) {
    303     *data_size = payload_size;
    304     return NO_ERROR;
    305   } else {
    306     ALOGE("%s: Read size %d doesnt match expected payload size %zu: %s",
    307           __FUNCTION__, rd_res, payload_size, strerror(errno));
    308     free(*data);
    309     *data = NULL;
    310     return errno ? errno : EIO;
    311   }
    312 }
    313 
    314 status_t QemuClient::doQuery(QemuQuery* query) {
    315   /* Make sure that query has been successfuly constructed. */
    316   if (query->mQueryDeliveryStatus != NO_ERROR) {
    317     ALOGE("%s: Query is invalid", __FUNCTION__);
    318     return query->mQueryDeliveryStatus;
    319   }
    320 
    321   LOGQ("Send query '%s'", query->mQuery);
    322 
    323   /* Send the query. */
    324   status_t res = sendMessage(query->mQuery, strlen(query->mQuery) + 1);
    325   if (res == NO_ERROR) {
    326     /* Read the response. */
    327     res = receiveMessage(reinterpret_cast<void**>(&query->mReplyBuffer),
    328                          &query->mReplySize);
    329     if (res == NO_ERROR) {
    330       LOGQ("Response to query '%s': Status = '%.2s', %d bytes in response",
    331            query->mQuery, query->mReplyBuffer, query->mReplySize);
    332     } else {
    333       ALOGE("%s Response to query '%s' has failed: %s", __FUNCTION__,
    334             query->mQuery, strerror(res));
    335     }
    336   } else {
    337     ALOGE("%s: Send query '%s' failed: %s", __FUNCTION__, query->mQuery,
    338           strerror(res));
    339   }
    340 
    341   /* Complete the query, and return its completion handling status. */
    342   const status_t res1 = query->completeQuery(res);
    343   ALOGE_IF(res1 != NO_ERROR && res1 != res,
    344            "%s: Error %d in query '%s' completion", __FUNCTION__, res1,
    345            query->mQuery);
    346   return res1;
    347 }
    348 
    349 /****************************************************************************
    350  * Qemu client for the 'factory' service.
    351  ***************************************************************************/
    352 
    353 /*
    354  * Factory service queries.
    355  */
    356 
    357 /* Queries list of cameras connected to the host. */
    358 const char FactoryQemuClient::mQueryList[] = "list";
    359 
    360 FactoryQemuClient::FactoryQemuClient() : QemuClient() {}
    361 
    362 FactoryQemuClient::~FactoryQemuClient() {}
    363 
    364 status_t FactoryQemuClient::listCameras(char** list) {
    365   ALOGV("%s", __FUNCTION__);
    366 
    367   QemuQuery query(mQueryList);
    368   if (doQuery(&query) || !query.isQuerySucceeded()) {
    369     ALOGE("%s: List cameras query failed: %s", __FUNCTION__,
    370           query.mReplyData ? query.mReplyData : "No error message");
    371     return query.getCompletionStatus();
    372   }
    373 
    374   /* Make sure there is a list returned. */
    375   if (query.mReplyDataSize == 0) {
    376     ALOGE("%s: No camera list is returned.", __FUNCTION__);
    377     return EINVAL;
    378   }
    379 
    380   /* Copy the list over. */
    381   *list = (char*)malloc(query.mReplyDataSize);
    382   if (*list != NULL) {
    383     memcpy(*list, query.mReplyData, query.mReplyDataSize);
    384     ALOGD("Emulated camera list: %s", *list);
    385     return NO_ERROR;
    386   } else {
    387     ALOGE("%s: Unable to allocate %zu bytes", __FUNCTION__,
    388           query.mReplyDataSize);
    389     return ENOMEM;
    390   }
    391 }
    392 
    393 /****************************************************************************
    394  * Qemu client for an 'emulated camera' service.
    395  ***************************************************************************/
    396 
    397 /*
    398  * Emulated camera queries
    399  */
    400 
    401 /* Connect to the camera device. */
    402 const char CameraQemuClient::mQueryConnect[] = "connect";
    403 /* Disconect from the camera device. */
    404 const char CameraQemuClient::mQueryDisconnect[] = "disconnect";
    405 /* Start capturing video from the camera device. */
    406 const char CameraQemuClient::mQueryStart[] = "start";
    407 /* Stop capturing video from the camera device. */
    408 const char CameraQemuClient::mQueryStop[] = "stop";
    409 /* Get next video frame from the camera device. */
    410 const char CameraQemuClient::mQueryFrame[] = "frame";
    411 
    412 CameraQemuClient::CameraQemuClient() : QemuClient() {}
    413 
    414 CameraQemuClient::~CameraQemuClient() {}
    415 
    416 status_t CameraQemuClient::queryConnect() {
    417   ALOGV("%s", __FUNCTION__);
    418 
    419   QemuQuery query(mQueryConnect);
    420   doQuery(&query);
    421   const status_t res = query.getCompletionStatus();
    422   ALOGE_IF(res != NO_ERROR, "%s: Query failed: %s", __FUNCTION__,
    423            query.mReplyData ? query.mReplyData : "No error message");
    424   return res;
    425 }
    426 
    427 status_t CameraQemuClient::queryDisconnect() {
    428   ALOGV("%s", __FUNCTION__);
    429 
    430   QemuQuery query(mQueryDisconnect);
    431   doQuery(&query);
    432   const status_t res = query.getCompletionStatus();
    433   ALOGE_IF(res != NO_ERROR, "%s: Query failed: %s", __FUNCTION__,
    434            query.mReplyData ? query.mReplyData : "No error message");
    435   return res;
    436 }
    437 
    438 status_t CameraQemuClient::queryStart(uint32_t pixel_format, int width,
    439                                       int height) {
    440   ALOGV("%s", __FUNCTION__);
    441 
    442   char query_str[256];
    443   snprintf(query_str, sizeof(query_str), "%s dim=%dx%d pix=%d", mQueryStart,
    444            width, height, pixel_format);
    445   QemuQuery query(query_str);
    446   doQuery(&query);
    447   const status_t res = query.getCompletionStatus();
    448   ALOGE_IF(res != NO_ERROR, "%s: Query failed: %s", __FUNCTION__,
    449            query.mReplyData ? query.mReplyData : "No error message");
    450   return res;
    451 }
    452 
    453 status_t CameraQemuClient::queryStop() {
    454   ALOGV("%s", __FUNCTION__);
    455 
    456   QemuQuery query(mQueryStop);
    457   doQuery(&query);
    458   const status_t res = query.getCompletionStatus();
    459   ALOGE_IF(res != NO_ERROR, "%s: Query failed: %s", __FUNCTION__,
    460            query.mReplyData ? query.mReplyData : "No error message");
    461   return res;
    462 }
    463 
    464 status_t CameraQemuClient::queryFrame(void* vframe, void* pframe,
    465                                       size_t vframe_size, size_t pframe_size,
    466                                       float r_scale, float g_scale,
    467                                       float b_scale, float exposure_comp) {
    468   ALOGV("%s", __FUNCTION__);
    469 
    470   char query_str[256];
    471   snprintf(query_str, sizeof(query_str),
    472            "%s video=%zu preview=%zu whiteb=%g,%g,%g expcomp=%g", mQueryFrame,
    473            (vframe && vframe_size) ? vframe_size : 0,
    474            (pframe && pframe_size) ? pframe_size : 0, r_scale, g_scale, b_scale,
    475            exposure_comp);
    476   QemuQuery query(query_str);
    477   doQuery(&query);
    478   const status_t res = query.getCompletionStatus();
    479   if (res != NO_ERROR) {
    480     ALOGE("%s: Query failed: %s", __FUNCTION__,
    481           query.mReplyData ? query.mReplyData : "No error message");
    482     return res;
    483   }
    484 
    485   /* Copy requested frames. */
    486   size_t cur_offset = 0;
    487   const uint8_t* frame = reinterpret_cast<const uint8_t*>(query.mReplyData);
    488   /* Video frame is always first. */
    489   if (vframe != NULL && vframe_size != 0) {
    490     /* Make sure that video frame is in. */
    491     if ((query.mReplyDataSize - cur_offset) >= vframe_size) {
    492       memcpy(vframe, frame, vframe_size);
    493       cur_offset += vframe_size;
    494     } else {
    495       ALOGE("%s: Reply %zu bytes is to small to contain %zu bytes video frame",
    496             __FUNCTION__, query.mReplyDataSize - cur_offset, vframe_size);
    497       return EINVAL;
    498     }
    499   }
    500   if (pframe != NULL && pframe_size != 0) {
    501     /* Make sure that preview frame is in. */
    502     if ((query.mReplyDataSize - cur_offset) >= pframe_size) {
    503       memcpy(pframe, frame + cur_offset, pframe_size);
    504       cur_offset += pframe_size;
    505     } else {
    506       ALOGE(
    507           "%s: Reply %zu bytes is to small to contain %zu bytes preview frame",
    508           __FUNCTION__, query.mReplyDataSize - cur_offset, pframe_size);
    509       return EINVAL;
    510     }
    511   }
    512 
    513   return NO_ERROR;
    514 }
    515 
    516 }; /* namespace android */
    517