Merge "media.c2: link libnativewindow to libcodec2_aidl" into main
diff --git a/media/libaudiohal/impl/DeviceHalAidl.cpp b/media/libaudiohal/impl/DeviceHalAidl.cpp
index e0110cf..362f373 100644
--- a/media/libaudiohal/impl/DeviceHalAidl.cpp
+++ b/media/libaudiohal/impl/DeviceHalAidl.cpp
@@ -1024,11 +1024,12 @@
     // There is not AIDL API defined for `prepareToDisconnectExternalDevice`.
     // Call `setConnectedState` instead.
     // TODO(b/279824103): call prepareToDisconnectExternalDevice when it is added.
-    const status_t status = setConnectedState(port, false /*connected*/);
-    if (status == NO_ERROR) {
+    if (const status_t status = setConnectedState(port, false /*connected*/); status == NO_ERROR) {
         mDeviceDisconnectionNotified.insert(port->id);
     }
-    return status;
+    // Return that there was no error as otherwise the disconnection procedure will not be
+    // considered complete for upper layers, and 'setConnectedState' will not be called again.
+    return NO_ERROR;
 }
 
 status_t DeviceHalAidl::setConnectedState(const struct audio_port_v7 *port, bool connected) {
@@ -1068,6 +1069,7 @@
             ALOGD("%s: device port for device %s found in the module %s",
                     __func__, matchDevice.toString().c_str(), mInstance.c_str());
         }
+        resetUnusedPatchesAndPortConfigs();
         // Use the ID of the "template" port, use all the information from the provided port.
         aidlPort.id = portsIt->first;
         AudioPort connectedPort;
@@ -1089,12 +1091,19 @@
             ALOGD("%s: device port for device %s found in the module %s",
                     __func__, matchDevice.toString().c_str(), mInstance.c_str());
         }
-        // Any streams opened on the external device must be closed by this time,
-        // thus we can clean up patches and port configs that were created for them.
         resetUnusedPatchesAndPortConfigs();
-        RETURN_STATUS_IF_ERROR(statusTFromBinderStatus(mModule->disconnectExternalDevice(
-                                portsIt->second.id)));
-        mPorts.erase(portsIt);
+        // Streams are closed by AudioFlinger independently from device disconnections.
+        // It is possible that the stream has not been closed yet.
+        const int32_t portId = portsIt->second.id;
+        if (!isPortHeldByAStream(portId)) {
+            RETURN_STATUS_IF_ERROR(statusTFromBinderStatus(
+                            mModule->disconnectExternalDevice(portId)));
+            mPorts.erase(portsIt);
+        } else {
+            ALOGD("%s: since device port ID %d is used by a stream, "
+                    "external device disconnection postponed", __func__, portId);
+            mConnectedPortIdsHeldByStreams.insert(portId);
+        }
     }
     return updateRoutes();
 }
@@ -1102,6 +1111,7 @@
 status_t DeviceHalAidl::setSimulateDeviceConnections(bool enabled) {
     TIME_CHECK();
     if (!mModule) return NO_INIT;
+    resetUnusedPatchesAndPortConfigs();
     ModuleDebug debug{ .simulateDeviceConnections = enabled };
     status_t status = statusTFromBinderStatus(mModule->setModuleDebug(debug));
     // This is important to log as it affects HAL behavior.
@@ -1670,6 +1680,28 @@
                         p.ext.template get<Tag::mix>().handle == ioHandle; });
 }
 
+bool DeviceHalAidl::isPortHeldByAStream(int32_t portId) {
+    // It is assumed that mStreams has already been cleaned up.
+    for (const auto& streamPair : mStreams) {
+        int32_t patchId = streamPair.second;
+        auto patchIt = mPatches.find(patchId);
+        if (patchIt == mPatches.end()) continue;
+        for (int32_t id : patchIt->second.sourcePortConfigIds) {
+            auto portConfigIt = mPortConfigs.find(id);
+            if (portConfigIt != mPortConfigs.end() && portConfigIt->second.portId == portId) {
+                return true;
+            }
+        }
+        for (int32_t id : patchIt->second.sinkPortConfigIds) {
+            auto portConfigIt = mPortConfigs.find(id);
+            if (portConfigIt != mPortConfigs.end() && portConfigIt->second.portId == portId) {
+                return true;
+            }
+        }
+    }
+    return false;
+}
+
 void DeviceHalAidl::resetPatch(int32_t patchId) {
     if (auto it = mPatches.find(patchId); it != mPatches.end()) {
         mPatches.erase(it);
@@ -1719,10 +1751,10 @@
     // The assumption is that port configs are used to create patches
     // (or to open streams, but that involves creation of patches, too). Thus,
     // orphaned port configs can and should be reset.
-    std::set<int32_t> portConfigIds;
+    std::map<int32_t, int32_t /*portID*/> portConfigIds;
     std::transform(mPortConfigs.begin(), mPortConfigs.end(),
             std::inserter(portConfigIds, portConfigIds.end()),
-            [](const auto& pcPair) { return pcPair.first; });
+            [](const auto& pcPair) { return std::make_pair(pcPair.first, pcPair.second.portId); });
     for (const auto& p : mPatches) {
         for (int32_t id : p.second.sourcePortConfigIds) portConfigIds.erase(id);
         for (int32_t id : p.second.sinkPortConfigIds) portConfigIds.erase(id);
@@ -1730,7 +1762,27 @@
     for (int32_t id : mInitialPortConfigIds) {
         portConfigIds.erase(id);
     }
-    for (int32_t id : portConfigIds) resetPortConfig(id);
+    std::set<int32_t> retryDeviceDisconnection;
+    for (const auto& portConfigAndIdPair : portConfigIds) {
+        resetPortConfig(portConfigAndIdPair.first);
+        if (mConnectedPortIdsHeldByStreams.count(portConfigAndIdPair.second) != 0) {
+            retryDeviceDisconnection.insert(portConfigAndIdPair.second);
+        }
+    }
+    for (int32_t portId : retryDeviceDisconnection) {
+        if (!isPortHeldByAStream(portId)) {
+            TIME_CHECK();
+            if (auto status = mModule->disconnectExternalDevice(portId); status.isOk()) {
+                mPorts.erase(portId);
+                mConnectedPortIdsHeldByStreams.erase(portId);
+                ALOGD("%s: executed postponed external device disconnection for port ID %d",
+                        __func__, portId);
+            }
+        }
+    }
+    if (!retryDeviceDisconnection.empty()) {
+        updateRoutes();
+    }
 }
 
 status_t DeviceHalAidl::updateRoutes() {
diff --git a/media/libaudiohal/impl/DeviceHalAidl.h b/media/libaudiohal/impl/DeviceHalAidl.h
index 2e393c7..329adba 100644
--- a/media/libaudiohal/impl/DeviceHalAidl.h
+++ b/media/libaudiohal/impl/DeviceHalAidl.h
@@ -257,6 +257,7 @@
             const ::aidl::android::media::audio::common::AudioConfig& config,
             const std::optional<::aidl::android::media::audio::common::AudioIoFlags>& flags,
             int32_t ioHandle);
+    bool isPortHeldByAStream(int32_t portId);
     status_t prepareToOpenStream(
         int32_t aidlHandle,
         const ::aidl::android::media::audio::common::AudioDevice& aidlDevice,
@@ -312,6 +313,7 @@
     std::mutex mLock;
     std::map<void*, Callbacks> mCallbacks GUARDED_BY(mLock);
     std::set<audio_port_handle_t> mDeviceDisconnectionNotified;
+    std::set<int32_t> mConnectedPortIdsHeldByStreams;
 };
 
 } // namespace android
diff --git a/media/libaudiohal/impl/EffectConversionHelperAidl.cpp b/media/libaudiohal/impl/EffectConversionHelperAidl.cpp
index e16fcf0..aecbba7 100644
--- a/media/libaudiohal/impl/EffectConversionHelperAidl.cpp
+++ b/media/libaudiohal/impl/EffectConversionHelperAidl.cpp
@@ -182,8 +182,9 @@
     RETURN_STATUS_IF_ERROR(statusTFromBinderStatus(mEffect->getState(&state)));
     // in case of buffer/ioHandle re-configure for an opened effect, close it and re-open
     if (state != State::INIT && mCommon != common) {
-        ALOGI("%s at state %s, closing effect", __func__,
-              android::internal::ToString(state).c_str());
+        ALOGI("%s at state %s, common parameter change from %s to %s, closing effect", __func__,
+              android::internal::ToString(state).c_str(), mCommon.toString().c_str(),
+              common.toString().c_str());
         RETURN_STATUS_IF_ERROR(statusTFromBinderStatus(mEffect->close()));
         RETURN_STATUS_IF_ERROR(statusTFromBinderStatus(mEffect->getState(&state)));
         mStatusQ.reset();
@@ -476,8 +477,9 @@
                   efGroup);
             status = (status == OK) ? BAD_VALUE : status;
         }
-    } else if (isBypassing()) {
-        // for effect with bypass (no processing) flag, it's okay to not have statusQ
+    } else if (isBypassingOrOffload()) {
+        // for effect with bypass (no processing) or offloadIndication flag, it's okay to not have
+        // statusQ
         return OK;
     }
 
@@ -485,12 +487,22 @@
     return status;
 }
 
+bool EffectConversionHelperAidl::isBypassingOrOffload() const {
+    return isBypassing() || isOffload();
+}
+
 bool EffectConversionHelperAidl::isBypassing() const {
     return mEffect &&
            (mDesc.common.flags.bypass ||
             (mIsProxyEffect && std::static_pointer_cast<EffectProxy>(mEffect)->isBypassing()));
 }
 
+bool EffectConversionHelperAidl::isOffload() const {
+    return mEffect &&
+           (mDesc.common.flags.offloadIndication ||
+            (mIsProxyEffect && std::static_pointer_cast<EffectProxy>(mEffect)->isOffload()));
+}
+
 Descriptor EffectConversionHelperAidl::getDescriptor() const {
     if (!mIsProxyEffect) {
         return mDesc;
diff --git a/media/libaudiohal/impl/EffectConversionHelperAidl.h b/media/libaudiohal/impl/EffectConversionHelperAidl.h
index 17ff944..b2ef155 100644
--- a/media/libaudiohal/impl/EffectConversionHelperAidl.h
+++ b/media/libaudiohal/impl/EffectConversionHelperAidl.h
@@ -41,7 +41,10 @@
     std::shared_ptr<DataMQ> getInputMQ() { return mInputQ; }
     std::shared_ptr<DataMQ> getOutputMQ() { return mOutputQ; }
     std::shared_ptr<android::hardware::EventFlag> getEventFlagGroup() { return mEfGroup; }
+
     bool isBypassing() const;
+    bool isOffload() const;
+    bool isBypassingOrOffload() const;
 
     ::aidl::android::hardware::audio::effect::Descriptor getDescriptor() const;
 
diff --git a/media/libaudiohal/impl/EffectProxy.cpp b/media/libaudiohal/impl/EffectProxy.cpp
index 7b5e195..4d7db4c 100644
--- a/media/libaudiohal/impl/EffectProxy.cpp
+++ b/media/libaudiohal/impl/EffectProxy.cpp
@@ -278,6 +278,10 @@
     return mSubEffects[mActiveSubIdx].descriptor.common.flags.bypass;
 }
 
+bool EffectProxy::isOffload() const {
+    return mSubEffects[mActiveSubIdx].descriptor.common.flags.offloadIndication;
+}
+
 binder_status_t EffectProxy::dump(int fd, const char** args, uint32_t numArgs) {
     const std::string dumpString = toString();
     write(fd, dumpString.c_str(), dumpString.size());
diff --git a/media/libaudiohal/impl/EffectProxy.h b/media/libaudiohal/impl/EffectProxy.h
index 18e1567..e14eb8a 100644
--- a/media/libaudiohal/impl/EffectProxy.h
+++ b/media/libaudiohal/impl/EffectProxy.h
@@ -98,6 +98,7 @@
     }
 
     bool isBypassing() const;
+    bool isOffload() const;
 
     // call dump for all sub-effects
     binder_status_t dump(int fd, const char** args, uint32_t numArgs) override;
diff --git a/services/camera/libcameraservice/device3/Camera3Device.cpp b/services/camera/libcameraservice/device3/Camera3Device.cpp
index 495badf..4198b71 100644
--- a/services/camera/libcameraservice/device3/Camera3Device.cpp
+++ b/services/camera/libcameraservice/device3/Camera3Device.cpp
@@ -2937,7 +2937,6 @@
         mRotateAndCropOverride(ANDROID_SCALER_ROTATE_AND_CROP_NONE),
         mComposerOutput(false),
         mCameraMute(ANDROID_SENSOR_TEST_PATTERN_MODE_OFF),
-        mCameraMuteChanged(false),
         mRepeatingLastFrameNumber(
             hardware::camera2::ICameraDeviceUser::NO_IN_FLIGHT_REPEATING_FRAMES),
         mPrepareVideoStream(false),
@@ -4110,7 +4109,6 @@
     Mutex::Autolock l(mTriggerMutex);
     if (muteMode != mCameraMute) {
         mCameraMute = muteMode;
-        mCameraMuteChanged = true;
     }
     return OK;
 }
diff --git a/services/camera/libcameraservice/device3/Camera3Device.h b/services/camera/libcameraservice/device3/Camera3Device.h
index e572718..d7cfa94 100644
--- a/services/camera/libcameraservice/device3/Camera3Device.h
+++ b/services/camera/libcameraservice/device3/Camera3Device.h
@@ -1085,7 +1085,6 @@
         camera_metadata_enum_android_scaler_rotate_and_crop_t mRotateAndCropOverride;
         bool               mComposerOutput;
         int32_t            mCameraMute; // 0 = no mute, otherwise the TEST_PATTERN_MODE to use
-        bool               mCameraMuteChanged;
 
         int64_t            mRepeatingLastFrameNumber;