Merge "camera2: Add height to the crop region metadata property" into jb-mr2-dev
diff --git a/services/camera/libcameraservice/CameraService.cpp b/services/camera/libcameraservice/CameraService.cpp
index cdeb92e..757a781 100644
--- a/services/camera/libcameraservice/CameraService.cpp
+++ b/services/camera/libcameraservice/CameraService.cpp
@@ -928,8 +928,15 @@
     ALOGV("Client::disconnect");
     BasicClient::disconnect();
     mCameraService->setCameraFree(mCameraId);
+
+    StatusVector rejectSourceStates;
+    rejectSourceStates.push_back(ICameraServiceListener::STATUS_NOT_PRESENT);
+    rejectSourceStates.push_back(ICameraServiceListener::STATUS_ENUMERATING);
+
+    // Transition to PRESENT if the camera is not in either of above 2 states
     mCameraService->updateStatus(ICameraServiceListener::STATUS_PRESENT,
-                                 mCameraId);
+                                 mCameraId,
+                                 &rejectSourceStates);
 }
 
 CameraService::Client::OpsCallback::OpsCallback(wp<BasicClient> client):
@@ -1111,15 +1118,11 @@
 }
 
 void CameraService::updateStatus(ICameraServiceListener::Status status,
-                                 int32_t cameraId) {
+                                 int32_t cameraId,
+                                 const StatusVector *rejectSourceStates) {
     // do not lock mServiceLock here or can get into a deadlock from
     //  connect() -> ProClient::disconnect -> updateStatus
     Mutex::Autolock lock(mStatusMutex);
-    updateStatusUnsafe(status, cameraId);
-}
-
-void CameraService::updateStatusUnsafe(ICameraServiceListener::Status status,
-                                       int32_t cameraId) {
 
     ICameraServiceListener::Status oldStatus = mStatusList[cameraId];
 
@@ -1139,6 +1142,26 @@
             return;
         }
 
+        if (rejectSourceStates != NULL) {
+            const StatusVector &rejectList = *rejectSourceStates;
+            StatusVector::const_iterator it = rejectList.begin();
+
+            /**
+             * Sometimes we want to conditionally do a transition.
+             * For example if a client disconnects, we want to go to PRESENT
+             * only if we weren't already in NOT_PRESENT or ENUMERATING.
+             */
+            for (; it != rejectList.end(); ++it) {
+                if (oldStatus == *it) {
+                    ALOGV("%s: Rejecting status transition for Camera ID %d, "
+                          " since the source state was was in one of the bad "
+                          " states.", __FUNCTION__, cameraId);
+                    mStatusList[cameraId] = oldStatus;
+                    return;
+                }
+            }
+        }
+
         /**
           * ProClients lose their exclusive lock.
           * - Done before the CameraClient can initialize the HAL device,
diff --git a/services/camera/libcameraservice/CameraService.h b/services/camera/libcameraservice/CameraService.h
index 8cb1691..710f164 100644
--- a/services/camera/libcameraservice/CameraService.h
+++ b/services/camera/libcameraservice/CameraService.h
@@ -341,14 +341,12 @@
     ICameraServiceListener::Status
                         getStatus(int cameraId) const;
 
+    typedef Vector<ICameraServiceListener::Status> StatusVector;
     // Broadcast the new status if it changed (locks the service mutex)
     void                updateStatus(
                             ICameraServiceListener::Status status,
-                            int32_t cameraId);
-    // Call this one when the service mutex is already held (idempotent)
-    void                updateStatusUnsafe(
-                            ICameraServiceListener::Status status,
-                            int32_t cameraId);
+                            int32_t cameraId,
+                            const StatusVector *rejectSourceStates = NULL);
 
     // IBinder::DeathRecipient implementation
     virtual void        binderDied(const wp<IBinder> &who);
diff --git a/services/camera/libcameraservice/camera2/CallbackProcessor.cpp b/services/camera/libcameraservice/camera2/CallbackProcessor.cpp
index dd37283..a3d6cb2 100644
--- a/services/camera/libcameraservice/camera2/CallbackProcessor.cpp
+++ b/services/camera/libcameraservice/camera2/CallbackProcessor.cpp
@@ -26,6 +26,7 @@
 #include "../CameraDeviceBase.h"
 #include "../Camera2Client.h"
 
+#define ALIGN(x, mask) ( ((x) + (mask) - 1) & ~((mask) - 1) )
 
 namespace android {
 namespace camera2 {
@@ -64,6 +65,14 @@
         return INVALID_OPERATION;
     }
 
+    // If possible, use the flexible YUV format
+    int32_t callbackFormat = params.previewFormat;
+    if (params.fastInfo.useFlexibleYuv &&
+            (params.previewFormat == HAL_PIXEL_FORMAT_YCrCb_420_SP ||
+             params.previewFormat == HAL_PIXEL_FORMAT_YV12) ) {
+        callbackFormat = HAL_PIXEL_FORMAT_YCbCr_420_888;
+    }
+
     if (mCallbackConsumer == 0) {
         // Create CPU buffer queue endpoint
         mCallbackConsumer = new CpuConsumer(kCallbackHeapCount);
@@ -86,12 +95,12 @@
         }
         if (currentWidth != (uint32_t)params.previewWidth ||
                 currentHeight != (uint32_t)params.previewHeight ||
-                currentFormat != (uint32_t)params.previewFormat) {
+                currentFormat != (uint32_t)callbackFormat) {
             // Since size should only change while preview is not running,
             // assuming that all existing use of old callback stream is
             // completed.
-            ALOGV("%s: Camera %d: Deleting stream %d since the buffer dimensions changed",
-                __FUNCTION__, mId, mCallbackStreamId);
+            ALOGV("%s: Camera %d: Deleting stream %d since the buffer "
+                    "parameters changed", __FUNCTION__, mId, mCallbackStreamId);
             res = device->deleteStream(mCallbackStreamId);
             if (res != OK) {
                 ALOGE("%s: Camera %d: Unable to delete old output stream "
@@ -104,12 +113,12 @@
     }
 
     if (mCallbackStreamId == NO_STREAM) {
-        ALOGV("Creating callback stream: %d %d format 0x%x",
+        ALOGV("Creating callback stream: %d x %d, format 0x%x, API format 0x%x",
                 params.previewWidth, params.previewHeight,
-                params.previewFormat);
+                callbackFormat, params.previewFormat);
         res = device->createStream(mCallbackWindow,
                 params.previewWidth, params.previewHeight,
-                params.previewFormat, 0, &mCallbackStreamId);
+                callbackFormat, 0, &mCallbackStreamId);
         if (res != OK) {
             ALOGE("%s: Camera %d: Can't create output stream for callbacks: "
                     "%s (%d)", __FUNCTION__, mId,
@@ -220,6 +229,8 @@
     ALOGV("%s: Camera %d: Preview callback available", __FUNCTION__,
             mId);
 
+    bool useFlexibleYuv = false;
+    int32_t previewFormat = 0;
     {
         SharedParameters::Lock l(client->getParameters());
 
@@ -246,10 +257,18 @@
             return OK;
         }
 
-        if (imgBuffer.format != l.mParameters.previewFormat) {
+        previewFormat = l.mParameters.previewFormat;
+        useFlexibleYuv = l.mParameters.fastInfo.useFlexibleYuv &&
+                (previewFormat == HAL_PIXEL_FORMAT_YCrCb_420_SP ||
+                 previewFormat == HAL_PIXEL_FORMAT_YV12);
+
+        int32_t expectedFormat = useFlexibleYuv ?
+                HAL_PIXEL_FORMAT_YCbCr_420_888 : previewFormat;
+
+        if (imgBuffer.format != expectedFormat) {
             ALOGE("%s: Camera %d: Unexpected format for callback: "
-                    "%x, expected %x", __FUNCTION__, mId,
-                    imgBuffer.format, l.mParameters.previewFormat);
+                    "0x%x, expected 0x%x", __FUNCTION__, mId,
+                    imgBuffer.format, expectedFormat);
             mCallbackConsumer->unlockBuffer(imgBuffer);
             return INVALID_OPERATION;
         }
@@ -262,9 +281,28 @@
         }
     }
 
+    uint32_t destYStride = 0;
+    uint32_t destCStride = 0;
+    if (useFlexibleYuv) {
+        if (previewFormat == HAL_PIXEL_FORMAT_YV12) {
+            // Strides must align to 16 for YV12
+            destYStride = ALIGN(imgBuffer.width, 16);
+            destCStride = ALIGN(destYStride / 2, 16);
+        } else {
+            // No padding for NV21
+            ALOG_ASSERT(previewFormat == HAL_PIXEL_FORMAT_YCrCb_420_SP,
+                    "Unexpected preview format 0x%x", previewFormat);
+            destYStride = imgBuffer.width;
+            destCStride = destYStride / 2;
+        }
+    } else {
+        destYStride = imgBuffer.stride;
+        // don't care about cStride
+    }
+
     size_t bufferSize = Camera2Client::calculateBufferSize(
             imgBuffer.width, imgBuffer.height,
-            imgBuffer.format, imgBuffer.stride);
+            previewFormat, destYStride);
     size_t currentBufferSize = (mCallbackHeap == 0) ?
             0 : (mCallbackHeap->mHeap->getSize() / kCallbackHeapCount);
     if (bufferSize != currentBufferSize) {
@@ -294,7 +332,7 @@
     mCallbackHeapHead = (mCallbackHeapHead + 1) & kCallbackHeapCount;
     mCallbackHeapFree--;
 
-    // TODO: Get rid of this memcpy by passing the gralloc queue all the way
+    // TODO: Get rid of this copy by passing the gralloc queue all the way
     // to app
 
     ssize_t offset;
@@ -303,7 +341,20 @@
             mCallbackHeap->mBuffers[heapIdx]->getMemory(&offset,
                     &size);
     uint8_t *data = (uint8_t*)heap->getBase() + offset;
-    memcpy(data, imgBuffer.data, bufferSize);
+
+    if (!useFlexibleYuv) {
+        // Can just memcpy when HAL format matches API format
+        memcpy(data, imgBuffer.data, bufferSize);
+    } else {
+        res = convertFromFlexibleYuv(previewFormat, data, imgBuffer,
+                destYStride, destCStride);
+        if (res != OK) {
+            ALOGE("%s: Camera %d: Can't convert between 0x%x and 0x%x formats!",
+                    __FUNCTION__, mId, imgBuffer.format, previewFormat);
+            mCallbackConsumer->unlockBuffer(imgBuffer);
+            return BAD_VALUE;
+        }
+    }
 
     ALOGV("%s: Freeing buffer", __FUNCTION__);
     mCallbackConsumer->unlockBuffer(imgBuffer);
@@ -328,5 +379,72 @@
     return OK;
 }
 
+status_t CallbackProcessor::convertFromFlexibleYuv(int32_t previewFormat,
+        uint8_t *dst,
+        const CpuConsumer::LockedBuffer &src,
+        uint32_t dstYStride,
+        uint32_t dstCStride) const {
+
+    if (previewFormat != HAL_PIXEL_FORMAT_YCrCb_420_SP &&
+            previewFormat != HAL_PIXEL_FORMAT_YV12) {
+        ALOGE("%s: Camera %d: Unexpected preview format when using "
+                "flexible YUV: 0x%x", __FUNCTION__, mId, previewFormat);
+        return INVALID_OPERATION;
+    }
+
+    // Copy Y plane, adjusting for stride
+    const uint8_t *ySrc = src.data;
+    uint8_t *yDst = dst;
+    for (size_t row = 0; row < src.height; row++) {
+        memcpy(yDst, ySrc, src.width);
+        ySrc += src.stride;
+        yDst += dstYStride;
+    }
+
+    // Copy/swizzle chroma planes, 4:2:0 subsampling
+    const uint8_t *uSrc = src.dataCb;
+    const uint8_t *vSrc = src.dataCr;
+    size_t chromaHeight = src.height / 2;
+    size_t chromaWidth = src.width / 2;
+    ssize_t chromaGap = src.chromaStride -
+            (chromaWidth * src.chromaStep);
+    size_t dstChromaGap = dstCStride - chromaWidth;
+
+    if (previewFormat == HAL_PIXEL_FORMAT_YCrCb_420_SP) {
+        // NV21
+        uint8_t *vuDst = yDst;
+        for (size_t row = 0; row < chromaHeight; row++) {
+            for (size_t col = 0; col < chromaWidth; col++) {
+                *(vuDst++) = *vSrc;
+                *(vuDst++) = *uSrc;
+                vSrc += src.chromaStep;
+                uSrc += src.chromaStep;
+            }
+            vSrc += chromaGap;
+            uSrc += chromaGap;
+        }
+    } else {
+        // YV12
+        ALOG_ASSERT(previewFormat == HAL_PIXEL_FORMAT_YV12,
+                "Unexpected preview format 0x%x", previewFormat);
+        uint8_t *vDst = yDst;
+        uint8_t *uDst = yDst + chromaHeight * dstCStride;
+        for (size_t row = 0; row < chromaHeight; row++) {
+            for (size_t col = 0; col < chromaWidth; col++) {
+                *(vDst++) = *vSrc;
+                *(uDst++) = *uSrc;
+                vSrc += src.chromaStep;
+                uSrc += src.chromaStep;
+            }
+            vSrc += chromaGap;
+            uSrc += chromaGap;
+            vDst += dstChromaGap;
+            uDst += dstChromaGap;
+        }
+    }
+
+    return OK;
+}
+
 }; // namespace camera2
 }; // namespace android
diff --git a/services/camera/libcameraservice/camera2/CallbackProcessor.h b/services/camera/libcameraservice/camera2/CallbackProcessor.h
index 1c40a03..d851a84 100644
--- a/services/camera/libcameraservice/camera2/CallbackProcessor.h
+++ b/services/camera/libcameraservice/camera2/CallbackProcessor.h
@@ -77,6 +77,13 @@
     status_t processNewCallback(sp<Camera2Client> &client);
     // Used when shutting down
     status_t discardNewCallback();
+
+    // Convert from flexible YUV to NV21 or YV12
+    status_t convertFromFlexibleYuv(int32_t previewFormat,
+            uint8_t *dst,
+            const CpuConsumer::LockedBuffer &src,
+            uint32_t dstYStride,
+            uint32_t dstCStride) const;
 };
 
 
diff --git a/services/camera/libcameraservice/camera2/Parameters.cpp b/services/camera/libcameraservice/camera2/Parameters.cpp
index f50ca9e..910aa19 100644
--- a/services/camera/libcameraservice/camera2/Parameters.cpp
+++ b/services/camera/libcameraservice/camera2/Parameters.cpp
@@ -152,7 +152,16 @@
                 supportedPreviewFormats +=
                     CameraParameters::PIXEL_FORMAT_RGBA8888;
                 break;
+            case HAL_PIXEL_FORMAT_YCbCr_420_888:
+                // Flexible YUV allows both YV12 and NV21
+                supportedPreviewFormats +=
+                    CameraParameters::PIXEL_FORMAT_YUV420P;
+                supportedPreviewFormats += ",";
+                supportedPreviewFormats +=
+                    CameraParameters::PIXEL_FORMAT_YUV420SP;
+                break;
             // Not advertizing JPEG, RAW_SENSOR, etc, for preview formats
+            case HAL_PIXEL_FORMAT_IMPLEMENTATION_DEFINED:
             case HAL_PIXEL_FORMAT_RAW_SENSOR:
             case HAL_PIXEL_FORMAT_BLOB:
                 addComma = false;
@@ -863,6 +872,11 @@
         staticInfo(ANDROID_LENS_INFO_AVAILABLE_FOCAL_LENGTHS);
     if (!availableFocalLengths.count) return NO_INIT;
 
+    camera_metadata_ro_entry_t availableFormats =
+        staticInfo(ANDROID_SCALER_AVAILABLE_FORMATS);
+    if (!availableFormats.count) return NO_INIT;
+
+
     if (sceneModeOverrides.count > 0) {
         // sceneModeOverrides is defined to have 3 entries for each scene mode,
         // which are AE, AWB, and AF override modes the HAL wants for that scene
@@ -940,6 +954,17 @@
         }
     }
 
+    // Check if the HAL supports HAL_PIXEL_FORMAT_YCbCr_420_888
+    fastInfo.useFlexibleYuv = false;
+    for (size_t i = 0; i < availableFormats.count; i++) {
+        if (availableFormats.data.i32[i] == HAL_PIXEL_FORMAT_YCbCr_420_888) {
+            fastInfo.useFlexibleYuv = true;
+            break;
+        }
+    }
+    ALOGV("Camera %d: Flexible YUV %s supported",
+            cameraId, fastInfo.useFlexibleYuv ? "is" : "is not");
+
     return OK;
 }
 
@@ -1085,15 +1110,24 @@
         }
         camera_metadata_ro_entry_t availableFormats =
             staticInfo(ANDROID_SCALER_AVAILABLE_FORMATS);
-        for (i = 0; i < availableFormats.count; i++) {
-            if (availableFormats.data.i32[i] == validatedParams.previewFormat)
-                break;
-        }
-        if (i == availableFormats.count) {
-            ALOGE("%s: Requested preview format %s (0x%x) is not supported",
-                    __FUNCTION__, newParams.getPreviewFormat(),
-                    validatedParams.previewFormat);
-            return BAD_VALUE;
+        // If using flexible YUV, always support NV21/YV12. Otherwise, check
+        // HAL's list.
+        if (! (fastInfo.useFlexibleYuv &&
+                (validatedParams.previewFormat ==
+                        HAL_PIXEL_FORMAT_YCrCb_420_SP ||
+                 validatedParams.previewFormat ==
+                        HAL_PIXEL_FORMAT_YV12) ) ) {
+            // Not using flexible YUV format, so check explicitly
+            for (i = 0; i < availableFormats.count; i++) {
+                if (availableFormats.data.i32[i] ==
+                        validatedParams.previewFormat) break;
+            }
+            if (i == availableFormats.count) {
+                ALOGE("%s: Requested preview format %s (0x%x) is not supported",
+                        __FUNCTION__, newParams.getPreviewFormat(),
+                        validatedParams.previewFormat);
+                return BAD_VALUE;
+            }
         }
     }
 
diff --git a/services/camera/libcameraservice/camera2/Parameters.h b/services/camera/libcameraservice/camera2/Parameters.h
index 6d85037..b994ec9 100644
--- a/services/camera/libcameraservice/camera2/Parameters.h
+++ b/services/camera/libcameraservice/camera2/Parameters.h
@@ -184,6 +184,7 @@
         };
         DefaultKeyedVector<uint8_t, OverrideModes> sceneModeOverrides;
         float minFocalLength;
+        bool useFlexibleYuv;
     } fastInfo;
 
     // Quirks information; these are short-lived flags to enable workarounds for