SupplicantStaIfaceHal.java revision c7a4b6706fa09042bb36a64036d86d88eb6e4126
1/* 2 * Copyright (C) 2017 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 */ 16package com.android.server.wifi; 17 18import android.content.Context; 19import android.hardware.wifi.supplicant.V1_0.ISupplicant; 20import android.hardware.wifi.supplicant.V1_0.ISupplicantIface; 21import android.hardware.wifi.supplicant.V1_0.ISupplicantNetwork; 22import android.hardware.wifi.supplicant.V1_0.ISupplicantStaIface; 23import android.hardware.wifi.supplicant.V1_0.ISupplicantStaNetwork; 24import android.hardware.wifi.supplicant.V1_0.IfaceType; 25import android.hardware.wifi.supplicant.V1_0.SupplicantStatus; 26import android.hardware.wifi.supplicant.V1_0.SupplicantStatusCode; 27import android.hidl.manager.V1_0.IServiceManager; 28import android.hidl.manager.V1_0.IServiceNotification; 29import android.net.IpConfiguration; 30import android.net.wifi.WifiConfiguration; 31import android.os.RemoteException; 32import android.util.Log; 33import android.util.MutableBoolean; 34import android.util.SparseArray; 35 36import com.android.server.wifi.util.NativeUtil; 37 38import java.util.ArrayList; 39import java.util.HashMap; 40import java.util.List; 41import java.util.Map; 42 43/** 44 * Hal calls for bring up/shut down of the supplicant daemon and for 45 * sending requests to the supplicant daemon 46 */ 47public class SupplicantStaIfaceHal { 48 private static final boolean DBG = false; 49 private static final String TAG = "SupplicantStaIfaceHal"; 50 private static final String SERVICE_MANAGER_NAME = "manager"; 51 private IServiceManager mIServiceManager = null; 52 // Supplicant HAL interface objects 53 private ISupplicant mISupplicant; 54 private ISupplicantStaIface mISupplicantStaIface; 55 // Currently configured network in wpa_supplicant 56 private SupplicantStaNetworkHal mCurrentNetwork; 57 // Currently configured network's framework network Id. 58 private int mFrameworkNetworkId; 59 private final Object mLock = new Object(); 60 private final Context mContext; 61 private final WifiMonitor mWifiMonitor; 62 63 public SupplicantStaIfaceHal(Context context, WifiMonitor monitor) { 64 mContext = context; 65 mWifiMonitor = monitor; 66 } 67 68 /** 69 * Registers a service notification for the ISupplicant service, which triggers intialization of 70 * the ISupplicantStaIface 71 * @return true if the service notification was successfully registered 72 */ 73 public boolean initialize() { 74 if (DBG) Log.i(TAG, "Registering ISupplicant service ready callback."); 75 synchronized (mLock) { 76 mISupplicant = null; 77 mISupplicantStaIface = null; 78 if (mIServiceManager != null) { 79 // Already have an IServiceManager and serviceNotification registered, don't 80 // don't register another. 81 return true; 82 } 83 try { 84 mIServiceManager = getServiceManagerMockable(); 85 if (mIServiceManager == null) { 86 Log.e(TAG, "Failed to get HIDL Service Manager"); 87 return false; 88 } 89 if (!mIServiceManager.linkToDeath(cookie -> { 90 Log.wtf(TAG, "IServiceManager died: cookie=" + cookie); 91 synchronized (mLock) { 92 supplicantServiceDiedHandler(); 93 mIServiceManager = null; // Will need to register a new ServiceNotification 94 } 95 }, 0)) { 96 Log.wtf(TAG, "Error on linkToDeath on IServiceManager"); 97 supplicantServiceDiedHandler(); 98 mIServiceManager = null; // Will need to register a new ServiceNotification 99 return false; 100 } 101 IServiceNotification serviceNotificationCb = new IServiceNotification.Stub() { 102 public void onRegistration(String fqName, String name, boolean preexisting) { 103 synchronized (mLock) { 104 if (DBG) { 105 Log.i(TAG, "IServiceNotification.onRegistration for: " + fqName 106 + ", " + name + " preexisting=" + preexisting); 107 } 108 if (!initSupplicantService() || !initSupplicantStaIface()) { 109 Log.e(TAG, "initalizing ISupplicantIfaces failed."); 110 supplicantServiceDiedHandler(); 111 } else { 112 Log.i(TAG, "Completed initialization of ISupplicant interfaces."); 113 } 114 } 115 } 116 }; 117 /* TODO(b/33639391) : Use the new ISupplicant.registerForNotifications() once it 118 exists */ 119 if (!mIServiceManager.registerForNotifications(ISupplicant.kInterfaceName, 120 "", serviceNotificationCb)) { 121 Log.e(TAG, "Failed to register for notifications to " 122 + ISupplicant.kInterfaceName); 123 mIServiceManager = null; // Will need to register a new ServiceNotification 124 return false; 125 } 126 } catch (RemoteException e) { 127 Log.e(TAG, "Exception while trying to register a listener for ISupplicant service: " 128 + e); 129 supplicantServiceDiedHandler(); 130 } 131 return true; 132 } 133 } 134 135 private boolean initSupplicantService() { 136 synchronized (mLock) { 137 try { 138 mISupplicant = getSupplicantMockable(); 139 } catch (RemoteException e) { 140 Log.e(TAG, "ISupplicant.getService exception: " + e); 141 return false; 142 } 143 if (mISupplicant == null) { 144 Log.e(TAG, "Got null ISupplicant service. Stopping supplicant HIDL startup"); 145 return false; 146 } 147 } 148 return true; 149 } 150 151 private boolean initSupplicantStaIface() { 152 synchronized (mLock) { 153 /** List all supplicant Ifaces */ 154 final ArrayList<ISupplicant.IfaceInfo> supplicantIfaces = new ArrayList<>(); 155 try { 156 mISupplicant.listInterfaces((SupplicantStatus status, 157 ArrayList<ISupplicant.IfaceInfo> ifaces) -> { 158 if (status.code != SupplicantStatusCode.SUCCESS) { 159 Log.e(TAG, "Getting Supplicant Interfaces failed: " + status.code); 160 return; 161 } 162 supplicantIfaces.addAll(ifaces); 163 }); 164 } catch (RemoteException e) { 165 Log.e(TAG, "ISupplicant.listInterfaces exception: " + e); 166 return false; 167 } 168 if (supplicantIfaces.size() == 0) { 169 Log.e(TAG, "Got zero HIDL supplicant ifaces. Stopping supplicant HIDL startup."); 170 return false; 171 } 172 Mutable<ISupplicantIface> supplicantIface = new Mutable<>(); 173 for (ISupplicant.IfaceInfo ifaceInfo : supplicantIfaces) { 174 if (ifaceInfo.type == IfaceType.STA) { 175 try { 176 mISupplicant.getInterface(ifaceInfo, 177 (SupplicantStatus status, ISupplicantIface iface) -> { 178 if (status.code != SupplicantStatusCode.SUCCESS) { 179 Log.e(TAG, "Failed to get ISupplicantIface " + status.code); 180 return; 181 } 182 supplicantIface.value = iface; 183 }); 184 } catch (RemoteException e) { 185 Log.e(TAG, "ISupplicant.getInterface exception: " + e); 186 return false; 187 } 188 break; 189 } 190 } 191 if (supplicantIface.value == null) { 192 Log.e(TAG, "initSupplicantStaIface got null iface"); 193 return false; 194 } 195 mISupplicantStaIface = getStaIfaceMockable(supplicantIface.value); 196 return true; 197 } 198 } 199 200 private void supplicantServiceDiedHandler() { 201 synchronized (mLock) { 202 mISupplicant = null; 203 mISupplicantStaIface = null; 204 } 205 } 206 207 /** 208 * Signals whether Initialization completed successfully. Only necessary for testing, is not 209 * needed to guard calls etc. 210 */ 211 public boolean isInitializationComplete() { 212 return mISupplicantStaIface != null; 213 } 214 215 /** 216 * Wrapper functions to access static HAL methods, created to be mockable in unit tests 217 */ 218 protected IServiceManager getServiceManagerMockable() throws RemoteException { 219 return IServiceManager.getService(SERVICE_MANAGER_NAME); 220 } 221 222 protected ISupplicant getSupplicantMockable() throws RemoteException { 223 return ISupplicant.getService(); 224 } 225 226 protected ISupplicantStaIface getStaIfaceMockable(ISupplicantIface iface) { 227 return ISupplicantStaIface.asInterface(iface.asBinder()); 228 } 229 230 /** 231 * Add a network configuration to wpa_supplicant. 232 * 233 * @param config Config corresponding to the network. 234 * @return SupplicantStaNetwork of the added network in wpa_supplicant. 235 */ 236 private SupplicantStaNetworkHal addNetwork(WifiConfiguration config) { 237 logi("addSupplicantStaNetwork via HIDL"); 238 if (config == null) { 239 loge("Cannot add NULL network!"); 240 return null; 241 } 242 SupplicantStaNetworkHal network = addNetwork(); 243 if (network == null) { 244 loge("Failed to add a network!"); 245 return null; 246 } 247 if (network.saveWifiConfiguration(config)) { 248 return network; 249 } else { 250 loge("Failed to save variables for: " + config.configKey()); 251 return null; 252 } 253 } 254 255 /** 256 * Add the provided network configuration to wpa_supplicant and initiate connection to it. 257 * This method does the following: 258 * 1. Triggers disconnect command to wpa_supplicant (if |shouldDisconnect| is true). 259 * 2. Remove any existing network in wpa_supplicant. 260 * 3. Add a new network to wpa_supplicant. 261 * 4. Save the provided configuration to wpa_supplicant. 262 * 5. Select the new network in wpa_supplicant. 263 * 264 * @param config WifiConfiguration parameters for the provided network. 265 * @param shouldDisconnect whether to trigger a disconnection or not. 266 * @return {@code true} if it succeeds, {@code false} otherwise 267 */ 268 public boolean connectToNetwork(WifiConfiguration config, boolean shouldDisconnect) { 269 mFrameworkNetworkId = WifiConfiguration.INVALID_NETWORK_ID; 270 mCurrentNetwork = null; 271 logd("connectToNetwork " + config.configKey() 272 + " (shouldDisconnect " + shouldDisconnect + ")"); 273 if (shouldDisconnect && !disconnect()) { 274 loge("Failed to trigger disconnect"); 275 return false; 276 } 277 if (!removeAllNetworks()) { 278 loge("Failed to remove existing networks"); 279 return false; 280 } 281 mCurrentNetwork = addNetwork(config); 282 if (mCurrentNetwork == null) { 283 loge("Failed to add/save network configuration: " + config.configKey()); 284 return false; 285 } 286 if (!mCurrentNetwork.select()) { 287 loge("Failed to select network configuration: " + config.configKey()); 288 return false; 289 } 290 mFrameworkNetworkId = config.networkId; 291 return true; 292 } 293 294 /** 295 * Initiates roaming to the already configured network in wpa_supplicant. If the network 296 * configuration provided does not match the already configured network, then this triggers 297 * a new connection attempt (instead of roam). 298 * 1. First check if we're attempting to connect to the same network as we currently have 299 * configured. 300 * 2. Set the new bssid for the network in wpa_supplicant. 301 * 3. Trigger reassociate command to wpa_supplicant. 302 * 303 * @param config WifiConfiguration parameters for the provided network. 304 * @return {@code true} if it succeeds, {@code false} otherwise 305 */ 306 public boolean roamToNetwork(WifiConfiguration config) { 307 if (mFrameworkNetworkId != config.networkId || mCurrentNetwork == null) { 308 Log.w(TAG, "Cannot roam to a different network, initiate new connection. " 309 + "Current network ID: " + mFrameworkNetworkId); 310 return connectToNetwork(config, false); 311 } 312 String bssid = config.getNetworkSelectionStatus().getNetworkSelectionBSSID(); 313 logd("roamToNetwork" + config.configKey() + " (bssid " + bssid + ")"); 314 if (!mCurrentNetwork.setBssid(bssid)) { 315 loge("Failed to set new bssid on network: " + config.configKey()); 316 return false; 317 } 318 if (!reassociate()) { 319 loge("Failed to trigger reassociate"); 320 return false; 321 } 322 return true; 323 } 324 325 /** 326 * Load all the configured networks from wpa_supplicant. 327 * 328 * @param configs Map of configuration key to configuration objects corresponding to all 329 * the networks. 330 * @param networkExtras Map of extra configuration parameters stored in wpa_supplicant.conf 331 * @return true if succeeds, false otherwise. 332 */ 333 public boolean loadNetworks(Map<String, WifiConfiguration> configs, 334 SparseArray<Map<String, String>> networkExtras) { 335 List<Integer> networkIds = listNetworks(); 336 if (networkIds == null) { 337 Log.e(TAG, "Failed to list networks"); 338 return false; 339 } 340 for (Integer networkId : networkIds) { 341 SupplicantStaNetworkHal network = getNetwork(networkId); 342 if (network == null) { 343 Log.e(TAG, "Failed to get network with ID: " + networkId); 344 return false; 345 } 346 WifiConfiguration config = new WifiConfiguration(); 347 Map<String, String> networkExtra = new HashMap<>(); 348 if (!network.loadWifiConfiguration(config, networkExtra)) { 349 Log.e(TAG, "Failed to load wifi configuration for network with ID: " + networkId); 350 return false; 351 } 352 // Set the default IP assignments. 353 config.setIpAssignment(IpConfiguration.IpAssignment.DHCP); 354 config.setProxySettings(IpConfiguration.ProxySettings.NONE); 355 356 networkExtras.put(networkId, networkExtra); 357 String configKey = networkExtra.get(SupplicantStaNetworkHal.ID_STRING_KEY_CONFIG_KEY); 358 final WifiConfiguration duplicateConfig = configs.put(configKey, config); 359 if (duplicateConfig != null) { 360 // The network is already known. Overwrite the duplicate entry. 361 Log.i(TAG, "Replacing duplicate network: " + duplicateConfig.networkId); 362 removeNetwork(duplicateConfig.networkId); 363 networkExtras.remove(duplicateConfig.networkId); 364 } 365 } 366 return true; 367 } 368 369 /** 370 * Remove all networks from supplicant 371 */ 372 public boolean removeAllNetworks() { 373 synchronized (mLock) { 374 ArrayList<Integer> networks = listNetworks(); 375 if (networks == null) { 376 Log.e(TAG, "removeAllNetworks failed, got null networks"); 377 return false; 378 } 379 for (int id : networks) { 380 if (!removeNetwork(id)) { 381 Log.e(TAG, "removeAllNetworks failed to remove network: " + id); 382 return false; 383 } 384 } 385 } 386 return true; 387 } 388 389 /** 390 * Gets the interface name. 391 * 392 * @return returns the name of Iface or null if the call fails 393 */ 394 private String getName() { 395 synchronized (mLock) { 396 MutableBoolean statusSuccess = new MutableBoolean(false); 397 final String methodStr = "getName"; 398 if (DBG) Log.i(TAG, methodStr); 399 if (!checkSupplicantStaIfaceAndLogFailure(methodStr)) return null; 400 final StringBuilder builder = new StringBuilder(); 401 try { 402 mISupplicantStaIface.getName((SupplicantStatus status, String name) -> { 403 statusSuccess.value = status.code == SupplicantStatusCode.SUCCESS; 404 if (!statusSuccess.value) { 405 Log.e(TAG, methodStr + " failed: " + status.debugMessage); 406 } else { 407 builder.append(name); 408 } 409 }); 410 } catch (RemoteException e) { 411 Log.e(TAG, "ISupplicantStaIface." + methodStr + ": exception: " + e); 412 supplicantServiceDiedHandler(); 413 } 414 if (statusSuccess.value) { 415 return builder.toString(); 416 } else { 417 return null; 418 } 419 } 420 } 421 422 /** 423 * Adds a new network. 424 * 425 * @return The ISupplicantNetwork object for the new network, or null if the call fails 426 */ 427 private SupplicantStaNetworkHal addNetwork() { 428 synchronized (mLock) { 429 MutableBoolean statusSuccess = new MutableBoolean(false); 430 Mutable<ISupplicantNetwork> newNetwork = new Mutable<>(); 431 final String methodStr = "addNetwork"; 432 if (DBG) Log.i(TAG, methodStr); 433 if (!checkSupplicantStaIfaceAndLogFailure(methodStr)) return null; 434 try { 435 mISupplicantStaIface.addNetwork((SupplicantStatus status, 436 ISupplicantNetwork network) -> { 437 statusSuccess.value = status.code == SupplicantStatusCode.SUCCESS; 438 if (!statusSuccess.value) { 439 Log.e(TAG, methodStr + " failed: " + status.debugMessage); 440 } else { 441 newNetwork.value = network; 442 } 443 }); 444 } catch (RemoteException e) { 445 Log.e(TAG, "ISupplicantStaIface." + methodStr + ": exception: " + e); 446 supplicantServiceDiedHandler(); 447 } 448 if (statusSuccess.value) { 449 return getStaNetworkMockable( 450 ISupplicantStaNetwork.asInterface(newNetwork.value.asBinder())); 451 } else { 452 return null; 453 } 454 } 455 } 456 457 /** 458 * Remove network from supplicant with network Id 459 * 460 * @return true if request is sent successfully, false otherwise. 461 */ 462 private boolean removeNetwork(int id) { 463 synchronized (mLock) { 464 final String methodStr = "removeNetwork"; 465 if (DBG) Log.i(TAG, methodStr); 466 if (!checkSupplicantStaIfaceAndLogFailure(methodStr)) return false; 467 try { 468 SupplicantStatus status = mISupplicantStaIface.removeNetwork(id); 469 return checkStatusAndLogFailure(status, methodStr); 470 } catch (RemoteException e) { 471 Log.e(TAG, "ISupplicantStaIface." + methodStr + ": exception:" + e); 472 supplicantServiceDiedHandler(); 473 return false; 474 } 475 } 476 } 477 478 /** 479 * Use this to mock the creation of SupplicantStaNetworkHal instance. 480 * 481 * @param iSupplicantStaNetwork ISupplicantStaNetwork instance retrieved from HIDL. 482 * @return The ISupplicantNetwork object for the given SupplicantNetworkId int, returns null if 483 * the call fails 484 */ 485 protected SupplicantStaNetworkHal getStaNetworkMockable( 486 ISupplicantStaNetwork iSupplicantStaNetwork) { 487 return new SupplicantStaNetworkHal(iSupplicantStaNetwork, mContext, mWifiMonitor); 488 } 489 490 /** 491 * @return The ISupplicantNetwork object for the given SupplicantNetworkId int, returns null if 492 * the call fails 493 */ 494 private SupplicantStaNetworkHal getNetwork(int id) { 495 synchronized (mLock) { 496 MutableBoolean statusSuccess = new MutableBoolean(false); 497 Mutable<ISupplicantNetwork> gotNetwork = new Mutable<>(); 498 final String methodStr = "getNetwork"; 499 if (DBG) Log.i(TAG, methodStr); 500 if (!checkSupplicantStaIfaceAndLogFailure(methodStr)) return null; 501 try { 502 mISupplicantStaIface.getNetwork(id, (SupplicantStatus status, 503 ISupplicantNetwork network) -> { 504 statusSuccess.value = status.code == SupplicantStatusCode.SUCCESS; 505 if (!statusSuccess.value) { 506 Log.e(TAG, methodStr + " failed: " + status.debugMessage); 507 } else { 508 gotNetwork.value = network; 509 } 510 }); 511 } catch (RemoteException e) { 512 Log.e(TAG, "ISupplicantStaIface." + methodStr + ": exception: " + e); 513 supplicantServiceDiedHandler(); 514 } 515 if (statusSuccess.value) { 516 return getStaNetworkMockable( 517 ISupplicantStaNetwork.asInterface(gotNetwork.value.asBinder())); 518 } else { 519 return null; 520 } 521 } 522 } 523 524 /** 525 * @return a list of SupplicantNetworkID ints for all networks controlled by supplicant, returns 526 * null if the call fails 527 */ 528 private java.util.ArrayList<Integer> listNetworks() { 529 synchronized (mLock) { 530 MutableBoolean statusSuccess = new MutableBoolean(false); 531 Mutable<ArrayList<Integer>> networkIdList = new Mutable<>(); 532 final String methodStr = "listNetworks"; 533 if (DBG) Log.i(TAG, methodStr); 534 if (!checkSupplicantStaIfaceAndLogFailure(methodStr)) return null; 535 try { 536 mISupplicantStaIface.listNetworks((SupplicantStatus status, 537 java.util.ArrayList<Integer> networkIds) -> { 538 statusSuccess.value = status.code == SupplicantStatusCode.SUCCESS; 539 if (!statusSuccess.value) { 540 Log.e(TAG, methodStr + " failed: " + status.debugMessage); 541 } else { 542 networkIdList.value = networkIds; 543 } 544 }); 545 } catch (RemoteException e) { 546 Log.e(TAG, "ISupplicantStaIface." + methodStr + ": exception: " + e); 547 supplicantServiceDiedHandler(); 548 } 549 if (statusSuccess.value) { 550 return networkIdList.value; 551 } else { 552 return null; 553 } 554 } 555 } 556 557 /** 558 * Trigger a reassociation even if the iface is currently connected. 559 * 560 * @return true if request is sent successfully, false otherwise. 561 */ 562 public boolean reassociate() { 563 synchronized (mLock) { 564 final String methodStr = "reassociate"; 565 if (DBG) Log.i(TAG, methodStr); 566 if (!checkSupplicantStaIfaceAndLogFailure(methodStr)) return false; 567 try { 568 SupplicantStatus status = mISupplicantStaIface.reassociate(); 569 return checkStatusAndLogFailure(status, methodStr); 570 } catch (RemoteException e) { 571 Log.e(TAG, "ISupplicantStaIface." + methodStr + ": exception:" + e); 572 supplicantServiceDiedHandler(); 573 return false; 574 } 575 } 576 } 577 578 /** 579 * Trigger a reconnection if the iface is disconnected. 580 * 581 * @return true if request is sent successfully, false otherwise. 582 */ 583 public boolean reconnect() { 584 synchronized (mLock) { 585 final String methodStr = "reconnect"; 586 if (DBG) Log.i(TAG, methodStr); 587 if (!checkSupplicantStaIfaceAndLogFailure(methodStr)) return false; 588 try { 589 SupplicantStatus status = mISupplicantStaIface.reconnect(); 590 return checkStatusAndLogFailure(status, methodStr); 591 } catch (RemoteException e) { 592 Log.e(TAG, "ISupplicantStaIface." + methodStr + ": exception:" + e); 593 supplicantServiceDiedHandler(); 594 return false; 595 } 596 } 597 } 598 599 /** 600 * Trigger a disconnection from the currently connected network. 601 * 602 * @return true if request is sent successfully, false otherwise. 603 */ 604 public boolean disconnect() { 605 synchronized (mLock) { 606 final String methodStr = "disconnect"; 607 if (DBG) Log.i(TAG, methodStr); 608 if (!checkSupplicantStaIfaceAndLogFailure(methodStr)) return false; 609 try { 610 SupplicantStatus status = mISupplicantStaIface.disconnect(); 611 return checkStatusAndLogFailure(status, methodStr); 612 } catch (RemoteException e) { 613 Log.e(TAG, "ISupplicantStaIface." + methodStr + ": exception:" + e); 614 supplicantServiceDiedHandler(); 615 return false; 616 } 617 } 618 } 619 620 /** 621 * Enable or disable power save mode. 622 * 623 * @param enable true to enable, false to disable. 624 * @return true if request is sent successfully, false otherwise. 625 */ 626 public boolean setPowerSave(boolean enable) { 627 synchronized (mLock) { 628 final String methodStr = "setPowerSave"; 629 if (DBG) Log.i(TAG, methodStr); 630 if (!checkSupplicantStaIfaceAndLogFailure(methodStr)) return false; 631 try { 632 SupplicantStatus status = mISupplicantStaIface.setPowerSave(enable); 633 return checkStatusAndLogFailure(status, methodStr); 634 } catch (RemoteException e) { 635 Log.e(TAG, "ISupplicantStaIface." + methodStr + ": exception:" + e); 636 supplicantServiceDiedHandler(); 637 return false; 638 } 639 } 640 } 641 642 /** 643 * Initiate TDLS discover with the specified AP. 644 * 645 * @param macAddress MAC Address of the AP. 646 * @return true if request is sent successfully, false otherwise. 647 */ 648 public boolean initiateTdlsDiscover(String macAddress) { 649 return initiateTdlsDiscover(NativeUtil.macAddressToByteArray(macAddress)); 650 } 651 /** See ISupplicantStaIface.hal for documentation */ 652 private boolean initiateTdlsDiscover(byte[/* 6 */] macAddress) { 653 synchronized (mLock) { 654 final String methodStr = "initiateTdlsDiscover"; 655 if (DBG) Log.i(TAG, methodStr); 656 if (!checkSupplicantStaIfaceAndLogFailure(methodStr)) return false; 657 try { 658 SupplicantStatus status = mISupplicantStaIface.initiateTdlsDiscover(macAddress); 659 return checkStatusAndLogFailure(status, methodStr); 660 } catch (RemoteException e) { 661 Log.e(TAG, "ISupplicantStaIface." + methodStr + ": exception:" + e); 662 supplicantServiceDiedHandler(); 663 return false; 664 } 665 } 666 } 667 668 /** 669 * Initiate TDLS setup with the specified AP. 670 * 671 * @param macAddress MAC Address of the AP. 672 * @return true if request is sent successfully, false otherwise. 673 */ 674 public boolean initiateTdlsSetup(String macAddress) { 675 return initiateTdlsSetup(NativeUtil.macAddressToByteArray(macAddress)); 676 } 677 /** See ISupplicantStaIface.hal for documentation */ 678 private boolean initiateTdlsSetup(byte[/* 6 */] macAddress) { 679 synchronized (mLock) { 680 final String methodStr = "initiateTdlsSetup"; 681 if (DBG) Log.i(TAG, methodStr); 682 if (!checkSupplicantStaIfaceAndLogFailure(methodStr)) return false; 683 try { 684 SupplicantStatus status = mISupplicantStaIface.initiateTdlsSetup(macAddress); 685 return checkStatusAndLogFailure(status, methodStr); 686 } catch (RemoteException e) { 687 Log.e(TAG, "ISupplicantStaIface." + methodStr + ": exception:" + e); 688 supplicantServiceDiedHandler(); 689 return false; 690 } 691 } 692 } 693 694 /** 695 * Initiate TDLS teardown with the specified AP. 696 * @param macAddress MAC Address of the AP. 697 * @return true if request is sent successfully, false otherwise. 698 */ 699 public boolean initiateTdlsTeardown(String macAddress) { 700 return initiateTdlsTeardown(NativeUtil.macAddressToByteArray(macAddress)); 701 } 702 703 /** See ISupplicantStaIface.hal for documentation */ 704 private boolean initiateTdlsTeardown(byte[/* 6 */] macAddress) { 705 synchronized (mLock) { 706 final String methodStr = "initiateTdlsTeardown"; 707 if (DBG) Log.i(TAG, methodStr); 708 if (!checkSupplicantStaIfaceAndLogFailure(methodStr)) return false; 709 try { 710 SupplicantStatus status = mISupplicantStaIface.initiateTdlsTeardown(macAddress); 711 return checkStatusAndLogFailure(status, methodStr); 712 } catch (RemoteException e) { 713 Log.e(TAG, "ISupplicantStaIface." + methodStr + ": exception:" + e); 714 supplicantServiceDiedHandler(); 715 return false; 716 } 717 } 718 } 719 720 /** 721 * Request the specified ANQP elements |elements| from the specified AP |bssid|. 722 * 723 * @param bssid BSSID of the AP 724 * @param infoElements ANQP elements to be queried. Refer to ISupplicantStaIface.AnqpInfoId. 725 * @param hs20SubTypes HS subtypes to be queried. Refer to ISupplicantStaIface.Hs20AnqpSubTypes. 726 * @return true if request is sent successfully, false otherwise. 727 */ 728 public boolean initiateAnqpQuery(String bssid, ArrayList<Short> infoElements, 729 ArrayList<Integer> hs20SubTypes) { 730 return initiateAnqpQuery( 731 NativeUtil.macAddressToByteArray(bssid), infoElements, hs20SubTypes); 732 } 733 734 /** See ISupplicantStaIface.hal for documentation */ 735 private boolean initiateAnqpQuery(byte[/* 6 */] macAddress, 736 java.util.ArrayList<Short> infoElements, java.util.ArrayList<Integer> subTypes) { 737 synchronized (mLock) { 738 final String methodStr = "initiateAnqpQuery"; 739 if (DBG) Log.i(TAG, methodStr); 740 if (!checkSupplicantStaIfaceAndLogFailure(methodStr)) return false; 741 try { 742 SupplicantStatus status = mISupplicantStaIface.initiateAnqpQuery(macAddress, 743 infoElements, subTypes); 744 return checkStatusAndLogFailure(status, methodStr); 745 } catch (RemoteException e) { 746 Log.e(TAG, "ISupplicantStaIface." + methodStr + ": exception:" + e); 747 supplicantServiceDiedHandler(); 748 return false; 749 } 750 } 751 } 752 753 /** 754 * Request the specified ANQP ICON from the specified AP |bssid|. 755 * 756 * @param bssid BSSID of the AP 757 * @param fileName Name of the file to request. 758 * @return true if request is sent successfully, false otherwise. 759 */ 760 public boolean initiateHs20IconQuery(String bssid, String fileName) { 761 return initiateHs20IconQuery(NativeUtil.macAddressToByteArray(bssid), fileName); 762 } 763 764 /** See ISupplicantStaIface.hal for documentation */ 765 private boolean initiateHs20IconQuery(byte[/* 6 */] macAddress, String fileName) { 766 synchronized (mLock) { 767 final String methodStr = "initiateHs20IconQuery"; 768 if (DBG) Log.i(TAG, methodStr); 769 if (!checkSupplicantStaIfaceAndLogFailure(methodStr)) return false; 770 try { 771 SupplicantStatus status = mISupplicantStaIface.initiateHs20IconQuery(macAddress, 772 fileName); 773 return checkStatusAndLogFailure(status, methodStr); 774 } catch (RemoteException e) { 775 Log.e(TAG, "ISupplicantStaIface." + methodStr + ": exception:" + e); 776 supplicantServiceDiedHandler(); 777 return false; 778 } 779 } 780 } 781 782 /** 783 * Makes a callback to HIDL to getMacAddress from supplicant 784 * 785 * @return string containing the MAC address, or null on a failed call 786 */ 787 public String getMacAddress() { 788 synchronized (mLock) { 789 MutableBoolean statusSuccess = new MutableBoolean(false); 790 final String methodStr = "getMacAddress"; 791 if (DBG) Log.i(TAG, methodStr); 792 if (!checkSupplicantStaIfaceAndLogFailure(methodStr)) return null; 793 Mutable<String> gotMac = new Mutable<>(); 794 try { 795 mISupplicantStaIface.getMacAddress((SupplicantStatus status, 796 byte[/* 6 */] macAddr) -> { 797 statusSuccess.value = status.code == SupplicantStatusCode.SUCCESS; 798 if (!statusSuccess.value) { 799 Log.e(TAG, methodStr + " failed: " + status.debugMessage); 800 } else { 801 gotMac.value = NativeUtil.macAddressFromByteArray(macAddr); 802 } 803 }); 804 } catch (RemoteException e) { 805 Log.e(TAG, "ISupplicantStaIface." + methodStr + ": exception: " + e); 806 supplicantServiceDiedHandler(); 807 } 808 if (statusSuccess.value) { 809 return gotMac.value; 810 } else { 811 return null; 812 } 813 } 814 } 815 816 /** 817 * Start using the added RX filters. 818 * 819 * @return true if request is sent successfully, false otherwise. 820 */ 821 public boolean startRxFilter() { 822 synchronized (mLock) { 823 final String methodStr = "startRxFilter"; 824 if (DBG) Log.i(TAG, methodStr); 825 if (!checkSupplicantStaIfaceAndLogFailure(methodStr)) return false; 826 try { 827 SupplicantStatus status = mISupplicantStaIface.startRxFilter(); 828 return checkStatusAndLogFailure(status, methodStr); 829 } catch (RemoteException e) { 830 Log.e(TAG, "ISupplicantStaIface." + methodStr + ": exception:" + e); 831 supplicantServiceDiedHandler(); 832 return false; 833 } 834 } 835 } 836 837 /** 838 * Stop using the added RX filters. 839 * 840 * @return true if request is sent successfully, false otherwise. 841 */ 842 public boolean stopRxFilter() { 843 synchronized (mLock) { 844 final String methodStr = "stopRxFilter"; 845 if (DBG) Log.i(TAG, methodStr); 846 if (!checkSupplicantStaIfaceAndLogFailure(methodStr)) return false; 847 try { 848 SupplicantStatus status = mISupplicantStaIface.stopRxFilter(); 849 return checkStatusAndLogFailure(status, methodStr); 850 } catch (RemoteException e) { 851 Log.e(TAG, "ISupplicantStaIface." + methodStr + ": exception:" + e); 852 supplicantServiceDiedHandler(); 853 return false; 854 } 855 } 856 } 857 858 public static final byte RX_FILTER_TYPE_V4_MULTICAST = 859 ISupplicantStaIface.RxFilterType.V6_MULTICAST; 860 public static final byte RX_FILTER_TYPE_V6_MULTICAST = 861 ISupplicantStaIface.RxFilterType.V6_MULTICAST; 862 /** 863 * Add an RX filter. 864 * 865 * @param type one of {@link #RX_FILTER_TYPE_V4_MULTICAST} or 866 * {@link #RX_FILTER_TYPE_V6_MULTICAST} values. 867 * @return true if request is sent successfully, false otherwise. 868 */ 869 private boolean addRxFilter(byte type) { 870 synchronized (mLock) { 871 final String methodStr = "addRxFilter"; 872 if (DBG) Log.i(TAG, methodStr); 873 if (!checkSupplicantStaIfaceAndLogFailure(methodStr)) return false; 874 try { 875 SupplicantStatus status = mISupplicantStaIface.addRxFilter(type); 876 return checkStatusAndLogFailure(status, methodStr); 877 } catch (RemoteException e) { 878 Log.e(TAG, "ISupplicantStaIface." + methodStr + ": exception:" + e); 879 supplicantServiceDiedHandler(); 880 return false; 881 } 882 } 883 } 884 885 /** 886 * Remove an RX filter. 887 * 888 * @param type one of {@link #RX_FILTER_TYPE_V4_MULTICAST} or 889 * {@link #RX_FILTER_TYPE_V6_MULTICAST} values. 890 * @return true if request is sent successfully, false otherwise. 891 */ 892 private boolean removeRxFilter(byte type) { 893 synchronized (mLock) { 894 final String methodStr = "removeRxFilter"; 895 if (DBG) Log.i(TAG, methodStr); 896 if (!checkSupplicantStaIfaceAndLogFailure(methodStr)) return false; 897 try { 898 SupplicantStatus status = mISupplicantStaIface.removeRxFilter(type); 899 return checkStatusAndLogFailure(status, methodStr); 900 } catch (RemoteException e) { 901 Log.e(TAG, "ISupplicantStaIface." + methodStr + ": exception:" + e); 902 supplicantServiceDiedHandler(); 903 return false; 904 } 905 } 906 } 907 908 public static final byte BT_COEX_MODE_ENABLED = ISupplicantStaIface.BtCoexistenceMode.ENABLED; 909 public static final byte BT_COEX_MODE_DISABLED = ISupplicantStaIface.BtCoexistenceMode.DISABLED; 910 public static final byte BT_COEX_MODE_SENSE = ISupplicantStaIface.BtCoexistenceMode.SENSE; 911 /** 912 * Set Bt co existense mode. 913 * 914 * @param mode one of the above {@link #BT_COEX_MODE_ENABLED}, {@link #BT_COEX_MODE_DISABLED} 915 * or {@link #BT_COEX_MODE_SENSE} values. 916 * @return true if request is sent successfully, false otherwise. 917 */ 918 public boolean setBtCoexistenceMode(byte mode) { 919 synchronized (mLock) { 920 final String methodStr = "setBtCoexistenceMode"; 921 if (DBG) Log.i(TAG, methodStr); 922 if (!checkSupplicantStaIfaceAndLogFailure(methodStr)) return false; 923 try { 924 SupplicantStatus status = mISupplicantStaIface.setBtCoexistenceMode(mode); 925 return checkStatusAndLogFailure(status, methodStr); 926 } catch (RemoteException e) { 927 Log.e(TAG, "ISupplicantStaIface." + methodStr + ": exception:" + e); 928 supplicantServiceDiedHandler(); 929 return false; 930 } 931 } 932 } 933 934 /** Enable or disable BT coexistence mode. 935 * 936 * @param enable true to enable, false to disable. 937 * @return true if request is sent successfully, false otherwise. 938 */ 939 public boolean setBtCoexistenceScanModeEnabled(boolean enable) { 940 synchronized (mLock) { 941 final String methodStr = "setBtCoexistenceScanModeEnabled"; 942 if (DBG) Log.i(TAG, methodStr); 943 if (!checkSupplicantStaIfaceAndLogFailure(methodStr)) return false; 944 try { 945 SupplicantStatus status = 946 mISupplicantStaIface.setBtCoexistenceScanModeEnabled(enable); 947 return checkStatusAndLogFailure(status, methodStr); 948 } catch (RemoteException e) { 949 Log.e(TAG, "ISupplicantStaIface." + methodStr + ": exception:" + e); 950 supplicantServiceDiedHandler(); 951 return false; 952 } 953 } 954 } 955 956 /** 957 * Enable or disable suspend mode optimizations. 958 * 959 * @param enable true to enable, false otherwise. 960 */ 961 public boolean setSuspendModeEnabled(boolean enable) { 962 synchronized (mLock) { 963 final String methodStr = "setSuspendModeEnabled"; 964 if (DBG) Log.i(TAG, methodStr); 965 if (!checkSupplicantStaIfaceAndLogFailure(methodStr)) return false; 966 try { 967 SupplicantStatus status = mISupplicantStaIface.setSuspendModeEnabled(enable); 968 return checkStatusAndLogFailure(status, methodStr); 969 } catch (RemoteException e) { 970 Log.e(TAG, "ISupplicantStaIface." + methodStr + ": exception:" + e); 971 supplicantServiceDiedHandler(); 972 return false; 973 } 974 } 975 } 976 977 /** 978 * Set country code. 979 * 980 * @param codeStr 2 byte ASCII string. For ex: US, CA. 981 * @return true if request is sent successfully, false otherwise. 982 */ 983 public boolean setCountryCode(String codeStr) { 984 return setCountryCode(NativeUtil.stringToByteArray(codeStr)); 985 } 986 987 /** See ISupplicantStaIface.hal for documentation */ 988 private boolean setCountryCode(byte[/* 2 */] code) { 989 synchronized (mLock) { 990 final String methodStr = "setCountryCode"; 991 if (DBG) Log.i(TAG, methodStr); 992 if (!checkSupplicantStaIfaceAndLogFailure(methodStr)) return false; 993 try { 994 SupplicantStatus status = mISupplicantStaIface.setCountryCode(code); 995 return checkStatusAndLogFailure(status, methodStr); 996 } catch (RemoteException e) { 997 Log.e(TAG, "ISupplicantStaIface." + methodStr + ": exception:" + e); 998 supplicantServiceDiedHandler(); 999 return false; 1000 } 1001 } 1002 } 1003 1004 /** 1005 * Returns false if SupplicantStaIface is null, and logs failure to call methodStr 1006 */ 1007 private boolean checkSupplicantStaIfaceAndLogFailure(final String methodStr) { 1008 if (DBG) Log.i(TAG, methodStr); 1009 if (mISupplicantStaIface == null) { 1010 Log.e(TAG, "Can't call " + methodStr + ", ISupplicantStaIface is null"); 1011 return false; 1012 } 1013 return true; 1014 } 1015 1016 /** 1017 * Returns true if provided status code is SUCCESS, logs debug message and returns false 1018 * otherwise 1019 */ 1020 private static boolean checkStatusAndLogFailure(SupplicantStatus status, 1021 final String methodStr) { 1022 if (DBG) Log.i(TAG, methodStr); 1023 if (status.code != SupplicantStatusCode.SUCCESS) { 1024 Log.e(TAG, methodStr + " failed: " + supplicantStatusCodeToString(status.code) + ", " 1025 + status.debugMessage); 1026 return false; 1027 } 1028 return true; 1029 } 1030 1031 /** 1032 * Converts SupplicantStatus code values to strings for debug logging 1033 * TODO(b/34811152) Remove this, or make it more break resistance 1034 */ 1035 public static String supplicantStatusCodeToString(int code) { 1036 switch (code) { 1037 case 0: 1038 return "SUCCESS"; 1039 case 1: 1040 return "FAILURE_UNKNOWN"; 1041 case 2: 1042 return "FAILURE_ARGS_INVALID"; 1043 case 3: 1044 return "FAILURE_IFACE_INVALID"; 1045 case 4: 1046 return "FAILURE_IFACE_UNKNOWN"; 1047 case 5: 1048 return "FAILURE_IFACE_EXISTS"; 1049 case 6: 1050 return "FAILURE_IFACE_DISABLED"; 1051 case 7: 1052 return "FAILURE_IFACE_NOT_DISCONNECTED"; 1053 case 8: 1054 return "FAILURE_NETWORK_INVALID"; 1055 case 9: 1056 return "FAILURE_NETWORK_UNKNOWN"; 1057 default: 1058 return "??? UNKNOWN_CODE"; 1059 } 1060 } 1061 1062 private static class Mutable<E> { 1063 public E value; 1064 1065 Mutable() { 1066 value = null; 1067 } 1068 1069 Mutable(E value) { 1070 this.value = value; 1071 } 1072 } 1073 1074 private void logd(String s) { 1075 Log.d(TAG, s); 1076 } 1077 1078 private void logi(String s) { 1079 Log.i(TAG, s); 1080 } 1081 1082 private void loge(String s) { 1083 Log.e(TAG, s); 1084 } 1085} 1086