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