Add new remote config support for calling audio configuration.

This commit is contained in:
Jim Gustafson
2025-04-29 11:37:32 -07:00
committed by Cody Henthorne
parent 2421bbdabb
commit 570a475229
16 changed files with 777 additions and 150 deletions

View File

@@ -567,15 +567,6 @@ class InternalSettingsFragment : DSLSettingsFragment(R.string.preferences__inter
}
)
radioListPref(
title = DSLSettingsText.from("Audio processing method"),
listItems = CallManager.AudioProcessingMethod.entries.map { it.name }.toTypedArray(),
selected = CallManager.AudioProcessingMethod.entries.indexOf(state.callingAudioProcessingMethod),
onSelected = {
viewModel.setInternalCallingAudioProcessingMethod(CallManager.AudioProcessingMethod.entries[it])
}
)
radioListPref(
title = DSLSettingsText.from("Bandwidth mode"),
listItems = CallManager.DataMode.entries.map { it.name }.toTypedArray(),
@@ -594,10 +585,55 @@ class InternalSettingsFragment : DSLSettingsFragment(R.string.preferences__inter
)
switchPref(
title = DSLSettingsText.from("Enable Oboe ADM"),
isChecked = state.callingEnableOboeAdm,
title = DSLSettingsText.from("Set Audio Config:"),
isChecked = state.callingSetAudioConfig,
onClick = {
viewModel.setInternalCallingEnableOboeAdm(!state.callingEnableOboeAdm)
viewModel.setInternalCallingSetAudioConfig(!state.callingSetAudioConfig)
}
)
switchPref(
title = DSLSettingsText.from(" Use Oboe ADM"),
isChecked = state.callingUseOboeAdm,
isEnabled = state.callingSetAudioConfig,
onClick = {
viewModel.setInternalCallingUseOboeAdm(!state.callingUseOboeAdm)
}
)
switchPref(
title = DSLSettingsText.from(" Use Software AEC"),
isChecked = state.callingUseSoftwareAec,
isEnabled = state.callingSetAudioConfig,
onClick = {
viewModel.setInternalCallingUseSoftwareAec(!state.callingUseSoftwareAec)
}
)
switchPref(
title = DSLSettingsText.from(" Use Software NS"),
isChecked = state.callingUseSoftwareNs,
isEnabled = state.callingSetAudioConfig,
onClick = {
viewModel.setInternalCallingUseSoftwareNs(!state.callingUseSoftwareNs)
}
)
switchPref(
title = DSLSettingsText.from(" Use Input Low Latency"),
isChecked = state.callingUseInputLowLatency,
isEnabled = state.callingSetAudioConfig,
onClick = {
viewModel.setInternalCallingUseInputLowLatency(!state.callingUseInputLowLatency)
}
)
switchPref(
title = DSLSettingsText.from(" Use Input Voice Comm"),
isChecked = state.callingUseInputVoiceComm,
isEnabled = state.callingSetAudioConfig,
onClick = {
viewModel.setInternalCallingUseInputVoiceComm(!state.callingUseInputVoiceComm)
}
)

View File

@@ -11,10 +11,14 @@ data class InternalSettingsState(
val allowCensorshipSetting: Boolean,
val forceWebsocketMode: Boolean,
val callingServer: String,
val callingAudioProcessingMethod: CallManager.AudioProcessingMethod,
val callingDataMode: CallManager.DataMode,
val callingDisableTelecom: Boolean,
val callingEnableOboeAdm: Boolean,
val callingSetAudioConfig: Boolean,
val callingUseOboeAdm: Boolean,
val callingUseSoftwareAec: Boolean,
val callingUseSoftwareNs: Boolean,
val callingUseInputLowLatency: Boolean,
val callingUseInputVoiceComm: Boolean,
val useBuiltInEmojiSet: Boolean,
val emojiVersion: EmojiFiles.Version?,
val removeSenderKeyMinimium: Boolean,

View File

@@ -94,11 +94,6 @@ class InternalSettingsViewModel(private val repository: InternalSettingsReposito
refresh()
}
fun setInternalCallingAudioProcessingMethod(method: CallManager.AudioProcessingMethod) {
preferenceDataStore.putInt(InternalValues.CALLING_AUDIO_PROCESSING_METHOD, method.ordinal)
refresh()
}
fun setInternalCallingDataMode(dataMode: CallManager.DataMode) {
preferenceDataStore.putInt(InternalValues.CALLING_DATA_MODE, dataMode.ordinal)
refresh()
@@ -109,8 +104,33 @@ class InternalSettingsViewModel(private val repository: InternalSettingsReposito
refresh()
}
fun setInternalCallingEnableOboeAdm(enabled: Boolean) {
preferenceDataStore.putBoolean(InternalValues.CALLING_ENABLE_OBOE_ADM, enabled)
fun setInternalCallingSetAudioConfig(enabled: Boolean) {
preferenceDataStore.putBoolean(InternalValues.CALLING_SET_AUDIO_CONFIG, enabled)
refresh()
}
fun setInternalCallingUseOboeAdm(enabled: Boolean) {
preferenceDataStore.putBoolean(InternalValues.CALLING_USE_OBOE_ADM, enabled)
refresh()
}
fun setInternalCallingUseSoftwareAec(enabled: Boolean) {
preferenceDataStore.putBoolean(InternalValues.CALLING_USE_SOFTWARE_AEC, enabled)
refresh()
}
fun setInternalCallingUseSoftwareNs(enabled: Boolean) {
preferenceDataStore.putBoolean(InternalValues.CALLING_USE_SOFTWARE_NS, enabled)
refresh()
}
fun setInternalCallingUseInputLowLatency(enabled: Boolean) {
preferenceDataStore.putBoolean(InternalValues.CALLING_USE_INPUT_LOW_LATENCY, enabled)
refresh()
}
fun setInternalCallingUseInputVoiceComm(enabled: Boolean) {
preferenceDataStore.putBoolean(InternalValues.CALLING_USE_INPUT_VOICE_COMM, enabled)
refresh()
}
@@ -152,10 +172,14 @@ class InternalSettingsViewModel(private val repository: InternalSettingsReposito
allowCensorshipSetting = SignalStore.internal.allowChangingCensorshipSetting,
forceWebsocketMode = SignalStore.internal.isWebsocketModeForced,
callingServer = SignalStore.internal.groupCallingServer,
callingAudioProcessingMethod = SignalStore.internal.callingAudioProcessingMethod,
callingDataMode = SignalStore.internal.callingDataMode,
callingDisableTelecom = SignalStore.internal.callingDisableTelecom,
callingEnableOboeAdm = SignalStore.internal.callingEnableOboeAdm,
callingSetAudioConfig = SignalStore.internal.callingSetAudioConfig,
callingUseOboeAdm = SignalStore.internal.callingUseOboeAdm,
callingUseSoftwareAec = SignalStore.internal.callingUseSoftwareAec,
callingUseSoftwareNs = SignalStore.internal.callingUseSoftwareNs,
callingUseInputLowLatency = SignalStore.internal.callingUseInputLowLatency,
callingUseInputVoiceComm = SignalStore.internal.callingUseInputVoiceComm,
useBuiltInEmojiSet = SignalStore.internal.forceBuiltInEmoji,
emojiVersion = null,
removeSenderKeyMinimium = SignalStore.internal.removeSenderKeyMinimum,

View File

@@ -1,6 +1,5 @@
package org.thoughtcrime.securesms.keyvalue
import org.signal.ringrtc.CallManager.AudioProcessingMethod
import org.signal.ringrtc.CallManager.DataMode
import org.thoughtcrime.securesms.BuildConfig
import org.thoughtcrime.securesms.util.Environment.Calling.defaultSfuUrl
@@ -16,10 +15,14 @@ class InternalValues internal constructor(store: KeyValueStore) : SignalStoreVal
const val REMOVE_SENDER_KEY_MINIMUM: String = "internal.remove_sender_key_minimum"
const val DELAY_RESENDS: String = "internal.delay_resends"
const val CALLING_SERVER: String = "internal.calling_server"
const val CALLING_AUDIO_PROCESSING_METHOD: String = "internal.calling_audio_processing_method"
const val CALLING_DATA_MODE: String = "internal.calling_bandwidth_mode"
const val CALLING_DISABLE_TELECOM: String = "internal.calling_disable_telecom"
const val CALLING_ENABLE_OBOE_ADM: String = "internal.calling_enable_oboe_adm"
const val CALLING_SET_AUDIO_CONFIG: String = "internal.calling_set_audio_config"
const val CALLING_USE_OBOE_ADM: String = "internal.calling_use_oboe_adm"
const val CALLING_USE_SOFTWARE_AEC: String = "internal.calling_use_software_aec"
const val CALLING_USE_SOFTWARE_NS: String = "internal.calling_use_software_ns"
const val CALLING_USE_INPUT_LOW_LATENCY: String = "internal.calling_use_input_low_latency"
const val CALLING_USE_INPUT_VOICE_COMM: String = "internal.calling_use_input_voice_comm"
const val SHAKE_TO_REPORT: String = "internal.shake_to_report"
const val DISABLE_STORAGE_SERVICE: String = "internal.disable_storage_service"
const val FORCE_WEBSOCKET_MODE: String = "internal.force_websocket_mode"
@@ -110,19 +113,6 @@ class InternalValues internal constructor(store: KeyValueStore) : SignalStoreVal
}
set(value) = putString(CALLING_SERVER, value)
/**
* Setting to override the default handling of hardware/software AEC.
*/
val callingAudioProcessingMethod: AudioProcessingMethod
get() {
return if (RemoteConfig.internalUser) {
val entryIndex = getInteger(CALLING_AUDIO_PROCESSING_METHOD, AudioProcessingMethod.Default.ordinal)
AudioProcessingMethod.entries[entryIndex]
} else {
AudioProcessingMethod.Default
}
}
/**
* Setting to override the default calling bandwidth mode.
*/
@@ -144,9 +134,34 @@ class InternalValues internal constructor(store: KeyValueStore) : SignalStoreVal
var callingDisableTelecom by booleanValue(CALLING_DISABLE_TELECOM, true).falseForExternalUsers()
/**
* Whether or not the Oboe ADM is used.
* Whether or not to override the audio settings from the remote configuration.
*/
var callingEnableOboeAdm by booleanValue(CALLING_ENABLE_OBOE_ADM, true).falseForExternalUsers()
var callingSetAudioConfig by booleanValue(CALLING_SET_AUDIO_CONFIG, true).falseForExternalUsers()
/**
* If overriding the audio settings, use the Oboe ADM or not.
*/
var callingUseOboeAdm by booleanValue(CALLING_USE_OBOE_ADM, true).defaultForExternalUsers()
/**
* If overriding the audio settings, use the Software AEC or not.
*/
var callingUseSoftwareAec by booleanValue(CALLING_USE_SOFTWARE_AEC, false).defaultForExternalUsers()
/**
* If overriding the audio settings, use the Software NS or not.
*/
var callingUseSoftwareNs by booleanValue(CALLING_USE_SOFTWARE_NS, false).defaultForExternalUsers()
/**
* If overriding the audio settings, use Low Latency for the input or not.
*/
var callingUseInputLowLatency by booleanValue(CALLING_USE_INPUT_LOW_LATENCY, true).defaultForExternalUsers()
/**
* If overriding the audio settings, use Voice Comm for the input or not.
*/
var callingUseInputVoiceComm by booleanValue(CALLING_USE_INPUT_VOICE_COMM, true).defaultForExternalUsers()
/**
* Whether or not the system is forced to be in 'websocket mode', where FCM is ignored and we use a foreground service to keep the app alive.

View File

@@ -66,8 +66,7 @@ class CallLinkPreJoinActionProcessor(
callLink.credentials.adminPassBytes,
ByteArray(0),
AUDIO_LEVELS_INTERVAL,
RingRtcDynamicConfiguration.getAudioProcessingMethod(),
RingRtcDynamicConfiguration.shouldUseOboeAdm(),
RingRtcDynamicConfiguration.getAudioConfig(),
webRtcInteractor.groupCallObserver
)
} catch (e: InvalidInputException) {

View File

@@ -52,8 +52,7 @@ public class GroupNetworkUnavailableActionProcessor extends WebRtcActionProcesso
SignalStore.internal().getGroupCallingServer(),
new byte[0],
null,
RingRtcDynamicConfiguration.getAudioProcessingMethod(),
RingRtcDynamicConfiguration.shouldUseOboeAdm(),
RingRtcDynamicConfiguration.getAudioConfig(),
webRtcInteractor.getGroupCallObserver());
if (groupCall == null) {

View File

@@ -50,8 +50,7 @@ public class GroupPreJoinActionProcessor extends GroupActionProcessor {
SignalStore.internal().getGroupCallingServer(),
new byte[0],
AUDIO_LEVELS_INTERVAL,
RingRtcDynamicConfiguration.getAudioProcessingMethod(),
RingRtcDynamicConfiguration.shouldUseOboeAdm(),
RingRtcDynamicConfiguration.getAudioConfig(),
webRtcInteractor.getGroupCallObserver());
if (groupCall == null) {

View File

@@ -103,8 +103,7 @@ public class IncomingCallActionProcessor extends DeviceAwareActionProcessor {
webRtcInteractor.getCallManager().proceed(activePeer.getCallId(),
context,
videoState.getLockableEglBase().require(),
RingRtcDynamicConfiguration.getAudioProcessingMethod(),
RingRtcDynamicConfiguration.shouldUseOboeAdm(),
RingRtcDynamicConfiguration.getAudioConfig(),
videoState.requireLocalSink(),
callParticipant.getVideoSink(),
videoState.requireCamera(),

View File

@@ -186,8 +186,7 @@ public final class IncomingGroupCallActionProcessor extends DeviceAwareActionPro
SignalStore.internal().getGroupCallingServer(),
new byte[0],
AUDIO_LEVELS_INTERVAL,
RingRtcDynamicConfiguration.getAudioProcessingMethod(),
RingRtcDynamicConfiguration.shouldUseOboeAdm(),
RingRtcDynamicConfiguration.getAudioConfig(),
webRtcInteractor.getGroupCallObserver());
if (groupCall == null) {

View File

@@ -152,8 +152,7 @@ public class OutgoingCallActionProcessor extends DeviceAwareActionProcessor {
webRtcInteractor.getCallManager().proceed(activePeer.getCallId(),
context,
videoState.getLockableEglBase().require(),
RingRtcDynamicConfiguration.getAudioProcessingMethod(),
RingRtcDynamicConfiguration.shouldUseOboeAdm(),
RingRtcDynamicConfiguration.getAudioConfig(),
videoState.requireLocalSink(),
callParticipant.getVideoSink(),
videoState.requireCamera(),

View File

@@ -2,73 +2,42 @@ package org.thoughtcrime.securesms.service.webrtc
import android.os.Build
import org.signal.core.util.asListContains
import org.signal.ringrtc.CallManager.AudioProcessingMethod
import org.signal.ringrtc.AudioConfig
import org.thoughtcrime.securesms.keyvalue.SignalStore
import org.thoughtcrime.securesms.util.RemoteConfig
import org.thoughtcrime.securesms.webrtc.audio.AudioDeviceConfig
/**
* Utility class to determine which AEC method RingRTC should use.
* Utility class to determine the audio configuration that RingRTC should use.
*/
object RingRtcDynamicConfiguration {
private val KNOWN_ISSUE_ROMS = "(lineage|calyxos)".toRegex(RegexOption.IGNORE_CASE)
@JvmStatic
fun getAudioProcessingMethod(): AudioProcessingMethod {
if (SignalStore.internal.callingAudioProcessingMethod != AudioProcessingMethod.Default) {
return SignalStore.internal.callingAudioProcessingMethod
}
return if (shouldUseOboeAdm()) {
when {
shouldUseSoftwareAecForOboe() || isKnownFaultyHardwareImplementation() -> AudioProcessingMethod.ForceSoftwareAec3
else -> AudioProcessingMethod.ForceHardware
}
} else {
when {
isHardwareBlocklisted() || isKnownFaultyHardwareImplementation() -> AudioProcessingMethod.ForceSoftwareAec3
isSoftwareBlocklisted() -> AudioProcessingMethod.ForceHardware
Build.VERSION.SDK_INT < 29 && RemoteConfig.useHardwareAecIfOlderThanApi29 -> AudioProcessingMethod.ForceHardware
Build.VERSION.SDK_INT < 29 -> AudioProcessingMethod.ForceSoftwareAec3
else -> AudioProcessingMethod.ForceHardware
}
}
}
private var lastFetchTime: Long = 0
fun isTelecomAllowedForDevice(): Boolean {
return RemoteConfig.telecomManufacturerAllowList.lowercase().asListContains(Build.MANUFACTURER.lowercase()) &&
!RemoteConfig.telecomModelBlocklist.lowercase().asListContains(Build.MODEL.lowercase())
}
private fun isHardwareBlocklisted(): Boolean {
return RemoteConfig.hardwareAecBlocklistModels.asListContains(Build.MODEL)
}
private fun isKnownFaultyHardwareImplementation(): Boolean {
return Build.PRODUCT.contains(KNOWN_ISSUE_ROMS) ||
Build.DISPLAY.contains(KNOWN_ISSUE_ROMS) ||
Build.HOST.contains(KNOWN_ISSUE_ROMS)
}
private fun isSoftwareBlocklisted(): Boolean {
return RemoteConfig.softwareAecBlocklistModels.asListContains(Build.MODEL)
}
@JvmStatic
fun shouldUseOboeAdm(): Boolean {
if (RemoteConfig.internalUser) {
return SignalStore.internal.callingEnableOboeAdm
fun getAudioConfig(): AudioConfig {
if (RemoteConfig.internalUser && SignalStore.internal.callingSetAudioConfig) {
// Use the internal audio settings.
var audioConfig = AudioConfig()
audioConfig.useOboe = SignalStore.internal.callingUseOboeAdm
audioConfig.useSoftwareAec = SignalStore.internal.callingUseSoftwareAec
audioConfig.useSoftwareNs = SignalStore.internal.callingUseSoftwareNs
audioConfig.useInputLowLatency = SignalStore.internal.callingUseInputLowLatency
audioConfig.useInputVoiceComm = SignalStore.internal.callingUseInputVoiceComm
return audioConfig
}
// For now, only allow the Oboe ADM to be used for custom ROMS.
return RemoteConfig.oboeDeployment && isKnownFaultyHardwareImplementation() && !shouldUseJavaAdm()
}
private fun shouldUseJavaAdm(): Boolean {
return RemoteConfig.useJavaAdmModels.asListContains(Build.MODEL)
}
private fun shouldUseSoftwareAecForOboe(): Boolean {
return RemoteConfig.useSoftwareAecForOboeModels.asListContains(Build.MODEL)
// Use the audio settings provided by the remote configuration.
if (lastFetchTime != SignalStore.remoteConfig.lastFetchTime) {
// The remote config has been updated.
AudioDeviceConfig.refresh()
lastFetchTime = SignalStore.remoteConfig.lastFetchTime
}
return AudioDeviceConfig.getCurrentConfig()
}
}

View File

@@ -694,37 +694,9 @@ object RemoteConfig {
hotSwappable = false
)
/** A comma-separated list of models that should *not* use hardware AEC for calling. */
val hardwareAecBlocklistModels: String by remoteString(
key = "android.calling.hardwareAecBlockList",
defaultValue = "",
hotSwappable = true
)
/** A comma-separated list of models that should *not* use software AEC for calling. */
val softwareAecBlocklistModels: String by remoteString(
key = "android.calling.softwareAecBlockList",
defaultValue = "",
hotSwappable = true
)
/** Whether the Oboe ADM should be used or not. */
val oboeDeployment: Boolean by remoteBoolean(
key = "android.calling.oboeDeployment",
defaultValue = false,
hotSwappable = false
)
/** A comma-separated list of models that should use the Java ADM instead of the Oboe ADM. */
val useJavaAdmModels: String by remoteString(
key = "android.calling.useJavaAdmList",
defaultValue = "",
hotSwappable = true
)
/** A comma-separated list of models that should use software AEC for calling with the Oboe ADM. */
val useSoftwareAecForOboeModels: String by remoteString(
key = "android.calling.useSoftwareAecForOboe",
/** A json string representing rules necessary to build an audio configuration for a device. */
val callingAudioDeviceConfig: String by remoteString(
key = "android.calling.audioDeviceConfig",
defaultValue = "",
hotSwappable = true
)
@@ -757,13 +729,6 @@ object RemoteConfig {
hotSwappable = false
)
/** Whether or not hardware AEC should be used for calling on devices older than API 29. */
val useHardwareAecIfOlderThanApi29: Boolean by remoteBoolean(
key = "android.calling.useHardwareAecIfOlderThanApi29",
defaultValue = false,
hotSwappable = true
)
/** Prefetch count for stories from a given user. */
val storiesAutoDownloadMaximum: Int by remoteInt(
key = "android.stories.autoDownloadMaximum",

View File

@@ -0,0 +1,191 @@
/*
* Copyright 2025 Signal Messenger, LLC
* SPDX-License-Identifier: AGPL-3.0-only
*/
package org.thoughtcrime.securesms.webrtc.audio
import android.content.pm.PackageManager
import android.media.audiofx.AcousticEchoCanceler
import android.media.audiofx.NoiseSuppressor
import android.os.Build
import androidx.annotation.VisibleForTesting
import com.fasterxml.jackson.annotation.JsonProperty
import org.signal.core.util.logging.Log
import org.signal.ringrtc.AudioConfig
import org.thoughtcrime.securesms.dependencies.AppDependencies
import org.thoughtcrime.securesms.util.JsonUtils
import org.thoughtcrime.securesms.util.RemoteConfig
import java.io.IOException
/**
* Remote config for audio devices to allow for more targeted configurations
*/
object AudioDeviceConfig {
private val TAG = Log.tag(AudioDeviceConfig::class.java)
private val CUSTOM_ROMS = "(lineage|calyxos)".toRegex(RegexOption.IGNORE_CASE)
private var currentConfig: AudioConfig? = null
@Synchronized
fun getCurrentConfig(): AudioConfig {
if (currentConfig == null) {
currentConfig = computeConfig()
}
return currentConfig!!
}
@Synchronized
fun refresh() {
currentConfig = computeConfig()
}
/**
* Defines rules that can be filtered and applied to a specific device based on the provided criteria
* @param target the targets to apply the rule to; can be specific devices and/or device classes
* @param settings the audio settings that the rule will set
* @param final forces an exit from the rule matching if true
*/
data class Rule(
@JsonProperty("target") val target: Target = Target(),
@JsonProperty("settings") val settings: Settings = Settings(),
@JsonProperty("final") val isFinal: Boolean = false
)
/**
* Defines the target devices for which a given rule should be applied
* @param include a list of device models to include; wildcard suffixes are supported
* @param exclude a list of device models to exclude; wildcard suffixes are supported
* @param custom whether or not a custom (non-AOSP) ROM should be considered
*/
data class Target(
@JsonProperty("include") val include: List<String> = emptyList(),
@JsonProperty("exclude") val exclude: List<String> = emptyList(),
@JsonProperty("custom") val isCustomRom: Boolean? = null
)
/**
* Defines the audio settings a rule can override, if specified
* @param oboe if true, use the Oboe ADM instead of the Java ADM
* @param softwareAec if true, use software AEC instead of the platform provided AEC
* @param softwareNs if true, use software NS instead of the platform provided NS
* @param inLowLatency if true, use low latency setting for input
* @param inVoiceComm if true, use voice communications setting for input
*/
data class Settings(
@JsonProperty("oboe") val oboe: Boolean? = null,
@JsonProperty("softwareAec") val softwareAec: Boolean? = null,
@JsonProperty("softwareNs") val softwareNs: Boolean? = null,
@JsonProperty("inLowLatency") val inLowLatency: Boolean? = null,
@JsonProperty("inVoiceComm") val inVoiceComm: Boolean? = null
)
@VisibleForTesting
fun computeConfig(): AudioConfig {
// Initialize the config with the default.
val config = AudioConfig()
val serialized = RemoteConfig.callingAudioDeviceConfig
if (serialized.isBlank()) {
Log.w(TAG, "No RemoteConfig.callingAudioDeviceConfig found! Using default.")
return applyOverrides(config)
}
val rules: List<Rule> = try {
JsonUtils.fromJsonArray(serialized, Rule::class.java)
} catch (e: IOException) {
Log.w(TAG, "Failed to parse callingAudioDeviceConfig json! Using default. " + e.message)
return applyOverrides(config)
}
for (rule in rules) {
if (matchesDevice(rule.target)) {
applySettings(rule.settings, config)
if (rule.isFinal) {
return applyOverrides(config)
}
}
}
return applyOverrides(config)
}
// Check if the target should apply to the device the code is running on.
private fun matchesDevice(target: Target): Boolean {
// Make sure the device is in the include list, if the list is present.
if (target.include.isNotEmpty() &&
!target.include.any { matchesModel(it) }
) {
return false
}
// If the device is in the exclude list, don't match it.
if (target.exclude.isNotEmpty() &&
target.exclude.any { matchesModel(it) }
) {
return false
}
// Check if the device needs to be a custom ROM or not, if the constraint is present.
if (target.isCustomRom != null &&
target.isCustomRom != isCustomRom()
) {
return false
}
return true
}
private fun matchesModel(model: String): Boolean {
return if (model.endsWith("*")) {
Build.MODEL.startsWith(model.substring(0, model.length - 1))
} else {
Build.MODEL.equals(model, ignoreCase = true)
}
}
private fun isCustomRom(): Boolean {
return Build.PRODUCT.contains(CUSTOM_ROMS) ||
Build.DISPLAY.contains(CUSTOM_ROMS) ||
Build.HOST.contains(CUSTOM_ROMS)
}
private fun applySettings(settings: Settings, config: AudioConfig) {
settings.oboe?.let { config.useOboe = it }
settings.softwareAec?.let { config.useSoftwareAec = it }
settings.softwareNs?.let { config.useSoftwareNs = it }
settings.inLowLatency?.let { config.useInputLowLatency = it }
settings.inVoiceComm?.let { config.useInputVoiceComm = it }
}
private fun applyOverrides(config: AudioConfig): AudioConfig {
if (!isCustomRom() && Build.VERSION.SDK_INT < 29) {
Log.w(TAG, "Device is less than API level 29, forcing software AEC/NS!")
config.useSoftwareAec = true
config.useSoftwareNs = true
}
if (!config.useSoftwareAec && !AcousticEchoCanceler.isAvailable()) {
Log.w(TAG, "Device does not implement AcousticEchoCanceler, overriding config!")
config.useSoftwareAec = true
}
if (!config.useSoftwareNs && !NoiseSuppressor.isAvailable()) {
Log.w(TAG, "Device does not implement NoiseSuppressor, overriding config!")
config.useSoftwareNs = true
}
val context = AppDependencies.application.applicationContext
if (config.useInputLowLatency &&
!context.packageManager.hasSystemFeature(PackageManager.FEATURE_AUDIO_LOW_LATENCY)
) {
Log.w(TAG, "Device does not implement FEATURE_AUDIO_LOW_LATENCY, overriding config!")
config.useInputLowLatency = false
}
return config
}
}