Merge "Handles duplicated NAL start code to fix crash on HLS streams."
diff --git a/camera/Android.mk b/camera/Android.mk
index 7286f92..3e7e5a5 100644
--- a/camera/Android.mk
+++ b/camera/Android.mk
@@ -1,14 +1,21 @@
-LOCAL_PATH:= $(call my-dir)
+CAMERA_CLIENT_LOCAL_PATH:= $(call my-dir)
+include $(call all-subdir-makefiles)
 include $(CLEAR_VARS)
 
+LOCAL_PATH := $(CAMERA_CLIENT_LOCAL_PATH)
+
 LOCAL_SRC_FILES:= \
 	Camera.cpp \
+	CameraMetadata.cpp \
 	CameraParameters.cpp \
 	ICamera.cpp \
 	ICameraClient.cpp \
 	ICameraService.cpp \
 	ICameraRecordingProxy.cpp \
-	ICameraRecordingProxyListener.cpp
+	ICameraRecordingProxyListener.cpp \
+	IProCameraUser.cpp \
+	IProCameraCallbacks.cpp \
+	ProCamera.cpp \
 
 LOCAL_SHARED_LIBRARIES := \
 	libcutils \
@@ -16,7 +23,11 @@
 	libbinder \
 	libhardware \
 	libui \
-	libgui
+	libgui \
+	libcamera_metadata \
+
+LOCAL_C_INCLUDES += \
+	system/media/camera/include \
 
 LOCAL_MODULE:= libcamera_client
 
diff --git a/camera/Camera.cpp b/camera/Camera.cpp
index 3aaacaf..be395ba 100644
--- a/camera/Camera.cpp
+++ b/camera/Camera.cpp
@@ -120,9 +120,10 @@
 {
     ALOGV("connect");
     sp<Camera> c = new Camera();
+    sp<ICameraClient> cl = c;
     const sp<ICameraService>& cs = getCameraService();
     if (cs != 0) {
-        c->mCamera = cs->connect(c, cameraId);
+        c->mCamera = cs->connect(cl, cameraId);
     }
     if (c->mCamera != 0) {
         c->mCamera->asBinder()->linkToDeath(c);
diff --git a/services/camera/libcameraservice/camera2/CameraMetadata.cpp b/camera/CameraMetadata.cpp
similarity index 96%
rename from services/camera/libcameraservice/camera2/CameraMetadata.cpp
rename to camera/CameraMetadata.cpp
index 835587d..fdd0610 100644
--- a/services/camera/libcameraservice/camera2/CameraMetadata.cpp
+++ b/camera/CameraMetadata.cpp
@@ -18,11 +18,10 @@
 #include <utils/Log.h>
 #include <utils/Errors.h>
 
-#include "CameraMetadata.h"
+#include <camera/CameraMetadata.h>
 
 namespace android {
 
-namespace camera2 {
 CameraMetadata::CameraMetadata() :
         mBuffer(NULL) {
 }
@@ -36,6 +35,10 @@
     mBuffer = clone_camera_metadata(other.mBuffer);
 }
 
+CameraMetadata::CameraMetadata(camera_metadata_t *buffer) : mBuffer(NULL) {
+    acquire(buffer);
+}
+
 CameraMetadata &CameraMetadata::operator=(const CameraMetadata &other) {
     return operator=(other.mBuffer);
 }
@@ -205,6 +208,11 @@
     return res;
 }
 
+bool CameraMetadata::exists(uint32_t tag) const {
+    camera_metadata_ro_entry entry;
+    return find_camera_metadata_ro_entry(mBuffer, tag, &entry) == 0;
+}
+
 camera_metadata_entry_t CameraMetadata::find(uint32_t tag) {
     status_t res;
     camera_metadata_entry entry;
@@ -292,5 +300,4 @@
     return OK;
 }
 
-}; // namespace camera2
 }; // namespace android
diff --git a/camera/ICameraService.cpp b/camera/ICameraService.cpp
index f2d367e..8237c66 100644
--- a/camera/ICameraService.cpp
+++ b/camera/ICameraService.cpp
@@ -65,6 +65,17 @@
         remote()->transact(BnCameraService::CONNECT, data, &reply);
         return interface_cast<ICamera>(reply.readStrongBinder());
     }
+
+    // connect to camera service (pro client)
+    virtual sp<IProCameraUser> connect(const sp<IProCameraCallbacks>& cameraCb, int cameraId)
+    {
+        Parcel data, reply;
+        data.writeInterfaceToken(ICameraService::getInterfaceDescriptor());
+        data.writeStrongBinder(cameraCb->asBinder());
+        data.writeInt32(cameraId);
+        remote()->transact(BnCameraService::CONNECT_PRO, data, &reply);
+        return interface_cast<IProCameraUser>(reply.readStrongBinder());
+    }
 };
 
 IMPLEMENT_META_INTERFACE(CameraService, "android.hardware.ICameraService");
@@ -97,6 +108,13 @@
             reply->writeStrongBinder(camera->asBinder());
             return NO_ERROR;
         } break;
+        case CONNECT_PRO: {
+            CHECK_INTERFACE(ICameraService, data, reply);
+            sp<IProCameraCallbacks> cameraClient = interface_cast<IProCameraCallbacks>(data.readStrongBinder());
+            sp<IProCameraUser> camera = connect(cameraClient, data.readInt32());
+            reply->writeStrongBinder(camera->asBinder());
+            return NO_ERROR;
+        } break;
         default:
             return BBinder::onTransact(code, data, reply, flags);
     }
diff --git a/camera/IProCameraCallbacks.cpp b/camera/IProCameraCallbacks.cpp
new file mode 100644
index 0000000..6cd36bf
--- /dev/null
+++ b/camera/IProCameraCallbacks.cpp
@@ -0,0 +1,188 @@
+/*
+**
+** Copyright 2013, The Android Open Source Project
+**
+** 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 "IProCameraCallbacks"
+#include <utils/Log.h>
+#include <stdint.h>
+#include <sys/types.h>
+
+#include <binder/Parcel.h>
+#include <gui/IGraphicBufferProducer.h>
+#include <gui/Surface.h>
+#include <utils/Mutex.h>
+
+#include <camera/IProCameraCallbacks.h>
+
+#include <system/camera_metadata.h>
+
+namespace android {
+
+enum {
+    NOTIFY_CALLBACK = IBinder::FIRST_CALL_TRANSACTION,
+    DATA_CALLBACK,
+    DATA_CALLBACK_TIMESTAMP,
+    LOCK_STATUS_CHANGED,
+    RESULT_RECEIVED,
+};
+
+void readMetadata(const Parcel& data, camera_metadata_t** out);
+void writeMetadata(Parcel& data, camera_metadata_t* metadata);
+
+class BpProCameraCallbacks: public BpInterface<IProCameraCallbacks>
+{
+public:
+    BpProCameraCallbacks(const sp<IBinder>& impl)
+        : BpInterface<IProCameraCallbacks>(impl)
+    {
+    }
+
+    // generic callback from camera service to app
+    void notifyCallback(int32_t msgType, int32_t ext1, int32_t ext2)
+    {
+        ALOGV("notifyCallback");
+        Parcel data, reply;
+        data.writeInterfaceToken(IProCameraCallbacks::getInterfaceDescriptor());
+        data.writeInt32(msgType);
+        data.writeInt32(ext1);
+        data.writeInt32(ext2);
+        remote()->transact(NOTIFY_CALLBACK, data, &reply, IBinder::FLAG_ONEWAY);
+    }
+
+    // generic data callback from camera service to app with image data
+    void dataCallback(int32_t msgType, const sp<IMemory>& imageData,
+                      camera_frame_metadata_t *metadata)
+    {
+        ALOGV("dataCallback");
+        Parcel data, reply;
+        data.writeInterfaceToken(IProCameraCallbacks::getInterfaceDescriptor());
+        data.writeInt32(msgType);
+        data.writeStrongBinder(imageData->asBinder());
+        if (metadata) {
+            data.writeInt32(metadata->number_of_faces);
+            data.write(metadata->faces,
+                            sizeof(camera_face_t) * metadata->number_of_faces);
+        }
+        remote()->transact(DATA_CALLBACK, data, &reply, IBinder::FLAG_ONEWAY);
+    }
+
+    // generic data callback from camera service to app with image data
+    void dataCallbackTimestamp(nsecs_t timestamp, int32_t msgType,
+                                                  const sp<IMemory>& imageData)
+    {
+        ALOGV("dataCallback");
+        Parcel data, reply;
+        data.writeInterfaceToken(IProCameraCallbacks::getInterfaceDescriptor());
+        data.writeInt64(timestamp);
+        data.writeInt32(msgType);
+        data.writeStrongBinder(imageData->asBinder());
+        remote()->transact(DATA_CALLBACK_TIMESTAMP, data, &reply,
+                                                          IBinder::FLAG_ONEWAY);
+    }
+
+    void onLockStatusChanged(LockStatus newLockStatus) {
+        ALOGV("onLockStatusChanged");
+        Parcel data, reply;
+        data.writeInterfaceToken(IProCameraCallbacks::getInterfaceDescriptor());
+        data.writeInt32(newLockStatus);
+        remote()->transact(LOCK_STATUS_CHANGED, data, &reply,
+                           IBinder::FLAG_ONEWAY);
+    }
+
+    void onResultReceived(int32_t frameId, camera_metadata* result) {
+        ALOGV("onResultReceived");
+        Parcel data, reply;
+        data.writeInterfaceToken(IProCameraCallbacks::getInterfaceDescriptor());
+        data.writeInt32(frameId);
+        writeMetadata(data, result);
+        remote()->transact(RESULT_RECEIVED, data, &reply, IBinder::FLAG_ONEWAY);
+    }
+};
+
+IMPLEMENT_META_INTERFACE(ProCameraCallbacks,
+                                        "android.hardware.IProCameraCallbacks");
+
+// ----------------------------------------------------------------------
+
+status_t BnProCameraCallbacks::onTransact(
+    uint32_t code, const Parcel& data, Parcel* reply, uint32_t flags)
+{
+    ALOGV("onTransact - code = %d", code);
+    switch(code) {
+        case NOTIFY_CALLBACK: {
+            ALOGV("NOTIFY_CALLBACK");
+            CHECK_INTERFACE(IProCameraCallbacks, data, reply);
+            int32_t msgType = data.readInt32();
+            int32_t ext1 = data.readInt32();
+            int32_t ext2 = data.readInt32();
+            notifyCallback(msgType, ext1, ext2);
+            return NO_ERROR;
+        } break;
+        case DATA_CALLBACK: {
+            ALOGV("DATA_CALLBACK");
+            CHECK_INTERFACE(IProCameraCallbacks, data, reply);
+            int32_t msgType = data.readInt32();
+            sp<IMemory> imageData = interface_cast<IMemory>(
+                                                       data.readStrongBinder());
+            camera_frame_metadata_t *metadata = NULL;
+            if (data.dataAvail() > 0) {
+                metadata = new camera_frame_metadata_t;
+                metadata->number_of_faces = data.readInt32();
+                metadata->faces = (camera_face_t *) data.readInplace(
+                        sizeof(camera_face_t) * metadata->number_of_faces);
+            }
+            dataCallback(msgType, imageData, metadata);
+            if (metadata) delete metadata;
+            return NO_ERROR;
+        } break;
+        case DATA_CALLBACK_TIMESTAMP: {
+            ALOGV("DATA_CALLBACK_TIMESTAMP");
+            CHECK_INTERFACE(IProCameraCallbacks, data, reply);
+            nsecs_t timestamp = data.readInt64();
+            int32_t msgType = data.readInt32();
+            sp<IMemory> imageData = interface_cast<IMemory>(
+                                                       data.readStrongBinder());
+            dataCallbackTimestamp(timestamp, msgType, imageData);
+            return NO_ERROR;
+        } break;
+        case LOCK_STATUS_CHANGED: {
+            ALOGV("LOCK_STATUS_CHANGED");
+            CHECK_INTERFACE(IProCameraCallbacks, data, reply);
+            LockStatus newLockStatus
+                                 = static_cast<LockStatus>(data.readInt32());
+            onLockStatusChanged(newLockStatus);
+            return NO_ERROR;
+        } break;
+        case RESULT_RECEIVED: {
+            ALOGV("RESULT_RECEIVED");
+            CHECK_INTERFACE(IProCameraCallbacks, data, reply);
+            int32_t frameId = data.readInt32();
+            camera_metadata_t *result = NULL;
+            readMetadata(data, &result);
+            onResultReceived(frameId, result);
+            return NO_ERROR;
+            break;
+        }
+        default:
+            return BBinder::onTransact(code, data, reply, flags);
+    }
+}
+
+// ----------------------------------------------------------------------------
+
+}; // namespace android
+
diff --git a/camera/IProCameraUser.cpp b/camera/IProCameraUser.cpp
new file mode 100644
index 0000000..c9d98aa
--- /dev/null
+++ b/camera/IProCameraUser.cpp
@@ -0,0 +1,409 @@
+/*
+**
+** Copyright 2013, The Android Open Source Project
+**
+** 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 "IProCameraUser"
+#include <utils/Log.h>
+#include <stdint.h>
+#include <sys/types.h>
+#include <binder/Parcel.h>
+#include <camera/IProCameraUser.h>
+#include <gui/IGraphicBufferProducer.h>
+#include <gui/Surface.h>
+#include <system/camera_metadata.h>
+
+namespace android {
+
+typedef Parcel::WritableBlob WritableBlob;
+typedef Parcel::ReadableBlob ReadableBlob;
+
+enum {
+    DISCONNECT = IBinder::FIRST_CALL_TRANSACTION,
+    CONNECT,
+    EXCLUSIVE_TRY_LOCK,
+    EXCLUSIVE_LOCK,
+    EXCLUSIVE_UNLOCK,
+    HAS_EXCLUSIVE_LOCK,
+    SUBMIT_REQUEST,
+    CANCEL_REQUEST,
+    REQUEST_STREAM,
+    CANCEL_STREAM,
+    CREATE_STREAM,
+    CREATE_DEFAULT_REQUEST,
+    GET_CAMERA_INFO,
+};
+
+/**
+  * Caller becomes the owner of the new metadata
+  * 'const Parcel' doesnt prevent us from calling the read functions.
+  *  which is interesting since it changes the internal state
+  */
+void readMetadata(const Parcel& data, camera_metadata_t** out) {
+    camera_metadata_t* metadata;
+
+    // arg0 = metadataSize (int32)
+    size_t metadataSize = static_cast<size_t>(data.readInt32());
+
+    if (metadataSize == 0) {
+        if (out) {
+            *out = NULL;
+        }
+        return;
+    }
+
+    // NOTE: this doesn't make sense to me. shouldnt the blob
+    // know how big it is? why do we have to specify the size
+    // to Parcel::readBlob ?
+
+    ReadableBlob blob;
+    // arg1 = metadata (blob)
+    {
+        data.readBlob(metadataSize, &blob);
+        const camera_metadata_t* tmp =
+                       reinterpret_cast<const camera_metadata_t*>(blob.data());
+        size_t entry_capacity = get_camera_metadata_entry_capacity(tmp);
+        size_t data_capacity = get_camera_metadata_data_capacity(tmp);
+
+        metadata = allocate_camera_metadata(entry_capacity, data_capacity);
+        copy_camera_metadata(metadata, metadataSize, tmp);
+    }
+    blob.release();
+
+    if (out) {
+        *out = metadata;
+    } else {
+        free_camera_metadata(metadata);
+    }
+}
+
+/**
+  * Caller retains ownership of metadata
+  * - Write 2 (int32 + blob) args in the current position
+  */
+void writeMetadata(Parcel& data, camera_metadata_t* metadata) {
+    // arg0 = metadataSize (int32)
+    size_t metadataSize;
+
+    if (metadata == NULL) {
+        data.writeInt32(0);
+        return;
+    }
+
+    metadataSize = get_camera_metadata_compact_size(metadata);
+    data.writeInt32(static_cast<int32_t>(metadataSize));
+
+    // arg1 = metadata (blob)
+    WritableBlob blob;
+    {
+        data.writeBlob(metadataSize, &blob);
+        copy_camera_metadata(blob.data(), metadataSize, metadata);
+    }
+    blob.release();
+}
+
+class BpProCameraUser: public BpInterface<IProCameraUser>
+{
+public:
+    BpProCameraUser(const sp<IBinder>& impl)
+        : BpInterface<IProCameraUser>(impl)
+    {
+    }
+
+    // disconnect from camera service
+    void disconnect()
+    {
+        ALOGV("disconnect");
+        Parcel data, reply;
+        data.writeInterfaceToken(IProCameraUser::getInterfaceDescriptor());
+        remote()->transact(DISCONNECT, data, &reply);
+    }
+
+    virtual status_t connect(const sp<IProCameraCallbacks>& cameraClient)
+    {
+        Parcel data, reply;
+        data.writeInterfaceToken(IProCameraUser::getInterfaceDescriptor());
+        data.writeStrongBinder(cameraClient->asBinder());
+        remote()->transact(CONNECT, data, &reply);
+        return reply.readInt32();
+    }
+
+    /* Shared ProCameraUser */
+
+    virtual status_t exclusiveTryLock()
+    {
+        Parcel data, reply;
+        data.writeInterfaceToken(IProCameraUser::getInterfaceDescriptor());
+        remote()->transact(EXCLUSIVE_TRY_LOCK, data, &reply);
+        return reply.readInt32();
+    }
+    virtual status_t exclusiveLock()
+    {
+        Parcel data, reply;
+        data.writeInterfaceToken(IProCameraUser::getInterfaceDescriptor());
+        remote()->transact(EXCLUSIVE_LOCK, data, &reply);
+        return reply.readInt32();
+    }
+
+    virtual status_t exclusiveUnlock()
+    {
+        Parcel data, reply;
+        data.writeInterfaceToken(IProCameraUser::getInterfaceDescriptor());
+        remote()->transact(EXCLUSIVE_UNLOCK, data, &reply);
+        return reply.readInt32();
+    }
+
+    virtual bool hasExclusiveLock()
+    {
+        Parcel data, reply;
+        data.writeInterfaceToken(IProCameraUser::getInterfaceDescriptor());
+        remote()->transact(HAS_EXCLUSIVE_LOCK, data, &reply);
+        return !!reply.readInt32();
+    }
+
+    virtual int submitRequest(camera_metadata_t* metadata, bool streaming)
+    {
+
+        Parcel data, reply;
+        data.writeInterfaceToken(IProCameraUser::getInterfaceDescriptor());
+
+        // arg0+arg1
+        writeMetadata(data, metadata);
+
+        // arg2 = streaming (bool)
+        data.writeInt32(streaming);
+
+        remote()->transact(SUBMIT_REQUEST, data, &reply);
+        return reply.readInt32();
+    }
+
+    virtual status_t cancelRequest(int requestId)
+    {
+        Parcel data, reply;
+        data.writeInterfaceToken(IProCameraUser::getInterfaceDescriptor());
+        data.writeInt32(requestId);
+
+        remote()->transact(CANCEL_REQUEST, data, &reply);
+        return reply.readInt32();
+    }
+
+    virtual status_t requestStream(int streamId)
+    {
+        Parcel data, reply;
+        data.writeInterfaceToken(IProCameraUser::getInterfaceDescriptor());
+        data.writeInt32(streamId);
+
+        remote()->transact(REQUEST_STREAM, data, &reply);
+        return reply.readInt32();
+    }
+    virtual status_t cancelStream(int streamId)
+    {
+        Parcel data, reply;
+        data.writeInterfaceToken(IProCameraUser::getInterfaceDescriptor());
+        data.writeInt32(streamId);
+
+        remote()->transact(CANCEL_STREAM, data, &reply);
+        return reply.readInt32();
+    }
+
+    virtual status_t createStream(int width, int height, int format,
+                          const sp<IGraphicBufferProducer>& bufferProducer,
+                          /*out*/
+                          int* streamId)
+    {
+        Parcel data, reply;
+        data.writeInterfaceToken(IProCameraUser::getInterfaceDescriptor());
+        data.writeInt32(width);
+        data.writeInt32(height);
+        data.writeInt32(format);
+
+        sp<IBinder> b(bufferProducer->asBinder());
+        data.writeStrongBinder(b);
+
+        remote()->transact(CREATE_STREAM, data, &reply);
+
+        int sId = reply.readInt32();
+        if (streamId) {
+            *streamId = sId;
+        }
+        return reply.readInt32();
+    }
+
+    // Create a request object from a template.
+    virtual status_t createDefaultRequest(int templateId,
+                                 /*out*/
+                                  camera_metadata** request)
+    {
+        Parcel data, reply;
+        data.writeInterfaceToken(IProCameraUser::getInterfaceDescriptor());
+        data.writeInt32(templateId);
+        remote()->transact(CREATE_DEFAULT_REQUEST, data, &reply);
+        readMetadata(reply, /*out*/request);
+        return reply.readInt32();
+    }
+
+
+    virtual status_t getCameraInfo(int cameraId, camera_metadata** info)
+    {
+        Parcel data, reply;
+        data.writeInterfaceToken(IProCameraUser::getInterfaceDescriptor());
+        data.writeInt32(cameraId);
+        remote()->transact(GET_CAMERA_INFO, data, &reply);
+        readMetadata(reply, /*out*/info);
+        return reply.readInt32();
+    }
+
+
+private:
+
+
+};
+
+IMPLEMENT_META_INTERFACE(ProCameraUser, "android.hardware.IProCameraUser");
+
+// ----------------------------------------------------------------------
+
+status_t BnProCameraUser::onTransact(
+    uint32_t code, const Parcel& data, Parcel* reply, uint32_t flags)
+{
+    switch(code) {
+        case DISCONNECT: {
+            ALOGV("DISCONNECT");
+            CHECK_INTERFACE(IProCameraUser, data, reply);
+            disconnect();
+            return NO_ERROR;
+        } break;
+        case CONNECT: {
+            CHECK_INTERFACE(IProCameraUser, data, reply);
+            sp<IProCameraCallbacks> cameraClient =
+                   interface_cast<IProCameraCallbacks>(data.readStrongBinder());
+            reply->writeInt32(connect(cameraClient));
+            return NO_ERROR;
+        } break;
+
+        /* Shared ProCameraUser */
+        case EXCLUSIVE_TRY_LOCK: {
+            CHECK_INTERFACE(IProCameraUser, data, reply);
+            reply->writeInt32(exclusiveTryLock());
+            return NO_ERROR;
+        } break;
+        case EXCLUSIVE_LOCK: {
+            CHECK_INTERFACE(IProCameraUser, data, reply);
+            reply->writeInt32(exclusiveLock());
+            return NO_ERROR;
+        } break;
+        case EXCLUSIVE_UNLOCK: {
+            CHECK_INTERFACE(IProCameraUser, data, reply);
+            reply->writeInt32(exclusiveUnlock());
+            return NO_ERROR;
+        } break;
+        case HAS_EXCLUSIVE_LOCK: {
+            CHECK_INTERFACE(IProCameraUser, data, reply);
+            reply->writeInt32(hasExclusiveLock());
+            return NO_ERROR;
+        } break;
+        case SUBMIT_REQUEST: {
+            CHECK_INTERFACE(IProCameraUser, data, reply);
+            camera_metadata_t* metadata;
+            readMetadata(data, /*out*/&metadata);
+
+            // arg2 = streaming (bool)
+            bool streaming = data.readInt32();
+
+            // return code: requestId (int32)
+            reply->writeInt32(submitRequest(metadata, streaming));
+
+            return NO_ERROR;
+        } break;
+        case CANCEL_REQUEST: {
+            CHECK_INTERFACE(IProCameraUser, data, reply);
+            int requestId = data.readInt32();
+            reply->writeInt32(cancelRequest(requestId));
+            return NO_ERROR;
+        } break;
+        case REQUEST_STREAM: {
+            CHECK_INTERFACE(IProCameraUser, data, reply);
+            int streamId = data.readInt32();
+            reply->writeInt32(requestStream(streamId));
+            return NO_ERROR;
+        } break;
+        case CANCEL_STREAM: {
+            CHECK_INTERFACE(IProCameraUser, data, reply);
+            int streamId = data.readInt32();
+            reply->writeInt32(cancelStream(streamId));
+            return NO_ERROR;
+        } break;
+        case CREATE_STREAM: {
+            CHECK_INTERFACE(IProCameraUser, data, reply);
+            int width, height, format;
+
+            width = data.readInt32();
+            height = data.readInt32();
+            format = data.readInt32();
+
+            sp<IGraphicBufferProducer> bp =
+               interface_cast<IGraphicBufferProducer>(data.readStrongBinder());
+
+            int streamId = -1;
+            status_t ret;
+            ret = createStream(width, height, format, bp, &streamId);
+
+            reply->writeInt32(streamId);
+            reply->writeInt32(ret);
+
+            return NO_ERROR;
+        } break;
+
+        case CREATE_DEFAULT_REQUEST: {
+            CHECK_INTERFACE(IProCameraUser, data, reply);
+
+            int templateId = data.readInt32();
+
+            camera_metadata_t* request = NULL;
+            status_t ret;
+            ret = createDefaultRequest(templateId, &request);
+
+            writeMetadata(*reply, request);
+            reply->writeInt32(ret);
+
+            free_camera_metadata(request);
+
+            return NO_ERROR;
+        } break;
+        case GET_CAMERA_INFO: {
+            CHECK_INTERFACE(IProCameraUser, data, reply);
+
+            int cameraId = data.readInt32();
+
+            camera_metadata_t* info = NULL;
+            status_t ret;
+            ret = getCameraInfo(cameraId, &info);
+
+            writeMetadata(*reply, info);
+            reply->writeInt32(ret);
+
+            free_camera_metadata(info);
+
+            return NO_ERROR;
+        } break;
+        default:
+            return BBinder::onTransact(code, data, reply, flags);
+    }
+}
+
+// ----------------------------------------------------------------------------
+
+}; // namespace android
diff --git a/camera/ProCamera.cpp b/camera/ProCamera.cpp
new file mode 100644
index 0000000..7c66d62
--- /dev/null
+++ b/camera/ProCamera.cpp
@@ -0,0 +1,537 @@
+/*
+**
+** Copyright (C) 2013, The Android Open Source Project
+**
+** 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 "ProCamera"
+#include <utils/Log.h>
+#include <utils/threads.h>
+#include <utils/Mutex.h>
+
+#include <binder/IPCThreadState.h>
+#include <binder/IServiceManager.h>
+#include <binder/IMemory.h>
+
+#include <camera/ProCamera.h>
+#include <camera/ICameraService.h>
+#include <camera/IProCameraUser.h>
+#include <camera/IProCameraCallbacks.h>
+
+#include <gui/IGraphicBufferProducer.h>
+#include <gui/Surface.h>
+
+#include <system/camera_metadata.h>
+
+namespace android {
+
+// client singleton for camera service binder interface
+Mutex ProCamera::mLock;
+sp<ICameraService> ProCamera::mCameraService;
+sp<ProCamera::DeathNotifier> ProCamera::mDeathNotifier;
+
+// establish binder interface to camera service
+const sp<ICameraService>& ProCamera::getCameraService()
+{
+    Mutex::Autolock _l(mLock);
+    if (mCameraService.get() == 0) {
+        sp<IServiceManager> sm = defaultServiceManager();
+        sp<IBinder> binder;
+        do {
+            binder = sm->getService(String16("media.camera"));
+            if (binder != 0)
+                break;
+            ALOGW("CameraService not published, waiting...");
+            usleep(500000); // 0.5 s
+        } while(true);
+        if (mDeathNotifier == NULL) {
+            mDeathNotifier = new DeathNotifier();
+        }
+        binder->linkToDeath(mDeathNotifier);
+        mCameraService = interface_cast<ICameraService>(binder);
+    }
+    ALOGE_IF(mCameraService==0, "no CameraService!?");
+    return mCameraService;
+}
+
+sp<ProCamera> ProCamera::connect(int cameraId)
+{
+    ALOGV("connect");
+    sp<ProCamera> c = new ProCamera();
+    sp<IProCameraCallbacks> cl = c;
+    const sp<ICameraService>& cs = getCameraService();
+    if (cs != 0) {
+        c->mCamera = cs->connect(cl, cameraId);
+    }
+    if (c->mCamera != 0) {
+        c->mCamera->asBinder()->linkToDeath(c);
+        c->mStatus = NO_ERROR;
+    } else {
+        c.clear();
+    }
+    return c;
+}
+
+void ProCamera::disconnect()
+{
+    ALOGV("%s: disconnect", __FUNCTION__);
+    if (mCamera != 0) {
+        mCamera->disconnect();
+        mCamera->asBinder()->unlinkToDeath(this);
+        mCamera = 0;
+    }
+    ALOGV("%s: disconnect (done)", __FUNCTION__);
+}
+
+ProCamera::ProCamera()
+{
+}
+
+ProCamera::~ProCamera()
+{
+
+}
+
+sp<IProCameraUser> ProCamera::remote()
+{
+    return mCamera;
+}
+
+void ProCamera::binderDied(const wp<IBinder>& who) {
+    ALOGW("IProCameraUser died");
+    notifyCallback(CAMERA_MSG_ERROR, CAMERA_ERROR_SERVER_DIED, 0);
+}
+
+void ProCamera::DeathNotifier::binderDied(const wp<IBinder>& who) {
+    ALOGV("binderDied");
+    Mutex::Autolock _l(ProCamera::mLock);
+    ProCamera::mCameraService.clear();
+    ALOGW("Camera service died!");
+}
+
+void ProCamera::setListener(const sp<ProCameraListener>& listener)
+{
+    Mutex::Autolock _l(mLock);
+    mListener = listener;
+}
+
+
+// callback from camera service
+void ProCamera::notifyCallback(int32_t msgType, int32_t ext1, int32_t ext2)
+{
+    sp<ProCameraListener> listener;
+    {
+        Mutex::Autolock _l(mLock);
+        listener = mListener;
+    }
+    if (listener != NULL) {
+        listener->notify(msgType, ext1, ext2);
+    }
+}
+
+// callback from camera service when frame or image is ready
+void ProCamera::dataCallback(int32_t msgType, const sp<IMemory>& dataPtr,
+                          camera_frame_metadata_t *metadata)
+{
+    sp<ProCameraListener> listener;
+    {
+        Mutex::Autolock _l(mLock);
+        listener = mListener;
+    }
+    if (listener != NULL) {
+        listener->postData(msgType, dataPtr, metadata);
+    }
+}
+
+// callback from camera service when timestamped frame is ready
+void ProCamera::dataCallbackTimestamp(nsecs_t timestamp, int32_t msgType,
+                                                    const sp<IMemory>& dataPtr)
+{
+    sp<ProCameraListener> listener;
+    {
+        Mutex::Autolock _l(mLock);
+        listener = mListener;
+    }
+    if (listener != NULL) {
+        listener->postDataTimestamp(timestamp, msgType, dataPtr);
+    } else {
+        ALOGW("No listener was set. Drop a recording frame.");
+    }
+}
+
+/* IProCameraUser's implementation */
+
+void ProCamera::onLockStatusChanged(
+                                 IProCameraCallbacks::LockStatus newLockStatus)
+{
+    ALOGV("%s: newLockStatus = %d", __FUNCTION__, newLockStatus);
+
+    sp<ProCameraListener> listener;
+    {
+        Mutex::Autolock _l(mLock);
+        listener = mListener;
+    }
+    if (listener != NULL) {
+        switch (newLockStatus) {
+            case IProCameraCallbacks::LOCK_ACQUIRED:
+                listener->onLockAcquired();
+                break;
+            case IProCameraCallbacks::LOCK_RELEASED:
+                listener->onLockReleased();
+                break;
+            case IProCameraCallbacks::LOCK_STOLEN:
+                listener->onLockStolen();
+                break;
+            default:
+                ALOGE("%s: Unknown lock status: %d",
+                      __FUNCTION__, newLockStatus);
+        }
+    }
+}
+
+void ProCamera::onResultReceived(int32_t frameId, camera_metadata* result) {
+    ALOGV("%s: frameId = %d, result = %p", __FUNCTION__, frameId, result);
+
+    sp<ProCameraListener> listener;
+    {
+        Mutex::Autolock _l(mLock);
+        listener = mListener;
+    }
+
+    CameraMetadata tmp(result);
+
+    // Unblock waitForFrame(id) callers
+    {
+        Mutex::Autolock al(mWaitMutex);
+        mMetadataReady = true;
+        mLatestMetadata = tmp;
+        mWaitCondition.broadcast();
+    }
+
+    result = tmp.release();
+
+    if (listener != NULL) {
+        listener->onResultReceived(frameId, result);
+    } else {
+        free_camera_metadata(result);
+    }
+
+}
+
+status_t ProCamera::exclusiveTryLock()
+{
+    sp <IProCameraUser> c = mCamera;
+    if (c == 0) return NO_INIT;
+
+    return c->exclusiveTryLock();
+}
+status_t ProCamera::exclusiveLock()
+{
+    sp <IProCameraUser> c = mCamera;
+    if (c == 0) return NO_INIT;
+
+    return c->exclusiveLock();
+}
+status_t ProCamera::exclusiveUnlock()
+{
+    sp <IProCameraUser> c = mCamera;
+    if (c == 0) return NO_INIT;
+
+    return c->exclusiveUnlock();
+}
+bool ProCamera::hasExclusiveLock()
+{
+    sp <IProCameraUser> c = mCamera;
+    if (c == 0) return NO_INIT;
+
+    return c->hasExclusiveLock();
+}
+
+// Note that the callee gets a copy of the metadata.
+int ProCamera::submitRequest(const struct camera_metadata* metadata,
+                             bool streaming)
+{
+    sp <IProCameraUser> c = mCamera;
+    if (c == 0) return NO_INIT;
+
+    return c->submitRequest(const_cast<struct camera_metadata*>(metadata),
+                            streaming);
+}
+
+status_t ProCamera::cancelRequest(int requestId)
+{
+    sp <IProCameraUser> c = mCamera;
+    if (c == 0) return NO_INIT;
+
+    return c->cancelRequest(requestId);
+}
+
+status_t ProCamera::deleteStream(int streamId)
+{
+    sp <IProCameraUser> c = mCamera;
+    if (c == 0) return NO_INIT;
+
+    status_t s = c->cancelStream(streamId);
+
+    mStreams.removeItem(streamId);
+
+    return s;
+}
+
+status_t ProCamera::createStream(int width, int height, int format,
+                          const sp<Surface>& surface,
+                          /*out*/
+                          int* streamId)
+{
+    *streamId = -1;
+
+    ALOGV("%s: createStreamW %dx%d (fmt=0x%x)", __FUNCTION__, width, height,
+                                                                       format);
+
+    if (surface == 0) {
+        return BAD_VALUE;
+    }
+
+    return createStream(width, height, format, surface->getIGraphicBufferProducer(),
+                        streamId);
+}
+
+status_t ProCamera::createStream(int width, int height, int format,
+                          const sp<IGraphicBufferProducer>& bufferProducer,
+                          /*out*/
+                          int* streamId) {
+    *streamId = -1;
+
+    ALOGV("%s: createStreamT %dx%d (fmt=0x%x)", __FUNCTION__, width, height,
+                                                                       format);
+
+    if (bufferProducer == 0) {
+        return BAD_VALUE;
+    }
+
+    sp <IProCameraUser> c = mCamera;
+    status_t stat = c->createStream(width, height, format, bufferProducer,
+                                    streamId);
+
+    if (stat == OK) {
+        StreamInfo s(*streamId);
+
+        mStreams.add(*streamId, s);
+    }
+
+    return stat;
+}
+
+status_t ProCamera::createStreamCpu(int width, int height, int format,
+                          int heapCount,
+                          /*out*/
+                          sp<CpuConsumer>* cpuConsumer,
+                          int* streamId)
+{
+    ALOGV("%s: createStreamW %dx%d (fmt=0x%x)", __FUNCTION__, width, height,
+                                                                        format);
+
+    *cpuConsumer = NULL;
+
+    sp <IProCameraUser> c = mCamera;
+    if (c == 0) return NO_INIT;
+
+    sp<CpuConsumer> cc = new CpuConsumer(heapCount);
+    cc->setName(String8("ProCamera::mCpuConsumer"));
+
+    sp<Surface> stc = new Surface(
+        cc->getProducerInterface());
+
+    status_t s = createStream(width, height, format, stc->getIGraphicBufferProducer(),
+                        streamId);
+
+    if (s != OK) {
+        ALOGE("%s: Failure to create stream %dx%d (fmt=0x%x)", __FUNCTION__,
+                    width, height, format);
+        return s;
+    }
+
+    sp<ProFrameListener> frameAvailableListener =
+        new ProFrameListener(this, *streamId);
+
+    getStreamInfo(*streamId).cpuStream = true;
+    getStreamInfo(*streamId).cpuConsumer = cc;
+    getStreamInfo(*streamId).stc = stc;
+    // for lifetime management
+    getStreamInfo(*streamId).frameAvailableListener = frameAvailableListener;
+
+    cc->setFrameAvailableListener(frameAvailableListener);
+
+    *cpuConsumer = cc;
+
+    return s;
+}
+
+int ProCamera::getNumberOfCameras() {
+    const sp<ICameraService> cs = getCameraService();
+
+    if (!cs.get()) {
+        return DEAD_OBJECT;
+    }
+    return cs->getNumberOfCameras();
+}
+
+camera_metadata* ProCamera::getCameraInfo(int cameraId) {
+    ALOGV("%s: cameraId = %d", __FUNCTION__, cameraId);
+
+    sp <IProCameraUser> c = mCamera;
+    if (c == 0) return NULL;
+
+    camera_metadata* ptr = NULL;
+    status_t status = c->getCameraInfo(cameraId, &ptr);
+
+    if (status != OK) {
+        ALOGE("%s: Failed to get camera info, error = %d", __FUNCTION__, status);
+    }
+
+    return ptr;
+}
+
+status_t ProCamera::createDefaultRequest(int templateId,
+                                             camera_metadata** request) const {
+    ALOGV("%s: templateId = %d", __FUNCTION__, templateId);
+
+    sp <IProCameraUser> c = mCamera;
+    if (c == 0) return NO_INIT;
+
+    return c->createDefaultRequest(templateId, request);
+}
+
+void ProCamera::onFrameAvailable(int streamId) {
+    ALOGV("%s: streamId = %d", __FUNCTION__, streamId);
+
+    sp<ProCameraListener> listener = mListener;
+    StreamInfo& stream = getStreamInfo(streamId);
+
+    CpuConsumer::LockedBuffer buf;
+
+    if (listener.get() != NULL) {
+        if (listener->useOnFrameAvailable()) {
+            listener->onFrameAvailable(streamId, stream.cpuConsumer);
+            return;
+        }
+    }
+
+    // Unblock waitForFrame(id) callers
+    {
+        Mutex::Autolock al(mWaitMutex);
+        getStreamInfo(streamId).frameReady++;
+        mWaitCondition.broadcast();
+    }
+}
+
+int ProCamera::waitForFrameBuffer(int streamId) {
+    status_t stat = BAD_VALUE;
+    Mutex::Autolock al(mWaitMutex);
+
+    StreamInfo& si = getStreamInfo(streamId);
+
+    if (si.frameReady > 0) {
+        int numFrames = si.frameReady;
+        si.frameReady = 0;
+        return numFrames;
+    } else {
+        while (true) {
+            stat = mWaitCondition.waitRelative(mWaitMutex,
+                                                mWaitTimeout);
+            if (stat != OK) {
+                ALOGE("%s: Error while waiting for frame buffer: %d",
+                    __FUNCTION__, stat);
+                return stat;
+            }
+
+            if (si.frameReady > 0) {
+                int numFrames = si.frameReady;
+                si.frameReady = 0;
+                return numFrames;
+            }
+            // else it was some other stream that got unblocked
+        }
+    }
+
+    return stat;
+}
+
+int ProCamera::dropFrameBuffer(int streamId, int count) {
+    StreamInfo& si = getStreamInfo(streamId);
+
+    if (!si.cpuStream) {
+        return BAD_VALUE;
+    } else if (count < 0) {
+        return BAD_VALUE;
+    }
+
+    int numDropped = 0;
+    for (int i = 0; i < count; ++i) {
+        CpuConsumer::LockedBuffer buffer;
+        if (si.cpuConsumer->lockNextBuffer(&buffer) != OK) {
+            break;
+        }
+
+        si.cpuConsumer->unlockBuffer(buffer);
+        numDropped++;
+    }
+
+    return numDropped;
+}
+
+status_t ProCamera::waitForFrameMetadata() {
+    status_t stat = BAD_VALUE;
+    Mutex::Autolock al(mWaitMutex);
+
+    if (mMetadataReady) {
+        return OK;
+    } else {
+        while (true) {
+            stat = mWaitCondition.waitRelative(mWaitMutex,
+                                               mWaitTimeout);
+
+            if (stat != OK) {
+                ALOGE("%s: Error while waiting for metadata: %d",
+                        __FUNCTION__, stat);
+                return stat;
+            }
+
+            if (mMetadataReady) {
+                mMetadataReady = false;
+                return OK;
+            }
+            // else it was some other stream or metadata
+        }
+    }
+
+    return stat;
+}
+
+CameraMetadata ProCamera::consumeFrameMetadata() {
+    Mutex::Autolock al(mWaitMutex);
+
+    // Destructive: Subsequent calls return empty metadatas
+    CameraMetadata tmp = mLatestMetadata;
+    mLatestMetadata.release();
+
+    return tmp;
+}
+
+ProCamera::StreamInfo& ProCamera::getStreamInfo(int streamId) {
+    return mStreams.editValueFor(streamId);
+}
+
+}; // namespace android
diff --git a/camera/tests/Android.mk b/camera/tests/Android.mk
new file mode 100644
index 0000000..e455943
--- /dev/null
+++ b/camera/tests/Android.mk
@@ -0,0 +1,38 @@
+LOCAL_PATH:= $(call my-dir)
+include $(CLEAR_VARS)
+
+LOCAL_SRC_FILES:= \
+	main.cpp \
+	ProCameraTests.cpp \
+
+LOCAL_SHARED_LIBRARIES := \
+	libutils \
+	libcutils \
+	libstlport \
+	libcamera_metadata \
+	libcamera_client \
+	libgui \
+	libsync \
+	libui \
+	libdl \
+	libbinder
+
+LOCAL_STATIC_LIBRARIES := \
+	libgtest
+
+LOCAL_C_INCLUDES += \
+	bionic \
+	bionic/libstdc++/include \
+	external/gtest/include \
+	external/stlport/stlport \
+	system/media/camera/include \
+	frameworks/av/services/camera/libcameraservice \
+	frameworks/av/include/camera \
+	frameworks/native/include \
+
+LOCAL_CFLAGS += -Wall -Wextra
+
+LOCAL_MODULE:= camera_client_test
+LOCAL_MODULE_TAGS := tests
+
+include $(BUILD_EXECUTABLE)
diff --git a/camera/tests/ProCameraTests.cpp b/camera/tests/ProCameraTests.cpp
new file mode 100644
index 0000000..f93e5cd
--- /dev/null
+++ b/camera/tests/ProCameraTests.cpp
@@ -0,0 +1,1042 @@
+/*
+ * Copyright (C) 2013 The Android Open Source Project
+ *
+ * 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.
+ */
+
+#include <gtest/gtest.h>
+#include <iostream>
+
+#include <binder/IPCThreadState.h>
+#include <utils/Thread.h>
+
+#include "Camera.h"
+#include "ProCamera.h"
+#include <utils/Vector.h>
+#include <utils/Mutex.h>
+#include <utils/Condition.h>
+
+#include <gui/SurfaceComposerClient.h>
+#include <gui/Surface.h>
+
+#include <system/camera_metadata.h>
+#include <hardware/camera2.h> // for CAMERA2_TEMPLATE_PREVIEW only
+#include <camera/CameraMetadata.h>
+
+namespace android {
+namespace camera2 {
+namespace tests {
+namespace client {
+
+#define CAMERA_ID 0
+#define TEST_DEBUGGING 0
+
+#define TEST_LISTENER_TIMEOUT 1000000000 // 1 second listener timeout
+#define TEST_FORMAT HAL_PIXEL_FORMAT_Y16 //TODO: YUY2 instead
+
+#define TEST_FORMAT_MAIN HAL_PIXEL_FORMAT_Y8
+#define TEST_FORMAT_DEPTH HAL_PIXEL_FORMAT_Y16
+
+// defaults for display "test"
+#define TEST_DISPLAY_FORMAT HAL_PIXEL_FORMAT_Y16
+#define TEST_DISPLAY_WIDTH 1280
+#define TEST_DISPLAY_HEIGHT 960
+
+#define TEST_CPU_FRAME_COUNT 2
+#define TEST_CPU_HEAP_COUNT 5
+
+#define TEST_FRAME_PROCESSING_DELAY_US 200000 // 200 ms
+
+#if TEST_DEBUGGING
+#define dout std::cerr
+#else
+#define dout if (0) std::cerr
+#endif
+
+#define EXPECT_OK(x) EXPECT_EQ(OK, (x))
+#define ASSERT_OK(x) ASSERT_EQ(OK, (x))
+
+class ProCameraTest;
+
+enum ProEvent {
+    UNKNOWN,
+    ACQUIRED,
+    RELEASED,
+    STOLEN,
+    BUFFER_RECEIVED,
+    RESULT_RECEIVED,
+};
+
+inline int ProEvent_Mask(ProEvent e) {
+    return (1 << static_cast<int>(e));
+}
+
+typedef Vector<ProEvent> EventList;
+
+class ProCameraTestThread : public Thread
+{
+public:
+    ProCameraTestThread() {
+    }
+
+    virtual bool threadLoop() {
+        mProc = ProcessState::self();
+        mProc->startThreadPool();
+
+        IPCThreadState *ptr = IPCThreadState::self();
+
+        ptr->joinThreadPool();
+
+        return false;
+    }
+
+    sp<ProcessState> mProc;
+};
+
+class ProCameraTestListener : public ProCameraListener {
+
+public:
+    static const int EVENT_MASK_ALL = 0xFFFFFFFF;
+
+    ProCameraTestListener() {
+        mEventMask = EVENT_MASK_ALL;
+    }
+
+    status_t WaitForEvent() {
+        Mutex::Autolock cal(mConditionMutex);
+
+        {
+            Mutex::Autolock al(mListenerMutex);
+
+            if (mProEventList.size() > 0) {
+                return OK;
+            }
+        }
+
+        return mListenerCondition.waitRelative(mConditionMutex,
+                                               TEST_LISTENER_TIMEOUT);
+    }
+
+    /* Read events into out. Existing queue is flushed */
+    void ReadEvents(EventList& out) {
+        Mutex::Autolock al(mListenerMutex);
+
+        for (size_t i = 0; i < mProEventList.size(); ++i) {
+            out.push(mProEventList[i]);
+        }
+
+        mProEventList.clear();
+    }
+
+    /**
+      * Dequeue 1 event from the event queue.
+      * Returns UNKNOWN if queue is empty
+      */
+    ProEvent ReadEvent() {
+        Mutex::Autolock al(mListenerMutex);
+
+        if (mProEventList.size() == 0) {
+            return UNKNOWN;
+        }
+
+        ProEvent ev = mProEventList[0];
+        mProEventList.removeAt(0);
+
+        return ev;
+    }
+
+    void SetEventMask(int eventMask) {
+        Mutex::Autolock al(mListenerMutex);
+        mEventMask = eventMask;
+    }
+
+private:
+    void QueueEvent(ProEvent ev) {
+        bool eventAdded = false;
+        {
+            Mutex::Autolock al(mListenerMutex);
+
+            if (ProEvent_Mask(ev) & mEventMask) {
+                mProEventList.push(ev);
+                eventAdded = true;
+            }
+        }
+
+        if (eventAdded) {
+            mListenerCondition.broadcast();
+        }
+    }
+
+protected:
+
+    //////////////////////////////////////////////////
+    ///////// ProCameraListener //////////////////////
+    //////////////////////////////////////////////////
+
+
+    // Lock has been acquired. Write operations now available.
+    virtual void onLockAcquired() {
+        QueueEvent(ACQUIRED);
+    }
+    // Lock has been released with exclusiveUnlock
+    virtual void onLockReleased() {
+        QueueEvent(RELEASED);
+    }
+
+    // Lock has been stolen by another client.
+    virtual void onLockStolen() {
+        QueueEvent(STOLEN);
+    }
+
+    // Lock free.
+    virtual void onTriggerNotify(int32_t ext1, int32_t ext2, int32_t ext3) {
+
+        dout << "Trigger notify: " << ext1 << " " << ext2
+             << " " << ext3 << std::endl;
+    }
+
+    virtual void onBufferReceived(int streamId,
+                                  const CpuConsumer::LockedBuffer& buf) {
+
+        dout << "Buffer received on streamId = " << streamId <<
+                ", dataPtr = " << (void*)buf.data <<
+                ", timestamp = " << buf.timestamp << std::endl;
+
+        QueueEvent(BUFFER_RECEIVED);
+
+    }
+    virtual void onResultReceived(int32_t frameId,
+                                  camera_metadata* request) {
+        dout << "Result received frameId = " << frameId
+             << ", requestPtr = " << (void*)request << std::endl;
+        QueueEvent(RESULT_RECEIVED);
+        free_camera_metadata(request);
+    }
+
+    // TODO: remove
+
+    virtual void notify(int32_t , int32_t , int32_t ) {}
+    virtual void postData(int32_t , const sp<IMemory>& ,
+                          camera_frame_metadata_t *) {}
+    virtual void postDataTimestamp(nsecs_t , int32_t , const sp<IMemory>& ) {}
+
+
+    Vector<ProEvent> mProEventList;
+    Mutex             mListenerMutex;
+    Mutex             mConditionMutex;
+    Condition         mListenerCondition;
+    int               mEventMask;
+};
+
+class ProCameraTest : public ::testing::Test {
+
+public:
+    ProCameraTest() {
+        char* displaySecsEnv = getenv("TEST_DISPLAY_SECS");
+        if (displaySecsEnv != NULL) {
+            mDisplaySecs = atoi(displaySecsEnv);
+            if (mDisplaySecs < 0) {
+                mDisplaySecs = 0;
+            }
+        } else {
+            mDisplaySecs = 0;
+        }
+
+        char* displayFmtEnv = getenv("TEST_DISPLAY_FORMAT");
+        if (displayFmtEnv != NULL) {
+            mDisplayFmt = FormatFromString(displayFmtEnv);
+        } else {
+            mDisplayFmt = TEST_DISPLAY_FORMAT;
+        }
+
+        char* displayWidthEnv = getenv("TEST_DISPLAY_WIDTH");
+        if (displayWidthEnv != NULL) {
+            mDisplayW = atoi(displayWidthEnv);
+            if (mDisplayW < 0) {
+                mDisplayW = 0;
+            }
+        } else {
+            mDisplayW = TEST_DISPLAY_WIDTH;
+        }
+
+        char* displayHeightEnv = getenv("TEST_DISPLAY_HEIGHT");
+        if (displayHeightEnv != NULL) {
+            mDisplayH = atoi(displayHeightEnv);
+            if (mDisplayH < 0) {
+                mDisplayH = 0;
+            }
+        } else {
+            mDisplayH = TEST_DISPLAY_HEIGHT;
+        }
+    }
+
+    static void SetUpTestCase() {
+        // Binder Thread Pool Initialization
+        mTestThread = new ProCameraTestThread();
+        mTestThread->run("ProCameraTestThread");
+    }
+
+    virtual void SetUp() {
+        mCamera = ProCamera::connect(CAMERA_ID);
+        ASSERT_NE((void*)NULL, mCamera.get());
+
+        mListener = new ProCameraTestListener();
+        mCamera->setListener(mListener);
+    }
+
+    virtual void TearDown() {
+        ASSERT_NE((void*)NULL, mCamera.get());
+        mCamera->disconnect();
+    }
+
+protected:
+    sp<ProCamera> mCamera;
+    sp<ProCameraTestListener> mListener;
+
+    static sp<Thread> mTestThread;
+
+    int mDisplaySecs;
+    int mDisplayFmt;
+    int mDisplayW;
+    int mDisplayH;
+
+    sp<SurfaceComposerClient> mComposerClient;
+    sp<SurfaceControl> mSurfaceControl;
+
+    sp<SurfaceComposerClient> mDepthComposerClient;
+    sp<SurfaceControl> mDepthSurfaceControl;
+
+    int getSurfaceWidth() {
+        return 512;
+    }
+    int getSurfaceHeight() {
+        return 512;
+    }
+
+    void createOnScreenSurface(sp<Surface>& surface) {
+        mComposerClient = new SurfaceComposerClient;
+        ASSERT_EQ(NO_ERROR, mComposerClient->initCheck());
+
+        mSurfaceControl = mComposerClient->createSurface(
+                String8("ProCameraTest StreamingImage Surface"),
+                getSurfaceWidth(), getSurfaceHeight(),
+                PIXEL_FORMAT_RGB_888, 0);
+
+        mSurfaceControl->setPosition(0, 0);
+
+        ASSERT_TRUE(mSurfaceControl != NULL);
+        ASSERT_TRUE(mSurfaceControl->isValid());
+
+        SurfaceComposerClient::openGlobalTransaction();
+        ASSERT_EQ(NO_ERROR, mSurfaceControl->setLayer(0x7FFFFFFF));
+        ASSERT_EQ(NO_ERROR, mSurfaceControl->show());
+        SurfaceComposerClient::closeGlobalTransaction();
+
+        sp<ANativeWindow> window = mSurfaceControl->getSurface();
+        surface = mSurfaceControl->getSurface();
+
+        ASSERT_NE((void*)NULL, surface.get());
+    }
+
+    void createDepthOnScreenSurface(sp<Surface>& surface) {
+        mDepthComposerClient = new SurfaceComposerClient;
+        ASSERT_EQ(NO_ERROR, mDepthComposerClient->initCheck());
+
+        mDepthSurfaceControl = mDepthComposerClient->createSurface(
+                String8("ProCameraTest StreamingImage Surface"),
+                getSurfaceWidth(), getSurfaceHeight(),
+                PIXEL_FORMAT_RGB_888, 0);
+
+        mDepthSurfaceControl->setPosition(640, 0);
+
+        ASSERT_TRUE(mDepthSurfaceControl != NULL);
+        ASSERT_TRUE(mDepthSurfaceControl->isValid());
+
+        SurfaceComposerClient::openGlobalTransaction();
+        ASSERT_EQ(NO_ERROR, mDepthSurfaceControl->setLayer(0x7FFFFFFF));
+        ASSERT_EQ(NO_ERROR, mDepthSurfaceControl->show());
+        SurfaceComposerClient::closeGlobalTransaction();
+
+        sp<ANativeWindow> window = mDepthSurfaceControl->getSurface();
+        surface = mDepthSurfaceControl->getSurface();
+
+        ASSERT_NE((void*)NULL, surface.get());
+    }
+
+    template <typename T>
+    static bool ExistsItem(T needle, T* array, size_t count) {
+        if (!array) {
+            return false;
+        }
+
+        for (size_t i = 0; i < count; ++i) {
+            if (array[i] == needle) {
+                return true;
+            }
+        }
+        return false;
+    }
+
+
+    static int FormatFromString(const char* str) {
+        std::string s(str);
+
+#define CMP_STR(x, y)                               \
+        if (s == #x) return HAL_PIXEL_FORMAT_ ## y;
+#define CMP_STR_SAME(x) CMP_STR(x, x)
+
+        CMP_STR_SAME( Y16);
+        CMP_STR_SAME( Y8);
+        CMP_STR_SAME( YV12);
+        CMP_STR(NV16, YCbCr_422_SP);
+        CMP_STR(NV21, YCrCb_420_SP);
+        CMP_STR(YUY2, YCbCr_422_I);
+        CMP_STR(RAW,  RAW_SENSOR);
+        CMP_STR(RGBA, RGBA_8888);
+
+        std::cerr << "Unknown format string " << str << std::endl;
+        return -1;
+
+    }
+
+    /**
+     * Creating a streaming request for these output streams from a template,
+     *  and submit it
+     */
+    void createSubmitRequestForStreams(uint8_t* streamIds, size_t count, int requestCount=-1) {
+
+        ASSERT_NE((void*)NULL, streamIds);
+        ASSERT_LT(0u, count);
+
+        camera_metadata_t *requestTmp = NULL;
+        EXPECT_OK(mCamera->createDefaultRequest(CAMERA2_TEMPLATE_PREVIEW,
+                                                /*out*/&requestTmp));
+        ASSERT_NE((void*)NULL, requestTmp);
+        CameraMetadata request(requestTmp);
+
+        // set the output streams. default is empty
+
+        uint32_t tag = static_cast<uint32_t>(ANDROID_REQUEST_OUTPUT_STREAMS);
+        request.update(tag, streamIds, count);
+
+        requestTmp = request.release();
+
+        if (requestCount < 0) {
+            EXPECT_OK(mCamera->submitRequest(requestTmp, /*streaming*/true));
+        } else {
+            for (int i = 0; i < requestCount; ++i) {
+                EXPECT_OK(mCamera->submitRequest(requestTmp,
+                                                 /*streaming*/false));
+            }
+        }
+        request.acquire(requestTmp);
+    }
+
+};
+
+sp<Thread> ProCameraTest::mTestThread;
+
+TEST_F(ProCameraTest, AvailableFormats) {
+    if (HasFatalFailure()) {
+        return;
+    }
+
+    CameraMetadata staticInfo = mCamera->getCameraInfo(CAMERA_ID);
+    ASSERT_FALSE(staticInfo.isEmpty());
+
+    uint32_t tag = static_cast<uint32_t>(ANDROID_SCALER_AVAILABLE_FORMATS);
+    EXPECT_TRUE(staticInfo.exists(tag));
+    camera_metadata_entry_t entry = staticInfo.find(tag);
+
+    EXPECT_TRUE(ExistsItem<int32_t>(HAL_PIXEL_FORMAT_YV12,
+                                                  entry.data.i32, entry.count));
+    EXPECT_TRUE(ExistsItem<int32_t>(HAL_PIXEL_FORMAT_YCrCb_420_SP,
+                                                  entry.data.i32, entry.count));
+}
+
+// test around exclusiveTryLock (immediate locking)
+TEST_F(ProCameraTest, LockingImmediate) {
+
+    if (HasFatalFailure()) {
+        return;
+    }
+
+    mListener->SetEventMask(ProEvent_Mask(ACQUIRED) |
+                            ProEvent_Mask(STOLEN)   |
+                            ProEvent_Mask(RELEASED));
+
+    EXPECT_FALSE(mCamera->hasExclusiveLock());
+    EXPECT_EQ(OK, mCamera->exclusiveTryLock());
+    // at this point we definitely have the lock
+
+    EXPECT_EQ(OK, mListener->WaitForEvent());
+    EXPECT_EQ(ACQUIRED, mListener->ReadEvent());
+
+    EXPECT_TRUE(mCamera->hasExclusiveLock());
+    EXPECT_EQ(OK, mCamera->exclusiveUnlock());
+
+    EXPECT_EQ(OK, mListener->WaitForEvent());
+    EXPECT_EQ(RELEASED, mListener->ReadEvent());
+
+    EXPECT_FALSE(mCamera->hasExclusiveLock());
+}
+
+// test around exclusiveLock (locking at some future point in time)
+TEST_F(ProCameraTest, LockingAsynchronous) {
+
+    if (HasFatalFailure()) {
+        return;
+    }
+
+
+    mListener->SetEventMask(ProEvent_Mask(ACQUIRED) |
+                            ProEvent_Mask(STOLEN)   |
+                            ProEvent_Mask(RELEASED));
+
+    // TODO: Add another procamera that has a lock here.
+    // then we can be test that the lock wont immediately be acquired
+
+    EXPECT_FALSE(mCamera->hasExclusiveLock());
+    EXPECT_EQ(OK, mCamera->exclusiveTryLock());
+    // at this point we definitely have the lock
+
+    EXPECT_EQ(OK, mListener->WaitForEvent());
+    EXPECT_EQ(ACQUIRED, mListener->ReadEvent());
+
+    EXPECT_TRUE(mCamera->hasExclusiveLock());
+    EXPECT_EQ(OK, mCamera->exclusiveUnlock());
+
+    EXPECT_EQ(OK, mListener->WaitForEvent());
+    EXPECT_EQ(RELEASED, mListener->ReadEvent());
+
+    EXPECT_FALSE(mCamera->hasExclusiveLock());
+}
+
+// Stream directly to the screen.
+TEST_F(ProCameraTest, DISABLED_StreamingImageSingle) {
+    if (HasFatalFailure()) {
+        return;
+    }
+
+    sp<Surface> surface;
+    if (mDisplaySecs > 0) {
+        createOnScreenSurface(/*out*/surface);
+    }
+    else {
+        dout << "Skipping, will not render to screen" << std::endl;
+        return;
+    }
+
+    int depthStreamId = -1;
+    EXPECT_OK(mCamera->createStream(mDisplayW, mDisplayH, mDisplayFmt, surface,
+                                    &depthStreamId));
+    EXPECT_NE(-1, depthStreamId);
+
+    EXPECT_OK(mCamera->exclusiveTryLock());
+
+    uint8_t streams[] = { depthStreamId };
+    ASSERT_NO_FATAL_FAILURE(createSubmitRequestForStreams(streams, /*count*/1));
+
+    dout << "will sleep now for " << mDisplaySecs << std::endl;
+    sleep(mDisplaySecs);
+
+    EXPECT_OK(mCamera->deleteStream(depthStreamId));
+    EXPECT_OK(mCamera->exclusiveUnlock());
+}
+
+// Stream directly to the screen.
+TEST_F(ProCameraTest, DISABLED_StreamingImageDual) {
+    if (HasFatalFailure()) {
+        return;
+    }
+    sp<Surface> surface;
+    sp<Surface> depthSurface;
+    if (mDisplaySecs > 0) {
+        createOnScreenSurface(/*out*/surface);
+        createDepthOnScreenSurface(/*out*/depthSurface);
+    }
+
+    int streamId = -1;
+    EXPECT_OK(mCamera->createStream(/*width*/1280, /*height*/960,
+              TEST_FORMAT_MAIN, surface, &streamId));
+    EXPECT_NE(-1, streamId);
+
+    int depthStreamId = -1;
+    EXPECT_OK(mCamera->createStream(/*width*/320, /*height*/240,
+              TEST_FORMAT_DEPTH, depthSurface, &depthStreamId));
+    EXPECT_NE(-1, depthStreamId);
+
+    EXPECT_OK(mCamera->exclusiveTryLock());
+    /*
+    */
+    /* iterate in a loop submitting requests every frame.
+     *  what kind of requests doesnt really matter, just whatever.
+     */
+
+    // it would probably be better to use CameraMetadata from camera service.
+    camera_metadata_t *request = NULL;
+    EXPECT_OK(mCamera->createDefaultRequest(CAMERA2_TEMPLATE_PREVIEW,
+              /*out*/&request));
+    EXPECT_NE((void*)NULL, request);
+
+    /*FIXME: dont need this later, at which point the above should become an
+             ASSERT_NE*/
+    if(request == NULL) request = allocate_camera_metadata(10, 100);
+
+    // set the output streams to just this stream ID
+
+    // wow what a verbose API.
+    uint8_t allStreams[] = { streamId, depthStreamId };
+    // IMPORTANT. bad things will happen if its not a uint8.
+    size_t streamCount = sizeof(allStreams) / sizeof(allStreams[0]);
+    camera_metadata_entry_t entry;
+    uint32_t tag = static_cast<uint32_t>(ANDROID_REQUEST_OUTPUT_STREAMS);
+    int find = find_camera_metadata_entry(request, tag, &entry);
+    if (find == -ENOENT) {
+        if (add_camera_metadata_entry(request, tag, &allStreams,
+                                      /*data_count*/streamCount) != OK) {
+            camera_metadata_t *tmp = allocate_camera_metadata(1000, 10000);
+            ASSERT_OK(append_camera_metadata(tmp, request));
+            free_camera_metadata(request);
+            request = tmp;
+
+            ASSERT_OK(add_camera_metadata_entry(request, tag, &allStreams,
+                                                /*data_count*/streamCount));
+        }
+    } else {
+        ASSERT_OK(update_camera_metadata_entry(request, entry.index,
+                  &allStreams, /*data_count*/streamCount, &entry));
+    }
+
+    EXPECT_OK(mCamera->submitRequest(request, /*streaming*/true));
+
+    dout << "will sleep now for " << mDisplaySecs << std::endl;
+    sleep(mDisplaySecs);
+
+    free_camera_metadata(request);
+
+    for (int i = 0; i < streamCount; ++i) {
+        EXPECT_OK(mCamera->deleteStream(allStreams[i]));
+    }
+    EXPECT_OK(mCamera->exclusiveUnlock());
+}
+
+TEST_F(ProCameraTest, CpuConsumerSingle) {
+    if (HasFatalFailure()) {
+        return;
+    }
+
+    mListener->SetEventMask(ProEvent_Mask(BUFFER_RECEIVED));
+
+    int streamId = -1;
+    sp<CpuConsumer> consumer;
+    EXPECT_OK(mCamera->createStreamCpu(/*width*/320, /*height*/240,
+                TEST_FORMAT_DEPTH, TEST_CPU_HEAP_COUNT, &consumer, &streamId));
+    EXPECT_NE(-1, streamId);
+
+    EXPECT_OK(mCamera->exclusiveTryLock());
+    EXPECT_EQ(OK, mListener->WaitForEvent());
+    EXPECT_EQ(ACQUIRED, mListener->ReadEvent());
+    /* iterate in a loop submitting requests every frame.
+     *  what kind of requests doesnt really matter, just whatever.
+     */
+
+    // it would probably be better to use CameraMetadata from camera service.
+    camera_metadata_t *request = NULL;
+    EXPECT_OK(mCamera->createDefaultRequest(CAMERA2_TEMPLATE_PREVIEW,
+        /*out*/&request));
+    EXPECT_NE((void*)NULL, request);
+
+    /*FIXME: dont need this later, at which point the above should become an
+      ASSERT_NE*/
+    if(request == NULL) request = allocate_camera_metadata(10, 100);
+
+    // set the output streams to just this stream ID
+
+    uint8_t allStreams[] = { streamId };
+    camera_metadata_entry_t entry;
+    uint32_t tag = static_cast<uint32_t>(ANDROID_REQUEST_OUTPUT_STREAMS);
+    int find = find_camera_metadata_entry(request, tag, &entry);
+    if (find == -ENOENT) {
+        if (add_camera_metadata_entry(request, tag, &allStreams,
+                /*data_count*/1) != OK) {
+            camera_metadata_t *tmp = allocate_camera_metadata(1000, 10000);
+            ASSERT_OK(append_camera_metadata(tmp, request));
+            free_camera_metadata(request);
+            request = tmp;
+
+            ASSERT_OK(add_camera_metadata_entry(request, tag, &allStreams,
+                /*data_count*/1));
+        }
+    } else {
+        ASSERT_OK(update_camera_metadata_entry(request, entry.index,
+            &allStreams, /*data_count*/1, &entry));
+    }
+
+    EXPECT_OK(mCamera->submitRequest(request, /*streaming*/true));
+
+    // Consume a couple of frames
+    for (int i = 0; i < TEST_CPU_FRAME_COUNT; ++i) {
+        EXPECT_EQ(OK, mListener->WaitForEvent());
+        EXPECT_EQ(BUFFER_RECEIVED, mListener->ReadEvent());
+    }
+
+    // Done: clean up
+    free_camera_metadata(request);
+    EXPECT_OK(mCamera->deleteStream(streamId));
+    EXPECT_OK(mCamera->exclusiveUnlock());
+}
+
+TEST_F(ProCameraTest, CpuConsumerDual) {
+    if (HasFatalFailure()) {
+        return;
+    }
+
+    mListener->SetEventMask(ProEvent_Mask(BUFFER_RECEIVED));
+
+    int streamId = -1;
+    sp<CpuConsumer> consumer;
+    EXPECT_OK(mCamera->createStreamCpu(/*width*/1280, /*height*/960,
+                TEST_FORMAT_MAIN, TEST_CPU_HEAP_COUNT, &consumer, &streamId));
+    EXPECT_NE(-1, streamId);
+
+    int depthStreamId = -1;
+    EXPECT_OK(mCamera->createStreamCpu(/*width*/320, /*height*/240,
+            TEST_FORMAT_DEPTH, TEST_CPU_HEAP_COUNT, &consumer, &depthStreamId));
+    EXPECT_NE(-1, depthStreamId);
+
+    EXPECT_OK(mCamera->exclusiveTryLock());
+    /*
+    */
+    /* iterate in a loop submitting requests every frame.
+     *  what kind of requests doesnt really matter, just whatever.
+     */
+
+    // it would probably be better to use CameraMetadata from camera service.
+    camera_metadata_t *request = NULL;
+    EXPECT_OK(mCamera->createDefaultRequest(CAMERA2_TEMPLATE_PREVIEW,
+                                            /*out*/&request));
+    EXPECT_NE((void*)NULL, request);
+
+    if(request == NULL) request = allocate_camera_metadata(10, 100);
+
+    // set the output streams to just this stream ID
+
+    // wow what a verbose API.
+    uint8_t allStreams[] = { streamId, depthStreamId };
+    size_t streamCount = 2;
+    camera_metadata_entry_t entry;
+    uint32_t tag = static_cast<uint32_t>(ANDROID_REQUEST_OUTPUT_STREAMS);
+    int find = find_camera_metadata_entry(request, tag, &entry);
+    if (find == -ENOENT) {
+        if (add_camera_metadata_entry(request, tag, &allStreams,
+                                      /*data_count*/streamCount) != OK) {
+            camera_metadata_t *tmp = allocate_camera_metadata(1000, 10000);
+            ASSERT_OK(append_camera_metadata(tmp, request));
+            free_camera_metadata(request);
+            request = tmp;
+
+            ASSERT_OK(add_camera_metadata_entry(request, tag, &allStreams,
+                                                   /*data_count*/streamCount));
+        }
+    } else {
+        ASSERT_OK(update_camera_metadata_entry(request, entry.index,
+                              &allStreams, /*data_count*/streamCount, &entry));
+    }
+
+    EXPECT_OK(mCamera->submitRequest(request, /*streaming*/true));
+
+    // Consume a couple of frames
+    for (int i = 0; i < TEST_CPU_FRAME_COUNT; ++i) {
+        // stream id 1
+        EXPECT_EQ(OK, mListener->WaitForEvent());
+        EXPECT_EQ(BUFFER_RECEIVED, mListener->ReadEvent());
+
+        // stream id 2
+        EXPECT_EQ(OK, mListener->WaitForEvent());
+        EXPECT_EQ(BUFFER_RECEIVED, mListener->ReadEvent());
+
+        //TODO: events should be a struct with some data like the stream id
+    }
+
+    // Done: clean up
+    free_camera_metadata(request);
+    EXPECT_OK(mCamera->deleteStream(streamId));
+    EXPECT_OK(mCamera->exclusiveUnlock());
+}
+
+TEST_F(ProCameraTest, ResultReceiver) {
+    if (HasFatalFailure()) {
+        return;
+    }
+
+    mListener->SetEventMask(ProEvent_Mask(RESULT_RECEIVED));
+    //FIXME: if this is run right after the previous test we get BUFFER_RECEIVED
+    // need to filter out events at read time
+
+    int streamId = -1;
+    sp<CpuConsumer> consumer;
+    EXPECT_OK(mCamera->createStreamCpu(/*width*/1280, /*height*/960,
+                TEST_FORMAT_MAIN, TEST_CPU_HEAP_COUNT, &consumer, &streamId));
+    EXPECT_NE(-1, streamId);
+
+    EXPECT_OK(mCamera->exclusiveTryLock());
+    /*
+    */
+    /* iterate in a loop submitting requests every frame.
+     *  what kind of requests doesnt really matter, just whatever.
+     */
+
+    camera_metadata_t *request = NULL;
+    EXPECT_OK(mCamera->createDefaultRequest(CAMERA2_TEMPLATE_PREVIEW,
+                                            /*out*/&request));
+    EXPECT_NE((void*)NULL, request);
+
+    /*FIXME*/
+    if(request == NULL) request = allocate_camera_metadata(10, 100);
+
+    // set the output streams to just this stream ID
+
+    uint8_t allStreams[] = { streamId };
+    size_t streamCount = 1;
+    camera_metadata_entry_t entry;
+    uint32_t tag = static_cast<uint32_t>(ANDROID_REQUEST_OUTPUT_STREAMS);
+    int find = find_camera_metadata_entry(request, tag, &entry);
+    if (find == -ENOENT) {
+        if (add_camera_metadata_entry(request, tag, &allStreams,
+                                      /*data_count*/streamCount) != OK) {
+            camera_metadata_t *tmp = allocate_camera_metadata(1000, 10000);
+            ASSERT_OK(append_camera_metadata(tmp, request));
+            free_camera_metadata(request);
+            request = tmp;
+
+            ASSERT_OK(add_camera_metadata_entry(request, tag, &allStreams,
+                                                /*data_count*/streamCount));
+        }
+    } else {
+        ASSERT_OK(update_camera_metadata_entry(request, entry.index,
+                               &allStreams, /*data_count*/streamCount, &entry));
+    }
+
+    EXPECT_OK(mCamera->submitRequest(request, /*streaming*/true));
+
+    // Consume a couple of results
+    for (int i = 0; i < TEST_CPU_FRAME_COUNT; ++i) {
+        EXPECT_EQ(OK, mListener->WaitForEvent());
+        EXPECT_EQ(RESULT_RECEIVED, mListener->ReadEvent());
+    }
+
+    // Done: clean up
+    free_camera_metadata(request);
+    EXPECT_OK(mCamera->deleteStream(streamId));
+    EXPECT_OK(mCamera->exclusiveUnlock());
+}
+
+TEST_F(ProCameraTest, WaitForResult) {
+    if (HasFatalFailure()) {
+        return;
+    }
+
+    int streamId = -1;
+    sp<CpuConsumer> consumer;
+    EXPECT_OK(mCamera->createStreamCpu(/*width*/1280, /*height*/960,
+                 TEST_FORMAT_MAIN, TEST_CPU_HEAP_COUNT, &consumer, &streamId));
+    EXPECT_NE(-1, streamId);
+
+    EXPECT_OK(mCamera->exclusiveTryLock());
+
+    uint8_t streams[] = { streamId };
+    ASSERT_NO_FATAL_FAILURE(createSubmitRequestForStreams(streams, /*count*/1));
+
+    // Consume a couple of results
+    for (int i = 0; i < TEST_CPU_FRAME_COUNT; ++i) {
+        EXPECT_OK(mCamera->waitForFrameMetadata());
+        CameraMetadata meta = mCamera->consumeFrameMetadata();
+        EXPECT_FALSE(meta.isEmpty());
+    }
+
+    // Done: clean up
+    consumer->abandon(); // since we didn't consume any of the buffers
+    EXPECT_OK(mCamera->deleteStream(streamId));
+    EXPECT_OK(mCamera->exclusiveUnlock());
+}
+
+TEST_F(ProCameraTest, WaitForSingleStreamBuffer) {
+    if (HasFatalFailure()) {
+        return;
+    }
+
+    int streamId = -1;
+    sp<CpuConsumer> consumer;
+    EXPECT_OK(mCamera->createStreamCpu(/*width*/1280, /*height*/960,
+                  TEST_FORMAT_MAIN, TEST_CPU_HEAP_COUNT, &consumer, &streamId));
+    EXPECT_NE(-1, streamId);
+
+    EXPECT_OK(mCamera->exclusiveTryLock());
+
+    uint8_t streams[] = { streamId };
+    ASSERT_NO_FATAL_FAILURE(createSubmitRequestForStreams(streams, /*count*/1,
+                                            /*requests*/TEST_CPU_FRAME_COUNT));
+
+    // Consume a couple of results
+    for (int i = 0; i < TEST_CPU_FRAME_COUNT; ++i) {
+        EXPECT_EQ(1, mCamera->waitForFrameBuffer(streamId));
+
+        CpuConsumer::LockedBuffer buf;
+        EXPECT_OK(consumer->lockNextBuffer(&buf));
+
+        dout << "Buffer synchronously received on streamId = " << streamId <<
+                ", dataPtr = " << (void*)buf.data <<
+                ", timestamp = " << buf.timestamp << std::endl;
+
+        EXPECT_OK(consumer->unlockBuffer(buf));
+    }
+
+    // Done: clean up
+    EXPECT_OK(mCamera->deleteStream(streamId));
+    EXPECT_OK(mCamera->exclusiveUnlock());
+}
+
+TEST_F(ProCameraTest, WaitForDualStreamBuffer) {
+    if (HasFatalFailure()) {
+        return;
+    }
+
+    const int REQUEST_COUNT = TEST_CPU_FRAME_COUNT * 10;
+
+    // 15 fps
+    int streamId = -1;
+    sp<CpuConsumer> consumer;
+    EXPECT_OK(mCamera->createStreamCpu(/*width*/1280, /*height*/960,
+                 TEST_FORMAT_MAIN, TEST_CPU_HEAP_COUNT, &consumer, &streamId));
+    EXPECT_NE(-1, streamId);
+
+    // 30 fps
+    int depthStreamId = -1;
+    sp<CpuConsumer> depthConsumer;
+    EXPECT_OK(mCamera->createStreamCpu(/*width*/320, /*height*/240,
+       TEST_FORMAT_DEPTH, TEST_CPU_HEAP_COUNT, &depthConsumer, &depthStreamId));
+    EXPECT_NE(-1, depthStreamId);
+
+    EXPECT_OK(mCamera->exclusiveTryLock());
+
+    uint8_t streams[] = { streamId, depthStreamId };
+    ASSERT_NO_FATAL_FAILURE(createSubmitRequestForStreams(streams, /*count*/2,
+                                                    /*requests*/REQUEST_COUNT));
+
+    // Consume two frames simultaneously. Unsynchronized by timestamps.
+    for (int i = 0; i < REQUEST_COUNT; ++i) {
+
+        // Get the metadata
+        EXPECT_OK(mCamera->waitForFrameMetadata());
+        CameraMetadata meta = mCamera->consumeFrameMetadata();
+        EXPECT_FALSE(meta.isEmpty());
+
+        // Get the buffers
+
+        EXPECT_EQ(1, mCamera->waitForFrameBuffer(depthStreamId));
+
+        /**
+          * Guaranteed to be able to consume the depth frame,
+          * since we waited on it.
+          */
+        CpuConsumer::LockedBuffer depthBuffer;
+        EXPECT_OK(depthConsumer->lockNextBuffer(&depthBuffer));
+
+        dout << "Depth Buffer synchronously received on streamId = " <<
+                streamId <<
+                ", dataPtr = " << (void*)depthBuffer.data <<
+                ", timestamp = " << depthBuffer.timestamp << std::endl;
+
+        EXPECT_OK(depthConsumer->unlockBuffer(depthBuffer));
+
+
+        /** Consume Greyscale frames if there are any.
+          * There may not be since it runs at half FPS */
+        CpuConsumer::LockedBuffer greyBuffer;
+        while (consumer->lockNextBuffer(&greyBuffer) == OK) {
+
+            dout << "GRAY Buffer synchronously received on streamId = " <<
+                streamId <<
+                ", dataPtr = " << (void*)greyBuffer.data <<
+                ", timestamp = " << greyBuffer.timestamp << std::endl;
+
+            EXPECT_OK(consumer->unlockBuffer(greyBuffer));
+        }
+    }
+
+    // Done: clean up
+    EXPECT_OK(mCamera->deleteStream(streamId));
+    EXPECT_OK(mCamera->exclusiveUnlock());
+}
+
+TEST_F(ProCameraTest, WaitForSingleStreamBufferAndDropFrames) {
+    if (HasFatalFailure()) {
+        return;
+    }
+
+    const int NUM_REQUESTS = 20 * TEST_CPU_FRAME_COUNT;
+
+    int streamId = -1;
+    sp<CpuConsumer> consumer;
+    EXPECT_OK(mCamera->createStreamCpu(/*width*/1280, /*height*/960,
+                  TEST_FORMAT_MAIN, TEST_CPU_HEAP_COUNT, &consumer, &streamId));
+    EXPECT_NE(-1, streamId);
+
+    EXPECT_OK(mCamera->exclusiveTryLock());
+
+    uint8_t streams[] = { streamId };
+    ASSERT_NO_FATAL_FAILURE(createSubmitRequestForStreams(streams, /*count*/1,
+                                                     /*requests*/NUM_REQUESTS));
+
+    // Consume a couple of results
+    for (int i = 0; i < NUM_REQUESTS; ++i) {
+        // Process at 10fps, stream is at 15fps.
+        // This means we will definitely fill up the buffer queue with
+        // extra buffers and need to drop them.
+        usleep(TEST_FRAME_PROCESSING_DELAY_US);
+
+        int numFrames;
+        EXPECT_TRUE((numFrames = mCamera->waitForFrameBuffer(streamId)) > 0);
+
+        // Drop all but the newest framebuffer
+        EXPECT_EQ(numFrames-1, mCamera->dropFrameBuffer(streamId, numFrames-1));
+
+        dout << "Dropped " << (numFrames - 1) << " frames" << std::endl;
+
+        // Skip the counter ahead, don't try to consume these frames again
+        i += numFrames-1;
+
+        // "Consume" the buffer
+        CpuConsumer::LockedBuffer buf;
+        EXPECT_OK(consumer->lockNextBuffer(&buf));
+
+        dout << "Buffer synchronously received on streamId = " << streamId <<
+                ", dataPtr = " << (void*)buf.data <<
+                ", timestamp = " << buf.timestamp << std::endl;
+
+        EXPECT_OK(consumer->unlockBuffer(buf));
+    }
+
+    // Done: clean up
+    EXPECT_OK(mCamera->deleteStream(streamId));
+    EXPECT_OK(mCamera->exclusiveUnlock());
+}
+
+
+
+}
+}
+}
+}
+
diff --git a/camera/tests/main.cpp b/camera/tests/main.cpp
new file mode 100644
index 0000000..8c8c515
--- /dev/null
+++ b/camera/tests/main.cpp
@@ -0,0 +1,27 @@
+/*
+ * Copyright (C) 2013 The Android Open Source Project
+ *
+ * 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.
+ */
+
+#include <gtest/gtest.h>
+
+
+int main(int argc, char **argv) {
+
+    ::testing::InitGoogleTest(&argc, argv);
+
+    int ret = RUN_ALL_TESTS();
+
+    return ret;
+}
diff --git a/include/camera/Camera.h b/include/camera/Camera.h
index 43dae1c..8b87de6 100644
--- a/include/camera/Camera.h
+++ b/include/camera/Camera.h
@@ -159,7 +159,7 @@
         sp<Camera>         mCamera;
     };
 
-private:
+protected:
                         Camera();
                         Camera(const Camera&);
                         Camera& operator=(const Camera);
diff --git a/services/camera/libcameraservice/camera2/CameraMetadata.h b/include/camera/CameraMetadata.h
similarity index 95%
rename from services/camera/libcameraservice/camera2/CameraMetadata.h
rename to include/camera/CameraMetadata.h
index aee6cd7..4289126 100644
--- a/services/camera/libcameraservice/camera2/CameraMetadata.h
+++ b/include/camera/CameraMetadata.h
@@ -14,15 +14,14 @@
  * limitations under the License.
  */
 
-#ifndef ANDROID_SERVERS_CAMERA_CAMERA2METADATA_CPP
-#define ANDROID_SERVERS_CAMERA_CAMERA2METADATA_CPP
+#ifndef ANDROID_CLIENT_CAMERA2_CAMERAMETADATA_CPP
+#define ANDROID_CLIENT_CAMERA2_CAMERAMETADATA_CPP
 
 #include "system/camera_metadata.h"
 #include <utils/String8.h>
 #include <utils/Vector.h>
 
 namespace android {
-namespace camera2 {
 
 /**
  * A convenience wrapper around the C-based camera_metadata_t library.
@@ -122,6 +121,12 @@
     }
 
     /**
+     * Check if a metadata entry exists for a given tag id
+     *
+     */
+    bool exists(uint32_t tag) const;
+
+    /**
      * Get metadata entry by tag id
      */
     camera_metadata_entry find(uint32_t tag);
@@ -167,7 +172,6 @@
 
 };
 
-}; // namespace camera2
 }; // namespace android
 
 #endif
diff --git a/include/camera/ICameraService.h b/include/camera/ICameraService.h
index 7d70c1e..11d7b65 100644
--- a/include/camera/ICameraService.h
+++ b/include/camera/ICameraService.h
@@ -23,6 +23,7 @@
 
 #include <camera/ICameraClient.h>
 #include <camera/ICamera.h>
+#include <camera/IProCameraUser.h>
 
 namespace android {
 
@@ -32,7 +33,8 @@
     enum {
         GET_NUMBER_OF_CAMERAS = IBinder::FIRST_CALL_TRANSACTION,
         GET_CAMERA_INFO,
-        CONNECT
+        CONNECT,
+        CONNECT_PRO
     };
 
 public:
@@ -43,6 +45,10 @@
                                           struct CameraInfo* cameraInfo) = 0;
     virtual sp<ICamera>     connect(const sp<ICameraClient>& cameraClient,
                                     int cameraId) = 0;
+
+    virtual sp<IProCameraUser>
+                            connect(const sp<IProCameraCallbacks>& cameraCb,
+                                    int cameraId) = 0;
 };
 
 // ----------------------------------------------------------------------------
diff --git a/include/camera/IProCameraCallbacks.h b/include/camera/IProCameraCallbacks.h
new file mode 100644
index 0000000..fc24026
--- /dev/null
+++ b/include/camera/IProCameraCallbacks.h
@@ -0,0 +1,72 @@
+/*
+ * Copyright (C) 2013 The Android Open Source Project
+ *
+ * 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.
+ */
+
+#ifndef ANDROID_HARDWARE_IPROCAMERA_CALLBACKS_H
+#define ANDROID_HARDWARE_IPROCAMERA_CALLBACKS_H
+
+#include <utils/RefBase.h>
+#include <binder/IInterface.h>
+#include <binder/Parcel.h>
+#include <binder/IMemory.h>
+#include <utils/Timers.h>
+#include <system/camera.h>
+
+struct camera_metadata;
+
+namespace android {
+
+class IProCameraCallbacks: public IInterface
+{
+public:
+    DECLARE_META_INTERFACE(ProCameraCallbacks);
+
+    virtual void            notifyCallback(int32_t msgType, int32_t ext1,
+                                                              int32_t ext2) = 0;
+    virtual void            dataCallback(int32_t msgType,
+                                         const sp<IMemory>& data,
+                                         camera_frame_metadata_t *metadata) = 0;
+    virtual void            dataCallbackTimestamp(nsecs_t timestamp,
+                                                  int32_t msgType,
+                                                  const sp<IMemory>& data) = 0;
+
+    enum LockStatus {
+        LOCK_ACQUIRED,
+        LOCK_RELEASED,
+        LOCK_STOLEN,
+    };
+
+    virtual void            onLockStatusChanged(LockStatus newLockStatus) = 0;
+
+    /** Missing by design: implementation is client-side in ProCamera.cpp **/
+    // virtual void onBufferReceived(int streamId,
+    //                               const CpuConsumer::LockedBufer& buf);
+    virtual void onResultReceived(int32_t frameId, camera_metadata* result) = 0;
+};
+
+// ----------------------------------------------------------------------------
+
+class BnProCameraCallbacks: public BnInterface<IProCameraCallbacks>
+{
+public:
+    virtual status_t    onTransact( uint32_t code,
+                                    const Parcel& data,
+                                    Parcel* reply,
+                                    uint32_t flags = 0);
+};
+
+}; // namespace android
+
+#endif
diff --git a/include/camera/IProCameraUser.h b/include/camera/IProCameraUser.h
new file mode 100644
index 0000000..7bddb0c
--- /dev/null
+++ b/include/camera/IProCameraUser.h
@@ -0,0 +1,98 @@
+/*
+ * Copyright (C) 2013 The Android Open Source Project
+ *
+ * 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.
+ */
+
+#ifndef ANDROID_HARDWARE_IPROCAMERAUSER_H
+#define ANDROID_HARDWARE_IPROCAMERAUSER_H
+
+#include <utils/RefBase.h>
+#include <binder/IInterface.h>
+#include <binder/Parcel.h>
+#include <binder/IMemory.h>
+#include <utils/String8.h>
+#include <camera/IProCameraCallbacks.h>
+
+struct camera_metadata;
+
+namespace android {
+
+class IProCameraUserClient;
+class IGraphicBufferProducer;
+class Surface;
+
+class IProCameraUser: public IInterface
+{
+public:
+    DECLARE_META_INTERFACE(ProCameraUser);
+
+    virtual void            disconnect() = 0;
+
+    // connect to the service, given a callbacks listener
+    virtual status_t        connect(const sp<IProCameraCallbacks>& callbacks)
+                                                                            = 0;
+
+    /**
+     * Locking
+     **/
+    virtual status_t        exclusiveTryLock() = 0;
+    virtual status_t        exclusiveLock() = 0;
+    virtual status_t        exclusiveUnlock() = 0;
+
+    virtual bool            hasExclusiveLock() = 0;
+
+    /**
+     * Request Handling
+     **/
+
+    // Note that the callee gets a copy of the metadata.
+    virtual int             submitRequest(struct camera_metadata* metadata,
+                                          bool streaming = false) = 0;
+    virtual status_t        cancelRequest(int requestId) = 0;
+
+    virtual status_t        requestStream(int streamId) = 0;
+    virtual status_t        cancelStream(int streamId) = 0;
+    virtual status_t        createStream(
+                                      int width, int height, int format,
+                                      const sp<IGraphicBufferProducer>& bufferProducer,
+                                      /*out*/
+                                      int* streamId) = 0;
+
+    // Create a request object from a template.
+    virtual status_t        createDefaultRequest(int templateId,
+                                                 /*out*/
+                                                 camera_metadata** request)
+                                                                           = 0;
+
+    // Get static camera metadata
+    virtual status_t        getCameraInfo(int cameraId,
+                                          /*out*/
+                                          camera_metadata** info) = 0;
+
+};
+
+// ----------------------------------------------------------------------------
+
+class BnProCameraUser: public BnInterface<IProCameraUser>
+{
+public:
+    virtual status_t    onTransact( uint32_t code,
+                                    const Parcel& data,
+                                    Parcel* reply,
+                                    uint32_t flags = 0);
+};
+
+}; // namespace android
+
+#endif
diff --git a/include/camera/ProCamera.h b/include/camera/ProCamera.h
new file mode 100644
index 0000000..cd2772c
--- /dev/null
+++ b/include/camera/ProCamera.h
@@ -0,0 +1,332 @@
+/*
+ * Copyright (C) 2013 The Android Open Source Project
+ *
+ * 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.
+ */
+
+#ifndef ANDROID_HARDWARE_PRO_CAMERA_H
+#define ANDROID_HARDWARE_PRO_CAMERA_H
+
+#include <utils/Timers.h>
+#include <utils/KeyedVector.h>
+#include <gui/IGraphicBufferProducer.h>
+#include <system/camera.h>
+#include <camera/IProCameraCallbacks.h>
+#include <camera/IProCameraUser.h>
+#include <camera/Camera.h>
+#include <camera/CameraMetadata.h>
+#include <gui/CpuConsumer.h>
+
+#include <utils/Condition.h>
+#include <utils/Mutex.h>
+
+struct camera_metadata;
+
+namespace android {
+
+// All callbacks on this class are concurrent
+// (they come from separate threads)
+class ProCameraListener : public CameraListener
+{
+public:
+    // Lock has been acquired. Write operations now available.
+    virtual void onLockAcquired() = 0;
+    // Lock has been released with exclusiveUnlock.
+    virtual void onLockReleased() = 0;
+    // Lock has been stolen by another client.
+    virtual void onLockStolen() = 0;
+
+    // Lock free.
+    virtual void onTriggerNotify(int32_t msgType, int32_t ext1, int32_t ext2)
+                                                                            = 0;
+
+    // OnBufferReceived and OnRequestReceived can come in with any order,
+    // use android.sensor.timestamp and LockedBuffer.timestamp to correlate them
+
+    // A new frame buffer has been received for this stream.
+    // -- This callback only fires for createStreamCpu streams
+    // -- Use buf.timestamp to correlate with metadata's
+    //    android.sensor.timestamp
+    // -- The buffer must not be accessed after this function call completes
+    virtual void onBufferReceived(int streamId,
+                                  const CpuConsumer::LockedBuffer& buf) = 0;
+    /**
+      * A new metadata buffer has been received.
+      * -- Ownership of request passes on to the callee, free with
+      *    free_camera_metadata.
+      */
+    virtual void onResultReceived(int32_t frameId, camera_metadata* result) = 0;
+
+
+    // A new frame buffer has been received for this stream.
+    // -- This callback only fires for createStreamCpu streams
+    // -- Use buf.timestamp to correlate with metadata's android.sensor.timestamp
+    // -- The buffer should be accessed with CpuConsumer::lockNextBuffer
+    //      and CpuConsumer::unlockBuffer
+    virtual void onFrameAvailable(int streamId,
+                                  const sp<CpuConsumer>& cpuConsumer) {
+    }
+
+    virtual bool useOnFrameAvailable() {
+        return false;
+    }
+};
+
+class ProCamera : public BnProCameraCallbacks, public IBinder::DeathRecipient
+{
+public:
+    /**
+     * Connect a shared camera. By default access is restricted to read only
+     * (Lock free) operations. To be able to submit custom requests a lock needs
+     * to be acquired with exclusive[Try]Lock.
+     */
+    static sp<ProCamera> connect(int cameraId);
+    virtual void disconnect();
+    virtual ~ProCamera();
+
+    void setListener(const sp<ProCameraListener>& listener);
+
+    /**
+     * Exclusive Locks:
+     * - We may request exclusive access to a camera if no other
+     *   clients are using the camera. This works as a traditional
+     *   client, writing/reading any camera state.
+     * - An application opening the camera (a regular 'Camera') will
+     *   always steal away the exclusive lock from a ProCamera,
+     *   this will call onLockReleased.
+     * - onLockAcquired will be called again once it is possible
+     *   to again exclusively lock the camera.
+     *
+     */
+
+    /**
+     * All exclusiveLock/unlock functions are asynchronous. The remote endpoint
+     * shall not block while waiting to acquire the lock. Instead the lock
+     * notifications will come in asynchronously on the listener.
+     */
+
+    /**
+      * Attempt to acquire the lock instantly (non-blocking)
+      * - If this succeeds, you do not need to wait for onLockAcquired
+      *   but the event will still be fired
+      *
+      * Returns -EBUSY if already locked. 0 on success.
+      */
+    status_t exclusiveTryLock();
+    // always returns 0. wait for onLockAcquired before lock is acquired.
+    status_t exclusiveLock();
+    // release a lock if we have one, or cancel the lock request.
+    status_t exclusiveUnlock();
+
+    // exclusive lock = do whatever we want. no lock = read only.
+    bool hasExclusiveLock();
+
+    /**
+     * < 0 error, >= 0 the request ID. streaming to have the request repeat
+     *    until cancelled.
+     * The request queue is flushed when a lock is released or stolen
+     *    if not locked will return PERMISSION_DENIED
+     */
+    int submitRequest(const struct camera_metadata* metadata,
+                                                        bool streaming = false);
+    // if not locked will return PERMISSION_DENIED, BAD_VALUE if requestId bad
+    status_t cancelRequest(int requestId);
+
+    /**
+     * Ask for a stream to be enabled.
+     * Lock free. Service maintains counter of streams.
+     */
+    status_t requestStream(int streamId);
+// TODO: remove requestStream, its useless.
+
+    /**
+      * Delete a stream.
+      * Lock free.
+      * Errors: BAD_VALUE if unknown stream ID.
+      *         PERMISSION_DENIED if the stream wasn't yours
+      */
+    status_t deleteStream(int streamId);
+
+    /**
+      * Create a new HW stream, whose sink will be the window.
+      * Lock free. Service maintains counter of streams.
+      * Errors: -EBUSY if too many streams created
+      */
+    status_t createStream(int width, int height, int format,
+                          const sp<Surface>& surface,
+                          /*out*/
+                          int* streamId);
+
+    /**
+      * Create a new HW stream, whose sink will be the SurfaceTexture.
+      * Lock free. Service maintains counter of streams.
+      * Errors: -EBUSY if too many streams created
+      */
+    status_t createStream(int width, int height, int format,
+                          const sp<IGraphicBufferProducer>& bufferProducer,
+                          /*out*/
+                          int* streamId);
+    status_t createStreamCpu(int width, int height, int format,
+                          int heapCount,
+                          /*out*/
+                          sp<CpuConsumer>* cpuConsumer,
+                          int* streamId);
+
+    // Create a request object from a template.
+    status_t createDefaultRequest(int templateId,
+                                 /*out*/
+                                  camera_metadata** request) const;
+
+    // Get number of cameras
+    static int getNumberOfCameras();
+
+    // Get static camera metadata
+    camera_metadata* getCameraInfo(int cameraId);
+
+    // Blocks until a frame is available (CPU streams only)
+    // - Obtain the frame data by calling CpuConsumer::lockNextBuffer
+    // - Release the frame data after use with CpuConsumer::unlockBuffer
+    // Return value:
+    // - >0 - number of frames available to be locked
+    // - <0 - error (refer to error codes)
+    // Error codes:
+    // -ETIMEDOUT if it took too long to get a frame
+    int waitForFrameBuffer(int streamId);
+
+    // Blocks until a metadata result is available
+    // - Obtain the metadata by calling consumeFrameMetadata()
+    // Error codes:
+    // -ETIMEDOUT if it took too long to get a frame
+    status_t waitForFrameMetadata();
+
+    // Get the latest metadata. This is destructive.
+    // - Calling this repeatedly will produce empty metadata objects.
+    // - Use waitForFrameMetadata to sync until new data is available.
+    CameraMetadata consumeFrameMetadata();
+
+    // Convenience method to drop frame buffers (CPU streams only)
+    // Return values:
+    //  >=0 - number of frames dropped (up to count)
+    //  <0  - error code
+    // Error codes:
+    //   BAD_VALUE - invalid streamId or count passed
+    int dropFrameBuffer(int streamId, int count);
+
+    sp<IProCameraUser>         remote();
+
+protected:
+    ////////////////////////////////////////////////////////
+    // IProCameraCallbacks implementation
+    ////////////////////////////////////////////////////////
+    virtual void        notifyCallback(int32_t msgType, int32_t ext,
+                                       int32_t ext2);
+    virtual void        dataCallback(int32_t msgType,
+                                     const sp<IMemory>& dataPtr,
+                                     camera_frame_metadata_t *metadata);
+    virtual void        dataCallbackTimestamp(nsecs_t timestamp,
+                                              int32_t msgType,
+                                              const sp<IMemory>& dataPtr);
+    virtual void        onLockStatusChanged(
+                                IProCameraCallbacks::LockStatus newLockStatus);
+
+    virtual void        onResultReceived(int32_t frameId,
+                                         camera_metadata* result);
+
+    class DeathNotifier: public IBinder::DeathRecipient
+    {
+    public:
+        DeathNotifier() {
+        }
+
+        virtual void binderDied(const wp<IBinder>& who);
+    };
+
+private:
+    ProCamera();
+
+    virtual void binderDied(const wp<IBinder>& who);
+
+    // helper function to obtain camera service handle
+    static const sp<ICameraService>& getCameraService();
+
+    static sp<DeathNotifier> mDeathNotifier;
+
+    sp<IProCameraUser>  mCamera;
+    status_t            mStatus;
+
+    sp<ProCameraListener>  mListener;
+
+    friend class DeathNotifier;
+
+    static  Mutex               mLock;
+    static  sp<ICameraService>  mCameraService;
+
+    class ProFrameListener : public CpuConsumer::FrameAvailableListener {
+    public:
+        ProFrameListener(wp<ProCamera> camera, int streamID) {
+            mCamera = camera;
+            mStreamId = streamID;
+        }
+
+    protected:
+        virtual void onFrameAvailable() {
+            sp<ProCamera> c = mCamera.promote();
+            if (c.get() != NULL) {
+                c->onFrameAvailable(mStreamId);
+            }
+        }
+
+    private:
+        wp<ProCamera> mCamera;
+        int mStreamId;
+    };
+    friend class ProFrameListener;
+
+    struct StreamInfo
+    {
+        StreamInfo(int streamId) {
+            this->streamID = streamId;
+            cpuStream = false;
+            frameReady = 0;
+        }
+
+        StreamInfo() {
+            streamID = -1;
+            cpuStream = false;
+        }
+
+        int  streamID;
+        bool cpuStream;
+        sp<CpuConsumer> cpuConsumer;
+        sp<ProFrameListener> frameAvailableListener;
+        sp<Surface> stc;
+        int frameReady;
+    };
+
+    Condition mWaitCondition;
+    Mutex     mWaitMutex;
+    static const nsecs_t mWaitTimeout = 1000000000; // 1sec
+    KeyedVector<int, StreamInfo> mStreams;
+    bool mMetadataReady;
+    CameraMetadata mLatestMetadata;
+
+    void onFrameAvailable(int streamId);
+
+    StreamInfo& getStreamInfo(int streamId);
+
+
+};
+
+}; // namespace android
+
+#endif
diff --git a/services/audioflinger/Android.mk b/services/audioflinger/Android.mk
index 6d42143..0855db6 100644
--- a/services/audioflinger/Android.mk
+++ b/services/audioflinger/Android.mk
@@ -66,14 +66,6 @@
 
 LOCAL_CFLAGS += -UFAST_TRACKS_AT_NON_NATIVE_SAMPLE_RATE
 
-# uncomment for dumpsys to write most recent audio output to .wav file
-# 47.5 seconds at 44.1 kHz, 8 megabytes
-# LOCAL_CFLAGS += -DTEE_SINK_FRAMES=0x200000
-
-# uncomment for dumpsys to write most recent audio input to .wav file
-# 47.5 seconds at 44.1 kHz, 8 megabytes
-# LOCAL_CFLAGS += -DTEE_SINK_INPUT_FRAMES=0x200000
-
 # uncomment to enable the audio watchdog
 # LOCAL_SRC_FILES += AudioWatchdog.cpp
 # LOCAL_CFLAGS += -DAUDIO_WATCHDOG
diff --git a/services/audioflinger/AudioFlinger.cpp b/services/audioflinger/AudioFlinger.cpp
index 47c2772..e0ab8cd 100644
--- a/services/audioflinger/AudioFlinger.cpp
+++ b/services/audioflinger/AudioFlinger.cpp
@@ -19,6 +19,7 @@
 #define LOG_TAG "AudioFlinger"
 //#define LOG_NDEBUG 0
 
+#include <dirent.h>
 #include <math.h>
 #include <signal.h>
 #include <sys/time.h>
@@ -61,6 +62,9 @@
 
 #include <media/IMediaLogService.h>
 
+#include <media/nbaio/Pipe.h>
+#include <media/nbaio/PipeReader.h>
+
 // ----------------------------------------------------------------------------
 
 // Note: the following macro is used for extremely verbose logging message.  In
@@ -86,6 +90,14 @@
 
 uint32_t AudioFlinger::mScreenState;
 
+bool AudioFlinger::mTeeSinkInputEnabled = false;
+bool AudioFlinger::mTeeSinkOutputEnabled = false;
+bool AudioFlinger::mTeeSinkTrackEnabled = false;
+
+size_t AudioFlinger::mTeeSinkInputFrames = kTeeSinkInputFramesDefault;
+size_t AudioFlinger::mTeeSinkOutputFrames = kTeeSinkOutputFramesDefault;
+size_t AudioFlinger::mTeeSinkTrackFrames = kTeeSinkTrackFramesDefault;
+
 // ----------------------------------------------------------------------------
 
 static int load_audio_interface(const char *if_name, audio_hw_device_t **dev)
@@ -134,6 +146,19 @@
     if (doLog) {
         mLogMemoryDealer = new MemoryDealer(kLogMemorySize, "LogWriters");
     }
+    (void) property_get("ro.debuggable", value, "0");
+    int debuggable = atoi(value);
+    int teeEnabled = 0;
+    if (debuggable) {
+        (void) property_get("af.tee", value, "0");
+        teeEnabled = atoi(value);
+    }
+    if (teeEnabled & 1)
+        mTeeSinkInputEnabled = true;
+    if (teeEnabled & 2)
+        mTeeSinkOutputEnabled = true;
+    if (teeEnabled & 4)
+        mTeeSinkTrackEnabled = true;
 }
 
 void AudioFlinger::onFirstRef()
@@ -1602,7 +1627,6 @@
         // Try to re-use most recently used Pipe to archive a copy of input for dumpsys,
         // or (re-)create if current Pipe is idle and does not match the new format
         sp<NBAIO_Sink> teeSink;
-#ifdef TEE_SINK_INPUT_FRAMES
         enum {
             TEE_SINK_NO,    // don't copy input
             TEE_SINK_NEW,   // copy input using a new pipe
@@ -1610,7 +1634,9 @@
         } kind;
         NBAIO_Format format = Format_from_SR_C(inStream->common.get_sample_rate(&inStream->common),
                                         popcount(inStream->common.get_channels(&inStream->common)));
-        if (format == Format_Invalid) {
+        if (!mTeeSinkInputEnabled) {
+            kind = TEE_SINK_NO;
+        } else if (format == Format_Invalid) {
             kind = TEE_SINK_NO;
         } else if (mRecordTeeSink == 0) {
             kind = TEE_SINK_NEW;
@@ -1623,7 +1649,7 @@
         }
         switch (kind) {
         case TEE_SINK_NEW: {
-            Pipe *pipe = new Pipe(TEE_SINK_INPUT_FRAMES, format);
+            Pipe *pipe = new Pipe(mTeeSinkInputFrames, format);
             size_t numCounterOffers = 0;
             const NBAIO_Format offers[1] = {format};
             ssize_t index = pipe->negotiate(offers, 1, NULL, numCounterOffers);
@@ -1644,7 +1670,7 @@
         default:
             break;
         }
-#endif
+
         AudioStreamIn *input = new AudioStreamIn(inHwDev, inStream);
 
         // Start record thread
@@ -2199,19 +2225,80 @@
     return NO_ERROR;
 }
 
+struct Entry {
+#define MAX_NAME 32     // %Y%m%d%H%M%S_%d.wav
+    char mName[MAX_NAME];
+};
+
+int comparEntry(const void *p1, const void *p2)
+{
+    return strcmp(((const Entry *) p1)->mName, ((const Entry *) p2)->mName);
+}
+
 void AudioFlinger::dumpTee(int fd, const sp<NBAIO_Source>& source, audio_io_handle_t id)
 {
     NBAIO_Source *teeSource = source.get();
     if (teeSource != NULL) {
+        // .wav rotation
+        // There is a benign race condition if 2 threads call this simultaneously.
+        // They would both traverse the directory, but the result would simply be
+        // failures at unlink() which are ignored.  It's also unlikely since
+        // normally dumpsys is only done by bugreport or from the command line.
+        char teePath[32+256];
+        strcpy(teePath, "/data/misc/media");
+        size_t teePathLen = strlen(teePath);
+        DIR *dir = opendir(teePath);
+        teePath[teePathLen++] = '/';
+        if (dir != NULL) {
+#define MAX_SORT 20 // number of entries to sort
+#define MAX_KEEP 10 // number of entries to keep
+            struct Entry entries[MAX_SORT];
+            size_t entryCount = 0;
+            while (entryCount < MAX_SORT) {
+                struct dirent de;
+                struct dirent *result = NULL;
+                int rc = readdir_r(dir, &de, &result);
+                if (rc != 0) {
+                    ALOGW("readdir_r failed %d", rc);
+                    break;
+                }
+                if (result == NULL) {
+                    break;
+                }
+                if (result != &de) {
+                    ALOGW("readdir_r returned unexpected result %p != %p", result, &de);
+                    break;
+                }
+                // ignore non .wav file entries
+                size_t nameLen = strlen(de.d_name);
+                if (nameLen <= 4 || nameLen >= MAX_NAME ||
+                        strcmp(&de.d_name[nameLen - 4], ".wav")) {
+                    continue;
+                }
+                strcpy(entries[entryCount++].mName, de.d_name);
+            }
+            (void) closedir(dir);
+            if (entryCount > MAX_KEEP) {
+                qsort(entries, entryCount, sizeof(Entry), comparEntry);
+                for (size_t i = 0; i < entryCount - MAX_KEEP; ++i) {
+                    strcpy(&teePath[teePathLen], entries[i].mName);
+                    (void) unlink(teePath);
+                }
+            }
+        } else {
+            if (fd >= 0) {
+                fdprintf(fd, "unable to rotate tees in %s: %s\n", teePath, strerror(errno));
+            }
+        }
         char teeTime[16];
         struct timeval tv;
         gettimeofday(&tv, NULL);
         struct tm tm;
         localtime_r(&tv.tv_sec, &tm);
-        strftime(teeTime, sizeof(teeTime), "%T", &tm);
-        char teePath[64];
-        sprintf(teePath, "/data/misc/media/%s_%d.wav", teeTime, id);
-        int teeFd = open(teePath, O_WRONLY | O_CREAT, S_IRUSR | S_IWUSR);
+        strftime(teeTime, sizeof(teeTime), "%Y%m%d%H%M%S", &tm);
+        snprintf(&teePath[teePathLen], sizeof(teePath) - teePathLen, "%s_%d.wav", teeTime, id);
+        // if 2 dumpsys are done within 1 second, and rotation didn't work, then discard 2nd
+        int teeFd = open(teePath, O_WRONLY | O_CREAT | O_EXCL | O_NOFOLLOW, S_IRUSR | S_IWUSR);
         if (teeFd >= 0) {
             char wavHeader[44];
             memcpy(wavHeader,
@@ -2253,9 +2340,13 @@
             temp =  total * channelCount * sizeof(short);
             write(teeFd, &temp, sizeof(temp));
             close(teeFd);
-            fdprintf(fd, "FastMixer tee copied to %s\n", teePath);
+            if (fd >= 0) {
+                fdprintf(fd, "tee copied to %s\n", teePath);
+            }
         } else {
-            fdprintf(fd, "FastMixer unable to create tee %s: \n", strerror(errno));
+            if (fd >= 0) {
+                fdprintf(fd, "unable to create tee %s: %s\n", teePath, strerror(errno));
+            }
         }
     }
 }
diff --git a/services/audioflinger/AudioFlinger.h b/services/audioflinger/AudioFlinger.h
index c3f08f6..44bd260 100644
--- a/services/audioflinger/AudioFlinger.h
+++ b/services/audioflinger/AudioFlinger.h
@@ -593,7 +593,24 @@
     sp<NBAIO_Source> mRecordTeeSource;
 
 public:
+    // tee sink, if enabled by property, allows dumpsys to write most recent audio to .wav file
     static void dumpTee(int fd, const sp<NBAIO_Source>& source, audio_io_handle_t id = 0);
+
+    // whether tee sink is enabled by property
+    static bool mTeeSinkInputEnabled;
+    static bool mTeeSinkOutputEnabled;
+    static bool mTeeSinkTrackEnabled;
+
+    // runtime configured size of each tee sink pipe, in frames
+    static size_t mTeeSinkInputFrames;
+    static size_t mTeeSinkOutputFrames;
+    static size_t mTeeSinkTrackFrames;
+
+    // compile-time default size of tee sink pipes, in frames
+    // 0x200000 stereo 16-bit PCM frames = 47.5 seconds at 44.1 kHz, 8 megabytes
+    static const size_t kTeeSinkInputFramesDefault = 0x200000;
+    static const size_t kTeeSinkOutputFramesDefault = 0x200000;
+    static const size_t kTeeSinkTrackFramesDefault = 0x1000;
 };
 
 #undef INCLUDING_FROM_AUDIOFLINGER_H
diff --git a/services/audioflinger/Threads.cpp b/services/audioflinger/Threads.cpp
index ba848d7..1209ea6 100644
--- a/services/audioflinger/Threads.cpp
+++ b/services/audioflinger/Threads.cpp
@@ -2124,19 +2124,19 @@
                 (monoPipe->maxFrames() * 7) / 8 : mNormalFrameCount * 2);
         mPipeSink = monoPipe;
 
-#ifdef TEE_SINK_FRAMES
-        // create a Pipe to archive a copy of FastMixer's output for dumpsys
-        Pipe *teeSink = new Pipe(TEE_SINK_FRAMES, format);
-        numCounterOffers = 0;
-        index = teeSink->negotiate(offers, 1, NULL, numCounterOffers);
-        ALOG_ASSERT(index == 0);
-        mTeeSink = teeSink;
-        PipeReader *teeSource = new PipeReader(*teeSink);
-        numCounterOffers = 0;
-        index = teeSource->negotiate(offers, 1, NULL, numCounterOffers);
-        ALOG_ASSERT(index == 0);
-        mTeeSource = teeSource;
-#endif
+        if (mTeeSinkOutputEnabled) {
+            // create a Pipe to archive a copy of FastMixer's output for dumpsys
+            Pipe *teeSink = new Pipe(mTeeSinkOutputFrames, format);
+            numCounterOffers = 0;
+            index = teeSink->negotiate(offers, 1, NULL, numCounterOffers);
+            ALOG_ASSERT(index == 0);
+            mTeeSink = teeSink;
+            PipeReader *teeSource = new PipeReader(*teeSink);
+            numCounterOffers = 0;
+            index = teeSource->negotiate(offers, 1, NULL, numCounterOffers);
+            ALOG_ASSERT(index == 0);
+            mTeeSource = teeSource;
+        }
 
         // create fast mixer and configure it initially with just one fast track for our submix
         mFastMixer = new FastMixer();
diff --git a/services/audioflinger/TrackBase.h b/services/audioflinger/TrackBase.h
index e0bd97a..fecbfda 100644
--- a/services/audioflinger/TrackBase.h
+++ b/services/audioflinger/TrackBase.h
@@ -141,4 +141,7 @@
     Vector < sp<SyncEvent> >mSyncEvents;
     const bool          mIsOut;
     ServerProxy*        mServerProxy;
+    const int           mId;
+    sp<NBAIO_Sink>      mTeeSink;
+    sp<NBAIO_Source>    mTeeSource;
 };
diff --git a/services/audioflinger/Tracks.cpp b/services/audioflinger/Tracks.cpp
index 315cbbc..724ce38 100644
--- a/services/audioflinger/Tracks.cpp
+++ b/services/audioflinger/Tracks.cpp
@@ -32,6 +32,9 @@
 #include "AudioFlinger.h"
 #include "ServiceUtilities.h"
 
+#include <media/nbaio/Pipe.h>
+#include <media/nbaio/PipeReader.h>
+
 // ----------------------------------------------------------------------------
 
 // Note: the following macro is used for extremely verbose logging message.  In
@@ -53,6 +56,8 @@
 //      TrackBase
 // ----------------------------------------------------------------------------
 
+static volatile int32_t nextTrackId = 55;
+
 // TrackBase constructor must be called with AudioFlinger::mLock held
 AudioFlinger::ThreadBase::TrackBase::TrackBase(
             ThreadBase *thread,
@@ -82,7 +87,8 @@
         mStepServerFailed(false),
         mSessionId(sessionId),
         mIsOut(isOut),
-        mServerProxy(NULL)
+        mServerProxy(NULL),
+        mId(android_atomic_inc(&nextTrackId))
 {
     // client == 0 implies sharedBuffer == 0
     ALOG_ASSERT(!(client == 0 && sharedBuffer != 0));
@@ -134,11 +140,30 @@
         }
         mBufferEnd = (uint8_t *)mBuffer + bufferSize;
         mServerProxy = new ServerProxy(mCblk, mBuffer, frameCount, mFrameSize, isOut);
+
+        if (mTeeSinkTrackEnabled) {
+        NBAIO_Format pipeFormat = Format_from_SR_C(mSampleRate, mChannelCount);
+        if (pipeFormat != Format_Invalid) {
+            Pipe *pipe = new Pipe(mTeeSinkTrackFrames, pipeFormat);
+            size_t numCounterOffers = 0;
+            const NBAIO_Format offers[1] = {pipeFormat};
+            ssize_t index = pipe->negotiate(offers, 1, NULL, numCounterOffers);
+            ALOG_ASSERT(index == 0);
+            PipeReader *pipeReader = new PipeReader(*pipe);
+            numCounterOffers = 0;
+            index = pipeReader->negotiate(offers, 1, NULL, numCounterOffers);
+            ALOG_ASSERT(index == 0);
+            mTeeSink = pipe;
+            mTeeSource = pipeReader;
+        }
+        }
+
     }
 }
 
 AudioFlinger::ThreadBase::TrackBase::~TrackBase()
 {
+    dumpTee(-1, mTeeSource, mId);
     // delete the proxy before deleting the shared memory it refers to, to avoid dangling reference
     delete mServerProxy;
     if (mCblk != NULL) {
@@ -164,6 +189,10 @@
 // This implementation of releaseBuffer() is used by Track and RecordTrack, but not TimedTrack
 void AudioFlinger::ThreadBase::TrackBase::releaseBuffer(AudioBufferProvider::Buffer* buffer)
 {
+    if (mTeeSink != 0) {
+        (void) mTeeSink->write(buffer->raw, buffer->frameCount);
+    }
+
     buffer->raw = NULL;
     mStepCount = buffer->frameCount;
     // FIXME See note at getNextBuffer()
diff --git a/services/camera/libcameraservice/Android.mk b/services/camera/libcameraservice/Android.mk
index 5245983..f76c861 100644
--- a/services/camera/libcameraservice/Android.mk
+++ b/services/camera/libcameraservice/Android.mk
@@ -10,8 +10,8 @@
     CameraService.cpp \
     CameraClient.cpp \
     Camera2Client.cpp \
+    ProCamera2Client.cpp \
     Camera2Device.cpp \
-    camera2/CameraMetadata.cpp \
     camera2/Parameters.cpp \
     camera2/FrameProcessor.cpp \
     camera2/StreamingProcessor.cpp \
@@ -20,7 +20,8 @@
     camera2/ZslProcessor.cpp \
     camera2/BurstCapture.cpp \
     camera2/JpegCompressor.cpp \
-    camera2/CaptureSequencer.cpp
+    camera2/CaptureSequencer.cpp \
+    camera2/ProFrameProcessor.cpp \
 
 LOCAL_SHARED_LIBRARIES:= \
     libui \
diff --git a/services/camera/libcameraservice/Camera2Client.h b/services/camera/libcameraservice/Camera2Client.h
index 4669958..a4d4478 100644
--- a/services/camera/libcameraservice/Camera2Client.h
+++ b/services/camera/libcameraservice/Camera2Client.h
@@ -157,7 +157,6 @@
     mutable Mutex mICameraLock;
 
     typedef camera2::Parameters Parameters;
-    typedef camera2::CameraMetadata CameraMetadata;
 
     status_t setPreviewWindowL(const sp<IBinder>& binder,
             sp<ANativeWindow> window);
diff --git a/services/camera/libcameraservice/Camera2Device.cpp b/services/camera/libcameraservice/Camera2Device.cpp
index 5bfa085..921c8fc 100644
--- a/services/camera/libcameraservice/Camera2Device.cpp
+++ b/services/camera/libcameraservice/Camera2Device.cpp
@@ -202,7 +202,7 @@
     return res;
 }
 
-const camera2::CameraMetadata& Camera2Device::info() const {
+const CameraMetadata& Camera2Device::info() const {
     ALOGVV("%s: E", __FUNCTION__);
 
     return mDeviceInfo;
diff --git a/services/camera/libcameraservice/Camera2Device.h b/services/camera/libcameraservice/Camera2Device.h
index 41df2e4..86ff80f 100644
--- a/services/camera/libcameraservice/Camera2Device.h
+++ b/services/camera/libcameraservice/Camera2Device.h
@@ -27,14 +27,12 @@
 #include <utils/Vector.h>
 
 #include "hardware/camera2.h"
-#include "camera2/CameraMetadata.h"
+#include "camera/CameraMetadata.h"
 
 namespace android {
 
 class Camera2Device : public virtual RefBase {
   public:
-    typedef camera2::CameraMetadata CameraMetadata;
-
     Camera2Device(int id);
 
     ~Camera2Device();
diff --git a/services/camera/libcameraservice/CameraService.cpp b/services/camera/libcameraservice/CameraService.cpp
index 31e20c5..eb8bc05 100644
--- a/services/camera/libcameraservice/CameraService.cpp
+++ b/services/camera/libcameraservice/CameraService.cpp
@@ -39,6 +39,7 @@
 #include "CameraService.h"
 #include "CameraClient.h"
 #include "Camera2Client.h"
+#include "ProCamera2Client.h"
 
 namespace android {
 
@@ -79,6 +80,8 @@
 
 void CameraService::onFirstRef()
 {
+    LOG1("CameraService::onFirstRef");
+
     BnCameraService::onFirstRef();
 
     if (hw_get_module(CAMERA_HARDWARE_MODULE_ID,
@@ -131,6 +134,26 @@
     return rc;
 }
 
+int CameraService::getDeviceVersion(int cameraId, int* facing) {
+    struct camera_info info;
+    if (mModule->get_camera_info(cameraId, &info) != OK) {
+        return -1;
+    }
+
+    int deviceVersion;
+    if (mModule->common.module_api_version >= CAMERA_MODULE_API_VERSION_2_0) {
+        deviceVersion = info.device_version;
+    } else {
+        deviceVersion = CAMERA_DEVICE_API_VERSION_1_0;
+    }
+
+    if (facing) {
+        *facing = info.facing;
+    }
+
+    return deviceVersion;
+}
+
 sp<ICamera> CameraService::connect(
         const sp<ICameraClient>& cameraClient, int cameraId) {
     int callingPid = getCallingPid();
@@ -166,6 +189,7 @@
                      callingPid);
                 return client;
             } else {
+                // TODOSC: need to support 1 regular client, multiple shared clients here
                 ALOGW("CameraService::connect X (pid %d) rejected (existing client).",
                       callingPid);
                 return NULL;
@@ -174,34 +198,37 @@
         mClient[cameraId].clear();
     }
 
+    /*
+    mBusy is set to false as the last step of the Client destructor,
+    after which it is guaranteed that the Client destructor has finished (
+    including any inherited destructors)
+
+    We only need this for a Client subclasses since we don't allow
+    multiple Clents to be opened concurrently, but multiple BasicClient
+    would be fine
+    */
     if (mBusy[cameraId]) {
         ALOGW("CameraService::connect X (pid %d) rejected"
                 " (camera %d is still busy).", callingPid, cameraId);
         return NULL;
     }
 
-    struct camera_info info;
-    if (mModule->get_camera_info(cameraId, &info) != OK) {
-        ALOGE("Invalid camera id %d", cameraId);
-        return NULL;
-    }
-
-    int deviceVersion;
-    if (mModule->common.module_api_version == CAMERA_MODULE_API_VERSION_2_0) {
-        deviceVersion = info.device_version;
-    } else {
-        deviceVersion = CAMERA_DEVICE_API_VERSION_1_0;
-    }
+    int facing = -1;
+    int deviceVersion = getDeviceVersion(cameraId, &facing);
 
     switch(deviceVersion) {
       case CAMERA_DEVICE_API_VERSION_1_0:
         client = new CameraClient(this, cameraClient, cameraId,
-                info.facing, callingPid, getpid());
+                facing, callingPid, getpid());
         break;
       case CAMERA_DEVICE_API_VERSION_2_0:
+      case CAMERA_DEVICE_API_VERSION_2_1:
         client = new Camera2Client(this, cameraClient, cameraId,
-                info.facing, callingPid, getpid());
+                facing, callingPid, getpid());
         break;
+      case -1:
+        ALOGE("Invalid camera id %d", cameraId);
+        return NULL;
       default:
         ALOGE("Unknown camera device HAL version: %d", deviceVersion);
         return NULL;
@@ -218,16 +245,80 @@
     return client;
 }
 
-void CameraService::removeClient(const sp<ICameraClient>& cameraClient) {
+sp<IProCameraUser> CameraService::connect(
+                                        const sp<IProCameraCallbacks>& cameraCb,
+                                        int cameraId)
+{
     int callingPid = getCallingPid();
-    LOG1("CameraService::removeClient E (pid %d)", callingPid);
+
+    LOG1("CameraService::connectPro E (pid %d, id %d)", callingPid, cameraId);
+
+    if (!mModule) {
+        ALOGE("Camera HAL module not loaded");
+        return NULL;
+    }
+
+    sp<ProClient> client;
+    if (cameraId < 0 || cameraId >= mNumberOfCameras) {
+        ALOGE("CameraService::connectPro X (pid %d) rejected (invalid cameraId %d).",
+            callingPid, cameraId);
+        return NULL;
+    }
+
+    char value[PROPERTY_VALUE_MAX];
+    property_get("sys.secpolicy.camera.disabled", value, "0");
+    if (strcmp(value, "1") == 0) {
+        // Camera is disabled by DevicePolicyManager.
+        ALOGI("Camera is disabled. connect X (pid %d) rejected", callingPid);
+        return NULL;
+    }
+
+    int facing = -1;
+    int deviceVersion = getDeviceVersion(cameraId, &facing);
+
+    switch(deviceVersion) {
+      case CAMERA_DEVICE_API_VERSION_1_0:
+        ALOGE("Camera id %d uses HALv1, doesn't support ProCamera", cameraId);
+        return NULL;
+        break;
+      case CAMERA_DEVICE_API_VERSION_2_0:
+      case CAMERA_DEVICE_API_VERSION_2_1:
+        client = new ProCamera2Client(this, cameraCb, cameraId,
+                facing, callingPid, getpid());
+        break;
+      case -1:
+        ALOGE("Invalid camera id %d", cameraId);
+        return NULL;
+      default:
+        ALOGE("Unknown camera device HAL version: %d", deviceVersion);
+        return NULL;
+    }
+
+    if (client->initialize(mModule) != OK) {
+        return NULL;
+    }
+
+    mProClientList[cameraId].push(client);
+
+    cameraCb->asBinder()->linkToDeath(this);
+
+    LOG1("CameraService::connect X (id %d, this pid is %d)", cameraId, getpid());
+    return client;
+
+
+    return NULL;
+}
+
+void CameraService::removeClientByRemote(const wp<IBinder>& remoteBinder) {
+    int callingPid = getCallingPid();
+    LOG1("CameraService::removeClientByRemote E (pid %d)", callingPid);
 
     // Declare this before the lock to make absolutely sure the
     // destructor won't be called with the lock held.
     Mutex::Autolock lock(mServiceLock);
 
     int outIndex;
-    sp<Client> client = findClientUnsafe(cameraClient->asBinder(), outIndex);
+    sp<Client> client = findClientUnsafe(remoteBinder, outIndex);
 
     if (client != 0) {
         // Found our camera, clear and leave.
@@ -235,9 +326,50 @@
         mClient[outIndex].clear();
 
         client->unlinkToDeath(this);
+    } else {
+
+        sp<ProClient> clientPro = findProClientUnsafe(remoteBinder);
+
+        if (clientPro != NULL) {
+            // Found our camera, clear and leave.
+            LOG1("removeClient: clear pro %p", clientPro.get());
+
+            clientPro->getRemoteCallback()->asBinder()->unlinkToDeath(this);
+        }
     }
 
-    LOG1("CameraService::removeClient X (pid %d)", callingPid);
+    LOG1("CameraService::removeClientByRemote X (pid %d)", callingPid);
+}
+
+sp<CameraService::ProClient> CameraService::findProClientUnsafe(
+                        const wp<IBinder>& cameraCallbacksRemote)
+{
+    sp<ProClient> clientPro;
+
+    for (int i = 0; i < mNumberOfCameras; ++i) {
+        Vector<size_t> removeIdx;
+
+        for (size_t j = 0; j < mProClientList[i].size(); ++j) {
+            wp<ProClient> cl = mProClientList[i][j];
+
+            sp<ProClient> clStrong = cl.promote();
+            if (clStrong != NULL && clStrong->getRemote() == cameraCallbacksRemote) {
+                clientPro = clStrong;
+                break;
+            } else if (clStrong == NULL) {
+                // mark to clean up dead ptr
+                removeIdx.push(j);
+            }
+        }
+
+        // remove stale ptrs (in reverse so the indices dont change)
+        for (ssize_t j = (ssize_t)removeIdx.size() - 1; j >= 0; --j) {
+            mProClientList[i].removeAt(removeIdx[j]);
+        }
+
+    }
+
+    return clientPro;
 }
 
 sp<CameraService::Client> CameraService::findClientUnsafe(
@@ -251,7 +383,7 @@
         if (mClient[i] == 0) continue;
 
         // Promote mClient. It can fail if we are called from this path:
-        // Client::~Client() -> disconnect() -> removeClient().
+        // Client::~Client() -> disconnect() -> removeClientByRemote().
         client = mClient[i].promote();
 
         // Clean up stale client entry
@@ -281,12 +413,12 @@
     return &mClientLock[cameraId];
 }
 
-sp<CameraService::Client> CameraService::getClientByRemote(
+sp<CameraService::BasicClient> CameraService::getClientByRemote(
                                 const wp<IBinder>& cameraClient) {
 
     // Declare this before the lock to make absolutely sure the
     // destructor won't be called with the lock held.
-    sp<Client> client;
+    sp<BasicClient> client;
 
     Mutex::Autolock lock(mServiceLock);
 
@@ -301,6 +433,7 @@
     // Permission checks
     switch (code) {
         case BnCameraService::CONNECT:
+        case BnCameraService::CONNECT_PRO:
             const int pid = getCallingPid();
             const int self_pid = getpid();
             if (pid != self_pid) {
@@ -389,17 +522,15 @@
 
 CameraService::Client::Client(const sp<CameraService>& cameraService,
         const sp<ICameraClient>& cameraClient,
-        int cameraId, int cameraFacing, int clientPid, int servicePid) {
+        int cameraId, int cameraFacing, int clientPid, int servicePid) :
+        CameraService::BasicClient(cameraService, cameraClient->asBinder(),
+                                   cameraId, cameraFacing,
+                                   clientPid, servicePid)
+{
     int callingPid = getCallingPid();
     LOG1("Client::Client E (pid %d, id %d)", callingPid, cameraId);
 
-    mCameraService = cameraService;
     mCameraClient = cameraClient;
-    mCameraId = cameraId;
-    mCameraFacing = cameraFacing;
-    mClientPid = clientPid;
-    mServicePid = servicePid;
-    mDestructionStarted = false;
 
     cameraService->setCameraBusy(cameraId);
     cameraService->loadSound();
@@ -408,12 +539,37 @@
 
 // tear down the client
 CameraService::Client::~Client() {
+    mDestructionStarted = true;
+
     mCameraService->releaseSound();
 
     // unconditionally disconnect. function is idempotent
     Client::disconnect();
 }
 
+CameraService::BasicClient::BasicClient(const sp<CameraService>& cameraService,
+                                   const sp<IBinder>& remoteCallback,
+                                   int cameraId, int cameraFacing,
+                                   int clientPid, int servicePid)
+{
+    mCameraService = cameraService;
+    mRemoteCallback = remoteCallback;
+    mCameraId = cameraId;
+    mCameraFacing = cameraFacing;
+    mClientPid = clientPid;
+    mServicePid = servicePid;
+
+    mDestructionStarted = false;
+}
+
+CameraService::BasicClient::~BasicClient() {
+    mDestructionStarted = true;
+}
+
+void CameraService::BasicClient::disconnect() {
+    mCameraService->removeClientByRemote(mRemoteCallback);
+}
+
 // ----------------------------------------------------------------------------
 
 Mutex* CameraService::Client::getClientLockFromCookie(void* user) {
@@ -438,11 +594,96 @@
 
 // NOTE: function is idempotent
 void CameraService::Client::disconnect() {
-    mCameraService->removeClient(mCameraClient);
+    BasicClient::disconnect();
     mCameraService->setCameraFree(mCameraId);
 }
 
 // ----------------------------------------------------------------------------
+//                  IProCamera
+// ----------------------------------------------------------------------------
+
+CameraService::ProClient::ProClient(const sp<CameraService>& cameraService,
+                const sp<IProCameraCallbacks>& remoteCallback,
+                int cameraId,
+                int cameraFacing,
+                int clientPid,
+                int servicePid)
+ :       CameraService::BasicClient(cameraService, remoteCallback->asBinder(),
+                                   cameraId, cameraFacing,
+                                   clientPid, servicePid)
+{
+    mRemoteCallback = remoteCallback;
+}
+
+CameraService::ProClient::~ProClient() {
+    mDestructionStarted = true;
+
+    ProClient::disconnect();
+}
+
+status_t CameraService::ProClient::connect(const sp<IProCameraCallbacks>& callbacks) {
+    ALOGE("%s: not implemented yet", __FUNCTION__);
+
+    return INVALID_OPERATION;
+}
+
+void CameraService::ProClient::disconnect() {
+    BasicClient::disconnect();
+}
+
+status_t CameraService::ProClient::initialize(camera_module_t* module)
+{
+    ALOGW("%s: not implemented yet", __FUNCTION__);
+    return OK;
+}
+
+status_t CameraService::ProClient::exclusiveTryLock() {
+    ALOGE("%s: not implemented yet", __FUNCTION__);
+    return INVALID_OPERATION;
+}
+
+status_t CameraService::ProClient::exclusiveLock() {
+    ALOGE("%s: not implemented yet", __FUNCTION__);
+    return INVALID_OPERATION;
+}
+
+status_t CameraService::ProClient::exclusiveUnlock() {
+    ALOGE("%s: not implemented yet", __FUNCTION__);
+    return INVALID_OPERATION;
+}
+
+bool CameraService::ProClient::hasExclusiveLock() {
+    ALOGE("%s: not implemented yet", __FUNCTION__);
+    return false;
+}
+
+status_t CameraService::ProClient::submitRequest(camera_metadata_t* request, bool streaming) {
+    ALOGE("%s: not implemented yet", __FUNCTION__);
+
+    free_camera_metadata(request);
+
+    return INVALID_OPERATION;
+}
+
+status_t CameraService::ProClient::cancelRequest(int requestId) {
+    ALOGE("%s: not implemented yet", __FUNCTION__);
+
+    return INVALID_OPERATION;
+}
+
+status_t CameraService::ProClient::requestStream(int streamId) {
+    ALOGE("%s: not implemented yet", __FUNCTION__);
+
+    return INVALID_OPERATION;
+}
+
+status_t CameraService::ProClient::cancelStream(int streamId) {
+    ALOGE("%s: not implemented yet", __FUNCTION__);
+
+    return INVALID_OPERATION;
+}
+
+// ----------------------------------------------------------------------------
 
 static const int kDumpLockRetries = 50;
 static const int kDumpLockSleep = 60000;
@@ -568,7 +809,7 @@
 
     ALOGV("java clients' binder died");
 
-    sp<Client> cameraClient = getClientByRemote(who);
+    sp<BasicClient> cameraClient = getClientByRemote(who);
 
     if (cameraClient == 0) {
         ALOGV("java clients' binder death already cleaned up (normal case)");
diff --git a/services/camera/libcameraservice/CameraService.h b/services/camera/libcameraservice/CameraService.h
index 41365a0..9e0f62a 100644
--- a/services/camera/libcameraservice/CameraService.h
+++ b/services/camera/libcameraservice/CameraService.h
@@ -18,6 +18,7 @@
 #ifndef ANDROID_SERVERS_CAMERA_CAMERASERVICE_H
 #define ANDROID_SERVERS_CAMERA_CAMERASERVICE_H
 
+#include <utils/Vector.h>
 #include <binder/BinderService.h>
 #include <camera/ICameraService.h>
 #include <hardware/camera.h>
@@ -40,27 +41,32 @@
     friend class BinderService<CameraService>;
 public:
     class Client;
+    class BasicClient;
+
+    // Implementation of BinderService<T>
     static char const* getServiceName() { return "media.camera"; }
 
                         CameraService();
     virtual             ~CameraService();
 
+    /////////////////////////////////////////////////////////////////////
+    // ICameraService
     virtual int32_t     getNumberOfCameras();
     virtual status_t    getCameraInfo(int cameraId,
                                       struct CameraInfo* cameraInfo);
     virtual sp<ICamera> connect(const sp<ICameraClient>& cameraClient, int cameraId);
-    virtual void        removeClient(const sp<ICameraClient>& cameraClient);
-    // returns plain pointer of client. Note that mClientLock should be acquired to
-    // prevent the client from destruction. The result can be NULL.
-    virtual Client*     getClientByIdUnsafe(int cameraId);
-    virtual Mutex*      getClientLockById(int cameraId);
+    virtual sp<IProCameraUser>
+                        connect(const sp<IProCameraCallbacks>& cameraCb, int cameraId);
 
-    virtual sp<Client>  getClientByRemote(const wp<IBinder>& cameraClient);
-
-    virtual status_t    dump(int fd, const Vector<String16>& args);
+    // Extra permissions checks
     virtual status_t    onTransact(uint32_t code, const Parcel& data,
                                    Parcel* reply, uint32_t flags);
-    virtual void onFirstRef();
+
+    virtual status_t    dump(int fd, const Vector<String16>& args);
+
+    /////////////////////////////////////////////////////////////////////
+    // Client functionality
+    virtual void        removeClientByRemote(const wp<IBinder>& remoteBinder);
 
     enum sound_kind {
         SOUND_SHUTTER = 0,
@@ -72,7 +78,53 @@
     void                playSound(sound_kind kind);
     void                releaseSound();
 
-    class Client : public BnCamera
+
+    /////////////////////////////////////////////////////////////////////
+    // CameraClient functionality
+
+    // returns plain pointer of client. Note that mClientLock should be acquired to
+    // prevent the client from destruction. The result can be NULL.
+    virtual Client*     getClientByIdUnsafe(int cameraId);
+    virtual Mutex*      getClientLockById(int cameraId);
+
+    class BasicClient : public virtual RefBase {
+    public:
+        virtual status_t initialize(camera_module_t *module) = 0;
+
+        virtual void          disconnect() = 0;
+
+        wp<IBinder>     getRemote() {
+            return mRemoteCallback;
+        }
+
+    protected:
+        BasicClient(const sp<CameraService>& cameraService,
+                const sp<IBinder>& remoteCallback,
+                int cameraId,
+                int cameraFacing,
+                int clientPid,
+                int servicePid);
+
+        virtual ~BasicClient();
+
+        // the instance is in the middle of destruction. When this is set,
+        // the instance should not be accessed from callback.
+        // CameraService's mClientLock should be acquired to access this.
+        // - subclasses should set this to true in their destructors.
+        bool                            mDestructionStarted;
+
+        // these are initialized in the constructor.
+        sp<CameraService>               mCameraService;  // immutable after constructor
+        int                             mCameraId;       // immutable after constructor
+        int                             mCameraFacing;   // immutable after constructor
+        pid_t                           mClientPid;
+        pid_t                           mServicePid;     // immutable after constructor
+
+        // - The app-side Binder interface to receive callbacks from us
+        wp<IBinder>                     mRemoteCallback; // immutable after constructor
+    };
+
+    class Client : public BnCamera, public BasicClient
     {
     public:
         // ICamera interface (see ICamera for details)
@@ -112,38 +164,82 @@
             return mCameraClient;
         }
 
-        virtual status_t initialize(camera_module_t *module) = 0;
-
-        virtual status_t dump(int fd, const Vector<String16>& args) = 0;
-
     protected:
         static Mutex*        getClientLockFromCookie(void* user);
         // convert client from cookie. Client lock should be acquired before getting Client.
         static Client*       getClientFromCookie(void* user);
 
-        // the instance is in the middle of destruction. When this is set,
-        // the instance should not be accessed from callback.
-        // CameraService's mClientLock should be acquired to access this.
-        bool                            mDestructionStarted;
+        // Initialized in constructor
 
-        // these are initialized in the constructor.
-        sp<CameraService>               mCameraService;  // immutable after constructor
+        // - The app-side Binder interface to receive callbacks from us
         sp<ICameraClient>               mCameraClient;
-        int                             mCameraId;       // immutable after constructor
-        int                             mCameraFacing;   // immutable after constructor
-        pid_t                           mClientPid;
-        pid_t                           mServicePid;     // immutable after constructor
+    };
+
+    class ProClient : public BnProCameraUser, public BasicClient {
+    public:
+        ProClient(const sp<CameraService>& cameraService,
+                const sp<IProCameraCallbacks>& remoteCallback,
+                int cameraId,
+                int cameraFacing,
+                int clientPid,
+                int servicePid);
+
+        virtual ~ProClient();
+
+        const sp<IProCameraCallbacks>& getRemoteCallback() {
+            return mRemoteCallback;
+        }
+
+        // BasicClient implementation
+        virtual status_t initialize(camera_module_t *module);
+
+        /***
+            IProCamera implementation
+         ***/
+
+
+        virtual status_t      connect(
+                                     const sp<IProCameraCallbacks>& callbacks);
+        virtual void          disconnect();
+
+        virtual status_t      exclusiveTryLock();
+        virtual status_t      exclusiveLock();
+        virtual status_t      exclusiveUnlock();
+
+        virtual bool          hasExclusiveLock();
+
+        // Note that the callee gets a copy of the metadata.
+        virtual int           submitRequest(camera_metadata_t* metadata,
+                                            bool streaming = false);
+        virtual status_t      cancelRequest(int requestId);
+
+        virtual status_t      requestStream(int streamId);
+        virtual status_t      cancelStream(int streamId);
+
+    protected:
+        sp<IProCameraCallbacks> mRemoteCallback;
 
     };
 
 private:
+
+    // Delay-load the Camera HAL module
+    virtual void onFirstRef();
+
+    virtual sp<BasicClient>  getClientByRemote(const wp<IBinder>& cameraClient);
+
     Mutex               mServiceLock;
     wp<Client>          mClient[MAX_CAMERAS];  // protected by mServiceLock
     Mutex               mClientLock[MAX_CAMERAS]; // prevent Client destruction inside callbacks
     int                 mNumberOfCameras;
 
+    typedef wp<ProClient> weak_pro_client_ptr;
+    Vector<weak_pro_client_ptr> mProClientList[MAX_CAMERAS];
+
     // needs to be called with mServiceLock held
     sp<Client>          findClientUnsafe(const wp<IBinder>& cameraClient, int& outIndex);
+    sp<ProClient>       findProClientUnsafe(
+                                     const wp<IBinder>& cameraCallbacksRemote);
 
     // atomics to record whether the hardware is allocated to some client.
     volatile int32_t    mBusy[MAX_CAMERAS];
@@ -161,6 +257,9 @@
 
     // IBinder::DeathRecipient implementation
     virtual void binderDied(const wp<IBinder> &who);
+
+    // Helpers
+    int                 getDeviceVersion(int cameraId, int* facing);
 };
 
 } // namespace android
diff --git a/services/camera/libcameraservice/ProCamera2Client.cpp b/services/camera/libcameraservice/ProCamera2Client.cpp
new file mode 100644
index 0000000..c264e2a
--- /dev/null
+++ b/services/camera/libcameraservice/ProCamera2Client.cpp
@@ -0,0 +1,498 @@
+/*
+ * Copyright (C) 2013 The Android Open Source Project
+ *
+ * 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_TAG "ProCamera2Client"
+#define ATRACE_TAG ATRACE_TAG_CAMERA
+//#define LOG_NDEBUG 0
+
+#include <utils/Log.h>
+#include <utils/Trace.h>
+
+#include <cutils/properties.h>
+#include <gui/Surface.h>
+#include <gui/Surface.h>
+#include "camera2/Parameters.h"
+#include "ProCamera2Client.h"
+#include "camera2/ProFrameProcessor.h"
+
+namespace android {
+using namespace camera2;
+
+static int getCallingPid() {
+    return IPCThreadState::self()->getCallingPid();
+}
+
+static int getCallingUid() {
+    return IPCThreadState::self()->getCallingUid();
+}
+
+// Interface used by CameraService
+
+ProCamera2Client::ProCamera2Client(const sp<CameraService>& cameraService,
+        const sp<IProCameraCallbacks>& remoteCallback,
+        int cameraId,
+        int cameraFacing,
+        int clientPid,
+        int servicePid):
+        ProClient(cameraService, remoteCallback,
+                cameraId, cameraFacing, clientPid, servicePid),
+        mSharedCameraCallbacks(remoteCallback)
+{
+    ATRACE_CALL();
+    ALOGI("ProCamera %d: Opened", cameraId);
+
+    mDevice = new Camera2Device(cameraId);
+
+    mExclusiveLock = false;
+}
+
+status_t ProCamera2Client::checkPid(const char* checkLocation) const {
+    int callingPid = getCallingPid();
+    if (callingPid == mClientPid) return NO_ERROR;
+
+    ALOGE("%s: attempt to use a locked camera from a different process"
+            " (old pid %d, new pid %d)", checkLocation, mClientPid, callingPid);
+    return PERMISSION_DENIED;
+}
+
+status_t ProCamera2Client::initialize(camera_module_t *module)
+{
+    ATRACE_CALL();
+    ALOGV("%s: Initializing client for camera %d", __FUNCTION__, mCameraId);
+    status_t res;
+
+    res = mDevice->initialize(module);
+    if (res != OK) {
+        ALOGE("%s: Camera %d: unable to initialize device: %s (%d)",
+                __FUNCTION__, mCameraId, strerror(-res), res);
+        return NO_INIT;
+    }
+
+    res = mDevice->setNotifyCallback(this);
+
+    String8 threadName;
+    mFrameProcessor = new ProFrameProcessor(this);
+    threadName = String8::format("PC2-%d-FrameProc",
+            mCameraId);
+    mFrameProcessor->run(threadName.string());
+
+    mFrameProcessor->registerListener(FRAME_PROCESSOR_LISTENER_MIN_ID,
+                                      FRAME_PROCESSOR_LISTENER_MAX_ID,
+                                      /*listener*/this);
+
+    return OK;
+}
+
+ProCamera2Client::~ProCamera2Client() {
+    ATRACE_CALL();
+
+    mDestructionStarted = true;
+
+    disconnect();
+
+    ALOGI("ProCamera %d: Closed", mCameraId);
+}
+
+status_t ProCamera2Client::exclusiveTryLock() {
+    ATRACE_CALL();
+    ALOGV("%s", __FUNCTION__);
+
+    Mutex::Autolock icl(mIProCameraUserLock);
+    SharedCameraCallbacks::Lock l(mSharedCameraCallbacks);
+
+    if (!mExclusiveLock) {
+        mExclusiveLock = true;
+
+        if (mRemoteCallback != NULL) {
+            mRemoteCallback->onLockStatusChanged(
+                              IProCameraCallbacks::LOCK_ACQUIRED);
+        }
+
+        ALOGV("%s: exclusive lock acquired", __FUNCTION__);
+
+        return OK;
+    }
+
+    // TODO: have a PERMISSION_DENIED case for when someone else owns the lock
+
+    // don't allow recursive locking
+    ALOGW("%s: exclusive lock already exists - recursive locking is not"
+          "allowed", __FUNCTION__);
+
+    return ALREADY_EXISTS;
+}
+
+status_t ProCamera2Client::exclusiveLock() {
+    ATRACE_CALL();
+    ALOGV("%s", __FUNCTION__);
+
+    Mutex::Autolock icl(mIProCameraUserLock);
+    SharedCameraCallbacks::Lock l(mSharedCameraCallbacks);
+
+    /**
+     * TODO: this should asynchronously 'wait' until the lock becomes available
+     * if another client already has an exclusive lock.
+     *
+     * once we have proper sharing support this will need to do
+     * more than just return immediately
+     */
+    if (!mExclusiveLock) {
+        mExclusiveLock = true;
+
+        if (mRemoteCallback != NULL) {
+            mRemoteCallback->onLockStatusChanged(IProCameraCallbacks::LOCK_ACQUIRED);
+        }
+
+        ALOGV("%s: exclusive lock acquired", __FUNCTION__);
+
+        return OK;
+    }
+
+    // don't allow recursive locking
+    ALOGW("%s: exclusive lock already exists - recursive locking is not allowed"
+                                                                , __FUNCTION__);
+    return ALREADY_EXISTS;
+}
+
+status_t ProCamera2Client::exclusiveUnlock() {
+    ATRACE_CALL();
+    ALOGV("%s", __FUNCTION__);
+
+    Mutex::Autolock icl(mIProCameraUserLock);
+    SharedCameraCallbacks::Lock l(mSharedCameraCallbacks);
+
+    // don't allow unlocking if we have no lock
+    if (!mExclusiveLock) {
+        ALOGW("%s: cannot unlock, no lock was held in the first place",
+              __FUNCTION__);
+        return BAD_VALUE;
+    }
+
+    mExclusiveLock = false;
+    if (mRemoteCallback != NULL ) {
+        mRemoteCallback->onLockStatusChanged(
+                                       IProCameraCallbacks::LOCK_RELEASED);
+    }
+    ALOGV("%s: exclusive lock released", __FUNCTION__);
+
+    return OK;
+}
+
+bool ProCamera2Client::hasExclusiveLock() {
+    return mExclusiveLock;
+}
+
+status_t ProCamera2Client::submitRequest(camera_metadata_t* request,
+                                         bool streaming) {
+    ATRACE_CALL();
+    ALOGV("%s", __FUNCTION__);
+
+    Mutex::Autolock icl(mIProCameraUserLock);
+    if (!mExclusiveLock) {
+        return PERMISSION_DENIED;
+    }
+
+    CameraMetadata metadata(request);
+
+    if (streaming) {
+        return mDevice->setStreamingRequest(metadata);
+    } else {
+        return mDevice->capture(metadata);
+    }
+
+    // unreachable. thx gcc for a useless warning
+    return OK;
+}
+
+status_t ProCamera2Client::cancelRequest(int requestId) {
+    ATRACE_CALL();
+    ALOGV("%s", __FUNCTION__);
+
+    Mutex::Autolock icl(mIProCameraUserLock);
+    if (!mExclusiveLock) {
+        return PERMISSION_DENIED;
+    }
+
+    ALOGE("%s: not fully implemented yet", __FUNCTION__);
+    return INVALID_OPERATION;
+}
+
+status_t ProCamera2Client::requestStream(int streamId) {
+    ALOGE("%s: not implemented yet", __FUNCTION__);
+
+    return INVALID_OPERATION;
+}
+
+status_t ProCamera2Client::cancelStream(int streamId) {
+    ATRACE_CALL();
+    ALOGV("%s (streamId = 0x%x)", __FUNCTION__, streamId);
+
+    status_t res;
+    if ( (res = checkPid(__FUNCTION__) ) != OK) return res;
+
+    Mutex::Autolock icl(mIProCameraUserLock);
+
+    mDevice->clearStreamingRequest();
+
+    status_t code;
+    if ((code = mDevice->waitUntilDrained()) != OK) {
+        ALOGE("%s: waitUntilDrained failed with code 0x%x", __FUNCTION__, code);
+    }
+
+    return mDevice->deleteStream(streamId);
+}
+
+status_t ProCamera2Client::createStream(int width, int height, int format,
+                      const sp<IGraphicBufferProducer>& bufferProducer,
+                      /*out*/
+                      int* streamId)
+{
+    if (streamId) {
+        *streamId = -1;
+    }
+
+    ATRACE_CALL();
+    ALOGV("%s (w = %d, h = %d, f = 0x%x)", __FUNCTION__, width, height, format);
+
+    status_t res;
+    if ( (res = checkPid(__FUNCTION__) ) != OK) return res;
+
+    Mutex::Autolock icl(mIProCameraUserLock);
+
+    sp<IBinder> binder;
+    sp<ANativeWindow> window;
+    if (bufferProducer != 0) {
+        binder = bufferProducer->asBinder();
+        window = new Surface(bufferProducer);
+    }
+
+    return mDevice->createStream(window, width, height, format, /*size*/1,
+                                 streamId);
+}
+
+// Create a request object from a template.
+// -- Caller owns the newly allocated metadata
+status_t ProCamera2Client::createDefaultRequest(int templateId,
+                             /*out*/
+                              camera_metadata** request)
+{
+    ATRACE_CALL();
+    ALOGV("%s (templateId = 0x%x)", __FUNCTION__, templateId);
+
+    if (request) {
+        *request = NULL;
+    }
+
+    status_t res;
+    if ( (res = checkPid(__FUNCTION__) ) != OK) return res;
+
+    Mutex::Autolock icl(mIProCameraUserLock);
+
+    CameraMetadata metadata;
+    if ( (res = mDevice->createDefaultRequest(templateId, &metadata) ) == OK) {
+        *request = metadata.release();
+    }
+
+    return res;
+}
+
+status_t ProCamera2Client::getCameraInfo(int cameraId,
+                                         /*out*/
+                                         camera_metadata** info)
+{
+    if (cameraId != mCameraId) {
+        return INVALID_OPERATION;
+    }
+
+    CameraMetadata deviceInfo = mDevice->info();
+    *info = deviceInfo.release();
+
+    return OK;
+}
+
+status_t ProCamera2Client::dump(int fd, const Vector<String16>& args) {
+    String8 result;
+    result.appendFormat("ProCamera2Client[%d] (%p) PID: %d, dump:\n",
+            mCameraId,
+            getRemoteCallback()->asBinder().get(),
+            mClientPid);
+    result.append("  State: ");
+
+    // TODO: print dynamic/request section from most recent requests
+    mFrameProcessor->dump(fd, args);
+
+#define CASE_APPEND_ENUM(x) case x: result.append(#x "\n"); break;
+
+    result = "  Device dump:\n";
+    write(fd, result.string(), result.size());
+
+    status_t res = mDevice->dump(fd, args);
+    if (res != OK) {
+        result = String8::format("   Error dumping device: %s (%d)",
+                strerror(-res), res);
+        write(fd, result.string(), result.size());
+    }
+
+#undef CASE_APPEND_ENUM
+    return NO_ERROR;
+}
+
+// IProCameraUser interface
+
+void ProCamera2Client::disconnect() {
+    ATRACE_CALL();
+    Mutex::Autolock icl(mIProCameraUserLock);
+    status_t res;
+
+    // Allow both client and the media server to disconnect at all times
+    int callingPid = getCallingPid();
+    if (callingPid != mClientPid && callingPid != mServicePid) return;
+
+    if (mDevice == 0) return;
+
+    ALOGV("Camera %d: Shutting down", mCameraId);
+    mFrameProcessor->removeListener(FRAME_PROCESSOR_LISTENER_MIN_ID,
+                                    FRAME_PROCESSOR_LISTENER_MAX_ID,
+                                    /*listener*/this);
+    mFrameProcessor->requestExit();
+    ALOGV("Camera %d: Waiting for threads", mCameraId);
+    mFrameProcessor->join();
+    ALOGV("Camera %d: Disconnecting device", mCameraId);
+
+    mDevice->disconnect();
+
+    mDevice.clear();
+
+    ProClient::disconnect();
+}
+
+status_t ProCamera2Client::connect(const sp<IProCameraCallbacks>& client) {
+    ATRACE_CALL();
+    ALOGV("%s: E", __FUNCTION__);
+    Mutex::Autolock icl(mIProCameraUserLock);
+
+    if (mClientPid != 0 && getCallingPid() != mClientPid) {
+        ALOGE("%s: Camera %d: Connection attempt from pid %d; "
+                "current locked to pid %d", __FUNCTION__,
+                mCameraId, getCallingPid(), mClientPid);
+        return BAD_VALUE;
+    }
+
+    mClientPid = getCallingPid();
+
+    mRemoteCallback = client;
+    mSharedCameraCallbacks = client;
+
+    return OK;
+}
+
+/** Device-related methods */
+
+void ProCamera2Client::notifyError(int errorCode, int arg1, int arg2) {
+    ALOGE("Error condition %d reported by HAL, arguments %d, %d", errorCode,
+                                                                    arg1, arg2);
+}
+
+void ProCamera2Client::notifyShutter(int frameNumber, nsecs_t timestamp) {
+    ALOGV("%s: Shutter notification for frame %d at time %lld", __FUNCTION__,
+            frameNumber, timestamp);
+}
+
+void ProCamera2Client::notifyAutoFocus(uint8_t newState, int triggerId) {
+    ALOGV("%s: Autofocus state now %d, last trigger %d",
+            __FUNCTION__, newState, triggerId);
+
+    SharedCameraCallbacks::Lock l(mSharedCameraCallbacks);
+    if (l.mRemoteCallback != 0) {
+        l.mRemoteCallback->notifyCallback(CAMERA_MSG_FOCUS_MOVE,
+                1, 0);
+    }
+    if (l.mRemoteCallback != 0) {
+        l.mRemoteCallback->notifyCallback(CAMERA_MSG_FOCUS,
+                1, 0);
+    }
+}
+
+void ProCamera2Client::notifyAutoExposure(uint8_t newState, int triggerId) {
+    ALOGV("%s: Autoexposure state now %d, last trigger %d",
+            __FUNCTION__, newState, triggerId);
+}
+
+void ProCamera2Client::notifyAutoWhitebalance(uint8_t newState, int triggerId) {
+    ALOGV("%s: Auto-whitebalance state now %d, last trigger %d",
+            __FUNCTION__, newState, triggerId);
+}
+
+int ProCamera2Client::getCameraId() const {
+    return mCameraId;
+}
+
+const sp<Camera2Device>& ProCamera2Client::getCameraDevice() {
+    return mDevice;
+}
+
+const sp<CameraService>& ProCamera2Client::getCameraService() {
+    return mCameraService;
+}
+
+ProCamera2Client::SharedCameraCallbacks::Lock::Lock(
+                                                 SharedCameraCallbacks &client):
+        mRemoteCallback(client.mRemoteCallback),
+        mSharedClient(client) {
+    mSharedClient.mRemoteCallbackLock.lock();
+}
+
+ProCamera2Client::SharedCameraCallbacks::Lock::~Lock() {
+    mSharedClient.mRemoteCallbackLock.unlock();
+}
+
+ProCamera2Client::SharedCameraCallbacks::SharedCameraCallbacks
+                                         (const sp<IProCameraCallbacks>&client):
+        mRemoteCallback(client) {
+}
+
+ProCamera2Client::SharedCameraCallbacks&
+                             ProCamera2Client::SharedCameraCallbacks::operator=(
+        const sp<IProCameraCallbacks>&client) {
+    Mutex::Autolock l(mRemoteCallbackLock);
+    mRemoteCallback = client;
+    return *this;
+}
+
+void ProCamera2Client::SharedCameraCallbacks::clear() {
+    Mutex::Autolock l(mRemoteCallbackLock);
+    mRemoteCallback.clear();
+}
+
+void ProCamera2Client::onFrameAvailable(int32_t frameId,
+                                        const CameraMetadata& frame) {
+    ATRACE_CALL();
+    ALOGV("%s", __FUNCTION__);
+
+    Mutex::Autolock icl(mIProCameraUserLock);
+    SharedCameraCallbacks::Lock l(mSharedCameraCallbacks);
+
+    if (mRemoteCallback != NULL) {
+        CameraMetadata tmp(frame);
+        camera_metadata_t* meta = tmp.release();
+        ALOGV("%s: meta = %p ", __FUNCTION__, meta);
+        mRemoteCallback->onResultReceived(frameId, meta);
+        tmp.acquire(meta);
+    }
+
+}
+
+} // namespace android
diff --git a/services/camera/libcameraservice/ProCamera2Client.h b/services/camera/libcameraservice/ProCamera2Client.h
new file mode 100644
index 0000000..cd0a2ae
--- /dev/null
+++ b/services/camera/libcameraservice/ProCamera2Client.h
@@ -0,0 +1,172 @@
+/*
+ * Copyright (C) 2013 The Android Open Source Project
+ *
+ * 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.
+ */
+
+#ifndef ANDROID_SERVERS_CAMERA_PROCAMERA2CLIENT_H
+#define ANDROID_SERVERS_CAMERA_PROCAMERA2CLIENT_H
+
+#include "Camera2Device.h"
+#include "CameraService.h"
+#include "camera2/ProFrameProcessor.h"
+
+namespace android {
+
+class IMemory;
+/**
+ * Implements the binder IProCameraUser API,
+ * meant for HAL2-level private API access.
+ */
+class ProCamera2Client :
+        public CameraService::ProClient,
+        public Camera2Device::NotificationListener,
+        public camera2::ProFrameProcessor::FilteredListener
+{
+public:
+    /**
+     * IProCameraUser interface (see IProCameraUser for details)
+     */
+    virtual status_t      connect(const sp<IProCameraCallbacks>& callbacks);
+    virtual void          disconnect();
+
+    virtual status_t      exclusiveTryLock();
+    virtual status_t      exclusiveLock();
+    virtual status_t      exclusiveUnlock();
+
+    virtual bool          hasExclusiveLock();
+
+    // Note that the callee gets a copy of the metadata.
+    virtual int           submitRequest(camera_metadata_t* metadata,
+                                        bool streaming = false);
+    virtual status_t      cancelRequest(int requestId);
+
+    virtual status_t      requestStream(int streamId);
+    virtual status_t      cancelStream(int streamId);
+
+    virtual status_t      createStream(int width, int height, int format,
+                                      const sp<IGraphicBufferProducer>& bufferProducer,
+                                      /*out*/
+                                      int* streamId);
+
+    // Create a request object from a template.
+    // -- Caller owns the newly allocated metadata
+    virtual status_t      createDefaultRequest(int templateId,
+                                               /*out*/
+                                               camera_metadata** request);
+
+    // Get the static metadata for the camera
+    // -- Caller owns the newly allocated metadata
+    virtual status_t      getCameraInfo(int cameraId,
+                                        /*out*/
+                                        camera_metadata** info);
+
+    /**
+     * Interface used by CameraService
+     */
+
+    ProCamera2Client(const sp<CameraService>& cameraService,
+            const sp<IProCameraCallbacks>& remoteCallback,
+            int cameraId,
+            int cameraFacing,
+            int clientPid,
+            int servicePid);
+    virtual ~ProCamera2Client();
+
+    status_t initialize(camera_module_t *module);
+
+    virtual status_t dump(int fd, const Vector<String16>& args);
+
+    /**
+     * Interface used by Camera2Device
+     */
+
+    virtual void notifyError(int errorCode, int arg1, int arg2);
+    virtual void notifyShutter(int frameNumber, nsecs_t timestamp);
+    virtual void notifyAutoFocus(uint8_t newState, int triggerId);
+    virtual void notifyAutoExposure(uint8_t newState, int triggerId);
+    virtual void notifyAutoWhitebalance(uint8_t newState, int triggerId);
+
+
+    int getCameraId() const;
+    const sp<Camera2Device>& getCameraDevice();
+    const sp<CameraService>& getCameraService();
+
+    /**
+     * Interface used by independent components of ProCamera2Client.
+     */
+
+    // Simple class to ensure that access to IProCameraCallbacks is serialized
+    // by requiring mRemoteCallbackLock to be locked before access to
+    // mCameraClient is possible.
+    class SharedCameraCallbacks {
+      public:
+        class Lock {
+          public:
+            Lock(SharedCameraCallbacks &client);
+            ~Lock();
+            sp<IProCameraCallbacks> &mRemoteCallback;
+          private:
+            SharedCameraCallbacks &mSharedClient;
+        };
+        SharedCameraCallbacks(const sp<IProCameraCallbacks>& client);
+        SharedCameraCallbacks& operator=(const sp<IProCameraCallbacks>& client);
+        void clear();
+      private:
+        sp<IProCameraCallbacks> mRemoteCallback;
+        mutable Mutex mRemoteCallbackLock;
+    } mSharedCameraCallbacks;
+
+protected:
+    /** FilteredListener implementation **/
+    virtual void onFrameAvailable(int32_t frameId, const CameraMetadata& frame);
+
+private:
+    /** IProCameraUser interface-related private members */
+
+    // Mutex that must be locked by methods implementing the IProCameraUser
+    // interface. Ensures serialization between incoming IProCameraUser calls.
+    // All methods below that append 'L' to the name assume that
+    // mIProCameraUserLock is locked when they're called
+    mutable Mutex mIProCameraUserLock;
+
+    // Used with stream IDs
+    static const int NO_STREAM = -1;
+
+    /* Preview/Recording related members */
+
+    sp<IBinder> mPreviewSurface;
+
+    /** Preview callback related members */
+    sp<camera2::ProFrameProcessor> mFrameProcessor;
+    static const int32_t FRAME_PROCESSOR_LISTENER_MIN_ID = 0;
+    static const int32_t FRAME_PROCESSOR_LISTENER_MAX_ID = 0x7fffffffL;
+
+    /** Camera2Device instance wrapping HAL2 entry */
+
+    sp<Camera2Device> mDevice;
+
+    /** Utility members */
+
+    // Verify that caller is the owner of the camera
+    status_t checkPid(const char *checkLocation) const;
+
+    // Whether or not we have an exclusive lock on the device
+    // - if no we can't modify the request queue.
+    // note that creating/deleting streams we own is still OK
+    bool mExclusiveLock;
+};
+
+}; // namespace android
+
+#endif
diff --git a/services/camera/libcameraservice/camera2/BurstCapture.h b/services/camera/libcameraservice/camera2/BurstCapture.h
index dfc45eb..a2cc893 100644
--- a/services/camera/libcameraservice/camera2/BurstCapture.h
+++ b/services/camera/libcameraservice/camera2/BurstCapture.h
@@ -17,7 +17,7 @@
 #ifndef ANDROID_SERVERS_CAMERA_BURST_CAPTURE_H
 #define ANDROID_SERVERS_CAMERA_BURST_CAPTURE_H
 
-#include "camera2/CameraMetadata.h"
+#include "camera/CameraMetadata.h"
 #include <binder/MemoryBase.h>
 #include <binder/MemoryHeapBase.h>
 #include <gui/CpuConsumer.h>
diff --git a/services/camera/libcameraservice/camera2/CallbackProcessor.h b/services/camera/libcameraservice/camera2/CallbackProcessor.h
index c2a1372..e68bb75 100644
--- a/services/camera/libcameraservice/camera2/CallbackProcessor.h
+++ b/services/camera/libcameraservice/camera2/CallbackProcessor.h
@@ -24,7 +24,7 @@
 #include <utils/Condition.h>
 #include <gui/CpuConsumer.h>
 #include "Parameters.h"
-#include "CameraMetadata.h"
+#include "camera/CameraMetadata.h"
 #include "Camera2Heap.h"
 
 namespace android {
diff --git a/services/camera/libcameraservice/camera2/CaptureSequencer.h b/services/camera/libcameraservice/camera2/CaptureSequencer.h
index c42df05..7db8007 100644
--- a/services/camera/libcameraservice/camera2/CaptureSequencer.h
+++ b/services/camera/libcameraservice/camera2/CaptureSequencer.h
@@ -23,7 +23,7 @@
 #include <utils/Vector.h>
 #include <utils/Mutex.h>
 #include <utils/Condition.h>
-#include "CameraMetadata.h"
+#include "camera/CameraMetadata.h"
 #include "Parameters.h"
 #include "FrameProcessor.h"
 
diff --git a/services/camera/libcameraservice/camera2/FrameProcessor.h b/services/camera/libcameraservice/camera2/FrameProcessor.h
index 3bd4e25..66e3cda 100644
--- a/services/camera/libcameraservice/camera2/FrameProcessor.h
+++ b/services/camera/libcameraservice/camera2/FrameProcessor.h
@@ -22,7 +22,7 @@
 #include <utils/Vector.h>
 #include <utils/KeyedVector.h>
 #include <utils/List.h>
-#include "CameraMetadata.h"
+#include "camera/CameraMetadata.h"
 
 struct camera_frame_metadata;
 
diff --git a/services/camera/libcameraservice/camera2/JpegProcessor.h b/services/camera/libcameraservice/camera2/JpegProcessor.h
index 836bd02..2283f28 100644
--- a/services/camera/libcameraservice/camera2/JpegProcessor.h
+++ b/services/camera/libcameraservice/camera2/JpegProcessor.h
@@ -24,7 +24,7 @@
 #include <utils/Condition.h>
 #include <gui/CpuConsumer.h>
 #include "Parameters.h"
-#include "CameraMetadata.h"
+#include "camera/CameraMetadata.h"
 
 namespace android {
 
diff --git a/services/camera/libcameraservice/camera2/Parameters.h b/services/camera/libcameraservice/camera2/Parameters.h
index 9f5f03b..fe3ec1d 100644
--- a/services/camera/libcameraservice/camera2/Parameters.h
+++ b/services/camera/libcameraservice/camera2/Parameters.h
@@ -25,8 +25,7 @@
 #include <utils/Vector.h>
 #include <utils/KeyedVector.h>
 #include <camera/CameraParameters.h>
-
-#include "CameraMetadata.h"
+#include <camera/CameraMetadata.h>
 
 namespace android {
 namespace camera2 {
diff --git a/services/camera/libcameraservice/camera2/ProFrameProcessor.cpp b/services/camera/libcameraservice/camera2/ProFrameProcessor.cpp
new file mode 100644
index 0000000..8d4933c
--- /dev/null
+++ b/services/camera/libcameraservice/camera2/ProFrameProcessor.cpp
@@ -0,0 +1,168 @@
+/*
+ * Copyright (C) 2013 The Android Open Source Project
+ *
+ * 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_TAG "Camera2-ProFrameProcessor"
+#define ATRACE_TAG ATRACE_TAG_CAMERA
+//#define LOG_NDEBUG 0
+
+#include <utils/Log.h>
+#include <utils/Trace.h>
+
+#include "ProFrameProcessor.h"
+#include "../Camera2Device.h"
+#include "../ProCamera2Client.h"
+
+namespace android {
+namespace camera2 {
+
+ProFrameProcessor::ProFrameProcessor(wp<ProCamera2Client> client):
+        Thread(false), mClient(client) {
+}
+
+ProFrameProcessor::~ProFrameProcessor() {
+    ALOGV("%s: Exit", __FUNCTION__);
+}
+
+status_t ProFrameProcessor::registerListener(int32_t minId,
+        int32_t maxId, wp<FilteredListener> listener) {
+    Mutex::Autolock l(mInputMutex);
+    ALOGV("%s: Registering listener for frame id range %d - %d",
+            __FUNCTION__, minId, maxId);
+    RangeListener rListener = { minId, maxId, listener };
+    mRangeListeners.push_back(rListener);
+    return OK;
+}
+
+status_t ProFrameProcessor::removeListener(int32_t minId,
+        int32_t maxId, wp<FilteredListener> listener) {
+    Mutex::Autolock l(mInputMutex);
+    List<RangeListener>::iterator item = mRangeListeners.begin();
+    while (item != mRangeListeners.end()) {
+        if (item->minId == minId &&
+                item->maxId == maxId &&
+                item->listener == listener) {
+            item = mRangeListeners.erase(item);
+        } else {
+            item++;
+        }
+    }
+    return OK;
+}
+
+void ProFrameProcessor::dump(int fd, const Vector<String16>& args) {
+    String8 result("    Latest received frame:\n");
+    write(fd, result.string(), result.size());
+    mLastFrame.dump(fd, 2, 6);
+}
+
+bool ProFrameProcessor::threadLoop() {
+    status_t res;
+
+    sp<Camera2Device> device;
+    {
+        sp<ProCamera2Client> client = mClient.promote();
+        if (client == 0) return false;
+        device = client->getCameraDevice();
+        if (device == 0) return false;
+    }
+
+    res = device->waitForNextFrame(kWaitDuration);
+    if (res == OK) {
+        sp<ProCamera2Client> client = mClient.promote();
+        if (client == 0) return false;
+        processNewFrames(client);
+    } else if (res != TIMED_OUT) {
+        ALOGE("ProCamera2Client::ProFrameProcessor: Error waiting for new "
+                "frames: %s (%d)", strerror(-res), res);
+    }
+
+    return true;
+}
+
+void ProFrameProcessor::processNewFrames(sp<ProCamera2Client> &client) {
+    status_t res;
+    ATRACE_CALL();
+    CameraMetadata frame;
+    while ( (res = client->getCameraDevice()->getNextFrame(&frame)) == OK) {
+        camera_metadata_entry_t entry;
+
+        entry = frame.find(ANDROID_REQUEST_FRAME_COUNT);
+        if (entry.count == 0) {
+            ALOGE("%s: Camera %d: Error reading frame number",
+                    __FUNCTION__, client->getCameraId());
+            break;
+        }
+        ATRACE_INT("cam2_frame", entry.data.i32[0]);
+
+        res = processListeners(frame, client);
+        if (res != OK) break;
+
+        if (!frame.isEmpty()) {
+            mLastFrame.acquire(frame);
+        }
+    }
+    if (res != NOT_ENOUGH_DATA) {
+        ALOGE("%s: Camera %d: Error getting next frame: %s (%d)",
+                __FUNCTION__, client->getCameraId(), strerror(-res), res);
+        return;
+    }
+
+    return;
+}
+
+status_t ProFrameProcessor::processListeners(const CameraMetadata &frame,
+        sp<ProCamera2Client> &client) {
+    status_t res;
+    ATRACE_CALL();
+    camera_metadata_ro_entry_t entry;
+
+    entry = frame.find(ANDROID_REQUEST_ID);
+    if (entry.count == 0) {
+        ALOGE("%s: Camera %d: Error reading frame id",
+                __FUNCTION__, client->getCameraId());
+        return BAD_VALUE;
+    }
+    int32_t frameId = entry.data.i32[0];
+
+    List<sp<FilteredListener> > listeners;
+    {
+        Mutex::Autolock l(mInputMutex);
+
+        List<RangeListener>::iterator item = mRangeListeners.begin();
+        while (item != mRangeListeners.end()) {
+            if (frameId >= item->minId &&
+                    frameId < item->maxId) {
+                sp<FilteredListener> listener = item->listener.promote();
+                if (listener == 0) {
+                    item = mRangeListeners.erase(item);
+                    continue;
+                } else {
+                    listeners.push_back(listener);
+                }
+            }
+            item++;
+        }
+    }
+    ALOGV("Got %d range listeners out of %d", listeners.size(), mRangeListeners.size());
+    List<sp<FilteredListener> >::iterator item = listeners.begin();
+    for (; item != listeners.end(); item++) {
+        (*item)->onFrameAvailable(frameId, frame);
+    }
+    return OK;
+}
+
+}; // namespace camera2
+}; // namespace android
diff --git a/services/camera/libcameraservice/camera2/ProFrameProcessor.h b/services/camera/libcameraservice/camera2/ProFrameProcessor.h
new file mode 100644
index 0000000..e4094a6
--- /dev/null
+++ b/services/camera/libcameraservice/camera2/ProFrameProcessor.h
@@ -0,0 +1,81 @@
+/*
+ * Copyright (C) 2013 The Android Open Source Project
+ *
+ * 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.
+ */
+
+#ifndef ANDROID_SERVERS_CAMERA_CAMERA2_PROFRAMEPROCESSOR_H
+#define ANDROID_SERVERS_CAMERA_CAMERA2_PROFRAMEPROCESSOR_H
+
+#include <utils/Thread.h>
+#include <utils/String16.h>
+#include <utils/Vector.h>
+#include <utils/KeyedVector.h>
+#include <utils/List.h>
+#include <camera/CameraMetadata.h>
+
+struct camera_frame_metadata;
+
+namespace android {
+
+class ProCamera2Client;
+
+namespace camera2 {
+
+/* Output frame metadata processing thread.  This thread waits for new
+ * frames from the device, and analyzes them as necessary.
+ */
+class ProFrameProcessor: public Thread {
+  public:
+    ProFrameProcessor(wp<ProCamera2Client> client);
+    ~ProFrameProcessor();
+
+    struct FilteredListener: virtual public RefBase {
+        virtual void onFrameAvailable(int32_t frameId,
+                const CameraMetadata &frame) = 0;
+    };
+
+    // Register a listener for a range of IDs [minId, maxId). Multiple listeners
+    // can be listening to the same range
+    status_t registerListener(int32_t minId, int32_t maxId, wp<FilteredListener> listener);
+    status_t removeListener(int32_t minId, int32_t maxId, wp<FilteredListener> listener);
+
+    void dump(int fd, const Vector<String16>& args);
+  private:
+    static const nsecs_t kWaitDuration = 10000000; // 10 ms
+    wp<ProCamera2Client> mClient;
+
+    virtual bool threadLoop();
+
+    Mutex mInputMutex;
+
+    struct RangeListener {
+        int32_t minId;
+        int32_t maxId;
+        wp<FilteredListener> listener;
+    };
+    List<RangeListener> mRangeListeners;
+
+    void processNewFrames(sp<ProCamera2Client> &client);
+
+    status_t processListeners(const CameraMetadata &frame,
+            sp<ProCamera2Client> &client);
+
+    CameraMetadata mLastFrame;
+};
+
+
+}; //namespace camera2
+}; //namespace android
+
+#endif
diff --git a/services/camera/libcameraservice/camera2/StreamingProcessor.h b/services/camera/libcameraservice/camera2/StreamingProcessor.h
index 96b100f..e5732ad 100644
--- a/services/camera/libcameraservice/camera2/StreamingProcessor.h
+++ b/services/camera/libcameraservice/camera2/StreamingProcessor.h
@@ -22,7 +22,7 @@
 #include <gui/BufferItemConsumer.h>
 
 #include "Parameters.h"
-#include "CameraMetadata.h"
+#include "camera/CameraMetadata.h"
 
 namespace android {
 
diff --git a/services/camera/libcameraservice/camera2/ZslProcessor.h b/services/camera/libcameraservice/camera2/ZslProcessor.h
index c80e7f4..ec16eef 100644
--- a/services/camera/libcameraservice/camera2/ZslProcessor.h
+++ b/services/camera/libcameraservice/camera2/ZslProcessor.h
@@ -25,7 +25,7 @@
 #include <gui/BufferItemConsumer.h>
 #include "Parameters.h"
 #include "FrameProcessor.h"
-#include "CameraMetadata.h"
+#include "camera/CameraMetadata.h"
 #include "Camera2Heap.h"
 #include "../Camera2Device.h"