Avoid ANR of TetherPreferenceController

Off load the following work from main thread,
- Calculate title
- Calculate summery

This also helps improve the latency.

Also migrate to registerTetheringEventCallback() since
TetheringManager.ACTION_TETHER_STATE_CHANGED is deprecated.

Fix: 311848767
Test: manual - on Network & internet page and turn on / off tethering
Test: unit tests
Change-Id: I6ee182b41ef51f691ea31938142be1a41faf5573
This commit is contained in:
Chaohui Wang
2023-11-28 18:57:04 +08:00
parent 6a3a3e2ac7
commit 880068d23a
8 changed files with 461 additions and 524 deletions

View File

@@ -0,0 +1,102 @@
/*
* Copyright (C) 2023 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.android.settings.network
import android.bluetooth.BluetoothAdapter
import android.bluetooth.BluetoothManager
import android.bluetooth.BluetoothPan
import android.bluetooth.BluetoothProfile
import android.content.Context
import android.content.IntentFilter
import android.net.TetheringInterface
import android.net.TetheringManager
import com.android.settingslib.spaprivileged.framework.common.broadcastReceiverFlow
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.ExperimentalCoroutinesApi
import kotlinx.coroutines.asExecutor
import kotlinx.coroutines.channels.awaitClose
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.callbackFlow
import kotlinx.coroutines.flow.combine
import kotlinx.coroutines.flow.conflate
import kotlinx.coroutines.flow.flatMapLatest
import kotlinx.coroutines.flow.flowOf
import kotlinx.coroutines.flow.flowOn
import kotlinx.coroutines.flow.merge
import kotlinx.coroutines.launch
class TetheredRepository(private val context: Context) {
private val tetheringManager = context.getSystemService(TetheringManager::class.java)!!
private val adapter = context.getSystemService(BluetoothManager::class.java)!!.adapter
fun tetheredTypesFlow(): Flow<Set<Int>> =
combine(
tetheredInterfacesFlow(),
isBluetoothTetheringOnFlow(),
) { tetheringInterfaces, isBluetoothTetheringOn ->
val mutableSet = tetheringInterfaces.map { it.type }.toMutableSet()
if (isBluetoothTetheringOn) mutableSet += TetheringManager.TETHERING_BLUETOOTH
mutableSet
}.conflate().flowOn(Dispatchers.Default)
private fun tetheredInterfacesFlow(): Flow<Set<TetheringInterface>> = callbackFlow {
val callback = object : TetheringManager.TetheringEventCallback {
override fun onTetheredInterfacesChanged(interfaces: Set<TetheringInterface>) {
trySend(interfaces)
}
}
tetheringManager.registerTetheringEventCallback(Dispatchers.Default.asExecutor(), callback)
awaitClose { tetheringManager.unregisterTetheringEventCallback(callback) }
}.conflate().flowOn(Dispatchers.Default)
@OptIn(ExperimentalCoroutinesApi::class)
private fun isBluetoothTetheringOnFlow(): Flow<Boolean> =
merge(
flowOf(null), // kick an initial value
context.broadcastReceiverFlow(IntentFilter(BluetoothAdapter.ACTION_STATE_CHANGED)),
).flatMapLatest {
if (adapter.getState() == BluetoothAdapter.STATE_ON) {
isBluetoothPanTetheringOnFlow()
} else {
flowOf(false)
}
}.conflate().flowOn(Dispatchers.Default)
private fun isBluetoothPanTetheringOnFlow() = callbackFlow {
var connectedProxy: BluetoothProfile? = null
val listener = object : BluetoothProfile.ServiceListener {
override fun onServiceConnected(profile: Int, proxy: BluetoothProfile) {
connectedProxy = proxy
launch(Dispatchers.Default) {
trySend((proxy as BluetoothPan).isTetheringOn)
}
}
override fun onServiceDisconnected(profile: Int) {}
}
adapter.getProfileProxy(context, listener, BluetoothProfile.PAN)
awaitClose {
connectedProxy?.let { adapter.closeProfileProxy(BluetoothProfile.PAN, it) }
}
}.conflate().flowOn(Dispatchers.Default)
}