diff --git a/app/src/main/java/org/thoughtcrime/securesms/keyvalue/InternalValues.java b/app/src/main/java/org/thoughtcrime/securesms/keyvalue/InternalValues.java index f2a6ae91fd..67e595224c 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/keyvalue/InternalValues.java +++ b/app/src/main/java/org/thoughtcrime/securesms/keyvalue/InternalValues.java @@ -157,14 +157,13 @@ public final class InternalValues extends SignalStoreValues { } /** - * The selected audio processing method to use (for AEC/NS). - *

- * The user must be an internal user otherwise the default method will be returned. For - * evaluation, internal users will use software processing by default unless the setting - * is changed in storage. + * Setting to override the default handling of hardware/software AEC. */ public synchronized CallManager.AudioProcessingMethod audioProcessingMethod() { - return FeatureFlags.internalUser() ? CallManager.AudioProcessingMethod.values()[getInteger(AUDIO_PROCESSING_METHOD, CallManager.AudioProcessingMethod.ForceSoftware.ordinal())] - : CallManager.AudioProcessingMethod.Default; + if (FeatureFlags.internalUser()) { + return CallManager.AudioProcessingMethod.values()[getInteger(AUDIO_PROCESSING_METHOD, CallManager.AudioProcessingMethod.Default.ordinal())]; + } else { + return CallManager.AudioProcessingMethod.Default; + } } } diff --git a/app/src/main/java/org/thoughtcrime/securesms/service/webrtc/AudioProcessingMethodSelector.kt b/app/src/main/java/org/thoughtcrime/securesms/service/webrtc/AudioProcessingMethodSelector.kt new file mode 100644 index 0000000000..ad58945552 --- /dev/null +++ b/app/src/main/java/org/thoughtcrime/securesms/service/webrtc/AudioProcessingMethodSelector.kt @@ -0,0 +1,33 @@ +package org.thoughtcrime.securesms.service.webrtc + +import android.os.Build +import org.signal.ringrtc.CallManager.AudioProcessingMethod +import org.thoughtcrime.securesms.keyvalue.SignalStore +import org.thoughtcrime.securesms.util.FeatureFlags + +/** + * Utility class to determine which AEC method RingRTC should use. + */ +object AudioProcessingMethodSelector { + + private val hardwareModels: Set by lazy { + FeatureFlags.hardwareAecModels() + .split(",") + .map { it.trim() } + .filter { it.isNotEmpty() } + .toSet() + } + + @JvmStatic + fun get(): AudioProcessingMethod { + if (SignalStore.internalValues().audioProcessingMethod() != AudioProcessingMethod.Default) { + return SignalStore.internalValues().audioProcessingMethod() + } + + return when { + FeatureFlags.forceDefaultAec() -> AudioProcessingMethod.Default + hardwareModels.contains(Build.MODEL) -> AudioProcessingMethod.ForceHardware + else -> AudioProcessingMethod.ForceSoftware + } + } +} diff --git a/app/src/main/java/org/thoughtcrime/securesms/service/webrtc/GroupNetworkUnavailableActionProcessor.java b/app/src/main/java/org/thoughtcrime/securesms/service/webrtc/GroupNetworkUnavailableActionProcessor.java index 0279c116e4..af018c026d 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/service/webrtc/GroupNetworkUnavailableActionProcessor.java +++ b/app/src/main/java/org/thoughtcrime/securesms/service/webrtc/GroupNetworkUnavailableActionProcessor.java @@ -46,7 +46,7 @@ class GroupNetworkUnavailableActionProcessor extends WebRtcActionProcessor { GroupCall groupCall = webRtcInteractor.getCallManager().createGroupCall(groupId, SignalStore.internalValues().groupCallingServer(), new byte[0], - SignalStore.internalValues().audioProcessingMethod(), + AudioProcessingMethodSelector.get(), webRtcInteractor.getGroupCallObserver()); return currentState.builder() diff --git a/app/src/main/java/org/thoughtcrime/securesms/service/webrtc/GroupPreJoinActionProcessor.java b/app/src/main/java/org/thoughtcrime/securesms/service/webrtc/GroupPreJoinActionProcessor.java index 4662e3a2b7..b5ad89d4d3 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/service/webrtc/GroupPreJoinActionProcessor.java +++ b/app/src/main/java/org/thoughtcrime/securesms/service/webrtc/GroupPreJoinActionProcessor.java @@ -45,7 +45,7 @@ public class GroupPreJoinActionProcessor extends GroupActionProcessor { GroupCall groupCall = webRtcInteractor.getCallManager().createGroupCall(groupId, SignalStore.internalValues().groupCallingServer(), new byte[0], - SignalStore.internalValues().audioProcessingMethod(), + AudioProcessingMethodSelector.get(), webRtcInteractor.getGroupCallObserver()); try { diff --git a/app/src/main/java/org/thoughtcrime/securesms/service/webrtc/IncomingCallActionProcessor.java b/app/src/main/java/org/thoughtcrime/securesms/service/webrtc/IncomingCallActionProcessor.java index a1db47e3c2..4a12eb0fe6 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/service/webrtc/IncomingCallActionProcessor.java +++ b/app/src/main/java/org/thoughtcrime/securesms/service/webrtc/IncomingCallActionProcessor.java @@ -68,7 +68,7 @@ public class IncomingCallActionProcessor extends DeviceAwareActionProcessor { webRtcInteractor.getCallManager().proceed(activePeer.getCallId(), context, videoState.getLockableEglBase().require(), - SignalStore.internalValues().audioProcessingMethod(), + AudioProcessingMethodSelector.get(), videoState.requireLocalSink(), callParticipant.getVideoSink(), videoState.requireCamera(), diff --git a/app/src/main/java/org/thoughtcrime/securesms/service/webrtc/IncomingGroupCallActionProcessor.java b/app/src/main/java/org/thoughtcrime/securesms/service/webrtc/IncomingGroupCallActionProcessor.java index fc0e736ab7..ae9c9f47ee 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/service/webrtc/IncomingGroupCallActionProcessor.java +++ b/app/src/main/java/org/thoughtcrime/securesms/service/webrtc/IncomingGroupCallActionProcessor.java @@ -170,7 +170,7 @@ public final class IncomingGroupCallActionProcessor extends DeviceAwareActionPro GroupCall groupCall = webRtcInteractor.getCallManager().createGroupCall(groupId, SignalStore.internalValues().groupCallingServer(), new byte[0], - SignalStore.internalValues().audioProcessingMethod(), + AudioProcessingMethodSelector.get(), webRtcInteractor.getGroupCallObserver()); try { diff --git a/app/src/main/java/org/thoughtcrime/securesms/service/webrtc/OutgoingCallActionProcessor.java b/app/src/main/java/org/thoughtcrime/securesms/service/webrtc/OutgoingCallActionProcessor.java index fd5cad52cd..e48766ec75 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/service/webrtc/OutgoingCallActionProcessor.java +++ b/app/src/main/java/org/thoughtcrime/securesms/service/webrtc/OutgoingCallActionProcessor.java @@ -14,7 +14,6 @@ import org.thoughtcrime.securesms.crypto.IdentityKeyUtil; import org.thoughtcrime.securesms.database.SignalDatabase; import org.thoughtcrime.securesms.events.CallParticipant; import org.thoughtcrime.securesms.events.WebRtcViewModel; -import org.thoughtcrime.securesms.keyvalue.SignalStore; import org.thoughtcrime.securesms.recipients.Recipient; import org.thoughtcrime.securesms.recipients.RecipientUtil; import org.thoughtcrime.securesms.ringrtc.RemotePeer; @@ -106,7 +105,7 @@ public class OutgoingCallActionProcessor extends DeviceAwareActionProcessor { webRtcInteractor.getCallManager().proceed(activePeer.getCallId(), context, videoState.getLockableEglBase().require(), - SignalStore.internalValues().audioProcessingMethod(), + AudioProcessingMethodSelector.get(), videoState.requireLocalSink(), callParticipant.getVideoSink(), videoState.requireCamera(), diff --git a/app/src/main/java/org/thoughtcrime/securesms/util/FeatureFlags.java b/app/src/main/java/org/thoughtcrime/securesms/util/FeatureFlags.java index 057f90440a..1ca33efb4a 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/util/FeatureFlags.java +++ b/app/src/main/java/org/thoughtcrime/securesms/util/FeatureFlags.java @@ -3,6 +3,7 @@ package org.thoughtcrime.securesms.util; import android.text.TextUtils; import androidx.annotation.NonNull; +import androidx.annotation.Nullable; import androidx.annotation.VisibleForTesting; import androidx.annotation.WorkerThread; @@ -89,6 +90,8 @@ public final class FeatureFlags { private static final String CDSH = "android.cdsh"; private static final String VOICE_NOTE_RECORDING_V2 = "android.voiceNoteRecordingV2.2"; private static final String GROUPS_V2_UPDATE_PAGING = "android.groupsv2.updatePaging"; + private static final String HARDWARE_AEC_MODELS = "android.calling.hardwareAecModels"; + private static final String FORCE_DEFAULT_AEC = "android.calling.forceDefaultAec"; /** * We will only store remote values for flags in this set. If you want a flag to be controllable @@ -131,7 +134,9 @@ public final class FeatureFlags { DONOR_BADGES_DISPLAY, CHANGE_NUMBER_ENABLED, VOICE_NOTE_RECORDING_V2, - GROUPS_V2_UPDATE_PAGING + GROUPS_V2_UPDATE_PAGING, + HARDWARE_AEC_MODELS, + FORCE_DEFAULT_AEC ); @VisibleForTesting @@ -187,7 +192,8 @@ public final class FeatureFlags { DONOR_BADGES_DISPLAY, DONATE_MEGAPHONE, VOICE_NOTE_RECORDING_V2, - GROUPS_V2_UPDATE_PAGING + GROUPS_V2_UPDATE_PAGING, + FORCE_DEFAULT_AEC ); /** @@ -443,6 +449,16 @@ public final class FeatureFlags { return getBoolean(GROUPS_V2_UPDATE_PAGING, false); } + /** A comma-separated list of models that should use hardware AEC for calling. */ + public static @NonNull String hardwareAecModels() { + return getString(HARDWARE_AEC_MODELS, ""); + } + + /** Whether or not all devices should be forced into using default AEC for calling. */ + public static boolean forceDefaultAec() { + return getBoolean(FORCE_DEFAULT_AEC, false); + } + /** Only for rendering debug info. */ public static synchronized @NonNull Map getMemoryValues() { return new TreeMap<>(REMOTE_VALUES);