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.wifitrackerlib.WifiEntry;
35 
36 import java.nio.charset.StandardCharsets;
37 
38 /** A utility class for Wi-Fi functions. */
39 public class WifiUtils extends com.android.settingslib.wifi.WifiUtils {
40 
41     private static final int SSID_ASCII_MIN_LENGTH = 1;
42     private static final int SSID_ASCII_MAX_LENGTH = 32;
43 
44 
isSSIDTooLong(String ssid)45     public static boolean isSSIDTooLong(String ssid) {
46         if (TextUtils.isEmpty(ssid)) {
47             return false;
48         }
49         return ssid.getBytes(StandardCharsets.UTF_8).length > SSID_ASCII_MAX_LENGTH;
50     }
51 
isSSIDTooShort(String ssid)52     public static boolean isSSIDTooShort(String ssid) {
53         if (TextUtils.isEmpty(ssid)) {
54             return true;
55         }
56         return ssid.length() < SSID_ASCII_MIN_LENGTH;
57     }
58 
59     /**
60      * Check if the hotspot password is valid.
61      */
isHotspotPasswordValid(String password, int securityType)62     public static boolean isHotspotPasswordValid(String password, int securityType) {
63         final SoftApConfiguration.Builder configBuilder = new SoftApConfiguration.Builder();
64         try {
65             configBuilder.setPassphrase(password, securityType);
66         } catch (IllegalArgumentException e) {
67             return false;
68         }
69         return true;
70     }
71 
72     /**
73      * This method is a stripped and negated version of WifiConfigStore.canModifyNetwork.
74      *
75      * @param context Context of caller
76      * @param config  The WiFi config.
77      * @return true if Settings cannot modify the config due to lockDown.
78      */
isNetworkLockedDown(Context context, WifiConfiguration config)79     public static boolean isNetworkLockedDown(Context context, WifiConfiguration config) {
80         if (config == null) {
81             return false;
82         }
83 
84         final DevicePolicyManager dpm =
85                 (DevicePolicyManager) context.getSystemService(Context.DEVICE_POLICY_SERVICE);
86         final PackageManager pm = context.getPackageManager();
87         final UserManager um = (UserManager) context.getSystemService(Context.USER_SERVICE);
88 
89         // Check if device has DPM capability. If it has and dpm is still null, then we
90         // treat this case with suspicion and bail out.
91         if (pm.hasSystemFeature(PackageManager.FEATURE_DEVICE_ADMIN) && dpm == null) {
92             return true;
93         }
94 
95         boolean isConfigEligibleForLockdown = false;
96         if (dpm != null) {
97             final ComponentName deviceOwner = dpm.getDeviceOwnerComponentOnAnyUser();
98             if (deviceOwner != null) {
99                 final int deviceOwnerUserId = dpm.getDeviceOwnerUserId();
100                 try {
101                     final int deviceOwnerUid = pm.getPackageUidAsUser(deviceOwner.getPackageName(),
102                             deviceOwnerUserId);
103                     isConfigEligibleForLockdown = deviceOwnerUid == config.creatorUid;
104                 } catch (PackageManager.NameNotFoundException e) {
105                     // don't care
106                 }
107             } else if (dpm.isOrganizationOwnedDeviceWithManagedProfile()) {
108                 int profileOwnerUserId = Utils.getManagedProfileId(um, UserHandle.myUserId());
109                 final ComponentName profileOwner = dpm.getProfileOwnerAsUser(profileOwnerUserId);
110                 if (profileOwner != null) {
111                     try {
112                         final int profileOwnerUid = pm.getPackageUidAsUser(
113                                 profileOwner.getPackageName(), profileOwnerUserId);
114                         isConfigEligibleForLockdown = profileOwnerUid == config.creatorUid;
115                     } catch (PackageManager.NameNotFoundException e) {
116                         // don't care
117                     }
118                 }
119             }
120         }
121         if (!isConfigEligibleForLockdown) {
122             return false;
123         }
124 
125         final ContentResolver resolver = context.getContentResolver();
126         final boolean isLockdownFeatureEnabled = Settings.Global.getInt(resolver,
127                 Settings.Global.WIFI_DEVICE_OWNER_CONFIGS_LOCKDOWN, 0) != 0;
128         return isLockdownFeatureEnabled;
129     }
130 
131     /** Returns true if the provided NetworkCapabilities indicate a captive portal network. */
canSignIntoNetwork(NetworkCapabilities capabilities)132     public static boolean canSignIntoNetwork(NetworkCapabilities capabilities) {
133         return (capabilities != null
134                 && capabilities.hasCapability(NetworkCapabilities.NET_CAPABILITY_CAPTIVE_PORTAL));
135     }
136 
137     /**
138      * Provides a simple way to generate a new {@link WifiConfiguration} obj from
139      * {@link ScanResult} or {@link WifiEntry}. Either {@code wifiEntry} or {@code scanResult
140      * } input should be not null for retrieving information, otherwise will throw
141      * IllegalArgumentException.
142      * This method prefers to take {@link WifiEntry} input in priority. Therefore this method
143      * will take {@link WifiEntry} input as preferred data extraction source when you input
144      * both {@link WifiEntry} and {@link ScanResult}, and ignore {@link ScanResult} input.
145      *
146      * Duplicated and simplified method from {@link WifiConfigController#getConfig()}.
147      * TODO(b/120827021): Should be removed if the there is have a common one in shared place (e.g.
148      * SettingsLib).
149      *
150      * @param wifiEntry Input data for retrieving WifiConfiguration.
151      * @param scanResult  Input data for retrieving WifiConfiguration.
152      * @return WifiConfiguration obj based on input.
153      */
getWifiConfig(WifiEntry wifiEntry, ScanResult scanResult)154     public static WifiConfiguration getWifiConfig(WifiEntry wifiEntry, ScanResult scanResult) {
155         if (wifiEntry == null && scanResult == null) {
156             throw new IllegalArgumentException(
157                     "At least one of WifiEntry and ScanResult input is required.");
158         }
159 
160         final WifiConfiguration config = new WifiConfiguration();
161         final int security;
162 
163         if (wifiEntry == null) {
164             config.SSID = "\"" + scanResult.SSID + "\"";
165             security = getWifiEntrySecurity(scanResult);
166         } else {
167             if (wifiEntry.getWifiConfiguration() == null) {
168                 config.SSID = "\"" + wifiEntry.getSsid() + "\"";
169             } else {
170                 config.networkId = wifiEntry.getWifiConfiguration().networkId;
171                 config.hiddenSSID = wifiEntry.getWifiConfiguration().hiddenSSID;
172             }
173             security = wifiEntry.getSecurity();
174         }
175 
176         switch (security) {
177             case WifiEntry.SECURITY_NONE:
178                 config.setSecurityParams(WifiConfiguration.SECURITY_TYPE_OPEN);
179                 break;
180 
181             case WifiEntry.SECURITY_WEP:
182                 config.setSecurityParams(WifiConfiguration.SECURITY_TYPE_WEP);
183                 break;
184 
185             case WifiEntry.SECURITY_PSK:
186                 config.setSecurityParams(WifiConfiguration.SECURITY_TYPE_PSK);
187                 break;
188 
189             case WifiEntry.SECURITY_EAP_SUITE_B:
190                 config.setSecurityParams(WifiConfiguration.SECURITY_TYPE_EAP_SUITE_B);
191                 break;
192 
193             case WifiEntry.SECURITY_EAP:
194                 config.setSecurityParams(WifiConfiguration.SECURITY_TYPE_EAP);
195                 break;
196 
197             case WifiEntry.SECURITY_SAE:
198                 config.setSecurityParams(WifiConfiguration.SECURITY_TYPE_SAE);
199                 break;
200 
201             case WifiEntry.SECURITY_OWE:
202                 config.setSecurityParams(WifiConfiguration.SECURITY_TYPE_OWE);
203                 break;
204 
205             default:
206                 break;
207         }
208         return config;
209     }
210 
211     /**
212      * Gets security value from ScanResult.
213      *
214      * @param result ScanResult
215      * @return Related security value based on {@link WifiEntry}.
216      */
getWifiEntrySecurity(ScanResult result)217     public static int getWifiEntrySecurity(ScanResult result) {
218         if (result.capabilities.contains("WEP")) {
219             return WifiEntry.SECURITY_WEP;
220         } else if (result.capabilities.contains("SAE")) {
221             return WifiEntry.SECURITY_SAE;
222         } else if (result.capabilities.contains("PSK")) {
223             return WifiEntry.SECURITY_PSK;
224         } else if (result.capabilities.contains("EAP_SUITE_B_192")) {
225             return WifiEntry.SECURITY_EAP_SUITE_B;
226         } else if (result.capabilities.contains("EAP")) {
227             return WifiEntry.SECURITY_EAP;
228         } else if (result.capabilities.contains("OWE")) {
229             return WifiEntry.SECURITY_OWE;
230         }
231 
232         return WifiEntry.SECURITY_NONE;
233     }
234 }
235