Merge changes I25423a2b,I24680f1a into jb-mr2-dev

* changes:
  ProCamera: Add CpuConsumer asynchronous mode support
  Camera: Drop ProCamera connections when a Camera connection happens
diff --git a/camera/Android.mk b/camera/Android.mk
index 3f30079..e33fb50 100644
--- a/camera/Android.mk
+++ b/camera/Android.mk
@@ -11,6 +11,7 @@
 	ICamera.cpp \
 	ICameraClient.cpp \
 	ICameraService.cpp \
+	ICameraServiceListener.cpp \
 	ICameraRecordingProxy.cpp \
 	ICameraRecordingProxyListener.cpp \
 	IProCameraUser.cpp \
diff --git a/camera/CameraBase.cpp b/camera/CameraBase.cpp
index 9b0e6bf..29096da 100644
--- a/camera/CameraBase.cpp
+++ b/camera/CameraBase.cpp
@@ -231,6 +231,22 @@
     return cs->getCameraInfo(cameraId, cameraInfo);
 }
 
+template <typename TCam, typename TCamTraits>
+status_t CameraBase<TCam, TCamTraits>::addServiceListener(
+                            const sp<ICameraServiceListener>& listener) {
+    const sp<ICameraService>& cs = getCameraService();
+    if (cs == 0) return UNKNOWN_ERROR;
+    return cs->addListener(listener);
+}
+
+template <typename TCam, typename TCamTraits>
+status_t CameraBase<TCam, TCamTraits>::removeServiceListener(
+                            const sp<ICameraServiceListener>& listener) {
+    const sp<ICameraService>& cs = getCameraService();
+    if (cs == 0) return UNKNOWN_ERROR;
+    return cs->removeListener(listener);
+}
+
 template class CameraBase<ProCamera>;
 template class CameraBase<Camera>;
 
diff --git a/camera/ICameraService.cpp b/camera/ICameraService.cpp
index b54d63f..134f7f0 100644
--- a/camera/ICameraService.cpp
+++ b/camera/ICameraService.cpp
@@ -23,6 +23,7 @@
 #include <binder/IServiceManager.h>
 
 #include <camera/ICameraService.h>
+#include <camera/ICameraServiceListener.h>
 #include <camera/IProCameraUser.h>
 #include <camera/IProCameraCallbacks.h>
 #include <camera/ICamera.h>
@@ -86,6 +87,24 @@
         remote()->transact(BnCameraService::CONNECT_PRO, data, &reply);
         return interface_cast<IProCameraUser>(reply.readStrongBinder());
     }
+
+    virtual status_t addListener(const sp<ICameraServiceListener>& listener)
+    {
+        Parcel data, reply;
+        data.writeInterfaceToken(ICameraService::getInterfaceDescriptor());
+        data.writeStrongBinder(listener->asBinder());
+        remote()->transact(BnCameraService::ADD_LISTENER, data, &reply);
+        return reply.readInt32();
+    }
+
+    virtual status_t removeListener(const sp<ICameraServiceListener>& listener)
+    {
+        Parcel data, reply;
+        data.writeInterfaceToken(ICameraService::getInterfaceDescriptor());
+        data.writeStrongBinder(listener->asBinder());
+        remote()->transact(BnCameraService::REMOVE_LISTENER, data, &reply);
+        return reply.readInt32();
+    }
 };
 
 IMPLEMENT_META_INTERFACE(CameraService, "android.hardware.ICameraService");
@@ -134,6 +153,20 @@
             reply->writeStrongBinder(camera->asBinder());
             return NO_ERROR;
         } break;
+        case ADD_LISTENER: {
+            CHECK_INTERFACE(ICameraService, data, reply);
+            sp<ICameraServiceListener> listener =
+                interface_cast<ICameraServiceListener>(data.readStrongBinder());
+            reply->writeInt32(addListener(listener));
+            return NO_ERROR;
+        } break;
+        case REMOVE_LISTENER: {
+            CHECK_INTERFACE(ICameraService, data, reply);
+            sp<ICameraServiceListener> listener =
+                interface_cast<ICameraServiceListener>(data.readStrongBinder());
+            reply->writeInt32(removeListener(listener));
+            return NO_ERROR;
+        } break;
         default:
             return BBinder::onTransact(code, data, reply, flags);
     }
diff --git a/camera/ICameraServiceListener.cpp b/camera/ICameraServiceListener.cpp
new file mode 100644
index 0000000..640ee35
--- /dev/null
+++ b/camera/ICameraServiceListener.cpp
@@ -0,0 +1,86 @@
+/*
+**
+** 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.
+*/
+
+#include <stdint.h>
+#include <sys/types.h>
+
+#include <binder/Parcel.h>
+#include <binder/IPCThreadState.h>
+#include <binder/IServiceManager.h>
+
+#include <camera/ICameraServiceListener.h>
+
+namespace android {
+
+namespace {
+    enum {
+        STATUS_CHANGED = IBinder::FIRST_CALL_TRANSACTION,
+    };
+}; // namespace anonymous
+
+class BpCameraServiceListener: public BpInterface<ICameraServiceListener>
+{
+
+public:
+    BpCameraServiceListener(const sp<IBinder>& impl)
+        : BpInterface<ICameraServiceListener>(impl)
+    {
+    }
+
+    virtual void onStatusChanged(Status status, int32_t cameraId)
+    {
+        Parcel data, reply;
+        data.writeInterfaceToken(
+                              ICameraServiceListener::getInterfaceDescriptor());
+
+        data.writeInt32(static_cast<int32_t>(status));
+        data.writeInt32(cameraId);
+
+        remote()->transact(STATUS_CHANGED,
+                           data,
+                           &reply,
+                           IBinder::FLAG_ONEWAY);
+    }
+};
+
+IMPLEMENT_META_INTERFACE(CameraServiceListener,
+                         "android.hardware.ICameraServiceListener");
+
+// ----------------------------------------------------------------------
+
+status_t BnCameraServiceListener::onTransact(
+    uint32_t code, const Parcel& data, Parcel* reply, uint32_t flags)
+{
+    switch(code) {
+        case STATUS_CHANGED: {
+            CHECK_INTERFACE(ICameraServiceListener, data, reply);
+
+            Status status = static_cast<Status>(data.readInt32());
+            int32_t cameraId = data.readInt32();
+
+            onStatusChanged(status, cameraId);
+
+            return NO_ERROR;
+        } break;
+        default:
+            return BBinder::onTransact(code, data, reply, flags);
+    }
+}
+
+// ----------------------------------------------------------------------------
+
+}; // namespace android
diff --git a/camera/ProCamera.cpp b/camera/ProCamera.cpp
index 13ba07c..3cfabf6 100644
--- a/camera/ProCamera.cpp
+++ b/camera/ProCamera.cpp
@@ -241,6 +241,17 @@
                                     int heapCount,
                                     /*out*/
                                     sp<CpuConsumer>* cpuConsumer,
+                                    int* streamId) {
+    return createStreamCpu(width, height, format, heapCount,
+                           /*synchronousMode*/true,
+                           cpuConsumer, streamId);
+}
+
+status_t ProCamera::createStreamCpu(int width, int height, int format,
+                                    int heapCount,
+                                    bool synchronousMode,
+                                    /*out*/
+                                    sp<CpuConsumer>* cpuConsumer,
                                     int* streamId)
 {
     ALOGV("%s: createStreamW %dx%d (fmt=0x%x)", __FUNCTION__, width, height,
@@ -251,7 +262,7 @@
     sp <IProCameraUser> c = mCamera;
     if (c == 0) return NO_INIT;
 
-    sp<CpuConsumer> cc = new CpuConsumer(heapCount);
+    sp<CpuConsumer> cc = new CpuConsumer(heapCount, synchronousMode);
     cc->setName(String8("ProCamera::mCpuConsumer"));
 
     sp<Surface> stc = new Surface(
@@ -272,6 +283,7 @@
 
     getStreamInfo(*streamId).cpuStream = true;
     getStreamInfo(*streamId).cpuConsumer = cc;
+    getStreamInfo(*streamId).synchronousMode = synchronousMode;
     getStreamInfo(*streamId).stc = stc;
     // for lifetime management
     getStreamInfo(*streamId).frameAvailableListener = frameAvailableListener;
@@ -373,6 +385,13 @@
         return BAD_VALUE;
     }
 
+    if (!si.synchronousMode) {
+        ALOGW("%s: No need to drop frames on asynchronous streams,"
+              " as asynchronous mode only keeps 1 latest frame around.",
+              __FUNCTION__);
+        return BAD_VALUE;
+    }
+
     int numDropped = 0;
     for (int i = 0; i < count; ++i) {
         CpuConsumer::LockedBuffer buffer;
diff --git a/camera/tests/ProCameraTests.cpp b/camera/tests/ProCameraTests.cpp
index 39456af..1a8564e 100644
--- a/camera/tests/ProCameraTests.cpp
+++ b/camera/tests/ProCameraTests.cpp
@@ -33,6 +33,8 @@
 #include <hardware/camera2.h> // for CAMERA2_TEMPLATE_PREVIEW only
 #include <camera/CameraMetadata.h>
 
+#include <camera/ICameraServiceListener.h>
+
 namespace android {
 namespace camera2 {
 namespace tests {
@@ -48,9 +50,9 @@
 #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_DISPLAY_FORMAT HAL_PIXEL_FORMAT_Y8
+#define TEST_DISPLAY_WIDTH 320
+#define TEST_DISPLAY_HEIGHT 240
 
 #define TEST_CPU_FRAME_COUNT 2
 #define TEST_CPU_HEAP_COUNT 5
@@ -68,6 +70,52 @@
 
 class ProCameraTest;
 
+struct ServiceListener : public BnCameraServiceListener {
+
+    ServiceListener() :
+        mLatestStatus(STATUS_UNKNOWN),
+        mPrevStatus(STATUS_UNKNOWN)
+    {
+    }
+
+    void onStatusChanged(Status status, int32_t cameraId) {
+        dout << "On status changed: 0x" << std::hex
+             << status << " cameraId " << cameraId
+             << std::endl;
+
+        Mutex::Autolock al(mMutex);
+
+        mLatestStatus = status;
+        mCondition.broadcast();
+    }
+
+    status_t waitForStatusChange(Status& newStatus) {
+        Mutex::Autolock al(mMutex);
+
+        if (mLatestStatus != mPrevStatus) {
+            newStatus = mLatestStatus;
+            mPrevStatus = mLatestStatus;
+            return OK;
+        }
+
+        status_t stat = mCondition.waitRelative(mMutex,
+                                               TEST_LISTENER_TIMEOUT);
+
+        if (stat == OK) {
+            newStatus = mLatestStatus;
+            mPrevStatus = mLatestStatus;
+        }
+
+        return stat;
+    }
+
+    Condition mCondition;
+    Mutex mMutex;
+
+    Status mLatestStatus;
+    Status mPrevStatus;
+};
+
 enum ProEvent {
     UNKNOWN,
     ACQUIRED,
@@ -441,7 +489,6 @@
         }
         request.acquire(requestTmp);
     }
-
 };
 
 sp<Thread> ProCameraTest::mTestThread;
@@ -538,18 +585,52 @@
     }
 
     int depthStreamId = -1;
-    EXPECT_OK(mCamera->createStream(mDisplayW, mDisplayH, mDisplayFmt, surface,
-                                    &depthStreamId));
-    EXPECT_NE(-1, depthStreamId);
 
-    EXPECT_OK(mCamera->exclusiveTryLock());
+    sp<ServiceListener> listener = new ServiceListener();
+    EXPECT_OK(ProCamera::addServiceListener(listener));
 
-    uint8_t streams[] = { depthStreamId };
-    ASSERT_NO_FATAL_FAILURE(createSubmitRequestForStreams(streams, /*count*/1));
+    ServiceListener::Status currentStatus = ServiceListener::STATUS_AVAILABLE;
 
-    dout << "will sleep now for " << mDisplaySecs << std::endl;
-    sleep(mDisplaySecs);
+    dout << "Will now stream and resume infinitely..." << std::endl;
+    while (true) {
 
+        if (currentStatus == ServiceListener::STATUS_AVAILABLE) {
+
+            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));
+        }
+
+        ServiceListener::Status stat = ServiceListener::STATUS_UNKNOWN;
+
+        // TODO: maybe check for getch every once in a while?
+        while (listener->waitForStatusChange(/*out*/stat) != OK);
+
+        if (currentStatus != stat) {
+            if (stat == ServiceListener::STATUS_AVAILABLE) {
+                dout << "Reconnecting to camera" << std::endl;
+                mCamera = ProCamera::connect(CAMERA_ID);
+            } else if (stat == ServiceListener::STATUS_NOT_AVAILABLE) {
+                dout << "Disconnecting from camera" << std::endl;
+                mCamera->disconnect();
+            } else {
+                dout << "Unknown status change "
+                     << std::hex << stat << std::endl;
+            }
+
+            currentStatus = stat;
+        }
+    }
+
+    EXPECT_OK(ProCamera::removeServiceListener(listener));
     EXPECT_OK(mCamera->deleteStream(depthStreamId));
     EXPECT_OK(mCamera->exclusiveUnlock());
 }
@@ -980,7 +1061,7 @@
     EXPECT_OK(mCamera->exclusiveUnlock());
 }
 
-TEST_F(ProCameraTest, WaitForSingleStreamBufferAndDropFrames) {
+TEST_F(ProCameraTest, WaitForSingleStreamBufferAndDropFramesSync) {
     if (HasFatalFailure()) {
         return;
     }
@@ -990,7 +1071,8 @@
     int streamId = -1;
     sp<CpuConsumer> consumer;
     EXPECT_OK(mCamera->createStreamCpu(/*width*/1280, /*height*/960,
-                  TEST_FORMAT_MAIN, TEST_CPU_HEAP_COUNT, &consumer, &streamId));
+                  TEST_FORMAT_MAIN, TEST_CPU_HEAP_COUNT,
+                  /*synchronousMode*/true, &consumer, &streamId));
     EXPECT_NE(-1, streamId);
 
     EXPECT_OK(mCamera->exclusiveTryLock());
@@ -1033,6 +1115,102 @@
     EXPECT_OK(mCamera->exclusiveUnlock());
 }
 
+TEST_F(ProCameraTest, WaitForSingleStreamBufferAndDropFramesAsync) {
+    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,
+                  /*synchronousMode*/false, &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) {
+        int numFrames;
+        EXPECT_TRUE((numFrames = mCamera->waitForFrameBuffer(streamId)) > 0);
+
+        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 asynchronously received on streamId = " << streamId <<
+                ", dataPtr = " << (void*)buf.data <<
+                ", timestamp = " << buf.timestamp << std::endl;
+
+        // 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);
+
+        EXPECT_OK(consumer->unlockBuffer(buf));
+    }
+
+    // Done: clean up
+    EXPECT_OK(mCamera->deleteStream(streamId));
+    EXPECT_OK(mCamera->exclusiveUnlock());
+}
+
+
+
+//TODO: refactor into separate file
+TEST_F(ProCameraTest, ServiceListenersSubscribe) {
+
+    ASSERT_EQ(4u, sizeof(ServiceListener::Status));
+
+    sp<ServiceListener> listener = new ServiceListener();
+
+    EXPECT_EQ(BAD_VALUE, ProCamera::removeServiceListener(listener));
+    EXPECT_OK(ProCamera::addServiceListener(listener));
+
+    EXPECT_EQ(ALREADY_EXISTS, ProCamera::addServiceListener(listener));
+    EXPECT_OK(ProCamera::removeServiceListener(listener));
+
+    EXPECT_EQ(BAD_VALUE, ProCamera::removeServiceListener(listener));
+}
+
+//TODO: refactor into separate file
+TEST_F(ProCameraTest, ServiceListenersFunctional) {
+
+    sp<ServiceListener> listener = new ServiceListener();
+
+    EXPECT_OK(ProCamera::addServiceListener(listener));
+
+    sp<Camera> cam = Camera::connect(CAMERA_ID,
+                                     /*clientPackageName*/String16(),
+                                     -1);
+    EXPECT_NE((void*)NULL, cam.get());
+
+    ServiceListener::Status stat = ServiceListener::STATUS_UNKNOWN;
+    EXPECT_OK(listener->waitForStatusChange(/*out*/stat));
+
+    EXPECT_EQ(ServiceListener::STATUS_NOT_AVAILABLE, stat);
+
+    if (cam.get()) {
+        cam->disconnect();
+    }
+
+    EXPECT_OK(listener->waitForStatusChange(/*out*/stat));
+    EXPECT_EQ(ServiceListener::STATUS_AVAILABLE, stat);
+
+    EXPECT_OK(ProCamera::removeServiceListener(listener));
+}
+
 
 
 }
diff --git a/include/camera/CameraBase.h b/include/camera/CameraBase.h
index fed28ea..2735a86 100644
--- a/include/camera/CameraBase.h
+++ b/include/camera/CameraBase.h
@@ -71,6 +71,12 @@
                                        /*out*/
                                        struct CameraInfo* cameraInfo);
 
+    static status_t      addServiceListener(
+                                    const sp<ICameraServiceListener>& listener);
+
+    static status_t      removeServiceListener(
+                                    const sp<ICameraServiceListener>& listener);
+
     sp<TCamUser>         remote();
 
     // Status is set to 'UNKNOWN_ERROR' after successful (re)connection
diff --git a/include/camera/ICameraService.h b/include/camera/ICameraService.h
index ef2b685..aaf6eb3 100644
--- a/include/camera/ICameraService.h
+++ b/include/camera/ICameraService.h
@@ -27,6 +27,7 @@
 class ICameraClient;
 class IProCameraUser;
 class IProCameraCallbacks;
+class ICameraServiceListener;
 
 class ICameraService : public IInterface
 {
@@ -35,7 +36,9 @@
         GET_NUMBER_OF_CAMERAS = IBinder::FIRST_CALL_TRANSACTION,
         GET_CAMERA_INFO,
         CONNECT,
-        CONNECT_PRO
+        CONNECT_PRO,
+        ADD_LISTENER,
+        REMOVE_LISTENER,
     };
 
     enum {
@@ -45,9 +48,18 @@
 public:
     DECLARE_META_INTERFACE(CameraService);
 
-    virtual int32_t         getNumberOfCameras() = 0;
-    virtual status_t        getCameraInfo(int cameraId,
+    virtual int32_t  getNumberOfCameras() = 0;
+    virtual status_t getCameraInfo(int cameraId,
                                           struct CameraInfo* cameraInfo) = 0;
+
+    // Returns 'OK' if operation succeeded
+    // - Errors: ALREADY_EXISTS if the listener was already added
+    virtual status_t addListener(const sp<ICameraServiceListener>& listener)
+                                                                            = 0;
+    // Returns 'OK' if operation succeeded
+    // - Errors: BAD_VALUE if specified listener was not in the listener list
+    virtual status_t removeListener(const sp<ICameraServiceListener>& listener)
+                                                                            = 0;
     /**
      * clientPackageName and clientUid are used for permissions checking.  if
      * clientUid == USE_CALLING_UID, then the calling UID is used instead. Only
diff --git a/include/camera/ICameraServiceListener.h b/include/camera/ICameraServiceListener.h
new file mode 100644
index 0000000..207116a
--- /dev/null
+++ b/include/camera/ICameraServiceListener.h
@@ -0,0 +1,64 @@
+/*
+ * 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_ICAMERASERVICE_LISTENER_H
+#define ANDROID_HARDWARE_ICAMERASERVICE_LISTENER_H
+
+#include <utils/RefBase.h>
+#include <binder/IInterface.h>
+#include <binder/Parcel.h>
+#include <hardware/camera_common.h>
+
+namespace android {
+
+class ICameraServiceListener : public IInterface
+{
+public:
+
+    enum Status {
+        // Device physically unplugged
+        STATUS_PRESENT          = CAMERA_DEVICE_STATUS_PRESENT,
+        // Device physically re-plugged
+        STATUS_NOT_PRESENT      = CAMERA_DEVICE_STATUS_NOT_PRESENT,
+
+        // Camera can be used exclusively
+        STATUS_AVAILABLE        = 0x80000000,
+        // Camera is in use by another app and cannot be used exclusively
+        STATUS_NOT_AVAILABLE,
+
+        // Use to initialize variables only
+        STATUS_UNKNOWN          = 0xFFFFFFFF,
+    };
+
+    DECLARE_META_INTERFACE(CameraServiceListener);
+
+    virtual void onStatusChanged(Status status, int32_t cameraId) = 0;
+};
+
+// ----------------------------------------------------------------------------
+
+class BnCameraServiceListener : public BnInterface<ICameraServiceListener>
+{
+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
index b228145..5d6cfaa 100644
--- a/include/camera/ProCamera.h
+++ b/include/camera/ProCamera.h
@@ -169,6 +169,9 @@
     /**
       * Delete a stream.
       * Lock free.
+      *
+      * NOTE: As a side effect this cancels ALL streaming requests.
+      *
       * Errors: BAD_VALUE if unknown stream ID.
       *         PERMISSION_DENIED if the stream wasn't yours
       */
@@ -198,6 +201,12 @@
                           /*out*/
                           sp<CpuConsumer>* cpuConsumer,
                           int* streamId);
+    status_t createStreamCpu(int width, int height, int format,
+                          int heapCount,
+                          bool synchronousMode,
+                          /*out*/
+                          sp<CpuConsumer>* cpuConsumer,
+                          int* streamId);
 
     // Create a request object from a template.
     status_t createDefaultRequest(int templateId,
@@ -293,6 +302,7 @@
         int  streamID;
         bool cpuStream;
         sp<CpuConsumer> cpuConsumer;
+        bool synchronousMode;
         sp<ProFrameListener> frameAvailableListener;
         sp<Surface> stc;
         int frameReady;
diff --git a/services/camera/libcameraservice/CameraService.cpp b/services/camera/libcameraservice/CameraService.cpp
index d7c8807..8c4f619 100644
--- a/services/camera/libcameraservice/CameraService.cpp
+++ b/services/camera/libcameraservice/CameraService.cpp
@@ -77,6 +77,10 @@
 {
     ALOGI("CameraService started (pid=%d)", getpid());
     gCameraService = this;
+
+    for (size_t i = 0; i < MAX_CAMERAS; ++i) {
+        mStatusList[i] = ICameraServiceListener::STATUS_AVAILABLE;
+    }
 }
 
 void CameraService::onFirstRef()
@@ -155,6 +159,23 @@
     return deviceVersion;
 }
 
+bool CameraService::isValidCameraId(int cameraId) {
+    int facing;
+    int deviceVersion = getDeviceVersion(cameraId, &facing);
+
+    switch(deviceVersion) {
+      case CAMERA_DEVICE_API_VERSION_1_0:
+      case CAMERA_DEVICE_API_VERSION_2_0:
+      case CAMERA_DEVICE_API_VERSION_2_1:
+      case CAMERA_DEVICE_API_VERSION_3_0:
+        return true;
+      default:
+        return false;
+    }
+
+    return false;
+}
+
 sp<ICamera> CameraService::connect(
         const sp<ICameraClient>& cameraClient,
         int cameraId,
@@ -236,6 +257,10 @@
     int facing = -1;
     int deviceVersion = getDeviceVersion(cameraId, &facing);
 
+    if (isValidCameraId(cameraId)) {
+        updateStatus(ICameraServiceListener::STATUS_NOT_AVAILABLE, cameraId);
+    }
+
     switch(deviceVersion) {
       case CAMERA_DEVICE_API_VERSION_1_0:
         client = new CameraClient(this, cameraClient,
@@ -259,6 +284,9 @@
     }
 
     if (client->initialize(mModule) != OK) {
+        // this is probably not recoverable.. but maybe the client can try again
+        updateStatus(ICameraServiceListener::STATUS_AVAILABLE, cameraId);
+
         return NULL;
     }
 
@@ -266,6 +294,7 @@
 
     mClient[cameraId] = client;
     LOG1("CameraService::connect X (id %d, this pid is %d)", cameraId, getpid());
+
     return client;
 }
 
@@ -275,6 +304,7 @@
                                         const String16& clientPackageName,
                                         int clientUid)
 {
+    String8 clientName8(clientPackageName);
     int callingPid = getCallingPid();
 
     // TODO: use clientPackageName and clientUid with appOpsMangr
@@ -301,6 +331,15 @@
         return NULL;
     }
 
+    // TODO: allow concurrent connections with a ProCamera
+    if (mBusy[cameraId]) {
+
+        ALOGW("CameraService::connectPro X (pid %d, \"%s\") rejected"
+                " (camera %d is still busy).", callingPid,
+                clientName8.string(), cameraId);
+        return NULL;
+    }
+
     int facing = -1;
     int deviceVersion = getDeviceVersion(cameraId, &facing);
 
@@ -333,9 +372,45 @@
     LOG1("CameraService::connectPro X (id %d, this pid is %d)", cameraId,
             getpid());
     return client;
+}
 
+status_t CameraService::addListener(
+                                const sp<ICameraServiceListener>& listener) {
+    ALOGV("%s: Add listener %p", __FUNCTION__, listener.get());
 
-    return NULL;
+    Mutex::Autolock lock(mServiceLock);
+
+    Vector<sp<ICameraServiceListener> >::iterator it, end;
+    for (it = mListenerList.begin(); it != mListenerList.end(); ++it) {
+        if ((*it)->asBinder() == listener->asBinder()) {
+            ALOGW("%s: Tried to add listener %p which was already subscribed",
+                  __FUNCTION__, listener.get());
+            return ALREADY_EXISTS;
+        }
+    }
+
+    mListenerList.push_back(listener);
+
+    return OK;
+}
+status_t CameraService::removeListener(
+                                const sp<ICameraServiceListener>& listener) {
+    ALOGV("%s: Remove listener %p", __FUNCTION__, listener.get());
+
+    Mutex::Autolock lock(mServiceLock);
+
+    Vector<sp<ICameraServiceListener> >::iterator it;
+    for (it = mListenerList.begin(); it != mListenerList.end(); ++it) {
+        if ((*it)->asBinder() == listener->asBinder()) {
+            mListenerList.erase(it);
+            return OK;
+        }
+    }
+
+    ALOGW("%s: Tried to remove a listener %p which was not subscribed",
+          __FUNCTION__, listener.get());
+
+    return BAD_VALUE;
 }
 
 void CameraService::removeClientByRemote(const wp<IBinder>& remoteBinder) {
@@ -699,6 +774,8 @@
 void CameraService::Client::disconnect() {
     BasicClient::disconnect();
     mCameraService->setCameraFree(mCameraId);
+    mCameraService->updateStatus(ICameraServiceListener::STATUS_AVAILABLE,
+                                 mCameraId);
 }
 
 CameraService::Client::OpsCallback::OpsCallback(wp<BasicClient> client):
@@ -774,6 +851,10 @@
     return false;
 }
 
+void CameraService::ProClient::onExclusiveLockStolen() {
+    ALOGE("%s: not implemented yet", __FUNCTION__);
+}
+
 status_t CameraService::ProClient::submitRequest(camera_metadata_t* request, bool streaming) {
     ALOGE("%s: not implemented yet", __FUNCTION__);
 
@@ -944,4 +1025,47 @@
 
 }
 
+void CameraService::updateStatus(ICameraServiceListener::Status status,
+                                 int32_t cameraId) {
+    // do not lock mServiceLock here or can get into a deadlock from
+    //  connect() -> ProClient::disconnect -> updateStatus
+    Mutex::Autolock lock(mStatusMutex);
+    updateStatusUnsafe(status, cameraId);
+}
+
+void CameraService::updateStatusUnsafe(ICameraServiceListener::Status status,
+                                       int32_t cameraId) {
+
+    ICameraServiceListener::Status oldStatus = mStatusList[cameraId];
+
+    mStatusList[cameraId] = status;
+
+    if (oldStatus != status) {
+        ALOGV("%s: Status has changed for camera ID %d from 0x%x to 0x%x",
+              __FUNCTION__, cameraId, (uint32_t)oldStatus, (uint32_t)status);
+
+        /**
+          * ProClients lose their exclusive lock.
+          * - Done before the CameraClient can initialize the HAL device,
+          *   since we want to be able to close it before they get to initialize
+          */
+        if (status == ICameraServiceListener::STATUS_NOT_AVAILABLE) {
+            Vector<wp<ProClient> > proClients(mProClientList[cameraId]);
+            Vector<wp<ProClient> >::const_iterator it;
+
+            for (it = proClients.begin(); it != proClients.end(); ++it) {
+                sp<ProClient> proCl = it->promote();
+                if (proCl.get() != NULL) {
+                    proCl->onExclusiveLockStolen();
+                }
+            }
+        }
+
+        Vector<sp<ICameraServiceListener> >::const_iterator it;
+        for (it = mListenerList.begin(); it != mListenerList.end(); ++it) {
+            (*it)->onStatusChanged(status, cameraId);
+        }
+    }
+}
+
 }; // namespace android
diff --git a/services/camera/libcameraservice/CameraService.h b/services/camera/libcameraservice/CameraService.h
index d93aa73..8acc63f 100644
--- a/services/camera/libcameraservice/CameraService.h
+++ b/services/camera/libcameraservice/CameraService.h
@@ -30,6 +30,8 @@
 #include <camera/IProCameraUser.h>
 #include <camera/IProCameraCallbacks.h>
 
+#include <camera/ICameraServiceListener.h>
+
 /* This needs to be increased if we can have more cameras */
 #define MAX_CAMERAS 2
 
@@ -67,6 +69,10 @@
     virtual sp<IProCameraUser> connect(const sp<IProCameraCallbacks>& cameraCb,
             int cameraId, const String16& clientPackageName, int clientUid);
 
+    virtual status_t    addListener(const sp<ICameraServiceListener>& listener);
+    virtual status_t    removeListener(
+                                    const sp<ICameraServiceListener>& listener);
+
     // Extra permissions checks
     virtual status_t    onTransact(uint32_t code, const Parcel& data,
                                    Parcel* reply, uint32_t flags);
@@ -263,6 +269,9 @@
         virtual status_t      requestStream(int streamId);
         virtual status_t      cancelStream(int streamId);
 
+        // Callbacks from camera service
+        virtual void          onExclusiveLockStolen();
+
     protected:
         virtual void          notifyError();
 
@@ -303,11 +312,30 @@
 
     camera_module_t *mModule;
 
+    Vector<sp<ICameraServiceListener> >
+                        mListenerList;
+
+    // guard only mStatusList and the broadcasting of ICameraServiceListener
+    Mutex               mStatusMutex;
+    ICameraServiceListener::Status
+                        mStatusList[MAX_CAMERAS];
+
+    // Broadcast the new status if it changed (locks the service mutex)
+    void                updateStatus(
+                            ICameraServiceListener::Status status,
+                            int32_t cameraId);
+    // Call this one when the service mutex is already held (idempotent)
+    void                updateStatusUnsafe(
+                            ICameraServiceListener::Status status,
+                            int32_t cameraId);
+
     // IBinder::DeathRecipient implementation
-    virtual void binderDied(const wp<IBinder> &who);
+    virtual void        binderDied(const wp<IBinder> &who);
 
     // Helpers
     int                 getDeviceVersion(int cameraId, int* facing);
+
+    bool                isValidCameraId(int cameraId);
 };
 
 } // namespace android
diff --git a/services/camera/libcameraservice/ProCamera2Client.cpp b/services/camera/libcameraservice/ProCamera2Client.cpp
index eda3012..6fed8b4 100644
--- a/services/camera/libcameraservice/ProCamera2Client.cpp
+++ b/services/camera/libcameraservice/ProCamera2Client.cpp
@@ -115,6 +115,8 @@
     Mutex::Autolock icl(mIProCameraUserLock);
     SharedCameraCallbacks::Lock l(mSharedCameraCallbacks);
 
+    if (!mDevice.get()) return PERMISSION_DENIED;
+
     if (!mExclusiveLock) {
         mExclusiveLock = true;
 
@@ -144,6 +146,8 @@
     Mutex::Autolock icl(mIProCameraUserLock);
     SharedCameraCallbacks::Lock l(mSharedCameraCallbacks);
 
+    if (!mDevice.get()) return PERMISSION_DENIED;
+
     /**
      * TODO: this should asynchronously 'wait' until the lock becomes available
      * if another client already has an exclusive lock.
@@ -197,12 +201,33 @@
     return mExclusiveLock;
 }
 
+void ProCamera2Client::onExclusiveLockStolen() {
+    ALOGV("%s: ProClient lost exclusivity (id %d)",
+          __FUNCTION__, mCameraId);
+
+    Mutex::Autolock icl(mIProCameraUserLock);
+    SharedCameraCallbacks::Lock l(mSharedCameraCallbacks);
+
+    if (mExclusiveLock && mRemoteCallback.get() != NULL) {
+        mRemoteCallback->onLockStatusChanged(
+                                       IProCameraCallbacks::LOCK_STOLEN);
+    }
+
+    mExclusiveLock = false;
+
+    //TODO: we should not need to detach the device, merely reset it.
+    detachDevice();
+}
+
 status_t ProCamera2Client::submitRequest(camera_metadata_t* request,
                                          bool streaming) {
     ATRACE_CALL();
     ALOGV("%s", __FUNCTION__);
 
     Mutex::Autolock icl(mIProCameraUserLock);
+
+    if (!mDevice.get()) return DEAD_OBJECT;
+
     if (!mExclusiveLock) {
         return PERMISSION_DENIED;
     }
@@ -224,6 +249,9 @@
     ALOGV("%s", __FUNCTION__);
 
     Mutex::Autolock icl(mIProCameraUserLock);
+
+    if (!mDevice.get()) return DEAD_OBJECT;
+
     if (!mExclusiveLock) {
         return PERMISSION_DENIED;
     }
@@ -247,6 +275,7 @@
 
     Mutex::Autolock icl(mIProCameraUserLock);
 
+    if (!mDevice.get()) return DEAD_OBJECT;
     mDevice->clearStreamingRequest();
 
     status_t code;
@@ -274,6 +303,8 @@
 
     Mutex::Autolock icl(mIProCameraUserLock);
 
+    if (!mDevice.get()) return DEAD_OBJECT;
+
     sp<IBinder> binder;
     sp<ANativeWindow> window;
     if (bufferProducer != 0) {
@@ -303,6 +334,8 @@
 
     Mutex::Autolock icl(mIProCameraUserLock);
 
+    if (!mDevice.get()) return DEAD_OBJECT;
+
     CameraMetadata metadata;
     if ( (res = mDevice->createDefaultRequest(templateId, &metadata) ) == OK) {
         *request = metadata.release();
@@ -319,6 +352,10 @@
         return INVALID_OPERATION;
     }
 
+    Mutex::Autolock icl(mIProCameraUserLock);
+
+    if (!mDevice.get()) return DEAD_OBJECT;
+
     CameraMetadata deviceInfo = mDevice->info();
     *info = deviceInfo.release();
 
@@ -341,6 +378,12 @@
     result = "  Device dump:\n";
     write(fd, result.string(), result.size());
 
+    if (!mDevice.get()) {
+        result = "  *** Device is detached\n";
+        write(fd, result.string(), result.size());
+        return NO_ERROR;
+    }
+
     status_t res = mDevice->dump(fd, args);
     if (res != OK) {
         result = String8::format("   Error dumping device: %s (%d)",
@@ -363,9 +406,19 @@
     int callingPid = getCallingPid();
     if (callingPid != mClientPid && callingPid != mServicePid) return;
 
+    ALOGV("Camera %d: Shutting down", mCameraId);
+
+    detachDevice();
+    ProClient::disconnect();
+
+    ALOGV("Camera %d: Shut down complete complete", mCameraId);
+}
+
+void ProCamera2Client::detachDevice() {
     if (mDevice == 0) return;
 
-    ALOGV("Camera %d: Shutting down", mCameraId);
+    ALOGV("Camera %d: Stopping processors", mCameraId);
+
     mFrameProcessor->removeListener(FRAME_PROCESSOR_LISTENER_MIN_ID,
                                     FRAME_PROCESSOR_LISTENER_MAX_ID,
                                     /*listener*/this);
@@ -374,11 +427,22 @@
     mFrameProcessor->join();
     ALOGV("Camera %d: Disconnecting device", mCameraId);
 
+    // WORKAROUND: HAL refuses to disconnect while there's streams in flight
+    {
+        mDevice->clearStreamingRequest();
+
+        status_t code;
+        if ((code = mDevice->waitUntilDrained()) != OK) {
+            ALOGE("%s: waitUntilDrained failed with code 0x%x", __FUNCTION__,
+                  code);
+        }
+    }
+
     mDevice->disconnect();
 
     mDevice.clear();
 
-    ProClient::disconnect();
+    ALOGV("Camera %d: Detach complete", mCameraId);
 }
 
 status_t ProCamera2Client::connect(const sp<IProCameraCallbacks>& client) {
diff --git a/services/camera/libcameraservice/ProCamera2Client.h b/services/camera/libcameraservice/ProCamera2Client.h
index 9f514f4..ff6f4e2 100644
--- a/services/camera/libcameraservice/ProCamera2Client.h
+++ b/services/camera/libcameraservice/ProCamera2Client.h
@@ -104,6 +104,9 @@
     const sp<Camera2Device>& getCameraDevice();
     const sp<CameraService>& getCameraService();
 
+    // Callbacks from camera service
+    virtual void onExclusiveLockStolen();
+
     /**
      * Interface used by independent components of ProCamera2Client.
      */
@@ -167,6 +170,8 @@
     // - if no we can't modify the request queue.
     // note that creating/deleting streams we own is still OK
     bool mExclusiveLock;
+
+    void detachDevice();
 };
 
 }; // namespace android