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