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 18 19 import android.Manifest 20 import android.app.settings.SettingsEnums.ACTION_WIFI 21 import android.app.settings.SettingsEnums.ACTION_WIFI_OFF 22 import android.app.settings.SettingsEnums.ACTION_WIFI_ON 23 import android.content.BroadcastReceiver 24 import android.content.Context 25 import android.content.Intent 26 import android.content.IntentFilter 27 import android.net.wifi.WifiManager 28 import android.os.UserManager 29 import android.provider.Settings 30 import android.widget.Toast 31 import androidx.preference.Preference 32 import androidx.preference.Preference.OnPreferenceChangeListener 33 import com.android.settings.R 34 import com.android.settings.contract.KEY_WIFI 35 import com.android.settings.metrics.PreferenceActionMetricsProvider 36 import com.android.settings.network.SatelliteRepository.Companion.isSatelliteOn 37 import com.android.settings.network.SatelliteWarningDialogActivity 38 import com.android.settings.overlay.FeatureFactory.Companion.featureFactory 39 import com.android.settings.restriction.PreferenceRestrictionMixin 40 import com.android.settings.wifi.utils.isDefaultNetworkWifi 41 import com.android.settings.wifi.utils.isWifiEnabled 42 import com.android.settings.wifi.utils.wifiManager 43 import com.android.settingslib.RestrictedSwitchPreference 44 import com.android.settingslib.WirelessUtils 45 import com.android.settingslib.datastore.AbstractKeyedDataObservable 46 import com.android.settingslib.datastore.KeyValueStore 47 import com.android.settingslib.datastore.Permissions 48 import com.android.settingslib.metadata.PreferenceChangeReason 49 import com.android.settingslib.metadata.PreferenceLifecycleProvider 50 import com.android.settingslib.metadata.PreferenceMetadata 51 import com.android.settingslib.metadata.ReadWritePermit 52 import com.android.settingslib.metadata.SensitivityLevel 53 import com.android.settingslib.metadata.SwitchPreference 54 import com.android.settingslib.preference.SwitchPreferenceBinding 55 56 // LINT.IfChange 57 class WifiSwitchPreference : 58 SwitchPreference(KEY, R.string.wifi), 59 SwitchPreferenceBinding, 60 PreferenceActionMetricsProvider, 61 OnPreferenceChangeListener, 62 PreferenceLifecycleProvider, 63 PreferenceRestrictionMixin { 64 65 override val keywords: Int 66 get() = R.string.keywords_wifi 67 68 override val preferenceActionMetrics: Int 69 get() = ACTION_WIFI 70 tagsnull71 override fun tags(context: Context) = arrayOf(KEY_WIFI) 72 73 override fun isEnabled(context: Context) = super<PreferenceRestrictionMixin>.isEnabled(context) 74 75 override val restrictionKeys 76 get() = arrayOf(UserManager.DISALLOW_CHANGE_WIFI_STATE) 77 78 override val useAdminDisabledSummary: Boolean 79 get() = true 80 81 override fun createWidget(context: Context) = RestrictedSwitchPreference(context) 82 83 override fun bind(preference: Preference, metadata: PreferenceMetadata) { 84 super.bind(preference, metadata) 85 preference.onPreferenceChangeListener = this 86 } 87 onPreferenceChangenull88 override fun onPreferenceChange(preference: Preference, newValue: Any): Boolean { 89 val context = preference.context 90 91 // Show dialog and do nothing under satellite mode. 92 if (isSatelliteOn(context)) { 93 context.startActivity( 94 Intent(context, SatelliteWarningDialogActivity::class.java) 95 .putExtra( 96 SatelliteWarningDialogActivity.EXTRA_TYPE_OF_SATELLITE_WARNING_DIALOG, 97 SatelliteWarningDialogActivity.TYPE_IS_WIFI, 98 ) 99 ) 100 return false 101 } 102 103 // Show toast message if Wi-Fi is not allowed in airplane mode 104 if (newValue == true && !context.isRadioAllowed()) { 105 Toast.makeText(context, R.string.wifi_in_airplane_mode, Toast.LENGTH_SHORT).show() 106 return false 107 } 108 109 return true 110 } 111 getReadPermissionsnull112 override fun getReadPermissions(context: Context) = 113 Permissions.allOf(Manifest.permission.ACCESS_WIFI_STATE) 114 115 override fun getWritePermissions(context: Context) = 116 Permissions.anyOf( 117 Manifest.permission.NETWORK_SETTINGS, 118 Manifest.permission.CHANGE_WIFI_STATE, 119 ) 120 121 override fun getReadPermit(context: Context, callingPid: Int, callingUid: Int) = 122 ReadWritePermit.ALLOW 123 124 override fun getWritePermit( 125 context: Context, 126 value: Boolean?, 127 callingPid: Int, 128 callingUid: Int, 129 ) = 130 when { 131 (value == true && !context.isRadioAllowed()) || isSatelliteOn(context) -> 132 ReadWritePermit.DISALLOW 133 else -> ReadWritePermit.ALLOW 134 } 135 136 override val sensitivityLevel 137 get() = SensitivityLevel.LOW_SENSITIVITY 138 storagenull139 override fun storage(context: Context): KeyValueStore = WifiSwitchStore(context) 140 141 @Suppress("UNCHECKED_CAST") 142 private class WifiSwitchStore(private val context: Context) : 143 AbstractKeyedDataObservable<String>(), KeyValueStore { 144 145 private var broadcastReceiver: BroadcastReceiver? = null 146 147 override fun contains(key: String) = key == KEY && context.wifiManager != null 148 149 override fun <T : Any> getValue(key: String, valueType: Class<T>): T? = 150 context.isWifiEnabled as T? 151 152 override fun <T : Any> setValue(key: String, valueType: Class<T>, value: T?) { 153 if (value !is Boolean) return 154 155 context.isWifiEnabled = value 156 157 val metricsFeature = featureFactory.metricsFeatureProvider 158 if (value) { 159 metricsFeature.action(context, ACTION_WIFI_ON) 160 } else { 161 metricsFeature.action(context, ACTION_WIFI_OFF, context.isDefaultNetworkWifi) 162 } 163 } 164 165 override fun onFirstObserverAdded() { 166 broadcastReceiver = 167 object : BroadcastReceiver() { 168 override fun onReceive(context: Context, intent: Intent) { 169 val wifiState = intent.wifiState 170 // do not notify for enabling/disabling state 171 if ( 172 wifiState == WifiManager.WIFI_STATE_ENABLED || 173 wifiState == WifiManager.WIFI_STATE_DISABLED 174 ) { 175 notifyChange(KEY, PreferenceChangeReason.VALUE) 176 } 177 } 178 } 179 context.registerReceiver( 180 broadcastReceiver, 181 IntentFilter(WifiManager.WIFI_STATE_CHANGED_ACTION), 182 ) 183 } 184 185 override fun onLastObserverRemoved() { 186 broadcastReceiver?.let { context.unregisterReceiver(it) } 187 } 188 } 189 190 companion object { 191 const val KEY = "main_toggle_wifi" 192 Contextnull193 private fun Context.isRadioAllowed() = 194 WirelessUtils.isRadioAllowed(this, Settings.Global.RADIO_WIFI) 195 196 private val Intent.wifiState 197 get() = getIntExtra(WifiManager.EXTRA_WIFI_STATE, WifiManager.WIFI_STATE_UNKNOWN) 198 } 199 } 200 // LINT.ThenChange(WifiSwitchPreferenceController.java) 201