Camera2: Still image support

- Add stream / request creation for still image capture
- Add takePicture call
- Add callback handler
- Fix shutdown bugs (wait until requests have drained)

Bug: 6243944

Change-Id: Id73eb7090e61b40b90348d1eb262f641ea5f3229
diff --git a/services/camera/libcameraservice/Camera2Client.cpp b/services/camera/libcameraservice/Camera2Client.cpp
index 1cea906..05a54b7 100644
--- a/services/camera/libcameraservice/Camera2Client.cpp
+++ b/services/camera/libcameraservice/Camera2Client.cpp
@@ -52,8 +52,10 @@
         Client(cameraService, cameraClient,
                 cameraId, cameraFacing, clientPid),
         mState(NOT_INITIALIZED),
-        mPreviewStreamId(NO_PREVIEW_STREAM),
-        mPreviewRequest(NULL)
+        mPreviewStreamId(NO_STREAM),
+        mPreviewRequest(NULL),
+        mCaptureStreamId(NO_STREAM),
+        mCaptureRequest(NULL)
 {
     ATRACE_CALL();
 
@@ -280,9 +282,16 @@
 
     stopPreviewLocked();
 
-    if (mPreviewStreamId != NO_PREVIEW_STREAM) {
+    mDevice->waitUntilDrained();
+
+    if (mPreviewStreamId != NO_STREAM) {
         mDevice->deleteStream(mPreviewStreamId);
-        mPreviewStreamId = NO_PREVIEW_STREAM;
+        mPreviewStreamId = NO_STREAM;
+    }
+
+    if (mCaptureStreamId != NO_STREAM) {
+        mDevice->deleteStream(mCaptureStreamId);
+        mCaptureStreamId = NO_STREAM;
     }
 
     CameraService::Client::disconnect();
@@ -323,7 +332,7 @@
         window = surface;
     }
 
-    return setPreviewWindow(binder,window);
+    return setPreviewWindowLocked(binder,window);
 }
 
 status_t Camera2Client::setPreviewTexture(
@@ -339,10 +348,10 @@
         binder = surfaceTexture->asBinder();
         window = new SurfaceTextureClient(surfaceTexture);
     }
-    return setPreviewWindow(binder, window);
+    return setPreviewWindowLocked(binder, window);
 }
 
-status_t Camera2Client::setPreviewWindow(const sp<IBinder>& binder,
+status_t Camera2Client::setPreviewWindowLocked(const sp<IBinder>& binder,
         const sp<ANativeWindow>& window) {
     ATRACE_CALL();
     status_t res;
@@ -351,7 +360,9 @@
         return NO_ERROR;
     }
 
-    if (mPreviewStreamId != NO_PREVIEW_STREAM) {
+    // TODO: Should wait until HAL has no remaining requests
+
+    if (mPreviewStreamId != NO_STREAM) {
         res = mDevice->deleteStream(mPreviewStreamId);
         if (res != OK) {
             return res;
@@ -359,7 +370,7 @@
     }
     res = mDevice->createStream(window,
             mParameters.previewWidth, mParameters.previewHeight,
-            CAMERA2_HAL_PIXEL_FORMAT_OPAQUE,
+            CAMERA2_HAL_PIXEL_FORMAT_OPAQUE, 0,
             &mPreviewStreamId);
     if (res != OK) {
         return res;
@@ -368,7 +379,7 @@
     mPreviewSurface = binder;
 
     if (mState == WAITING_FOR_PREVIEW_WINDOW) {
-        return startPreview();
+        return startPreviewLocked();
     }
 
     return OK;
@@ -382,11 +393,15 @@
 status_t Camera2Client::startPreview() {
     ATRACE_CALL();
     Mutex::Autolock icl(mICameraLock);
+    return startPreviewLocked();
+}
 
+status_t Camera2Client::startPreviewLocked() {
+    ATRACE_CALL();
     status_t res;
-    if (mState == PREVIEW) return INVALID_OPERATION;
+    if (mState >= PREVIEW) return INVALID_OPERATION;
 
-    if (mPreviewStreamId == NO_PREVIEW_STREAM) {
+    if (mPreviewStreamId == NO_STREAM) {
         mState = WAITING_FOR_PREVIEW_WINDOW;
         return OK;
     }
@@ -438,10 +453,28 @@
 
 void Camera2Client::stopPreviewLocked() {
     ATRACE_CALL();
-    if (mState != PREVIEW) return;
-
-    mDevice->setStreamingRequest(NULL);
-    mState = STOPPED;
+    switch (mState) {
+        case NOT_INITIALIZED:
+            ALOGE("%s: Camera %d: Call before initialized",
+                    __FUNCTION__, mCameraId);
+            break;
+        case STOPPED:
+            break;
+        case STILL_CAPTURE:
+            ALOGE("%s: Camera %d: Cannot stop preview during still capture.",
+                    __FUNCTION__, mCameraId);
+            break;
+        case RECORD:
+            // TODO: Handle record stop here
+        case PREVIEW:
+            mDevice->setStreamingRequest(NULL);
+        case WAITING_FOR_PREVIEW_WINDOW:
+            mState = STOPPED;
+            break;
+        default:
+            ALOGE("%s: Camera %d: Unknown state %d", __FUNCTION__, mCameraId,
+                    mState);
+    }
 }
 
 bool Camera2Client::previewEnabled() {
@@ -493,7 +526,95 @@
 status_t Camera2Client::takePicture(int msgType) {
     ATRACE_CALL();
     Mutex::Autolock icl(mICameraLock);
-    return BAD_VALUE;
+    status_t res;
+
+    switch (mState) {
+        case NOT_INITIALIZED:
+        case STOPPED:
+        case WAITING_FOR_PREVIEW_WINDOW:
+            ALOGE("%s: Camera %d: Cannot take picture without preview enabled",
+                    __FUNCTION__, mCameraId);
+            return INVALID_OPERATION;
+        case PREVIEW:
+        case RECORD:
+            // Good to go for takePicture
+            break;
+        case STILL_CAPTURE:
+        case VIDEO_SNAPSHOT:
+            ALOGE("%s: Camera %d: Already taking a picture",
+                    __FUNCTION__, mCameraId);
+            return INVALID_OPERATION;
+    }
+
+    Mutex::Autolock pl(mParamsLock);
+
+    res = updateCaptureStream();
+
+    if (mCaptureRequest == NULL) {
+        updateCaptureRequest();
+    }
+
+    // TODO: For video snapshot, need 3 streams here
+    camera_metadata_entry_t outputStreams;
+    uint8_t streamIds[2] = { mPreviewStreamId, mCaptureStreamId };
+    res = find_camera_metadata_entry(mCaptureRequest,
+            ANDROID_REQUEST_OUTPUT_STREAMS,
+            &outputStreams);
+    if (res == NAME_NOT_FOUND) {
+        res = add_camera_metadata_entry(mCaptureRequest,
+                ANDROID_REQUEST_OUTPUT_STREAMS,
+                streamIds, 2);
+    } else if (res == OK) {
+        res = update_camera_metadata_entry(mCaptureRequest,
+                outputStreams.index, streamIds, 2, NULL);
+    }
+
+    if (res != OK) {
+        ALOGE("%s: Camera %d: Unable to set up still image capture request: "
+                "%s (%d)",
+                __FUNCTION__, mCameraId, strerror(-res), res);
+        return res;
+    }
+
+    camera_metadata_t *captureCopy = clone_camera_metadata(mCaptureRequest);
+    if (captureCopy == NULL) {
+        ALOGE("%s: Camera %d: Unable to copy capture request for HAL device",
+                __FUNCTION__, mCameraId);
+        return NO_MEMORY;
+    }
+
+    if (mState == PREVIEW) {
+        res = mDevice->setStreamingRequest(NULL);
+        if (res != OK) {
+            ALOGE("%s: Camera %d: Unable to stop preview for still capture: "
+                    "%s (%d)",
+                    __FUNCTION__, mCameraId, strerror(-res), res);
+            return res;
+        }
+    }
+
+    res = mDevice->capture(captureCopy);
+    if (res != OK) {
+        ALOGE("%s: Camera %d: Unable to submit still image capture request: "
+                "%s (%d)",
+                __FUNCTION__, mCameraId, strerror(-res), res);
+        return res;
+    }
+
+    switch (mState) {
+        case PREVIEW:
+            mState = STILL_CAPTURE;
+            break;
+        case RECORD:
+            mState = VIDEO_SNAPSHOT;
+            break;
+        default:
+            ALOGE("%s: Camera %d: Unknown state for still capture!",
+                    __FUNCTION__, mCameraId);
+            return INVALID_OPERATION;
+    }
+
+    return OK;
 }
 
 status_t Camera2Client::setParameters(const String8& params) {
@@ -991,6 +1112,7 @@
     mParameters.videoStabilization = videoStabilization;
 
     updatePreviewRequest();
+    updateCaptureRequest();
 
     return OK;
 }
@@ -1013,6 +1135,65 @@
 
 /** Device-related methods */
 
+void Camera2Client::onCaptureAvailable() {
+    ATRACE_CALL();
+    status_t res;
+    sp<ICameraClient> currentClient;
+    CpuConsumer::LockedBuffer imgBuffer;
+    {
+        Mutex::Autolock icl(mICameraLock);
+
+        // TODO: Signal errors here upstream
+        if (mState != STILL_CAPTURE && mState != VIDEO_SNAPSHOT) {
+            ALOGE("%s: Camera %d: Still image produced unexpectedly!",
+                    __FUNCTION__, mCameraId);
+            return;
+        }
+
+        res = mCaptureConsumer->lockNextBuffer(&imgBuffer);
+        if (res != OK) {
+            ALOGE("%s: Camera %d: Error receiving still image buffer: %s (%d)",
+                    __FUNCTION__, mCameraId, strerror(-res), res);
+            return;
+        }
+
+        if (imgBuffer.format != HAL_PIXEL_FORMAT_BLOB) {
+            ALOGE("%s: Camera %d: Unexpected format for still image: "
+                    "%x, expected %x", __FUNCTION__, mCameraId,
+                    imgBuffer.format,
+                    HAL_PIXEL_FORMAT_BLOB);
+            mCaptureConsumer->unlockBuffer(imgBuffer);
+            return;
+        }
+
+        // TODO: Optimize this to avoid memcopy
+        void* captureMemory = mCaptureHeap->getBase();
+        size_t size = mCaptureHeap->getSize();
+        memcpy(captureMemory, imgBuffer.data, size);
+
+        mCaptureConsumer->unlockBuffer(imgBuffer);
+
+        currentClient = mCameraClient;
+        switch (mState) {
+            case STILL_CAPTURE:
+                mState = STOPPED;
+                break;
+            case VIDEO_SNAPSHOT:
+                mState = RECORD;
+                break;
+            default:
+                ALOGE("%s: Camera %d: Unexpected state %d", __FUNCTION__,
+                        mCameraId, mState);
+                break;
+        }
+    }
+    // Call outside mICameraLock to allow re-entrancy from notification
+    if (currentClient != 0) {
+        currentClient->dataCallback(CAMERA_MSG_COMPRESSED_IMAGE,
+                mCaptureMemory, NULL);
+    }
+}
+
 camera_metadata_entry_t Camera2Client::staticInfo(uint32_t tag,
         size_t minCount, size_t maxCount) {
     status_t res;
@@ -1752,6 +1933,88 @@
     return OK;
 }
 
+status_t Camera2Client::updateCaptureStream() {
+    status_t res;
+    // Find out buffer size for JPEG
+    camera_metadata_entry_t maxJpegSize =
+            staticInfo(ANDROID_JPEG_MAX_SIZE);
+    if (maxJpegSize.count == 0) {
+        ALOGE("%s: Camera %d: Can't find ANDROID_JPEG_MAX_SIZE!",
+                __FUNCTION__, mCameraId);
+        return INVALID_OPERATION;
+    }
+
+    if (mCaptureConsumer == 0) {
+        // Create CPU buffer queue endpoint
+        mCaptureConsumer = new CpuConsumer(1);
+        mCaptureConsumer->setFrameAvailableListener(new CaptureWaiter(this));
+        mCaptureConsumer->setName(String8("Camera2Client::CaptureConsumer"));
+        mCaptureWindow = new SurfaceTextureClient(
+            mCaptureConsumer->getProducerInterface());
+        // Create memory for API consumption
+        mCaptureHeap = new MemoryHeapBase(maxJpegSize.data.i32[0], 0,
+                "Camera2Client::CaptureHeap");
+        if (mCaptureHeap->getSize() == 0) {
+            ALOGE("%s: Camera %d: Unable to allocate memory for capture",
+                    __FUNCTION__, mCameraId);
+            return NO_MEMORY;
+        }
+        mCaptureMemory = new MemoryBase(mCaptureHeap,
+                0, maxJpegSize.data.i32[0]);
+    }
+    if (mCaptureStreamId == NO_STREAM) {
+        // Create stream for HAL production
+        res = mDevice->createStream(mCaptureWindow,
+                mParameters.pictureWidth, mParameters.pictureHeight,
+                HAL_PIXEL_FORMAT_BLOB, maxJpegSize.data.i32[0],
+                &mCaptureStreamId);
+        if (res != OK) {
+            ALOGE("%s: Camera %d: Can't create output stream for capture: "
+                    "%s (%d)", __FUNCTION__, mCameraId, strerror(-res), res);
+            return res;
+        }
+
+    } else {
+        // Check if stream parameters have to change
+        uint32_t currentWidth, currentHeight;
+        res = mDevice->getStreamInfo(mCaptureStreamId,
+                &currentWidth, &currentHeight, 0);
+        if (res != OK) {
+            ALOGE("%s: Camera %d: Error querying capture output stream info: "
+                    "%s (%d)", __FUNCTION__, mCameraId, strerror(-res), res);
+            return res;
+        }
+        if (currentWidth != (uint32_t)mParameters.pictureWidth ||
+                currentHeight != (uint32_t)mParameters.pictureHeight) {
+            res = mDevice->deleteStream(mCaptureStreamId);
+            if (res != OK) {
+                ALOGE("%s: Camera %d: Unable to delete old output stream "
+                        "for capture: %s (%d)", __FUNCTION__, mCameraId,
+                        strerror(-res), res);
+                return res;
+            }
+            mCaptureStreamId = NO_STREAM;
+            return updateCaptureStream();
+        }
+    }
+    return OK;
+}
+status_t Camera2Client::updateCaptureRequest() {
+    ATRACE_CALL();
+    status_t res;
+    if (mCaptureRequest == NULL) {
+        res = mDevice->createDefaultRequest(CAMERA2_TEMPLATE_STILL_CAPTURE,
+                &mCaptureRequest);
+        if (res != OK) {
+            ALOGE("%s: Camera %d: Unable to create default still image request:"
+                    " %s (%d)", __FUNCTION__, mCameraId, strerror(-res), res);
+            return res;
+        }
+    }
+    // TODO: Adjust for params changes
+    return OK;
+}
+
 int Camera2Client::formatStringToEnum(const char *format) {
     return
         !strcmp(format, CameraParameters::PIXEL_FORMAT_YUV422SP) ?
diff --git a/services/camera/libcameraservice/Camera2Client.h b/services/camera/libcameraservice/Camera2Client.h
index e2a0086..5457343 100644
--- a/services/camera/libcameraservice/Camera2Client.h
+++ b/services/camera/libcameraservice/Camera2Client.h
@@ -20,6 +20,9 @@
 #include "Camera2Device.h"
 #include "CameraService.h"
 #include "camera/CameraParameters.h"
+#include <binder/MemoryBase.h>
+#include <binder/MemoryHeapBase.h>
+#include <gui/CpuConsumer.h>
 
 namespace android {
 
@@ -73,7 +76,8 @@
         WAITING_FOR_PREVIEW_WINDOW,
         PREVIEW,
         RECORD,
-        STILL_CAPTURE
+        STILL_CAPTURE,
+        VIDEO_SNAPSHOT
     } mState;
 
     /** ICamera interface-related private members */
@@ -82,11 +86,15 @@
     // Ensures serialization between incoming ICamera calls
     mutable Mutex mICameraLock;
 
-    status_t setPreviewWindow(const sp<IBinder>& binder,
-            const sp<ANativeWindow>& window);
-    void stopPreviewLocked();
+    // The following must be called with mICamaeraLock already locked
 
-    // Mutex that must be locked before accessing mParams, mParamsFlattened
+    status_t setPreviewWindowLocked(const sp<IBinder>& binder,
+            const sp<ANativeWindow>& window);
+
+    void stopPreviewLocked();
+    status_t startPreviewLocked();
+
+    // Mutex that must be locked before accessing mParameters, mParamsFlattened
     mutable Mutex mParamsLock;
     String8 mParamsFlattened;
     // Current camera state; this is the contents of the CameraParameters object
@@ -164,16 +172,34 @@
 
     /** Camera device-related private members */
 
+    // Simple listener that forwards frame available notifications from
+    // a CPU consumer to the capture notification
+    class CaptureWaiter: public CpuConsumer::FrameAvailableListener {
+      public:
+        CaptureWaiter(Camera2Client *parent) : mParent(parent) {}
+        void onFrameAvailable() { mParent->onCaptureAvailable(); }
+      private:
+        Camera2Client *mParent;
+    };
+
+    void onCaptureAvailable();
+
     // Number of zoom steps to simulate
     static const unsigned int NUM_ZOOM_STEPS = 10;
-    // Used with mPreviewStreamId
-    static const int NO_PREVIEW_STREAM = -1;
+    // Used with mPreviewStreamId, mCaptureStreamId
+    static const int NO_STREAM = -1;
 
     sp<IBinder> mPreviewSurface;
     int mPreviewStreamId;
     camera_metadata_t *mPreviewRequest;
 
+    int mCaptureStreamId;
+    sp<CpuConsumer>    mCaptureConsumer;
+    sp<ANativeWindow>  mCaptureWindow;
+    sp<CaptureWaiter>  mCaptureWaiter;
     camera_metadata_t *mCaptureRequest;
+    sp<MemoryHeapBase> mCaptureHeap;
+    sp<MemoryBase>     mCaptureMemory;
 
     sp<Camera2Device> mDevice;
 
@@ -194,6 +220,11 @@
     // Update preview request based on mParams
     status_t updatePreviewRequest();
 
+    // Update capture request based on mParams
+    status_t updateCaptureRequest();
+    // Update capture stream based on mParams
+    status_t updateCaptureStream();
+
     // Convert camera1 preview format string to camera2 enum
     static int formatStringToEnum(const char *format);
     static const char *formatEnumToString(int format);
diff --git a/services/camera/libcameraservice/Camera2Device.cpp b/services/camera/libcameraservice/Camera2Device.cpp
index b81d54c..8cb872a 100644
--- a/services/camera/libcameraservice/Camera2Device.cpp
+++ b/services/camera/libcameraservice/Camera2Device.cpp
@@ -111,8 +111,15 @@
     return mDeviceInfo;
 }
 
-status_t Camera2Device::setStreamingRequest(camera_metadata_t* request)
-{
+status_t Camera2Device::capture(camera_metadata_t* request) {
+    ALOGV("%s: E", __FUNCTION__);
+
+    mRequestQueue.enqueue(request);
+    return OK;
+}
+
+
+status_t Camera2Device::setStreamingRequest(camera_metadata_t* request) {
     ALOGV("%s: E", __FUNCTION__);
 
     mRequestQueue.setStreamSlot(request);
@@ -120,13 +127,13 @@
 }
 
 status_t Camera2Device::createStream(sp<ANativeWindow> consumer,
-        uint32_t width, uint32_t height, int format, int *id) {
+        uint32_t width, uint32_t height, int format, size_t size, int *id) {
     status_t res;
     ALOGV("%s: E", __FUNCTION__);
 
     sp<StreamAdapter> stream = new StreamAdapter(mDevice);
 
-    res = stream->connectToDevice(consumer, width, height, format);
+    res = stream->connectToDevice(consumer, width, height, format, size);
     if (res != OK) {
         ALOGE("%s: Camera %d: Unable to create stream (%d x %d, format %x):"
                 "%s (%d)",
@@ -140,6 +147,31 @@
     return OK;
 }
 
+status_t Camera2Device::getStreamInfo(int id,
+        uint32_t *width, uint32_t *height, uint32_t *format) {
+    ALOGV("%s: E", __FUNCTION__);
+    bool found = false;
+    StreamList::iterator streamI;
+    for (streamI = mStreams.begin();
+         streamI != mStreams.end(); streamI++) {
+        if ((*streamI)->getId() == id) {
+            found = true;
+            break;
+        }
+    }
+    if (!found) {
+        ALOGE("%s: Camera %d: Stream %d does not exist",
+                __FUNCTION__, mId, id);
+        return BAD_VALUE;
+    }
+
+    if (width) *width = (*streamI)->getWidth();
+    if (height) *height = (*streamI)->getHeight();
+    if (format) *format = (*streamI)->getFormat();
+
+    return OK;
+}
+
 status_t Camera2Device::deleteStream(int id) {
     ALOGV("%s: E", __FUNCTION__);
 
@@ -163,7 +195,29 @@
 status_t Camera2Device::createDefaultRequest(int templateId,
         camera_metadata_t **request) {
     ALOGV("%s: E", __FUNCTION__);
-    return mDevice->ops->construct_default_request(mDevice, templateId, request);
+    return mDevice->ops->construct_default_request(
+        mDevice, templateId, request);
+}
+
+status_t Camera2Device::waitUntilDrained() {
+    static const uint32_t kSleepTime = 50000; // 50 ms
+    static const uint32_t kMaxSleepTime = 10000000; // 10 s
+
+    if (mRequestQueue.getBufferCount() ==
+            CAMERA2_REQUEST_QUEUE_IS_BOTTOMLESS) return INVALID_OPERATION;
+
+    // TODO: Set up notifications from HAL, instead of sleeping here
+    uint32_t totalTime = 0;
+    while (mDevice->ops->get_in_progress_count(mDevice) > 0) {
+        usleep(kSleepTime);
+        totalTime += kSleepTime;
+        if (totalTime > kMaxSleepTime) {
+            ALOGE("%s: Waited %d us, requests still in flight", __FUNCTION__,
+                    totalTime);
+            return TIMED_OUT;
+        }
+    }
+    return OK;
 }
 
 /**
@@ -463,8 +517,9 @@
     disconnect();
 }
 
-status_t Camera2Device::StreamAdapter::connectToDevice(sp<ANativeWindow> consumer,
-        uint32_t width, uint32_t height, int format) {
+status_t Camera2Device::StreamAdapter::connectToDevice(
+        sp<ANativeWindow> consumer,
+        uint32_t width, uint32_t height, int format, size_t size) {
     status_t res;
 
     if (mState != DISCONNECTED) return INVALID_OPERATION;
@@ -476,6 +531,7 @@
     mConsumerInterface = consumer;
     mWidth = width;
     mHeight = height;
+    mSize = (format == HAL_PIXEL_FORMAT_BLOB) ? size : 0;
     mFormatRequested = format;
 
     // Allocate device-side stream interface
@@ -534,13 +590,24 @@
         return res;
     }
 
-    res = native_window_set_buffers_geometry(mConsumerInterface.get(),
-            mWidth, mHeight, mFormat);
-    if (res != OK) {
-        ALOGE("%s: Unable to configure stream buffer geometry"
-                " %d x %d, format 0x%x for stream %d",
-                __FUNCTION__, mWidth, mHeight, mFormat, mId);
-        return res;
+    if (mFormat == HAL_PIXEL_FORMAT_BLOB) {
+        res = native_window_set_buffers_geometry(mConsumerInterface.get(),
+                mSize, 1, mFormat);
+        if (res != OK) {
+            ALOGE("%s: Unable to configure compressed stream buffer geometry"
+                    " %d x %d, size %d for stream %d",
+                    __FUNCTION__, mWidth, mHeight, mSize, mId);
+            return res;
+        }
+    } else {
+        res = native_window_set_buffers_geometry(mConsumerInterface.get(),
+                mWidth, mHeight, mFormat);
+        if (res != OK) {
+            ALOGE("%s: Unable to configure stream buffer geometry"
+                    " %d x %d, format 0x%x for stream %d",
+                    __FUNCTION__, mWidth, mHeight, mFormat, mId);
+            return res;
+        }
     }
 
     int maxConsumerBuffers;
@@ -641,10 +708,6 @@
     return OK;
 }
 
-int Camera2Device::StreamAdapter::getId() {
-    return mId;
-}
-
 const camera2_stream_ops *Camera2Device::StreamAdapter::getStreamOps() {
     return static_cast<camera2_stream_ops *>(this);
 }
diff --git a/services/camera/libcameraservice/Camera2Device.h b/services/camera/libcameraservice/Camera2Device.h
index 2da3ade..7071561 100644
--- a/services/camera/libcameraservice/Camera2Device.h
+++ b/services/camera/libcameraservice/Camera2Device.h
@@ -37,17 +37,58 @@
 
     camera_metadata_t* info();
 
-    status_t setStreamingRequest(camera_metadata_t* request);
+    /**
+     * Submit request for capture. The Camera2Device takes ownership of the
+     * passed-in buffer.
+     */
+    status_t capture(camera_metadata_t *request);
 
+    /**
+     * Submit request for streaming. The Camera2Device makes a copy of the
+     * passed-in buffer and the caller retains ownership.
+     */
+    status_t setStreamingRequest(camera_metadata_t *request);
+
+    /**
+     * Create an output stream of the requested size and format.
+     *
+     * If format is CAMERA2_HAL_PIXEL_FORMAT_OPAQUE, then the HAL device selects
+     * an appropriate format; it can be queried with getStreamInfo.
+     *
+     * If format is HAL_PIXEL_FORMAT_COMPRESSED, the size parameter must be
+     * equal to the size in bytes of the buffers to allocate for the stream. For
+     * other formats, the size parameter is ignored.
+     */
     status_t createStream(sp<ANativeWindow> consumer,
-            uint32_t width, uint32_t height, int format,
+            uint32_t width, uint32_t height, int format, size_t size,
             int *id);
 
+    /**
+     * Get information about a given stream.
+     */
+    status_t getStreamInfo(int id,
+            uint32_t *width, uint32_t *height, uint32_t *format);
+
+    /**
+     * Delete stream. Must not be called if there are requests in flight which
+     * reference that stream.
+     */
     status_t deleteStream(int id);
 
+    /**
+     * Create a metadata buffer with fields that the HAL device believes are
+     * best for the given use case
+     */
     status_t createDefaultRequest(int templateId,
             camera_metadata_t **request);
 
+    /**
+     * Wait until all requests have been processed. Returns INVALID_OPERATION if
+     * the streaming slot is not empty, or TIMED_OUT if the requests haven't
+     * finished processing in 10 seconds.
+     */
+    status_t waitUntilDrained();
+
   private:
 
     const int mId;
@@ -150,13 +191,27 @@
 
         ~StreamAdapter();
 
+        /**
+         * Create a HAL device stream of the requested size and format.
+         *
+         * If format is CAMERA2_HAL_PIXEL_FORMAT_OPAQUE, then the HAL device
+         * selects an appropriate format; it can be queried with getFormat.
+         *
+         * If format is HAL_PIXEL_FORMAT_COMPRESSED, the size parameter must
+         * be equal to the size in bytes of the buffers to allocate for the
+         * stream. For other formats, the size parameter is ignored.
+         */
         status_t connectToDevice(sp<ANativeWindow> consumer,
-                uint32_t width, uint32_t height, int format);
+                uint32_t width, uint32_t height, int format, size_t size);
 
         status_t disconnect();
 
-        // Get stream ID. Only valid after a successful connectToDevice call.
-        int      getId();
+        // Get stream parameters.
+        // Only valid after a successful connectToDevice call.
+        int      getId() const     { return mId; }
+        uint32_t getWidth() const  { return mWidth; }
+        uint32_t getHeight() const { return mHeight; }
+        uint32_t getFormat() const { return mFormat; }
 
       private:
         enum {
@@ -174,6 +229,7 @@
         uint32_t mWidth;
         uint32_t mHeight;
         uint32_t mFormat;
+        size_t   mSize;
         uint32_t mUsage;
         uint32_t mMaxProducerBuffers;
         uint32_t mMaxConsumerBuffers;