1 /* 2 * Copyright (C) 2008 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 #ifndef ANDROID_HARDWARE_CAMERA_HARDWARE_INTERFACE_H 18 #define ANDROID_HARDWARE_CAMERA_HARDWARE_INTERFACE_H 19 20 #include <binder/IMemory.h> 21 #include <binder/MemoryBase.h> 22 #include <binder/MemoryHeapBase.h> 23 #include <utils/RefBase.h> 24 #include <ui/GraphicBuffer.h> 25 #include <camera/Camera.h> 26 #include <camera/CameraParameters.h> 27 #include <system/window.h> 28 #include <hardware/camera.h> 29 30 namespace android { 31 32 typedef void (*notify_callback)(int32_t msgType, 33 int32_t ext1, 34 int32_t ext2, 35 void* user); 36 37 typedef void (*data_callback)(int32_t msgType, 38 const sp<IMemory> &dataPtr, 39 camera_frame_metadata_t *metadata, 40 void* user); 41 42 typedef void (*data_callback_timestamp)(nsecs_t timestamp, 43 int32_t msgType, 44 const sp<IMemory> &dataPtr, 45 void *user); 46 47 /** 48 * CameraHardwareInterface.h defines the interface to the 49 * camera hardware abstraction layer, used for setting and getting 50 * parameters, live previewing, and taking pictures. It is used for 51 * HAL devices with version CAMERA_DEVICE_API_VERSION_1_0 only. 52 * 53 * It is a referenced counted interface with RefBase as its base class. 54 * CameraService calls openCameraHardware() to retrieve a strong pointer to the 55 * instance of this interface and may be called multiple times. The 56 * following steps describe a typical sequence: 57 * 58 * -# After CameraService calls openCameraHardware(), getParameters() and 59 * setParameters() are used to initialize the camera instance. 60 * -# startPreview() is called. 61 * 62 * Prior to taking a picture, CameraService often calls autofocus(). When auto 63 * focusing has completed, the camera instance sends a CAMERA_MSG_FOCUS notification, 64 * which informs the application whether focusing was successful. The camera instance 65 * only sends this message once and it is up to the application to call autoFocus() 66 * again if refocusing is desired. 67 * 68 * CameraService calls takePicture() to request the camera instance take a 69 * picture. At this point, if a shutter, postview, raw, and/or compressed 70 * callback is desired, the corresponding message must be enabled. Any memory 71 * provided in a data callback must be copied if it's needed after returning. 72 */ 73 74 class CameraHardwareInterface : public virtual RefBase { 75 public: 76 CameraHardwareInterface(const char *name) 77 { 78 mDevice = 0; 79 mName = name; 80 } 81 82 ~CameraHardwareInterface() 83 { 84 ALOGI("Destroying camera %s", mName.string()); 85 if(mDevice) { 86 int rc = mDevice->common.close(&mDevice->common); 87 if (rc != OK) 88 ALOGE("Could not close camera %s: %d", mName.string(), rc); 89 } 90 } 91 92 status_t initialize(hw_module_t *module) 93 { 94 ALOGI("Opening camera %s", mName.string()); 95 int rc = module->methods->open(module, mName.string(), 96 (hw_device_t **)&mDevice); 97 if (rc != OK) { 98 ALOGE("Could not open camera %s: %d", mName.string(), rc); 99 return rc; 100 } 101 initHalPreviewWindow(); 102 return rc; 103 } 104 105 /** Set the ANativeWindow to which preview frames are sent */ 106 status_t setPreviewWindow(const sp<ANativeWindow>& buf) 107 { 108 ALOGV("%s(%s) buf %p", __FUNCTION__, mName.string(), buf.get()); 109 110 if (mDevice->ops->set_preview_window) { 111 mPreviewWindow = buf; 112 mHalPreviewWindow.user = this; 113 ALOGV("%s &mHalPreviewWindow %p mHalPreviewWindow.user %p", __FUNCTION__, 114 &mHalPreviewWindow, mHalPreviewWindow.user); 115 return mDevice->ops->set_preview_window(mDevice, 116 buf.get() ? &mHalPreviewWindow.nw : 0); 117 } 118 return INVALID_OPERATION; 119 } 120 121 /** Set the notification and data callbacks */ 122 void setCallbacks(notify_callback notify_cb, 123 data_callback data_cb, 124 data_callback_timestamp data_cb_timestamp, 125 void* user) 126 { 127 mNotifyCb = notify_cb; 128 mDataCb = data_cb; 129 mDataCbTimestamp = data_cb_timestamp; 130 mCbUser = user; 131 132 ALOGV("%s(%s)", __FUNCTION__, mName.string()); 133 134 if (mDevice->ops->set_callbacks) { 135 mDevice->ops->set_callbacks(mDevice, 136 __notify_cb, 137 __data_cb, 138 __data_cb_timestamp, 139 __get_memory, 140 this); 141 } 142 } 143 144 /** 145 * The following three functions all take a msgtype, 146 * which is a bitmask of the messages defined in 147 * include/ui/Camera.h 148 */ 149 150 /** 151 * Enable a message, or set of messages. 152 */ 153 void enableMsgType(int32_t msgType) 154 { 155 ALOGV("%s(%s)", __FUNCTION__, mName.string()); 156 if (mDevice->ops->enable_msg_type) 157 mDevice->ops->enable_msg_type(mDevice, msgType); 158 } 159 160 /** 161 * Disable a message, or a set of messages. 162 * 163 * Once received a call to disableMsgType(CAMERA_MSG_VIDEO_FRAME), camera hal 164 * should not rely on its client to call releaseRecordingFrame() to release 165 * video recording frames sent out by the cameral hal before and after the 166 * disableMsgType(CAMERA_MSG_VIDEO_FRAME) call. Camera hal clients must not 167 * modify/access any video recording frame after calling 168 * disableMsgType(CAMERA_MSG_VIDEO_FRAME). 169 */ 170 void disableMsgType(int32_t msgType) 171 { 172 ALOGV("%s(%s)", __FUNCTION__, mName.string()); 173 if (mDevice->ops->disable_msg_type) 174 mDevice->ops->disable_msg_type(mDevice, msgType); 175 } 176 177 /** 178 * Query whether a message, or a set of messages, is enabled. 179 * Note that this is operates as an AND, if any of the messages 180 * queried are off, this will return false. 181 */ 182 int msgTypeEnabled(int32_t msgType) 183 { 184 ALOGV("%s(%s)", __FUNCTION__, mName.string()); 185 if (mDevice->ops->msg_type_enabled) 186 return mDevice->ops->msg_type_enabled(mDevice, msgType); 187 return false; 188 } 189 190 /** 191 * Start preview mode. 192 */ 193 status_t startPreview() 194 { 195 ALOGV("%s(%s)", __FUNCTION__, mName.string()); 196 if (mDevice->ops->start_preview) 197 return mDevice->ops->start_preview(mDevice); 198 return INVALID_OPERATION; 199 } 200 201 /** 202 * Stop a previously started preview. 203 */ 204 void stopPreview() 205 { 206 ALOGV("%s(%s)", __FUNCTION__, mName.string()); 207 if (mDevice->ops->stop_preview) 208 mDevice->ops->stop_preview(mDevice); 209 } 210 211 /** 212 * Returns true if preview is enabled. 213 */ 214 int previewEnabled() 215 { 216 ALOGV("%s(%s)", __FUNCTION__, mName.string()); 217 if (mDevice->ops->preview_enabled) 218 return mDevice->ops->preview_enabled(mDevice); 219 return false; 220 } 221 222 /** 223 * Request the camera hal to store meta data or real YUV data in 224 * the video buffers send out via CAMERA_MSG_VIDEO_FRRAME for a 225 * recording session. If it is not called, the default camera 226 * hal behavior is to store real YUV data in the video buffers. 227 * 228 * This method should be called before startRecording() in order 229 * to be effective. 230 * 231 * If meta data is stored in the video buffers, it is up to the 232 * receiver of the video buffers to interpret the contents and 233 * to find the actual frame data with the help of the meta data 234 * in the buffer. How this is done is outside of the scope of 235 * this method. 236 * 237 * Some camera hal may not support storing meta data in the video 238 * buffers, but all camera hal should support storing real YUV data 239 * in the video buffers. If the camera hal does not support storing 240 * the meta data in the video buffers when it is requested to do 241 * do, INVALID_OPERATION must be returned. It is very useful for 242 * the camera hal to pass meta data rather than the actual frame 243 * data directly to the video encoder, since the amount of the 244 * uncompressed frame data can be very large if video size is large. 245 * 246 * @param enable if true to instruct the camera hal to store 247 * meta data in the video buffers; false to instruct 248 * the camera hal to store real YUV data in the video 249 * buffers. 250 * 251 * @return OK on success. 252 */ 253 254 status_t storeMetaDataInBuffers(int enable) 255 { 256 ALOGV("%s(%s)", __FUNCTION__, mName.string()); 257 if (mDevice->ops->store_meta_data_in_buffers) 258 return mDevice->ops->store_meta_data_in_buffers(mDevice, enable); 259 return enable ? INVALID_OPERATION: OK; 260 } 261 262 /** 263 * Start record mode. When a record image is available a CAMERA_MSG_VIDEO_FRAME 264 * message is sent with the corresponding frame. Every record frame must be released 265 * by a cameral hal client via releaseRecordingFrame() before the client calls 266 * disableMsgType(CAMERA_MSG_VIDEO_FRAME). After the client calls 267 * disableMsgType(CAMERA_MSG_VIDEO_FRAME), it is camera hal's responsibility 268 * to manage the life-cycle of the video recording frames, and the client must 269 * not modify/access any video recording frames. 270 */ 271 status_t startRecording() 272 { 273 ALOGV("%s(%s)", __FUNCTION__, mName.string()); 274 if (mDevice->ops->start_recording) 275 return mDevice->ops->start_recording(mDevice); 276 return INVALID_OPERATION; 277 } 278 279 /** 280 * Stop a previously started recording. 281 */ 282 void stopRecording() 283 { 284 ALOGV("%s(%s)", __FUNCTION__, mName.string()); 285 if (mDevice->ops->stop_recording) 286 mDevice->ops->stop_recording(mDevice); 287 } 288 289 /** 290 * Returns true if recording is enabled. 291 */ 292 int recordingEnabled() 293 { 294 ALOGV("%s(%s)", __FUNCTION__, mName.string()); 295 if (mDevice->ops->recording_enabled) 296 return mDevice->ops->recording_enabled(mDevice); 297 return false; 298 } 299 300 /** 301 * Release a record frame previously returned by CAMERA_MSG_VIDEO_FRAME. 302 * 303 * It is camera hal client's responsibility to release video recording 304 * frames sent out by the camera hal before the camera hal receives 305 * a call to disableMsgType(CAMERA_MSG_VIDEO_FRAME). After it receives 306 * the call to disableMsgType(CAMERA_MSG_VIDEO_FRAME), it is camera hal's 307 * responsibility of managing the life-cycle of the video recording 308 * frames. 309 */ 310 void releaseRecordingFrame(const sp<IMemory>& mem) 311 { 312 ALOGV("%s(%s)", __FUNCTION__, mName.string()); 313 if (mDevice->ops->release_recording_frame) { 314 ssize_t offset; 315 size_t size; 316 sp<IMemoryHeap> heap = mem->getMemory(&offset, &size); 317 void *data = ((uint8_t *)heap->base()) + offset; 318 return mDevice->ops->release_recording_frame(mDevice, data); 319 } 320 } 321 322 /** 323 * Start auto focus, the notification callback routine is called 324 * with CAMERA_MSG_FOCUS once when focusing is complete. autoFocus() 325 * will be called again if another auto focus is needed. 326 */ 327 status_t autoFocus() 328 { 329 ALOGV("%s(%s)", __FUNCTION__, mName.string()); 330 if (mDevice->ops->auto_focus) 331 return mDevice->ops->auto_focus(mDevice); 332 return INVALID_OPERATION; 333 } 334 335 /** 336 * Cancels auto-focus function. If the auto-focus is still in progress, 337 * this function will cancel it. Whether the auto-focus is in progress 338 * or not, this function will return the focus position to the default. 339 * If the camera does not support auto-focus, this is a no-op. 340 */ 341 status_t cancelAutoFocus() 342 { 343 ALOGV("%s(%s)", __FUNCTION__, mName.string()); 344 if (mDevice->ops->cancel_auto_focus) 345 return mDevice->ops->cancel_auto_focus(mDevice); 346 return INVALID_OPERATION; 347 } 348 349 /** 350 * Take a picture. 351 */ 352 status_t takePicture() 353 { 354 ALOGV("%s(%s)", __FUNCTION__, mName.string()); 355 if (mDevice->ops->take_picture) 356 return mDevice->ops->take_picture(mDevice); 357 return INVALID_OPERATION; 358 } 359 360 /** 361 * Cancel a picture that was started with takePicture. Calling this 362 * method when no picture is being taken is a no-op. 363 */ 364 status_t cancelPicture() 365 { 366 ALOGV("%s(%s)", __FUNCTION__, mName.string()); 367 if (mDevice->ops->cancel_picture) 368 return mDevice->ops->cancel_picture(mDevice); 369 return INVALID_OPERATION; 370 } 371 372 /** 373 * Set the camera parameters. This returns BAD_VALUE if any parameter is 374 * invalid or not supported. */ 375 status_t setParameters(const CameraParameters ¶ms) 376 { 377 ALOGV("%s(%s)", __FUNCTION__, mName.string()); 378 if (mDevice->ops->set_parameters) 379 return mDevice->ops->set_parameters(mDevice, 380 params.flatten().string()); 381 return INVALID_OPERATION; 382 } 383 384 /** Return the camera parameters. */ 385 CameraParameters getParameters() const 386 { 387 ALOGV("%s(%s)", __FUNCTION__, mName.string()); 388 CameraParameters parms; 389 if (mDevice->ops->get_parameters) { 390 char *temp = mDevice->ops->get_parameters(mDevice); 391 String8 str_parms(temp); 392 if (mDevice->ops->put_parameters) 393 mDevice->ops->put_parameters(mDevice, temp); 394 else 395 free(temp); 396 parms.unflatten(str_parms); 397 } 398 return parms; 399 } 400 401 /** 402 * Send command to camera driver. 403 */ 404 status_t sendCommand(int32_t cmd, int32_t arg1, int32_t arg2) 405 { 406 ALOGV("%s(%s)", __FUNCTION__, mName.string()); 407 if (mDevice->ops->send_command) 408 return mDevice->ops->send_command(mDevice, cmd, arg1, arg2); 409 return INVALID_OPERATION; 410 } 411 412 /** 413 * Release the hardware resources owned by this object. Note that this is 414 * *not* done in the destructor. 415 */ 416 void release() { 417 ALOGV("%s(%s)", __FUNCTION__, mName.string()); 418 if (mDevice->ops->release) 419 mDevice->ops->release(mDevice); 420 } 421 422 /** 423 * Dump state of the camera hardware 424 */ 425 status_t dump(int fd, const Vector<String16>& /*args*/) const 426 { 427 ALOGV("%s(%s)", __FUNCTION__, mName.string()); 428 if (mDevice->ops->dump) 429 return mDevice->ops->dump(mDevice, fd); 430 return OK; // It's fine if the HAL doesn't implement dump() 431 } 432 433 private: 434 camera_device_t *mDevice; 435 String8 mName; 436 437 static void __notify_cb(int32_t msg_type, int32_t ext1, 438 int32_t ext2, void *user) 439 { 440 ALOGV("%s", __FUNCTION__); 441 CameraHardwareInterface *__this = 442 static_cast<CameraHardwareInterface *>(user); 443 __this->mNotifyCb(msg_type, ext1, ext2, __this->mCbUser); 444 } 445 446 static void __data_cb(int32_t msg_type, 447 const camera_memory_t *data, unsigned int index, 448 camera_frame_metadata_t *metadata, 449 void *user) 450 { 451 ALOGV("%s", __FUNCTION__); 452 CameraHardwareInterface *__this = 453 static_cast<CameraHardwareInterface *>(user); 454 sp<CameraHeapMemory> mem(static_cast<CameraHeapMemory *>(data->handle)); 455 if (index >= mem->mNumBufs) { 456 ALOGE("%s: invalid buffer index %d, max allowed is %d", __FUNCTION__, 457 index, mem->mNumBufs); 458 return; 459 } 460 __this->mDataCb(msg_type, mem->mBuffers[index], metadata, __this->mCbUser); 461 } 462 463 static void __data_cb_timestamp(nsecs_t timestamp, int32_t msg_type, 464 const camera_memory_t *data, unsigned index, 465 void *user) 466 { 467 ALOGV("%s", __FUNCTION__); 468 CameraHardwareInterface *__this = 469 static_cast<CameraHardwareInterface *>(user); 470 // Start refcounting the heap object from here on. When the clients 471 // drop all references, it will be destroyed (as well as the enclosed 472 // MemoryHeapBase. 473 sp<CameraHeapMemory> mem(static_cast<CameraHeapMemory *>(data->handle)); 474 if (index >= mem->mNumBufs) { 475 ALOGE("%s: invalid buffer index %d, max allowed is %d", __FUNCTION__, 476 index, mem->mNumBufs); 477 return; 478 } 479 __this->mDataCbTimestamp(timestamp, msg_type, mem->mBuffers[index], __this->mCbUser); 480 } 481 482 // This is a utility class that combines a MemoryHeapBase and a MemoryBase 483 // in one. Since we tend to use them in a one-to-one relationship, this is 484 // handy. 485 486 class CameraHeapMemory : public RefBase { 487 public: 488 CameraHeapMemory(int fd, size_t buf_size, uint_t num_buffers = 1) : 489 mBufSize(buf_size), 490 mNumBufs(num_buffers) 491 { 492 mHeap = new MemoryHeapBase(fd, buf_size * num_buffers); 493 commonInitialization(); 494 } 495 496 CameraHeapMemory(size_t buf_size, uint_t num_buffers = 1) : 497 mBufSize(buf_size), 498 mNumBufs(num_buffers) 499 { 500 mHeap = new MemoryHeapBase(buf_size * num_buffers); 501 commonInitialization(); 502 } 503 504 void commonInitialization() 505 { 506 handle.data = mHeap->base(); 507 handle.size = mBufSize * mNumBufs; 508 handle.handle = this; 509 510 mBuffers = new sp<MemoryBase>[mNumBufs]; 511 for (uint_t i = 0; i < mNumBufs; i++) 512 mBuffers[i] = new MemoryBase(mHeap, 513 i * mBufSize, 514 mBufSize); 515 516 handle.release = __put_memory; 517 } 518 519 virtual ~CameraHeapMemory() 520 { 521 delete [] mBuffers; 522 } 523 524 size_t mBufSize; 525 uint_t mNumBufs; 526 sp<MemoryHeapBase> mHeap; 527 sp<MemoryBase> *mBuffers; 528 529 camera_memory_t handle; 530 }; 531 532 static camera_memory_t* __get_memory(int fd, size_t buf_size, uint_t num_bufs, 533 void *user __attribute__((unused))) 534 { 535 CameraHeapMemory *mem; 536 if (fd < 0) 537 mem = new CameraHeapMemory(buf_size, num_bufs); 538 else 539 mem = new CameraHeapMemory(fd, buf_size, num_bufs); 540 mem->incStrong(mem); 541 return &mem->handle; 542 } 543 544 static void __put_memory(camera_memory_t *data) 545 { 546 if (!data) 547 return; 548 549 CameraHeapMemory *mem = static_cast<CameraHeapMemory *>(data->handle); 550 mem->decStrong(mem); 551 } 552 553 static ANativeWindow *__to_anw(void *user) 554 { 555 CameraHardwareInterface *__this = 556 reinterpret_cast<CameraHardwareInterface *>(user); 557 return __this->mPreviewWindow.get(); 558 } 559 #define anw(n) __to_anw(((struct camera_preview_window *)n)->user) 560 561 static int __dequeue_buffer(struct preview_stream_ops* w, 562 buffer_handle_t** buffer, int *stride) 563 { 564 int rc; 565 ANativeWindow *a = anw(w); 566 ANativeWindowBuffer* anb; 567 rc = native_window_dequeue_buffer_and_wait(a, &anb); 568 if (!rc) { 569 *buffer = &anb->handle; 570 *stride = anb->stride; 571 } 572 return rc; 573 } 574 575 #ifndef container_of 576 #define container_of(ptr, type, member) ({ \ 577 const typeof(((type *) 0)->member) *__mptr = (ptr); \ 578 (type *) ((char *) __mptr - (char *)(&((type *)0)->member)); }) 579 #endif 580 581 static int __lock_buffer(struct preview_stream_ops* w, 582 buffer_handle_t* /*buffer*/) 583 { 584 ANativeWindow *a = anw(w); 585 (void)a; 586 return 0; 587 } 588 589 static int __enqueue_buffer(struct preview_stream_ops* w, 590 buffer_handle_t* buffer) 591 { 592 ANativeWindow *a = anw(w); 593 return a->queueBuffer(a, 594 container_of(buffer, ANativeWindowBuffer, handle), -1); 595 } 596 597 static int __cancel_buffer(struct preview_stream_ops* w, 598 buffer_handle_t* buffer) 599 { 600 ANativeWindow *a = anw(w); 601 return a->cancelBuffer(a, 602 container_of(buffer, ANativeWindowBuffer, handle), -1); 603 } 604 605 static int __set_buffer_count(struct preview_stream_ops* w, int count) 606 { 607 ANativeWindow *a = anw(w); 608 return native_window_set_buffer_count(a, count); 609 } 610 611 static int __set_buffers_geometry(struct preview_stream_ops* w, 612 int width, int height, int format) 613 { 614 ANativeWindow *a = anw(w); 615 return native_window_set_buffers_geometry(a, 616 width, height, format); 617 } 618 619 static int __set_crop(struct preview_stream_ops *w, 620 int left, int top, int right, int bottom) 621 { 622 ANativeWindow *a = anw(w); 623 android_native_rect_t crop; 624 crop.left = left; 625 crop.top = top; 626 crop.right = right; 627 crop.bottom = bottom; 628 return native_window_set_crop(a, &crop); 629 } 630 631 static int __set_timestamp(struct preview_stream_ops *w, 632 int64_t timestamp) { 633 ANativeWindow *a = anw(w); 634 return native_window_set_buffers_timestamp(a, timestamp); 635 } 636 637 static int __set_usage(struct preview_stream_ops* w, int usage) 638 { 639 ANativeWindow *a = anw(w); 640 return native_window_set_usage(a, usage); 641 } 642 643 static int __set_swap_interval(struct preview_stream_ops *w, int interval) 644 { 645 ANativeWindow *a = anw(w); 646 return a->setSwapInterval(a, interval); 647 } 648 649 static int __get_min_undequeued_buffer_count( 650 const struct preview_stream_ops *w, 651 int *count) 652 { 653 ANativeWindow *a = anw(w); 654 return a->query(a, NATIVE_WINDOW_MIN_UNDEQUEUED_BUFFERS, count); 655 } 656 657 void initHalPreviewWindow() 658 { 659 mHalPreviewWindow.nw.cancel_buffer = __cancel_buffer; 660 mHalPreviewWindow.nw.lock_buffer = __lock_buffer; 661 mHalPreviewWindow.nw.dequeue_buffer = __dequeue_buffer; 662 mHalPreviewWindow.nw.enqueue_buffer = __enqueue_buffer; 663 mHalPreviewWindow.nw.set_buffer_count = __set_buffer_count; 664 mHalPreviewWindow.nw.set_buffers_geometry = __set_buffers_geometry; 665 mHalPreviewWindow.nw.set_crop = __set_crop; 666 mHalPreviewWindow.nw.set_timestamp = __set_timestamp; 667 mHalPreviewWindow.nw.set_usage = __set_usage; 668 mHalPreviewWindow.nw.set_swap_interval = __set_swap_interval; 669 670 mHalPreviewWindow.nw.get_min_undequeued_buffer_count = 671 __get_min_undequeued_buffer_count; 672 } 673 674 sp<ANativeWindow> mPreviewWindow; 675 676 struct camera_preview_window { 677 struct preview_stream_ops nw; 678 void *user; 679 }; 680 681 struct camera_preview_window mHalPreviewWindow; 682 683 notify_callback mNotifyCb; 684 data_callback mDataCb; 685 data_callback_timestamp mDataCbTimestamp; 686 void *mCbUser; 687 }; 688 689 }; // namespace android 690 691 #endif 692