• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * 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.settings.network.tether
18 
19 import android.bluetooth.BluetoothAdapter
20 import android.bluetooth.BluetoothPan
21 import android.bluetooth.BluetoothProfile
22 import android.content.BroadcastReceiver
23 import android.content.Context
24 import android.content.Intent
25 import android.content.IntentFilter
26 import android.net.ConnectivityManager
27 import android.net.TetheringManager
28 import android.os.Handler
29 import android.os.Looper
30 import com.android.settings.R
31 import com.android.settings.datausage.DataSaverBackend
32 import com.android.settingslib.datastore.KeyValueStore
33 import com.android.settingslib.datastore.KeyedDataObservable
34 import com.android.settingslib.metadata.PreferenceAvailabilityProvider
35 import com.android.settingslib.metadata.PreferenceLifecycleContext
36 import com.android.settingslib.metadata.PreferenceLifecycleProvider
37 import com.android.settingslib.metadata.ReadWritePermit
38 import com.android.settingslib.metadata.SensitivityLevel
39 import com.android.settingslib.metadata.SwitchPreference
40 import java.util.concurrent.atomic.AtomicReference
41 
42 // LINT.IfChange
43 @Suppress("DEPRECATION")
44 class BluetoothTetherSwitchPreference :
45     SwitchPreference(KEY, R.string.bluetooth_tether_checkbox_text),
46     PreferenceAvailabilityProvider,
47     PreferenceLifecycleProvider {
48 
49     private var tetherChangeReceiver: BroadcastReceiver? = null
50 
51     override val summary: Int
52         get() = R.string.bluetooth_tethering_subtext
53 
54     override val keywords: Int
55         get() = R.string.keywords_hotspot_tethering
56 
storagenull57     override fun storage(context: Context): KeyValueStore = BluetoothTetherStore(context)
58 
59     override fun isAvailable(context: Context): Boolean {
60         BluetoothAdapter.getDefaultAdapter() ?: return false
61         val tetheringManager = context.getSystemService(TetheringManager::class.java)
62         val bluetoothRegexs = tetheringManager?.tetherableBluetoothRegexs
63         return bluetoothRegexs?.isNotEmpty() == true
64     }
65 
isEnablednull66     override fun isEnabled(context: Context): Boolean {
67         val adapter = BluetoothAdapter.getDefaultAdapter() ?: return false
68         val btState = adapter.state
69         /* TODO: when bluetooth is off, btstate will be `state_turning_on` -> `state_off` ->
70         `state_turning_on` -> `state_on`, causing preference enable status incorrect. */
71         when (btState) {
72             BluetoothAdapter.STATE_TURNING_OFF,
73             BluetoothAdapter.STATE_TURNING_ON -> return false
74             else -> {}
75         }
76         val dataSaverBackend = DataSaverBackend(context)
77         return !dataSaverBackend.isDataSaverEnabled
78     }
79 
getReadPermitnull80     override fun getReadPermit(context: Context, callingPid: Int, callingUid: Int) =
81         ReadWritePermit.ALLOW
82 
83     override fun getWritePermit(
84         context: Context,
85         value: Boolean?,
86         callingPid: Int,
87         callingUid: Int,
88     ) = ReadWritePermit.ALLOW
89 
90     override val sensitivityLevel: Int
91         get() = SensitivityLevel.LOW_SENSITIVITY
92 
93     override fun onCreate(context: PreferenceLifecycleContext) {
94         val receiver =
95             object : BroadcastReceiver() {
96                 override fun onReceive(content: Context, intent: Intent) {
97                     when (intent.action) {
98                         TetheringManager.ACTION_TETHER_STATE_CHANGED,
99                         Intent.ACTION_MEDIA_SHARED,
100                         Intent.ACTION_MEDIA_UNSHARED,
101                         BluetoothAdapter.ACTION_STATE_CHANGED,
102                         BluetoothPan.ACTION_TETHERING_STATE_CHANGED ->
103                             context.notifyPreferenceChange(KEY)
104                     }
105                 }
106             }
107         tetherChangeReceiver = receiver
108         var filter = IntentFilter(TetheringManager.ACTION_TETHER_STATE_CHANGED)
109         val intent = context.registerReceiver(receiver, filter)
110 
111         filter = IntentFilter()
112         filter.addAction(Intent.ACTION_MEDIA_SHARED)
113         filter.addAction(Intent.ACTION_MEDIA_UNSHARED)
114         filter.addDataScheme("file")
115         context.registerReceiver(receiver, filter)
116 
117         filter = IntentFilter()
118         filter.addAction(BluetoothAdapter.ACTION_STATE_CHANGED)
119         filter.addAction(BluetoothPan.ACTION_TETHERING_STATE_CHANGED)
120         context.registerReceiver(receiver, filter)
121     }
122 
onDestroynull123     override fun onDestroy(context: PreferenceLifecycleContext) {
124         tetherChangeReceiver?.let {
125             context.unregisterReceiver(it)
126             tetherChangeReceiver = null
127         }
128     }
129 
130     @Suppress("UNCHECKED_CAST")
131     private class BluetoothTetherStore(private val context: Context) :
132         KeyedDataObservable<String>(), KeyValueStore {
133 
134         val bluetoothPan = AtomicReference<BluetoothPan>()
135 
containsnull136         override fun contains(key: String) = key == KEY
137 
138         override fun <T : Any> getValue(key: String, valueType: Class<T>): T? {
139             // TODO: support async operation in background thread
140             val adapter = BluetoothAdapter.getDefaultAdapter() ?: return false as T
141             if (bluetoothPan.get() == null) {
142                 val profileServiceListener: BluetoothProfile.ServiceListener =
143                     object : BluetoothProfile.ServiceListener {
144                         override fun onServiceConnected(profile: Int, proxy: BluetoothProfile) {
145                             if (bluetoothPan.get() == null) {
146                                 bluetoothPan.set(proxy as BluetoothPan)
147                                 notifyChange(KEY, 0)
148                             }
149                         }
150 
151                         override fun onServiceDisconnected(profile: Int) {
152                             /* Do nothing */
153                         }
154                     }
155                 // TODO: adapter.closeProfileProxy(bluetoothPan.get())
156                 adapter.getProfileProxy(
157                     context.applicationContext,
158                     profileServiceListener,
159                     BluetoothProfile.PAN,
160                 )
161             }
162 
163             val btState = adapter.state
164             val pan = bluetoothPan.get()
165             return ((btState == BluetoothAdapter.STATE_ON ||
166                 btState == BluetoothAdapter.STATE_TURNING_OFF) && pan != null && pan.isTetheringOn)
167                 as T?
168         }
169 
setValuenull170         override fun <T : Any> setValue(key: String, valueType: Class<T>, value: T?) {
171             if (value == null) return
172             val connectivityManager =
173                 context.getSystemService(ConnectivityManager::class.java) ?: return
174             if (value as Boolean) {
175                 val handler by lazy { Handler(Looper.getMainLooper()) }
176                 val startTetheringCallback = OnStartTetheringCallback()
177                 fun startTethering() {
178                     connectivityManager.startTethering(
179                         ConnectivityManager.TETHERING_BLUETOOTH,
180                         true,
181                         startTetheringCallback,
182                         handler,
183                     )
184                 }
185 
186                 val adapter = BluetoothAdapter.getDefaultAdapter()
187                 if (adapter.state == BluetoothAdapter.STATE_OFF) {
188                     adapter.enable()
189                     val filter = IntentFilter(BluetoothAdapter.ACTION_STATE_CHANGED)
190                     val tetherChangeReceiver =
191                         object : BroadcastReceiver() {
192                             override fun onReceive(context: Context, intent: Intent) {
193                                 if (
194                                     intent.getIntExtra(
195                                         BluetoothAdapter.EXTRA_STATE,
196                                         BluetoothAdapter.ERROR,
197                                     ) == BluetoothAdapter.STATE_ON
198                                 ) {
199                                     startTethering()
200                                     context.unregisterReceiver(this)
201                                 }
202                             }
203                         }
204                     val intent = context.registerReceiver(tetherChangeReceiver, filter)
205                     if (intent != null) tetherChangeReceiver.onReceive(context, intent)
206                 } else {
207                     startTethering()
208                 }
209             } else {
210                 connectivityManager.stopTethering(ConnectivityManager.TETHERING_BLUETOOTH)
211             }
212         }
213 
214         private inner class OnStartTetheringCallback :
215             ConnectivityManager.OnStartTetheringCallback() {
onTetheringStartednull216             override fun onTetheringStarted() {
217                 notifyChange(KEY, 0)
218             }
219 
onTetheringFailednull220             override fun onTetheringFailed() {
221                 notifyChange(KEY, 0)
222             }
223         }
224     }
225 
226     companion object {
227         const val KEY = "enable_bluetooth_tethering"
228     }
229 }
230 // LINT.ThenChange(TetherSettings.java)
231