• 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 
17 package com.android.settings.wifi;
18 
19 import android.app.admin.DevicePolicyManager;
20 import android.content.ComponentName;
21 import android.content.ContentResolver;
22 import android.content.Context;
23 import android.content.pm.PackageManager;
24 import android.net.NetworkCapabilities;
25 import android.net.wifi.ScanResult;
26 import android.net.wifi.SoftApConfiguration;
27 import android.net.wifi.WifiConfiguration;
28 import android.os.UserHandle;
29 import android.os.UserManager;
30 import android.provider.Settings;
31 import android.text.TextUtils;
32 
33 import com.android.settings.Utils;
34 import com.android.settingslib.wifi.AccessPoint;
35 
36 import java.nio.charset.StandardCharsets;
37 
38 public class WifiUtils {
39 
40     private static final int SSID_ASCII_MIN_LENGTH = 1;
41     private static final int SSID_ASCII_MAX_LENGTH = 32;
42 
43 
isSSIDTooLong(String ssid)44     public static boolean isSSIDTooLong(String ssid) {
45         if (TextUtils.isEmpty(ssid)) {
46             return false;
47         }
48         return ssid.getBytes(StandardCharsets.UTF_8).length > SSID_ASCII_MAX_LENGTH;
49     }
50 
isSSIDTooShort(String ssid)51     public static boolean isSSIDTooShort(String ssid) {
52         if (TextUtils.isEmpty(ssid)) {
53             return true;
54         }
55         return ssid.length() < SSID_ASCII_MIN_LENGTH;
56     }
57 
58     /**
59      * Check if the WPA2-PSK hotspot password is valid.
60      */
isHotspotWpa2PasswordValid(String password)61     public static boolean isHotspotWpa2PasswordValid(String password) {
62         final SoftApConfiguration.Builder configBuilder = new SoftApConfiguration.Builder();
63         try {
64             configBuilder.setPassphrase(password, SoftApConfiguration.SECURITY_TYPE_WPA2_PSK);
65         } catch (IllegalArgumentException e) {
66             return false;
67         }
68         return true;
69     }
70 
71     /**
72      * This method is a stripped and negated version of WifiConfigStore.canModifyNetwork.
73      *
74      * @param context Context of caller
75      * @param config  The WiFi config.
76      * @return true if Settings cannot modify the config due to lockDown.
77      */
isNetworkLockedDown(Context context, WifiConfiguration config)78     public static boolean isNetworkLockedDown(Context context, WifiConfiguration config) {
79         if (config == null) {
80             return false;
81         }
82 
83         final DevicePolicyManager dpm =
84                 (DevicePolicyManager) context.getSystemService(Context.DEVICE_POLICY_SERVICE);
85         final PackageManager pm = context.getPackageManager();
86         final UserManager um = (UserManager) context.getSystemService(Context.USER_SERVICE);
87 
88         // Check if device has DPM capability. If it has and dpm is still null, then we
89         // treat this case with suspicion and bail out.
90         if (pm.hasSystemFeature(PackageManager.FEATURE_DEVICE_ADMIN) && dpm == null) {
91             return true;
92         }
93 
94         boolean isConfigEligibleForLockdown = false;
95         if (dpm != null) {
96             final ComponentName deviceOwner = dpm.getDeviceOwnerComponentOnAnyUser();
97             if (deviceOwner != null) {
98                 final int deviceOwnerUserId = dpm.getDeviceOwnerUserId();
99                 try {
100                     final int deviceOwnerUid = pm.getPackageUidAsUser(deviceOwner.getPackageName(),
101                             deviceOwnerUserId);
102                     isConfigEligibleForLockdown = deviceOwnerUid == config.creatorUid;
103                 } catch (PackageManager.NameNotFoundException e) {
104                     // don't care
105                 }
106             } else if (dpm.isOrganizationOwnedDeviceWithManagedProfile()) {
107                 int profileOwnerUserId = Utils.getManagedProfileId(um, UserHandle.myUserId());
108                 final ComponentName profileOwner = dpm.getProfileOwnerAsUser(profileOwnerUserId);
109                 if (profileOwner != null) {
110                     try {
111                         final int profileOwnerUid = pm.getPackageUidAsUser(
112                                 profileOwner.getPackageName(), profileOwnerUserId);
113                         isConfigEligibleForLockdown = profileOwnerUid == config.creatorUid;
114                     } catch (PackageManager.NameNotFoundException e) {
115                         // don't care
116                     }
117                 }
118             }
119         }
120         if (!isConfigEligibleForLockdown) {
121             return false;
122         }
123 
124         final ContentResolver resolver = context.getContentResolver();
125         final boolean isLockdownFeatureEnabled = Settings.Global.getInt(resolver,
126                 Settings.Global.WIFI_DEVICE_OWNER_CONFIGS_LOCKDOWN, 0) != 0;
127         return isLockdownFeatureEnabled;
128     }
129 
130     /** Returns true if the provided NetworkCapabilities indicate a captive portal network. */
canSignIntoNetwork(NetworkCapabilities capabilities)131     public static boolean canSignIntoNetwork(NetworkCapabilities capabilities) {
132         return (capabilities != null
133                 && capabilities.hasCapability(NetworkCapabilities.NET_CAPABILITY_CAPTIVE_PORTAL));
134     }
135 
136     /**
137      * Provides a simple way to generate a new {@link WifiConfiguration} obj from
138      * {@link ScanResult} or {@link AccessPoint}. Either {@code accessPoint} or {@code scanResult
139      * } input should be not null for retrieving information, otherwise will throw
140      * IllegalArgumentException.
141      * This method prefers to take {@link AccessPoint} input in priority. Therefore this method
142      * will take {@link AccessPoint} input as preferred data extraction source when you input
143      * both {@link AccessPoint} and {@link ScanResult}, and ignore {@link ScanResult} input.
144      *
145      * Duplicated and simplified method from {@link WifiConfigController#getConfig()}.
146      * TODO(b/120827021): Should be removed if the there is have a common one in shared place (e.g.
147      * SettingsLib).
148      *
149      * @param accessPoint Input data for retrieving WifiConfiguration.
150      * @param scanResult  Input data for retrieving WifiConfiguration.
151      * @return WifiConfiguration obj based on input.
152      */
getWifiConfig(AccessPoint accessPoint, ScanResult scanResult, String password)153     public static WifiConfiguration getWifiConfig(AccessPoint accessPoint, ScanResult scanResult,
154             String password) {
155         if (accessPoint == null && scanResult == null) {
156             throw new IllegalArgumentException(
157                     "At least one of AccessPoint and ScanResult input is required.");
158         }
159 
160         final WifiConfiguration config = new WifiConfiguration();
161         final int security;
162 
163         if (accessPoint == null) {
164             config.SSID = AccessPoint.convertToQuotedString(scanResult.SSID);
165             security = getAccessPointSecurity(scanResult);
166         } else {
167             if (!accessPoint.isSaved()) {
168                 config.SSID = AccessPoint.convertToQuotedString(
169                         accessPoint.getSsidStr());
170             } else {
171                 config.networkId = accessPoint.getConfig().networkId;
172                 config.hiddenSSID = accessPoint.getConfig().hiddenSSID;
173             }
174             security = accessPoint.getSecurity();
175         }
176 
177         switch (security) {
178             case AccessPoint.SECURITY_NONE:
179                 config.setSecurityParams(WifiConfiguration.SECURITY_TYPE_OPEN);
180                 break;
181 
182             case AccessPoint.SECURITY_WEP:
183                 config.setSecurityParams(WifiConfiguration.SECURITY_TYPE_WEP);
184                 if (!TextUtils.isEmpty(password)) {
185                     int length = password.length();
186                     // WEP-40, WEP-104, and 256-bit WEP (WEP-232?)
187                     if ((length == 10 || length == 26 || length == 58)
188                             && password.matches("[0-9A-Fa-f]*")) {
189                         config.wepKeys[0] = password;
190                     } else {
191                         config.wepKeys[0] = '"' + password + '"';
192                     }
193                 }
194                 break;
195 
196             case AccessPoint.SECURITY_PSK:
197                 config.setSecurityParams(WifiConfiguration.SECURITY_TYPE_PSK);
198                 if (!TextUtils.isEmpty(password)) {
199                     if (password.matches("[0-9A-Fa-f]{64}")) {
200                         config.preSharedKey = password;
201                     } else {
202                         config.preSharedKey = '"' + password + '"';
203                     }
204                 }
205                 break;
206 
207             case AccessPoint.SECURITY_EAP:
208             case AccessPoint.SECURITY_EAP_SUITE_B:
209                 if (security == AccessPoint.SECURITY_EAP_SUITE_B) {
210                     // allowedSuiteBCiphers will be set according to certificate type
211                     config.setSecurityParams(WifiConfiguration.SECURITY_TYPE_EAP_SUITE_B);
212                 } else {
213                     config.setSecurityParams(WifiConfiguration.SECURITY_TYPE_EAP);
214                 }
215 
216                 if (!TextUtils.isEmpty(password)) {
217                     config.enterpriseConfig.setPassword(password);
218                 }
219                 break;
220             case AccessPoint.SECURITY_SAE:
221                 config.setSecurityParams(WifiConfiguration.SECURITY_TYPE_SAE);
222                 if (!TextUtils.isEmpty(password)) {
223                     config.preSharedKey = '"' + password + '"';
224                 }
225                 break;
226 
227             case AccessPoint.SECURITY_OWE:
228                 config.setSecurityParams(WifiConfiguration.SECURITY_TYPE_OWE);
229                 break;
230 
231             default:
232                 break;
233         }
234 
235         return config;
236     }
237 
238 
239     /**
240      * Gets security value from ScanResult.
241      *
242      * Duplicated method from {@link AccessPoint#getSecurity(ScanResult)}.
243      * TODO(b/120827021): Should be removed if the there is have a common one in shared place (e.g.
244      * SettingsLib).
245      *
246      * @param result ScanResult
247      * @return Related security value based on {@link AccessPoint}.
248      */
getAccessPointSecurity(ScanResult result)249     public static int getAccessPointSecurity(ScanResult result) {
250         if (result.capabilities.contains("WEP")) {
251             return AccessPoint.SECURITY_WEP;
252         } else if (result.capabilities.contains("SAE")) {
253             return AccessPoint.SECURITY_SAE;
254         } else if (result.capabilities.contains("PSK")) {
255             return AccessPoint.SECURITY_PSK;
256         } else if (result.capabilities.contains("EAP_SUITE_B_192")) {
257             return AccessPoint.SECURITY_EAP_SUITE_B;
258         } else if (result.capabilities.contains("EAP")) {
259             return AccessPoint.SECURITY_EAP;
260         } else if (result.capabilities.contains("OWE")) {
261             return AccessPoint.SECURITY_OWE;
262         }
263 
264         return AccessPoint.SECURITY_NONE;
265     }
266 
267 
268     public static final int CONNECT_TYPE_OTHERS = 0;
269     public static final int CONNECT_TYPE_OPEN_NETWORK = 1;
270     public static final int CONNECT_TYPE_SAVED_NETWORK = 2;
271     public static final int CONNECT_TYPE_OSU_PROVISION = 3;
272 
273     /**
274      * Gets the connecting type of {@link AccessPoint}.
275      */
getConnectingType(AccessPoint accessPoint)276     public static int getConnectingType(AccessPoint accessPoint) {
277         final WifiConfiguration config = accessPoint.getConfig();
278         if (accessPoint.isOsuProvider()) {
279             return CONNECT_TYPE_OSU_PROVISION;
280         } else if ((accessPoint.getSecurity() == AccessPoint.SECURITY_NONE) ||
281                 (accessPoint.getSecurity() == AccessPoint.SECURITY_OWE)) {
282             return CONNECT_TYPE_OPEN_NETWORK;
283         } else if (accessPoint.isSaved() && config != null
284                 && config.getNetworkSelectionStatus() != null
285                 && config.getNetworkSelectionStatus().hasEverConnected()) {
286             return CONNECT_TYPE_SAVED_NETWORK;
287         } else if (accessPoint.isPasspoint()) {
288             // Access point provided by an installed Passpoint provider, connect using
289             // the associated config.
290             return CONNECT_TYPE_SAVED_NETWORK;
291         } else {
292             return CONNECT_TYPE_OTHERS;
293         }
294     }
295 }
296