• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2017 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 package com.android.car.settings.wifi;
17 
18 import static android.net.wifi.WifiConfiguration.NetworkSelectionStatus.NETWORK_SELECTION_ENABLED;
19 
20 import android.annotation.DrawableRes;
21 import android.annotation.Nullable;
22 import android.app.admin.DevicePolicyManager;
23 import android.content.ComponentName;
24 import android.content.ContentResolver;
25 import android.content.Context;
26 import android.content.pm.PackageManager;
27 import android.net.NetworkCapabilities;
28 import android.net.NetworkInfo;
29 import android.net.wifi.WifiConfiguration;
30 import android.net.wifi.WifiManager;
31 import android.provider.Settings;
32 import android.text.TextUtils;
33 import android.widget.Toast;
34 
35 import androidx.annotation.NonNull;
36 import androidx.annotation.StringRes;
37 
38 import com.android.car.settings.R;
39 import com.android.car.settings.common.Logger;
40 import com.android.settingslib.wifi.AccessPoint;
41 
42 import java.util.regex.Pattern;
43 
44 /**
45  * A collections of util functions for WIFI.
46  */
47 public class WifiUtil {
48 
49     private static final Logger LOG = new Logger(WifiUtil.class);
50 
51     /** Value that is returned when we fail to connect wifi. */
52     public static final int INVALID_NET_ID = -1;
53     private static final Pattern HEX_PATTERN = Pattern.compile("^[0-9A-F]+$");
54 
55     @DrawableRes
getIconRes(int state)56     public static int getIconRes(int state) {
57         switch (state) {
58             case WifiManager.WIFI_STATE_ENABLING:
59             case WifiManager.WIFI_STATE_DISABLED:
60                 return R.drawable.ic_settings_wifi_disabled;
61             default:
62                 return R.drawable.ic_settings_wifi;
63         }
64     }
65 
isWifiOn(int state)66     public static boolean isWifiOn(int state) {
67         switch (state) {
68             case WifiManager.WIFI_STATE_ENABLING:
69             case WifiManager.WIFI_STATE_DISABLED:
70                 return false;
71             default:
72                 return true;
73         }
74     }
75 
76     /**
77      * @return 0 if no proper description can be found.
78      */
79     @StringRes
getStateDesc(int state)80     public static Integer getStateDesc(int state) {
81         switch (state) {
82             case WifiManager.WIFI_STATE_ENABLING:
83                 return R.string.wifi_starting;
84             case WifiManager.WIFI_STATE_DISABLING:
85                 return R.string.wifi_stopping;
86             case WifiManager.WIFI_STATE_DISABLED:
87                 return R.string.wifi_disabled;
88             default:
89                 return 0;
90         }
91     }
92 
93     /**
94      * Returns {@Code true} if wifi is available on this device.
95      */
isWifiAvailable(Context context)96     public static boolean isWifiAvailable(Context context) {
97         return context.getPackageManager().hasSystemFeature(PackageManager.FEATURE_WIFI);
98     }
99 
100     /**
101      * Gets a unique key for a {@link AccessPoint}.
102      */
getKey(AccessPoint accessPoint)103     public static String getKey(AccessPoint accessPoint) {
104         return String.valueOf(accessPoint.hashCode());
105     }
106 
107     /**
108      * This method is a stripped and negated version of WifiConfigStore.canModifyNetwork.
109      *
110      * @param context Context of caller
111      * @param config  The WiFi config.
112      * @return {@code true} if Settings cannot modify the config due to lockDown.
113      */
isNetworkLockedDown(Context context, WifiConfiguration config)114     public static boolean isNetworkLockedDown(Context context, WifiConfiguration config) {
115         if (config == null) {
116             return false;
117         }
118 
119         final DevicePolicyManager dpm =
120                 (DevicePolicyManager) context.getSystemService(Context.DEVICE_POLICY_SERVICE);
121         final PackageManager pm = context.getPackageManager();
122 
123         // Check if device has DPM capability. If it has and dpm is still null, then we
124         // treat this case with suspicion and bail out.
125         if (pm.hasSystemFeature(PackageManager.FEATURE_DEVICE_ADMIN) && dpm == null) {
126             return true;
127         }
128 
129         boolean isConfigEligibleForLockdown = false;
130         if (dpm != null) {
131             final ComponentName deviceOwner = dpm.getDeviceOwnerComponentOnAnyUser();
132             if (deviceOwner != null) {
133                 final int deviceOwnerUserId = dpm.getDeviceOwnerUserId();
134                 try {
135                     final int deviceOwnerUid = pm.getPackageUidAsUser(deviceOwner.getPackageName(),
136                             deviceOwnerUserId);
137                     isConfigEligibleForLockdown = deviceOwnerUid == config.creatorUid;
138                 } catch (PackageManager.NameNotFoundException e) {
139                     // don't care
140                 }
141             }
142         }
143         if (!isConfigEligibleForLockdown) {
144             return false;
145         }
146 
147         final ContentResolver resolver = context.getContentResolver();
148         final boolean isLockdownFeatureEnabled = Settings.Global.getInt(resolver,
149                 Settings.Global.WIFI_DEVICE_OWNER_CONFIGS_LOCKDOWN, 0) != 0;
150         return isLockdownFeatureEnabled;
151     }
152 
153     /**
154      * Returns {@code true} if the network security type doesn't require authentication.
155      */
isOpenNetwork(int security)156     public static boolean isOpenNetwork(int security) {
157         return security == AccessPoint.SECURITY_NONE || security == AccessPoint.SECURITY_OWE;
158     }
159 
160     /**
161      * Returns {@code true} if the provided NetworkCapabilities indicate a captive portal network.
162      */
canSignIntoNetwork(NetworkCapabilities capabilities)163     public static boolean canSignIntoNetwork(NetworkCapabilities capabilities) {
164         return (capabilities != null
165                 && capabilities.hasCapability(NetworkCapabilities.NET_CAPABILITY_CAPTIVE_PORTAL));
166     }
167 
168     /**
169      * Attempts to connect to a specified access point
170      * @param listener for callbacks on success or failure of connection attempt (can be null)
171      */
connectToAccessPoint(Context context, String ssid, int security, String password, boolean hidden, @Nullable WifiManager.ActionListener listener)172     public static void connectToAccessPoint(Context context, String ssid, int security,
173             String password, boolean hidden, @Nullable WifiManager.ActionListener listener) {
174         WifiManager wifiManager = context.getSystemService(WifiManager.class);
175         WifiConfiguration wifiConfig = getWifiConfig(ssid, security, password, hidden);
176         wifiManager.connect(wifiConfig, listener);
177     }
178 
getWifiConfig(String ssid, int security, String password, boolean hidden)179     private static WifiConfiguration getWifiConfig(String ssid, int security,
180             String password, boolean hidden) {
181         WifiConfiguration wifiConfig = new WifiConfiguration();
182         wifiConfig.SSID = String.format("\"%s\"", ssid);
183         wifiConfig.hiddenSSID = hidden;
184 
185         return finishWifiConfig(wifiConfig, security, password);
186     }
187 
188     /** Similar to above, but uses AccessPoint to get additional relevant information. */
getWifiConfig(@onNull AccessPoint accessPoint, String password)189     public static WifiConfiguration getWifiConfig(@NonNull AccessPoint accessPoint,
190             String password) {
191         if (accessPoint == null) {
192             throw new IllegalArgumentException("AccessPoint input is required.");
193         }
194 
195         WifiConfiguration wifiConfig = new WifiConfiguration();
196         if (!accessPoint.isSaved()) {
197             wifiConfig.SSID = AccessPoint.convertToQuotedString(
198                     accessPoint.getSsidStr());
199         } else {
200             wifiConfig.networkId = accessPoint.getConfig().networkId;
201             wifiConfig.hiddenSSID = accessPoint.getConfig().hiddenSSID;
202         }
203 
204         return finishWifiConfig(wifiConfig, accessPoint.getSecurity(), password);
205     }
206 
finishWifiConfig(WifiConfiguration wifiConfig, int security, String password)207     private static WifiConfiguration finishWifiConfig(WifiConfiguration wifiConfig, int security,
208             String password) {
209         switch (security) {
210             case AccessPoint.SECURITY_NONE:
211                 wifiConfig.setSecurityParams(WifiConfiguration.SECURITY_TYPE_OPEN);
212                 break;
213             case AccessPoint.SECURITY_WEP:
214                 wifiConfig.setSecurityParams(WifiConfiguration.SECURITY_TYPE_WEP);
215                 if (!TextUtils.isEmpty(password)) {
216                     int length = password.length();
217                     // WEP-40, WEP-104, and 256-bit WEP (WEP-232?)
218                     if ((length == 10 || length == 26 || length == 58)
219                             && password.matches("[0-9A-Fa-f]*")) {
220                         wifiConfig.wepKeys[0] = password;
221                     } else {
222                         wifiConfig.wepKeys[0] = '"' + password + '"';
223                     }
224                 }
225                 break;
226             case AccessPoint.SECURITY_PSK:
227                 wifiConfig.setSecurityParams(WifiConfiguration.SECURITY_TYPE_PSK);
228                 if (!TextUtils.isEmpty(password)) {
229                     if (password.matches("[0-9A-Fa-f]{64}")) {
230                         wifiConfig.preSharedKey = password;
231                     } else {
232                         wifiConfig.preSharedKey = '"' + password + '"';
233                     }
234                 }
235                 break;
236             case AccessPoint.SECURITY_EAP:
237             case AccessPoint.SECURITY_EAP_SUITE_B:
238                 if (security == AccessPoint.SECURITY_EAP_SUITE_B) {
239                     // allowedSuiteBCiphers will be set according to certificate type
240                     wifiConfig.setSecurityParams(WifiConfiguration.SECURITY_TYPE_EAP_SUITE_B);
241                 } else {
242                     wifiConfig.setSecurityParams(WifiConfiguration.SECURITY_TYPE_EAP);
243                 }
244                 if (!TextUtils.isEmpty(password)) {
245                     wifiConfig.enterpriseConfig.setPassword(password);
246                 }
247                 break;
248             case AccessPoint.SECURITY_SAE:
249                 wifiConfig.setSecurityParams(WifiConfiguration.SECURITY_TYPE_SAE);
250                 if (!TextUtils.isEmpty(password)) {
251                     wifiConfig.preSharedKey = '"' + password + '"';
252                 }
253                 break;
254             case AccessPoint.SECURITY_OWE:
255                 wifiConfig.setSecurityParams(WifiConfiguration.SECURITY_TYPE_OWE);
256                 break;
257             default:
258                 throw new IllegalArgumentException("unknown security type " + security);
259         }
260         return wifiConfig;
261     }
262 
263     /** Forget the network specified by {@code accessPoint}. */
forget(Context context, AccessPoint accessPoint)264     public static void forget(Context context, AccessPoint accessPoint) {
265         WifiManager wifiManager = context.getSystemService(WifiManager.class);
266         if (!accessPoint.isSaved()) {
267             if (accessPoint.getNetworkInfo() != null
268                     && accessPoint.getNetworkInfo().getState() != NetworkInfo.State.DISCONNECTED) {
269                 // Network is active but has no network ID - must be ephemeral.
270                 wifiManager.disableEphemeralNetwork(
271                         AccessPoint.convertToQuotedString(accessPoint.getSsidStr()));
272             } else {
273                 // Should not happen, but a monkey seems to trigger it
274                 LOG.e("Failed to forget invalid network " + accessPoint.getConfig());
275                 return;
276             }
277         } else {
278             wifiManager.forget(accessPoint.getConfig().networkId, new WifiManager.ActionListener() {
279                 @Override
280                 public void onSuccess() {
281                     LOG.d("Network successfully forgotten");
282                 }
283 
284                 @Override
285                 public void onFailure(int reason) {
286                     LOG.d("Could not forget network. Failure code: " + reason);
287                     Toast.makeText(context, R.string.wifi_failed_forget_message,
288                             Toast.LENGTH_SHORT).show();
289                 }
290             });
291         }
292     }
293 
294     /** Returns {@code true} if the access point was disabled due to the wrong password. */
isAccessPointDisabledByWrongPassword(AccessPoint accessPoint)295     public static boolean isAccessPointDisabledByWrongPassword(AccessPoint accessPoint) {
296         WifiConfiguration config = accessPoint.getConfig();
297         if (config == null) {
298             return false;
299         }
300         WifiConfiguration.NetworkSelectionStatus networkStatus =
301                 config.getNetworkSelectionStatus();
302         if (networkStatus == null
303                 || networkStatus.getNetworkSelectionStatus() == NETWORK_SELECTION_ENABLED) {
304             return false;
305         }
306         return networkStatus.getNetworkSelectionDisableReason()
307                 == WifiConfiguration.NetworkSelectionStatus.DISABLED_BY_WRONG_PASSWORD;
308     }
309 
isHexString(String password)310     private static boolean isHexString(String password) {
311         return HEX_PATTERN.matcher(password).matches();
312     }
313 }
314