1 /*
2  * Copyright (C) 2018 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.dpp;
18 
19 import android.app.KeyguardManager;
20 import android.content.Context;
21 import android.content.Intent;
22 import android.hardware.biometrics.BiometricPrompt;
23 import android.net.wifi.SoftApConfiguration;
24 import android.net.wifi.WifiConfiguration;
25 import android.net.wifi.WifiConfiguration.KeyMgmt;
26 import android.net.wifi.WifiManager;
27 import android.os.CancellationSignal;
28 import android.os.Handler;
29 import android.os.Looper;
30 import android.os.UserHandle;
31 import android.os.VibrationEffect;
32 import android.os.Vibrator;
33 import android.security.keystore.KeyGenParameterSpec;
34 import android.security.keystore.KeyProperties;
35 import android.text.TextUtils;
36 
37 import com.android.settings.R;
38 import com.android.settings.Utils;
39 import com.android.settingslib.wifi.AccessPoint;
40 import com.android.wifitrackerlib.WifiEntry;
41 
42 import java.security.InvalidAlgorithmParameterException;
43 import java.security.InvalidKeyException;
44 import java.security.NoSuchAlgorithmException;
45 import java.time.Duration;
46 import java.util.List;
47 
48 import javax.crypto.BadPaddingException;
49 import javax.crypto.Cipher;
50 import javax.crypto.IllegalBlockSizeException;
51 import javax.crypto.KeyGenerator;
52 import javax.crypto.NoSuchPaddingException;
53 import javax.crypto.SecretKey;
54 
55 /**
56  * Here are the items shared by both WifiDppConfiguratorActivity & WifiDppEnrolleeActivity
57  *
58  * @see WifiQrCode
59  */
60 public class WifiDppUtils {
61     /**
62      * The fragment tag specified to FragmentManager for container activities to manage fragments.
63      */
64     static final String TAG_FRAGMENT_QR_CODE_SCANNER = "qr_code_scanner_fragment";
65 
66     /**
67      * @see #TAG_FRAGMENT_QR_CODE_SCANNER
68      */
69     static final String TAG_FRAGMENT_QR_CODE_GENERATOR = "qr_code_generator_fragment";
70 
71     /**
72      * @see #TAG_FRAGMENT_QR_CODE_SCANNER
73      */
74     static final String TAG_FRAGMENT_CHOOSE_SAVED_WIFI_NETWORK =
75             "choose_saved_wifi_network_fragment";
76 
77     /**
78      * @see #TAG_FRAGMENT_QR_CODE_SCANNER
79      */
80     static final String TAG_FRAGMENT_ADD_DEVICE = "add_device_fragment";
81 
82     /** The data is one of the static String SECURITY_* in {@link WifiQrCode} */
83     static final String EXTRA_WIFI_SECURITY = "security";
84 
85     /** The data corresponding to {@code WifiConfiguration} SSID */
86     static final String EXTRA_WIFI_SSID = "ssid";
87 
88     /** The data corresponding to {@code WifiConfiguration} preSharedKey */
89     static final String EXTRA_WIFI_PRE_SHARED_KEY = "preSharedKey";
90 
91     /** The data corresponding to {@code WifiConfiguration} hiddenSSID */
92     static final String EXTRA_WIFI_HIDDEN_SSID = "hiddenSsid";
93 
94     /** The data corresponding to {@code WifiConfiguration} networkId */
95     static final String EXTRA_WIFI_NETWORK_ID = "networkId";
96 
97     /** The data to recognize if it's a Wi-Fi hotspot for configuration */
98     static final String EXTRA_IS_HOTSPOT = "isHotspot";
99 
100     /**
101      * Default status code for Easy Connect
102      */
103     static final int EASY_CONNECT_EVENT_FAILURE_NONE = 0;
104 
105     /**
106      * Success status code for Easy Connect.
107      */
108     static final int EASY_CONNECT_EVENT_SUCCESS = 1;
109 
110     private static final Duration VIBRATE_DURATION_QR_CODE_RECOGNITION = Duration.ofMillis(3);
111 
112     private static final String AES_CBC_PKCS7_PADDING = "AES/CBC/PKCS7Padding";
113 
114     /**
115      * Returns whether the device support WiFi DPP.
116      */
isWifiDppEnabled(Context context)117     static boolean isWifiDppEnabled(Context context) {
118         final WifiManager manager = context.getSystemService(WifiManager.class);
119         return manager.isEasyConnectSupported();
120     }
121 
122     /**
123      * Returns an intent to launch QR code scanner for Wi-Fi DPP enrollee.
124      *
125      * After enrollee success, the callee activity will return connecting WifiConfiguration by
126      * putExtra {@code WifiDialogActivity.KEY_WIFI_CONFIGURATION} for
127      * {@code Activity#setResult(int resultCode, Intent data)}. The calling object should check
128      * if it's available before using it.
129      *
130      * @param ssid The data corresponding to {@code WifiConfiguration} SSID
131      * @return Intent for launching QR code scanner
132      */
getEnrolleeQrCodeScannerIntent(Context context, String ssid)133     public static Intent getEnrolleeQrCodeScannerIntent(Context context, String ssid) {
134         final Intent intent = new Intent(context, WifiDppEnrolleeActivity.class);
135         intent.setAction(WifiDppEnrolleeActivity.ACTION_ENROLLEE_QR_CODE_SCANNER);
136         if (!TextUtils.isEmpty(ssid)) {
137             intent.putExtra(EXTRA_WIFI_SSID, ssid);
138         }
139         return intent;
140     }
141 
getPresharedKey(WifiManager wifiManager, WifiConfiguration wifiConfiguration)142     private static String getPresharedKey(WifiManager wifiManager,
143             WifiConfiguration wifiConfiguration) {
144         final List<WifiConfiguration> privilegedWifiConfigurations =
145                 wifiManager.getPrivilegedConfiguredNetworks();
146 
147         for (WifiConfiguration privilegedWifiConfiguration : privilegedWifiConfigurations) {
148             if (privilegedWifiConfiguration.networkId == wifiConfiguration.networkId) {
149                 // WEP uses a shared key hence the AuthAlgorithm.SHARED is used
150                 // to identify it.
151                 if (wifiConfiguration.allowedKeyManagement.get(KeyMgmt.NONE)
152                         && wifiConfiguration.allowedAuthAlgorithms.get(
153                         WifiConfiguration.AuthAlgorithm.SHARED)) {
154                     return privilegedWifiConfiguration
155                             .wepKeys[privilegedWifiConfiguration.wepTxKeyIndex];
156                 } else {
157                     return privilegedWifiConfiguration.preSharedKey;
158                 }
159             }
160         }
161         return wifiConfiguration.preSharedKey;
162     }
163 
removeFirstAndLastDoubleQuotes(String str)164     static String removeFirstAndLastDoubleQuotes(String str) {
165         if (TextUtils.isEmpty(str)) {
166             return str;
167         }
168 
169         int begin = 0;
170         int end = str.length() - 1;
171         if (str.charAt(begin) == '\"') {
172             begin++;
173         }
174         if (str.charAt(end) == '\"') {
175             end--;
176         }
177         return str.substring(begin, end+1);
178     }
179 
getSecurityString(WifiConfiguration config)180     static String getSecurityString(WifiConfiguration config) {
181         if (config.allowedKeyManagement.get(KeyMgmt.SAE)) {
182             return WifiQrCode.SECURITY_SAE;
183         }
184         if (config.allowedKeyManagement.get(KeyMgmt.OWE)) {
185             return WifiQrCode.SECURITY_NO_PASSWORD;
186         }
187         if (config.allowedKeyManagement.get(KeyMgmt.WPA_PSK) ||
188                 config.allowedKeyManagement.get(KeyMgmt.WPA2_PSK)) {
189             return WifiQrCode.SECURITY_WPA_PSK;
190         }
191         return (config.wepKeys[0] == null) ?
192                 WifiQrCode.SECURITY_NO_PASSWORD : WifiQrCode.SECURITY_WEP;
193     }
194 
getSecurityString(WifiEntry wifiEntry)195     static String getSecurityString(WifiEntry wifiEntry) {
196         final int security = wifiEntry.getSecurity();
197         switch (security) {
198             case WifiEntry.SECURITY_SAE:
199                 return WifiQrCode.SECURITY_SAE;
200             case WifiEntry.SECURITY_PSK:
201                 return WifiQrCode.SECURITY_WPA_PSK;
202             case WifiEntry.SECURITY_WEP:
203                 return WifiQrCode.SECURITY_WEP;
204             case WifiEntry.SECURITY_OWE:
205             case WifiEntry.SECURITY_NONE:
206             default:
207                 return WifiQrCode.SECURITY_NO_PASSWORD;
208         }
209     }
210 
211     /**
212      * Returns an intent to launch QR code generator. It may return null if the security is not
213      * supported by QR code generator.
214      *
215      * Do not use this method for Wi-Fi hotspot network, use
216      * {@code getHotspotConfiguratorIntentOrNull} instead.
217      *
218      * @param context     The context to use for the content resolver
219      * @param wifiManager An instance of {@link WifiManager}
220      * @param accessPoint An instance of {@link AccessPoint}
221      * @return Intent for launching QR code generator
222      */
getConfiguratorQrCodeGeneratorIntentOrNull(Context context, WifiManager wifiManager, AccessPoint accessPoint)223     public static Intent getConfiguratorQrCodeGeneratorIntentOrNull(Context context,
224             WifiManager wifiManager, AccessPoint accessPoint) {
225         final Intent intent = new Intent(context, WifiDppConfiguratorActivity.class);
226         if (isSupportConfiguratorQrCodeGenerator(context, accessPoint)) {
227             intent.setAction(WifiDppConfiguratorActivity.ACTION_CONFIGURATOR_QR_CODE_GENERATOR);
228         } else {
229             return null;
230         }
231 
232         final WifiConfiguration wifiConfiguration = accessPoint.getConfig();
233         setConfiguratorIntentExtra(intent, wifiManager, wifiConfiguration);
234 
235         // For a transition mode Wi-Fi AP, creates a QR code that's compatible with more devices
236         if (accessPoint.isPskSaeTransitionMode()) {
237             intent.putExtra(EXTRA_WIFI_SECURITY, WifiQrCode.SECURITY_WPA_PSK);
238         }
239 
240         return intent;
241     }
242 
243     /**
244      * Returns an intent to launch QR code generator. It may return null if the security is not
245      * supported by QR code generator.
246      *
247      * Do not use this method for Wi-Fi hotspot network, use
248      * {@code getHotspotConfiguratorIntentOrNull} instead.
249      *
250      * @param context     The context to use for the content resolver
251      * @param wifiManager An instance of {@link WifiManager}
252      * @param wifiEntry An instance of {@link WifiEntry}
253      * @return Intent for launching QR code generator
254      */
getConfiguratorQrCodeGeneratorIntentOrNull(Context context, WifiManager wifiManager, WifiEntry wifiEntry)255     public static Intent getConfiguratorQrCodeGeneratorIntentOrNull(Context context,
256             WifiManager wifiManager, WifiEntry wifiEntry) {
257         final Intent intent = new Intent(context, WifiDppConfiguratorActivity.class);
258         if (wifiEntry.canShare()) {
259             intent.setAction(WifiDppConfiguratorActivity.ACTION_CONFIGURATOR_QR_CODE_GENERATOR);
260         } else {
261             return null;
262         }
263 
264         final WifiConfiguration wifiConfiguration = wifiEntry.getWifiConfiguration();
265         setConfiguratorIntentExtra(intent, wifiManager, wifiConfiguration);
266 
267         return intent;
268     }
269 
270     /**
271      * Returns an intent to launch QR code scanner. It may return null if the security is not
272      * supported by QR code scanner.
273      *
274      * @param context     The context to use for the content resolver
275      * @param wifiManager An instance of {@link WifiManager}
276      * @param wifiEntry An instance of {@link WifiEntry}
277      * @return Intent for launching QR code scanner
278      */
getConfiguratorQrCodeScannerIntentOrNull(Context context, WifiManager wifiManager, WifiEntry wifiEntry)279     public static Intent getConfiguratorQrCodeScannerIntentOrNull(Context context,
280             WifiManager wifiManager, WifiEntry wifiEntry) {
281         final Intent intent = new Intent(context, WifiDppConfiguratorActivity.class);
282         if (wifiEntry.canEasyConnect()) {
283             intent.setAction(WifiDppConfiguratorActivity.ACTION_CONFIGURATOR_QR_CODE_SCANNER);
284         } else {
285             return null;
286         }
287 
288         final WifiConfiguration wifiConfiguration = wifiEntry.getWifiConfiguration();
289         setConfiguratorIntentExtra(intent, wifiManager, wifiConfiguration);
290 
291         if (wifiConfiguration.networkId == WifiConfiguration.INVALID_NETWORK_ID) {
292             throw new IllegalArgumentException("Invalid network ID");
293         } else {
294             intent.putExtra(EXTRA_WIFI_NETWORK_ID, wifiConfiguration.networkId);
295         }
296 
297         return intent;
298     }
299 
300     /**
301      * Returns an intent to launch QR code generator for the Wi-Fi hotspot. It may return null if
302      * the security is not supported by QR code generator.
303      *
304      * @param context The context to use for the content resolver
305      * @param wifiManager An instance of {@link WifiManager}
306      * @param softApConfiguration {@link SoftApConfiguration} of the Wi-Fi hotspot
307      * @return Intent for launching QR code generator
308      */
getHotspotConfiguratorIntentOrNull(Context context, WifiManager wifiManager, SoftApConfiguration softApConfiguration)309     public static Intent getHotspotConfiguratorIntentOrNull(Context context,
310             WifiManager wifiManager, SoftApConfiguration softApConfiguration) {
311         final Intent intent = new Intent(context, WifiDppConfiguratorActivity.class);
312         if (isSupportHotspotConfiguratorQrCodeGenerator(softApConfiguration)) {
313             intent.setAction(WifiDppConfiguratorActivity.ACTION_CONFIGURATOR_QR_CODE_GENERATOR);
314         } else {
315             return null;
316         }
317 
318         final String ssid = removeFirstAndLastDoubleQuotes(softApConfiguration.getSsid());
319         String security;
320         final int securityType = softApConfiguration.getSecurityType();
321         if (securityType == SoftApConfiguration.SECURITY_TYPE_WPA3_SAE) {
322             security = WifiQrCode.SECURITY_SAE;
323         } else if (securityType == SoftApConfiguration.SECURITY_TYPE_WPA2_PSK
324                 || securityType == SoftApConfiguration.SECURITY_TYPE_WPA3_SAE_TRANSITION) {
325             security = WifiQrCode.SECURITY_WPA_PSK;
326         } else {
327             security = WifiQrCode.SECURITY_NO_PASSWORD;
328         }
329 
330         // When the value of this key is read, the actual key is not returned, just a "*".
331         // Call privileged system API to obtain actual key.
332         final String preSharedKey = removeFirstAndLastDoubleQuotes(
333                 softApConfiguration.getPassphrase());
334 
335         if (!TextUtils.isEmpty(ssid)) {
336             intent.putExtra(EXTRA_WIFI_SSID, ssid);
337         }
338         if (!TextUtils.isEmpty(security)) {
339             intent.putExtra(EXTRA_WIFI_SECURITY, security);
340         }
341         if (!TextUtils.isEmpty(preSharedKey)) {
342             intent.putExtra(EXTRA_WIFI_PRE_SHARED_KEY, preSharedKey);
343         }
344         intent.putExtra(EXTRA_WIFI_HIDDEN_SSID, softApConfiguration.isHiddenSsid());
345 
346 
347         intent.putExtra(EXTRA_WIFI_NETWORK_ID, WifiConfiguration.INVALID_NETWORK_ID);
348         intent.putExtra(EXTRA_IS_HOTSPOT, true);
349 
350         return intent;
351     }
352 
353     /**
354      * Set all extra except {@code EXTRA_WIFI_NETWORK_ID} for the intent to
355      * launch configurator activity later.
356      *
357      * @param intent the target to set extra
358      * @param wifiManager an instance of {@code WifiManager}
359      * @param wifiConfiguration the Wi-Fi network for launching configurator activity
360      */
setConfiguratorIntentExtra(Intent intent, WifiManager wifiManager, WifiConfiguration wifiConfiguration)361     private static void setConfiguratorIntentExtra(Intent intent, WifiManager wifiManager,
362             WifiConfiguration wifiConfiguration) {
363         final String ssid = removeFirstAndLastDoubleQuotes(wifiConfiguration.SSID);
364         final String security = getSecurityString(wifiConfiguration);
365 
366         // When the value of this key is read, the actual key is not returned, just a "*".
367         // Call privileged system API to obtain actual key.
368         final String preSharedKey = removeFirstAndLastDoubleQuotes(getPresharedKey(wifiManager,
369                 wifiConfiguration));
370 
371         if (!TextUtils.isEmpty(ssid)) {
372             intent.putExtra(EXTRA_WIFI_SSID, ssid);
373         }
374         if (!TextUtils.isEmpty(security)) {
375             intent.putExtra(EXTRA_WIFI_SECURITY, security);
376         }
377         if (!TextUtils.isEmpty(preSharedKey)) {
378             intent.putExtra(EXTRA_WIFI_PRE_SHARED_KEY, preSharedKey);
379         }
380         intent.putExtra(EXTRA_WIFI_HIDDEN_SSID, wifiConfiguration.hiddenSSID);
381     }
382 
383     /**
384      * Checks whether the device is unlocked recently.
385      *
386      * @param keyStoreAlias key
387      * @param seconds how many seconds since the device is unlocked
388      * @return whether the device is unlocked within the time
389      */
isUnlockedWithinSeconds(String keyStoreAlias, int seconds)390     public static boolean isUnlockedWithinSeconds(String keyStoreAlias, int seconds) {
391         try {
392             Cipher cipher = Cipher.getInstance(AES_CBC_PKCS7_PADDING);
393             cipher.init(Cipher.ENCRYPT_MODE, generateSecretKey(keyStoreAlias, seconds));
394             cipher.doFinal();
395             return true;
396         } catch (NoSuchPaddingException
397                  | IllegalBlockSizeException
398                  | NoSuchAlgorithmException
399                  | BadPaddingException
400                  | InvalidKeyException e) {
401             return false;
402         }
403     }
404 
generateSecretKey(String keyStoreAlias, int seconds)405     private static SecretKey generateSecretKey(String keyStoreAlias, int seconds) {
406         KeyGenParameterSpec spec = new KeyGenParameterSpec
407                 .Builder(keyStoreAlias, KeyProperties.PURPOSE_ENCRYPT)
408                 .setBlockModes(KeyProperties.BLOCK_MODE_CBC)
409                 .setEncryptionPaddings(KeyProperties.ENCRYPTION_PADDING_PKCS7)
410                 .setUserAuthenticationRequired(true)
411                 .setUserAuthenticationParameters(
412                         seconds,
413                         KeyProperties.AUTH_DEVICE_CREDENTIAL | KeyProperties.AUTH_BIOMETRIC_STRONG)
414                 .build();
415         try {
416             KeyGenerator keyGenerator = KeyGenerator.getInstance(KeyProperties.KEY_ALGORITHM_AES);
417             keyGenerator.init(spec);
418             return keyGenerator.generateKey();
419         } catch (NoSuchAlgorithmException
420                  | InvalidAlgorithmParameterException e) {
421             return null;
422         }
423     }
424 
425     /**
426      * Shows authentication screen to confirm credentials (pin, pattern or password) for the current
427      * user of the device.
428      *
429      * @param context The {@code Context} used to get {@code KeyguardManager} service
430      * @param successRunnable The {@code Runnable} which will be executed if the user does not setup
431      *                        device security or if lock screen is unlocked
432      */
showLockScreen(Context context, Runnable successRunnable)433     public static void showLockScreen(Context context, Runnable successRunnable) {
434         final KeyguardManager keyguardManager = (KeyguardManager) context.getSystemService(
435                 Context.KEYGUARD_SERVICE);
436 
437         if (keyguardManager.isKeyguardSecure()) {
438             final BiometricPrompt.AuthenticationCallback authenticationCallback =
439                     new BiometricPrompt.AuthenticationCallback() {
440                         @Override
441                         public void onAuthenticationSucceeded(
442                                     BiometricPrompt.AuthenticationResult result) {
443                             successRunnable.run();
444                         }
445 
446                         @Override
447                         public void onAuthenticationError(int errorCode, CharSequence errString) {
448                             //Do nothing
449                         }
450             };
451 
452             final int userId = UserHandle.myUserId();
453 
454             final BiometricPrompt.Builder builder = new BiometricPrompt.Builder(context)
455                     .setTitle(context.getText(R.string.wifi_dpp_lockscreen_title));
456 
457             if (keyguardManager.isDeviceSecure()) {
458                 builder.setDeviceCredentialAllowed(true);
459                 builder.setTextForDeviceCredential(
460                         null /* title */,
461                         Utils.getConfirmCredentialStringForUser(
462                                 context, userId, Utils.getCredentialType(context, userId)),
463                         null /* description */);
464             }
465 
466             final BiometricPrompt bp = builder.build();
467             final Handler handler = new Handler(Looper.getMainLooper());
468             bp.authenticate(new CancellationSignal(),
469                     runnable -> handler.post(runnable),
470                     authenticationCallback);
471         } else {
472             successRunnable.run();
473         }
474     }
475 
476     /**
477      * Checks if QR code generator supports to config other devices with the Wi-Fi network
478      *
479      * @param context The context to use for {@code WifiManager}
480      * @param accessPoint The {@link AccessPoint} of the Wi-Fi network
481      */
isSupportConfiguratorQrCodeGenerator(Context context, AccessPoint accessPoint)482     public static boolean isSupportConfiguratorQrCodeGenerator(Context context,
483             AccessPoint accessPoint) {
484         if (accessPoint.isPasspoint()) {
485             return false;
486         }
487         return isSupportZxing(context, accessPoint.getSecurity());
488     }
489 
490     /**
491      * Checks if this device supports to be configured by the Wi-Fi network of the security
492      *
493      * @param context The context to use for {@code WifiManager}
494      * @param wifiEntrySecurity The security constants defined in {@link WifiEntry}
495      */
isSupportEnrolleeQrCodeScanner(Context context, int wifiEntrySecurity)496     public static boolean isSupportEnrolleeQrCodeScanner(Context context, int wifiEntrySecurity) {
497         return isSupportWifiDpp(context, wifiEntrySecurity)
498                 || isSupportZxing(context, wifiEntrySecurity);
499     }
500 
isSupportHotspotConfiguratorQrCodeGenerator( SoftApConfiguration softApConfiguration)501     private static boolean isSupportHotspotConfiguratorQrCodeGenerator(
502             SoftApConfiguration softApConfiguration) {
503         final int securityType = softApConfiguration.getSecurityType();
504         return securityType == SoftApConfiguration.SECURITY_TYPE_WPA3_SAE
505                 || securityType == SoftApConfiguration.SECURITY_TYPE_WPA3_SAE_TRANSITION
506                 || securityType == SoftApConfiguration.SECURITY_TYPE_WPA2_PSK
507                 || securityType == SoftApConfiguration.SECURITY_TYPE_OPEN;
508     }
509 
isSupportWifiDpp(Context context, int wifiEntrySecurity)510     private static boolean isSupportWifiDpp(Context context, int wifiEntrySecurity) {
511         if (!isWifiDppEnabled(context)) {
512             return false;
513         }
514 
515         // DPP 1.0 only supports SAE and PSK.
516         final WifiManager wifiManager = context.getSystemService(WifiManager.class);
517         switch (wifiEntrySecurity) {
518             case WifiEntry.SECURITY_SAE:
519                 if (wifiManager.isWpa3SaeSupported()) {
520                     return true;
521                 }
522                 break;
523             case WifiEntry.SECURITY_PSK:
524                 return true;
525             default:
526         }
527         return false;
528     }
529 
isSupportZxing(Context context, int wifiEntrySecurity)530     private static boolean isSupportZxing(Context context, int wifiEntrySecurity) {
531         final WifiManager wifiManager = context.getSystemService(WifiManager.class);
532         switch (wifiEntrySecurity) {
533             case WifiEntry.SECURITY_PSK:
534             case WifiEntry.SECURITY_WEP:
535             case WifiEntry.SECURITY_NONE:
536                 return true;
537             case WifiEntry.SECURITY_SAE:
538                 if (wifiManager.isWpa3SaeSupported()) {
539                     return true;
540                 }
541                 break;
542             case WifiEntry.SECURITY_OWE:
543                 if (wifiManager.isEnhancedOpenSupported()) {
544                     return true;
545                 }
546                 break;
547             default:
548         }
549         return false;
550     }
551 
triggerVibrationForQrCodeRecognition(Context context)552     static void triggerVibrationForQrCodeRecognition(Context context) {
553         Vibrator vibrator = (Vibrator) context.getSystemService(Context.VIBRATOR_SERVICE);
554         if (vibrator == null) {
555           return;
556         }
557         vibrator.vibrate(VibrationEffect.createOneShot(
558                 VIBRATE_DURATION_QR_CODE_RECOGNITION.toMillis(),
559                 VibrationEffect.DEFAULT_AMPLITUDE));
560     }
561 
562     @WifiEntry.Security
getSecurityTypeFromWifiConfiguration(WifiConfiguration config)563     static int getSecurityTypeFromWifiConfiguration(WifiConfiguration config) {
564         if (config.allowedKeyManagement.get(WifiConfiguration.KeyMgmt.SAE)) {
565             return WifiEntry.SECURITY_SAE;
566         }
567         if (config.allowedKeyManagement.get(WifiConfiguration.KeyMgmt.WPA_PSK)) {
568             return WifiEntry.SECURITY_PSK;
569         }
570         if (config.allowedKeyManagement.get(WifiConfiguration.KeyMgmt.SUITE_B_192)) {
571             return WifiEntry.SECURITY_EAP_SUITE_B;
572         }
573         if (config.allowedKeyManagement.get(WifiConfiguration.KeyMgmt.WPA_EAP)
574                 || config.allowedKeyManagement.get(WifiConfiguration.KeyMgmt.IEEE8021X)) {
575             return WifiEntry.SECURITY_EAP;
576         }
577         if (config.allowedKeyManagement.get(WifiConfiguration.KeyMgmt.OWE)) {
578             return WifiEntry.SECURITY_OWE;
579         }
580         return (config.wepKeys[0] != null) ? WifiEntry.SECURITY_WEP : WifiEntry.SECURITY_NONE;
581     }
582 }
583