2 * Copyright (C) 2011 The Android Open Source Project
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
8 * http://www.apache.org/licenses/LICENSE-2.0
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.
18 * Contains implementation of classes that encapsulate connection to camera
19 * services in the emulator via qemu pipe.
23 #define LOG_TAG "EmulatedCamera_QemuClient"
24 #include <cutils/log.h>
25 #include "EmulatedCamera.h"
26 #include "QemuClient.h"
30 #define LOGQ(...) ALOGD(__VA_ARGS__)
32 #define LOGQ(...) (void(0))
37 /****************************************************************************
39 ***************************************************************************/
41 QemuQuery::QemuQuery()
42 : mQuery(mQueryPrealloc),
43 mQueryDeliveryStatus(NO_ERROR),
53 QemuQuery::QemuQuery(const char* query_string)
54 : mQuery(mQueryPrealloc),
55 mQueryDeliveryStatus(NO_ERROR),
62 mQueryDeliveryStatus = QemuQuery::createQuery(query_string, NULL);
65 QemuQuery::QemuQuery(const char* query_name, const char* query_param)
66 : mQuery(mQueryPrealloc),
67 mQueryDeliveryStatus(NO_ERROR),
74 mQueryDeliveryStatus = QemuQuery::createQuery(query_name, query_param);
77 QemuQuery::~QemuQuery()
79 QemuQuery::resetQuery();
82 status_t QemuQuery::createQuery(const char* name, const char* param)
84 /* Reset from the previous use. */
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.",
91 mQueryDeliveryStatus = EINVAL;
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);
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;
110 /* At this point mQuery buffer is big enough for the query. */
112 sprintf(mQuery, "%s %s", name, param);
114 memcpy(mQuery, name, name_len + 1);
120 status_t QemuQuery::completeQuery(status_t status)
122 /* Save query completion status. */
123 mQueryDeliveryStatus = status;
124 if (mQueryDeliveryStatus != NO_ERROR) {
125 return mQueryDeliveryStatus;
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;
139 /* Lets see the reply status. */
140 if (!memcmp(mReplyBuffer, "ok", 2)) {
142 } else if (!memcmp(mReplyBuffer, "ko", 2)) {
145 ALOGE("%s: Invalid query reply: '%s'", __FUNCTION__, mReplyBuffer);
146 mQueryDeliveryStatus = EINVAL;
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
154 if (mReplyBuffer[2] != ':') {
155 ALOGE("%s: Invalid query reply: '%s'", __FUNCTION__, mReplyBuffer);
156 mQueryDeliveryStatus = EINVAL;
159 mReplyData = mReplyBuffer + 3;
160 mReplyDataSize = mReplySize - 3;
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;
174 void QemuQuery::resetQuery()
176 if (mQuery != NULL && mQuery != mQueryPrealloc) {
179 mQuery = mQueryPrealloc;
180 mQueryDeliveryStatus = NO_ERROR;
181 if (mReplyBuffer != NULL) {
186 mReplySize = mReplyDataSize = 0;
190 /****************************************************************************
192 ***************************************************************************/
194 /* Camera service name. */
195 const char QemuClient::mCameraServiceName[] = "camera";
197 QemuClient::QemuClient()
202 QemuClient::~QemuClient()
209 /****************************************************************************
211 ***************************************************************************/
213 status_t QemuClient::connectClient(const char* param)
215 ALOGV("%s: '%s'", __FUNCTION__, param ? param : "");
217 /* Make sure that client is not connected already. */
219 ALOGE("%s: Qemu client is already connected", __FUNCTION__);
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. */
227 snprintf(pipe_name, sizeof(pipe_name), "qemud:%s", mCameraServiceName);
228 mPipeFD = qemu_pipe_open(pipe_name);
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) +
234 sprintf(connection_str, "qemud:%s:%s", mCameraServiceName, param);
236 mPipeFD = qemu_pipe_open(connection_str);
237 delete[] connection_str;
240 ALOGE("%s: Unable to connect to the camera service '%s': %s",
241 __FUNCTION__, param ? param : "Factory", strerror(errno));
242 return errno ? errno : EINVAL;
248 void QemuClient::disconnectClient()
250 ALOGV("%s", __FUNCTION__);
258 status_t QemuClient::sendMessage(const void* data, size_t data_size)
261 ALOGE("%s: Qemu client is not connected", __FUNCTION__);
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) {
273 ALOGE("%s: Error sending data via qemu pipe: '%s'",
274 __FUNCTION__, strerror(errno));
275 return errno ? errno : EIO;
279 status_t QemuClient::receiveMessage(void** data, size_t* data_size)
285 ALOGE("%s: Qemu client is not connected", __FUNCTION__);
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. */
294 char payload_size_str[9];
295 int rd_res = qemud_fd_read(mPipeFD, payload_size_str, 8);
297 ALOGE("%s: Unable to obtain payload size: %s",
298 __FUNCTION__, strerror(errno));
299 return errno ? errno : EIO;
302 /* Convert payload size. */
304 payload_size_str[8] = '\0';
305 payload_size = strtol(payload_size_str, NULL, 16);
307 ALOGE("%s: Invalid payload size '%s'", __FUNCTION__, payload_size_str);
311 /* Allocate payload data buffer, and read the payload there. */
312 *data = malloc(payload_size);
314 ALOGE("%s: Unable to allocate %zu bytes payload buffer",
315 __FUNCTION__, payload_size);
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;
323 ALOGE("%s: Read size %d doesnt match expected payload size %zu: %s",
324 __FUNCTION__, rd_res, payload_size, strerror(errno));
327 return errno ? errno : EIO;
331 status_t QemuClient::doQuery(QemuQuery* query)
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;
339 LOGQ("Send query '%s'", query->mQuery);
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),
347 if (res == NO_ERROR) {
348 LOGQ("Response to query '%s': Status = '%.2s', %d bytes in response",
349 query->mQuery, query->mReplyBuffer, query->mReplySize);
351 ALOGE("%s Response to query '%s' has failed: %s",
352 __FUNCTION__, query->mQuery, strerror(res));
355 ALOGE("%s: Send query '%s' failed: %s",
356 __FUNCTION__, query->mQuery, strerror(res));
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);
367 /****************************************************************************
368 * Qemu client for the 'factory' service.
369 ***************************************************************************/
372 * Factory service queries.
375 /* Queries list of cameras connected to the host. */
376 const char FactoryQemuClient::mQueryList[] = "list";
378 FactoryQemuClient::FactoryQemuClient()
383 FactoryQemuClient::~FactoryQemuClient()
387 status_t FactoryQemuClient::listCameras(char** list)
389 ALOGV("%s", __FUNCTION__);
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();
398 /* Make sure there is a list returned. */
399 if (query.mReplyDataSize == 0) {
400 ALOGE("%s: No camera list is returned.", __FUNCTION__);
404 /* Copy the list over. */
405 *list = (char*)malloc(query.mReplyDataSize);
407 memcpy(*list, query.mReplyData, query.mReplyDataSize);
408 ALOGD("Emulated camera list: %s", *list);
411 ALOGE("%s: Unable to allocate %zu bytes",
412 __FUNCTION__, query.mReplyDataSize);
417 /****************************************************************************
418 * Qemu client for an 'emulated camera' service.
419 ***************************************************************************/
422 * Emulated camera queries
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";
436 CameraQemuClient::CameraQemuClient()
441 CameraQemuClient::~CameraQemuClient()
446 status_t CameraQemuClient::queryConnect()
448 ALOGV("%s", __FUNCTION__);
450 QemuQuery query(mQueryConnect);
452 const status_t res = query.getCompletionStatus();
453 ALOGE_IF(res != NO_ERROR, "%s: Query failed: %s",
454 __FUNCTION__, query.mReplyData ? query.mReplyData :
459 status_t CameraQemuClient::queryDisconnect()
461 ALOGV("%s", __FUNCTION__);
463 QemuQuery query(mQueryDisconnect);
465 const status_t res = query.getCompletionStatus();
466 ALOGE_IF(res != NO_ERROR, "%s: Query failed: %s",
467 __FUNCTION__, query.mReplyData ? query.mReplyData :
472 status_t CameraQemuClient::queryStart(uint32_t pixel_format,
476 ALOGV("%s", __FUNCTION__);
479 snprintf(query_str, sizeof(query_str), "%s dim=%dx%d pix=%d",
480 mQueryStart, width, height, pixel_format);
481 QemuQuery query(query_str);
483 const status_t res = query.getCompletionStatus();
484 ALOGE_IF(res != NO_ERROR, "%s: Query failed: %s",
485 __FUNCTION__, query.mReplyData ? query.mReplyData :
490 status_t CameraQemuClient::queryStop()
492 ALOGV("%s", __FUNCTION__);
494 QemuQuery query(mQueryStop);
496 const status_t res = query.getCompletionStatus();
497 ALOGE_IF(res != NO_ERROR, "%s: Query failed: %s",
498 __FUNCTION__, query.mReplyData ? query.mReplyData :
503 status_t CameraQemuClient::queryFrame(void* vframe,
512 ALOGV("%s", __FUNCTION__);
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,
519 QemuQuery query(query_str);
521 const status_t res = query.getCompletionStatus();
522 if( res != NO_ERROR) {
523 ALOGE("%s: Query failed: %s",
524 __FUNCTION__, query.mReplyData ? query.mReplyData :
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;
539 ALOGE("%s: Reply %zu bytes is to small to contain %zu bytes video frame",
540 __FUNCTION__, query.mReplyDataSize - cur_offset, vframe_size);
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;
550 ALOGE("%s: Reply %zu bytes is to small to contain %zu bytes preview frame",
551 __FUNCTION__, query.mReplyDataSize - cur_offset, pframe_size);
559 }; /* namespace android */