1 /*
<lambda>null2  * 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.details2
18 
19 import android.content.Context
20 import android.net.wifi.WifiConfiguration
21 import android.net.wifi.WifiManager
22 import android.os.Bundle
23 import android.os.Handler
24 import android.os.HandlerThread
25 import android.os.Looper
26 import android.os.Process
27 import android.os.SimpleClock
28 import androidx.compose.foundation.layout.Column
29 import androidx.compose.foundation.layout.Spacer
30 import androidx.compose.foundation.layout.width
31 import androidx.compose.runtime.Composable
32 import androidx.compose.runtime.getValue
33 import androidx.compose.runtime.mutableIntStateOf
34 import androidx.compose.runtime.mutableStateOf
35 import androidx.compose.runtime.remember
36 import androidx.compose.runtime.saveable.rememberSaveable
37 import androidx.compose.runtime.setValue
38 import androidx.compose.ui.Modifier
39 import androidx.compose.ui.platform.LocalContext
40 import androidx.compose.ui.platform.LocalLifecycleOwner
41 import androidx.compose.ui.res.stringArrayResource
42 import androidx.compose.ui.res.stringResource
43 import androidx.navigation.NavType
44 import androidx.navigation.navArgument
45 import com.android.settings.R
46 import com.android.settings.overlay.FeatureFactory.Companion.featureFactory
47 import com.android.settingslib.spa.framework.common.SettingsPageProvider
48 import com.android.settingslib.spa.framework.theme.SettingsDimension
49 import com.android.settingslib.spa.widget.preference.ListPreferenceModel
50 import com.android.settingslib.spa.widget.preference.ListPreferenceOption
51 import com.android.settingslib.spa.widget.preference.RadioPreferences
52 import com.android.settingslib.spa.widget.preference.SwitchPreference
53 import com.android.settingslib.spa.widget.preference.SwitchPreferenceModel
54 import com.android.settingslib.spa.widget.scaffold.RegularScaffold
55 import com.android.settingslib.spa.widget.ui.CategoryTitle
56 import com.android.wifitrackerlib.WifiEntry
57 import java.time.Clock
58 import java.time.ZoneOffset
59 
60 const val WIFI_ENTRY_KEY = "wifiEntryKey"
61 
62 object WifiPrivacyPageProvider : SettingsPageProvider {
63     override val name = "WifiPrivacy"
64     const val TAG = "WifiPrivacyPageProvider"
65 
66     override val parameter = listOf(
67         navArgument(WIFI_ENTRY_KEY) { type = NavType.StringType },
68     )
69 
70     @Composable
71     override fun Page(arguments: Bundle?) {
72         val wifiEntryKey = arguments!!.getString(WIFI_ENTRY_KEY)
73         if (wifiEntryKey != null) {
74             val context = LocalContext.current
75             val lifecycle = LocalLifecycleOwner.current.lifecycle
76             val wifiEntry = remember {
77                 getWifiEntry(context, wifiEntryKey, lifecycle)
78             }
79             WifiPrivacyPage(wifiEntry)
80         }
81     }
82 
83     fun getRoute(
84         wifiEntryKey: String,
85     ): String = "${name}/$wifiEntryKey"
86 }
87 
88 @Composable
WifiPrivacyPagenull89 fun WifiPrivacyPage(wifiEntry: WifiEntry) {
90     val isSelectable: Boolean = wifiEntry.canSetPrivacy()
91     RegularScaffold(
92         title = stringResource(id = R.string.wifi_privacy_settings)
93     ) {
94         Column {
95             val title = stringResource(id = R.string.wifi_privacy_mac_settings)
96             val wifiPrivacyEntries = stringArrayResource(R.array.wifi_privacy_entries)
97             val wifiPrivacyValues = stringArrayResource(R.array.wifi_privacy_values)
98             val textsSelectedId = rememberSaveable { mutableIntStateOf(wifiEntry.privacy) }
99             val dataList = remember {
100                 wifiPrivacyEntries.mapIndexed { index, text ->
101                     ListPreferenceOption(id = wifiPrivacyValues[index].toInt(), text = text)
102                 }
103             }
104             RadioPreferences(remember {
105                 object : ListPreferenceModel {
106                     override val title = title
107                     override val options = dataList
108                     override val selectedId = textsSelectedId
109                     override val onIdSelected: (Int) -> Unit = {
110                         textsSelectedId.intValue = it
111                         onSelectedChange(wifiEntry, it)
112                     }
113                     override val enabled = { isSelectable }
114                 }
115             })
116             wifiEntry.wifiConfiguration?.let {
117                 DeviceNameSwitchPreference(it)
118             }
119         }
120     }
121 }
122 
123 @Composable
DeviceNameSwitchPreferencenull124 fun DeviceNameSwitchPreference(wifiConfiguration: WifiConfiguration){
125     Spacer(modifier = Modifier.width(SettingsDimension.itemDividerHeight))
126     CategoryTitle(title = stringResource(R.string.wifi_privacy_device_name_settings))
127     Spacer(modifier = Modifier.width(SettingsDimension.itemDividerHeight))
128     var checked by remember {
129         mutableStateOf(wifiConfiguration.isSendDhcpHostnameEnabled)
130     }
131     val context = LocalContext.current
132     val wifiManager = context.getSystemService(WifiManager::class.java)!!
133     SwitchPreference(object : SwitchPreferenceModel {
134         override val title =
135             context.resources.getString(
136                 R.string.wifi_privacy_send_device_name_toggle_title
137             )
138         override val summary =
139             {
140                 context.resources.getString(
141                     R.string.wifi_privacy_send_device_name_toggle_summary
142                 )
143             }
144         override val checked = { checked }
145         override val onCheckedChange: (Boolean) -> Unit = { newChecked ->
146             wifiConfiguration.isSendDhcpHostnameEnabled = newChecked
147             wifiManager.save(wifiConfiguration, null /* listener */)
148             checked = newChecked
149         }
150     })
151 }
152 
onSelectedChangenull153 fun onSelectedChange(wifiEntry: WifiEntry, privacy: Int) {
154     if (wifiEntry.privacy == privacy) {
155         // Prevent disconnection + reconnection if settings not changed.
156         return
157     }
158     wifiEntry.setPrivacy(privacy)
159 
160     // To activate changing, we need to reconnect network. WiFi will auto connect to
161     // current network after disconnect(). Only needed when this is connected network.
162 
163     // To activate changing, we need to reconnect network. WiFi will auto connect to
164     // current network after disconnect(). Only needed when this is connected network.
165     if (wifiEntry.getConnectedState() == WifiEntry.CONNECTED_STATE_CONNECTED) {
166         wifiEntry.disconnect(null /* callback */)
167         wifiEntry.connect(null /* callback */)
168     }
169 }
170 
getWifiEntrynull171 fun getWifiEntry(
172     context: Context,
173     wifiEntryKey: String,
174     liftCycle: androidx.lifecycle.Lifecycle
175 ): WifiEntry {
176     // Max age of tracked WifiEntries
177     val MAX_SCAN_AGE_MILLIS: Long = 15000
178     // Interval between initiating SavedNetworkTracker scans
179     val SCAN_INTERVAL_MILLIS: Long = 10000
180     val mWorkerThread = HandlerThread(
181         WifiPrivacyPageProvider.TAG,
182         Process.THREAD_PRIORITY_BACKGROUND
183     )
184     mWorkerThread.start()
185     val elapsedRealtimeClock: Clock = object : SimpleClock(ZoneOffset.UTC) {
186         override fun millis(): Long {
187             return android.os.SystemClock.elapsedRealtime()
188         }
189     }
190     val mNetworkDetailsTracker = featureFactory
191         .wifiTrackerLibProvider
192         .createNetworkDetailsTracker(
193             liftCycle,
194             context,
195             Handler(Looper.getMainLooper()),
196             mWorkerThread.getThreadHandler(),
197             elapsedRealtimeClock,
198             MAX_SCAN_AGE_MILLIS,
199             SCAN_INTERVAL_MILLIS,
200             wifiEntryKey
201         )
202     return mNetworkDetailsTracker.wifiEntry
203 }
204