mirror of
https://github.com/signalapp/Signal-Android.git
synced 2026-02-25 20:23:19 +00:00
Add new remote config support for calling audio configuration.
This commit is contained in:
committed by
Cody Henthorne
parent
2421bbdabb
commit
570a475229
@@ -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)
|
||||
}
|
||||
)
|
||||
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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.
|
||||
|
||||
@@ -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) {
|
||||
|
||||
@@ -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) {
|
||||
|
||||
@@ -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) {
|
||||
|
||||
@@ -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(),
|
||||
|
||||
@@ -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) {
|
||||
|
||||
@@ -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(),
|
||||
|
||||
@@ -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()
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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",
|
||||
|
||||
@@ -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
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user