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