@@ -21,8 +21,10 @@ import android.media.AudioAttributes
2121import android.media.AudioManager
2222import android.os.Build
2323import android.os.Handler
24+ import android.os.HandlerThread
2425import android.os.Looper
2526import com.twilio.audioswitch.*
27+ import io.livekit.android.util.LKLog
2628import javax.inject.Inject
2729import javax.inject.Singleton
2830
@@ -138,13 +140,26 @@ constructor(private val context: Context) : AudioHandler {
138140
139141 private var audioSwitch: AbstractAudioSwitch ? = null
140142
141- // AudioSwitch is not threadsafe, so all calls should be done on the main thread.
142- private val handler = Handler (Looper .getMainLooper())
143+ // AudioSwitch is not threadsafe, so all calls should be done through a single thread.
144+ private var handler: Handler ? = null
145+ private var thread: HandlerThread ? = null
143146
147+ @Synchronized
144148 override fun start () {
149+ if (handler != null || thread != null ) {
150+ LKLog .i { " AudioSwitchHandler called start multiple times?" }
151+ }
152+
153+ if (thread == null ) {
154+ thread = HandlerThread (" AudioSwitchHandlerThread" ).also { it.start() }
155+ }
156+ if (handler == null ) {
157+ handler = Handler (thread!! .looper)
158+ }
159+
145160 if (audioSwitch == null ) {
146- handler.removeCallbacksAndMessages(null )
147- handler.postAtFrontOfQueue {
161+ handler? .removeCallbacksAndMessages(null )
162+ handler? .postAtFrontOfQueue {
148163 val switch =
149164 if (Build .VERSION .SDK_INT >= Build .VERSION_CODES .M ) {
150165 AudioSwitch (
@@ -176,12 +191,17 @@ constructor(private val context: Context) : AudioHandler {
176191 }
177192 }
178193
194+ @Synchronized
179195 override fun stop () {
180- handler.removeCallbacksAndMessages(null )
181- handler.postAtFrontOfQueue {
196+ handler? .removeCallbacksAndMessages(null )
197+ handler? .postAtFrontOfQueue {
182198 audioSwitch?.stop()
183199 audioSwitch = null
184200 }
201+ thread?.quitSafely()
202+
203+ handler = null
204+ thread = null
185205 }
186206
187207 /* *
@@ -199,11 +219,12 @@ constructor(private val context: Context) : AudioHandler {
199219 /* *
200220 * Select a specific audio device.
201221 */
222+ @Synchronized
202223 fun selectDevice (audioDevice : AudioDevice ? ) {
203- if (Looper .myLooper() == Looper .getMainLooper() ) {
224+ if (Looper .myLooper() == handler?.looper ) {
204225 audioSwitch?.selectDevice(audioDevice)
205226 } else {
206- handler.post {
227+ handler? .post {
207228 audioSwitch?.selectDevice(audioDevice)
208229 }
209230 }
0 commit comments