Loading audio/aidl/default/Module.cpp +107 −33 Original line number Diff line number Diff line Loading @@ -467,58 +467,132 @@ ndk::ScopedAStatus Module::updateStreamsConnectedState(const AudioPatch& oldPatc fillConnectionsHelper(connections, patch.sinkPortConfigIds, patch.sourcePortConfigIds); } // Otherwise, there are no streams to notify. }; auto restoreOldConnections = [&](const std::set<int32_t>& mixPortIds, const bool continueWithEmptyDevices) { for (const auto mixPort : mixPortIds) { if (auto it = oldConnections.find(mixPort); continueWithEmptyDevices || it != oldConnections.end()) { const std::vector<AudioDevice> d = it != oldConnections.end() ? getDevicesFromDevicePortConfigIds(it->second) : std::vector<AudioDevice>(); if (auto status = mStreams.setStreamConnectedDevices(mixPort, d); status.isOk()) { LOG(WARNING) << ":updateStreamsConnectedState: rollback: mix port config:" << mixPort << (d.empty() ? "; not connected" : std::string("; connected to ") + ::android::internal::ToString(d)); } else { // can't do much about rollback failures LOG(ERROR) << ":updateStreamsConnectedState: rollback: failed for mix port config:" << mixPort; } } } }; fillConnections(oldConnections, oldPatch); fillConnections(newConnections, newPatch); /** * Illustration of oldConnections and newConnections * * oldConnections { * a : {A,B,C}, * b : {D}, * d : {H,I,J}, * e : {N,O,P}, * f : {Q,R}, * g : {T,U,V}, * } * * newConnections { * a : {A,B,C}, * c : {E,F,G}, * d : {K,L,M}, * e : {N,P}, * f : {Q,R,S}, * g : {U,V,W}, * } * * Expected routings: * 'a': is ignored both in disconnect step and connect step, * due to same devices both in oldConnections and newConnections. * 'b': handled only in disconnect step with empty devices because 'b' is only present * in oldConnections. * 'c': handled only in connect step with {E,F,G} devices because 'c' is only present * in newConnections. * 'd': handled only in connect step with {K,L,M} devices because 'd' is also present * in newConnections and it is ignored in disconnected step. * 'e': handled only in connect step with {N,P} devices because 'e' is also present * in newConnections and it is ignored in disconnect step. please note that there * is no exclusive disconnection for device {O}. * 'f': handled only in connect step with {Q,R,S} devices because 'f' is also present * in newConnections and it is ignored in disconnect step. Even though stream is * already connected with {Q,R} devices and connection happens with {Q,R,S}. * 'g': handled only in connect step with {U,V,W} devices because 'g' is also present * in newConnections and it is ignored in disconnect step. There is no exclusive * disconnection with devices {T,U,V}. * * If, any failure, will lead to restoreOldConnections (rollback). * The aim of the restoreOldConnections is to make connections back to oldConnections. * Failures in restoreOldConnections aren't handled. */ std::for_each(oldConnections.begin(), oldConnections.end(), [&](const auto& connectionPair) { const int32_t mixPortConfigId = connectionPair.first; if (auto it = newConnections.find(mixPortConfigId); it == newConnections.end() || it->second != connectionPair.second) { if (auto status = mStreams.setStreamConnectedDevices(mixPortConfigId, {}); std::set<int32_t> idsToConnectBackOnFailure; // disconnection step for (const auto& [oldMixPortConfigId, oldDevicePortConfigIds] : oldConnections) { if (auto it = newConnections.find(oldMixPortConfigId); it == newConnections.end()) { idsToConnectBackOnFailure.insert(oldMixPortConfigId); if (auto status = mStreams.setStreamConnectedDevices(oldMixPortConfigId, {}); status.isOk()) { LOG(DEBUG) << "updateStreamsConnectedState: The stream on port config id " << mixPortConfigId << " has been disconnected"; LOG(DEBUG) << __func__ << ": The stream on port config id " << oldMixPortConfigId << " has been disconnected"; } else { // Disconnection is tricky to roll back, just register a failure. maybeFailure = std::move(status); // proceed to rollback even on one failure break; } } }); if (!maybeFailure.isOk()) return maybeFailure; std::set<int32_t> idsToDisconnectOnFailure; std::for_each(newConnections.begin(), newConnections.end(), [&](const auto& connectionPair) { const int32_t mixPortConfigId = connectionPair.first; if (auto it = oldConnections.find(mixPortConfigId); it == oldConnections.end() || it->second != connectionPair.second) { const auto connectedDevices = getDevicesFromDevicePortConfigIds(connectionPair.second); } if (!maybeFailure.isOk()) { restoreOldConnections(idsToConnectBackOnFailure, false /*continueWithEmptyDevices*/); LOG(WARNING) << __func__ << ": failed to disconnect from old patch. attempted rollback"; return maybeFailure; } std::set<int32_t> idsToRollbackOnFailure; // connection step for (const auto& [newMixPortConfigId, newDevicePortConfigIds] : newConnections) { if (auto it = oldConnections.find(newMixPortConfigId); it == oldConnections.end() || it->second != newDevicePortConfigIds) { const auto connectedDevices = getDevicesFromDevicePortConfigIds(newDevicePortConfigIds); idsToRollbackOnFailure.insert(newMixPortConfigId); if (connectedDevices.empty()) { // This is important as workers use the vector size to derive the connection status. LOG(FATAL) << "updateStreamsConnectedState: No connected devices found for port " "config id " << mixPortConfigId; LOG(FATAL) << __func__ << ": No connected devices found for port config id " << newMixPortConfigId; } if (auto status = mStreams.setStreamConnectedDevices(mixPortConfigId, connectedDevices); if (auto status = mStreams.setStreamConnectedDevices(newMixPortConfigId, connectedDevices); status.isOk()) { LOG(DEBUG) << "updateStreamsConnectedState: The stream on port config id " << mixPortConfigId << " has been connected to: " LOG(DEBUG) << __func__ << ": The stream on port config id " << newMixPortConfigId << " has been connected to: " << ::android::internal::ToString(connectedDevices); } else { maybeFailure = std::move(status); idsToDisconnectOnFailure.insert(mixPortConfigId); // proceed to rollback even on one failure break; } } }); } if (!maybeFailure.isOk()) { LOG(WARNING) << __func__ << ": " << mType << ": Due to a failure, disconnecting streams on port config ids " << ::android::internal::ToString(idsToDisconnectOnFailure); std::for_each(idsToDisconnectOnFailure.begin(), idsToDisconnectOnFailure.end(), [&](const auto& portConfigId) { auto status = mStreams.setStreamConnectedDevices(portConfigId, {}); (void)status.isOk(); // Can't do much about a failure here. }); restoreOldConnections(idsToConnectBackOnFailure, false /*continueWithEmptyDevices*/); restoreOldConnections(idsToRollbackOnFailure, true /*continueWithEmptyDevices*/); LOG(WARNING) << __func__ << ": failed to connect for new patch. attempted rollback"; return maybeFailure; } return ndk::ScopedAStatus::ok(); } Loading Loading
audio/aidl/default/Module.cpp +107 −33 Original line number Diff line number Diff line Loading @@ -467,58 +467,132 @@ ndk::ScopedAStatus Module::updateStreamsConnectedState(const AudioPatch& oldPatc fillConnectionsHelper(connections, patch.sinkPortConfigIds, patch.sourcePortConfigIds); } // Otherwise, there are no streams to notify. }; auto restoreOldConnections = [&](const std::set<int32_t>& mixPortIds, const bool continueWithEmptyDevices) { for (const auto mixPort : mixPortIds) { if (auto it = oldConnections.find(mixPort); continueWithEmptyDevices || it != oldConnections.end()) { const std::vector<AudioDevice> d = it != oldConnections.end() ? getDevicesFromDevicePortConfigIds(it->second) : std::vector<AudioDevice>(); if (auto status = mStreams.setStreamConnectedDevices(mixPort, d); status.isOk()) { LOG(WARNING) << ":updateStreamsConnectedState: rollback: mix port config:" << mixPort << (d.empty() ? "; not connected" : std::string("; connected to ") + ::android::internal::ToString(d)); } else { // can't do much about rollback failures LOG(ERROR) << ":updateStreamsConnectedState: rollback: failed for mix port config:" << mixPort; } } } }; fillConnections(oldConnections, oldPatch); fillConnections(newConnections, newPatch); /** * Illustration of oldConnections and newConnections * * oldConnections { * a : {A,B,C}, * b : {D}, * d : {H,I,J}, * e : {N,O,P}, * f : {Q,R}, * g : {T,U,V}, * } * * newConnections { * a : {A,B,C}, * c : {E,F,G}, * d : {K,L,M}, * e : {N,P}, * f : {Q,R,S}, * g : {U,V,W}, * } * * Expected routings: * 'a': is ignored both in disconnect step and connect step, * due to same devices both in oldConnections and newConnections. * 'b': handled only in disconnect step with empty devices because 'b' is only present * in oldConnections. * 'c': handled only in connect step with {E,F,G} devices because 'c' is only present * in newConnections. * 'd': handled only in connect step with {K,L,M} devices because 'd' is also present * in newConnections and it is ignored in disconnected step. * 'e': handled only in connect step with {N,P} devices because 'e' is also present * in newConnections and it is ignored in disconnect step. please note that there * is no exclusive disconnection for device {O}. * 'f': handled only in connect step with {Q,R,S} devices because 'f' is also present * in newConnections and it is ignored in disconnect step. Even though stream is * already connected with {Q,R} devices and connection happens with {Q,R,S}. * 'g': handled only in connect step with {U,V,W} devices because 'g' is also present * in newConnections and it is ignored in disconnect step. There is no exclusive * disconnection with devices {T,U,V}. * * If, any failure, will lead to restoreOldConnections (rollback). * The aim of the restoreOldConnections is to make connections back to oldConnections. * Failures in restoreOldConnections aren't handled. */ std::for_each(oldConnections.begin(), oldConnections.end(), [&](const auto& connectionPair) { const int32_t mixPortConfigId = connectionPair.first; if (auto it = newConnections.find(mixPortConfigId); it == newConnections.end() || it->second != connectionPair.second) { if (auto status = mStreams.setStreamConnectedDevices(mixPortConfigId, {}); std::set<int32_t> idsToConnectBackOnFailure; // disconnection step for (const auto& [oldMixPortConfigId, oldDevicePortConfigIds] : oldConnections) { if (auto it = newConnections.find(oldMixPortConfigId); it == newConnections.end()) { idsToConnectBackOnFailure.insert(oldMixPortConfigId); if (auto status = mStreams.setStreamConnectedDevices(oldMixPortConfigId, {}); status.isOk()) { LOG(DEBUG) << "updateStreamsConnectedState: The stream on port config id " << mixPortConfigId << " has been disconnected"; LOG(DEBUG) << __func__ << ": The stream on port config id " << oldMixPortConfigId << " has been disconnected"; } else { // Disconnection is tricky to roll back, just register a failure. maybeFailure = std::move(status); // proceed to rollback even on one failure break; } } }); if (!maybeFailure.isOk()) return maybeFailure; std::set<int32_t> idsToDisconnectOnFailure; std::for_each(newConnections.begin(), newConnections.end(), [&](const auto& connectionPair) { const int32_t mixPortConfigId = connectionPair.first; if (auto it = oldConnections.find(mixPortConfigId); it == oldConnections.end() || it->second != connectionPair.second) { const auto connectedDevices = getDevicesFromDevicePortConfigIds(connectionPair.second); } if (!maybeFailure.isOk()) { restoreOldConnections(idsToConnectBackOnFailure, false /*continueWithEmptyDevices*/); LOG(WARNING) << __func__ << ": failed to disconnect from old patch. attempted rollback"; return maybeFailure; } std::set<int32_t> idsToRollbackOnFailure; // connection step for (const auto& [newMixPortConfigId, newDevicePortConfigIds] : newConnections) { if (auto it = oldConnections.find(newMixPortConfigId); it == oldConnections.end() || it->second != newDevicePortConfigIds) { const auto connectedDevices = getDevicesFromDevicePortConfigIds(newDevicePortConfigIds); idsToRollbackOnFailure.insert(newMixPortConfigId); if (connectedDevices.empty()) { // This is important as workers use the vector size to derive the connection status. LOG(FATAL) << "updateStreamsConnectedState: No connected devices found for port " "config id " << mixPortConfigId; LOG(FATAL) << __func__ << ": No connected devices found for port config id " << newMixPortConfigId; } if (auto status = mStreams.setStreamConnectedDevices(mixPortConfigId, connectedDevices); if (auto status = mStreams.setStreamConnectedDevices(newMixPortConfigId, connectedDevices); status.isOk()) { LOG(DEBUG) << "updateStreamsConnectedState: The stream on port config id " << mixPortConfigId << " has been connected to: " LOG(DEBUG) << __func__ << ": The stream on port config id " << newMixPortConfigId << " has been connected to: " << ::android::internal::ToString(connectedDevices); } else { maybeFailure = std::move(status); idsToDisconnectOnFailure.insert(mixPortConfigId); // proceed to rollback even on one failure break; } } }); } if (!maybeFailure.isOk()) { LOG(WARNING) << __func__ << ": " << mType << ": Due to a failure, disconnecting streams on port config ids " << ::android::internal::ToString(idsToDisconnectOnFailure); std::for_each(idsToDisconnectOnFailure.begin(), idsToDisconnectOnFailure.end(), [&](const auto& portConfigId) { auto status = mStreams.setStreamConnectedDevices(portConfigId, {}); (void)status.isOk(); // Can't do much about a failure here. }); restoreOldConnections(idsToConnectBackOnFailure, false /*continueWithEmptyDevices*/); restoreOldConnections(idsToRollbackOnFailure, true /*continueWithEmptyDevices*/); LOG(WARNING) << __func__ << ": failed to connect for new patch. attempted rollback"; return maybeFailure; } return ndk::ScopedAStatus::ok(); } Loading