Camera: Filter logical camera ids to API1 app

A lot of API1 apps don't handle logical/physical cameras well.
For each [logical, physical1, phiscal2] id combo, only advertise the
first ids in the HAL id list.

Test: Camera CTS
Bug: 80075565
Change-Id: I1e300a8e13eff2bfef0a6a157a56717a7b73976c
diff --git a/services/camera/libcameraservice/api2/CameraDeviceClient.cpp b/services/camera/libcameraservice/api2/CameraDeviceClient.cpp
index c49de8e..98d0534 100644
--- a/services/camera/libcameraservice/api2/CameraDeviceClient.cpp
+++ b/services/camera/libcameraservice/api2/CameraDeviceClient.cpp
@@ -624,11 +624,19 @@
         return STATUS_ERROR(CameraService::ERROR_DISCONNECTED, "Camera device no longer alive");
     }
 
-    if (!checkPhysicalCameraId(physicalCameraId)) {
-        String8 msg = String8::format("Camera %s: Camera doesn't support physicalCameraId %s.",
+    if (physicalCameraId.size() > 0) {
+        std::vector<std::string> physicalCameraIds;
+        std::string physicalId(physicalCameraId.string());
+        bool logicalCamera =
+                CameraProviderManager::isLogicalCamera(mDevice->info(), &physicalCameraIds);
+        if (!logicalCamera ||
+                std::find(physicalCameraIds.begin(), physicalCameraIds.end(), physicalId) ==
+                physicalCameraIds.end()) {
+            String8 msg = String8::format("Camera %s: Camera doesn't support physicalCameraId %s.",
                     mCameraIdStr.string(), physicalCameraId.string());
-        ALOGE("%s: %s", __FUNCTION__, msg.string());
-        return STATUS_ERROR(CameraService::ERROR_ILLEGAL_ARGUMENT, msg.string());
+            ALOGE("%s: %s", __FUNCTION__, msg.string());
+            return STATUS_ERROR(CameraService::ERROR_ILLEGAL_ARGUMENT, msg.string());
+        }
     }
     std::vector<sp<Surface>> surfaces;
     std::vector<sp<IBinder>> binders;
@@ -1144,43 +1152,6 @@
     return binder::Status::ok();
 }
 
-bool CameraDeviceClient::checkPhysicalCameraId(const String8& physicalCameraId) {
-    if (0 == physicalCameraId.size()) {
-        return true;
-    }
-
-    CameraMetadata staticInfo = mDevice->info();
-    camera_metadata_entry_t entryCap;
-    bool isLogicalCam = false;
-
-    entryCap = staticInfo.find(ANDROID_REQUEST_AVAILABLE_CAPABILITIES);
-    for (size_t i = 0; i < entryCap.count; ++i) {
-        uint8_t capability = entryCap.data.u8[i];
-        if (capability == ANDROID_REQUEST_AVAILABLE_CAPABILITIES_LOGICAL_MULTI_CAMERA) {
-            isLogicalCam = true;
-        }
-    }
-    if (!isLogicalCam) {
-        return false;
-    }
-
-    camera_metadata_entry_t entryIds = staticInfo.find(ANDROID_LOGICAL_MULTI_CAMERA_PHYSICAL_IDS);
-    const uint8_t* ids = entryIds.data.u8;
-    size_t start = 0;
-    for (size_t i = 0; i < entryIds.count; ++i) {
-        if (ids[i] == '\0') {
-            if (start != i) {
-                String8 currentId((const char*)ids+start);
-                if (currentId == physicalCameraId) {
-                    return true;
-                }
-            }
-            start = i+1;
-        }
-    }
-    return false;
-}
-
 bool CameraDeviceClient::roundBufferDimensionNearest(int32_t width, int32_t height,
         int32_t format, android_dataspace dataSpace, const CameraMetadata& info,
         /*out*/int32_t* outWidth, /*out*/int32_t* outHeight) {
diff --git a/services/camera/libcameraservice/common/CameraProviderManager.cpp b/services/camera/libcameraservice/common/CameraProviderManager.cpp
index 66e9196..43f1a91 100644
--- a/services/camera/libcameraservice/common/CameraProviderManager.cpp
+++ b/services/camera/libcameraservice/common/CameraProviderManager.cpp
@@ -98,9 +98,14 @@
     std::lock_guard<std::mutex> lock(mInterfaceMutex);
     std::vector<std::string> deviceIds;
     for (auto& provider : mProviders) {
-        for (auto& id : provider->mUniqueAPI1CompatibleCameraIds) {
-            deviceIds.push_back(id);
-        }
+        std::vector<std::string> providerDeviceIds = provider->mUniqueAPI1CompatibleCameraIds;
+
+        // API1 app doesn't handle logical and physical camera devices well. So
+        // for each [logical, physical1, physical2, ...] id combo, only take the
+        // first id advertised by HAL, and filter out the rest.
+        filterLogicalCameraIdsLocked(providerDeviceIds);
+
+        deviceIds.insert(deviceIds.end(), providerDeviceIds.begin(), providerDeviceIds.end());
     }
 
     std::sort(deviceIds.begin(), deviceIds.end(),
@@ -172,11 +177,7 @@
 status_t CameraProviderManager::getCameraCharacteristics(const std::string &id,
         CameraMetadata* characteristics) const {
     std::lock_guard<std::mutex> lock(mInterfaceMutex);
-
-    auto deviceInfo = findDeviceInfoLocked(id, /*minVersion*/ {3,0}, /*maxVersion*/ {4,0});
-    if (deviceInfo == nullptr) return NAME_NOT_FOUND;
-
-    return deviceInfo->getCameraCharacteristics(characteristics);
+    return getCameraCharacteristicsLocked(id, characteristics);
 }
 
 status_t CameraProviderManager::getHighestSupportedVersion(const std::string &id,
@@ -391,6 +392,37 @@
     return ret;
 }
 
+bool CameraProviderManager::isLogicalCamera(const CameraMetadata& staticInfo,
+        std::vector<std::string>* physicalCameraIds) {
+    bool isLogicalCam = false;
+    camera_metadata_ro_entry_t entryCap;
+
+    entryCap = staticInfo.find(ANDROID_REQUEST_AVAILABLE_CAPABILITIES);
+    for (size_t i = 0; i < entryCap.count; ++i) {
+        uint8_t capability = entryCap.data.u8[i];
+        if (capability == ANDROID_REQUEST_AVAILABLE_CAPABILITIES_LOGICAL_MULTI_CAMERA) {
+            isLogicalCam = true;
+            break;
+        }
+    }
+    if (!isLogicalCam) {
+        return false;
+    }
+
+    camera_metadata_ro_entry_t entryIds = staticInfo.find(ANDROID_LOGICAL_MULTI_CAMERA_PHYSICAL_IDS);
+    const uint8_t* ids = entryIds.data.u8;
+    size_t start = 0;
+    for (size_t i = 0; i < entryIds.count; ++i) {
+        if (ids[i] == '\0') {
+            if (start != i) {
+                physicalCameraIds->push_back((const char*)ids+start);
+            }
+            start = i+1;
+        }
+    }
+    return true;
+}
+
 status_t CameraProviderManager::addProviderLocked(const std::string& newProvider, bool expected) {
     for (const auto& providerInfo : mProviders) {
         if (providerInfo->mProviderName == newProvider) {
@@ -599,7 +631,7 @@
 
     mUniqueCameraIds.insert(id);
     if (isAPI1Compatible) {
-        mUniqueAPI1CompatibleCameraIds.insert(id);
+        mUniqueAPI1CompatibleCameraIds.push_back(id);
     }
 
     if (parsedId != nullptr) {
@@ -613,7 +645,9 @@
         if ((*it)->mId == id) {
             mUniqueCameraIds.erase(id);
             if ((*it)->isAPI1Compatible()) {
-                mUniqueAPI1CompatibleCameraIds.erase(id);
+                mUniqueAPI1CompatibleCameraIds.erase(std::remove(
+                        mUniqueAPI1CompatibleCameraIds.begin(),
+                        mUniqueAPI1CompatibleCameraIds.end(), id));
             }
             mDevices.erase(it);
             break;
@@ -1417,5 +1451,51 @@
     return OK;
 }
 
+status_t CameraProviderManager::getCameraCharacteristicsLocked(const std::string &id,
+        CameraMetadata* characteristics) const {
+    auto deviceInfo = findDeviceInfoLocked(id, /*minVersion*/ {3,0}, /*maxVersion*/ {4,0});
+    if (deviceInfo == nullptr) return NAME_NOT_FOUND;
+
+    return deviceInfo->getCameraCharacteristics(characteristics);
+}
+
+void CameraProviderManager::filterLogicalCameraIdsLocked(
+        std::vector<std::string>& deviceIds) const
+{
+    std::unordered_set<std::string> removedIds;
+
+    for (auto& deviceId : deviceIds) {
+        CameraMetadata info;
+        status_t res = getCameraCharacteristicsLocked(deviceId, &info);
+        if (res != OK) {
+            ALOGE("%s: Failed to getCameraCharacteristics for id %s", __FUNCTION__,
+                    deviceId.c_str());
+            return;
+        }
+
+        // idCombo contains the ids of a logical camera and its physical cameras
+        std::vector<std::string> idCombo;
+        bool logicalCamera = CameraProviderManager::isLogicalCamera(info, &idCombo);
+        if (!logicalCamera) {
+            continue;
+        }
+        idCombo.push_back(deviceId);
+
+        for (auto& id : deviceIds) {
+            auto foundId = std::find(idCombo.begin(), idCombo.end(), id);
+            if (foundId == idCombo.end()) {
+                continue;
+            }
+
+            idCombo.erase(foundId);
+            removedIds.insert(idCombo.begin(), idCombo.end());
+            break;
+        }
+    }
+
+    deviceIds.erase(std::remove_if(deviceIds.begin(), deviceIds.end(),
+            [&removedIds](const std::string& s) {return removedIds.find(s) != removedIds.end();}),
+            deviceIds.end());
+}
 
 } // namespace android
diff --git a/services/camera/libcameraservice/common/CameraProviderManager.h b/services/camera/libcameraservice/common/CameraProviderManager.h
index bbe6789..b8b8b8c 100644
--- a/services/camera/libcameraservice/common/CameraProviderManager.h
+++ b/services/camera/libcameraservice/common/CameraProviderManager.h
@@ -230,6 +230,13 @@
             hardware::hidl_version minVersion = hardware::hidl_version{0,0},
             hardware::hidl_version maxVersion = hardware::hidl_version{1000,0}) const;
 
+    /*
+     * Check if a camera with staticInfo is a logical camera. And if yes, return
+     * the physical camera ids.
+     */
+    static bool isLogicalCamera(const CameraMetadata& staticInfo,
+            std::vector<std::string>* physicalCameraIds);
+
 private:
     // All private members, unless otherwise noted, expect mInterfaceMutex to be locked before use
     mutable std::mutex mInterfaceMutex;
@@ -314,7 +321,7 @@
         std::vector<std::unique_ptr<DeviceInfo>> mDevices;
         std::unordered_set<std::string> mUniqueCameraIds;
         int mUniqueDeviceCount;
-        std::unordered_set<std::string> mUniqueAPI1CompatibleCameraIds;
+        std::vector<std::string> mUniqueAPI1CompatibleCameraIds;
 
         // HALv1-specific camera fields, including the actual device interface
         struct DeviceInfo1 : public DeviceInfo {
@@ -414,6 +421,9 @@
     static const char* torchStatusToString(
         const hardware::camera::common::V1_0::TorchModeStatus&);
 
+    status_t getCameraCharacteristicsLocked(const std::string &id,
+            CameraMetadata* characteristics) const;
+    void filterLogicalCameraIdsLocked(std::vector<std::string>& deviceIds) const;
 };
 
 } // namespace android