Camera: Enable session parameter reconfiguration queries

Check with Hal whether stream reconfiguration is required
in case of session parameter updates.

Bug: 122609098
Test: Manual using application,
vts-tradefed run commandAndExit vts --skip-all-system-status-check
--skip-preconditions --module VtsHalCameraProviderV2_4Target -l INFO
Change-Id: Ic02525871aa079393b28b2da53764093f95f881d
diff --git a/camera/device/3.5/ICameraDeviceSession.hal b/camera/device/3.5/ICameraDeviceSession.hal
index b2b71cd..d0cfe39 100644
--- a/camera/device/3.5/ICameraDeviceSession.hal
+++ b/camera/device/3.5/ICameraDeviceSession.hal
@@ -17,6 +17,7 @@
 package android.hardware.camera.device@3.5;
 
 import android.hardware.camera.common@1.0::Status;
+import @3.2::CameraMetadata;
 import @3.4::ICameraDeviceSession;
 import @3.4::HalStreamConfiguration;
 
@@ -99,4 +100,49 @@
         vec<int32_t> streamIds,
         uint32_t streamConfigCounter
     );
+
+    /**
+     * isReconfigurationRequired:
+     *
+     * Check whether complete stream reconfiguration is required for possible new session
+     * parameter values.
+     *
+     * This method must be called by the camera framework in case the client changes
+     * the value of any advertised session parameters. Depending on the specific values
+     * the HAL can decide whether a complete stream reconfiguration is required. In case
+     * the HAL returns false, the camera framework must skip the internal reconfiguration.
+     * In case Hal returns true, the framework must reconfigure the streams and pass the
+     * new session parameter values accordingly.
+     * This call may be done by the framework some time before the request with new parameters
+     * is submitted to the HAL, and the request may be cancelled before it ever gets submitted.
+     * Therefore, the HAL must not use this query as an indication to change its behavior in any
+     * way.
+     * ------------------------------------------------------------------------
+     *
+     * Preconditions:
+     *
+     * The framework can call this method at any time after active
+     * session configuration. There must be no impact on the performance of
+     * pending camera requests in any way. In particular there must not be
+     * any glitches or delays during normal camera streaming.
+     *
+     * Performance requirements:
+     * HW and SW camera settings must not be changed and there must not be
+     * a user-visible impact on camera performance.
+     *
+     * @param oldSessionParams Before session parameters, usually the current session parameters.
+     * @param newSessionParams The new session parameters which may be set by client.
+     *
+     * @return Status Status code for the operation, one of:
+     *     OK:
+     *          On successful reconfiguration required query.
+     *     METHOD_NOT_SUPPORTED:
+     *          The camera device does not support the reconfiguration query.
+     *     INTERNAL_ERROR:
+     *          The reconfiguration query cannot complete due to internal
+     *          error.
+     * @return true in case the stream reconfiguration is required, false otherwise.
+     */
+    isReconfigurationRequired(CameraMetadata oldSessionParams, CameraMetadata newSessionParams)
+            generates(Status status, bool reconfigurationNeeded);
 };
diff --git a/camera/device/3.5/default/CameraDeviceSession.cpp b/camera/device/3.5/default/CameraDeviceSession.cpp
index 0770f04..d9c6eef 100644
--- a/camera/device/3.5/default/CameraDeviceSession.cpp
+++ b/camera/device/3.5/default/CameraDeviceSession.cpp
@@ -356,6 +356,35 @@
     d->returnStreamBuffers(num_buffers, buffers);
 }
 
+Return<void> CameraDeviceSession::isReconfigurationRequired(
+        const V3_2::CameraMetadata& oldSessionParams, const V3_2::CameraMetadata& newSessionParams,
+        ICameraDeviceSession::isReconfigurationRequired_cb _hidl_cb) {
+    if (mDevice->ops->is_reconfiguration_required != nullptr) {
+        const camera_metadata_t *oldParams, *newParams;
+        V3_2::implementation::convertFromHidl(oldSessionParams, &oldParams);
+        V3_2::implementation::convertFromHidl(newSessionParams, &newParams);
+        auto ret = mDevice->ops->is_reconfiguration_required(mDevice, oldParams, newParams);
+        switch (ret) {
+            case 0:
+                _hidl_cb(Status::OK, true);
+                break;
+            case -EINVAL:
+                _hidl_cb(Status::OK, false);
+                break;
+            case -ENOSYS:
+                _hidl_cb(Status::METHOD_NOT_SUPPORTED, true);
+                break;
+            default:
+                _hidl_cb(Status::INTERNAL_ERROR, true);
+                break;
+        };
+    } else {
+        _hidl_cb(Status::METHOD_NOT_SUPPORTED, true);
+    }
+
+    return Void();
+}
+
 } // namespace implementation
 }  // namespace V3_5
 }  // namespace device
diff --git a/camera/device/3.5/default/ExternalCameraDeviceSession.cpp b/camera/device/3.5/default/ExternalCameraDeviceSession.cpp
index ae7c45e..00c1d0d 100644
--- a/camera/device/3.5/default/ExternalCameraDeviceSession.cpp
+++ b/camera/device/3.5/default/ExternalCameraDeviceSession.cpp
@@ -295,6 +295,15 @@
     return 0;
 }
 
+Return<void> ExternalCameraDeviceSession::isReconfigurationRequired(
+        const V3_2::CameraMetadata& /*oldSessionParams*/,
+        const V3_2::CameraMetadata& /*newSessionParams*/,
+        ICameraDeviceSession::isReconfigurationRequired_cb _hidl_cb) {
+    //Stub implementation
+    _hidl_cb(Status::OK, true);
+    return Void();
+}
+
 } // namespace implementation
 }  // namespace V3_5
 }  // namespace device
diff --git a/camera/device/3.5/default/include/device_v3_5_impl/CameraDeviceSession.h b/camera/device/3.5/default/include/device_v3_5_impl/CameraDeviceSession.h
index 4f7284c..87d616c 100644
--- a/camera/device/3.5/default/include/device_v3_5_impl/CameraDeviceSession.h
+++ b/camera/device/3.5/default/include/device_v3_5_impl/CameraDeviceSession.h
@@ -93,6 +93,9 @@
             hidl_vec<buffer_handle_t*>& allBufPtrs,
             hidl_vec<int>& allFences) override;
 
+    Return<void> isReconfigurationRequired(const V3_2::CameraMetadata& oldSessionParams,
+            const V3_2::CameraMetadata& newSessionParams,
+            ICameraDeviceSession::isReconfigurationRequired_cb _hidl_cb);
     /**
      * Static callback forwarding methods from HAL to instance
      */
@@ -238,6 +241,11 @@
             return mParent->signalStreamFlush(requests, streamConfigCounter);
         }
 
+        virtual Return<void> isReconfigurationRequired(const V3_2::CameraMetadata& oldSessionParams,
+                const V3_2::CameraMetadata& newSessionParams,
+                ICameraDeviceSession::isReconfigurationRequired_cb _hidl_cb) override {
+            return mParent->isReconfigurationRequired(oldSessionParams, newSessionParams, _hidl_cb);
+        }
     private:
         sp<CameraDeviceSession> mParent;
     };
diff --git a/camera/device/3.5/default/include/ext_device_v3_5_impl/ExternalCameraDeviceSession.h b/camera/device/3.5/default/include/ext_device_v3_5_impl/ExternalCameraDeviceSession.h
index aa119fc..d2b5e89 100644
--- a/camera/device/3.5/default/include/ext_device_v3_5_impl/ExternalCameraDeviceSession.h
+++ b/camera/device/3.5/default/include/ext_device_v3_5_impl/ExternalCameraDeviceSession.h
@@ -106,6 +106,10 @@
             const hidl_vec<int32_t>& requests,
             uint32_t streamConfigCounter);
 
+    Return<void> isReconfigurationRequired(const V3_2::CameraMetadata& oldSessionParams,
+            const V3_2::CameraMetadata& newSessionParams,
+            ICameraDeviceSession::isReconfigurationRequired_cb _hidl_cb);
+
     virtual void initOutputThread() override;
     virtual void closeOutputThread() override;
     void closeOutputThreadImpl();
@@ -247,6 +251,12 @@
             return mParent->signalStreamFlush(requests, streamConfigCounter);
         }
 
+        virtual Return<void> isReconfigurationRequired(const V3_2::CameraMetadata& oldSessionParams,
+                const V3_2::CameraMetadata& newSessionParams,
+                ICameraDeviceSession::isReconfigurationRequired_cb _hidl_cb) override {
+            return mParent->isReconfigurationRequired(oldSessionParams, newSessionParams, _hidl_cb);
+        }
+
     private:
         sp<ExternalCameraDeviceSession> mParent;
     };
diff --git a/camera/provider/2.4/vts/functional/VtsHalCameraProviderV2_4TargetTest.cpp b/camera/provider/2.4/vts/functional/VtsHalCameraProviderV2_4TargetTest.cpp
index fb78483..3949346 100644
--- a/camera/provider/2.4/vts/functional/VtsHalCameraProviderV2_4TargetTest.cpp
+++ b/camera/provider/2.4/vts/functional/VtsHalCameraProviderV2_4TargetTest.cpp
@@ -779,6 +779,9 @@
             hidl_vec<int32_t> streamIds, sp<DeviceCb> cb,
             uint32_t streamConfigCounter = 0);
 
+    void verifySessionReconfigurationQuery(sp<device::V3_5::ICameraDeviceSession> session3_5,
+            camera_metadata* oldSessionParams, camera_metadata* newSessionParams);
+
     static Status getAvailableOutputStreams(camera_metadata_t *staticMeta,
             std::vector<AvailableStream> &outputStreams,
             const AvailableStream *threshold = nullptr);
@@ -3303,7 +3306,8 @@
         }
 
         android::hardware::camera::common::V1_0::helper::CameraMetadata previewRequestSettings;
-        android::hardware::camera::common::V1_0::helper::CameraMetadata sessionParams;
+        android::hardware::camera::common::V1_0::helper::CameraMetadata sessionParams,
+                modifiedSessionParams;
         constructFilteredSettings(session, availableSessionKeys, RequestTemplate::PREVIEW,
                 &previewRequestSettings, &sessionParams);
         if (sessionParams.isEmpty()) {
@@ -3334,13 +3338,28 @@
         ::android::hardware::camera::device::V3_5::StreamConfiguration config3_5;
         config.streams = streams;
         config.operationMode = StreamConfigurationMode::NORMAL_MODE;
-        const camera_metadata_t *sessionParamsBuffer = sessionParams.getAndLock();
-        config.sessionParams.setToExternal(
-                reinterpret_cast<uint8_t *> (const_cast<camera_metadata_t *> (sessionParamsBuffer)),
+        modifiedSessionParams = sessionParams;
+        auto sessionParamsBuffer = sessionParams.release();
+        config.sessionParams.setToExternal(reinterpret_cast<uint8_t *> (sessionParamsBuffer),
                 get_camera_metadata_size(sessionParamsBuffer));
         config3_5.v3_4 = config;
         config3_5.streamConfigCounter = 0;
         if (session3_5 != nullptr) {
+            bool newSessionParamsAvailable = false;
+            for (const auto& it : availableSessionKeys) {
+                if (modifiedSessionParams.exists(it)) {
+                    modifiedSessionParams.erase(it);
+                    newSessionParamsAvailable = true;
+                    break;
+                }
+            }
+            if (newSessionParamsAvailable) {
+                auto modifiedSessionParamsBuffer = modifiedSessionParams.release();
+                verifySessionReconfigurationQuery(session3_5, sessionParamsBuffer,
+                        modifiedSessionParamsBuffer);
+                modifiedSessionParams.acquire(modifiedSessionParamsBuffer);
+            }
+
             ret = session3_5->configureStreams_3_5(config3_5,
                     [](Status s, device::V3_4::HalStreamConfiguration halConfig) {
                         ASSERT_EQ(Status::OK, s);
@@ -3353,7 +3372,7 @@
                         ASSERT_EQ(1u, halConfig.streams.size());
                     });
         }
-
+        sessionParams.acquire(sessionParamsBuffer);
         ASSERT_TRUE(ret.isOk());
 
         free_camera_metadata(staticMetaBuffer);
@@ -6096,6 +6115,35 @@
     }
 }
 
+void CameraHidlTest::verifySessionReconfigurationQuery(
+        sp<device::V3_5::ICameraDeviceSession> session3_5, camera_metadata* oldSessionParams,
+        camera_metadata* newSessionParams) {
+    ASSERT_NE(nullptr, session3_5.get());
+    ASSERT_NE(nullptr, oldSessionParams);
+    ASSERT_NE(nullptr, newSessionParams);
+
+    android::hardware::hidl_vec<uint8_t> oldParams, newParams;
+    oldParams.setToExternal(reinterpret_cast<uint8_t*>(oldSessionParams),
+            get_camera_metadata_size(oldSessionParams));
+    newParams.setToExternal(reinterpret_cast<uint8_t*>(newSessionParams),
+            get_camera_metadata_size(newSessionParams));
+    android::hardware::camera::common::V1_0::Status callStatus;
+    auto hidlCb = [&callStatus] (android::hardware::camera::common::V1_0::Status s,
+            bool /*requiredFlag*/) {
+        callStatus = s;
+    };
+    auto ret = session3_5->isReconfigurationRequired(oldParams, newParams, hidlCb);
+    ASSERT_TRUE(ret.isOk());
+    switch (callStatus) {
+        case android::hardware::camera::common::V1_0::Status::OK:
+        case android::hardware::camera::common::V1_0::Status::METHOD_NOT_SUPPORTED:
+            break;
+        case android::hardware::camera::common::V1_0::Status::INTERNAL_ERROR:
+        default:
+            ADD_FAILURE() << "Query calllback failed";
+    }
+}
+
 int main(int argc, char **argv) {
   ::testing::AddGlobalTestEnvironment(CameraHidlEnvironment::Instance());
   ::testing::InitGoogleTest(&argc, argv);