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 <cutils/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 
QemuQuery()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 
QemuQuery(const char * query_string)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 
QemuQuery(const char * query_name,const char * query_param)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 
~QemuQuery()81 QemuQuery::~QemuQuery()
82 {
83     QemuQuery::resetQuery();
84 }
85 
createQuery(const char * name,const char * param)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 
completeQuery(status_t status)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 
resetQuery()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 
QemuClient()201 QemuClient::QemuClient()
202     : mPipeFD(-1)
203 {
204 }
205 
~QemuClient()206 QemuClient::~QemuClient()
207 {
208     if (mPipeFD >= 0) {
209         close(mPipeFD);
210     }
211 }
212 
213 /****************************************************************************
214  * Qemu client API
215  ***************************************************************************/
216 
connectClient(const char * param)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 
disconnectClient()253 void QemuClient::disconnectClient()
254 {
255     ALOGV("%s", __FUNCTION__);
256 
257     if (mPipeFD >= 0) {
258         close(mPipeFD);
259         mPipeFD = -1;
260     }
261 }
262 
sendMessage(const void * data,size_t data_size)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 
receiveMessage(void ** data,size_t * data_size)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 
doQuery(QemuQuery * query)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 
FactoryQemuClient()379 FactoryQemuClient::FactoryQemuClient()
380     : QemuClient()
381 {
382 }
383 
~FactoryQemuClient()384 FactoryQemuClient::~FactoryQemuClient()
385 {
386 }
387 
listCameras(char ** list)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 
CameraQemuClient()437 CameraQemuClient::CameraQemuClient()
438     : QemuClient()
439 {
440 }
441 
~CameraQemuClient()442 CameraQemuClient::~CameraQemuClient()
443 {
444 
445 }
446 
queryConnect()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 
queryDisconnect()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 
queryStart(uint32_t pixel_format,int width,int height)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 
queryStop()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 
queryFrame(void * vframe,void * pframe,size_t vframe_size,size_t pframe_size,float r_scale,float g_scale,float b_scale,float exposure_comp)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 {
513     ALOGV("%s", __FUNCTION__);
514 
515     char query_str[256];
516     snprintf(query_str, sizeof(query_str), "%s video=%zu preview=%zu whiteb=%g,%g,%g expcomp=%g",
517              mQueryFrame, (vframe && vframe_size) ? vframe_size : 0,
518              (pframe && pframe_size) ? pframe_size : 0, r_scale, g_scale, b_scale,
519              exposure_comp);
520     QemuQuery query(query_str);
521     doQuery(&query);
522     const status_t res = query.getCompletionStatus();
523     if( res != NO_ERROR) {
524         ALOGE("%s: Query failed: %s",
525              __FUNCTION__, query.mReplyData ? query.mReplyData :
526                                               "No error message");
527         return res;
528     }
529 
530     /* Copy requested frames. */
531     size_t cur_offset = 0;
532     const uint8_t* frame = reinterpret_cast<const uint8_t*>(query.mReplyData);
533     /* Video frame is always first. */
534     if (vframe != NULL && vframe_size != 0) {
535         /* Make sure that video frame is in. */
536         if ((query.mReplyDataSize - cur_offset) >= vframe_size) {
537             memcpy(vframe, frame, vframe_size);
538             cur_offset += vframe_size;
539         } else {
540             ALOGE("%s: Reply %zu bytes is to small to contain %zu bytes video frame",
541                  __FUNCTION__, query.mReplyDataSize - cur_offset, vframe_size);
542             return EINVAL;
543         }
544     }
545     if (pframe != NULL && pframe_size != 0) {
546         /* Make sure that preview frame is in. */
547         if ((query.mReplyDataSize - cur_offset) >= pframe_size) {
548             memcpy(pframe, frame + cur_offset, pframe_size);
549             cur_offset += pframe_size;
550         } else {
551             ALOGE("%s: Reply %zu bytes is to small to contain %zu bytes preview frame",
552                  __FUNCTION__, query.mReplyDataSize - cur_offset, pframe_size);
553             return EINVAL;
554         }
555     }
556 
557     return NO_ERROR;
558 }
559 
560 }; /* namespace android */
561