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