1 /*
2  * Copyright 2023 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 package com.android.server.bluetooth
17 
18 import android.content.ContentResolver
19 import android.database.ContentObserver
20 import android.os.Handler
21 import android.os.Looper
22 import android.provider.Settings
23 
24 private const val TAG = "BluetoothRadioModeListener"
25 
26 /**
27  * Listen on radio mode and trigger the callback when it change
28  *
29  * @param radio: The radio to listen for, eg: Settings.Global.AIRPLANE_MODE_RADIOS
30  * @param modeKey: The associated mode key, eg: Settings.Global.AIRPLANE_MODE_ON
31  * @param callback: The callback to trigger when there is a mode change, pass new mode as parameter
32  * @return The initial value of the radio
33  */
initializeRadioModeListenernull34 internal fun initializeRadioModeListener(
35     looper: Looper,
36     resolver: ContentResolver,
37     radio: String,
38     modeKey: String,
39     callback: (m: Boolean) -> Unit
40 ): Boolean {
41     val observer =
42         object : ContentObserver(Handler(looper)) {
43             override fun onChange(selfChange: Boolean) {
44                 callback(getRadioModeValue(resolver, radio, modeKey))
45             }
46         }
47 
48     val notifyForDescendants = false
49 
50     resolver.registerContentObserver(
51         Settings.Global.getUriFor(radio),
52         notifyForDescendants,
53         observer
54     )
55     resolver.registerContentObserver(
56         Settings.Global.getUriFor(modeKey),
57         notifyForDescendants,
58         observer
59     )
60     return getRadioModeValue(resolver, radio, modeKey)
61 }
62 
63 /**
64  * Check if Bluetooth is impacted by the radio and fetch global mode status
65  *
66  * @return whether Bluetooth should consider this radio or not
67  */
getRadioModeValuenull68 private fun getRadioModeValue(resolver: ContentResolver, radio: String, modeKey: String): Boolean {
69     return if (isSensitive(resolver, radio)) {
70         isGlobalModeOn(resolver, modeKey)
71     } else {
72         Log.d(TAG, "Not sensitive to " + radio + " change. Forced to false")
73         false
74     }
75 }
76 
77 /**
78  * *Do not use outside of this file to avoid async issues*
79  *
80  * @return false if Bluetooth should not listen for mode change related to the {@code radio}
81  */
isSensitivenull82 private fun isSensitive(resolver: ContentResolver, radio: String): Boolean {
83     val radios = Settings.Global.getString(resolver, radio)
84     return radios != null && radios.contains(Settings.Global.RADIO_BLUETOOTH)
85 }
86 
87 /**
88  * *Do not use outside of this file to avoid async issues*
89  *
90  * @return whether mode {@code modeKey} is on or off in Global settings
91  */
isGlobalModeOnnull92 private fun isGlobalModeOn(resolver: ContentResolver, modeKey: String): Boolean {
93     return Settings.Global.getInt(resolver, modeKey, 0) == 1
94 }
95