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