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);