CameraService: Update camera and torch state lists also at runtime

The Camera subsystem maintains lists of camera and torch states, that
are updated during Camera Provider enumeration. These lists have to
be updated at runtime when a camera is plugged in or unplugged too.
Reuse the same code for both cases.

Also fix a bogus PRESENT callback in stopCameraOps.

Change-Id: I9028a3b25d8d983441e89d52a354bed61e3f8976
Signed-off-by: Guennadi Liakhovetski <guennadi.liakhovetski@intel.com>
diff --git a/services/camera/libcameraservice/CameraService.cpp b/services/camera/libcameraservice/CameraService.cpp
index 20bd5e4..6abfa81 100644
--- a/services/camera/libcameraservice/CameraService.cpp
+++ b/services/camera/libcameraservice/CameraService.cpp
@@ -243,29 +243,10 @@
         }
 
         if (!cameraFound) {
-            hardware::camera::common::V1_0::CameraResourceCost cost;
-            res = mCameraProviderManager->getResourceCost(cameraId, &cost);
-            if (res != OK) {
-                ALOGE("Failed to query device resource cost: %s (%d)", strerror(-res), res);
-                continue;
-            }
-            std::set<String8> conflicting;
-            for (size_t i = 0; i < cost.conflictingDevices.size(); i++) {
-                conflicting.emplace(String8(cost.conflictingDevices[i].c_str()));
-            }
-
-            {
-                Mutex::Autolock lock(mCameraStatesLock);
-                mCameraStates.emplace(id8,
-                    std::make_shared<CameraState>(id8, cost.resourceCost, conflicting));
-            }
+            addStates(id8);
         }
 
         onDeviceStatusChanged(id8, CameraDeviceStatus::PRESENT);
-
-        if (mFlashlight->hasFlashUnit(id8)) {
-            mTorchStatusMap.add(id8, TorchModeStatus::AVAILABLE_OFF);
-        }
     }
 
     return OK;
@@ -300,6 +281,31 @@
     enumerateProviders();
 }
 
+void CameraService::addStates(const String8 id) {
+    std::string cameraId(id.c_str());
+    hardware::camera::common::V1_0::CameraResourceCost cost;
+    status_t res = mCameraProviderManager->getResourceCost(cameraId, &cost);
+    if (res != OK) {
+        ALOGE("Failed to query device resource cost: %s (%d)", strerror(-res), res);
+        return;
+    }
+    std::set<String8> conflicting;
+    for (size_t i = 0; i < cost.conflictingDevices.size(); i++) {
+        conflicting.emplace(String8(cost.conflictingDevices[i].c_str()));
+    }
+
+    {
+        Mutex::Autolock lock(mCameraStatesLock);
+        mCameraStates.emplace(id, std::make_shared<CameraState>(id, cost.resourceCost,
+                                                                conflicting));
+    }
+
+    if (mFlashlight->hasFlashUnit(id)) {
+        mTorchStatusMap.add(id, TorchModeStatus::AVAILABLE_OFF);
+    }
+    logDeviceAdded(id, "Device added");
+}
+
 void CameraService::onDeviceStatusChanged(const String8& id,
         CameraDeviceStatus newHalStatus) {
     ALOGI("%s: Status changed for cameraId=%s, newStatus=%d", __FUNCTION__,
@@ -311,8 +317,13 @@
 
     if (state == nullptr) {
         if (newStatus == StatusInternal::PRESENT) {
-            ALOGW("%s: Unknown camera ID %s, probably newly registered?",
+            ALOGI("%s: Unknown camera ID %s, a new camera is added",
                     __FUNCTION__, id.string());
+
+            // First add as absent to make sure clients are notified below
+            addStates(id);
+
+            updateStatus(newStatus, id);
         } else {
             ALOGE("%s: Bad camera ID %s", __FUNCTION__, id.string());
         }
@@ -2229,8 +2240,11 @@
                 mClientPackageName);
         mOpsActive = false;
 
+        // This function is called when a client disconnects. This should
+        // release the camera, but actually only if it was in a proper
+        // functional state, i.e. with status NOT_AVAILABLE
         std::initializer_list<StatusInternal> rejected = {StatusInternal::PRESENT,
-                StatusInternal::ENUMERATING};
+                StatusInternal::ENUMERATING, StatusInternal::NOT_PRESENT};
 
         // Transition to PRESENT if the camera is not in either of the rejected states
         sCameraService->updateStatus(StatusInternal::PRESENT,
@@ -2322,7 +2336,7 @@
 
 CameraService::CameraState::CameraState(const String8& id, int cost,
         const std::set<String8>& conflicting) : mId(id),
-        mStatus(StatusInternal::PRESENT), mCost(cost), mConflicting(conflicting) {}
+        mStatus(StatusInternal::NOT_PRESENT), mCost(cost), mConflicting(conflicting) {}
 
 CameraService::CameraState::~CameraState() {}
 
diff --git a/services/camera/libcameraservice/CameraService.h b/services/camera/libcameraservice/CameraService.h
index 6d5dde8..e9373a6 100644
--- a/services/camera/libcameraservice/CameraService.h
+++ b/services/camera/libcameraservice/CameraService.h
@@ -512,6 +512,9 @@
     // Eumerate all camera providers in the system
     status_t enumerateProviders();
 
+    // Add a new camera to camera and torch state lists
+    void addStates(const String8 id);
+
     // Check if we can connect, before we acquire the service lock.
     // The returned originalClientPid is the PID of the original process that wants to connect to
     // camera.