Migrating vendor/qcom/android to vendor/qcom/android-open
diff --git a/QualcommCameraHardware.cpp b/QualcommCameraHardware.cpp
new file mode 100644
index 0000000..17911d4
--- /dev/null
+++ b/QualcommCameraHardware.cpp
@@ -0,0 +1,2578 @@
+/*
+** Copyright 2008, Google Inc.
+**
+** Licensed under the Apache License, Version 2.0 (the "License");
+** you may not use this file except in compliance with the License.
+** You may obtain a copy of the License at
+**
+**     http://www.apache.org/licenses/LICENSE-2.0
+**
+** Unless required by applicable law or agreed to in writing, software
+** distributed under the License is distributed on an "AS IS" BASIS,
+** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+** See the License for the specific language governing permissions and
+** limitations under the License.
+*/
+
+//#define LOG_NDEBUG 0
+#define LOG_TAG "QualcommCameraHardware"
+#include <utils/Log.h>
+
+#include "QualcommCameraHardware.h"
+
+#include <utils/Errors.h>
+#include <utils/threads.h>
+#include <binder/MemoryHeapPmem.h>
+#include <utils/String16.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <unistd.h>
+#if HAVE_ANDROID_OS
+#include <linux/android_pmem.h>
+#endif
+#include <linux/ioctl.h>
+#include <ui/CameraParameters.h>
+
+#define LIKELY(exp)   __builtin_expect(!!(exp), 1)
+#define UNLIKELY(exp) __builtin_expect(!!(exp), 0)
+
+extern "C" {
+#include <fcntl.h>
+#include <time.h>
+#include <pthread.h>
+#include <stdio.h>
+#include <string.h>
+#include <unistd.h>
+#include <termios.h>
+#include <assert.h>
+#include <stdlib.h>
+#include <ctype.h>
+#include <signal.h>
+#include <errno.h>
+#include <sys/mman.h>
+#include <sys/system_properties.h>
+#include <sys/time.h>
+#include <stdlib.h>
+
+#include <media/msm_camera.h>
+
+#include <camera.h>
+#include <camframe.h>
+#include <mm-still/jpeg/jpege.h>
+
+#define THUMBNAIL_WIDTH        512
+#define THUMBNAIL_HEIGHT       384
+#define THUMBNAIL_WIDTH_STR   "512"
+#define THUMBNAIL_HEIGHT_STR  "384"
+#define DEFAULT_PICTURE_WIDTH  2048
+#define DEFAULT_PICTURE_HEIGHT 1536
+#define THUMBNAIL_BUFFER_SIZE (THUMBNAIL_WIDTH * THUMBNAIL_HEIGHT * 3/2)
+#define MAX_ZOOM_LEVEL 5
+#define NOT_FOUND -1
+
+#if DLOPEN_LIBMMCAMERA
+#include <dlfcn.h>
+
+void* (*LINK_cam_conf)(void *data);
+void* (*LINK_cam_frame)(void *data);
+bool  (*LINK_jpeg_encoder_init)();
+void  (*LINK_jpeg_encoder_join)();
+bool  (*LINK_jpeg_encoder_encode)(const cam_ctrl_dimension_t *dimen,
+                                  const uint8_t *thumbnailbuf, int thumbnailfd,
+                                  const uint8_t *snapshotbuf, int snapshotfd,
+                                  common_crop_t *scaling_parms);
+int  (*LINK_camframe_terminate)(void);
+int8_t (*LINK_jpeg_encoder_setMainImageQuality)(uint32_t quality);
+int8_t (*LINK_jpeg_encoder_setThumbnailQuality)(uint32_t quality);
+int8_t (*LINK_jpeg_encoder_setRotation)(uint32_t rotation);
+int8_t (*LINK_jpeg_encoder_setLocation)(const camera_position_type *location);
+const struct camera_size_type *(*LINK_default_sensor_get_snapshot_sizes)(int *len);
+int (*LINK_launch_cam_conf_thread)(void);
+int (*LINK_release_cam_conf_thread)(void);
+int8_t (*LINK_zoom_crop_upscale)(uint32_t width, uint32_t height,
+    uint32_t cropped_width, uint32_t cropped_height, uint8_t *img_buf);
+
+// callbacks
+void  (**LINK_mmcamera_camframe_callback)(struct msm_frame *frame);
+void  (**LINK_mmcamera_jpegfragment_callback)(uint8_t *buff_ptr,
+                                              uint32_t buff_size);
+void  (**LINK_mmcamera_jpeg_callback)(jpeg_event_t status);
+void  (**LINK_mmcamera_shutter_callback)(common_crop_t *crop);
+#else
+#define LINK_cam_conf cam_conf
+#define LINK_cam_frame cam_frame
+#define LINK_jpeg_encoder_init jpeg_encoder_init
+#define LINK_jpeg_encoder_join jpeg_encoder_join
+#define LINK_jpeg_encoder_encode jpeg_encoder_encode
+#define LINK_camframe_terminate camframe_terminate
+#define LINK_jpeg_encoder_setMainImageQuality jpeg_encoder_setMainImageQuality
+#define LINK_jpeg_encoder_setThumbnailQuality jpeg_encoder_setThumbnailQuality
+#define LINK_jpeg_encoder_setRotation jpeg_encoder_setRotation
+#define LINK_jpeg_encoder_setLocation jpeg_encoder_setLocation
+#define LINK_default_sensor_get_snapshot_sizes default_sensor_get_snapshot_sizes
+#define LINK_launch_cam_conf_thread launch_cam_conf_thread
+#define LINK_release_cam_conf_thread release_cam_conf_thread
+#define LINK_zoom_crop_upscale zoom_crop_upscale
+extern void (*mmcamera_camframe_callback)(struct msm_frame *frame);
+extern void (*mmcamera_jpegfragment_callback)(uint8_t *buff_ptr,
+                                      uint32_t buff_size);
+extern void (*mmcamera_jpeg_callback)(jpeg_event_t status);
+extern void (*mmcamera_shutter_callback)(common_crop_t *crop);
+#endif
+
+} // extern "C"
+
+#ifndef HAVE_CAMERA_SIZE_TYPE
+struct camera_size_type {
+    int width;
+    int height;
+};
+#endif
+
+#define DEFAULT_PREVIEW_SETTING 2
+static const camera_size_type preview_sizes[] = {
+    { 1280, 720 }, // 720P, reserved
+    { 800, 480 }, // WVGA
+    { 720, 480 },
+    { 640, 480 }, // VGA
+    { 576, 432 },
+    { 480, 320 }, // HVGA
+    { 384, 288 },
+    { 352, 288 }, // CIF
+    { 320, 240 }, // QVGA
+    { 240, 160 }, // SQVGA
+    { 176, 144 }, // QCIF
+};
+#define PREVIEW_SIZE_COUNT (sizeof(preview_sizes)/sizeof(camera_size_type))
+
+static const camera_size_type* picture_sizes;
+static int PICTURE_SIZE_COUNT;
+
+static int attr_lookup(const str_map arr[], int len, const char *name)
+{
+    if (name) {
+        for (int i = 0; i < len; i++) {
+            if (!strcmp(arr[i].desc, name))
+                return arr[i].val;
+        }
+    }
+    return NOT_FOUND;
+}
+
+// round to the next power of two
+static inline unsigned clp2(unsigned x)
+{
+    x = x - 1;
+    x = x | (x >> 1);
+    x = x | (x >> 2);
+    x = x | (x >> 4);
+    x = x | (x >> 8);
+    x = x | (x >>16);
+    return x + 1;
+}
+
+namespace android {
+// from aeecamera.h
+static const str_map whitebalance[] = {
+    { CameraParameters::WHITE_BALANCE_AUTO,            CAMERA_WB_AUTO },
+    { CameraParameters::WHITE_BALANCE_INCANDESCENT,    CAMERA_WB_INCANDESCENT },
+    { CameraParameters::WHITE_BALANCE_FLUORESCENT,     CAMERA_WB_FLUORESCENT },
+    { CameraParameters::WHITE_BALANCE_DAYLIGHT,        CAMERA_WB_DAYLIGHT },
+    { CameraParameters::WHITE_BALANCE_CLOUDY_DAYLIGHT, CAMERA_WB_CLOUDY_DAYLIGHT }
+};
+
+// from camera_effect_t. This list must match aeecamera.h
+static const str_map effects[] = {
+    { CameraParameters::EFFECT_NONE,       CAMERA_EFFECT_OFF },
+    { CameraParameters::EFFECT_MONO,       CAMERA_EFFECT_MONO },
+    { CameraParameters::EFFECT_NEGATIVE,   CAMERA_EFFECT_NEGATIVE },
+    { CameraParameters::EFFECT_SOLARIZE,   CAMERA_EFFECT_SOLARIZE },
+    { CameraParameters::EFFECT_SEPIA,      CAMERA_EFFECT_SEPIA },
+    { CameraParameters::EFFECT_POSTERIZE,  CAMERA_EFFECT_POSTERIZE },
+    { CameraParameters::EFFECT_WHITEBOARD, CAMERA_EFFECT_WHITEBOARD },
+    { CameraParameters::EFFECT_BLACKBOARD, CAMERA_EFFECT_BLACKBOARD },
+    { CameraParameters::EFFECT_AQUA,       CAMERA_EFFECT_AQUA }
+};
+
+// from qcamera/common/camera.h
+static const str_map antibanding[] = {
+    { CameraParameters::ANTIBANDING_OFF,  CAMERA_ANTIBANDING_OFF },
+    { CameraParameters::ANTIBANDING_50HZ, CAMERA_ANTIBANDING_50HZ },
+    { CameraParameters::ANTIBANDING_60HZ, CAMERA_ANTIBANDING_60HZ },
+    { CameraParameters::ANTIBANDING_AUTO, CAMERA_ANTIBANDING_AUTO }
+};
+
+/* Mapping from MCC to antibanding type */
+struct country_map {
+    uint32_t country_code;
+    camera_antibanding_type type;
+};
+
+static struct country_map country_numeric[] = {
+    { 202, CAMERA_ANTIBANDING_50HZ }, // Greece
+    { 204, CAMERA_ANTIBANDING_50HZ }, // Netherlands
+    { 206, CAMERA_ANTIBANDING_50HZ }, // Belgium
+    { 208, CAMERA_ANTIBANDING_50HZ }, // France
+    { 212, CAMERA_ANTIBANDING_50HZ }, // Monaco
+    { 213, CAMERA_ANTIBANDING_50HZ }, // Andorra
+    { 214, CAMERA_ANTIBANDING_50HZ }, // Spain
+    { 216, CAMERA_ANTIBANDING_50HZ }, // Hungary
+    { 219, CAMERA_ANTIBANDING_50HZ }, // Croatia
+    { 220, CAMERA_ANTIBANDING_50HZ }, // Serbia
+    { 222, CAMERA_ANTIBANDING_50HZ }, // Italy
+    { 226, CAMERA_ANTIBANDING_50HZ }, // Romania
+    { 228, CAMERA_ANTIBANDING_50HZ }, // Switzerland
+    { 230, CAMERA_ANTIBANDING_50HZ }, // Czech Republic
+    { 231, CAMERA_ANTIBANDING_50HZ }, // Slovakia
+    { 232, CAMERA_ANTIBANDING_50HZ }, // Austria
+    { 234, CAMERA_ANTIBANDING_50HZ }, // United Kingdom
+    { 235, CAMERA_ANTIBANDING_50HZ }, // United Kingdom
+    { 238, CAMERA_ANTIBANDING_50HZ }, // Denmark
+    { 240, CAMERA_ANTIBANDING_50HZ }, // Sweden
+    { 242, CAMERA_ANTIBANDING_50HZ }, // Norway
+    { 244, CAMERA_ANTIBANDING_50HZ }, // Finland
+    { 246, CAMERA_ANTIBANDING_50HZ }, // Lithuania
+    { 247, CAMERA_ANTIBANDING_50HZ }, // Latvia
+    { 248, CAMERA_ANTIBANDING_50HZ }, // Estonia
+    { 250, CAMERA_ANTIBANDING_50HZ }, // Russian Federation
+    { 255, CAMERA_ANTIBANDING_50HZ }, // Ukraine
+    { 257, CAMERA_ANTIBANDING_50HZ }, // Belarus
+    { 259, CAMERA_ANTIBANDING_50HZ }, // Moldova
+    { 260, CAMERA_ANTIBANDING_50HZ }, // Poland
+    { 262, CAMERA_ANTIBANDING_50HZ }, // Germany
+    { 266, CAMERA_ANTIBANDING_50HZ }, // Gibraltar
+    { 268, CAMERA_ANTIBANDING_50HZ }, // Portugal
+    { 270, CAMERA_ANTIBANDING_50HZ }, // Luxembourg
+    { 272, CAMERA_ANTIBANDING_50HZ }, // Ireland
+    { 274, CAMERA_ANTIBANDING_50HZ }, // Iceland
+    { 276, CAMERA_ANTIBANDING_50HZ }, // Albania
+    { 278, CAMERA_ANTIBANDING_50HZ }, // Malta
+    { 280, CAMERA_ANTIBANDING_50HZ }, // Cyprus
+    { 282, CAMERA_ANTIBANDING_50HZ }, // Georgia
+    { 283, CAMERA_ANTIBANDING_50HZ }, // Armenia
+    { 284, CAMERA_ANTIBANDING_50HZ }, // Bulgaria
+    { 286, CAMERA_ANTIBANDING_50HZ }, // Turkey
+    { 288, CAMERA_ANTIBANDING_50HZ }, // Faroe Islands
+    { 290, CAMERA_ANTIBANDING_50HZ }, // Greenland
+    { 293, CAMERA_ANTIBANDING_50HZ }, // Slovenia
+    { 294, CAMERA_ANTIBANDING_50HZ }, // Macedonia
+    { 295, CAMERA_ANTIBANDING_50HZ }, // Liechtenstein
+    { 297, CAMERA_ANTIBANDING_50HZ }, // Montenegro
+    { 302, CAMERA_ANTIBANDING_60HZ }, // Canada
+    { 310, CAMERA_ANTIBANDING_60HZ }, // United States of America
+    { 311, CAMERA_ANTIBANDING_60HZ }, // United States of America
+    { 312, CAMERA_ANTIBANDING_60HZ }, // United States of America
+    { 313, CAMERA_ANTIBANDING_60HZ }, // United States of America
+    { 314, CAMERA_ANTIBANDING_60HZ }, // United States of America
+    { 315, CAMERA_ANTIBANDING_60HZ }, // United States of America
+    { 316, CAMERA_ANTIBANDING_60HZ }, // United States of America
+    { 330, CAMERA_ANTIBANDING_60HZ }, // Puerto Rico
+    { 334, CAMERA_ANTIBANDING_60HZ }, // Mexico
+    { 338, CAMERA_ANTIBANDING_50HZ }, // Jamaica
+    { 340, CAMERA_ANTIBANDING_50HZ }, // Martinique
+    { 342, CAMERA_ANTIBANDING_50HZ }, // Barbados
+    { 346, CAMERA_ANTIBANDING_60HZ }, // Cayman Islands
+    { 350, CAMERA_ANTIBANDING_60HZ }, // Bermuda
+    { 352, CAMERA_ANTIBANDING_50HZ }, // Grenada
+    { 354, CAMERA_ANTIBANDING_60HZ }, // Montserrat
+    { 362, CAMERA_ANTIBANDING_50HZ }, // Netherlands Antilles
+    { 363, CAMERA_ANTIBANDING_60HZ }, // Aruba
+    { 364, CAMERA_ANTIBANDING_60HZ }, // Bahamas
+    { 365, CAMERA_ANTIBANDING_60HZ }, // Anguilla
+    { 366, CAMERA_ANTIBANDING_50HZ }, // Dominica
+    { 368, CAMERA_ANTIBANDING_60HZ }, // Cuba
+    { 370, CAMERA_ANTIBANDING_60HZ }, // Dominican Republic
+    { 372, CAMERA_ANTIBANDING_60HZ }, // Haiti
+    { 401, CAMERA_ANTIBANDING_50HZ }, // Kazakhstan
+    { 402, CAMERA_ANTIBANDING_50HZ }, // Bhutan
+    { 404, CAMERA_ANTIBANDING_50HZ }, // India
+    { 405, CAMERA_ANTIBANDING_50HZ }, // India
+    { 410, CAMERA_ANTIBANDING_50HZ }, // Pakistan
+    { 413, CAMERA_ANTIBANDING_50HZ }, // Sri Lanka
+    { 414, CAMERA_ANTIBANDING_50HZ }, // Myanmar
+    { 415, CAMERA_ANTIBANDING_50HZ }, // Lebanon
+    { 416, CAMERA_ANTIBANDING_50HZ }, // Jordan
+    { 417, CAMERA_ANTIBANDING_50HZ }, // Syria
+    { 418, CAMERA_ANTIBANDING_50HZ }, // Iraq
+    { 419, CAMERA_ANTIBANDING_50HZ }, // Kuwait
+    { 420, CAMERA_ANTIBANDING_60HZ }, // Saudi Arabia
+    { 421, CAMERA_ANTIBANDING_50HZ }, // Yemen
+    { 422, CAMERA_ANTIBANDING_50HZ }, // Oman
+    { 424, CAMERA_ANTIBANDING_50HZ }, // United Arab Emirates
+    { 425, CAMERA_ANTIBANDING_50HZ }, // Israel
+    { 426, CAMERA_ANTIBANDING_50HZ }, // Bahrain
+    { 427, CAMERA_ANTIBANDING_50HZ }, // Qatar
+    { 428, CAMERA_ANTIBANDING_50HZ }, // Mongolia
+    { 429, CAMERA_ANTIBANDING_50HZ }, // Nepal
+    { 430, CAMERA_ANTIBANDING_50HZ }, // United Arab Emirates
+    { 431, CAMERA_ANTIBANDING_50HZ }, // United Arab Emirates
+    { 432, CAMERA_ANTIBANDING_50HZ }, // Iran
+    { 434, CAMERA_ANTIBANDING_50HZ }, // Uzbekistan
+    { 436, CAMERA_ANTIBANDING_50HZ }, // Tajikistan
+    { 437, CAMERA_ANTIBANDING_50HZ }, // Kyrgyz Rep
+    { 438, CAMERA_ANTIBANDING_50HZ }, // Turkmenistan
+    { 440, CAMERA_ANTIBANDING_60HZ }, // Japan
+    { 441, CAMERA_ANTIBANDING_60HZ }, // Japan
+    { 452, CAMERA_ANTIBANDING_50HZ }, // Vietnam
+    { 454, CAMERA_ANTIBANDING_50HZ }, // Hong Kong
+    { 455, CAMERA_ANTIBANDING_50HZ }, // Macao
+    { 456, CAMERA_ANTIBANDING_50HZ }, // Cambodia
+    { 457, CAMERA_ANTIBANDING_50HZ }, // Laos
+    { 460, CAMERA_ANTIBANDING_50HZ }, // China
+    { 466, CAMERA_ANTIBANDING_60HZ }, // Taiwan
+    { 470, CAMERA_ANTIBANDING_50HZ }, // Bangladesh
+    { 472, CAMERA_ANTIBANDING_50HZ }, // Maldives
+    { 502, CAMERA_ANTIBANDING_50HZ }, // Malaysia
+    { 505, CAMERA_ANTIBANDING_50HZ }, // Australia
+    { 510, CAMERA_ANTIBANDING_50HZ }, // Indonesia
+    { 514, CAMERA_ANTIBANDING_50HZ }, // East Timor
+    { 515, CAMERA_ANTIBANDING_60HZ }, // Philippines
+    { 520, CAMERA_ANTIBANDING_50HZ }, // Thailand
+    { 525, CAMERA_ANTIBANDING_50HZ }, // Singapore
+    { 530, CAMERA_ANTIBANDING_50HZ }, // New Zealand
+    { 535, CAMERA_ANTIBANDING_60HZ }, // Guam
+    { 536, CAMERA_ANTIBANDING_50HZ }, // Nauru
+    { 537, CAMERA_ANTIBANDING_50HZ }, // Papua New Guinea
+    { 539, CAMERA_ANTIBANDING_50HZ }, // Tonga
+    { 541, CAMERA_ANTIBANDING_50HZ }, // Vanuatu
+    { 542, CAMERA_ANTIBANDING_50HZ }, // Fiji
+    { 544, CAMERA_ANTIBANDING_60HZ }, // American Samoa
+    { 545, CAMERA_ANTIBANDING_50HZ }, // Kiribati
+    { 546, CAMERA_ANTIBANDING_50HZ }, // New Caledonia
+    { 548, CAMERA_ANTIBANDING_50HZ }, // Cook Islands
+    { 602, CAMERA_ANTIBANDING_50HZ }, // Egypt
+    { 603, CAMERA_ANTIBANDING_50HZ }, // Algeria
+    { 604, CAMERA_ANTIBANDING_50HZ }, // Morocco
+    { 605, CAMERA_ANTIBANDING_50HZ }, // Tunisia
+    { 606, CAMERA_ANTIBANDING_50HZ }, // Libya
+    { 607, CAMERA_ANTIBANDING_50HZ }, // Gambia
+    { 608, CAMERA_ANTIBANDING_50HZ }, // Senegal
+    { 609, CAMERA_ANTIBANDING_50HZ }, // Mauritania
+    { 610, CAMERA_ANTIBANDING_50HZ }, // Mali
+    { 611, CAMERA_ANTIBANDING_50HZ }, // Guinea
+    { 613, CAMERA_ANTIBANDING_50HZ }, // Burkina Faso
+    { 614, CAMERA_ANTIBANDING_50HZ }, // Niger
+    { 616, CAMERA_ANTIBANDING_50HZ }, // Benin
+    { 617, CAMERA_ANTIBANDING_50HZ }, // Mauritius
+    { 618, CAMERA_ANTIBANDING_50HZ }, // Liberia
+    { 619, CAMERA_ANTIBANDING_50HZ }, // Sierra Leone
+    { 620, CAMERA_ANTIBANDING_50HZ }, // Ghana
+    { 621, CAMERA_ANTIBANDING_50HZ }, // Nigeria
+    { 622, CAMERA_ANTIBANDING_50HZ }, // Chad
+    { 623, CAMERA_ANTIBANDING_50HZ }, // Central African Republic
+    { 624, CAMERA_ANTIBANDING_50HZ }, // Cameroon
+    { 625, CAMERA_ANTIBANDING_50HZ }, // Cape Verde
+    { 627, CAMERA_ANTIBANDING_50HZ }, // Equatorial Guinea
+    { 631, CAMERA_ANTIBANDING_50HZ }, // Angola
+    { 633, CAMERA_ANTIBANDING_50HZ }, // Seychelles
+    { 634, CAMERA_ANTIBANDING_50HZ }, // Sudan
+    { 636, CAMERA_ANTIBANDING_50HZ }, // Ethiopia
+    { 637, CAMERA_ANTIBANDING_50HZ }, // Somalia
+    { 638, CAMERA_ANTIBANDING_50HZ }, // Djibouti
+    { 639, CAMERA_ANTIBANDING_50HZ }, // Kenya
+    { 640, CAMERA_ANTIBANDING_50HZ }, // Tanzania
+    { 641, CAMERA_ANTIBANDING_50HZ }, // Uganda
+    { 642, CAMERA_ANTIBANDING_50HZ }, // Burundi
+    { 643, CAMERA_ANTIBANDING_50HZ }, // Mozambique
+    { 645, CAMERA_ANTIBANDING_50HZ }, // Zambia
+    { 646, CAMERA_ANTIBANDING_50HZ }, // Madagascar
+    { 647, CAMERA_ANTIBANDING_50HZ }, // France
+    { 648, CAMERA_ANTIBANDING_50HZ }, // Zimbabwe
+    { 649, CAMERA_ANTIBANDING_50HZ }, // Namibia
+    { 650, CAMERA_ANTIBANDING_50HZ }, // Malawi
+    { 651, CAMERA_ANTIBANDING_50HZ }, // Lesotho
+    { 652, CAMERA_ANTIBANDING_50HZ }, // Botswana
+    { 653, CAMERA_ANTIBANDING_50HZ }, // Swaziland
+    { 654, CAMERA_ANTIBANDING_50HZ }, // Comoros
+    { 655, CAMERA_ANTIBANDING_50HZ }, // South Africa
+    { 657, CAMERA_ANTIBANDING_50HZ }, // Eritrea
+    { 702, CAMERA_ANTIBANDING_60HZ }, // Belize
+    { 704, CAMERA_ANTIBANDING_60HZ }, // Guatemala
+    { 706, CAMERA_ANTIBANDING_60HZ }, // El Salvador
+    { 708, CAMERA_ANTIBANDING_60HZ }, // Honduras
+    { 710, CAMERA_ANTIBANDING_60HZ }, // Nicaragua
+    { 712, CAMERA_ANTIBANDING_60HZ }, // Costa Rica
+    { 714, CAMERA_ANTIBANDING_60HZ }, // Panama
+    { 722, CAMERA_ANTIBANDING_50HZ }, // Argentina
+    { 724, CAMERA_ANTIBANDING_60HZ }, // Brazil
+    { 730, CAMERA_ANTIBANDING_50HZ }, // Chile
+    { 732, CAMERA_ANTIBANDING_60HZ }, // Colombia
+    { 734, CAMERA_ANTIBANDING_60HZ }, // Venezuela
+    { 736, CAMERA_ANTIBANDING_50HZ }, // Bolivia
+    { 738, CAMERA_ANTIBANDING_60HZ }, // Guyana
+    { 740, CAMERA_ANTIBANDING_60HZ }, // Ecuador
+    { 742, CAMERA_ANTIBANDING_50HZ }, // French Guiana
+    { 744, CAMERA_ANTIBANDING_50HZ }, // Paraguay
+    { 746, CAMERA_ANTIBANDING_60HZ }, // Suriname
+    { 748, CAMERA_ANTIBANDING_50HZ }, // Uruguay
+    { 750, CAMERA_ANTIBANDING_50HZ }, // Falkland Islands
+};
+
+#define country_number (sizeof(country_numeric) / sizeof(country_map))
+
+/* Look up pre-sorted antibanding_type table by current MCC. */
+static camera_antibanding_type camera_get_location(void) {
+    char value[PROP_VALUE_MAX];
+    char country_value[PROP_VALUE_MAX];
+    uint32_t country_code, count;
+    memset(value, 0x00, sizeof(value));
+    memset(country_value, 0x00, sizeof(country_value));
+    if (!__system_property_get("gsm.operator.numeric", value)) {
+        return CAMERA_ANTIBANDING_60HZ;
+    }
+    memcpy(country_value, value, 3);
+    country_code = atoi(country_value);
+    LOGD("value:%s, country value:%s, country code:%d\n",
+            value, country_value, country_code);
+    int left = 0;
+    int right = country_number - 1;
+    while (left <= right) {
+        int index = (left + right) >> 1;
+        if (country_numeric[index].country_code == country_code)
+            return country_numeric[index].type;
+        else if (country_numeric[index].country_code > country_code)
+            right = index - 1;
+        else
+            left = index + 1;
+    }
+    return CAMERA_ANTIBANDING_60HZ;
+}
+
+// from camera.h, led_mode_t
+static const str_map flash[] = {
+    { CameraParameters::FLASH_MODE_OFF,  LED_MODE_OFF },
+    { CameraParameters::FLASH_MODE_AUTO, LED_MODE_AUTO },
+    { CameraParameters::FLASH_MODE_ON, LED_MODE_ON }
+};
+
+#define DONT_CARE 0
+static const str_map focus_modes[] = {
+    { CameraParameters::FOCUS_MODE_AUTO,     DONT_CARE },
+    { CameraParameters::FOCUS_MODE_INFINITY, DONT_CARE }
+};
+
+static bool parameter_string_initialized = false;
+static String8 preview_size_values;
+static String8 picture_size_values;
+static String8 antibanding_values;
+static String8 effect_values;
+static String8 whitebalance_values;
+static String8 flash_values;
+static String8 focus_mode_values;
+
+static String8 create_sizes_str(const camera_size_type *sizes, int len) {
+    String8 str;
+    char buffer[32];
+
+    if (len > 0) {
+        sprintf(buffer, "%dx%d", sizes[0].width, sizes[0].height);
+        str.append(buffer);
+    }
+    for (int i = 1; i < len; i++) {
+        sprintf(buffer, ",%dx%d", sizes[i].width, sizes[i].height);
+        str.append(buffer);
+    }
+    return str;
+}
+
+static String8 create_values_str(const str_map *values, int len) {
+    String8 str;
+
+    if (len > 0) {
+        str.append(values[0].desc);
+    }
+    for (int i = 1; i < len; i++) {
+        str.append(",");
+        str.append(values[i].desc);
+    }
+    return str;
+}
+
+static Mutex singleton_lock;
+static bool singleton_releasing;
+static Condition singleton_wait;
+
+static void receive_camframe_callback(struct msm_frame *frame);
+static void receive_jpeg_fragment_callback(uint8_t *buff_ptr, uint32_t buff_size);
+static void receive_jpeg_callback(jpeg_event_t status);
+static void receive_shutter_callback(common_crop_t *crop);
+
+QualcommCameraHardware::QualcommCameraHardware()
+    : mParameters(),
+      mCameraRunning(false),
+      mPreviewInitialized(false),
+      mFrameThreadRunning(false),
+      mSnapshotThreadRunning(false),
+      mReleasedRecordingFrame(false),
+      mPreviewFrameSize(0),
+      mRawSize(0),
+      mCameraControlFd(-1),
+      mAutoFocusThreadRunning(false),
+      mAutoFocusFd(-1),
+      mInPreviewCallback(false),
+      mMsgEnabled(0),
+      mNotifyCallback(0),
+      mDataCallback(0),
+      mDataCallbackTimestamp(0),
+      mCallbackCookie(0)
+{
+    memset(&mDimension, 0, sizeof(mDimension));
+    memset(&mCrop, 0, sizeof(mCrop));
+    LOGV("constructor EX");
+}
+
+void QualcommCameraHardware::initDefaultParameters()
+{
+    LOGV("initDefaultParameters E");
+
+    // Initialize constant parameter strings. This will happen only once in the
+    // lifetime of the mediaserver process.
+    if (!parameter_string_initialized) {
+        antibanding_values = create_values_str(
+            antibanding, sizeof(antibanding) / sizeof(str_map));
+        effect_values = create_values_str(
+            effects, sizeof(effects) / sizeof(str_map));
+        whitebalance_values = create_values_str(
+            whitebalance, sizeof(whitebalance) / sizeof(str_map));
+        preview_size_values = create_sizes_str(
+            preview_sizes, PREVIEW_SIZE_COUNT);
+        picture_size_values = create_sizes_str(
+            picture_sizes, PICTURE_SIZE_COUNT);
+        flash_values = create_values_str(
+            flash, sizeof(flash) / sizeof(str_map));
+        focus_mode_values = create_values_str(
+            focus_modes, sizeof(focus_modes) / sizeof(str_map));
+        parameter_string_initialized = true;
+    }
+
+    const camera_size_type *ps = &preview_sizes[DEFAULT_PREVIEW_SETTING];
+    mParameters.setPreviewSize(ps->width, ps->height);
+    mDimension.display_width = ps->width;
+    mDimension.display_height = ps->height;
+    mParameters.setPreviewFrameRate(15);
+    mParameters.setPreviewFormat("yuv420sp"); // informative
+
+    mParameters.setPictureSize(DEFAULT_PICTURE_WIDTH, DEFAULT_PICTURE_HEIGHT);
+    mParameters.setPictureFormat("jpeg"); // informative
+
+    mParameters.set(CameraParameters::KEY_JPEG_QUALITY, "100"); // max quality
+    mParameters.set(CameraParameters::KEY_JPEG_THUMBNAIL_WIDTH,
+                    THUMBNAIL_WIDTH_STR); // informative
+    mParameters.set(CameraParameters::KEY_JPEG_THUMBNAIL_HEIGHT,
+                    THUMBNAIL_HEIGHT_STR); // informative
+    mDimension.ui_thumbnail_width = THUMBNAIL_WIDTH;
+    mDimension.ui_thumbnail_height = THUMBNAIL_HEIGHT;
+    mParameters.set(CameraParameters::KEY_JPEG_THUMBNAIL_QUALITY, "90");
+
+    mParameters.set(CameraParameters::KEY_ANTIBANDING,
+                    CameraParameters::ANTIBANDING_AUTO);
+    mParameters.set(CameraParameters::KEY_EFFECT,
+                    CameraParameters::EFFECT_NONE);
+    mParameters.set(CameraParameters::KEY_WHITE_BALANCE,
+                    CameraParameters::WHITE_BALANCE_AUTO);
+    mParameters.set(CameraParameters::KEY_FOCUS_MODE,
+                    CameraParameters::FOCUS_MODE_AUTO);
+
+    mParameters.set(CameraParameters::KEY_SUPPORTED_PREVIEW_SIZES,
+                    preview_size_values.string());
+    mParameters.set(CameraParameters::KEY_SUPPORTED_PICTURE_SIZES,
+                    picture_size_values.string());
+    mParameters.set(CameraParameters::KEY_SUPPORTED_ANTIBANDING,
+                    antibanding_values);
+    mParameters.set(CameraParameters::KEY_SUPPORTED_EFFECTS, effect_values);
+    mParameters.set(CameraParameters::KEY_SUPPORTED_WHITE_BALANCE,
+                    whitebalance_values);
+    mParameters.set(CameraParameters::KEY_SUPPORTED_FOCUS_MODES,
+                    focus_mode_values);
+
+    if (mSensorInfo.flash_enabled) {
+        mParameters.set(CameraParameters::KEY_FLASH_MODE,
+                        CameraParameters::FLASH_MODE_OFF);
+        mParameters.set(CameraParameters::KEY_SUPPORTED_FLASH_MODES,
+                        flash_values);
+    }
+
+    mParameters.set("zoom-supported", "true");
+    mParameters.set("max-zoom", MAX_ZOOM_LEVEL);
+    mParameters.set("zoom", 0);
+
+    if (setParameters(mParameters) != NO_ERROR) {
+        LOGE("Failed to set default parameters?!");
+    }
+
+    LOGV("initDefaultParameters X");
+}
+
+#define ROUND_TO_PAGE(x)  (((x)+0xfff)&~0xfff)
+
+bool QualcommCameraHardware::startCamera()
+{
+    LOGV("startCamera E");
+#if DLOPEN_LIBMMCAMERA
+    libmmcamera = ::dlopen("liboemcamera.so", RTLD_NOW);
+    LOGV("loading liboemcamera at %p", libmmcamera);
+    if (!libmmcamera) {
+        LOGE("FATAL ERROR: could not dlopen liboemcamera.so: %s", dlerror());
+        return false;
+    }
+
+    *(void **)&LINK_cam_frame =
+        ::dlsym(libmmcamera, "cam_frame");
+    *(void **)&LINK_camframe_terminate =
+        ::dlsym(libmmcamera, "camframe_terminate");
+
+    *(void **)&LINK_jpeg_encoder_init =
+        ::dlsym(libmmcamera, "jpeg_encoder_init");
+
+    *(void **)&LINK_jpeg_encoder_encode =
+        ::dlsym(libmmcamera, "jpeg_encoder_encode");
+
+    *(void **)&LINK_jpeg_encoder_join =
+        ::dlsym(libmmcamera, "jpeg_encoder_join");
+
+    *(void **)&LINK_mmcamera_camframe_callback =
+        ::dlsym(libmmcamera, "mmcamera_camframe_callback");
+
+    *LINK_mmcamera_camframe_callback = receive_camframe_callback;
+
+    *(void **)&LINK_mmcamera_jpegfragment_callback =
+        ::dlsym(libmmcamera, "mmcamera_jpegfragment_callback");
+
+    *LINK_mmcamera_jpegfragment_callback = receive_jpeg_fragment_callback;
+
+    *(void **)&LINK_mmcamera_jpeg_callback =
+        ::dlsym(libmmcamera, "mmcamera_jpeg_callback");
+
+    *LINK_mmcamera_jpeg_callback = receive_jpeg_callback;
+
+    *(void **)&LINK_mmcamera_shutter_callback =
+        ::dlsym(libmmcamera, "mmcamera_shutter_callback");
+
+    *LINK_mmcamera_shutter_callback = receive_shutter_callback;
+
+    *(void**)&LINK_jpeg_encoder_setMainImageQuality =
+        ::dlsym(libmmcamera, "jpeg_encoder_setMainImageQuality");
+
+    *(void**)&LINK_jpeg_encoder_setThumbnailQuality =
+        ::dlsym(libmmcamera, "jpeg_encoder_setThumbnailQuality");
+
+    *(void**)&LINK_jpeg_encoder_setRotation =
+        ::dlsym(libmmcamera, "jpeg_encoder_setRotation");
+
+    *(void**)&LINK_jpeg_encoder_setLocation =
+        ::dlsym(libmmcamera, "jpeg_encoder_setLocation");
+
+    *(void **)&LINK_cam_conf =
+        ::dlsym(libmmcamera, "cam_conf");
+
+    *(void **)&LINK_default_sensor_get_snapshot_sizes =
+        ::dlsym(libmmcamera, "default_sensor_get_snapshot_sizes");
+
+    *(void **)&LINK_launch_cam_conf_thread =
+        ::dlsym(libmmcamera, "launch_cam_conf_thread");
+
+    *(void **)&LINK_release_cam_conf_thread =
+        ::dlsym(libmmcamera, "release_cam_conf_thread");
+
+    *(void **)&LINK_zoom_crop_upscale =
+        ::dlsym(libmmcamera, "zoom_crop_upscale");
+
+#else
+    mmcamera_camframe_callback = receive_camframe_callback;
+    mmcamera_jpegfragment_callback = receive_jpeg_fragment_callback;
+    mmcamera_jpeg_callback = receive_jpeg_callback;
+    mmcamera_shutter_callback = receive_shutter_callback;
+#endif // DLOPEN_LIBMMCAMERA
+
+    /* The control thread is in libcamera itself. */
+    mCameraControlFd = open(MSM_CAMERA_CONTROL, O_RDWR);
+    if (mCameraControlFd < 0) {
+        LOGE("startCamera X: %s open failed: %s!",
+             MSM_CAMERA_CONTROL,
+             strerror(errno));
+        return false;
+    }
+
+    /* This will block until the control thread is launched. After that, sensor
+     * information becomes available.
+     */
+
+    if (LINK_launch_cam_conf_thread()) {
+        LOGE("failed to launch the camera config thread");
+        return false;
+    }
+
+    memset(&mSensorInfo, 0, sizeof(mSensorInfo));
+    if (ioctl(mCameraControlFd,
+              MSM_CAM_IOCTL_GET_SENSOR_INFO,
+              &mSensorInfo) < 0)
+        LOGW("%s: cannot retrieve sensor info!", __FUNCTION__);
+    else
+        LOGI("%s: camsensor name %s, flash %d", __FUNCTION__,
+             mSensorInfo.name, mSensorInfo.flash_enabled);
+
+    picture_sizes = LINK_default_sensor_get_snapshot_sizes(&PICTURE_SIZE_COUNT);
+    if (!picture_sizes || !PICTURE_SIZE_COUNT) {
+        LOGE("startCamera X: could not get snapshot sizes");
+        return false;
+    }
+
+    LOGV("startCamera X");
+    return true;
+}
+
+status_t QualcommCameraHardware::dump(int fd,
+                                      const Vector<String16>& args) const
+{
+    const size_t SIZE = 256;
+    char buffer[SIZE];
+    String8 result;
+
+    // Dump internal primitives.
+    result.append("QualcommCameraHardware::dump");
+    snprintf(buffer, 255, "mMsgEnabled (%d)\n", mMsgEnabled);
+    result.append(buffer);
+    int width, height;
+    mParameters.getPreviewSize(&width, &height);
+    snprintf(buffer, 255, "preview width(%d) x height (%d)\n", width, height);
+    result.append(buffer);
+    mParameters.getPictureSize(&width, &height);
+    snprintf(buffer, 255, "raw width(%d) x height (%d)\n", width, height);
+    result.append(buffer);
+    snprintf(buffer, 255,
+             "preview frame size(%d), raw size (%d), jpeg size (%d) "
+             "and jpeg max size (%d)\n", mPreviewFrameSize, mRawSize,
+             mJpegSize, mJpegMaxSize);
+    result.append(buffer);
+    write(fd, result.string(), result.size());
+
+    // Dump internal objects.
+    if (mPreviewHeap != 0) {
+        mPreviewHeap->dump(fd, args);
+    }
+    if (mRawHeap != 0) {
+        mRawHeap->dump(fd, args);
+    }
+    if (mJpegHeap != 0) {
+        mJpegHeap->dump(fd, args);
+    }
+    mParameters.dump(fd, args);
+    return NO_ERROR;
+}
+
+static bool native_set_afmode(int camfd, isp3a_af_mode_t af_type)
+{
+    int rc;
+    struct msm_ctrl_cmd ctrlCmd;
+
+    ctrlCmd.timeout_ms = 5000;
+    ctrlCmd.type = CAMERA_SET_PARM_AUTO_FOCUS;
+    ctrlCmd.length = sizeof(af_type);
+    ctrlCmd.value = &af_type;
+    ctrlCmd.resp_fd = camfd; // FIXME: this will be put in by the kernel
+
+    if ((rc = ioctl(camfd, MSM_CAM_IOCTL_CTRL_COMMAND, &ctrlCmd)) < 0)
+        LOGE("native_set_afmode: ioctl fd %d error %s\n",
+             camfd,
+             strerror(errno));
+
+    LOGV("native_set_afmode: ctrlCmd.status == %d\n", ctrlCmd.status);
+    return rc >= 0 && ctrlCmd.status == CAMERA_EXIT_CB_DONE;
+}
+
+static bool native_cancel_afmode(int camfd, int af_fd)
+{
+    int rc;
+    struct msm_ctrl_cmd ctrlCmd;
+
+    ctrlCmd.timeout_ms = 0;
+    ctrlCmd.type = CAMERA_AUTO_FOCUS_CANCEL;
+    ctrlCmd.length = 0;
+    ctrlCmd.value = NULL;
+    ctrlCmd.resp_fd = -1; // there's no response fd
+
+    if ((rc = ioctl(camfd, MSM_CAM_IOCTL_CTRL_COMMAND_2, &ctrlCmd)) < 0)
+    {
+        LOGE("native_cancel_afmode: ioctl fd %d error %s\n",
+             camfd,
+             strerror(errno));
+        return false;
+    }
+
+    return true;
+}
+
+static bool native_start_preview(int camfd)
+{
+    struct msm_ctrl_cmd ctrlCmd;
+
+    ctrlCmd.timeout_ms = 5000;
+    ctrlCmd.type       = CAMERA_START_PREVIEW;
+    ctrlCmd.length     = 0;
+    ctrlCmd.resp_fd    = camfd; // FIXME: this will be put in by the kernel
+
+    if (ioctl(camfd, MSM_CAM_IOCTL_CTRL_COMMAND, &ctrlCmd) < 0) {
+        LOGE("native_start_preview: MSM_CAM_IOCTL_CTRL_COMMAND fd %d error %s",
+             camfd,
+             strerror(errno));
+        return false;
+    }
+
+    return true;
+}
+
+static bool native_get_picture (int camfd, common_crop_t *crop)
+{
+    struct msm_ctrl_cmd ctrlCmd;
+
+    ctrlCmd.timeout_ms = 5000;
+    ctrlCmd.length     = sizeof(common_crop_t);
+    ctrlCmd.value      = crop;
+
+    if(ioctl(camfd, MSM_CAM_IOCTL_GET_PICTURE, &ctrlCmd) < 0) {
+        LOGE("native_get_picture: MSM_CAM_IOCTL_GET_PICTURE fd %d error %s",
+             camfd,
+             strerror(errno));
+        return false;
+    }
+
+    LOGV("crop: in1_w %d", crop->in1_w);
+    LOGV("crop: in1_h %d", crop->in1_h);
+    LOGV("crop: out1_w %d", crop->out1_w);
+    LOGV("crop: out1_h %d", crop->out1_h);
+
+    LOGV("crop: in2_w %d", crop->in2_w);
+    LOGV("crop: in2_h %d", crop->in2_h);
+    LOGV("crop: out2_w %d", crop->out2_w);
+    LOGV("crop: out2_h %d", crop->out2_h);
+
+    LOGV("crop: update %d", crop->update_flag);
+
+    return true;
+}
+
+static bool native_stop_preview(int camfd)
+{
+    struct msm_ctrl_cmd ctrlCmd;
+    ctrlCmd.timeout_ms = 5000;
+    ctrlCmd.type       = CAMERA_STOP_PREVIEW;
+    ctrlCmd.length     = 0;
+    ctrlCmd.resp_fd    = camfd; // FIXME: this will be put in by the kernel
+
+    if(ioctl(camfd, MSM_CAM_IOCTL_CTRL_COMMAND, &ctrlCmd) < 0) {
+        LOGE("native_stop_preview: ioctl fd %d error %s",
+             camfd,
+             strerror(errno));
+        return false;
+    }
+
+    return true;
+}
+
+static bool native_prepare_snapshot(int camfd)
+{
+    int ioctlRetVal = true;
+    struct msm_ctrl_cmd ctrlCmd;
+
+    ctrlCmd.timeout_ms = 1000;
+    ctrlCmd.type       = CAMERA_PREPARE_SNAPSHOT;
+    ctrlCmd.length     = 0;
+    ctrlCmd.value      = NULL;
+    ctrlCmd.resp_fd = camfd;
+
+    if (ioctl(camfd, MSM_CAM_IOCTL_CTRL_COMMAND, &ctrlCmd) < 0) {
+        LOGE("native_prepare_snapshot: ioctl fd %d error %s",
+             camfd,
+             strerror(errno));
+        return false;
+    }
+    return true;
+}
+
+static bool native_start_snapshot(int camfd)
+{
+    struct msm_ctrl_cmd ctrlCmd;
+
+    ctrlCmd.timeout_ms = 5000;
+    ctrlCmd.type       = CAMERA_START_SNAPSHOT;
+    ctrlCmd.length     = 0;
+    ctrlCmd.resp_fd    = camfd; // FIXME: this will be put in by the kernel
+
+    if(ioctl(camfd, MSM_CAM_IOCTL_CTRL_COMMAND, &ctrlCmd) < 0) {
+        LOGE("native_start_snapshot: ioctl fd %d error %s",
+             camfd,
+             strerror(errno));
+        return false;
+    }
+
+    return true;
+}
+
+static bool native_stop_snapshot (int camfd)
+{
+    struct msm_ctrl_cmd ctrlCmd;
+
+    ctrlCmd.timeout_ms = 0;
+    ctrlCmd.type       = CAMERA_STOP_SNAPSHOT;
+    ctrlCmd.length     = 0;
+    ctrlCmd.resp_fd    = -1;
+
+    if (ioctl(camfd, MSM_CAM_IOCTL_CTRL_COMMAND_2, &ctrlCmd) < 0) {
+        LOGE("native_stop_snapshot: ioctl fd %d error %s",
+             camfd,
+             strerror(errno));
+        return false;
+    }
+
+    return true;
+}
+
+bool QualcommCameraHardware::native_jpeg_encode(void)
+{
+    int jpeg_quality = mParameters.getInt("jpeg-quality");
+    if (jpeg_quality >= 0) {
+        LOGV("native_jpeg_encode, current jpeg main img quality =%d",
+             jpeg_quality);
+        if(!LINK_jpeg_encoder_setMainImageQuality(jpeg_quality)) {
+            LOGE("native_jpeg_encode set jpeg-quality failed");
+            return false;
+        }
+    }
+
+    int thumbnail_quality = mParameters.getInt("jpeg-thumbnail-quality");
+    if (thumbnail_quality >= 0) {
+        LOGV("native_jpeg_encode, current jpeg thumbnail quality =%d",
+             thumbnail_quality);
+        if(!LINK_jpeg_encoder_setThumbnailQuality(thumbnail_quality)) {
+            LOGE("native_jpeg_encode set thumbnail-quality failed");
+            return false;
+        }
+    }
+
+    int rotation = mParameters.getInt("rotation");
+    if (rotation >= 0) {
+        LOGV("native_jpeg_encode, rotation = %d", rotation);
+        if(!LINK_jpeg_encoder_setRotation(rotation)) {
+            LOGE("native_jpeg_encode set rotation failed");
+            return false;
+        }
+    }
+
+    jpeg_set_location();
+
+    if (!LINK_jpeg_encoder_encode(&mDimension,
+                                  (uint8_t *)mThumbnailHeap->mHeap->base(),
+                                  mThumbnailHeap->mHeap->getHeapID(),
+                                  (uint8_t *)mRawHeap->mHeap->base(),
+                                  mRawHeap->mHeap->getHeapID(),
+                                  &mCrop)) {
+        LOGE("native_jpeg_encode: jpeg_encoder_encode failed.");
+        return false;
+    }
+    return true;
+}
+
+bool QualcommCameraHardware::native_set_parm(
+    cam_ctrl_type type, uint16_t length, void *value)
+{
+    struct msm_ctrl_cmd ctrlCmd;
+
+    ctrlCmd.timeout_ms = 5000;
+    ctrlCmd.type       = (uint16_t)type;
+    ctrlCmd.length     = length;
+    // FIXME: this will be put in by the kernel
+    ctrlCmd.resp_fd    = mCameraControlFd;
+    ctrlCmd.value = value;
+
+    LOGV("%s: fd %d, type %d, length %d", __FUNCTION__,
+         mCameraControlFd, type, length);
+    if (ioctl(mCameraControlFd, MSM_CAM_IOCTL_CTRL_COMMAND, &ctrlCmd) < 0 ||
+                ctrlCmd.status != CAM_CTRL_SUCCESS) {
+        LOGE("%s: error (%s): fd %d, type %d, length %d, status %d",
+             __FUNCTION__, strerror(errno),
+             mCameraControlFd, type, length, ctrlCmd.status);
+        return false;
+    }
+    return true;
+}
+
+void QualcommCameraHardware::jpeg_set_location()
+{
+    bool encode_location = true;
+    camera_position_type pt;
+
+#define PARSE_LOCATION(what,type,fmt,desc) do {                                \
+        pt.what = 0;                                                           \
+        const char *what##_str = mParameters.get("gps-"#what);                 \
+        LOGV("GPS PARM %s --> [%s]", "gps-"#what, what##_str);                 \
+        if (what##_str) {                                                      \
+            type what = 0;                                                     \
+            if (sscanf(what##_str, fmt, &what) == 1)                           \
+                pt.what = what;                                                \
+            else {                                                             \
+                LOGE("GPS " #what " %s could not"                              \
+                     " be parsed as a " #desc, what##_str);                    \
+                encode_location = false;                                       \
+            }                                                                  \
+        }                                                                      \
+        else {                                                                 \
+            LOGV("GPS " #what " not specified: "                               \
+                 "defaulting to zero in EXIF header.");                        \
+            encode_location = false;                                           \
+       }                                                                       \
+    } while(0)
+
+    PARSE_LOCATION(timestamp, long, "%ld", "long");
+    if (!pt.timestamp) pt.timestamp = time(NULL);
+    PARSE_LOCATION(altitude, short, "%hd", "short");
+    PARSE_LOCATION(latitude, double, "%lf", "double float");
+    PARSE_LOCATION(longitude, double, "%lf", "double float");
+
+#undef PARSE_LOCATION
+
+    if (encode_location) {
+        LOGD("setting image location ALT %d LAT %lf LON %lf",
+             pt.altitude, pt.latitude, pt.longitude);
+        if (!LINK_jpeg_encoder_setLocation(&pt)) {
+            LOGE("jpeg_set_location: LINK_jpeg_encoder_setLocation failed.");
+        }
+    }
+    else LOGV("not setting image location");
+}
+
+void QualcommCameraHardware::runFrameThread(void *data)
+{
+    LOGV("runFrameThread E");
+
+    int cnt;
+
+#if DLOPEN_LIBMMCAMERA
+    // We need to maintain a reference to libqcamera.so for the duration of the
+    // frame thread, because we do not know when it will exit relative to the
+    // lifetime of this object.  We do not want to dlclose() libqcamera while
+    // LINK_cam_frame is still running.
+    void *libhandle = ::dlopen("liboemcamera.so", RTLD_NOW);
+    LOGV("FRAME: loading libqcamera at %p", libhandle);
+    if (!libhandle) {
+        LOGE("FATAL ERROR: could not dlopen liboemcamera.so: %s", dlerror());
+    }
+    if (libhandle)
+#endif
+    {
+        LINK_cam_frame(data);
+    }
+
+    mPreviewHeap.clear();
+
+#if DLOPEN_LIBMMCAMERA
+    if (libhandle) {
+        ::dlclose(libhandle);
+        LOGV("FRAME: dlclose(libqcamera)");
+    }
+#endif
+
+    mFrameThreadWaitLock.lock();
+    mFrameThreadRunning = false;
+    mFrameThreadWait.signal();
+    mFrameThreadWaitLock.unlock();
+
+    LOGV("runFrameThread X");
+}
+
+void *frame_thread(void *user)
+{
+    LOGD("frame_thread E");
+    sp<QualcommCameraHardware> obj = QualcommCameraHardware::getInstance();
+    if (obj != 0) {
+        obj->runFrameThread(user);
+    }
+    else LOGW("not starting frame thread: the object went away!");
+    LOGD("frame_thread X");
+    return NULL;
+}
+
+bool QualcommCameraHardware::initPreview()
+{
+    // See comments in deinitPreview() for why we have to wait for the frame
+    // thread here, and why we can't use pthread_join().
+    int previewWidth, previewHeight;
+    mParameters.getPreviewSize(&previewWidth, &previewHeight);
+    LOGI("initPreview E: preview size=%dx%d", previewWidth, previewHeight);
+    mFrameThreadWaitLock.lock();
+    while (mFrameThreadRunning) {
+        LOGV("initPreview: waiting for old frame thread to complete.");
+        mFrameThreadWait.wait(mFrameThreadWaitLock);
+        LOGV("initPreview: old frame thread completed.");
+    }
+    mFrameThreadWaitLock.unlock();
+
+    mSnapshotThreadWaitLock.lock();
+    while (mSnapshotThreadRunning) {
+        LOGV("initPreview: waiting for old snapshot thread to complete.");
+        mSnapshotThreadWait.wait(mSnapshotThreadWaitLock);
+        LOGV("initPreview: old snapshot thread completed.");
+    }
+    mSnapshotThreadWaitLock.unlock();
+
+    int cnt = 0;
+    mPreviewFrameSize = previewWidth * previewHeight * 3/2;
+    mPreviewHeap = new PmemPool("/dev/pmem_adsp",
+                                MemoryHeapBase::READ_ONLY | MemoryHeapBase::NO_CACHING,
+                                mCameraControlFd,
+                                MSM_PMEM_OUTPUT2,
+                                mPreviewFrameSize,
+                                kPreviewBufferCount,
+                                mPreviewFrameSize,
+                                "preview");
+
+    if (!mPreviewHeap->initialized()) {
+        mPreviewHeap.clear();
+        LOGE("initPreview X: could not initialize preview heap.");
+        return false;
+    }
+
+    // mDimension will be filled with thumbnail_width, thumbnail_height,
+    // orig_picture_dx, and orig_picture_dy after this function call. We need to
+    // keep it for jpeg_encoder_encode.
+    bool ret = native_set_parm(CAMERA_SET_PARM_DIMENSION,
+                               sizeof(cam_ctrl_dimension_t), &mDimension);
+
+    if (ret) {
+        for (cnt = 0; cnt < kPreviewBufferCount; cnt++) {
+            frames[cnt].fd = mPreviewHeap->mHeap->getHeapID();
+            frames[cnt].buffer =
+                (uint32_t)mPreviewHeap->mHeap->base() + mPreviewHeap->mAlignedBufferSize * cnt;
+            frames[cnt].y_off = 0;
+            frames[cnt].cbcr_off = previewWidth * previewHeight;
+            frames[cnt].path = MSM_FRAME_ENC;
+        }
+
+        mFrameThreadWaitLock.lock();
+        pthread_attr_t attr;
+        pthread_attr_init(&attr);
+        pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED);
+        mFrameThreadRunning = !pthread_create(&mFrameThread,
+                                              &attr,
+                                              frame_thread,
+                                              &frames[kPreviewBufferCount-1]);
+        ret = mFrameThreadRunning;
+        mFrameThreadWaitLock.unlock();
+    }
+
+    LOGV("initPreview X: %d", ret);
+    return ret;
+}
+
+void QualcommCameraHardware::deinitPreview(void)
+{
+    LOGI("deinitPreview E");
+
+    // When we call deinitPreview(), we signal to the frame thread that it
+    // needs to exit, but we DO NOT WAIT for it to complete here.  The problem
+    // is that deinitPreview is sometimes called from the frame-thread's
+    // callback, when the refcount on the Camera client reaches zero.  If we
+    // called pthread_join(), we would deadlock.  So, we just call
+    // LINK_camframe_terminate() in deinitPreview(), which makes sure that
+    // after the preview callback returns, the camframe thread will exit.  We
+    // could call pthread_join() in initPreview() to join the last frame
+    // thread.  However, we would also have to call pthread_join() in release
+    // as well, shortly before we destroy the object; this would cause the same
+    // deadlock, since release(), like deinitPreview(), may also be called from
+    // the frame-thread's callback.  This we have to make the frame thread
+    // detached, and use a separate mechanism to wait for it to complete.
+
+    if (LINK_camframe_terminate() < 0)
+        LOGE("failed to stop the camframe thread: %s",
+             strerror(errno));
+    LOGI("deinitPreview X");
+}
+
+bool QualcommCameraHardware::initRaw(bool initJpegHeap)
+{
+    int rawWidth, rawHeight;
+    mParameters.getPictureSize(&rawWidth, &rawHeight);
+    LOGV("initRaw E: picture size=%dx%d", rawWidth, rawHeight);
+
+    // mDimension will be filled with thumbnail_width, thumbnail_height,
+    // orig_picture_dx, and orig_picture_dy after this function call. We need to
+    // keep it for jpeg_encoder_encode.
+    bool ret = native_set_parm(CAMERA_SET_PARM_DIMENSION,
+                               sizeof(cam_ctrl_dimension_t), &mDimension);
+    if(!ret) {
+        LOGE("initRaw X: failed to set dimension");
+        return false;
+    }
+
+    if (mJpegHeap != NULL) {
+        LOGV("initRaw: clearing old mJpegHeap.");
+        mJpegHeap.clear();
+    }
+
+    // Snapshot
+    mRawSize = rawWidth * rawHeight * 3 / 2;
+    mJpegMaxSize = rawWidth * rawHeight * 3 / 2;
+
+    LOGV("initRaw: initializing mRawHeap.");
+    mRawHeap =
+        new PmemPool("/dev/pmem_camera",
+                     MemoryHeapBase::READ_ONLY,
+                     mCameraControlFd,
+                     MSM_PMEM_MAINIMG,
+                     mJpegMaxSize,
+                     kRawBufferCount,
+                     mRawSize,
+                     "snapshot camera");
+
+    if (!mRawHeap->initialized()) {
+        LOGE("initRaw X failed with pmem_camera, trying with pmem_adsp");
+        mRawHeap =
+            new PmemPool("/dev/pmem_adsp",
+                         MemoryHeapBase::READ_ONLY,
+                         mCameraControlFd,
+                         MSM_PMEM_MAINIMG,
+                         mJpegMaxSize,
+                         kRawBufferCount,
+                         mRawSize,
+                         "snapshot camera");
+        if (!mRawHeap->initialized()) {
+            mRawHeap.clear();
+            LOGE("initRaw X: error initializing mRawHeap");
+            return false;
+        }
+    }
+
+    LOGV("do_mmap snapshot pbuf = %p, pmem_fd = %d",
+         (uint8_t *)mRawHeap->mHeap->base(), mRawHeap->mHeap->getHeapID());
+
+    // Jpeg
+
+    if (initJpegHeap) {
+        LOGV("initRaw: initializing mJpegHeap.");
+        mJpegHeap =
+            new AshmemPool(mJpegMaxSize,
+                           kJpegBufferCount,
+                           0, // we do not know how big the picture will be
+                           "jpeg");
+
+        if (!mJpegHeap->initialized()) {
+            mJpegHeap.clear();
+            mRawHeap.clear();
+            LOGE("initRaw X failed: error initializing mJpegHeap.");
+            return false;
+        }
+
+        // Thumbnails
+
+        mThumbnailHeap =
+            new PmemPool("/dev/pmem_adsp",
+                         MemoryHeapBase::READ_ONLY,
+                         mCameraControlFd,
+                         MSM_PMEM_THUMBNAIL,
+                         THUMBNAIL_BUFFER_SIZE,
+                         1,
+                         THUMBNAIL_BUFFER_SIZE,
+                         "thumbnail");
+
+        if (!mThumbnailHeap->initialized()) {
+            mThumbnailHeap.clear();
+            mJpegHeap.clear();
+            mRawHeap.clear();
+            LOGE("initRaw X failed: error initializing mThumbnailHeap.");
+            return false;
+        }
+    }
+
+    LOGV("initRaw X");
+    return true;
+}
+
+void QualcommCameraHardware::deinitRaw()
+{
+    LOGV("deinitRaw E");
+
+    mThumbnailHeap.clear();
+    mJpegHeap.clear();
+    mRawHeap.clear();
+    mDisplayHeap.clear();
+
+    LOGV("deinitRaw X");
+}
+
+void QualcommCameraHardware::release()
+{
+    LOGD("release E");
+    Mutex::Autolock l(&mLock);
+
+#if DLOPEN_LIBMMCAMERA
+    if (libmmcamera == NULL) {
+        LOGE("ERROR: multiple release!");
+        return;
+    }
+#else
+#warning "Cannot detect multiple release when not dlopen()ing libqcamera!"
+#endif
+
+    int cnt, rc;
+    struct msm_ctrl_cmd ctrlCmd;
+
+    if (mCameraRunning) {
+        if(mDataCallbackTimestamp && (mMsgEnabled & CAMERA_MSG_VIDEO_FRAME)) {
+            mRecordFrameLock.lock();
+            mReleasedRecordingFrame = true;
+            mRecordWait.signal();
+            mRecordFrameLock.unlock();
+        }
+        stopPreviewInternal();
+    }
+
+    LINK_jpeg_encoder_join();
+    deinitRaw();
+
+    ctrlCmd.timeout_ms = 5000;
+    ctrlCmd.length = 0;
+    ctrlCmd.type = (uint16_t)CAMERA_EXIT;
+    ctrlCmd.resp_fd = mCameraControlFd; // FIXME: this will be put in by the kernel
+    if (ioctl(mCameraControlFd, MSM_CAM_IOCTL_CTRL_COMMAND, &ctrlCmd) < 0)
+        LOGE("ioctl CAMERA_EXIT fd %d error %s",
+             mCameraControlFd, strerror(errno));
+
+    LINK_release_cam_conf_thread();
+
+    close(mCameraControlFd);
+    mCameraControlFd = -1;
+
+#if DLOPEN_LIBMMCAMERA
+    if (libmmcamera) {
+        ::dlclose(libmmcamera);
+        LOGV("dlclose(libqcamera)");
+        libmmcamera = NULL;
+    }
+#endif
+
+    Mutex::Autolock lock(&singleton_lock);
+    singleton_releasing = true;
+
+    LOGD("release X");
+}
+
+QualcommCameraHardware::~QualcommCameraHardware()
+{
+    LOGD("~QualcommCameraHardware E");
+    Mutex::Autolock lock(&singleton_lock);
+    singleton.clear();
+    singleton_releasing = false;
+    singleton_wait.signal();
+    LOGD("~QualcommCameraHardware X");
+}
+
+sp<IMemoryHeap> QualcommCameraHardware::getRawHeap() const
+{
+    LOGV("getRawHeap");
+    return mDisplayHeap != NULL ? mDisplayHeap->mHeap : NULL;
+}
+
+sp<IMemoryHeap> QualcommCameraHardware::getPreviewHeap() const
+{
+    LOGV("getPreviewHeap");
+    return mPreviewHeap != NULL ? mPreviewHeap->mHeap : NULL;
+}
+
+status_t QualcommCameraHardware::startPreviewInternal()
+{
+    if(mCameraRunning) {
+        LOGV("startPreview X: preview already running.");
+        return NO_ERROR;
+    }
+
+    if (!mPreviewInitialized) {
+        mPreviewInitialized = initPreview();
+        if (!mPreviewInitialized) {
+            LOGE("startPreview X initPreview failed.  Not starting preview.");
+            return UNKNOWN_ERROR;
+        }
+    }
+
+    mCameraRunning = native_start_preview(mCameraControlFd);
+    if(!mCameraRunning) {
+        deinitPreview();
+        mPreviewInitialized = false;
+        LOGE("startPreview X: native_start_preview failed!");
+        return UNKNOWN_ERROR;
+    }
+
+    LOGV("startPreview X");
+    return NO_ERROR;
+}
+
+status_t QualcommCameraHardware::startPreview()
+{
+    LOGV("startPreview E");
+    Mutex::Autolock l(&mLock);
+    return startPreviewInternal();
+}
+
+void QualcommCameraHardware::stopPreviewInternal()
+{
+    LOGV("stopPreviewInternal E: %d", mCameraRunning);
+    if (mCameraRunning) {
+        // Cancel auto focus.
+        {
+            if (mNotifyCallback && (mMsgEnabled & CAMERA_MSG_FOCUS)) {
+                cancelAutoFocusInternal();
+            }
+        }
+
+        mCameraRunning = !native_stop_preview(mCameraControlFd);
+        if (!mCameraRunning && mPreviewInitialized) {
+            deinitPreview();
+            mPreviewInitialized = false;
+        }
+        else LOGE("stopPreviewInternal: failed to stop preview");
+    }
+    LOGV("stopPreviewInternal X: %d", mCameraRunning);
+}
+
+void QualcommCameraHardware::stopPreview()
+{
+    LOGV("stopPreview: E");
+    Mutex::Autolock l(&mLock);
+    {
+        if (mDataCallbackTimestamp && (mMsgEnabled & CAMERA_MSG_VIDEO_FRAME))
+            return;
+    }
+    stopPreviewInternal();
+    LOGV("stopPreview: X");
+}
+
+void QualcommCameraHardware::runAutoFocus()
+{
+    bool status = true;
+    void *libhandle = NULL;
+
+    mAutoFocusThreadLock.lock();
+    // Skip autofocus if focus mode is infinity.
+    if (strcmp(mParameters.get(CameraParameters::KEY_FOCUS_MODE),
+               CameraParameters::FOCUS_MODE_INFINITY) == 0) {
+        goto done;
+    }
+
+    mAutoFocusFd = open(MSM_CAMERA_CONTROL, O_RDWR);
+    if (mAutoFocusFd < 0) {
+        LOGE("autofocus: cannot open %s: %s",
+             MSM_CAMERA_CONTROL,
+             strerror(errno));
+        mAutoFocusThreadRunning = false;
+        mAutoFocusThreadLock.unlock();
+        return;
+    }
+
+#if DLOPEN_LIBMMCAMERA
+    // We need to maintain a reference to libqcamera.so for the duration of the
+    // AF thread, because we do not know when it will exit relative to the
+    // lifetime of this object.  We do not want to dlclose() libqcamera while
+    // LINK_cam_frame is still running.
+    libhandle = ::dlopen("liboemcamera.so", RTLD_NOW);
+    LOGV("AF: loading libqcamera at %p", libhandle);
+    if (!libhandle) {
+        LOGE("FATAL ERROR: could not dlopen liboemcamera.so: %s", dlerror());
+        close(mAutoFocusFd);
+        mAutoFocusFd = -1;
+        mAutoFocusThreadRunning = false;
+        mAutoFocusThreadLock.unlock();
+        return;
+    }
+#endif
+
+    /* This will block until either AF completes or is cancelled. */
+    LOGV("af start (fd %d)", mAutoFocusFd);
+    status = native_set_afmode(mAutoFocusFd, AF_MODE_AUTO);
+    LOGV("af done: %d", (int)status);
+    close(mAutoFocusFd);
+    mAutoFocusFd = -1;
+
+done:
+    mAutoFocusThreadRunning = false;
+    mAutoFocusThreadLock.unlock();
+
+    mCallbackLock.lock();
+    bool autoFocusEnabled = mNotifyCallback && (mMsgEnabled & CAMERA_MSG_FOCUS);
+    notify_callback cb = mNotifyCallback;
+    void *data = mCallbackCookie;
+    mCallbackLock.unlock();
+    if (autoFocusEnabled)
+        cb(CAMERA_MSG_FOCUS, status, 0, data);
+
+#if DLOPEN_LIBMMCAMERA
+    if (libhandle) {
+        ::dlclose(libhandle);
+        LOGV("AF: dlclose(libqcamera)");
+    }
+#endif
+}
+
+status_t QualcommCameraHardware::cancelAutoFocusInternal()
+{
+    LOGV("cancelAutoFocusInternal E");
+
+#if 0
+    if (mAutoFocusFd < 0) {
+        LOGV("cancelAutoFocusInternal X: not in progress");
+        return NO_ERROR;
+    }
+#endif
+
+    status_t rc = native_cancel_afmode(mCameraControlFd, mAutoFocusFd) ?
+        NO_ERROR :
+        UNKNOWN_ERROR;
+
+    LOGV("cancelAutoFocusInternal X: %d", rc);
+    return rc;
+}
+
+void *auto_focus_thread(void *user)
+{
+    LOGV("auto_focus_thread E");
+    sp<QualcommCameraHardware> obj = QualcommCameraHardware::getInstance();
+    if (obj != 0) {
+        obj->runAutoFocus();
+    }
+    else LOGW("not starting autofocus: the object went away!");
+    LOGV("auto_focus_thread X");
+    return NULL;
+}
+
+status_t QualcommCameraHardware::autoFocus()
+{
+    LOGV("autoFocus E");
+    Mutex::Autolock l(&mLock);
+
+    if (mCameraControlFd < 0) {
+        LOGE("not starting autofocus: main control fd %d", mCameraControlFd);
+        return UNKNOWN_ERROR;
+    }
+
+    {
+        mAutoFocusThreadLock.lock();
+        if (!mAutoFocusThreadRunning) {
+            // Create a detached thread here so that we don't have to wait
+            // for it when we cancel AF.
+            pthread_t thr;
+            pthread_attr_t attr;
+            pthread_attr_init(&attr);
+            pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED);
+            mAutoFocusThreadRunning =
+                !pthread_create(&thr, &attr,
+                                auto_focus_thread, NULL);
+            if (!mAutoFocusThreadRunning) {
+                LOGE("failed to start autofocus thread");
+                mAutoFocusThreadLock.unlock();
+                return UNKNOWN_ERROR;
+            }
+        }
+        mAutoFocusThreadLock.unlock();
+    }
+
+    LOGV("autoFocus X");
+    return NO_ERROR;
+}
+
+status_t QualcommCameraHardware::cancelAutoFocus()
+{
+    LOGV("cancelAutoFocus E");
+    Mutex::Autolock l(&mLock);
+
+    int rc = NO_ERROR;
+    if (mCameraRunning && mNotifyCallback && (mMsgEnabled & CAMERA_MSG_FOCUS)) {
+        rc = cancelAutoFocusInternal();
+    }
+
+    LOGV("cancelAutoFocus X");
+    return rc;
+}
+
+void QualcommCameraHardware::runSnapshotThread(void *data)
+{
+    LOGV("runSnapshotThread E");
+    if (native_start_snapshot(mCameraControlFd))
+        receiveRawPicture();
+    else
+        LOGE("main: native_start_snapshot failed!");
+
+    mSnapshotThreadWaitLock.lock();
+    mSnapshotThreadRunning = false;
+    mSnapshotThreadWait.signal();
+    mSnapshotThreadWaitLock.unlock();
+
+    LOGV("runSnapshotThread X");
+}
+
+void *snapshot_thread(void *user)
+{
+    LOGD("snapshot_thread E");
+    sp<QualcommCameraHardware> obj = QualcommCameraHardware::getInstance();
+    if (obj != 0) {
+        obj->runSnapshotThread(user);
+    }
+    else LOGW("not starting snapshot thread: the object went away!");
+    LOGD("snapshot_thread X");
+    return NULL;
+}
+
+status_t QualcommCameraHardware::takePicture()
+{
+    LOGV("takePicture(%d)", mMsgEnabled);
+    Mutex::Autolock l(&mLock);
+
+    // Wait for old snapshot thread to complete.
+    mSnapshotThreadWaitLock.lock();
+    while (mSnapshotThreadRunning) {
+        LOGV("takePicture: waiting for old snapshot thread to complete.");
+        mSnapshotThreadWait.wait(mSnapshotThreadWaitLock);
+        LOGV("takePicture: old snapshot thread completed.");
+    }
+
+    if(!native_prepare_snapshot(mCameraControlFd)) {
+        mSnapshotThreadWaitLock.unlock();
+        return UNKNOWN_ERROR;
+    }
+
+    stopPreviewInternal();
+
+    if (!initRaw(mDataCallback && (mMsgEnabled & CAMERA_MSG_COMPRESSED_IMAGE))) {
+        LOGE("initRaw failed.  Not taking picture.");
+        mSnapshotThreadWaitLock.unlock();
+        return UNKNOWN_ERROR;
+    }
+
+    mShutterLock.lock();
+    mShutterPending = true;
+    mShutterLock.unlock();
+
+    pthread_attr_t attr;
+    pthread_attr_init(&attr);
+    pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED);
+    mSnapshotThreadRunning = !pthread_create(&mSnapshotThread,
+                                             &attr,
+                                             snapshot_thread,
+                                             NULL);
+    mSnapshotThreadWaitLock.unlock();
+
+    LOGV("takePicture: X");
+    return mSnapshotThreadRunning ? NO_ERROR : UNKNOWN_ERROR;
+}
+
+status_t QualcommCameraHardware::cancelPicture()
+{
+    status_t rc;
+    LOGV("cancelPicture: E");
+    rc = native_stop_snapshot(mCameraControlFd) ? NO_ERROR : UNKNOWN_ERROR;
+    LOGV("cancelPicture: X: %d", rc);
+    return rc;
+}
+
+status_t QualcommCameraHardware::setParameters(const CameraParameters& params)
+{
+    LOGV("setParameters: E params = %p", &params);
+
+    Mutex::Autolock l(&mLock);
+    status_t rc, final_rc = NO_ERROR;
+
+    if ((rc = setPreviewSize(params)))  final_rc = rc;
+    if ((rc = setPictureSize(params)))  final_rc = rc;
+    if ((rc = setJpegQuality(params)))  final_rc = rc;
+    if ((rc = setAntibanding(params)))  final_rc = rc;
+    if ((rc = setEffect(params)))       final_rc = rc;
+    if ((rc = setWhiteBalance(params))) final_rc = rc;
+    if ((rc = setFlash(params)))        final_rc = rc;
+    if ((rc = setGpsLocation(params)))  final_rc = rc;
+    if ((rc = setRotation(params)))     final_rc = rc;
+    if ((rc = setZoom(params)))         final_rc = rc;
+    if ((rc = setFocusMode(params)))    final_rc = rc;
+    if ((rc = setOrientation(params)))  final_rc = rc;
+
+    LOGV("setParameters: X");
+    return final_rc;
+}
+
+CameraParameters QualcommCameraHardware::getParameters() const
+{
+    LOGV("getParameters: EX");
+    return mParameters;
+}
+
+status_t QualcommCameraHardware::sendCommand(int32_t command, int32_t arg1,
+                                             int32_t arg2)
+{
+    LOGV("sendCommand: EX");
+    return BAD_VALUE;
+}
+
+extern "C" sp<CameraHardwareInterface> openCameraHardware()
+{
+    LOGV("openCameraHardware: call createInstance");
+    return QualcommCameraHardware::createInstance();
+}
+
+wp<QualcommCameraHardware> QualcommCameraHardware::singleton;
+
+// If the hardware already exists, return a strong pointer to the current
+// object. If not, create a new hardware object, put it in the singleton,
+// and return it.
+sp<CameraHardwareInterface> QualcommCameraHardware::createInstance()
+{
+    LOGD("createInstance: E");
+
+    Mutex::Autolock lock(&singleton_lock);
+
+    // Wait until the previous release is done.
+    while (singleton_releasing) {
+        LOGD("Wait for previous release.");
+        singleton_wait.wait(singleton_lock);
+    }
+
+    if (singleton != 0) {
+        sp<CameraHardwareInterface> hardware = singleton.promote();
+        if (hardware != 0) {
+            LOGD("createInstance: X return existing hardware=%p", &(*hardware));
+            return hardware;
+        }
+    }
+
+    {
+        struct stat st;
+        int rc = stat("/dev/oncrpc", &st);
+        if (rc < 0) {
+            LOGD("createInstance: X failed to create hardware: %s", strerror(errno));
+            return NULL;
+        }
+    }
+
+    QualcommCameraHardware *cam = new QualcommCameraHardware();
+    sp<QualcommCameraHardware> hardware(cam);
+    singleton = hardware;
+
+    if (!cam->startCamera()) {
+        LOGE("%s: startCamera failed!", __FUNCTION__);
+        return NULL;
+    }
+
+    cam->initDefaultParameters();
+    LOGD("createInstance: X created hardware=%p", &(*hardware));
+    return hardware;
+}
+
+// For internal use only, hence the strong pointer to the derived type.
+sp<QualcommCameraHardware> QualcommCameraHardware::getInstance()
+{
+    sp<CameraHardwareInterface> hardware = singleton.promote();
+    if (hardware != 0) {
+        //    LOGV("getInstance: X old instance of hardware");
+        return sp<QualcommCameraHardware>(static_cast<QualcommCameraHardware*>(hardware.get()));
+    } else {
+        LOGV("getInstance: X new instance of hardware");
+        return sp<QualcommCameraHardware>();
+    }
+}
+
+void QualcommCameraHardware::receivePreviewFrame(struct msm_frame *frame)
+{
+//    LOGV("receivePreviewFrame E");
+
+    if (!mCameraRunning) {
+        LOGE("ignoring preview callback--camera has been stopped");
+        return;
+    }
+
+    mCallbackLock.lock();
+    int msgEnabled = mMsgEnabled;
+    data_callback pcb = mDataCallback;
+    void *pdata = mCallbackCookie;
+    data_callback_timestamp rcb = mDataCallbackTimestamp;
+    void *rdata = mCallbackCookie;
+    mCallbackLock.unlock();
+
+    // Find the offset within the heap of the current buffer.
+    ssize_t offset =
+        (ssize_t)frame->buffer - (ssize_t)mPreviewHeap->mHeap->base();
+    offset /= mPreviewHeap->mAlignedBufferSize;
+
+    mInPreviewCallback = true;
+    if (pcb != NULL && (msgEnabled & CAMERA_MSG_PREVIEW_FRAME))
+        pcb(CAMERA_MSG_PREVIEW_FRAME, mPreviewHeap->mBuffers[offset],
+            pdata);
+
+    if(rcb != NULL && (msgEnabled & CAMERA_MSG_VIDEO_FRAME)) {
+        rcb(systemTime(), CAMERA_MSG_VIDEO_FRAME, mPreviewHeap->mBuffers[offset], rdata);
+        Mutex::Autolock rLock(&mRecordFrameLock);
+        if (mReleasedRecordingFrame != true) {
+            LOGV("block waiting for frame release");
+            mRecordWait.wait(mRecordFrameLock);
+            LOGV("frame released, continuing");
+        }
+        mReleasedRecordingFrame = false;
+    }
+    mInPreviewCallback = false;
+
+//    LOGV("receivePreviewFrame X");
+}
+
+status_t QualcommCameraHardware::startRecording()
+{
+    LOGV("startRecording E");
+    Mutex::Autolock l(&mLock);
+    mReleasedRecordingFrame = false;
+    return startPreviewInternal();
+}
+
+void QualcommCameraHardware::stopRecording()
+{
+    LOGV("stopRecording: E");
+    Mutex::Autolock l(&mLock);
+    {
+        mRecordFrameLock.lock();
+        mReleasedRecordingFrame = true;
+        mRecordWait.signal();
+        mRecordFrameLock.unlock();
+
+        if(mDataCallback && (mMsgEnabled & CAMERA_MSG_PREVIEW_FRAME)) {
+            LOGV("stopRecording: X, preview still in progress");
+            return;
+        }
+    }
+
+    stopPreviewInternal();
+    LOGV("stopRecording: X");
+}
+
+void QualcommCameraHardware::releaseRecordingFrame(
+       const sp<IMemory>& mem __attribute__((unused)))
+{
+    LOGV("releaseRecordingFrame E");
+    Mutex::Autolock rLock(&mRecordFrameLock);
+    mReleasedRecordingFrame = true;
+    mRecordWait.signal();
+    LOGV("releaseRecordingFrame X");
+}
+
+bool QualcommCameraHardware::recordingEnabled()
+{
+    return mCameraRunning && mDataCallbackTimestamp && (mMsgEnabled & CAMERA_MSG_VIDEO_FRAME);
+}
+
+void QualcommCameraHardware::notifyShutter(common_crop_t *crop)
+{
+    mShutterLock.lock();
+    image_rect_type size;
+
+    if (mShutterPending && mNotifyCallback && (mMsgEnabled & CAMERA_MSG_SHUTTER)) {
+        LOGV("out2_w=%d, out2_h=%d, in2_w=%d, in2_h=%d",
+             crop->out2_w, crop->out2_h, crop->in2_w, crop->in2_h);
+        LOGV("out1_w=%d, out1_h=%d, in1_w=%d, in1_h=%d",
+             crop->out1_w, crop->out1_h, crop->in1_w, crop->in1_h);
+
+        // To workaround a bug in MDP which happens if either
+        // dimension > 2048, we display the thumbnail instead.
+        mDisplayHeap = mRawHeap;
+        if (crop->in1_w == 0 || crop->in1_h == 0) {
+            // Full size
+            size.width = mDimension.picture_width;
+            size.height = mDimension.picture_height;
+            if (size.width > 2048 || size.height > 2048) {
+                size.width = mDimension.ui_thumbnail_width;
+                size.height = mDimension.ui_thumbnail_height;
+                mDisplayHeap = mThumbnailHeap;
+            }
+        } else {
+            // Cropped
+            size.width = crop->in2_w & ~1;
+            size.height = crop->in2_h & ~1;
+            if (size.width > 2048 || size.height > 2048) {
+                size.width = crop->in1_w & ~1;
+                size.height = crop->in1_h & ~1;
+                mDisplayHeap = mThumbnailHeap;
+            }
+        }
+
+        mNotifyCallback(CAMERA_MSG_SHUTTER, (int32_t)&size, 0,
+                        mCallbackCookie);
+        mShutterPending = false;
+    }
+    mShutterLock.unlock();
+}
+
+static void receive_shutter_callback(common_crop_t *crop)
+{
+    LOGV("receive_shutter_callback: E");
+    sp<QualcommCameraHardware> obj = QualcommCameraHardware::getInstance();
+    if (obj != 0) {
+        obj->notifyShutter(crop);
+    }
+    LOGV("receive_shutter_callback: X");
+}
+
+// Crop the picture in place.
+static void crop_yuv420(uint32_t width, uint32_t height,
+                 uint32_t cropped_width, uint32_t cropped_height,
+                 uint8_t *image)
+{
+    uint32_t i, x, y;
+    uint8_t* chroma_src, *chroma_dst;
+
+    // Calculate the start position of the cropped area.
+    x = (width - cropped_width) / 2;
+    y = (height - cropped_height) / 2;
+    x &= ~1;
+    y &= ~1;
+
+    // Copy luma component.
+    for(i = 0; i < cropped_height; i++)
+        memcpy(image + i * cropped_width,
+               image + width * (y + i) + x,
+               cropped_width);
+
+    chroma_src = image + width * height;
+    chroma_dst = image + cropped_width * cropped_height;
+
+    // Copy chroma components.
+    cropped_height /= 2;
+    y /= 2;
+    for(i = 0; i < cropped_height; i++)
+        memcpy(chroma_dst + i * cropped_width,
+               chroma_src + width * (y + i) + x,
+               cropped_width);
+}
+
+void QualcommCameraHardware::receiveRawPicture()
+{
+    LOGV("receiveRawPicture: E");
+
+    Mutex::Autolock cbLock(&mCallbackLock);
+    if (mDataCallback && (mMsgEnabled & CAMERA_MSG_RAW_IMAGE)) {
+        if(native_get_picture(mCameraControlFd, &mCrop) == false) {
+            LOGE("getPicture failed!");
+            return;
+        }
+        mCrop.in1_w &= ~1;
+        mCrop.in1_h &= ~1;
+        mCrop.in2_w &= ~1;
+        mCrop.in2_h &= ~1;
+
+        // By the time native_get_picture returns, picture is taken. Call
+        // shutter callback if cam config thread has not done that.
+        notifyShutter(&mCrop);
+
+        // Crop the image if zoomed.
+        if (mCrop.in2_w != 0 && mCrop.in2_h != 0) {
+            crop_yuv420(mCrop.out2_w, mCrop.out2_h, mCrop.in2_w, mCrop.in2_h,
+                 (uint8_t *)mRawHeap->mHeap->base());
+            crop_yuv420(mCrop.out1_w, mCrop.out1_h, mCrop.in1_w, mCrop.in1_h,
+                 (uint8_t *)mThumbnailHeap->mHeap->base());
+            // We do not need jpeg encoder to upscale the image. Set the new
+            // dimension for encoder.
+            mDimension.orig_picture_dx = mCrop.in2_w;
+            mDimension.orig_picture_dy = mCrop.in2_h;
+            mDimension.thumbnail_width = mCrop.in1_w;
+            mDimension.thumbnail_height = mCrop.in1_h;
+            memset(&mCrop, 0, sizeof(mCrop));
+        }
+
+        mDataCallback(CAMERA_MSG_RAW_IMAGE, mDisplayHeap->mBuffers[0],
+                            mCallbackCookie);
+    }
+    else LOGV("Raw-picture callback was canceled--skipping.");
+
+    if (mDataCallback && (mMsgEnabled & CAMERA_MSG_COMPRESSED_IMAGE)) {
+        mJpegSize = 0;
+        if (LINK_jpeg_encoder_init()) {
+            if(native_jpeg_encode()) {
+                LOGV("receiveRawPicture: X (success)");
+                return;
+            }
+            LOGE("jpeg encoding failed");
+        }
+        else LOGE("receiveRawPicture X: jpeg_encoder_init failed.");
+    }
+    else LOGV("JPEG callback is NULL, not encoding image.");
+    deinitRaw();
+    LOGV("receiveRawPicture: X");
+}
+
+void QualcommCameraHardware::receiveJpegPictureFragment(
+    uint8_t *buff_ptr, uint32_t buff_size)
+{
+    uint32_t remaining = mJpegHeap->mHeap->virtualSize();
+    remaining -= mJpegSize;
+    uint8_t *base = (uint8_t *)mJpegHeap->mHeap->base();
+
+    LOGV("receiveJpegPictureFragment size %d", buff_size);
+    if (buff_size > remaining) {
+        LOGE("receiveJpegPictureFragment: size %d exceeds what "
+             "remains in JPEG heap (%d), truncating",
+             buff_size,
+             remaining);
+        buff_size = remaining;
+    }
+    memcpy(base + mJpegSize, buff_ptr, buff_size);
+    mJpegSize += buff_size;
+}
+
+void QualcommCameraHardware::receiveJpegPicture(void)
+{
+    LOGV("receiveJpegPicture: E image (%d uint8_ts out of %d)",
+         mJpegSize, mJpegHeap->mBufferSize);
+    Mutex::Autolock cbLock(&mCallbackLock);
+
+    int index = 0, rc;
+
+    if (mDataCallback && (mMsgEnabled & CAMERA_MSG_COMPRESSED_IMAGE)) {
+        // The reason we do not allocate into mJpegHeap->mBuffers[offset] is
+        // that the JPEG image's size will probably change from one snapshot
+        // to the next, so we cannot reuse the MemoryBase object.
+        sp<MemoryBase> buffer = new
+            MemoryBase(mJpegHeap->mHeap,
+                       index * mJpegHeap->mBufferSize +
+                       0,
+                       mJpegSize);
+        mDataCallback(CAMERA_MSG_COMPRESSED_IMAGE, buffer, mCallbackCookie);
+        buffer = NULL;
+    }
+    else LOGV("JPEG callback was cancelled--not delivering image.");
+
+    LINK_jpeg_encoder_join();
+    deinitRaw();
+
+    LOGV("receiveJpegPicture: X callback done.");
+}
+
+bool QualcommCameraHardware::previewEnabled()
+{
+    return mCameraRunning && mDataCallback && (mMsgEnabled & CAMERA_MSG_PREVIEW_FRAME);
+}
+
+status_t QualcommCameraHardware::setPreviewSize(const CameraParameters& params)
+{
+    int width, height;
+    params.getPreviewSize(&width, &height);
+    LOGV("requested preview size %d x %d", width, height);
+
+    // Validate the preview size
+    for (size_t i = 0; i < PREVIEW_SIZE_COUNT; ++i) {
+        if (width == preview_sizes[i].width
+           && height == preview_sizes[i].height) {
+            mParameters.setPreviewSize(width, height);
+            mDimension.display_width = width;
+            mDimension.display_height = height;
+            return NO_ERROR;
+        }
+    }
+    LOGE("Invalid preview size requested: %dx%d", width, height);
+    return BAD_VALUE;
+}
+
+status_t QualcommCameraHardware::setPictureSize(const CameraParameters& params)
+{
+    int width, height;
+    params.getPictureSize(&width, &height);
+    LOGV("requested picture size %d x %d", width, height);
+
+    // Validate the picture size
+    for (int i = 0; i < PICTURE_SIZE_COUNT; ++i) {
+        if (width == picture_sizes[i].width
+          && height == picture_sizes[i].height) {
+            mParameters.setPictureSize(width, height);
+            mDimension.picture_width = width;
+            mDimension.picture_height = height;
+            return NO_ERROR;
+        }
+    }
+    LOGE("Invalid picture size requested: %dx%d", width, height);
+    return BAD_VALUE;
+}
+
+status_t QualcommCameraHardware::setJpegQuality(const CameraParameters& params) {
+    status_t rc = NO_ERROR;
+    int quality = params.getInt(CameraParameters::KEY_JPEG_QUALITY);
+    if (quality > 0 && quality <= 100) {
+        mParameters.set(CameraParameters::KEY_JPEG_QUALITY, quality);
+    } else {
+        LOGE("Invalid jpeg quality=%d", quality);
+        rc = BAD_VALUE;
+    }
+
+    quality = params.getInt(CameraParameters::KEY_JPEG_THUMBNAIL_QUALITY);
+    if (quality > 0 && quality <= 100) {
+        mParameters.set(CameraParameters::KEY_JPEG_THUMBNAIL_QUALITY, quality);
+    } else {
+        LOGE("Invalid jpeg thumbnail quality=%d", quality);
+        rc = BAD_VALUE;
+    }
+    return rc;
+}
+
+status_t QualcommCameraHardware::setEffect(const CameraParameters& params)
+{
+    const char *str = params.get(CameraParameters::KEY_EFFECT);
+    if (str != NULL) {
+        int32_t value = attr_lookup(effects, sizeof(effects) / sizeof(str_map), str);
+        if (value != NOT_FOUND) {
+            mParameters.set(CameraParameters::KEY_EFFECT, str);
+            bool ret = native_set_parm(CAMERA_SET_PARM_EFFECT, sizeof(value),
+                                       (void *)&value);
+            return ret ? NO_ERROR : UNKNOWN_ERROR;
+        }
+    }
+    LOGE("Invalid effect value: %s", (str == NULL) ? "NULL" : str);
+    return BAD_VALUE;
+}
+
+status_t QualcommCameraHardware::setWhiteBalance(const CameraParameters& params)
+{
+    const char *str = params.get(CameraParameters::KEY_WHITE_BALANCE);
+    if (str != NULL) {
+        int32_t value = attr_lookup(whitebalance, sizeof(whitebalance) / sizeof(str_map), str);
+        if (value != NOT_FOUND) {
+            mParameters.set(CameraParameters::KEY_WHITE_BALANCE, str);
+            bool ret = native_set_parm(CAMERA_SET_PARM_WB, sizeof(value),
+                                       (void *)&value);
+            return ret ? NO_ERROR : UNKNOWN_ERROR;
+        }
+    }
+    LOGE("Invalid whitebalance value: %s", (str == NULL) ? "NULL" : str);
+    return BAD_VALUE;
+}
+
+status_t QualcommCameraHardware::setFlash(const CameraParameters& params)
+{
+    if (!mSensorInfo.flash_enabled) {
+        LOGV("%s: flash not supported", __FUNCTION__);
+        return NO_ERROR;
+    }
+
+    const char *str = params.get(CameraParameters::KEY_FLASH_MODE);
+    if (str != NULL) {
+        int32_t value = attr_lookup(flash, sizeof(flash) / sizeof(str_map), str);
+        if (value != NOT_FOUND) {
+            mParameters.set(CameraParameters::KEY_FLASH_MODE, str);
+            bool ret = native_set_parm(CAMERA_SET_PARM_LED_MODE,
+                                       sizeof(value), (void *)&value);
+            return ret ? NO_ERROR : UNKNOWN_ERROR;
+        }
+    }
+    LOGE("Invalid flash mode value: %s", (str == NULL) ? "NULL" : str);
+    return BAD_VALUE;
+}
+
+status_t QualcommCameraHardware::setAntibanding(const CameraParameters& params)
+{
+    const char *str = params.get(CameraParameters::KEY_ANTIBANDING);
+    if (str != NULL) {
+        int value = (camera_antibanding_type)attr_lookup(
+          antibanding, sizeof(antibanding) / sizeof(str_map), str);
+        if (value != NOT_FOUND) {
+            camera_antibanding_type temp = (camera_antibanding_type) value;
+            // We don't have auto antibanding now, and simply set the frequency by country.
+            if (temp == CAMERA_ANTIBANDING_AUTO) {
+                temp = camera_get_location();
+            }
+            mParameters.set(CameraParameters::KEY_ANTIBANDING, str);
+            native_set_parm(CAMERA_SET_PARM_ANTIBANDING, sizeof(camera_antibanding_type), (void *)&temp);
+            return NO_ERROR;
+        }
+    }
+    LOGE("Invalid antibanding value: %s", (str == NULL) ? "NULL" : str);
+    return BAD_VALUE;
+}
+
+status_t QualcommCameraHardware::setGpsLocation(const CameraParameters& params)
+{
+    const char *latitude = params.get(CameraParameters::KEY_GPS_LATITUDE);
+    if (latitude) {
+        mParameters.set(CameraParameters::KEY_GPS_LATITUDE, latitude);
+    }
+
+    const char *longitude = params.get(CameraParameters::KEY_GPS_LONGITUDE);
+    if (longitude) {
+        mParameters.set(CameraParameters::KEY_GPS_LONGITUDE, longitude);
+    }
+
+    const char *altitude = params.get(CameraParameters::KEY_GPS_ALTITUDE);
+    if (altitude) {
+        mParameters.set(CameraParameters::KEY_GPS_ALTITUDE, altitude);
+    }
+
+    const char *timestamp = params.get(CameraParameters::KEY_GPS_TIMESTAMP);
+    if (timestamp) {
+        mParameters.set(CameraParameters::KEY_GPS_TIMESTAMP, timestamp);
+    }
+    return NO_ERROR;
+}
+
+status_t QualcommCameraHardware::setRotation(const CameraParameters& params)
+{
+    status_t rc = NO_ERROR;
+    int rotation = params.getInt(CameraParameters::KEY_ROTATION);
+    if (rotation != NOT_FOUND) {
+        if (rotation == 0 || rotation == 90 || rotation == 180
+            || rotation == 270) {
+          mParameters.set(CameraParameters::KEY_ROTATION, rotation);
+        } else {
+            LOGE("Invalid rotation value: %d", rotation);
+            rc = BAD_VALUE;
+        }
+    }
+    return rc;
+}
+
+status_t QualcommCameraHardware::setZoom(const CameraParameters& params)
+{
+    status_t rc = NO_ERROR;
+    // No matter how many different zoom values the driver can provide, HAL
+    // provides applictations the same number of zoom levels. The maximum driver
+    // zoom value depends on sensor output (VFE input) and preview size (VFE
+    // output) because VFE can only crop and cannot upscale. If the preview size
+    // is bigger, the maximum zoom ratio is smaller. However, we want the
+    // zoom ratio of each zoom level is always the same whatever the preview
+    // size is. Ex: zoom level 1 is always 1.2x, zoom level 2 is 1.44x, etc. So,
+    // we need to have a fixed maximum zoom value and do read it from the
+    // driver.
+    static const int ZOOM_STEP = 6;
+    int32_t zoom_level = params.getInt("zoom");
+
+    LOGI("Set zoom=%d", zoom_level);
+    if(zoom_level >= 0 && zoom_level <= MAX_ZOOM_LEVEL) {
+        mParameters.set("zoom", zoom_level);
+        int32_t zoom_value = ZOOM_STEP * zoom_level;
+        bool ret = native_set_parm(CAMERA_SET_PARM_ZOOM,
+            sizeof(zoom_value), (void *)&zoom_value);
+        rc = ret ? NO_ERROR : UNKNOWN_ERROR;
+    } else {
+        rc = BAD_VALUE;
+    }
+
+    return rc;
+}
+
+status_t QualcommCameraHardware::setFocusMode(const CameraParameters& params)
+{
+    const char *str = params.get(CameraParameters::KEY_FOCUS_MODE);
+    if (str != NULL) {
+        int32_t value = attr_lookup(focus_modes,
+                                    sizeof(focus_modes) / sizeof(str_map), str);
+        if (value != NOT_FOUND) {
+            mParameters.set(CameraParameters::KEY_FOCUS_MODE, str);
+            // Focus step is reset to infinity when preview is started. We do
+            // not need to do anything now.
+            return NO_ERROR;
+        }
+    }
+    LOGE("Invalid focus mode value: %s", (str == NULL) ? "NULL" : str);
+    return BAD_VALUE;
+}
+
+status_t QualcommCameraHardware::setOrientation(const CameraParameters& params)
+{
+    const char *str = params.get("orientation");
+
+    if (str != NULL) {
+        if (strcmp(str, "portrait") == 0 || strcmp(str, "landscape") == 0) {
+            // Camera service needs this to decide if the preview frames and raw
+            // pictures should be rotated.
+            mParameters.set("orientation", str);
+        } else {
+            LOGE("Invalid orientation value: %s", str);
+            return BAD_VALUE;
+        }
+    }
+    return NO_ERROR;
+}
+
+QualcommCameraHardware::MemPool::MemPool(int buffer_size, int num_buffers,
+                                         int frame_size,
+                                         const char *name) :
+    mBufferSize(buffer_size),
+    mNumBuffers(num_buffers),
+    mFrameSize(frame_size),
+    mBuffers(NULL), mName(name)
+{
+    int page_size_minus_1 = getpagesize() - 1;
+    mAlignedBufferSize = (buffer_size + page_size_minus_1) & (~page_size_minus_1);
+}
+
+void QualcommCameraHardware::MemPool::completeInitialization()
+{
+    // If we do not know how big the frame will be, we wait to allocate
+    // the buffers describing the individual frames until we do know their
+    // size.
+
+    if (mFrameSize > 0) {
+        mBuffers = new sp<MemoryBase>[mNumBuffers];
+        for (int i = 0; i < mNumBuffers; i++) {
+            mBuffers[i] = new
+                MemoryBase(mHeap,
+                           i * mAlignedBufferSize,
+                           mFrameSize);
+        }
+    }
+}
+
+QualcommCameraHardware::AshmemPool::AshmemPool(int buffer_size, int num_buffers,
+                                               int frame_size,
+                                               const char *name) :
+    QualcommCameraHardware::MemPool(buffer_size,
+                                    num_buffers,
+                                    frame_size,
+                                    name)
+{
+    LOGV("constructing MemPool %s backed by ashmem: "
+         "%d frames @ %d uint8_ts, "
+         "buffer size %d",
+         mName,
+         num_buffers, frame_size, buffer_size);
+
+    int page_mask = getpagesize() - 1;
+    int ashmem_size = buffer_size * num_buffers;
+    ashmem_size += page_mask;
+    ashmem_size &= ~page_mask;
+
+    mHeap = new MemoryHeapBase(ashmem_size);
+
+    completeInitialization();
+}
+
+static bool register_buf(int camfd,
+                         int size,
+                         int pmempreviewfd,
+                         uint32_t offset,
+                         uint8_t *buf,
+                         int pmem_type,
+                         bool vfe_can_write,
+                         bool register_buffer = true);
+
+QualcommCameraHardware::PmemPool::PmemPool(const char *pmem_pool,
+                                           int flags,
+                                           int camera_control_fd,
+                                           int pmem_type,
+                                           int buffer_size, int num_buffers,
+                                           int frame_size,
+                                           const char *name) :
+    QualcommCameraHardware::MemPool(buffer_size,
+                                    num_buffers,
+                                    frame_size,
+                                    name),
+    mPmemType(pmem_type),
+    mCameraControlFd(dup(camera_control_fd))
+{
+    LOGV("constructing MemPool %s backed by pmem pool %s: "
+         "%d frames @ %d bytes, buffer size %d",
+         mName,
+         pmem_pool, num_buffers, frame_size,
+         buffer_size);
+
+    LOGV("%s: duplicating control fd %d --> %d",
+         __FUNCTION__,
+         camera_control_fd, mCameraControlFd);
+
+    // Make a new mmap'ed heap that can be shared across processes.
+    // mAlignedBufferSize is already in 4k aligned. (do we need total size necessary to be in power of 2??)
+    mAlignedSize = mAlignedBufferSize * num_buffers;
+
+    sp<MemoryHeapBase> masterHeap =
+        new MemoryHeapBase(pmem_pool, mAlignedSize, flags);
+
+    if (masterHeap->getHeapID() < 0) {
+        LOGE("failed to construct master heap for pmem pool %s", pmem_pool);
+        masterHeap.clear();
+        return;
+    }
+
+    sp<MemoryHeapPmem> pmemHeap = new MemoryHeapPmem(masterHeap, flags);
+    if (pmemHeap->getHeapID() >= 0) {
+        pmemHeap->slap();
+        masterHeap.clear();
+        mHeap = pmemHeap;
+        pmemHeap.clear();
+
+        mFd = mHeap->getHeapID();
+        if (::ioctl(mFd, PMEM_GET_SIZE, &mSize)) {
+            LOGE("pmem pool %s ioctl(PMEM_GET_SIZE) error %s (%d)",
+                 pmem_pool,
+                 ::strerror(errno), errno);
+            mHeap.clear();
+            return;
+        }
+
+        LOGV("pmem pool %s ioctl(fd = %d, PMEM_GET_SIZE) is %ld",
+             pmem_pool,
+             mFd,
+             mSize.len);
+		LOGD("mBufferSize=%d, mAlignedBufferSize=%d\n", mBufferSize, mAlignedBufferSize);
+        // Unregister preview buffers with the camera drivers.  Allow the VFE to write
+        // to all preview buffers except for the last one.
+        for (int cnt = 0; cnt < num_buffers; ++cnt) {
+            register_buf(mCameraControlFd,
+                         mBufferSize,
+                         mHeap->getHeapID(),
+                         mAlignedBufferSize * cnt,
+                         (uint8_t *)mHeap->base() + mAlignedBufferSize * cnt,
+                         pmem_type,
+                         !(cnt == num_buffers - 1 && pmem_type == MSM_PMEM_OUTPUT2));
+        }
+
+        completeInitialization();
+    }
+    else LOGE("pmem pool %s error: could not create master heap!",
+              pmem_pool);
+}
+
+QualcommCameraHardware::PmemPool::~PmemPool()
+{
+    LOGV("%s: %s E", __FUNCTION__, mName);
+    if (mHeap != NULL) {
+        // Unregister preview buffers with the camera drivers.
+        for (int cnt = 0; cnt < mNumBuffers; ++cnt) {
+            register_buf(mCameraControlFd,
+                         mBufferSize,
+                         mHeap->getHeapID(),
+                         mAlignedBufferSize * cnt,
+                         (uint8_t *)mHeap->base() + mAlignedBufferSize * cnt,
+                         mPmemType,
+                         false,
+                         false /* unregister */);
+        }
+    }
+    LOGV("destroying PmemPool %s: closing control fd %d",
+         mName,
+         mCameraControlFd);
+    close(mCameraControlFd);
+    LOGV("%s: %s X", __FUNCTION__, mName);
+}
+
+QualcommCameraHardware::MemPool::~MemPool()
+{
+    LOGV("destroying MemPool %s", mName);
+    if (mFrameSize > 0)
+        delete [] mBuffers;
+    mHeap.clear();
+    LOGV("destroying MemPool %s completed", mName);
+}
+
+static bool register_buf(int camfd,
+                         int size,
+                         int pmempreviewfd,
+                         uint32_t offset,
+                         uint8_t *buf,
+                         int pmem_type,
+                         bool vfe_can_write,
+                         bool register_buffer)
+{
+    struct msm_pmem_info pmemBuf;
+
+    pmemBuf.type     = pmem_type;
+    pmemBuf.fd       = pmempreviewfd;
+    pmemBuf.offset   = offset;
+    pmemBuf.len      = size;
+    pmemBuf.vaddr    = buf;
+    pmemBuf.y_off    = 0;
+    pmemBuf.cbcr_off = PAD_TO_WORD(size * 2 / 3);
+    pmemBuf.active   = vfe_can_write;
+
+    LOGV("register_buf: camfd = %d, reg = %d buffer = %p",
+         camfd, !register_buffer, buf);
+    if (ioctl(camfd,
+              register_buffer ?
+              MSM_CAM_IOCTL_REGISTER_PMEM :
+              MSM_CAM_IOCTL_UNREGISTER_PMEM,
+              &pmemBuf) < 0) {
+        LOGE("register_buf: MSM_CAM_IOCTL_(UN)REGISTER_PMEM fd %d error %s",
+             camfd,
+             strerror(errno));
+        return false;
+    }
+    return true;
+}
+
+status_t QualcommCameraHardware::MemPool::dump(int fd, const Vector<String16>& args) const
+{
+    const size_t SIZE = 256;
+    char buffer[SIZE];
+    String8 result;
+    snprintf(buffer, 255, "QualcommCameraHardware::AshmemPool::dump\n");
+    result.append(buffer);
+    if (mName) {
+        snprintf(buffer, 255, "mem pool name (%s)\n", mName);
+        result.append(buffer);
+    }
+    if (mHeap != 0) {
+        snprintf(buffer, 255, "heap base(%p), size(%d), flags(%d), device(%s)\n",
+                 mHeap->getBase(), mHeap->getSize(),
+                 mHeap->getFlags(), mHeap->getDevice());
+        result.append(buffer);
+    }
+    snprintf(buffer, 255,
+             "buffer size (%d), number of buffers (%d), frame size(%d)",
+             mBufferSize, mNumBuffers, mFrameSize);
+    result.append(buffer);
+    write(fd, result.string(), result.size());
+    return NO_ERROR;
+}
+
+static void receive_camframe_callback(struct msm_frame *frame)
+{
+    sp<QualcommCameraHardware> obj = QualcommCameraHardware::getInstance();
+    if (obj != 0) {
+        obj->receivePreviewFrame(frame);
+    }
+}
+
+static void receive_jpeg_fragment_callback(uint8_t *buff_ptr, uint32_t buff_size)
+{
+    LOGV("receive_jpeg_fragment_callback E");
+    sp<QualcommCameraHardware> obj = QualcommCameraHardware::getInstance();
+    if (obj != 0) {
+        obj->receiveJpegPictureFragment(buff_ptr, buff_size);
+    }
+    LOGV("receive_jpeg_fragment_callback X");
+}
+
+static void receive_jpeg_callback(jpeg_event_t status)
+{
+    LOGV("receive_jpeg_callback E (completion status %d)", status);
+    if (status == JPEG_EVENT_DONE) {
+        sp<QualcommCameraHardware> obj = QualcommCameraHardware::getInstance();
+        if (obj != 0) {
+            obj->receiveJpegPicture();
+        }
+    }
+    LOGV("receive_jpeg_callback X");
+}
+
+void QualcommCameraHardware::setCallbacks(notify_callback notify_cb,
+                             data_callback data_cb,
+                             data_callback_timestamp data_cb_timestamp,
+                             void* user)
+{
+    Mutex::Autolock lock(mLock);
+    mNotifyCallback = notify_cb;
+    mDataCallback = data_cb;
+    mDataCallbackTimestamp = data_cb_timestamp;
+    mCallbackCookie = user;
+}
+
+void QualcommCameraHardware::enableMsgType(int32_t msgType)
+{
+    Mutex::Autolock lock(mLock);
+    mMsgEnabled |= msgType;
+}
+
+void QualcommCameraHardware::disableMsgType(int32_t msgType)
+{
+    Mutex::Autolock lock(mLock);
+    mMsgEnabled &= ~msgType;
+}
+
+bool QualcommCameraHardware::msgTypeEnabled(int32_t msgType)
+{
+    return (mMsgEnabled & msgType);
+}
+
+}; // namespace android