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