• 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.wifi.tether
18 
19 import android.Manifest
20 import android.app.settings.SettingsEnums.ACTION_WIFI_HOTSPOT
21 import android.app.settings.SettingsEnums.WIFI_TETHER_SETTINGS
22 import android.content.Context
23 import android.content.Intent
24 import android.net.TetheringManager
25 import android.net.TetheringManager.TETHERING_WIFI
26 import android.net.wifi.WifiClient
27 import android.net.wifi.WifiManager
28 import android.os.UserManager
29 import android.text.BidiFormatter
30 import android.util.Log
31 import com.android.settings.R
32 import com.android.settings.Utils
33 import com.android.settings.contract.KEY_WIFI_HOTSPOT
34 import com.android.settings.core.SubSettingLauncher
35 import com.android.settings.datausage.DataSaverMainSwitchPreference.Companion.KEY as DATA_SAVER_KEY
36 import com.android.settings.metrics.PreferenceActionMetricsProvider
37 import com.android.settings.restriction.PreferenceRestrictionMixin
38 import com.android.settings.wifi.WifiUtils.canShowWifiHotspot
39 import com.android.settings.wifi.utils.tetheringManager
40 import com.android.settings.wifi.utils.wifiApState
41 import com.android.settings.wifi.utils.wifiManager
42 import com.android.settings.wifi.utils.wifiSoftApSsid
43 import com.android.settingslib.PrimarySwitchPreferenceBinding
44 import com.android.settingslib.TetherUtil
45 import com.android.settingslib.datastore.AbstractKeyedDataObservable
46 import com.android.settingslib.datastore.HandlerExecutor
47 import com.android.settingslib.datastore.KeyValueStore
48 import com.android.settingslib.datastore.KeyedObserver
49 import com.android.settingslib.datastore.Permissions
50 import com.android.settingslib.metadata.PreferenceAvailabilityProvider
51 import com.android.settingslib.metadata.PreferenceChangeReason
52 import com.android.settingslib.metadata.PreferenceSummaryProvider
53 import com.android.settingslib.metadata.ReadWritePermit
54 import com.android.settingslib.metadata.SensitivityLevel
55 import com.android.settingslib.metadata.SwitchPreference
56 import com.android.settingslib.wifi.WifiUtils.Companion.getWifiTetherSummaryForConnectedDevices
57 
58 // LINT.IfChange
59 class WifiHotspotSwitchPreference(context: Context, dataSaverStore: KeyValueStore) :
60     SwitchPreference(KEY, R.string.wifi_hotspot_checkbox_text),
61     PrimarySwitchPreferenceBinding,
62     PreferenceActionMetricsProvider,
63     PreferenceAvailabilityProvider,
64     PreferenceSummaryProvider,
65     PreferenceRestrictionMixin {
66 
67     override val preferenceActionMetrics: Int
68         get() = ACTION_WIFI_HOTSPOT
69 
tagsnull70     override fun tags(context: Context) = arrayOf(KEY_WIFI_HOTSPOT)
71 
72     private val wifiHotspotStore = WifiHotspotStore(context, dataSaverStore)
73 
74     override fun isAvailable(context: Context) =
75         canShowWifiHotspot(context) &&
76             TetherUtil.isTetherAvailable(context) &&
77             !Utils.isMonkeyRunning()
78 
79     override fun getSummary(context: Context): CharSequence? =
80         when (context.wifiApState) {
81             WifiManager.WIFI_AP_STATE_ENABLING -> context.getString(R.string.wifi_tether_starting)
82             WifiManager.WIFI_AP_STATE_ENABLED -> {
83                 val sapClientsSize = wifiHotspotStore.sapClientsSize
84                 if (sapClientsSize == null) {
85                     context.getString(
86                         R.string.wifi_tether_enabled_subtext,
87                         BidiFormatter.getInstance().unicodeWrap(context.wifiSoftApSsid),
88                     )
89                 } else {
90                     getWifiTetherSummaryForConnectedDevices(context, sapClientsSize)
91                 }
92             }
93             WifiManager.WIFI_AP_STATE_DISABLING -> context.getString(R.string.wifi_tether_stopping)
94             WifiManager.WIFI_AP_STATE_DISABLED ->
95                 context.getString(R.string.wifi_hotspot_off_subtext)
96             else ->
97                 when (wifiHotspotStore.sapFailureReason) {
98                     WifiManager.SAP_START_FAILURE_NO_CHANNEL ->
99                         context.getString(R.string.wifi_sap_no_channel_error)
100                     else -> context.getString(R.string.wifi_error)
101                 }
102         }
103 
intentnull104     override fun intent(context: Context): Intent? =
105         SubSettingLauncher(context)
106             .apply {
107                 setDestination(WifiTetherSettings::class.java.name)
108                 setTitleRes(R.string.wifi_hotspot_checkbox_text)
109                 setSourceMetricsCategory(WIFI_TETHER_SETTINGS)
110             }
111             .toIntent()
112 
isEnablednull113     override fun isEnabled(context: Context) =
114         wifiHotspotStore.dataSaverStore.getBoolean(DATA_SAVER_KEY) != true &&
115             super<PreferenceRestrictionMixin>.isEnabled(context)
116 
117     override val restrictionKeys
118         get() = arrayOf(UserManager.DISALLOW_WIFI_TETHERING)
119 
120     override fun getReadPermissions(context: Context) =
121         Permissions.allOf(Manifest.permission.ACCESS_WIFI_STATE)
122 
123     override fun getWritePermissions(context: Context) =
124         Permissions.allOf(Manifest.permission.TETHER_PRIVILEGED)
125 
126     override fun getReadPermit(context: Context, callingPid: Int, callingUid: Int) =
127         ReadWritePermit.ALLOW
128 
129     override fun getWritePermit(
130         context: Context,
131         value: Boolean?,
132         callingPid: Int,
133         callingUid: Int,
134     ) = ReadWritePermit.ALLOW
135 
136     override val sensitivityLevel
137         get() = SensitivityLevel.HIGH_SENSITIVITY
138 
139     override fun storage(context: Context): KeyValueStore = wifiHotspotStore
140 
141     @Suppress("UNCHECKED_CAST")
142     private class WifiHotspotStore(
143         private val context: Context,
144         val dataSaverStore: KeyValueStore,
145     ) :
146         AbstractKeyedDataObservable<String>(),
147         KeyValueStore,
148         WifiTetherSoftApManager.WifiTetherSoftApCallback,
149         TetheringManager.StartTetheringCallback,
150         KeyedObserver<String> {
151 
152         private var wifiTetherSoftApManager: WifiTetherSoftApManager? = null
153         var sapFailureReason: Int? = null
154         var sapClientsSize: Int? = null
155 
156         override fun contains(key: String) =
157             key == KEY && context.wifiManager != null && context.tetheringManager != null
158 
159         override fun <T : Any> getValue(key: String, valueType: Class<T>): T? {
160             val wifiApState = context.wifiApState
161             val value =
162                 wifiApState == WifiManager.WIFI_AP_STATE_ENABLING ||
163                     wifiApState == WifiManager.WIFI_AP_STATE_ENABLED
164             return value as T?
165         }
166 
167         override fun <T : Any> setValue(key: String, valueType: Class<T>, value: T?) {
168             if (value !is Boolean) return
169             val tetheringManager = context.tetheringManager ?: return
170             if (value) {
171                 tetheringManager.startTethering(TETHERING_WIFI, HandlerExecutor.main, this)
172             } else {
173                 tetheringManager.stopTethering(TETHERING_WIFI)
174             }
175         }
176 
177         override fun onFirstObserverAdded() {
178             val apManager = WifiTetherSoftApManager(context.wifiManager, this)
179             wifiTetherSoftApManager = apManager
180             apManager.registerSoftApCallback()
181             dataSaverStore.addObserver(DATA_SAVER_KEY, this, HandlerExecutor.main)
182         }
183 
184         override fun onLastObserverRemoved() {
185             dataSaverStore.removeObserver(DATA_SAVER_KEY, this)
186             wifiTetherSoftApManager?.unRegisterSoftApCallback()
187         }
188 
189         override fun onStateChanged(state: Int, failureReason: Int) {
190             Log.d(TAG, "onStateChanged(),state=$state,failureReason=$failureReason")
191             sapFailureReason = failureReason
192             if (state == WifiManager.WIFI_AP_STATE_DISABLED) sapClientsSize = null
193             notifyChange(KEY, PreferenceChangeReason.VALUE)
194         }
195 
196         override fun onConnectedClientsChanged(clients: List<WifiClient>?) {
197             sapClientsSize = clients?.size ?: 0
198             Log.d(TAG, "onConnectedClientsChanged(),sapClientsSize=$sapClientsSize")
199             notifyChange(KEY, PreferenceChangeReason.STATE)
200         }
201 
202         override fun onTetheringStarted() {}
203 
204         override fun onTetheringFailed(error: Int) {
205             Log.e(TAG, "onTetheringFailed(),error=$error")
206         }
207 
208         override fun onKeyChanged(key: String, reason: Int) = notifyChange(KEY, reason)
209     }
210 
211     companion object {
212         const val TAG = "WifiHotspotSwitchPreference"
213         const val KEY = "wifi_tether"
214     }
215 }
216 // LINT.ThenChange(WifiTetherPreferenceController.java)
217