1 /*
2  * Copyright (C) 2021 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.tv.settings.library.network;
18 
19 
20 import android.app.Activity;
21 import android.content.Context;
22 import android.content.Intent;
23 import android.net.wifi.WifiConfiguration;
24 import android.net.wifi.WifiInfo;
25 import android.net.wifi.WifiManager;
26 import android.os.Binder;
27 import android.os.Bundle;
28 import android.os.UserHandle;
29 import android.text.TextUtils;
30 
31 import androidx.annotation.NonNull;
32 
33 import com.android.tv.settings.library.ManagerUtil;
34 import com.android.tv.settings.library.PreferenceCompat;
35 import com.android.tv.settings.library.UIUpdateCallback;
36 import com.android.tv.settings.library.data.PreferenceCompatManager;
37 import com.android.tv.settings.library.data.PreferenceControllerState;
38 import com.android.tv.settings.library.settingslib.RestrictedLockUtils;
39 import com.android.tv.settings.library.util.AbstractPreferenceController;
40 import com.android.tv.settings.library.util.DataBinder;
41 import com.android.tv.settings.library.util.ResourcesUtil;
42 import com.android.tv.settings.library.util.RestrictedPreferenceController;
43 
44 import java.util.ArrayList;
45 import java.util.List;
46 
47 public class WifiDetailsState extends PreferenceControllerState implements
48         ConnectivityListener.Listener, ConnectivityListener.WifiNetworkListener {
49     private static final String TAG = "WifiDetailsState";
50 
51     private static final String ARG_ACCESS_POINT_STATE = "apBundle";
52     private static final String KEY_CONNECTION_STATUS = "connection_status";
53     private static final String KEY_IP_ADDRESS = "ip_address";
54     private static final String KEY_MAC_ADDRESS = "mac_address";
55     private static final String KEY_SIGNAL_STRENGTH = "signal_strength";
56     private static final String KEY_RANDOM_MAC = "random_mac";
57     private static final String VALUE_MAC_RANDOM = "random";
58     private static final String VALUE_MAC_DEVICE = "device";
59     public static final String EXTRA_NETWORK_ID = "network_id";
60 
61     public static final int REQUEST_CODE_FORGET_NETWORK = 1;
62 
63     private NetworkModule mNetworkModule;
64     private AccessPoint mAccessPoint;
65     PreferenceCompatManager mPreferenceCompatManager;
66     private PreferenceCompat mConnectionStatusPref;
67     private PreferenceCompat mIpAddressPref;
68     private PreferenceCompat mMacAddressPref;
69     private PreferenceCompat mSignalStrengthPref;
70     private PreferenceCompat mRandomMacPref;
71     private AbstractPreferenceController mProxySettingsPrefController;
72     private AbstractPreferenceController mIpSettingsPrefController;
73     private AbstractPreferenceController mForgetNetworkPrefController;
74 
WifiDetailsState(Context context, UIUpdateCallback callback)75     public WifiDetailsState(Context context,
76             UIUpdateCallback callback) {
77         super(context, callback);
78         mNetworkModule = NetworkModule.getInstance(mContext);
79     }
80 
prepareArgs(@onNull Bundle args, AccessPoint accessPoint)81     public static void prepareArgs(@NonNull Bundle args, AccessPoint accessPoint) {
82         args.putBinder(ARG_ACCESS_POINT_STATE, DataBinder.with(accessPoint));
83     }
84 
85     @Override
onCreate(Bundle extras)86     public void onCreate(Bundle extras) {
87         mNetworkModule = NetworkModule.getInstance(mContext);
88         mPreferenceCompatManager = new PreferenceCompatManager();
89         mAccessPoint = DataBinder.getData(extras.getBinder(ARG_ACCESS_POINT_STATE));
90         if (mUIUpdateCallback != null) {
91             mUIUpdateCallback.notifyUpdateScreenTitle(getStateIdentifier(),
92                     String.valueOf(mAccessPoint.getSsid()));
93         }
94         mConnectionStatusPref = mPreferenceCompatManager.getOrCreatePrefCompat(
95                 KEY_CONNECTION_STATUS);
96         mIpAddressPref = mPreferenceCompatManager.getOrCreatePrefCompat(KEY_IP_ADDRESS);
97         mMacAddressPref = mPreferenceCompatManager.getOrCreatePrefCompat(KEY_MAC_ADDRESS);
98         mSignalStrengthPref = mPreferenceCompatManager.getOrCreatePrefCompat(
99                 KEY_SIGNAL_STRENGTH);
100         mRandomMacPref = mPreferenceCompatManager.getOrCreatePrefCompat(KEY_RANDOM_MAC);
101         mRandomMacPref.setType(PreferenceCompat.TYPE_LIST);
102         super.onCreate(extras);
103     }
104 
105     @Override
onStart()106     public void onStart() {
107         super.onStart();
108         mNetworkModule.addState(this);
109     }
110 
111     @Override
onResume()112     public void onResume() {
113         super.onResume();
114         update();
115     }
116 
117     @Override
onStop()118     public void onStop() {
119         super.onStop();
120         mNetworkModule.getConnectivityListener().stop();
121         mNetworkModule.removeState(this);
122     }
123 
124     @Override
onPreferenceTreeClick(String[] key, boolean status)125     public boolean onPreferenceTreeClick(String[] key, boolean status) {
126         return super.onPreferenceTreeClick(key, status);
127     }
128 
129     @Override
onActivityResult(int requestCode, int resultCode, Intent data)130     public void onActivityResult(int requestCode, int resultCode, Intent data) {
131         if (requestCode == REQUEST_CODE_FORGET_NETWORK) {
132             if (resultCode == Activity.RESULT_OK) {
133                 WifiManager wifiManager = mContext.getSystemService(WifiManager.class);
134                 wifiManager.forget(mAccessPoint.getConfig().networkId, null);
135             }
136         }
137     }
138 
139     @Override
onPreferenceChange(String[] key, Object newValue)140     public boolean onPreferenceChange(String[] key, Object newValue) {
141         mNetworkModule.getConnectivityListener().applyMacRandomizationSetting(
142                 mAccessPoint,
143                 VALUE_MAC_RANDOM.equals(newValue));
144         return true;
145     }
146 
147     @Override
getStateIdentifier()148     public int getStateIdentifier() {
149         return ManagerUtil.STATE_WIFI_DETAILS;
150     }
151 
152     @Override
onConnectivityChange()153     public void onConnectivityChange() {
154         update();
155     }
156 
157     @Override
onWifiListChanged()158     public void onWifiListChanged() {
159         final List<AccessPoint> accessPoints = mNetworkModule
160                 .getConnectivityListener().getAvailableNetworks();
161         for (final AccessPoint accessPoint : accessPoints) {
162             if (TextUtils.equals(mAccessPoint.getSsidStr(), accessPoint.getSsidStr())
163                     && mAccessPoint.getSecurity() == accessPoint.getSecurity()) {
164                 // Make sure we're not holding on to the one we inflated from the bundle, because
165                 // it won't be updated
166                 mAccessPoint = accessPoint;
167                 break;
168             }
169         }
170         update();
171     }
172 
update()173     private void update() {
174         List<PreferenceCompat> preferenceCompats = new ArrayList<>();
175         if (mAccessPoint == null) {
176             return;
177         }
178         final boolean active = mAccessPoint.isActive();
179 
180         mConnectionStatusPref.setSummary(active
181                 ? ResourcesUtil.getString(mContext, "connected")
182                 : ResourcesUtil.getString(mContext, "not_connected"));
183         mIpAddressPref.setVisible(active);
184         mSignalStrengthPref.setVisible(active);
185         preferenceCompats.add(mConnectionStatusPref);
186         preferenceCompats.add(mIpAddressPref);
187         preferenceCompats.add(mSignalStrengthPref);
188         preferenceCompats.add(mMacAddressPref);
189 
190         if (active) {
191             mIpAddressPref.setSummary(mNetworkModule.getConnectivityListener().getWifiIpAddress());
192             mSignalStrengthPref.setSummary(getSignalStrength());
193         }
194 
195         // Mac address related Preferences (info entry and random mac setting entry)
196         String macAddress = mNetworkModule.getConnectivityListener()
197                 .getWifiMacAddress(mAccessPoint);
198         if (active && !TextUtils.isEmpty(macAddress)) {
199             mMacAddressPref.setVisible(true);
200             updateMacAddressPref(macAddress);
201             updateRandomMacPref();
202         } else {
203             mMacAddressPref.setVisible(false);
204             mRandomMacPref.setVisible(false);
205         }
206 
207         if (mUIUpdateCallback != null) {
208             mUIUpdateCallback.notifyUpdateAll(getStateIdentifier(), preferenceCompats);
209         }
210     }
211 
212     @Override
onCreatePreferenceControllers(Context context)213     protected List<AbstractPreferenceController> onCreatePreferenceControllers(Context context) {
214         List<AbstractPreferenceController> controllers = new ArrayList<>();
215         mProxySettingsPrefController = new ProxySettingsPreferenceController(context,
216                 mUIUpdateCallback, getStateIdentifier(), mPreferenceCompatManager, mAccessPoint);
217         mIpSettingsPrefController = new IpSettingsPreferenceController(context,
218                 mUIUpdateCallback, getStateIdentifier(), mPreferenceCompatManager, mAccessPoint);
219         mForgetNetworkPrefController = new ForgetNetworkPreferenceController(context,
220                 mUIUpdateCallback, getStateIdentifier(), mPreferenceCompatManager, mAccessPoint);
221         controllers.add(mProxySettingsPrefController);
222         controllers.add(mIpSettingsPrefController);
223         controllers.add(mForgetNetworkPrefController);
224         return controllers;
225     }
226 
updateMacAddressPref(String macAddress)227     private void updateMacAddressPref(String macAddress) {
228         if (WifiInfo.DEFAULT_MAC_ADDRESS.equals(macAddress)) {
229             mMacAddressPref.setSummary(
230                     ResourcesUtil.getString(mContext, "mac_address_not_available"));
231         } else {
232             mMacAddressPref.setSummary(macAddress);
233         }
234         if (mAccessPoint == null || mAccessPoint.getConfig() == null) {
235             return;
236         }
237         // For saved Passpoint network, framework doesn't have the field to keep the MAC choice
238         // persistently, so Passpoint network will always use the default value so far, which is
239         // randomized MAC address, so don't need to modify title.
240         if (mAccessPoint.isPasspoint()) {
241             return;
242         }
243         mMacAddressPref.setTitle(
244                 (mAccessPoint.getConfig().macRandomizationSetting
245                         == WifiConfiguration.RANDOMIZATION_PERSISTENT)
246                         ? ResourcesUtil.getString(mContext, "title_randomized_mac_address")
247                         : ResourcesUtil.getString(mContext, "title_mac_address"));
248     }
249 
updateRandomMacPref()250     private void updateRandomMacPref() {
251         ConnectivityListener connectivityListener = mNetworkModule.getConnectivityListener();
252         mRandomMacPref.setVisible(connectivityListener.isMacAddressRandomizationSupported());
253         boolean isMacRandomized =
254                 (connectivityListener.getWifiMacRandomizationSetting(mAccessPoint)
255                         == WifiConfiguration.RANDOMIZATION_PERSISTENT);
256         mRandomMacPref.setValue(isMacRandomized ? VALUE_MAC_RANDOM : VALUE_MAC_DEVICE);
257         if (!mAccessPoint.getWifiEntry().canSetPrivacy()) {
258             mRandomMacPref.setSelectable(PreferenceCompat.STATUS_OFF);
259             mRandomMacPref.setSummary(ResourcesUtil.getString(
260                     mContext, "mac_address_ephemeral_summary"));
261         } else {
262             mRandomMacPref.setSelectable(PreferenceCompat.STATUS_ON);
263             String[] entries = ResourcesUtil.getStringArray(
264                     mContext, "random_mac_settings_entries");
265             mRandomMacPref.setHasOnPreferenceChangeListener(true);
266             mRandomMacPref.setSummary(entries[isMacRandomized ? 0 : 1]);
267         }
268     }
269 
getSignalStrength()270     private String getSignalStrength() {
271         String[] signalLevels = ResourcesUtil
272                 .getStringArray(mContext, "wifi_signal_strength");
273         if (signalLevels != null) {
274             int strength = mNetworkModule.getConnectivityListener()
275                     .getWifiSignalStrength(signalLevels.length);
276             return signalLevels[strength];
277         }
278         return "";
279     }
280 
updateRestrictedPreference( PreferenceCompat preferenceCompat, Context context, AccessPoint accessPoint, RestrictedPreferenceController restrictedPreferenceController)281     public static void updateRestrictedPreference(
282             PreferenceCompat preferenceCompat, Context context, AccessPoint accessPoint,
283             RestrictedPreferenceController restrictedPreferenceController) {
284         WifiConfiguration wifiConfiguration = accessPoint.getConfig();
285         boolean canModifyNetwork = !WifiHelper.isNetworkLockedDown(
286                 context, wifiConfiguration);
287         preferenceCompat.setVisible(wifiConfiguration != null);
288         if (canModifyNetwork) {
289             restrictedPreferenceController.setDisabledByAdmin(null);
290             restrictedPreferenceController.setEnabled(true);
291         } else {
292             RestrictedLockUtils.EnforcedAdmin admin = RestrictedLockUtils.getProfileOrDeviceOwner(
293                     context,
294                     UserHandle.of(UserHandle.myUserId()));
295             restrictedPreferenceController.setDisabledByAdmin(admin);
296         }
297     }
298 }
299