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.content.BroadcastReceiver
20 import android.content.Context
21 import android.content.Intent
22 import android.content.IntentFilter
23 import android.net.wifi.WifiManager
24 import android.os.UserManager
25 import android.provider.Settings
26 import android.util.Log
27 import android.widget.Toast
28 import androidx.preference.Preference
29 import androidx.preference.Preference.OnPreferenceChangeListener
30 import com.android.settings.PreferenceRestrictionMixin
31 import com.android.settings.R
32 import com.android.settings.network.SatelliteRepository
33 import com.android.settings.network.SatelliteWarningDialogActivity
34 import com.android.settingslib.RestrictedSwitchPreference
35 import com.android.settingslib.WirelessUtils
36 import com.android.settingslib.datastore.AbstractKeyedDataObservable
37 import com.android.settingslib.datastore.DataChangeReason
38 import com.android.settingslib.datastore.KeyValueStore
39 import com.android.settingslib.metadata.PreferenceLifecycleProvider
40 import com.android.settingslib.metadata.PreferenceMetadata
41 import com.android.settingslib.metadata.ReadWritePermit
42 import com.android.settingslib.metadata.SensitivityLevel
43 import com.android.settingslib.metadata.SwitchPreference
44 import com.android.settingslib.preference.SwitchPreferenceBinding
45 import java.util.concurrent.Executors
46 import java.util.concurrent.TimeUnit
47 
48 // LINT.IfChange
49 class WifiSwitchPreference :
50     SwitchPreference(KEY, R.string.wifi),
51     SwitchPreferenceBinding,
52     OnPreferenceChangeListener,
53     PreferenceLifecycleProvider,
54     PreferenceRestrictionMixin {
55 
56     override val keywords: Int
57         get() = R.string.keywords_wifi
58 
isEnablednull59     override fun isEnabled(context: Context) = super<PreferenceRestrictionMixin>.isEnabled(context)
60 
61     override val restrictionKeys
62         get() = arrayOf(UserManager.DISALLOW_CHANGE_WIFI_STATE)
63 
64     override val useAdminDisabledSummary: Boolean
65         get() = true
66 
67     override fun createWidget(context: Context) = RestrictedSwitchPreference(context)
68 
69     override fun bind(preference: Preference, metadata: PreferenceMetadata) {
70         super.bind(preference, metadata)
71         preference.onPreferenceChangeListener = this
72     }
73 
onPreferenceChangenull74     override fun onPreferenceChange(preference: Preference, newValue: Any): Boolean {
75         val context = preference.context
76 
77         // Show dialog and do nothing under satellite mode.
78         if (context.isSatelliteOn()) {
79             context.startActivity(
80                 Intent(context, SatelliteWarningDialogActivity::class.java)
81                     .putExtra(
82                         SatelliteWarningDialogActivity.EXTRA_TYPE_OF_SATELLITE_WARNING_DIALOG,
83                         SatelliteWarningDialogActivity.TYPE_IS_WIFI,
84                     )
85             )
86             return false
87         }
88 
89         // Show toast message if Wi-Fi is not allowed in airplane mode
90         if (newValue == true && !context.isRadioAllowed()) {
91             Toast.makeText(context, R.string.wifi_in_airplane_mode, Toast.LENGTH_SHORT).show()
92             return false
93         }
94 
95         return true
96     }
97 
getReadPermitnull98     override fun getReadPermit(context: Context, myUid: Int, callingUid: Int) =
99         ReadWritePermit.ALLOW
100 
101     override fun getWritePermit(context: Context, value: Boolean?, myUid: Int, callingUid: Int) =
102         when {
103             (value == true && !context.isRadioAllowed()) || context.isSatelliteOn() ->
104                 ReadWritePermit.DISALLOW
105             else -> ReadWritePermit.ALLOW
106         }
107 
108     override val sensitivityLevel
109         get() = SensitivityLevel.LOW_SENSITIVITY
110 
storagenull111     override fun storage(context: Context): KeyValueStore = WifiSwitchStore(context)
112 
113     @Suppress("UNCHECKED_CAST")
114     private class WifiSwitchStore(private val context: Context) :
115         AbstractKeyedDataObservable<String>(), KeyValueStore {
116 
117         private var broadcastReceiver: BroadcastReceiver? = null
118 
119         override fun contains(key: String) =
120             key == KEY && context.getSystemService(WifiManager::class.java) != null
121 
122         override fun <T : Any> getValue(key: String, valueType: Class<T>): T? =
123             context.getSystemService(WifiManager::class.java)?.isWifiEnabled as T?
124 
125         @Suppress("DEPRECATION")
126         override fun <T : Any> setValue(key: String, valueType: Class<T>, value: T?) {
127             if (value is Boolean) {
128                 context.getSystemService(WifiManager::class.java)?.isWifiEnabled = value
129             }
130         }
131 
132         override fun onFirstObserverAdded() {
133             broadcastReceiver =
134                 object : BroadcastReceiver() {
135                     override fun onReceive(context: Context, intent: Intent) {
136                         val wifiState = intent.wifiState
137                         // do not notify for enabling/disabling state
138                         if (
139                             wifiState == WifiManager.WIFI_STATE_ENABLED ||
140                                 wifiState == WifiManager.WIFI_STATE_DISABLED
141                         ) {
142                             notifyChange(KEY, DataChangeReason.UPDATE)
143                         }
144                     }
145                 }
146             context.registerReceiver(
147                 broadcastReceiver,
148                 IntentFilter(WifiManager.WIFI_STATE_CHANGED_ACTION),
149             )
150         }
151 
152         override fun onLastObserverRemoved() {
153             broadcastReceiver?.let { context.unregisterReceiver(it) }
154         }
155     }
156 
157     companion object {
158         const val TAG = "WifiSwitchPreference"
159         const val KEY = "main_toggle_wifi"
160 
Contextnull161         private fun Context.isRadioAllowed() =
162             WirelessUtils.isRadioAllowed(this, Settings.Global.RADIO_WIFI)
163 
164         private fun Context.isSatelliteOn() =
165             try {
166                 SatelliteRepository(this)
167                     .requestIsSessionStarted(Executors.newSingleThreadExecutor())
168                     .get(2000, TimeUnit.MILLISECONDS)
169             } catch (e: Exception) {
170                 Log.e(TAG, "Error to get satellite status : $e")
171                 false
172             }
173 
174         private val Intent.wifiState
175             get() = getIntExtra(WifiManager.EXTRA_WIFI_STATE, WifiManager.WIFI_STATE_UNKNOWN)
176     }
177 }
178 // LINT.ThenChange(WifiSwitchPreferenceController.java)
179