kopia lustrzana https://github.com/ryukoposting/Signal-Android
Implement new feature flag strategy for AEC selection.
rodzic
d935d1deca
commit
3ac63cc59d
|
@ -1,6 +1,7 @@
|
||||||
package org.thoughtcrime.securesms.service.webrtc
|
package org.thoughtcrime.securesms.service.webrtc
|
||||||
|
|
||||||
import android.os.Build
|
import android.os.Build
|
||||||
|
import androidx.annotation.VisibleForTesting
|
||||||
import org.signal.ringrtc.CallManager.AudioProcessingMethod
|
import org.signal.ringrtc.CallManager.AudioProcessingMethod
|
||||||
import org.thoughtcrime.securesms.keyvalue.SignalStore
|
import org.thoughtcrime.securesms.keyvalue.SignalStore
|
||||||
import org.thoughtcrime.securesms.util.FeatureFlags
|
import org.thoughtcrime.securesms.util.FeatureFlags
|
||||||
|
@ -10,24 +11,47 @@ import org.thoughtcrime.securesms.util.FeatureFlags
|
||||||
*/
|
*/
|
||||||
object AudioProcessingMethodSelector {
|
object AudioProcessingMethodSelector {
|
||||||
|
|
||||||
private val hardwareModels: Set<String> by lazy {
|
|
||||||
FeatureFlags.hardwareAecModels()
|
|
||||||
.split(",")
|
|
||||||
.map { it.trim() }
|
|
||||||
.filter { it.isNotEmpty() }
|
|
||||||
.toSet()
|
|
||||||
}
|
|
||||||
|
|
||||||
@JvmStatic
|
@JvmStatic
|
||||||
fun get(): AudioProcessingMethod {
|
fun get(): AudioProcessingMethod {
|
||||||
if (SignalStore.internalValues().audioProcessingMethod() != AudioProcessingMethod.Default) {
|
if (SignalStore.internalValues().audioProcessingMethod() != AudioProcessingMethod.Default) {
|
||||||
return SignalStore.internalValues().audioProcessingMethod()
|
return SignalStore.internalValues().audioProcessingMethod()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
val useAec3: Boolean = FeatureFlags.useAec3()
|
||||||
|
|
||||||
return when {
|
return when {
|
||||||
FeatureFlags.forceDefaultAec() -> AudioProcessingMethod.Default
|
isHardwareBlocklisted() && useAec3 -> AudioProcessingMethod.ForceSoftwareAec3
|
||||||
hardwareModels.contains(Build.MODEL) -> AudioProcessingMethod.ForceHardware
|
isHardwareBlocklisted() -> AudioProcessingMethod.ForceSoftwareAecM
|
||||||
else -> AudioProcessingMethod.ForceSoftwareAecM
|
isSoftwareBlocklisted() -> AudioProcessingMethod.ForceHardware
|
||||||
|
Build.VERSION.SDK_INT < 29 && FeatureFlags.useHardwareAecIfOlderThanApi29() -> AudioProcessingMethod.ForceHardware
|
||||||
|
Build.VERSION.SDK_INT < 29 && useAec3 -> AudioProcessingMethod.ForceSoftwareAec3
|
||||||
|
Build.VERSION.SDK_INT < 29 -> AudioProcessingMethod.ForceSoftwareAecM
|
||||||
|
else -> AudioProcessingMethod.ForceHardware
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private fun isHardwareBlocklisted(): Boolean {
|
||||||
|
return modelInList(Build.MODEL, FeatureFlags.hardwareAecBlocklistModels())
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun isSoftwareBlocklisted(): Boolean {
|
||||||
|
return modelInList(Build.MODEL, FeatureFlags.softwareAecBlocklistModels())
|
||||||
|
}
|
||||||
|
|
||||||
|
@VisibleForTesting
|
||||||
|
fun modelInList(model: String, serializedList: String): Boolean {
|
||||||
|
val items: List<String> = serializedList
|
||||||
|
.split(",")
|
||||||
|
.map { it.trim() }
|
||||||
|
.filter { it.isNotEmpty() }
|
||||||
|
.toList()
|
||||||
|
|
||||||
|
val exactMatches = items.filter { it.last() != '*' }
|
||||||
|
val prefixMatches = items.filter { it.last() == '*' }
|
||||||
|
|
||||||
|
return exactMatches.contains(model) ||
|
||||||
|
prefixMatches
|
||||||
|
.map { it.substring(0, it.length - 1) }
|
||||||
|
.any { model.startsWith(it) }
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -88,11 +88,13 @@ public final class FeatureFlags {
|
||||||
private static final String DONOR_BADGES = "android.donorBadges.6";
|
private static final String DONOR_BADGES = "android.donorBadges.6";
|
||||||
private static final String DONOR_BADGES_DISPLAY = "android.donorBadges.display.4";
|
private static final String DONOR_BADGES_DISPLAY = "android.donorBadges.display.4";
|
||||||
private static final String CDSH = "android.cdsh";
|
private static final String CDSH = "android.cdsh";
|
||||||
private static final String HARDWARE_AEC_MODELS = "android.calling.hardwareAecModels";
|
|
||||||
private static final String FORCE_DEFAULT_AEC = "android.calling.forceDefaultAec";
|
|
||||||
private static final String STORIES = "android.stories";
|
private static final String STORIES = "android.stories";
|
||||||
private static final String STORIES_TEXT_FUNCTIONS = "android.stories.text.functions";
|
private static final String STORIES_TEXT_FUNCTIONS = "android.stories.text.functions";
|
||||||
private static final String STORIES_TEXT_POSTS = "android.stories.text.posts";
|
private static final String STORIES_TEXT_POSTS = "android.stories.text.posts";
|
||||||
|
private static final String HARDWARE_AEC_BLOCKLIST_MODELS = "android.calling.hardwareAecBlockList";
|
||||||
|
private static final String SOFTWARE_AEC_BLOCKLIST_MODELS = "android.calling.softwareAecBlockList";
|
||||||
|
private static final String USE_HARDWARE_AEC_IF_OLD = "android.calling.useHardwareAecIfOlderThanApi29";
|
||||||
|
private static final String USE_AEC3 = "android.calling.useAec3";
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* We will only store remote values for flags in this set. If you want a flag to be controllable
|
* We will only store remote values for flags in this set. If you want a flag to be controllable
|
||||||
|
@ -133,11 +135,13 @@ public final class FeatureFlags {
|
||||||
SENDER_KEY_MAX_AGE,
|
SENDER_KEY_MAX_AGE,
|
||||||
DONOR_BADGES,
|
DONOR_BADGES,
|
||||||
DONOR_BADGES_DISPLAY,
|
DONOR_BADGES_DISPLAY,
|
||||||
HARDWARE_AEC_MODELS,
|
|
||||||
FORCE_DEFAULT_AEC,
|
|
||||||
STORIES,
|
STORIES,
|
||||||
STORIES_TEXT_FUNCTIONS,
|
STORIES_TEXT_FUNCTIONS,
|
||||||
STORIES_TEXT_POSTS
|
STORIES_TEXT_POSTS,
|
||||||
|
HARDWARE_AEC_BLOCKLIST_MODELS,
|
||||||
|
SOFTWARE_AEC_BLOCKLIST_MODELS,
|
||||||
|
USE_HARDWARE_AEC_IF_OLD,
|
||||||
|
USE_AEC3
|
||||||
);
|
);
|
||||||
|
|
||||||
@VisibleForTesting
|
@VisibleForTesting
|
||||||
|
@ -192,7 +196,10 @@ public final class FeatureFlags {
|
||||||
SENDER_KEY_MAX_AGE,
|
SENDER_KEY_MAX_AGE,
|
||||||
DONOR_BADGES_DISPLAY,
|
DONOR_BADGES_DISPLAY,
|
||||||
DONATE_MEGAPHONE,
|
DONATE_MEGAPHONE,
|
||||||
FORCE_DEFAULT_AEC
|
HARDWARE_AEC_BLOCKLIST_MODELS,
|
||||||
|
SOFTWARE_AEC_BLOCKLIST_MODELS,
|
||||||
|
USE_HARDWARE_AEC_IF_OLD,
|
||||||
|
USE_AEC3
|
||||||
);
|
);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -454,14 +461,24 @@ public final class FeatureFlags {
|
||||||
return Environment.IS_STAGING && getBoolean(CDSH, false);
|
return Environment.IS_STAGING && getBoolean(CDSH, false);
|
||||||
}
|
}
|
||||||
|
|
||||||
/** A comma-separated list of models that should use hardware AEC for calling. */
|
/** A comma-separated list of models that should *not* use hardware AEC for calling. */
|
||||||
public static @NonNull String hardwareAecModels() {
|
public static @NonNull String hardwareAecBlocklistModels() {
|
||||||
return getString(HARDWARE_AEC_MODELS, "");
|
return getString(HARDWARE_AEC_BLOCKLIST_MODELS, "");
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Whether or not all devices should be forced into using default AEC for calling. */
|
/** A comma-separated list of models that should *not* use software AEC for calling. */
|
||||||
public static boolean forceDefaultAec() {
|
public static @NonNull String softwareAecBlocklistModels() {
|
||||||
return getBoolean(FORCE_DEFAULT_AEC, false);
|
return getString(SOFTWARE_AEC_BLOCKLIST_MODELS, "");
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Whether or not hardware AEC should be used for calling on devices older than API 29. */
|
||||||
|
public static boolean useHardwareAecIfOlderThanApi29() {
|
||||||
|
return getBoolean(USE_HARDWARE_AEC_IF_OLD, false);
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Whether or not {@link org.signal.ringrtc.CallManager.AudioProcessingMethod#ForceSoftwareAec3} can be used */
|
||||||
|
public static boolean useAec3() {
|
||||||
|
return getBoolean(USE_AEC3, true);
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Only for rendering debug info. */
|
/** Only for rendering debug info. */
|
||||||
|
|
|
@ -0,0 +1,39 @@
|
||||||
|
package org.thoughtcrime.securesms.service.webrtc
|
||||||
|
|
||||||
|
import org.junit.Assert.assertEquals
|
||||||
|
import org.junit.Test
|
||||||
|
import org.junit.runner.RunWith
|
||||||
|
import org.junit.runners.Parameterized
|
||||||
|
|
||||||
|
@RunWith(Parameterized::class)
|
||||||
|
class AudioProcessingMethodSelectorTest_modelInList(
|
||||||
|
private val model: String,
|
||||||
|
private val serializedList: String,
|
||||||
|
private val expected: Boolean
|
||||||
|
) {
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun testModelInList() {
|
||||||
|
val actual = AudioProcessingMethodSelector.modelInList(model, serializedList)
|
||||||
|
assertEquals(expected, actual)
|
||||||
|
}
|
||||||
|
|
||||||
|
companion object {
|
||||||
|
@JvmStatic
|
||||||
|
@Parameterized.Parameters(name = "{index}: modelInList(model={0}, list={1})={2}")
|
||||||
|
fun data(): List<Array<Any>> {
|
||||||
|
return listOf<Array<Any>>(
|
||||||
|
arrayOf("a", "a", true),
|
||||||
|
arrayOf("a", "a,b", true),
|
||||||
|
arrayOf("a", "c,a,b", true),
|
||||||
|
arrayOf("ab", "a*", true),
|
||||||
|
arrayOf("ab", "c,a*,b", true),
|
||||||
|
arrayOf("abc", "c,ab*,b", true),
|
||||||
|
|
||||||
|
arrayOf("a", "b", false),
|
||||||
|
arrayOf("a", "abc", false),
|
||||||
|
arrayOf("b", "a*", false),
|
||||||
|
).toList()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
Ładowanie…
Reference in New Issue