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