Camera: Drop ProCamera connections when a Camera connection happens

* Also adds an ICameraServiceListener with available/not available statuses

Bug: 8291653
Change-Id: I24680f1a2dc109510caf451cf7c7bd180b670d84
diff --git a/camera/tests/ProCameraTests.cpp b/camera/tests/ProCameraTests.cpp
index 39456af..c61e71a 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());
 }
@@ -1035,6 +1116,51 @@
 
 
 
+//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));
+}
+
+
+
 }
 }
 }