Files
app_Settings/src/com/android/settings/network/TetheredRepository.kt
hoffc de3f413ecd Fix NPE force close if disabling BT feature
adapter can be null in some SPs which disable bluetooth feature
from framework, so it will lead to NPE force close in these SPs.

Add null check for adapter.

Bug: 345584461

Change-Id: I3fabe94a97cc9baf7b1739fe1c9160a52b50d7d4
2024-06-07 13:00:03 +08:00

103 lines
4.0 KiB
Kotlin

/*
* 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)
}