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