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/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