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 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 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 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 77 QemuQuery::~QemuQuery() 78 { 79 QemuQuery::resetQuery(); 80 } 81 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 %d 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 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 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 197 QemuClient::QemuClient() 198 : mPipeFD(-1) 199 { 200 } 201 202 QemuClient::~QemuClient() 203 { 204 if (mPipeFD >= 0) { 205 close(mPipeFD); 206 } 207 } 208 209 /**************************************************************************** 210 * Qemu client API 211 ***************************************************************************/ 212 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 248 void QemuClient::disconnectClient() 249 { 250 ALOGV("%s", __FUNCTION__); 251 252 if (mPipeFD >= 0) { 253 close(mPipeFD); 254 mPipeFD = -1; 255 } 256 } 257 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 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 %d 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 %d: %s", 324 __FUNCTION__, rd_res, payload_size, strerror(errno)); 325 free(*data); 326 *data = NULL; 327 return errno ? errno : EIO; 328 } 329 } 330 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 378 FactoryQemuClient::FactoryQemuClient() 379 : QemuClient() 380 { 381 } 382 383 FactoryQemuClient::~FactoryQemuClient() 384 { 385 } 386 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 %d 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 436 CameraQemuClient::CameraQemuClient() 437 : QemuClient() 438 { 439 } 440 441 CameraQemuClient::~CameraQemuClient() 442 { 443 444 } 445 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 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 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 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 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=%d preview=%d 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 %d bytes is to small to contain %d 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 %d bytes is to small to contain %d 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