• 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.ActionBar;
20 import android.app.Activity;
21 import android.app.admin.DevicePolicyManager;
22 import android.content.ComponentName;
23 import android.content.ContentResolver;
24 import android.content.Context;
25 import android.content.pm.PackageManager;
26 import android.net.NetworkCapabilities;
27 import android.net.TetheringManager;
28 import android.net.wifi.ScanResult;
29 import android.net.wifi.SoftApConfiguration;
30 import android.net.wifi.WifiConfiguration;
31 import android.net.wifi.WifiManager;
32 import android.os.UserHandle;
33 import android.os.UserManager;
34 import android.provider.Settings;
35 import android.text.TextUtils;
36 import android.util.Log;
37 import android.util.TypedValue;
38 
39 import androidx.annotation.NonNull;
40 import androidx.annotation.VisibleForTesting;
41 import androidx.core.graphics.Insets;
42 import androidx.core.view.ViewCompat;
43 import androidx.core.view.WindowInsetsCompat;
44 
45 import com.android.settings.R;
46 import com.android.settings.Utils;
47 import com.android.wifitrackerlib.WifiEntry;
48 
49 import java.nio.charset.StandardCharsets;
50 
51 /** A utility class for Wi-Fi functions. */
52 public class WifiUtils extends com.android.settingslib.wifi.WifiUtils {
53 
54     static final String TAG = "WifiUtils";
55 
56     private static final int SSID_ASCII_MIN_LENGTH = 1;
57     private static final int SSID_ASCII_MAX_LENGTH = 32;
58 
59     private static final int PSK_PASSPHRASE_ASCII_MIN_LENGTH = 8;
60     private static final int PSK_PASSPHRASE_ASCII_MAX_LENGTH = 63;
61 
62     private static Boolean sCanShowWifiHotspotCached;
63 
isSSIDTooLong(String ssid)64     public static boolean isSSIDTooLong(String ssid) {
65         if (TextUtils.isEmpty(ssid)) {
66             return false;
67         }
68         return ssid.getBytes(StandardCharsets.UTF_8).length > SSID_ASCII_MAX_LENGTH;
69     }
70 
isSSIDTooShort(String ssid)71     public static boolean isSSIDTooShort(String ssid) {
72         if (TextUtils.isEmpty(ssid)) {
73             return true;
74         }
75         return ssid.length() < SSID_ASCII_MIN_LENGTH;
76     }
77 
78     /**
79      * Check if the hotspot password is valid.
80      */
isHotspotPasswordValid(String password, int securityType)81     public static boolean isHotspotPasswordValid(String password, int securityType) {
82         final SoftApConfiguration.Builder configBuilder = new SoftApConfiguration.Builder();
83         try {
84             if (securityType == SoftApConfiguration.SECURITY_TYPE_WPA2_PSK
85                     || securityType == SoftApConfiguration.SECURITY_TYPE_WPA3_SAE_TRANSITION) {
86                 if (password.length() < PSK_PASSPHRASE_ASCII_MIN_LENGTH
87                         || password.length() > PSK_PASSPHRASE_ASCII_MAX_LENGTH) {
88                     return false;
89                 }
90             }
91             configBuilder.setPassphrase(password, securityType);
92         } catch (Exception e) {
93             return false;
94         }
95         return true;
96     }
97 
98     /**
99      * This method is a stripped and negated version of WifiConfigStore.canModifyNetwork.
100      *
101      * @param context Context of caller
102      * @param config  The WiFi config.
103      * @return true if Settings cannot modify the config due to lockDown.
104      */
isNetworkLockedDown(Context context, WifiConfiguration config)105     public static boolean isNetworkLockedDown(Context context, WifiConfiguration config) {
106         if (context == null || config == null) {
107             return false;
108         }
109 
110         final DevicePolicyManager dpm =
111                 (DevicePolicyManager) context.getSystemService(Context.DEVICE_POLICY_SERVICE);
112         final PackageManager pm = context.getPackageManager();
113         final UserManager um = (UserManager) context.getSystemService(Context.USER_SERVICE);
114 
115         // Check if device has DPM capability. If it has and dpm is still null, then we
116         // treat this case with suspicion and bail out.
117         if (pm.hasSystemFeature(PackageManager.FEATURE_DEVICE_ADMIN) && dpm == null) {
118             return true;
119         }
120 
121         boolean isConfigEligibleForLockdown = false;
122         if (dpm != null) {
123             final ComponentName deviceOwner = dpm.getDeviceOwnerComponentOnAnyUser();
124             if (deviceOwner != null) {
125                 final int deviceOwnerUserId = dpm.getDeviceOwnerUserId();
126                 try {
127                     final int deviceOwnerUid = pm.getPackageUidAsUser(deviceOwner.getPackageName(),
128                             deviceOwnerUserId);
129                     isConfigEligibleForLockdown = deviceOwnerUid == config.creatorUid;
130                 } catch (PackageManager.NameNotFoundException e) {
131                     // don't care
132                 }
133             } else if (dpm.isOrganizationOwnedDeviceWithManagedProfile()) {
134                 int profileOwnerUserId = Utils.getManagedProfileId(um, UserHandle.myUserId());
135                 final ComponentName profileOwner = dpm.getProfileOwnerAsUser(profileOwnerUserId);
136                 if (profileOwner != null) {
137                     try {
138                         final int profileOwnerUid = pm.getPackageUidAsUser(
139                                 profileOwner.getPackageName(), profileOwnerUserId);
140                         isConfigEligibleForLockdown = profileOwnerUid == config.creatorUid;
141                     } catch (PackageManager.NameNotFoundException e) {
142                         // don't care
143                     }
144                 }
145             }
146         }
147         if (!isConfigEligibleForLockdown) {
148             return false;
149         }
150 
151         final ContentResolver resolver = context.getContentResolver();
152         final boolean isLockdownFeatureEnabled = Settings.Global.getInt(resolver,
153                 Settings.Global.WIFI_DEVICE_OWNER_CONFIGS_LOCKDOWN, 0) != 0;
154         return isLockdownFeatureEnabled;
155     }
156 
157     /** Returns true if the provided NetworkCapabilities indicate a captive portal network. */
canSignIntoNetwork(NetworkCapabilities capabilities)158     public static boolean canSignIntoNetwork(NetworkCapabilities capabilities) {
159         return (capabilities != null
160                 && capabilities.hasCapability(NetworkCapabilities.NET_CAPABILITY_CAPTIVE_PORTAL));
161     }
162 
163     /**
164      * Provides a simple way to generate a new {@link WifiConfiguration} obj from
165      * {@link ScanResult} or {@link WifiEntry}. Either {@code wifiEntry} or {@code scanResult
166      * } input should be not null for retrieving information, otherwise will throw
167      * IllegalArgumentException.
168      * This method prefers to take {@link WifiEntry} input in priority. Therefore this method
169      * will take {@link WifiEntry} input as preferred data extraction source when you input
170      * both {@link WifiEntry} and {@link ScanResult}, and ignore {@link ScanResult} input.
171      *
172      * Duplicated and simplified method from {@link WifiConfigController#getConfig()}.
173      * TODO(b/120827021): Should be removed if the there is have a common one in shared place (e.g.
174      * SettingsLib).
175      *
176      * @param wifiEntry Input data for retrieving WifiConfiguration.
177      * @param scanResult  Input data for retrieving WifiConfiguration.
178      * @return WifiConfiguration obj based on input.
179      */
getWifiConfig(WifiEntry wifiEntry, ScanResult scanResult)180     public static WifiConfiguration getWifiConfig(WifiEntry wifiEntry, ScanResult scanResult) {
181         if (wifiEntry == null && scanResult == null) {
182             throw new IllegalArgumentException(
183                     "At least one of WifiEntry and ScanResult input is required.");
184         }
185 
186         final WifiConfiguration config = new WifiConfiguration();
187         final int security;
188 
189         if (wifiEntry == null) {
190             config.SSID = "\"" + scanResult.SSID + "\"";
191             security = getWifiEntrySecurity(scanResult);
192         } else {
193             if (wifiEntry.getWifiConfiguration() == null) {
194                 config.SSID = "\"" + wifiEntry.getSsid() + "\"";
195             } else {
196                 config.networkId = wifiEntry.getWifiConfiguration().networkId;
197                 config.hiddenSSID = wifiEntry.getWifiConfiguration().hiddenSSID;
198             }
199             security = wifiEntry.getSecurity();
200         }
201 
202         switch (security) {
203             case WifiEntry.SECURITY_NONE:
204                 config.setSecurityParams(WifiConfiguration.SECURITY_TYPE_OPEN);
205                 break;
206 
207             case WifiEntry.SECURITY_WEP:
208                 config.setSecurityParams(WifiConfiguration.SECURITY_TYPE_WEP);
209                 break;
210 
211             case WifiEntry.SECURITY_PSK:
212                 config.setSecurityParams(WifiConfiguration.SECURITY_TYPE_PSK);
213                 break;
214 
215             case WifiEntry.SECURITY_EAP_SUITE_B:
216                 config.setSecurityParams(WifiConfiguration.SECURITY_TYPE_EAP_SUITE_B);
217                 break;
218 
219             case WifiEntry.SECURITY_EAP:
220                 config.setSecurityParams(WifiConfiguration.SECURITY_TYPE_EAP);
221                 break;
222 
223             case WifiEntry.SECURITY_SAE:
224                 config.setSecurityParams(WifiConfiguration.SECURITY_TYPE_SAE);
225                 break;
226 
227             case WifiEntry.SECURITY_OWE:
228                 config.setSecurityParams(WifiConfiguration.SECURITY_TYPE_OWE);
229                 break;
230 
231             default:
232                 break;
233         }
234         return config;
235     }
236 
237     /**
238      * Gets security value from ScanResult.
239      *
240      * @param result ScanResult
241      * @return Related security value based on {@link WifiEntry}.
242      */
getWifiEntrySecurity(ScanResult result)243     public static int getWifiEntrySecurity(ScanResult result) {
244         if (result.capabilities.contains("WEP")) {
245             return WifiEntry.SECURITY_WEP;
246         } else if (result.capabilities.contains("SAE")) {
247             return WifiEntry.SECURITY_SAE;
248         } else if (result.capabilities.contains("PSK")) {
249             return WifiEntry.SECURITY_PSK;
250         } else if (result.capabilities.contains("EAP_SUITE_B_192")) {
251             return WifiEntry.SECURITY_EAP_SUITE_B;
252         } else if (result.capabilities.contains("EAP")) {
253             return WifiEntry.SECURITY_EAP;
254         } else if (result.capabilities.contains("OWE")) {
255             return WifiEntry.SECURITY_OWE;
256         }
257 
258         return WifiEntry.SECURITY_NONE;
259     }
260 
261     /**
262      * Check if Wi-Fi hotspot settings can be displayed.
263      *
264      * @param context Context of caller
265      * @return true if Wi-Fi hotspot settings can be displayed
266      */
checkShowWifiHotspot(Context context)267     public static boolean checkShowWifiHotspot(Context context) {
268         if (context == null) return false;
269 
270         boolean showWifiHotspotSettings =
271                 context.getResources().getBoolean(R.bool.config_show_wifi_hotspot_settings);
272         if (!showWifiHotspotSettings) {
273             Log.w(TAG, "config_show_wifi_hotspot_settings:false");
274             return false;
275         }
276 
277         WifiManager wifiManager = context.getSystemService(WifiManager.class);
278         if (wifiManager == null) {
279             Log.e(TAG, "WifiManager is null");
280             return false;
281         }
282 
283         TetheringManager tetheringManager = context.getSystemService(TetheringManager.class);
284         if (tetheringManager == null) {
285             Log.e(TAG, "TetheringManager is null");
286             return false;
287         }
288         String[] wifiRegexs = tetheringManager.getTetherableWifiRegexs();
289         if (wifiRegexs == null || wifiRegexs.length == 0) {
290             Log.w(TAG, "TetherableWifiRegexs is empty");
291             return false;
292         }
293         return true;
294     }
295 
296     /**
297      * Return the cached result to see if Wi-Fi hotspot settings can be displayed.
298      *
299      * @param context Context of caller
300      * @return true if Wi-Fi hotspot settings can be displayed
301      */
canShowWifiHotspot(Context context)302     public static boolean canShowWifiHotspot(Context context) {
303         if (sCanShowWifiHotspotCached == null) {
304             sCanShowWifiHotspotCached = checkShowWifiHotspot(context);
305         }
306         return sCanShowWifiHotspotCached;
307     }
308 
309     /**
310      * Sets the sCanShowWifiHotspotCached for testing purposes.
311      *
312      * @param cached Cached value for #canShowWifiHotspot()
313      */
314     @VisibleForTesting
setCanShowWifiHotspotCached(Boolean cached)315     public static void setCanShowWifiHotspotCached(Boolean cached) {
316         sCanShowWifiHotspotCached = cached;
317     }
318 
319     /**
320      * Enable new edge to edge feature.
321      *
322      * @param activity the Activity need to setup the edge to edge feature.
323      */
setupEdgeToEdge(@onNull Activity activity)324     public static void setupEdgeToEdge(@NonNull Activity activity) {
325         final ActionBar actionBar = activity.getActionBar();
326         if (actionBar == null) {
327             return;
328         }
329 
330         final TypedValue typedValue = new TypedValue();
331         if (activity.getTheme().resolveAttribute(
332                 com.android.internal.R.attr.actionBarSize, typedValue, true)) {
333             ViewCompat.setOnApplyWindowInsetsListener(activity.findViewById(android.R.id.content),
334                     (v, windowInsets) -> {
335                         Insets insets = windowInsets.getInsets(
336                                 WindowInsetsCompat.Type.systemBars() |
337                                 WindowInsetsCompat.Type.ime());
338 
339                         // Apply the insets paddings to the view.
340                         v.setPadding(insets.left, insets.top, insets.right, insets.bottom);
341 
342                         // Return CONSUMED if you don't want the window insets to keep being
343                         // passed down to descendant views.
344                         return WindowInsetsCompat.CONSUMED;
345                     });
346         }
347     }
348 }
349