Camera: Add hotplug support (for fixed # of cameras)

* Minor: also change addListener to fire the current status upon subscription
* Minor: STATUS_AVAILABLE is now an alias for STATUS_PRESENT and deprecated

Change-Id: I254608a7332095e3ef201ffea64cff156cfc1b3e
diff --git a/services/camera/libcameraservice/CameraService.cpp b/services/camera/libcameraservice/CameraService.cpp
index 5a6a3c8..2db5224 100644
--- a/services/camera/libcameraservice/CameraService.cpp
+++ b/services/camera/libcameraservice/CameraService.cpp
@@ -66,6 +66,20 @@
     return IPCThreadState::self()->getCallingUid();
 }
 
+extern "C" {
+static void camera_device_status_change(
+        const struct camera_module_callbacks* callbacks,
+        int camera_id,
+        int new_status) {
+    sp<CameraService> cs = const_cast<CameraService*>(
+                                static_cast<const CameraService*>(callbacks));
+
+    cs->onDeviceStatusChanged(
+        camera_id,
+        new_status);
+}
+} // extern "C"
+
 // ----------------------------------------------------------------------------
 
 // This is ugly and only safe if we never re-create the CameraService, but
@@ -79,8 +93,10 @@
     gCameraService = this;
 
     for (size_t i = 0; i < MAX_CAMERAS; ++i) {
-        mStatusList[i] = ICameraServiceListener::STATUS_AVAILABLE;
+        mStatusList[i] = ICameraServiceListener::STATUS_PRESENT;
     }
+
+    this->camera_device_status_change = android::camera_device_status_change;
 }
 
 void CameraService::onFirstRef()
@@ -105,6 +121,11 @@
         for (int i = 0; i < mNumberOfCameras; i++) {
             setCameraFree(i);
         }
+
+        if (mModule->common.module_api_version >=
+                CAMERA_MODULE_API_VERSION_2_1) {
+            mModule->set_callbacks(this);
+        }
     }
 }
 
@@ -118,6 +139,67 @@
     gCameraService = NULL;
 }
 
+void CameraService::onDeviceStatusChanged(int cameraId,
+                                          int newStatus)
+{
+    ALOGI("%s: Status changed for cameraId=%d, newStatus=%d", __FUNCTION__,
+          cameraId, newStatus);
+
+    if (cameraId < 0 || cameraId >= MAX_CAMERAS) {
+        ALOGE("%s: Bad camera ID %d", __FUNCTION__, cameraId);
+        return;
+    }
+
+    if ((int)getStatus(cameraId) == newStatus) {
+        ALOGE("%s: State transition to the same status 0x%x not allowed",
+              __FUNCTION__, (uint32_t)newStatus);
+        return;
+    }
+
+    /* don't do this in updateStatus
+       since it is also called from connect and we could get into a deadlock */
+    if (newStatus == CAMERA_DEVICE_STATUS_NOT_PRESENT) {
+        Vector<sp<BasicClient> > clientsToDisconnect;
+        {
+           Mutex::Autolock al(mServiceLock);
+
+           /* Find all clients that we need to disconnect */
+           sp<Client> client = mClient[cameraId].promote();
+           if (client.get() != NULL) {
+               clientsToDisconnect.push_back(client);
+           }
+
+           int i = cameraId;
+           for (size_t j = 0; j < mProClientList[i].size(); ++j) {
+               sp<ProClient> cl = mProClientList[i][j].promote();
+               if (cl != NULL) {
+                   clientsToDisconnect.push_back(cl);
+               }
+           }
+        }
+
+        /* now disconnect them. don't hold the lock
+           or we can get into a deadlock */
+
+        for (size_t i = 0; i < clientsToDisconnect.size(); ++i) {
+            sp<BasicClient> client = clientsToDisconnect[i];
+
+            client->disconnect();
+            /**
+             * The remote app will no longer be able to call methods on the
+             * client since the client PID will be reset to 0
+             */
+        }
+
+        ALOGV("%s: After unplug, disconnected %d clients",
+              __FUNCTION__, clientsToDisconnect.size());
+    }
+
+    updateStatus(
+            static_cast<ICameraServiceListener::Status>(newStatus), cameraId);
+
+}
+
 int32_t CameraService::getNumberOfCameras() {
     return mNumberOfCameras;
 }
@@ -212,6 +294,19 @@
         return false;
     }
 
+    ICameraServiceListener::Status currentStatus = getStatus(cameraId);
+    if (currentStatus == ICameraServiceListener::STATUS_NOT_PRESENT) {
+        ALOGI("Camera is not plugged in,"
+               " connect X (pid %d) rejected", callingPid);
+        return false;
+    } else if (currentStatus == ICameraServiceListener::STATUS_ENUMERATING) {
+        ALOGI("Camera is enumerating,"
+               " connect X (pid %d) rejected", callingPid);
+        return false;
+    }
+    // Else don't check for STATUS_NOT_AVAILABLE.
+    //  -- It's done implicitly in canConnectUnsafe /w the mBusy array
+
     return true;
 }
 
@@ -293,6 +388,7 @@
         // If there are other non-exclusive users of the camera,
         //  this will tear them down before we can reuse the camera
         if (isValidCameraId(cameraId)) {
+            // transition from PRESENT -> NOT_AVAILABLE
             updateStatus(ICameraServiceListener::STATUS_NOT_AVAILABLE,
                          cameraId);
         }
@@ -321,7 +417,8 @@
 
         if (!connectFinishUnsafe(client, client->asBinder())) {
             // this is probably not recoverable.. maybe the client can try again
-            updateStatus(ICameraServiceListener::STATUS_AVAILABLE, cameraId);
+            // OK: we can only get here if we were originally in PRESENT state
+            updateStatus(ICameraServiceListener::STATUS_PRESENT, cameraId);
 
             return NULL;
         }
@@ -429,6 +526,15 @@
 
     mListenerList.push_back(listener);
 
+    /* Immediately signal current status to this listener only */
+    {
+        Mutex::Autolock m(mStatusMutex) ;
+        int numCams = getNumberOfCameras();
+        for (int i = 0; i < numCams; ++i) {
+            listener->onStatusChanged(mStatusList[i], i);
+        }
+    }
+
     return OK;
 }
 status_t CameraService::removeListener(
@@ -719,6 +825,8 @@
 
 void CameraService::BasicClient::disconnect() {
     mCameraService->removeClientByRemote(mRemoteBinder);
+    // client shouldn't be able to call into us anymore
+    mClientPid = 0;
 }
 
 status_t CameraService::BasicClient::startCameraOps() {
@@ -816,7 +924,7 @@
 void CameraService::Client::disconnect() {
     BasicClient::disconnect();
     mCameraService->setCameraFree(mCameraId);
-    mCameraService->updateStatus(ICameraServiceListener::STATUS_AVAILABLE,
+    mCameraService->updateStatus(ICameraServiceListener::STATUS_PRESENT,
                                  mCameraId);
 }
 
@@ -1017,6 +1125,16 @@
         ALOGV("%s: Status has changed for camera ID %d from 0x%x to 0x%x",
               __FUNCTION__, cameraId, (uint32_t)oldStatus, (uint32_t)status);
 
+        if (oldStatus == ICameraServiceListener::STATUS_NOT_PRESENT &&
+            (status != ICameraServiceListener::STATUS_PRESENT &&
+             status != ICameraServiceListener::STATUS_ENUMERATING)) {
+
+            ALOGW("%s: From NOT_PRESENT can only transition into PRESENT"
+                  " or ENUMERATING", __FUNCTION__);
+            mStatusList[cameraId] = oldStatus;
+            return;
+        }
+
         /**
           * ProClients lose their exclusive lock.
           * - Done before the CameraClient can initialize the HAL device,
@@ -1041,4 +1159,14 @@
     }
 }
 
+ICameraServiceListener::Status CameraService::getStatus(int cameraId) const {
+    if (cameraId < 0 || cameraId >= MAX_CAMERAS) {
+        ALOGE("%s: Invalid camera ID %d", __FUNCTION__, cameraId);
+        return ICameraServiceListener::STATUS_UNKNOWN;
+    }
+
+    Mutex::Autolock al(mStatusMutex);
+    return mStatusList[cameraId];
+}
+
 }; // namespace android
diff --git a/services/camera/libcameraservice/CameraService.h b/services/camera/libcameraservice/CameraService.h
index c5e495f..8cb1691 100644
--- a/services/camera/libcameraservice/CameraService.h
+++ b/services/camera/libcameraservice/CameraService.h
@@ -45,7 +45,8 @@
 class CameraService :
     public BinderService<CameraService>,
     public BnCameraService,
-    public IBinder::DeathRecipient
+    public IBinder::DeathRecipient,
+    public camera_module_callbacks_t
 {
     friend class BinderService<CameraService>;
 public:
@@ -59,6 +60,11 @@
     virtual             ~CameraService();
 
     /////////////////////////////////////////////////////////////////////
+    // HAL Callbacks
+    virtual void        onDeviceStatusChanged(int cameraId,
+                                              int newStatus);
+
+    /////////////////////////////////////////////////////////////////////
     // ICameraService
     virtual int32_t     getNumberOfCameras();
     virtual status_t    getCameraInfo(int cameraId,
@@ -327,10 +333,14 @@
                         mListenerList;
 
     // guard only mStatusList and the broadcasting of ICameraServiceListener
-    Mutex               mStatusMutex;
+    mutable Mutex       mStatusMutex;
     ICameraServiceListener::Status
                         mStatusList[MAX_CAMERAS];
 
+    // Read the current status (locks mStatusMutex)
+    ICameraServiceListener::Status
+                        getStatus(int cameraId) const;
+
     // Broadcast the new status if it changed (locks the service mutex)
     void                updateStatus(
                             ICameraServiceListener::Status status,