1 /* <lambda>null2 * Copyright (C) 2024 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17 package com.android.settingslib.volume.data.repository 18 19 import android.content.ContentResolver 20 import android.database.ContentObserver 21 import android.media.AudioDeviceInfo 22 import android.media.AudioManager 23 import android.media.AudioManager.AudioDeviceCategory 24 import android.media.AudioManager.OnCommunicationDeviceChangedListener 25 import android.media.IVolumeController 26 import android.provider.Settings 27 import android.util.Log 28 import android.view.KeyEvent 29 import androidx.concurrent.futures.DirectExecutor 30 import com.android.internal.util.ConcurrentUtils 31 import com.android.settingslib.volume.data.model.VolumeControllerEvent 32 import com.android.settingslib.volume.shared.AudioLogger 33 import com.android.settingslib.volume.shared.AudioManagerEventsReceiver 34 import com.android.settingslib.volume.shared.model.AudioManagerEvent 35 import com.android.settingslib.volume.shared.model.AudioStream 36 import com.android.settingslib.volume.shared.model.AudioStreamModel 37 import com.android.settingslib.volume.shared.model.RingerMode 38 import com.android.settingslib.volume.shared.model.StreamAudioManagerEvent 39 import kotlin.coroutines.CoroutineContext 40 import kotlinx.coroutines.CoroutineScope 41 import kotlinx.coroutines.channels.awaitClose 42 import kotlinx.coroutines.flow.Flow 43 import kotlinx.coroutines.flow.MutableSharedFlow 44 import kotlinx.coroutines.flow.SharingStarted 45 import kotlinx.coroutines.flow.StateFlow 46 import kotlinx.coroutines.flow.asSharedFlow 47 import kotlinx.coroutines.flow.callbackFlow 48 import kotlinx.coroutines.flow.conflate 49 import kotlinx.coroutines.flow.distinctUntilChanged 50 import kotlinx.coroutines.flow.emptyFlow 51 import kotlinx.coroutines.flow.filter 52 import kotlinx.coroutines.flow.filterIsInstance 53 import kotlinx.coroutines.flow.filterNotNull 54 import kotlinx.coroutines.flow.flowOn 55 import kotlinx.coroutines.flow.map 56 import kotlinx.coroutines.flow.merge 57 import kotlinx.coroutines.flow.onEach 58 import kotlinx.coroutines.flow.onStart 59 import kotlinx.coroutines.flow.stateIn 60 import kotlinx.coroutines.launch 61 import kotlinx.coroutines.withContext 62 63 /** Provides audio streams state and managing functionality. */ 64 interface AudioRepository { 65 66 /** Current [AudioManager.getMode]. */ 67 val mode: StateFlow<Int> 68 69 /** 70 * Ringtone mode. 71 * 72 * @see AudioManager.getRingerModeInternal 73 */ 74 val ringerMode: StateFlow<RingerMode> 75 76 /** 77 * Communication device. Emits null when there is no communication device available. 78 * 79 * @see AudioDeviceInfo.getType 80 */ 81 val communicationDevice: StateFlow<AudioDeviceInfo?> 82 83 /** Events from [AudioManager.setVolumeController] */ 84 val volumeControllerEvents: Flow<VolumeControllerEvent> 85 86 fun init() 87 88 /** State of the [AudioStream]. */ 89 fun getAudioStream(audioStream: AudioStream): Flow<AudioStreamModel> 90 91 /** Returns the last audible volume before stream was muted. */ 92 suspend fun getLastAudibleVolume(audioStream: AudioStream): Int 93 94 suspend fun setVolume(audioStream: AudioStream, volume: Int) 95 96 /** 97 * Mutes and un-mutes [audioStream]. Returns true when the state changes and false the 98 * otherwise. 99 */ 100 suspend fun setMuted(audioStream: AudioStream, isMuted: Boolean): Boolean 101 102 suspend fun setRingerModeInternal(audioStream: AudioStream, mode: RingerMode) 103 104 /** Gets audio device category. */ 105 @AudioDeviceCategory suspend fun getBluetoothAudioDeviceCategory(bluetoothAddress: String): Int 106 107 suspend fun notifyVolumeControllerVisible(isVisible: Boolean) 108 109 fun dispatchMediaKeyEvent(event: KeyEvent) 110 } 111 112 class AudioRepositoryImpl( 113 private val audioManagerEventsReceiver: AudioManagerEventsReceiver, 114 private val audioManager: AudioManager, 115 private val contentResolver: ContentResolver, 116 private val backgroundCoroutineContext: CoroutineContext, 117 private val coroutineScope: CoroutineScope, 118 private val logger: AudioLogger, 119 shouldUseVolumeController: Boolean, 120 ) : AudioRepository { 121 122 private val volumeController = ProducingVolumeController() 123 private val streamSettingNames: Map<AudioStream, String> = 124 mapOf( 125 AudioStream(AudioManager.STREAM_VOICE_CALL) to Settings.System.VOLUME_VOICE, 126 AudioStream(AudioManager.STREAM_SYSTEM) to Settings.System.VOLUME_SYSTEM, 127 AudioStream(AudioManager.STREAM_RING) to Settings.System.VOLUME_RING, 128 AudioStream(AudioManager.STREAM_MUSIC) to Settings.System.VOLUME_MUSIC, 129 AudioStream(AudioManager.STREAM_ALARM) to Settings.System.VOLUME_ALARM, 130 AudioStream(AudioManager.STREAM_NOTIFICATION) to Settings.System.VOLUME_NOTIFICATION, 131 AudioStream(AudioManager.STREAM_BLUETOOTH_SCO) to Settings.System.VOLUME_BLUETOOTH_SCO, 132 AudioStream(AudioManager.STREAM_ACCESSIBILITY) to Settings.System.VOLUME_ACCESSIBILITY, 133 AudioStream(AudioManager.STREAM_ASSISTANT) to Settings.System.VOLUME_ASSISTANT, 134 ) 135 136 override val volumeControllerEvents: Flow<VolumeControllerEvent> = 137 if (shouldUseVolumeController) { 138 volumeController.events 139 } else { 140 emptyFlow() 141 } 142 143 override val mode: StateFlow<Int> = <lambda>null144 callbackFlow { 145 val listener = AudioManager.OnModeChangedListener { newMode -> trySend(newMode) } 146 audioManager.addOnModeChangedListener(ConcurrentUtils.DIRECT_EXECUTOR, listener) 147 awaitClose { audioManager.removeOnModeChangedListener(listener) } 148 } <lambda>null149 .onStart { emit(audioManager.mode) } 150 .flowOn(backgroundCoroutineContext) 151 .stateIn(coroutineScope, SharingStarted.WhileSubscribed(), audioManager.mode) 152 153 override val ringerMode: StateFlow<RingerMode> = 154 audioManagerEventsReceiver.events 155 .filterIsInstance(AudioManagerEvent.InternalRingerModeChanged::class) <lambda>null156 .map { RingerMode(audioManager.ringerModeInternal) } <lambda>null157 .onStart { emit(RingerMode(audioManager.ringerModeInternal)) } 158 .flowOn(backgroundCoroutineContext) 159 .stateIn( 160 coroutineScope, 161 SharingStarted.WhileSubscribed(), 162 RingerMode(audioManager.ringerModeInternal), 163 ) 164 165 override val communicationDevice: StateFlow<AudioDeviceInfo?> 166 get() = <lambda>null167 callbackFlow { 168 val listener = OnCommunicationDeviceChangedListener { trySend(Unit) } 169 audioManager.addOnCommunicationDeviceChangedListener( 170 ConcurrentUtils.DIRECT_EXECUTOR, 171 listener, 172 ) 173 174 awaitClose { audioManager.removeOnCommunicationDeviceChangedListener(listener) } 175 } 176 .filterNotNull() <lambda>null177 .map { audioManager.communicationDevice } <lambda>null178 .onStart { emit(audioManager.communicationDevice) } 179 .flowOn(backgroundCoroutineContext) 180 .stateIn( 181 coroutineScope, 182 SharingStarted.WhileSubscribed(), 183 audioManager.communicationDevice, 184 ) 185 initnull186 override fun init() { 187 try { 188 audioManager.volumeController = volumeController 189 } catch (error: SecurityException) { 190 Log.wtf("AudioManager", "Unable to set the volume controller", error) 191 } 192 } 193 getAudioStreamnull194 override fun getAudioStream(audioStream: AudioStream): Flow<AudioStreamModel> { 195 return merge( 196 audioManagerEventsReceiver.events.filter { 197 if (it is StreamAudioManagerEvent) { 198 it.audioStream == audioStream 199 } else { 200 true 201 } 202 }, 203 volumeSettingChanges(audioStream), 204 volumeControllerEvents.filter { it is VolumeControllerEvent.VolumeChanged }, 205 ) 206 .conflate() 207 .map { getCurrentAudioStream(audioStream) } 208 .onStart { emit(getCurrentAudioStream(audioStream)) } 209 .distinctUntilChanged() 210 .onEach { logger.onVolumeUpdateReceived(audioStream, it) } 211 .flowOn(backgroundCoroutineContext) 212 } 213 getCurrentAudioStreamnull214 private fun getCurrentAudioStream(audioStream: AudioStream): AudioStreamModel { 215 return AudioStreamModel( 216 audioStream = audioStream, 217 minVolume = getMinVolume(audioStream), 218 maxVolume = audioManager.getStreamMaxVolume(audioStream.value), 219 volume = audioManager.getStreamVolume(audioStream.value), 220 isAffectedByMute = audioManager.isStreamMutableByUi(audioStream.value), 221 isAffectedByRingerMode = audioManager.isStreamAffectedByRingerMode(audioStream.value), 222 isMuted = audioManager.isStreamMute(audioStream.value), 223 ) 224 } 225 getLastAudibleVolumenull226 override suspend fun getLastAudibleVolume(audioStream: AudioStream): Int { 227 return withContext(backgroundCoroutineContext) { 228 audioManager.getLastAudibleStreamVolume(audioStream.value) 229 } 230 } 231 setVolumenull232 override suspend fun setVolume(audioStream: AudioStream, volume: Int) { 233 withContext(backgroundCoroutineContext) { 234 logger.onSetVolumeRequested(audioStream, volume) 235 audioManager.setStreamVolume(audioStream.value, volume, 0) 236 } 237 } 238 setMutednull239 override suspend fun setMuted(audioStream: AudioStream, isMuted: Boolean): Boolean { 240 return withContext(backgroundCoroutineContext) { 241 if (isMuted == audioManager.isStreamMute(audioStream.value)) { 242 false 243 } else { 244 audioManager.adjustStreamVolume( 245 audioStream.value, 246 if (isMuted) AudioManager.ADJUST_MUTE else AudioManager.ADJUST_UNMUTE, 247 0, 248 ) 249 true 250 } 251 } 252 } 253 setRingerModeInternalnull254 override suspend fun setRingerModeInternal(audioStream: AudioStream, mode: RingerMode) { 255 withContext(backgroundCoroutineContext) { audioManager.ringerModeInternal = mode.value } 256 } 257 258 @AudioDeviceCategory getBluetoothAudioDeviceCategorynull259 override suspend fun getBluetoothAudioDeviceCategory(bluetoothAddress: String): Int { 260 return withContext(backgroundCoroutineContext) { 261 audioManager.getBluetoothAudioDeviceCategory(bluetoothAddress) 262 } 263 } 264 notifyVolumeControllerVisiblenull265 override suspend fun notifyVolumeControllerVisible(isVisible: Boolean) { 266 withContext(backgroundCoroutineContext) { 267 audioManager.notifyVolumeControllerVisible(volumeController, isVisible) 268 } 269 } 270 dispatchMediaKeyEventnull271 override fun dispatchMediaKeyEvent(event: KeyEvent) { 272 audioManager.dispatchMediaKeyEvent(event) 273 } 274 getMinVolumenull275 private fun getMinVolume(stream: AudioStream): Int = 276 try { 277 audioManager.getStreamMinVolume(stream.value) 278 } catch (e: IllegalArgumentException) { 279 // Fallback to STREAM_VOICE_CALL because 280 // CallVolumePreferenceController.java default 281 // return STREAM_VOICE_CALL in getAudioStream 282 audioManager.getStreamMinVolume(AudioManager.STREAM_VOICE_CALL) 283 } 284 volumeSettingChangesnull285 private fun volumeSettingChanges(audioStream: AudioStream): Flow<Unit> { 286 val uri = streamSettingNames[audioStream]?.let(Settings.System::getUriFor) 287 uri ?: return emptyFlow() 288 return callbackFlow { 289 val observer = 290 object : ContentObserver(DirectExecutor.INSTANCE, 0) { 291 override fun onChange(selfChange: Boolean) { 292 launch { send(Unit) } 293 } 294 } 295 contentResolver.registerContentObserver(uri, false, observer) 296 awaitClose { contentResolver.unregisterContentObserver(observer) } 297 } 298 } 299 } 300 301 private class ProducingVolumeController : IVolumeController.Stub() { 302 303 private val mutableEvents = MutableSharedFlow<VolumeControllerEvent>(extraBufferCapacity = 32) 304 val events = mutableEvents.asSharedFlow() 305 displaySafeVolumeWarningnull306 override fun displaySafeVolumeWarning(flags: Int) { 307 mutableEvents.tryEmit(VolumeControllerEvent.DisplaySafeVolumeWarning(flags)) 308 } 309 volumeChangednull310 override fun volumeChanged(streamType: Int, flags: Int) { 311 mutableEvents.tryEmit(VolumeControllerEvent.VolumeChanged(streamType, flags)) 312 } 313 masterMuteChangednull314 override fun masterMuteChanged(flags: Int) { 315 mutableEvents.tryEmit(VolumeControllerEvent.MasterMuteChanged(flags)) 316 } 317 setLayoutDirectionnull318 override fun setLayoutDirection(layoutDirection: Int) { 319 mutableEvents.tryEmit(VolumeControllerEvent.SetLayoutDirection(layoutDirection)) 320 } 321 dismissnull322 override fun dismiss() { 323 mutableEvents.tryEmit(VolumeControllerEvent.Dismiss) 324 } 325 setA11yModenull326 override fun setA11yMode(mode: Int) { 327 mutableEvents.tryEmit(VolumeControllerEvent.SetA11yMode(mode)) 328 } 329 displayCsdWarningnull330 override fun displayCsdWarning(csdWarning: Int, displayDurationMs: Int) { 331 mutableEvents.tryEmit( 332 VolumeControllerEvent.DisplayCsdWarning(csdWarning, displayDurationMs) 333 ) 334 } 335 } 336