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.server.wifi; 18 19 import static android.net.wifi.WifiManager.EASY_CONNECT_NETWORK_ROLE_AP; 20 21 import android.annotation.Nullable; 22 import android.content.Context; 23 import android.hardware.wifi.supplicant.V1_2.DppAkm; 24 import android.hardware.wifi.supplicant.V1_2.DppNetRole; 25 import android.hardware.wifi.supplicant.V1_3.DppProgressCode; 26 import android.hardware.wifi.supplicant.V1_3.DppSuccessCode; 27 import android.hardware.wifi.supplicant.V1_4.DppCurve; 28 import android.hardware.wifi.supplicant.V1_4.DppFailureCode; 29 import android.net.wifi.EasyConnectStatusCallback; 30 import android.net.wifi.IDppCallback; 31 import android.net.wifi.ScanResult; 32 import android.net.wifi.WifiConfiguration; 33 import android.net.wifi.WifiManager; 34 import android.os.Build; 35 import android.os.Handler; 36 import android.os.IBinder; 37 import android.os.RemoteException; 38 import android.text.TextUtils; 39 import android.util.Log; 40 import android.util.SparseArray; 41 42 import androidx.annotation.RequiresApi; 43 44 import com.android.internal.annotations.VisibleForTesting; 45 import com.android.internal.util.WakeupMessage; 46 import com.android.modules.utils.build.SdkLevel; 47 import com.android.server.wifi.WifiNative.DppEventCallback; 48 import com.android.server.wifi.util.ApConfigUtil; 49 import com.android.server.wifi.util.WifiPermissionsUtil; 50 51 import java.util.ArrayList; 52 import java.util.List; 53 54 /** 55 * DPP Manager class 56 * Implements the DPP Initiator APIs and callbacks 57 */ 58 public class DppManager { 59 private static final String TAG = "DppManager"; 60 private final Handler mHandler; 61 62 private DppRequestInfo mDppRequestInfo = null; 63 private final WifiNative mWifiNative; 64 private String mClientIfaceName; 65 private boolean mVerboseLoggingEnabled; 66 WifiConfigManager mWifiConfigManager; 67 private final Context mContext; 68 @VisibleForTesting 69 public WakeupMessage mDppTimeoutMessage = null; 70 private final Clock mClock; 71 private static final String DPP_TIMEOUT_TAG = TAG + " Request Timeout"; 72 private static final int DPP_TIMEOUT_MS = 40_000; // 40 seconds 73 private static final int DPP_RESPONDER_TIMEOUT_MS = 300_000; // 5 minutes 74 public static final int DPP_AUTH_ROLE_INACTIVE = -1; 75 public static final int DPP_AUTH_ROLE_INITIATOR = 0; 76 public static final int DPP_AUTH_ROLE_RESPONDER = 1; 77 private final DppMetrics mDppMetrics; 78 private final ScanRequestProxy mScanRequestProxy; 79 private final WifiPermissionsUtil mWifiPermissionsUtil; 80 81 private final DppEventCallback mDppEventCallback = new DppEventCallback() { 82 @Override 83 public void onSuccessConfigReceived(WifiConfiguration newWifiConfiguration) { 84 mHandler.post(() -> { 85 DppManager.this.onSuccessConfigReceived(newWifiConfiguration); 86 }); 87 } 88 89 @Override 90 public void onSuccess(int dppStatusCode) { 91 mHandler.post(() -> { 92 DppManager.this.onSuccess(dppStatusCode); 93 }); 94 } 95 96 @Override 97 public void onProgress(int dppStatusCode) { 98 mHandler.post(() -> { 99 DppManager.this.onProgress(dppStatusCode); 100 }); 101 } 102 103 @Override 104 public void onFailure(int dppStatusCode, String ssid, String channelList, int[] bandList) { 105 mHandler.post(() -> { 106 DppManager.this.onFailure(dppStatusCode, ssid, channelList, bandList); 107 }); 108 } 109 }; 110 DppManager(Handler handler, WifiNative wifiNative, WifiConfigManager wifiConfigManager, Context context, DppMetrics dppMetrics, ScanRequestProxy scanRequestProxy, WifiPermissionsUtil wifiPermissionsUtil)111 DppManager(Handler handler, WifiNative wifiNative, WifiConfigManager wifiConfigManager, 112 Context context, DppMetrics dppMetrics, ScanRequestProxy scanRequestProxy, 113 WifiPermissionsUtil wifiPermissionsUtil) { 114 mHandler = handler; 115 mWifiNative = wifiNative; 116 mWifiConfigManager = wifiConfigManager; 117 mWifiNative.registerDppEventCallback(mDppEventCallback); 118 mContext = context; 119 mClock = new Clock(); 120 mDppMetrics = dppMetrics; 121 mScanRequestProxy = scanRequestProxy; 122 mWifiPermissionsUtil = wifiPermissionsUtil; 123 124 // Setup timer 125 mDppTimeoutMessage = new WakeupMessage(mContext, mHandler, 126 DPP_TIMEOUT_TAG, () -> { 127 timeoutDppRequest(); 128 }); 129 } 130 encodeStringToHex(String str)131 private static String encodeStringToHex(String str) { 132 if ((str.length() > 1) && (str.charAt(0) == '"') && (str.charAt(str.length() - 1) == '"')) { 133 // Remove the surrounding quotes 134 str = str.substring(1, str.length() - 1); 135 136 // Convert to Hex 137 char[] charsArray = str.toCharArray(); 138 StringBuffer hexBuffer = new StringBuffer(); 139 for (int i = 0; i < charsArray.length; i++) { 140 hexBuffer.append(Integer.toHexString((int) charsArray[i])); 141 } 142 return hexBuffer.toString(); 143 } 144 return str; 145 } 146 timeoutDppRequest()147 private void timeoutDppRequest() { 148 logd("DPP timeout"); 149 150 if (mDppRequestInfo == null) { 151 Log.e(TAG, "DPP timeout with no request info"); 152 return; 153 } 154 155 // Clean up supplicant resources 156 if (!mWifiNative.stopDppInitiator(mClientIfaceName)) { 157 Log.e(TAG, "Failed to stop DPP Initiator"); 158 } 159 160 // Clean up resources and let the caller know about the timeout 161 onFailure(DppFailureCode.TIMEOUT); 162 } 163 164 /** 165 * Start DPP request in Configurator-Initiator mode. The goal of this call is to send the 166 * selected Wi-Fi configuration to a remote peer so it could join that network. 167 * 168 * @param uid UID 169 * @param packageName Package name of the calling app 170 * @param clientIfaceName Client interface to use for this operation. 171 * @param binder Binder object 172 * @param enrolleeUri The Enrollee URI, scanned externally (e.g. via QR code) 173 * @param selectedNetworkId The selected Wi-Fi network ID to be sent 174 * @param enrolleeNetworkRole Network role of remote enrollee: STA or AP 175 * @param callback DPP Callback object 176 */ startDppAsConfiguratorInitiator(int uid, @Nullable String packageName, @Nullable String clientIfaceName, IBinder binder, String enrolleeUri, int selectedNetworkId, @WifiManager.EasyConnectNetworkRole int enrolleeNetworkRole, IDppCallback callback)177 public void startDppAsConfiguratorInitiator(int uid, @Nullable String packageName, 178 @Nullable String clientIfaceName, IBinder binder, String enrolleeUri, 179 int selectedNetworkId, @WifiManager.EasyConnectNetworkRole int enrolleeNetworkRole, 180 IDppCallback callback) { 181 mDppMetrics.updateDppConfiguratorInitiatorRequests(); 182 if (isSessionInProgress()) { 183 try { 184 Log.e(TAG, "DPP request already in progress"); 185 Log.e(TAG, "Ongoing request - UID: " + mDppRequestInfo.uid 186 + " Package: " + mDppRequestInfo.packageName 187 + ", New request - UID: " + uid + " Package: " + packageName); 188 189 mDppMetrics.updateDppFailure(EasyConnectStatusCallback 190 .EASY_CONNECT_EVENT_FAILURE_BUSY); 191 // On going DPP. Call the failure callback directly 192 callback.onFailure(EasyConnectStatusCallback.EASY_CONNECT_EVENT_FAILURE_BUSY, null, 193 null, new int[0]); 194 } catch (RemoteException e) { 195 // Empty 196 } 197 return; 198 } 199 200 mClientIfaceName = clientIfaceName; 201 if (mClientIfaceName == null) { 202 try { 203 Log.e(TAG, "Wi-Fi client interface does not exist"); 204 // On going DPP. Call the failure callback directly 205 mDppMetrics.updateDppFailure(EasyConnectStatusCallback 206 .EASY_CONNECT_EVENT_FAILURE_GENERIC); 207 callback.onFailure(EasyConnectStatusCallback.EASY_CONNECT_EVENT_FAILURE_GENERIC, 208 null, null, new int[0]); 209 } catch (RemoteException e) { 210 // Empty 211 } 212 return; 213 } 214 215 WifiConfiguration selectedNetwork = mWifiConfigManager 216 .getConfiguredNetworkWithoutMasking(selectedNetworkId); 217 218 if (selectedNetwork == null) { 219 try { 220 Log.e(TAG, "Selected network is null"); 221 // On going DPP. Call the failure callback directly 222 mDppMetrics.updateDppFailure(EasyConnectStatusCallback 223 .EASY_CONNECT_EVENT_FAILURE_INVALID_NETWORK); 224 callback.onFailure(EasyConnectStatusCallback 225 .EASY_CONNECT_EVENT_FAILURE_INVALID_NETWORK, null, null, new int[0]); 226 } catch (RemoteException e) { 227 // Empty 228 } 229 return; 230 } 231 232 String password = null; 233 String psk = null; 234 int securityAkm; 235 236 // Currently support either SAE mode or PSK mode 237 // Check PSK first because PSK config always has a SAE type as a upgrading type. 238 if (selectedNetwork.isSecurityType(WifiConfiguration.SECURITY_TYPE_PSK)) { 239 if (selectedNetwork.preSharedKey.matches(String.format("[0-9A-Fa-f]{%d}", 64))) { 240 // PSK 241 psk = selectedNetwork.preSharedKey; 242 } else { 243 // Passphrase 244 password = selectedNetwork.preSharedKey; 245 } 246 securityAkm = DppAkm.PSK; 247 } else if (selectedNetwork.isSecurityType(WifiConfiguration.SECURITY_TYPE_SAE)) { 248 // SAE 249 password = selectedNetwork.preSharedKey; 250 securityAkm = DppAkm.SAE; 251 } else { 252 try { 253 // Key management must be either PSK or SAE 254 Log.e(TAG, "Key management must be either PSK or SAE"); 255 mDppMetrics.updateDppFailure(EasyConnectStatusCallback 256 .EASY_CONNECT_EVENT_FAILURE_INVALID_NETWORK); 257 callback.onFailure( 258 EasyConnectStatusCallback.EASY_CONNECT_EVENT_FAILURE_INVALID_NETWORK, null, 259 null, new int[0]); 260 } catch (RemoteException e) { 261 // Empty 262 } 263 return; 264 } 265 266 mDppRequestInfo = new DppRequestInfo(); 267 mDppRequestInfo.uid = uid; 268 mDppRequestInfo.packageName = packageName; 269 mDppRequestInfo.binder = binder; 270 mDppRequestInfo.callback = callback; 271 mDppRequestInfo.authRole = DPP_AUTH_ROLE_INITIATOR; 272 273 if (!linkToDeath(mDppRequestInfo)) { 274 // Notify failure and clean up 275 onFailure(EasyConnectStatusCallback.EASY_CONNECT_EVENT_FAILURE_GENERIC); 276 return; 277 } 278 279 logd("Interface " + mClientIfaceName + ": Initializing URI: " + enrolleeUri); 280 281 mDppRequestInfo.startTime = mClock.getElapsedSinceBootMillis(); 282 mDppTimeoutMessage.schedule(mDppRequestInfo.startTime + DPP_TIMEOUT_MS); 283 284 // Send Enrollee URI and get a peer ID 285 int peerId = mWifiNative.addDppPeerUri(mClientIfaceName, enrolleeUri); 286 287 if (peerId < 0) { 288 Log.e(TAG, "DPP add URI failure"); 289 290 // Notify failure and clean up 291 onFailure(DppFailureCode.INVALID_URI); 292 return; 293 } 294 mDppRequestInfo.peerId = peerId; 295 296 // Auth init 297 logd("Authenticating"); 298 299 String ssidEncoded = encodeStringToHex(selectedNetwork.SSID); 300 String passwordEncoded = null; 301 302 if (password != null) { 303 passwordEncoded = encodeStringToHex(selectedNetwork.preSharedKey); 304 } 305 306 if (!mWifiNative.startDppConfiguratorInitiator(mClientIfaceName, 307 mDppRequestInfo.peerId, 0, ssidEncoded, passwordEncoded, psk, 308 enrolleeNetworkRole == EASY_CONNECT_NETWORK_ROLE_AP ? DppNetRole.AP 309 : DppNetRole.STA, 310 securityAkm)) { 311 Log.e(TAG, "DPP Start Configurator Initiator failure"); 312 313 // Notify failure and clean up 314 onFailure(DppFailureCode.FAILURE); 315 return; 316 } 317 318 logd("Success: Started DPP Initiator with peer ID " 319 + mDppRequestInfo.peerId); 320 } 321 322 /** 323 * Start DPP request in Enrollee-Initiator mode. The goal of this call is to receive a 324 * Wi-Fi configuration object from the peer configurator in order to join a network. 325 * 326 * @param uid UID 327 * @param clientIfaceName Client interface to use for this operation. 328 * @param binder Binder object 329 * @param configuratorUri The Configurator URI, scanned externally (e.g. via QR code) 330 * @param callback DPP Callback object 331 */ startDppAsEnrolleeInitiator(int uid, @Nullable String clientIfaceName, IBinder binder, String configuratorUri, IDppCallback callback)332 public void startDppAsEnrolleeInitiator(int uid, @Nullable String clientIfaceName, 333 IBinder binder, String configuratorUri, IDppCallback callback) { 334 mDppMetrics.updateDppEnrolleeInitiatorRequests(); 335 if (isSessionInProgress()) { 336 try { 337 Log.e(TAG, "DPP request already in progress"); 338 Log.e(TAG, "Ongoing request UID: " + mDppRequestInfo.uid + ", new UID: " 339 + uid); 340 341 mDppMetrics.updateDppFailure(EasyConnectStatusCallback 342 .EASY_CONNECT_EVENT_FAILURE_BUSY); 343 // On going DPP. Call the failure callback directly 344 callback.onFailure(EasyConnectStatusCallback.EASY_CONNECT_EVENT_FAILURE_BUSY, null, 345 null, new int[0]); 346 } catch (RemoteException e) { 347 // Empty 348 } 349 return; 350 } 351 352 mDppRequestInfo = new DppRequestInfo(); 353 mDppRequestInfo.uid = uid; 354 mDppRequestInfo.binder = binder; 355 mDppRequestInfo.callback = callback; 356 mDppRequestInfo.authRole = DPP_AUTH_ROLE_INITIATOR; 357 358 if (!linkToDeath(mDppRequestInfo)) { 359 // Notify failure and clean up 360 onFailure(DppFailureCode.FAILURE); 361 return; 362 } 363 364 mDppRequestInfo.startTime = mClock.getElapsedSinceBootMillis(); 365 mDppTimeoutMessage.schedule(mDppRequestInfo.startTime + DPP_TIMEOUT_MS); 366 367 mClientIfaceName = clientIfaceName; 368 logd("Interface " + mClientIfaceName + ": Initializing URI: " + configuratorUri); 369 370 // Send Configurator URI and get a peer ID 371 int peerId = mWifiNative.addDppPeerUri(mClientIfaceName, configuratorUri); 372 373 if (peerId < 0) { 374 Log.e(TAG, "DPP add URI failure"); 375 onFailure(DppFailureCode.INVALID_URI); 376 return; 377 } 378 mDppRequestInfo.peerId = peerId; 379 380 // Auth init 381 logd("Authenticating"); 382 383 if (!mWifiNative.startDppEnrolleeInitiator(mClientIfaceName, mDppRequestInfo.peerId, 384 0)) { 385 Log.e(TAG, "DPP Start Enrollee Initiator failure"); 386 387 // Notify failure and clean up 388 onFailure(DppFailureCode.FAILURE); 389 return; 390 } 391 392 logd("Success: Started DPP Initiator with peer ID " 393 + mDppRequestInfo.peerId); 394 } 395 396 /** 397 * Start DPP request in Enrollee-Responder mode. The goal of this call is to receive a 398 * Wi-Fi configuration object from the peer configurator by showing a QR code and being scanned 399 * by the peer configurator. 400 * 401 * @param uid UID 402 * @param clientIfaceName Client interface to use for this operation. 403 * @param binder Binder object 404 * @param deviceInfo The Device specific info to attach to the generated URI 405 * @param curve Elliptic curve cryptography type used to generate DPP 406 * public/private key pair. 407 * @param callback DPP Callback object 408 */ startDppAsEnrolleeResponder(int uid, @Nullable String clientIfaceName, IBinder binder, @Nullable String deviceInfo, @WifiManager.EasyConnectCryptographyCurve int curve, IDppCallback callback)409 public void startDppAsEnrolleeResponder(int uid, @Nullable String clientIfaceName, 410 IBinder binder, @Nullable String deviceInfo, 411 @WifiManager.EasyConnectCryptographyCurve int curve, IDppCallback callback) { 412 mDppMetrics.updateDppEnrolleeResponderRequests(); 413 if (isSessionInProgress()) { 414 try { 415 Log.e(TAG, "DPP request already in progress"); 416 Log.e(TAG, "Ongoing request UID: " + mDppRequestInfo.uid + ", new UID: " 417 + uid); 418 419 mDppMetrics.updateDppFailure(EasyConnectStatusCallback 420 .EASY_CONNECT_EVENT_FAILURE_BUSY); 421 // On going DPP. Call the failure callback directly 422 callback.onFailure(EasyConnectStatusCallback.EASY_CONNECT_EVENT_FAILURE_BUSY, 423 null, null, new int[0]); 424 } catch (RemoteException e) { 425 // Empty 426 } 427 return; 428 } 429 430 mDppRequestInfo = new DppRequestInfo(); 431 mDppRequestInfo.uid = uid; 432 mDppRequestInfo.binder = binder; 433 mDppRequestInfo.callback = callback; 434 mDppRequestInfo.authRole = DPP_AUTH_ROLE_RESPONDER; 435 436 if (!linkToDeath(mDppRequestInfo)) { 437 // Notify failure and clean up 438 onFailure(DppFailureCode.FAILURE); 439 return; 440 } 441 442 mDppRequestInfo.startTime = mClock.getElapsedSinceBootMillis(); 443 mDppTimeoutMessage.schedule(mDppRequestInfo.startTime + DPP_RESPONDER_TIMEOUT_MS); 444 445 mClientIfaceName = clientIfaceName; 446 logd("Interface " + mClientIfaceName + " Product Info: " + deviceInfo 447 + " Curve: " + curve); 448 449 String info = deviceInfo == null ? "" : deviceInfo; 450 // Generate a QR code based bootstrap info 451 WifiNative.DppBootstrapQrCodeInfo bootstrapInfo = null; 452 if (SdkLevel.isAtLeastS()) { 453 bootstrapInfo = 454 mWifiNative.generateDppBootstrapInfoForResponder(mClientIfaceName, deviceInfo, 455 convertEasyConnectCryptographyCurveToHidlDppCurve(curve)); 456 } 457 458 if (bootstrapInfo == null || bootstrapInfo.bootstrapId < 0 459 || TextUtils.isEmpty(bootstrapInfo.uri)) { 460 Log.e(TAG, "DPP request to generate URI failed"); 461 onFailure(DppFailureCode.URI_GENERATION); 462 return; 463 } 464 465 mDppRequestInfo.bootstrapId = bootstrapInfo.bootstrapId; 466 logd("BootstrapId:" + mDppRequestInfo.bootstrapId + " URI: " + bootstrapInfo.uri); 467 468 if (!mWifiNative.startDppEnrolleeResponder(mClientIfaceName, bootstrapInfo.listenChannel)) { 469 Log.e(TAG, "DPP Start Enrollee Responder failure"); 470 // Notify failure and clean up 471 onFailure(DppFailureCode.FAILURE); 472 return; 473 } 474 475 logd("Success: Started DPP Enrollee Responder on listen channel " 476 + bootstrapInfo.listenChannel); 477 478 try { 479 mDppRequestInfo.callback.onBootstrapUriGenerated(bootstrapInfo.uri); 480 } catch (RemoteException e) { 481 Log.e(TAG, " onBootstrapUriGenerated Callback failure"); 482 onFailure(DppFailureCode.FAILURE); 483 return; 484 } 485 } 486 487 /** 488 * Stop a current DPP session 489 * 490 * @param uid User ID 491 */ stopDppSession(int uid)492 public void stopDppSession(int uid) { 493 if (!isSessionInProgress()) { 494 logd("UID " + uid + " called stop DPP session with no active DPP session"); 495 return; 496 } 497 498 if (mDppRequestInfo.uid != uid) { 499 Log.e(TAG, "UID " + uid + " called stop DPP session but UID " + mDppRequestInfo.uid 500 + " has started it"); 501 return; 502 } 503 504 // Clean up supplicant resources 505 if (mDppRequestInfo.authRole == DPP_AUTH_ROLE_INITIATOR) { 506 if (!mWifiNative.stopDppInitiator(mClientIfaceName)) { 507 Log.e(TAG, "Failed to stop DPP Initiator"); 508 } 509 } 510 511 cleanupDppResources(); 512 513 logd("Success: Stopped DPP Session"); 514 } 515 cleanupDppResources()516 private void cleanupDppResources() { 517 logd("DPP clean up resources"); 518 if (!isSessionInProgress()) { 519 return; 520 } 521 522 // Cancel pending timeout 523 mDppTimeoutMessage.cancel(); 524 525 // Remove the URI from the supplicant list 526 if (mDppRequestInfo.authRole == DPP_AUTH_ROLE_INITIATOR) { 527 if (!mWifiNative.removeDppUri(mClientIfaceName, mDppRequestInfo.peerId)) { 528 Log.e(TAG, "Failed to remove DPP URI ID " + mDppRequestInfo.peerId); 529 } 530 } else if (mDppRequestInfo.authRole == DPP_AUTH_ROLE_RESPONDER) { 531 if (!mWifiNative.stopDppResponder(mClientIfaceName, mDppRequestInfo.bootstrapId)) { 532 Log.e(TAG, "Failed to stop DPP Responder"); 533 } 534 } 535 536 mDppRequestInfo.binder.unlinkToDeath(mDppRequestInfo.dr, 0); 537 538 mDppRequestInfo = null; 539 } 540 541 /** 542 * Indicates whether there is a dpp session in progress or not. 543 */ isSessionInProgress()544 public boolean isSessionInProgress() { 545 return mDppRequestInfo != null; 546 } 547 548 private static class DppRequestInfo { 549 public int uid; 550 public String packageName; 551 public IBinder binder; 552 public IBinder.DeathRecipient dr; 553 public int peerId; 554 public IDppCallback callback; 555 public long startTime; 556 public int authRole = DPP_AUTH_ROLE_INACTIVE; 557 public int bootstrapId; 558 559 @Override toString()560 public String toString() { 561 return new StringBuilder("DppRequestInfo: uid=").append(uid).append(", binder=").append( 562 binder).append(", dr=").append(dr) 563 .append(", callback=").append(callback) 564 .append(", peerId=").append(peerId) 565 .append(", authRole=").append(authRole) 566 .append(", bootstrapId=").append(bootstrapId).toString(); 567 } 568 } 569 570 /** 571 * Enable vervose logging from DppManager 572 * 573 * @param verbose 0 to disable verbose logging, or any other value to enable. 574 */ enableVerboseLogging(int verbose)575 public void enableVerboseLogging(int verbose) { 576 mVerboseLoggingEnabled = verbose != 0 ? true : false; 577 } 578 onSuccessConfigReceived(WifiConfiguration newWifiConfiguration)579 private void onSuccessConfigReceived(WifiConfiguration newWifiConfiguration) { 580 try { 581 logd("onSuccessConfigReceived"); 582 583 if (mDppRequestInfo != null) { 584 long now = mClock.getElapsedSinceBootMillis(); 585 mDppMetrics.updateDppOperationTime((int) (now - mDppRequestInfo.startTime)); 586 587 NetworkUpdateResult networkUpdateResult = mWifiConfigManager 588 .addOrUpdateNetwork(newWifiConfiguration, mDppRequestInfo.uid); 589 590 if (networkUpdateResult.isSuccess()) { 591 mDppMetrics.updateDppEnrolleeSuccess(); 592 if (mDppRequestInfo.authRole == DPP_AUTH_ROLE_RESPONDER) { 593 mDppMetrics.updateDppEnrolleeResponderSuccess(); 594 } 595 mDppRequestInfo.callback.onSuccessConfigReceived( 596 WifiConfigurationUtil.addSecurityTypeToNetworkId( 597 networkUpdateResult.getNetworkId(), 598 newWifiConfiguration.getDefaultSecurityParams() 599 .getSecurityType())); 600 } else { 601 Log.e(TAG, "DPP configuration received, but failed to update network"); 602 mDppMetrics.updateDppFailure(EasyConnectStatusCallback 603 .EASY_CONNECT_EVENT_FAILURE_CONFIGURATION); 604 mDppRequestInfo.callback.onFailure(EasyConnectStatusCallback 605 .EASY_CONNECT_EVENT_FAILURE_CONFIGURATION, null, null, new int[0]); 606 } 607 } else { 608 Log.e(TAG, "Unexpected null Wi-Fi configuration object"); 609 } 610 } catch (RemoteException e) { 611 Log.e(TAG, "Callback failure"); 612 } 613 614 // Success, DPP is complete. Clear the DPP session automatically 615 cleanupDppResources(); 616 } 617 onSuccess(int dppStatusCode)618 private void onSuccess(int dppStatusCode) { 619 try { 620 if (mDppRequestInfo == null) { 621 Log.e(TAG, "onSuccess event without a request information object"); 622 return; 623 } 624 625 logd("onSuccess: " + dppStatusCode); 626 long now = mClock.getElapsedSinceBootMillis(); 627 mDppMetrics.updateDppOperationTime((int) (now - mDppRequestInfo.startTime)); 628 629 int dppSuccessCode; 630 631 // Convert from HAL codes to WifiManager/user codes 632 switch (dppStatusCode) { 633 case DppSuccessCode.CONFIGURATION_SENT: 634 mDppMetrics.updateDppR1CapableEnrolleeResponderDevices(); 635 dppSuccessCode = EasyConnectStatusCallback 636 .EASY_CONNECT_EVENT_SUCCESS_CONFIGURATION_SENT; 637 break; 638 639 case DppSuccessCode.CONFIGURATION_APPLIED: 640 dppSuccessCode = EasyConnectStatusCallback 641 .EASY_CONNECT_EVENT_SUCCESS_CONFIGURATION_APPLIED; 642 break; 643 644 default: 645 Log.e(TAG, "onSuccess: unknown code " + dppStatusCode); 646 // Success, DPP is complete. Clear the DPP session automatically 647 cleanupDppResources(); 648 return; 649 } 650 651 mDppMetrics.updateDppConfiguratorSuccess(dppSuccessCode); 652 mDppRequestInfo.callback.onSuccess(dppSuccessCode); 653 654 } catch (RemoteException e) { 655 Log.e(TAG, "Callback failure"); 656 } 657 658 // Success, DPP is complete. Clear the DPP session automatically 659 cleanupDppResources(); 660 } 661 onProgress(int dppStatusCode)662 private void onProgress(int dppStatusCode) { 663 try { 664 if (mDppRequestInfo == null) { 665 Log.e(TAG, "onProgress event without a request information object"); 666 return; 667 } 668 669 logd("onProgress: " + dppStatusCode); 670 671 int dppProgressCode; 672 673 // Convert from HAL codes to WifiManager/user codes 674 switch (dppStatusCode) { 675 case DppProgressCode.AUTHENTICATION_SUCCESS: 676 dppProgressCode = EasyConnectStatusCallback 677 .EASY_CONNECT_EVENT_PROGRESS_AUTHENTICATION_SUCCESS; 678 break; 679 680 case DppProgressCode.RESPONSE_PENDING: 681 dppProgressCode = EasyConnectStatusCallback 682 .EASY_CONNECT_EVENT_PROGRESS_RESPONSE_PENDING; 683 break; 684 685 case DppProgressCode.CONFIGURATION_SENT_WAITING_RESPONSE: 686 mDppMetrics.updateDppR2CapableEnrolleeResponderDevices(); 687 dppProgressCode = EasyConnectStatusCallback 688 .EASY_CONNECT_EVENT_PROGRESS_CONFIGURATION_SENT_WAITING_RESPONSE; 689 break; 690 691 case DppProgressCode.CONFIGURATION_ACCEPTED: 692 dppProgressCode = EasyConnectStatusCallback 693 .EASY_CONNECT_EVENT_PROGRESS_CONFIGURATION_ACCEPTED; 694 break; 695 696 default: 697 Log.e(TAG, "onProgress: unknown code " + dppStatusCode); 698 return; 699 } 700 701 mDppRequestInfo.callback.onProgress(dppProgressCode); 702 703 } catch (RemoteException e) { 704 Log.e(TAG, "Callback failure"); 705 } 706 } 707 onFailure(int dppStatusCode)708 private void onFailure(int dppStatusCode) { 709 onFailure(dppStatusCode, null, null, null); 710 } 711 712 /** 713 * 714 * This function performs the Enrollee compatibility check with the network. 715 * Compatibilty check is done based on the channel match. 716 * The logic looks into the scan cache and checks if network's 717 * operating channel match with one of the channel in enrollee's scanned channel list. 718 * 719 * @param ssid Network name. 720 * @param channelList contains the list of operating class/channels enrollee used to search for 721 * the network. 722 * Reference: DPP spec section: DPP Connection Status Object section. 723 * (eg for channelList: "81/1,2,3,4,5,6,7,8,9,10,11,117/40,115/48") 724 * @return True On compatibility check failures due to error conditions or 725 * when AP is not seen in scan cache or when AP is seen in scan cache and 726 * operating channel is included in enrollee's scanned channel list. 727 * False when network's operating channel is not included in Enrollee's 728 * scanned channel list. 729 * 730 */ isEnrolleeCompatibleWithNetwork(String ssid, String channelList)731 private boolean isEnrolleeCompatibleWithNetwork(String ssid, String channelList) { 732 if (ssid == null || channelList == null) { 733 return true; 734 } 735 SparseArray<int[]> dppChannelList = WifiManager.parseDppChannelList(channelList); 736 737 if (dppChannelList.size() == 0) { 738 Log.d(TAG, "No channels found after parsing channel list string"); 739 return true; 740 } 741 742 List<Integer> freqList = new ArrayList<Integer>(); 743 744 /* Convert the received operatingClass/channels to list of frequencies */ 745 for (int i = 0; i < dppChannelList.size(); i++) { 746 /* Derive the band corresponding to operating class */ 747 int operatingClass = dppChannelList.keyAt(i); 748 int[] channels = dppChannelList.get(operatingClass); 749 int band = ApConfigUtil.getBandFromOperatingClass(operatingClass); 750 if (band < 0) { 751 Log.e(TAG, "Band corresponding to the operating class: " + operatingClass 752 + " not found in the table"); 753 continue; 754 } 755 /* Derive frequency list from channel and band */ 756 for (int j = 0; j < channels.length; j++) { 757 int freq = ApConfigUtil.convertChannelToFrequency(channels[j], band); 758 if (freq < 0) { 759 Log.e(TAG, "Invalid frequency after converting channel: " + channels[j] 760 + " band: " + band); 761 continue; 762 } 763 freqList.add(freq); 764 } 765 } 766 767 if (freqList.size() == 0) { 768 Log.d(TAG, "frequency list is empty"); 769 return true; 770 } 771 772 /* Check the scan cache for the network enrollee tried to find */ 773 boolean isNetworkInScanCache = false; 774 boolean channelMatch = false; 775 for (ScanResult scanResult : mScanRequestProxy.getScanResults()) { 776 if (!ssid.equals(scanResult.SSID)) { 777 continue; 778 } 779 isNetworkInScanCache = true; 780 if (freqList.contains(scanResult.frequency)) { 781 channelMatch = true; 782 break; 783 } 784 } 785 786 if (isNetworkInScanCache & !channelMatch) { 787 Log.d(TAG, "Optionally update the error code to " 788 + "ENROLLEE_FAILED_TO_SCAN_NETWORK_CHANNEL as enrollee didn't scan" 789 + "network's operating channel"); 790 mDppMetrics.updateDppR2EnrolleeResponderIncompatibleConfiguration(); 791 return false; 792 } 793 return true; 794 } 795 796 private @EasyConnectStatusCallback.EasyConnectFailureStatusCode int getFailureStatusCodeOnEnrolleeInCompatibleWithNetwork()797 getFailureStatusCodeOnEnrolleeInCompatibleWithNetwork() { 798 if (!SdkLevel.isAtLeastS() || mDppRequestInfo.packageName != null 799 && mWifiPermissionsUtil.isTargetSdkLessThan( 800 mDppRequestInfo.packageName, Build.VERSION_CODES.S, 801 mDppRequestInfo.uid)) { 802 return EasyConnectStatusCallback 803 .EASY_CONNECT_EVENT_FAILURE_NOT_COMPATIBLE; 804 } else { 805 return EasyConnectStatusCallback 806 .EASY_CONNECT_EVENT_FAILURE_ENROLLEE_FAILED_TO_SCAN_NETWORK_CHANNEL; 807 } 808 } 809 onFailure(int dppStatusCode, String ssid, String channelList, int[] bandList)810 private void onFailure(int dppStatusCode, String ssid, String channelList, int[] bandList) { 811 try { 812 if (mDppRequestInfo == null) { 813 Log.e(TAG, "onFailure event without a request information object"); 814 return; 815 } 816 817 logd("OnFailure: " + dppStatusCode); 818 819 long now = mClock.getElapsedSinceBootMillis(); 820 mDppMetrics.updateDppOperationTime((int) (now - mDppRequestInfo.startTime)); 821 822 int dppFailureCode; 823 824 // Convert from HAL codes to WifiManager/user codes 825 switch (dppStatusCode) { 826 case DppFailureCode.INVALID_URI: 827 dppFailureCode = 828 EasyConnectStatusCallback.EASY_CONNECT_EVENT_FAILURE_INVALID_URI; 829 break; 830 831 case DppFailureCode.AUTHENTICATION: 832 dppFailureCode = 833 EasyConnectStatusCallback.EASY_CONNECT_EVENT_FAILURE_AUTHENTICATION; 834 break; 835 836 case DppFailureCode.NOT_COMPATIBLE: 837 dppFailureCode = 838 EasyConnectStatusCallback.EASY_CONNECT_EVENT_FAILURE_NOT_COMPATIBLE; 839 break; 840 841 case DppFailureCode.CONFIGURATION: 842 dppFailureCode = 843 EasyConnectStatusCallback.EASY_CONNECT_EVENT_FAILURE_CONFIGURATION; 844 break; 845 846 case DppFailureCode.BUSY: 847 dppFailureCode = EasyConnectStatusCallback.EASY_CONNECT_EVENT_FAILURE_BUSY; 848 break; 849 850 case DppFailureCode.TIMEOUT: 851 dppFailureCode = EasyConnectStatusCallback.EASY_CONNECT_EVENT_FAILURE_TIMEOUT; 852 break; 853 854 case DppFailureCode.NOT_SUPPORTED: 855 dppFailureCode = 856 EasyConnectStatusCallback.EASY_CONNECT_EVENT_FAILURE_NOT_SUPPORTED; 857 break; 858 859 case DppFailureCode.CANNOT_FIND_NETWORK: 860 // This is the only case where channel list is populated, according to the 861 // DPP spec section 6.3.5.2 DPP Connection Status Object 862 if (isEnrolleeCompatibleWithNetwork(ssid, channelList)) { 863 dppFailureCode = 864 EasyConnectStatusCallback 865 .EASY_CONNECT_EVENT_FAILURE_CANNOT_FIND_NETWORK; 866 } else { 867 dppFailureCode = getFailureStatusCodeOnEnrolleeInCompatibleWithNetwork(); 868 } 869 break; 870 871 case DppFailureCode.ENROLLEE_AUTHENTICATION: 872 dppFailureCode = EasyConnectStatusCallback 873 .EASY_CONNECT_EVENT_FAILURE_ENROLLEE_AUTHENTICATION; 874 break; 875 876 case DppFailureCode.CONFIGURATION_REJECTED: 877 dppFailureCode = EasyConnectStatusCallback 878 .EASY_CONNECT_EVENT_FAILURE_ENROLLEE_REJECTED_CONFIGURATION; 879 break; 880 881 case DppFailureCode.URI_GENERATION: 882 if (SdkLevel.isAtLeastS()) { 883 dppFailureCode = EasyConnectStatusCallback 884 .EASY_CONNECT_EVENT_FAILURE_URI_GENERATION; 885 } else { 886 dppFailureCode = EasyConnectStatusCallback 887 .EASY_CONNECT_EVENT_FAILURE_GENERIC; 888 } 889 break; 890 891 case DppFailureCode.FAILURE: 892 default: 893 dppFailureCode = EasyConnectStatusCallback.EASY_CONNECT_EVENT_FAILURE_GENERIC; 894 break; 895 } 896 897 mDppMetrics.updateDppFailure(dppFailureCode); 898 if (bandList == null) { 899 bandList = new int[0]; 900 } 901 mDppRequestInfo.callback.onFailure(dppFailureCode, ssid, channelList, bandList); 902 903 } catch (RemoteException e) { 904 Log.e(TAG, "Callback failure"); 905 } 906 907 // All failures are fatal, clear the DPP session 908 cleanupDppResources(); 909 } 910 logd(String message)911 private void logd(String message) { 912 if (mVerboseLoggingEnabled) { 913 Log.d(TAG, message); 914 } 915 } 916 linkToDeath(DppRequestInfo dppRequestInfo)917 private boolean linkToDeath(DppRequestInfo dppRequestInfo) { 918 // register for binder death 919 dppRequestInfo.dr = new IBinder.DeathRecipient() { 920 @Override 921 public void binderDied() { 922 if (dppRequestInfo == null) { 923 return; 924 } 925 926 logd("binderDied: uid=" + dppRequestInfo.uid); 927 928 mHandler.post(() -> { 929 cleanupDppResources(); 930 }); 931 } 932 }; 933 934 try { 935 dppRequestInfo.binder.linkToDeath(dppRequestInfo.dr, 0); 936 } catch (RemoteException e) { 937 Log.e(TAG, "Error on linkToDeath - " + e); 938 dppRequestInfo.dr = null; 939 return false; 940 } 941 942 return true; 943 } 944 945 @RequiresApi(Build.VERSION_CODES.S) convertEasyConnectCryptographyCurveToHidlDppCurve( @ifiManager.EasyConnectCryptographyCurve int curve)946 private int convertEasyConnectCryptographyCurveToHidlDppCurve( 947 @WifiManager.EasyConnectCryptographyCurve int curve) { 948 switch (curve) { 949 case WifiManager.EASY_CONNECT_CRYPTOGRAPHY_CURVE_SECP384R1: 950 return DppCurve.SECP384R1; 951 case WifiManager.EASY_CONNECT_CRYPTOGRAPHY_CURVE_SECP521R1: 952 return DppCurve.SECP521R1; 953 case WifiManager.EASY_CONNECT_CRYPTOGRAPHY_CURVE_BRAINPOOLP256R1: 954 return DppCurve.BRAINPOOLP256R1; 955 case WifiManager.EASY_CONNECT_CRYPTOGRAPHY_CURVE_BRAINPOOLP384R1: 956 return DppCurve.BRAINPOOLP384R1; 957 case WifiManager.EASY_CONNECT_CRYPTOGRAPHY_CURVE_BRAINPOOLP512R1: 958 return DppCurve.BRAINPOOLP512R1; 959 case WifiManager.EASY_CONNECT_CRYPTOGRAPHY_CURVE_PRIME256V1: 960 default: 961 return DppCurve.PRIME256V1; 962 } 963 } 964 } 965