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 <log/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 
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   *mQuery = '\0';
50 }
51 
QemuQuery(const char * query_string)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 
QemuQuery(const char * query_name,const char * query_param)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 
~QemuQuery()74 QemuQuery::~QemuQuery() { QemuQuery::resetQuery(); }
75 
createQuery(const char * name,const char * param)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 
completeQuery(status_t status)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 
resetQuery()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 
QemuClient()188 QemuClient::QemuClient() : mPipeFD(-1) {}
189 
~QemuClient()190 QemuClient::~QemuClient() {
191   if (mPipeFD >= 0) {
192     close(mPipeFD);
193   }
194 }
195 
196 /****************************************************************************
197  * Qemu client API
198  ***************************************************************************/
199 
connectClient(const char * param)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 
disconnectClient()234 void QemuClient::disconnectClient() {
235   ALOGV("%s", __FUNCTION__);
236 
237   if (mPipeFD >= 0) {
238     close(mPipeFD);
239     mPipeFD = -1;
240   }
241 }
242 
sendMessage(const void * data,size_t data_size)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 
receiveMessage(void ** data,size_t * data_size)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 
doQuery(QemuQuery * query)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 
FactoryQemuClient()360 FactoryQemuClient::FactoryQemuClient() : QemuClient() {}
361 
~FactoryQemuClient()362 FactoryQemuClient::~FactoryQemuClient() {}
363 
listCameras(char ** list)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 
CameraQemuClient()412 CameraQemuClient::CameraQemuClient() : QemuClient() {}
413 
~CameraQemuClient()414 CameraQemuClient::~CameraQemuClient() {}
415 
queryConnect()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 
queryDisconnect()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 
queryStart(uint32_t pixel_format,int width,int height)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 
queryStop()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 
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)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