Camera: Add NDK test for extended availability callback

Test: NativeCameraManagerTest
Bug: 148146086
Change-Id: I76021b4940bb29b1158a0e2224e9903818b70d67
diff --git a/tests/camera/libctscamera2jni/native-camera-jni.cpp b/tests/camera/libctscamera2jni/native-camera-jni.cpp
index 2a19eb4..db77d79 100644
--- a/tests/camera/libctscamera2jni/native-camera-jni.cpp
+++ b/tests/camera/libctscamera2jni/native-camera-jni.cpp
@@ -57,6 +57,8 @@
 
 class CameraServiceListener {
   public:
+    typedef std::set<std::pair<std::string, std::string>> StringPairSet;
+
     static void onAvailable(void* obj, const char* cameraId) {
         ALOGV("Camera %s onAvailable", cameraId);
         if (obj == nullptr) {
@@ -93,11 +95,38 @@
         return;
     }
 
+    static void onPhysicalCameraAvailable(void* obj, const char* cameraId,
+            const char* physicalCameraId) {
+        ALOGV("Camera %s : %s onAvailable", cameraId, physicalCameraId);
+        if (obj == nullptr) {
+            return;
+        }
+        CameraServiceListener* thiz = reinterpret_cast<CameraServiceListener*>(obj);
+        std::lock_guard<std::mutex> lock(thiz->mMutex);
+        thiz->mOnPhysicalCameraAvailableCount++;
+        return;
+    }
+
+    static void onPhysicalCameraUnavailable(void* obj, const char* cameraId,
+            const char* physicalCameraId) {
+        ALOGV("Camera %s : %s onUnavailable", cameraId, physicalCameraId);
+        if (obj == nullptr) {
+            return;
+        }
+        CameraServiceListener* thiz = reinterpret_cast<CameraServiceListener*>(obj);
+        std::lock_guard<std::mutex> lock(thiz->mMutex);
+        thiz->mUnavailablePhysicalCameras.emplace(cameraId, physicalCameraId);
+        return;
+    }
+
+
     void resetCount() {
         std::lock_guard<std::mutex> lock(mMutex);
         mOnAvailableCount = 0;
         mOnUnavailableCount = 0;
         mOnCameraAccessPrioritiesChangedCount = 0;
+        mOnPhysicalCameraAvailableCount = 0;
+        mUnavailablePhysicalCameras.clear();
         return;
     }
 
@@ -116,6 +145,16 @@
         return mOnCameraAccessPrioritiesChangedCount;
     }
 
+    int getPhysicalCameraAvailableCount() {
+        std::lock_guard<std::mutex> lock(mMutex);
+        return mOnPhysicalCameraAvailableCount;
+    }
+
+    StringPairSet getUnavailablePhysicalCameras() {
+        std::lock_guard<std::mutex> lock(mMutex);
+        return mUnavailablePhysicalCameras;
+    }
+
     bool isAvailable(const char* cameraId) {
         std::lock_guard<std::mutex> lock(mMutex);
         if (mAvailableMap.count(cameraId) == 0) {
@@ -129,7 +168,9 @@
     int mOnAvailableCount = 0;
     int mOnUnavailableCount = 0;
     int mOnCameraAccessPrioritiesChangedCount = 0;
+    int mOnPhysicalCameraAvailableCount = 0;
     std::map<std::string, bool> mAvailableMap;
+    StringPairSet mUnavailablePhysicalCameras;
 };
 
 class CameraDeviceListener {
@@ -1687,6 +1728,113 @@
 
 extern "C" jboolean
 Java_android_hardware_camera2_cts_NativeCameraManagerTest_\
+testCameraManagerExtendedAvailabilityCallbackNative(
+        JNIEnv* env, jclass /*clazz*/) {
+    ALOGV("%s", __FUNCTION__);
+    bool pass = false;
+    ACameraManager* mgr = ACameraManager_create();
+    ACameraIdList *cameraIdList = nullptr;
+    camera_status_t ret = ACameraManager_getCameraIdList(mgr, &cameraIdList);
+    int numCameras = cameraIdList->numCameras;
+    CameraServiceListener listener;
+    CameraServiceListener::StringPairSet unavailablePhysicalCameras;
+    CameraServiceListener::StringPairSet physicalCameraIdPairs;
+    ACameraManager_ExtendedAvailabilityCallbacks cbs {
+            {
+                &listener,
+                CameraServiceListener::onAvailable,
+                CameraServiceListener::onUnavailable
+            },
+            CameraServiceListener::onCameraAccessPrioritiesChanged,
+            CameraServiceListener::onPhysicalCameraAvailable,
+            CameraServiceListener::onPhysicalCameraUnavailable,
+            {}
+    };
+
+    ret = ACameraManager_registerExtendedAvailabilityCallback(mgr, &cbs);
+    if (ret != ACAMERA_OK) {
+        LOG_ERROR(errorString, "Register extended availability callback failed: ret %d", ret);
+        goto cleanup;
+    }
+    sleep(1); // sleep a second to give some time for callbacks to happen
+
+    // Should at least get onAvailable for each camera once
+    if (listener.getAvailableCount() < numCameras) {
+        LOG_ERROR(errorString, "Expect at least %d available callback but only got %d",
+                numCameras, listener.getAvailableCount());
+        goto cleanup;
+    }
+
+    {
+        int availablePhysicalCamera = listener.getPhysicalCameraAvailableCount();
+        if (availablePhysicalCamera > 0) {
+            LOG_ERROR(errorString, "Expect no available callback, but got %d",
+                    availablePhysicalCamera);
+        }
+    }
+
+    unavailablePhysicalCameras = listener.getUnavailablePhysicalCameras();
+    for (int i = 0; i < numCameras; i++) {
+        const char* cameraId = cameraIdList->cameraIds[i];
+        if (cameraId == nullptr) {
+            LOG_ERROR(errorString, "Testcase returned null camera id for camera %d", i);
+            goto cleanup;
+        }
+        ACameraMetadata* c;
+        ret = ACameraManager_getCameraCharacteristics(mgr, cameraId, &c);
+        if (ret != ACAMERA_OK || c == nullptr) {
+            LOG_ERROR(errorString, "Get camera %s characteristics failure", cameraId);
+            goto cleanup;
+        }
+        std::unique_ptr<ACameraMetadata> chars(c);
+
+        size_t physicalCameraCnt = 0;
+        const char *const* physicalCameraIds = nullptr;
+        if (!ACameraMetadata_isLogicalMultiCamera(
+                chars.get(), &physicalCameraCnt, &physicalCameraIds)) {
+            continue;
+        }
+        for (size_t j = 0; j < physicalCameraCnt; j++) {
+            physicalCameraIdPairs.emplace(cameraId, physicalCameraIds[j]);
+        }
+    }
+    for (const auto& unavailIdPair : unavailablePhysicalCameras) {
+        bool validPair = false;
+        for (const auto& idPair : physicalCameraIdPairs) {
+            if (idPair.first == unavailIdPair.first && idPair.second == unavailIdPair.second) {
+                validPair = true;
+                break;
+            }
+        }
+        if (!validPair) {
+            LOG_ERROR(errorString, "Expect valid unavailable physical cameras, but got %s : %s",
+                    unavailIdPair.first.c_str(), unavailIdPair.second.c_str());
+            goto cleanup;
+        }
+    }
+
+    ret = ACameraManager_unregisterExtendedAvailabilityCallback(mgr, &cbs);
+    if (ret != ACAMERA_OK) {
+        LOG_ERROR(errorString, "Unregister extended availability callback failed: ret %d", ret);
+        goto cleanup;
+    }
+    pass = true;
+cleanup:
+    if (cameraIdList) {
+        ACameraManager_deleteCameraIdList(cameraIdList);
+    }
+    if (mgr) {
+        ACameraManager_delete(mgr);
+    }
+    ALOGI("%s %s", __FUNCTION__, pass ? "pass" : "failed");
+    if (!pass) {
+        throwAssertionError(env, errorString);
+    }
+    return pass;
+}
+
+extern "C" jboolean
+Java_android_hardware_camera2_cts_NativeCameraManagerTest_\
 testCameraManagerAvailabilityCallbackNative(
         JNIEnv* env, jclass /*clazz*/) {
     ALOGV("%s", __FUNCTION__);
@@ -3620,6 +3768,10 @@
                 CameraServiceListener::onUnavailable;
         mServiceCb->onCameraAccessPrioritiesChanged =
                 CameraServiceListener::onCameraAccessPrioritiesChanged;
+        mServiceCb->onPhysicalCameraAvailable =
+                CameraServiceListener::onPhysicalCameraAvailable;
+        mServiceCb->onPhysicalCameraUnavailable =
+                CameraServiceListener::onPhysicalCameraUnavailable;
 }
 
 camera_status_t AvailabilityContext::initialize() {
@@ -3991,4 +4143,4 @@
     ALOGV("javaLensFacingU8 = %d, ndkLensFacingU8 = %d",
           javaLensFacingU8, ndkLensFacingU8);
     return (javaLensFacingU8 == ndkLensFacingU8);
-}
\ No newline at end of file
+}
diff --git a/tests/camera/src/android/hardware/camera2/cts/NativeCameraManagerTest.java b/tests/camera/src/android/hardware/camera2/cts/NativeCameraManagerTest.java
index 0ed5f0e..03c788e 100644
--- a/tests/camera/src/android/hardware/camera2/cts/NativeCameraManagerTest.java
+++ b/tests/camera/src/android/hardware/camera2/cts/NativeCameraManagerTest.java
@@ -60,6 +60,12 @@
     }
 
     @Test
+    public void testCameraManagerExtendedAvailabilityCallback() {
+        assertTrue("testCameraManagerExtendedAvailabilityCallback fail, see log for details",
+                testCameraManagerExtendedAvailabilityCallbackNative());
+    }
+
+    @Test
     public void testCameraManagerCameraCharacteristics() {
         assertTrue("testCameraManagerCameraCharacteristics fail, see log for details",
                 testCameraManagerCharacteristicsNative());
@@ -68,5 +74,6 @@
     private static native boolean testCameraManagerGetAndCloseNative();
     private static native boolean testCameraManagerGetCameraIdsNative();
     private static native boolean testCameraManagerAvailabilityCallbackNative();
+    private static native boolean testCameraManagerExtendedAvailabilityCallbackNative();
     private static native boolean testCameraManagerCharacteristicsNative();
 }