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