1 /* 2 * Copyright (C) 2016 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.p2p; 18 19 import android.annotation.NonNull; 20 import android.hardware.wifi.supplicant.V1_0.ISupplicant; 21 import android.hardware.wifi.supplicant.V1_0.ISupplicantIface; 22 import android.hardware.wifi.supplicant.V1_0.ISupplicantNetwork; 23 import android.hardware.wifi.supplicant.V1_0.ISupplicantP2pIface; 24 import android.hardware.wifi.supplicant.V1_0.ISupplicantP2pIfaceCallback; 25 import android.hardware.wifi.supplicant.V1_0.ISupplicantP2pNetwork; 26 import android.hardware.wifi.supplicant.V1_0.IfaceType; 27 import android.hardware.wifi.supplicant.V1_0.SupplicantStatus; 28 import android.hardware.wifi.supplicant.V1_0.SupplicantStatusCode; 29 import android.hardware.wifi.supplicant.V1_0.WpsConfigMethods; 30 import android.hidl.manager.V1_0.IServiceManager; 31 import android.hidl.manager.V1_0.IServiceNotification; 32 import android.net.wifi.WpsInfo; 33 import android.net.wifi.p2p.WifiP2pConfig; 34 import android.net.wifi.p2p.WifiP2pDevice; 35 import android.net.wifi.p2p.WifiP2pGroup; 36 import android.net.wifi.p2p.WifiP2pGroupList; 37 import android.net.wifi.p2p.WifiP2pManager; 38 import android.net.wifi.p2p.nsd.WifiP2pServiceInfo; 39 import android.os.IHwBinder.DeathRecipient; 40 import android.os.RemoteException; 41 import android.text.TextUtils; 42 import android.util.Log; 43 44 import com.android.server.wifi.util.ArrayUtils; 45 import com.android.server.wifi.util.NativeUtil; 46 47 import java.nio.ByteBuffer; 48 import java.nio.ByteOrder; 49 import java.util.ArrayList; 50 import java.util.Arrays; 51 import java.util.List; 52 import java.util.NoSuchElementException; 53 import java.util.regex.Matcher; 54 import java.util.regex.Pattern; 55 import java.util.stream.Collectors; 56 57 /** 58 * Native calls sending requests to the P2P Hals, and callbacks for receiving P2P events 59 * 60 * {@hide} 61 */ 62 public class SupplicantP2pIfaceHal { 63 private static final String TAG = "SupplicantP2pIfaceHal"; 64 private static boolean sVerboseLoggingEnabled = true; 65 private static final int RESULT_NOT_VALID = -1; 66 private static final int DEFAULT_OPERATING_CLASS = 81; 67 /** 68 * Regex pattern for extracting the wps device type bytes. 69 * Matches a strings like the following: "<categ>-<OUI>-<subcateg>"; 70 */ 71 private static final Pattern WPS_DEVICE_TYPE_PATTERN = 72 Pattern.compile("^(\\d{1,2})-([0-9a-fA-F]{8})-(\\d{1,2})$"); 73 74 private Object mLock = new Object(); 75 76 // Supplicant HAL HIDL interface objects 77 private IServiceManager mIServiceManager = null; 78 private ISupplicant mISupplicant = null; 79 private ISupplicantIface mHidlSupplicantIface = null; 80 private ISupplicantP2pIface mISupplicantP2pIface = null; 81 private final IServiceNotification mServiceNotificationCallback = 82 new IServiceNotification.Stub() { 83 public void onRegistration(String fqName, String name, boolean preexisting) { 84 synchronized (mLock) { 85 if (sVerboseLoggingEnabled) { 86 Log.i(TAG, "IServiceNotification.onRegistration for: " + fqName 87 + ", " + name + " preexisting=" + preexisting); 88 } 89 if (!initSupplicantService()) { 90 Log.e(TAG, "initalizing ISupplicant failed."); 91 supplicantServiceDiedHandler(); 92 } else { 93 Log.i(TAG, "Completed initialization of ISupplicant interfaces."); 94 } 95 } 96 } 97 }; 98 private final DeathRecipient mServiceManagerDeathRecipient = 99 cookie -> { 100 Log.w(TAG, "IServiceManager died: cookie=" + cookie); 101 synchronized (mLock) { 102 supplicantServiceDiedHandler(); 103 mIServiceManager = null; // Will need to register a new ServiceNotification 104 } 105 }; 106 private final DeathRecipient mSupplicantDeathRecipient = 107 cookie -> { 108 Log.w(TAG, "ISupplicant/ISupplicantP2pIface died: cookie=" + cookie); 109 synchronized (mLock) { 110 supplicantServiceDiedHandler(); 111 } 112 }; 113 114 private final WifiP2pMonitor mMonitor; 115 private SupplicantP2pIfaceCallback mCallback = null; 116 SupplicantP2pIfaceHal(WifiP2pMonitor monitor)117 public SupplicantP2pIfaceHal(WifiP2pMonitor monitor) { 118 mMonitor = monitor; 119 } 120 linkToServiceManagerDeath()121 private boolean linkToServiceManagerDeath() { 122 if (mIServiceManager == null) return false; 123 try { 124 if (!mIServiceManager.linkToDeath(mServiceManagerDeathRecipient, 0)) { 125 Log.wtf(TAG, "Error on linkToDeath on IServiceManager"); 126 supplicantServiceDiedHandler(); 127 mIServiceManager = null; // Will need to register a new ServiceNotification 128 return false; 129 } 130 } catch (RemoteException e) { 131 Log.e(TAG, "IServiceManager.linkToDeath exception", e); 132 return false; 133 } 134 return true; 135 } 136 137 /** 138 * Enable verbose logging for all sub modules. 139 */ enableVerboseLogging(int verbose)140 public static void enableVerboseLogging(int verbose) { 141 sVerboseLoggingEnabled = verbose > 0; 142 SupplicantP2pIfaceCallback.enableVerboseLogging(verbose); 143 } 144 145 /** 146 * Registers a service notification for the ISupplicant service, which triggers intialization of 147 * the ISupplicantP2pIface 148 * @return true if the service notification was successfully registered 149 */ initialize()150 public boolean initialize() { 151 if (sVerboseLoggingEnabled) Log.i(TAG, "Registering ISupplicant service ready callback."); 152 synchronized (mLock) { 153 if (mIServiceManager != null) { 154 Log.i(TAG, "Supplicant HAL already initialized."); 155 // Already have an IServiceManager and serviceNotification registered, don't 156 // don't register another. 157 return true; 158 } 159 mISupplicant = null; 160 mISupplicantP2pIface = null; 161 try { 162 mIServiceManager = getServiceManagerMockable(); 163 if (mIServiceManager == null) { 164 Log.e(TAG, "Failed to get HIDL Service Manager"); 165 return false; 166 } 167 if (!linkToServiceManagerDeath()) { 168 return false; 169 } 170 /* TODO(b/33639391) : Use the new ISupplicant.registerForNotifications() once it 171 exists */ 172 if (!mIServiceManager.registerForNotifications( 173 ISupplicant.kInterfaceName, "", mServiceNotificationCallback)) { 174 Log.e(TAG, "Failed to register for notifications to " 175 + ISupplicant.kInterfaceName); 176 mIServiceManager = null; // Will need to register a new ServiceNotification 177 return false; 178 } 179 180 // Successful completion by the end of the 'try' block. This will prevent reporting 181 // proper initialization after exception is caught. 182 return true; 183 } catch (RemoteException e) { 184 Log.e(TAG, "Exception while trying to register a listener for ISupplicant service: " 185 + e); 186 supplicantServiceDiedHandler(); 187 } 188 return false; 189 } 190 } 191 linkToSupplicantDeath()192 private boolean linkToSupplicantDeath() { 193 if (mISupplicant == null) return false; 194 try { 195 if (!mISupplicant.linkToDeath(mSupplicantDeathRecipient, 0)) { 196 Log.wtf(TAG, "Error on linkToDeath on ISupplicant"); 197 supplicantServiceDiedHandler(); 198 return false; 199 } 200 } catch (RemoteException e) { 201 Log.e(TAG, "ISupplicant.linkToDeath exception", e); 202 return false; 203 } 204 return true; 205 } 206 initSupplicantService()207 private boolean initSupplicantService() { 208 synchronized (mLock) { 209 try { 210 mISupplicant = getSupplicantMockable(); 211 } catch (RemoteException e) { 212 Log.e(TAG, "ISupplicant.getService exception: " + e); 213 return false; 214 } 215 if (mISupplicant == null) { 216 Log.e(TAG, "Got null ISupplicant service. Stopping supplicant HIDL startup"); 217 return false; 218 } 219 if (!linkToSupplicantDeath()) { 220 return false; 221 } 222 } 223 return true; 224 } 225 linkToSupplicantP2pIfaceDeath()226 private boolean linkToSupplicantP2pIfaceDeath() { 227 if (mISupplicantP2pIface == null) return false; 228 try { 229 if (!mISupplicantP2pIface.linkToDeath(mSupplicantDeathRecipient, 0)) { 230 Log.wtf(TAG, "Error on linkToDeath on ISupplicantP2pIface"); 231 supplicantServiceDiedHandler(); 232 return false; 233 } 234 } catch (RemoteException e) { 235 Log.e(TAG, "ISupplicantP2pIface.linkToDeath exception", e); 236 return false; 237 } 238 return true; 239 } 240 241 /** 242 * Setup the P2p iface. 243 * 244 * @param ifaceName Name of the interface. 245 * @return true on success, false otherwise. 246 */ setupIface(@onNull String ifaceName)247 public boolean setupIface(@NonNull String ifaceName) { 248 synchronized (mLock) { 249 if (mISupplicantP2pIface != null) return false; 250 ISupplicantIface ifaceHwBinder; 251 if (isV1_1()) { 252 ifaceHwBinder = addIfaceV1_1(ifaceName); 253 } else { 254 ifaceHwBinder = getIfaceV1_0(ifaceName); 255 } 256 if (ifaceHwBinder == null) { 257 Log.e(TAG, "initSupplicantP2pIface got null iface"); 258 return false; 259 } 260 mISupplicantP2pIface = getP2pIfaceMockable(ifaceHwBinder); 261 if (!linkToSupplicantP2pIfaceDeath()) { 262 return false; 263 } 264 if (mISupplicantP2pIface != null && mMonitor != null) { 265 mCallback = new SupplicantP2pIfaceCallback(ifaceName, mMonitor); 266 if (!registerCallback(mCallback)) { 267 Log.e(TAG, "Callback registration failed. Initialization incomplete."); 268 return false; 269 } 270 } 271 return true; 272 } 273 } 274 getIfaceV1_0(@onNull String ifaceName)275 private ISupplicantIface getIfaceV1_0(@NonNull String ifaceName) { 276 /** List all supplicant Ifaces */ 277 final ArrayList<ISupplicant.IfaceInfo> supplicantIfaces = new ArrayList(); 278 try { 279 mISupplicant.listInterfaces((SupplicantStatus status, 280 ArrayList<ISupplicant.IfaceInfo> ifaces) -> { 281 if (status.code != SupplicantStatusCode.SUCCESS) { 282 Log.e(TAG, "Getting Supplicant Interfaces failed: " + status.code); 283 return; 284 } 285 supplicantIfaces.addAll(ifaces); 286 }); 287 } catch (RemoteException e) { 288 Log.e(TAG, "ISupplicant.listInterfaces exception: " + e); 289 return null; 290 } 291 if (supplicantIfaces.size() == 0) { 292 Log.e(TAG, "Got zero HIDL supplicant ifaces. Stopping supplicant HIDL startup."); 293 supplicantServiceDiedHandler(); 294 return null; 295 } 296 SupplicantResult<ISupplicantIface> supplicantIface = 297 new SupplicantResult("getInterface()"); 298 for (ISupplicant.IfaceInfo ifaceInfo : supplicantIfaces) { 299 if (ifaceInfo.type == IfaceType.P2P && ifaceName.equals(ifaceInfo.name)) { 300 try { 301 mISupplicant.getInterface(ifaceInfo, 302 (SupplicantStatus status, ISupplicantIface iface) -> { 303 if (status.code != SupplicantStatusCode.SUCCESS) { 304 Log.e(TAG, "Failed to get ISupplicantIface " + status.code); 305 return; 306 } 307 supplicantIface.setResult(status, iface); 308 }); 309 } catch (RemoteException | IllegalArgumentException e) { 310 Log.e(TAG, "ISupplicant.getInterface exception: " + e); 311 supplicantServiceDiedHandler(); 312 return null; 313 } 314 break; 315 } 316 } 317 return supplicantIface.getResult(); 318 } 319 addIfaceV1_1(@onNull String ifaceName)320 private ISupplicantIface addIfaceV1_1(@NonNull String ifaceName) { 321 synchronized (mLock) { 322 ISupplicant.IfaceInfo ifaceInfo = new ISupplicant.IfaceInfo(); 323 ifaceInfo.name = ifaceName; 324 ifaceInfo.type = IfaceType.P2P; 325 SupplicantResult<ISupplicantIface> supplicantIface = 326 new SupplicantResult("addInterface(" + ifaceInfo + ")"); 327 try { 328 android.hardware.wifi.supplicant.V1_1.ISupplicant supplicant_v1_1 = 329 getSupplicantMockableV1_1(); 330 if (supplicant_v1_1 == null) { 331 Log.e(TAG, "Can't call addIface: ISupplicantP2pIface is null"); 332 return null; 333 } 334 supplicant_v1_1.addInterface(ifaceInfo, 335 (SupplicantStatus status, ISupplicantIface iface) -> { 336 if (status.code != SupplicantStatusCode.SUCCESS 337 && status.code != SupplicantStatusCode.FAILURE_IFACE_EXISTS) { 338 Log.e(TAG, "Failed to get ISupplicantIface " + status.code); 339 return; 340 } 341 supplicantIface.setResult(status, iface); 342 }); 343 } catch (RemoteException e) { 344 Log.e(TAG, "ISupplicant.addInterface exception: " + e); 345 supplicantServiceDiedHandler(); 346 return null; 347 } 348 return supplicantIface.getResult(); 349 } 350 } 351 352 /** 353 * Teardown the P2P interface. 354 * 355 * @param ifaceName Name of the interface. 356 * @return true on success, false otherwise. 357 */ teardownIface(@onNull String ifaceName)358 public boolean teardownIface(@NonNull String ifaceName) { 359 synchronized (mLock) { 360 if (mISupplicantP2pIface == null) return false; 361 // Only supported for V1.1 362 if (isV1_1()) { 363 return removeIfaceV1_1(ifaceName); 364 } 365 return true; 366 } 367 } 368 369 /** 370 * Remove the P2p iface. 371 * 372 * @return true on success, false otherwise. 373 */ removeIfaceV1_1(@onNull String ifaceName)374 private boolean removeIfaceV1_1(@NonNull String ifaceName) { 375 synchronized (mLock) { 376 try { 377 android.hardware.wifi.supplicant.V1_1.ISupplicant supplicant_v1_1 = 378 getSupplicantMockableV1_1(); 379 if (supplicant_v1_1 == null) { 380 Log.e(TAG, "Can't call removeIface: ISupplicantP2pIface is null"); 381 return false; 382 } 383 ISupplicant.IfaceInfo ifaceInfo = new ISupplicant.IfaceInfo(); 384 ifaceInfo.name = ifaceName; 385 ifaceInfo.type = IfaceType.P2P; 386 SupplicantStatus status = supplicant_v1_1.removeInterface(ifaceInfo); 387 if (status.code != SupplicantStatusCode.SUCCESS) { 388 Log.e(TAG, "Failed to remove iface " + status.code); 389 return false; 390 } 391 mCallback = null; 392 } catch (RemoteException e) { 393 Log.e(TAG, "ISupplicant.removeInterface exception: " + e); 394 supplicantServiceDiedHandler(); 395 return false; 396 } 397 mISupplicantP2pIface = null; 398 return true; 399 } 400 } 401 supplicantServiceDiedHandler()402 private void supplicantServiceDiedHandler() { 403 synchronized (mLock) { 404 mISupplicant = null; 405 mISupplicantP2pIface = null; 406 } 407 } 408 409 410 /** 411 * Signals whether Initialization completed successfully. 412 */ isInitializationStarted()413 public boolean isInitializationStarted() { 414 synchronized (mLock) { 415 return mIServiceManager != null; 416 } 417 } 418 419 /** 420 * Signals whether Initialization completed successfully. Only necessary for testing, is not 421 * needed to guard calls etc. 422 */ isInitializationComplete()423 public boolean isInitializationComplete() { 424 return mISupplicant != null; 425 } 426 427 /** 428 * Wrapper functions to access static HAL methods, created to be mockable in unit tests 429 */ getServiceManagerMockable()430 protected IServiceManager getServiceManagerMockable() throws RemoteException { 431 return IServiceManager.getService(); 432 } 433 getSupplicantMockable()434 protected ISupplicant getSupplicantMockable() throws RemoteException { 435 try { 436 return ISupplicant.getService(); 437 } catch (NoSuchElementException e) { 438 Log.e(TAG, "Failed to get ISupplicant", e); 439 return null; 440 } 441 } 442 getSupplicantMockableV1_1()443 protected android.hardware.wifi.supplicant.V1_1.ISupplicant getSupplicantMockableV1_1() 444 throws RemoteException { 445 synchronized (mLock) { 446 try { 447 return android.hardware.wifi.supplicant.V1_1.ISupplicant.castFrom( 448 mISupplicant); 449 } catch (NoSuchElementException e) { 450 Log.e(TAG, "Failed to get ISupplicant", e); 451 return null; 452 } 453 } 454 } 455 getP2pIfaceMockable(ISupplicantIface iface)456 protected ISupplicantP2pIface getP2pIfaceMockable(ISupplicantIface iface) { 457 return ISupplicantP2pIface.asInterface(iface.asBinder()); 458 } 459 460 protected android.hardware.wifi.supplicant.V1_2.ISupplicantP2pIface getP2pIfaceMockableV1_2()461 getP2pIfaceMockableV1_2() { 462 if (mISupplicantP2pIface == null) return null; 463 return android.hardware.wifi.supplicant.V1_2.ISupplicantP2pIface.castFrom( 464 mISupplicantP2pIface); 465 } 466 getP2pNetworkMockable(ISupplicantNetwork network)467 protected ISupplicantP2pNetwork getP2pNetworkMockable(ISupplicantNetwork network) { 468 return ISupplicantP2pNetwork.asInterface(network.asBinder()); 469 } 470 471 /** 472 * Check if the device is running V1_1 supplicant service. 473 * @return 474 */ isV1_1()475 private boolean isV1_1() { 476 synchronized (mLock) { 477 try { 478 return (getSupplicantMockableV1_1() != null); 479 } catch (RemoteException e) { 480 Log.e(TAG, "ISupplicant.getService exception: " + e); 481 supplicantServiceDiedHandler(); 482 return false; 483 } 484 } 485 } 486 logd(String s)487 protected static void logd(String s) { 488 if (sVerboseLoggingEnabled) Log.d(TAG, s); 489 } 490 logCompletion(String operation, SupplicantStatus status)491 protected static void logCompletion(String operation, SupplicantStatus status) { 492 if (status == null) { 493 Log.w(TAG, operation + " failed: no status code returned."); 494 } else if (status.code == SupplicantStatusCode.SUCCESS) { 495 logd(operation + " completed successfully."); 496 } else { 497 Log.w(TAG, operation + " failed: " + status.code + " (" + status.debugMessage + ")"); 498 } 499 } 500 501 502 /** 503 * Returns false if SupplicantP2pIface is null, and logs failure to call methodStr 504 */ checkSupplicantP2pIfaceAndLogFailure(String method)505 private boolean checkSupplicantP2pIfaceAndLogFailure(String method) { 506 if (mISupplicantP2pIface == null) { 507 Log.e(TAG, "Can't call " + method + ": ISupplicantP2pIface is null"); 508 return false; 509 } 510 return true; 511 } 512 513 /** 514 * Returns SupplicantP2pIface on success, logs failure to call methodStr 515 * and returns false otherwise 516 */ 517 private android.hardware.wifi.supplicant.V1_2.ISupplicantP2pIface getSupplicantP2pIfaceAndLogFailureV1_2(String method)518 getSupplicantP2pIfaceAndLogFailureV1_2(String method) { 519 synchronized (mLock) { 520 android.hardware.wifi.supplicant.V1_2.ISupplicantP2pIface p2pIfaceV12 = 521 getP2pIfaceMockableV1_2(); 522 if (p2pIfaceV12 == null) { 523 Log.e(TAG, "Can't call " + method + ": ISupplicantP2pIface is null"); 524 return null; 525 } 526 return p2pIfaceV12; 527 } 528 } 529 wpsInfoToConfigMethod(int info)530 private int wpsInfoToConfigMethod(int info) { 531 switch (info) { 532 case WpsInfo.PBC: 533 return ISupplicantP2pIface.WpsProvisionMethod.PBC; 534 535 case WpsInfo.DISPLAY: 536 return ISupplicantP2pIface.WpsProvisionMethod.DISPLAY; 537 538 case WpsInfo.KEYPAD: 539 case WpsInfo.LABEL: 540 return ISupplicantP2pIface.WpsProvisionMethod.KEYPAD; 541 542 default: 543 Log.e(TAG, "Unsupported WPS provision method: " + info); 544 return RESULT_NOT_VALID; 545 } 546 } 547 548 /** 549 * Retrieves the name of the network interface. 550 * 551 * @return name Name of the network interface, e.g., wlan0 552 */ getName()553 public String getName() { 554 synchronized (mLock) { 555 if (!checkSupplicantP2pIfaceAndLogFailure("getName")) return null; 556 SupplicantResult<String> result = new SupplicantResult("getName()"); 557 558 try { 559 mISupplicantP2pIface.getName( 560 (SupplicantStatus status, String name) -> { 561 result.setResult(status, name); 562 }); 563 } catch (RemoteException e) { 564 Log.e(TAG, "ISupplicantP2pIface exception: " + e); 565 supplicantServiceDiedHandler(); 566 } 567 return result.getResult(); 568 } 569 } 570 571 572 /** 573 * Register for callbacks from this interface. 574 * 575 * These callbacks are invoked for events that are specific to this interface. 576 * Registration of multiple callback objects is supported. These objects must 577 * be automatically deleted when the corresponding client process is dead or 578 * if this interface is removed. 579 * 580 * @param receiver An instance of the |ISupplicantP2pIfaceCallback| HIDL 581 * interface object. 582 * @return boolean value indicating whether operation was successful. 583 */ registerCallback(ISupplicantP2pIfaceCallback receiver)584 public boolean registerCallback(ISupplicantP2pIfaceCallback receiver) { 585 synchronized (mLock) { 586 if (!checkSupplicantP2pIfaceAndLogFailure("registerCallback")) return false; 587 SupplicantResult<Void> result = new SupplicantResult("registerCallback()"); 588 try { 589 result.setResult(mISupplicantP2pIface.registerCallback(receiver)); 590 } catch (RemoteException e) { 591 Log.e(TAG, "ISupplicantP2pIface exception: " + e); 592 supplicantServiceDiedHandler(); 593 } 594 return result.isSuccess(); 595 } 596 } 597 598 599 /** 600 * Initiate a P2P service discovery with a (optional) timeout. 601 * 602 * @param timeout Max time to be spent is peforming discovery. 603 * Set to 0 to indefinely continue discovery untill and explicit 604 * |stopFind| is sent. 605 * @return boolean value indicating whether operation was successful. 606 */ find(int timeout)607 public boolean find(int timeout) { 608 synchronized (mLock) { 609 if (!checkSupplicantP2pIfaceAndLogFailure("find")) return false; 610 611 if (timeout < 0) { 612 Log.e(TAG, "Invalid timeout value: " + timeout); 613 return false; 614 } 615 SupplicantResult<Void> result = new SupplicantResult("find(" + timeout + ")"); 616 try { 617 result.setResult(mISupplicantP2pIface.find(timeout)); 618 } catch (RemoteException e) { 619 Log.e(TAG, "ISupplicantP2pIface exception: " + e); 620 supplicantServiceDiedHandler(); 621 } 622 return result.isSuccess(); 623 } 624 } 625 626 627 /** 628 * Stop an ongoing P2P service discovery. 629 * 630 * @return boolean value indicating whether operation was successful. 631 */ stopFind()632 public boolean stopFind() { 633 synchronized (mLock) { 634 if (!checkSupplicantP2pIfaceAndLogFailure("stopFind")) return false; 635 SupplicantResult<Void> result = new SupplicantResult("stopFind()"); 636 try { 637 result.setResult(mISupplicantP2pIface.stopFind()); 638 } catch (RemoteException e) { 639 Log.e(TAG, "ISupplicantP2pIface exception: " + e); 640 supplicantServiceDiedHandler(); 641 } 642 return result.isSuccess(); 643 } 644 } 645 646 647 /** 648 * Flush P2P peer table and state. 649 * 650 * @return boolean value indicating whether operation was successful. 651 */ flush()652 public boolean flush() { 653 synchronized (mLock) { 654 if (!checkSupplicantP2pIfaceAndLogFailure("flush")) return false; 655 SupplicantResult<Void> result = new SupplicantResult("flush()"); 656 try { 657 result.setResult(mISupplicantP2pIface.flush()); 658 } catch (RemoteException e) { 659 Log.e(TAG, "ISupplicantP2pIface exception: " + e); 660 supplicantServiceDiedHandler(); 661 } 662 return result.isSuccess(); 663 } 664 } 665 666 667 /** 668 * This command can be used to flush all services from the 669 * device. 670 * 671 * @return boolean value indicating whether operation was successful. 672 */ serviceFlush()673 public boolean serviceFlush() { 674 synchronized (mLock) { 675 if (!checkSupplicantP2pIfaceAndLogFailure("serviceFlush")) return false; 676 SupplicantResult<Void> result = new SupplicantResult("serviceFlush()"); 677 try { 678 result.setResult(mISupplicantP2pIface.flushServices()); 679 } catch (RemoteException e) { 680 Log.e(TAG, "ISupplicantP2pIface exception: " + e); 681 supplicantServiceDiedHandler(); 682 } 683 return result.isSuccess(); 684 } 685 } 686 687 688 /** 689 * Turn on/off power save mode for the interface. 690 * 691 * @param groupIfName Group interface name to use. 692 * @param enable Indicate if power save is to be turned on/off. 693 * 694 * @return boolean value indicating whether operation was successful. 695 */ setPowerSave(String groupIfName, boolean enable)696 public boolean setPowerSave(String groupIfName, boolean enable) { 697 synchronized (mLock) { 698 if (!checkSupplicantP2pIfaceAndLogFailure("setPowerSave")) return false; 699 SupplicantResult<Void> result = new SupplicantResult( 700 "setPowerSave(" + groupIfName + ", " + enable + ")"); 701 try { 702 result.setResult(mISupplicantP2pIface.setPowerSave(groupIfName, enable)); 703 } catch (RemoteException e) { 704 Log.e(TAG, "ISupplicantP2pIface exception: " + e); 705 supplicantServiceDiedHandler(); 706 } 707 return result.isSuccess(); 708 } 709 } 710 711 712 /** 713 * Set the Maximum idle time in seconds for P2P groups. 714 * This value controls how long a P2P group is maintained after there 715 * is no other members in the group. As a group owner, this means no 716 * associated stations in the group. As a P2P client, this means no 717 * group owner seen in scan results. 718 * 719 * @param groupIfName Group interface name to use. 720 * @param timeoutInSec Timeout value in seconds. 721 * 722 * @return boolean value indicating whether operation was successful. 723 */ setGroupIdle(String groupIfName, int timeoutInSec)724 public boolean setGroupIdle(String groupIfName, int timeoutInSec) { 725 synchronized (mLock) { 726 if (!checkSupplicantP2pIfaceAndLogFailure("setGroupIdle")) return false; 727 // Basic checking here. Leave actual parameter validation to supplicant. 728 if (timeoutInSec < 0) { 729 Log.e(TAG, "Invalid group timeout value " + timeoutInSec); 730 return false; 731 } 732 733 SupplicantResult<Void> result = new SupplicantResult( 734 "setGroupIdle(" + groupIfName + ", " + timeoutInSec + ")"); 735 try { 736 result.setResult(mISupplicantP2pIface.setGroupIdle(groupIfName, timeoutInSec)); 737 } catch (RemoteException e) { 738 Log.e(TAG, "ISupplicantP2pIface exception: " + e); 739 supplicantServiceDiedHandler(); 740 } 741 return result.isSuccess(); 742 } 743 } 744 745 746 /** 747 * Set the postfix to be used for P2P SSID's. 748 * 749 * @param postfix String to be appended to SSID. 750 * 751 * @return boolean value indicating whether operation was successful. 752 */ setSsidPostfix(String postfix)753 public boolean setSsidPostfix(String postfix) { 754 synchronized (mLock) { 755 if (!checkSupplicantP2pIfaceAndLogFailure("setSsidPostfix")) return false; 756 // Basic checking here. Leave actual parameter validation to supplicant. 757 if (postfix == null) { 758 Log.e(TAG, "Invalid SSID postfix value (null)."); 759 return false; 760 } 761 762 SupplicantResult<Void> result = new SupplicantResult("setSsidPostfix(" + postfix + ")"); 763 try { 764 result.setResult(mISupplicantP2pIface.setSsidPostfix( 765 NativeUtil.decodeSsid("\"" + postfix + "\""))); 766 } catch (RemoteException e) { 767 Log.e(TAG, "ISupplicantP2pIface exception: " + e); 768 supplicantServiceDiedHandler(); 769 } catch (IllegalArgumentException e) { 770 Log.e(TAG, "Could not decode SSID.", e); 771 return false; 772 } 773 774 return result.isSuccess(); 775 } 776 } 777 778 779 /** 780 * Start P2P group formation with a discovered P2P peer. This includes 781 * optional group owner negotiation, group interface setup, provisioning, 782 * and establishing data connection. 783 * 784 * @param config Configuration to use to connect to remote device. 785 * @param joinExistingGroup Indicates that this is a command to join an 786 * existing group as a client. It skips the group owner negotiation 787 * part. This must send a Provision Discovery Request message to the 788 * target group owner before associating for WPS provisioning. 789 * 790 * @return String containing generated pin, if selected provision method 791 * uses PIN. 792 */ connect(WifiP2pConfig config, boolean joinExistingGroup)793 public String connect(WifiP2pConfig config, boolean joinExistingGroup) { 794 if (config == null) return null; 795 synchronized (mLock) { 796 if (!checkSupplicantP2pIfaceAndLogFailure("setSsidPostfix")) return null; 797 798 if (config == null) { 799 Log.e(TAG, "Could not connect: null config."); 800 return null; 801 } 802 803 if (config.deviceAddress == null) { 804 Log.e(TAG, "Could not parse null mac address."); 805 return null; 806 } 807 808 if (config.wps.setup == WpsInfo.PBC && !TextUtils.isEmpty(config.wps.pin)) { 809 Log.e(TAG, "Expected empty pin for PBC."); 810 return null; 811 } 812 813 byte[] peerAddress = null; 814 try { 815 peerAddress = NativeUtil.macAddressToByteArray(config.deviceAddress); 816 } catch (Exception e) { 817 Log.e(TAG, "Could not parse peer mac address.", e); 818 return null; 819 } 820 821 int provisionMethod = wpsInfoToConfigMethod(config.wps.setup); 822 if (provisionMethod == RESULT_NOT_VALID) { 823 Log.e(TAG, "Invalid WPS config method: " + config.wps.setup); 824 return null; 825 } 826 // NOTE: preSelectedPin cannot be null, otherwise hal would crash. 827 String preSelectedPin = TextUtils.isEmpty(config.wps.pin) ? "" : config.wps.pin; 828 boolean persistent = (config.netId == WifiP2pGroup.NETWORK_ID_PERSISTENT); 829 830 if (config.groupOwnerIntent < 0 || config.groupOwnerIntent > 15) { 831 Log.e(TAG, "Invalid group owner intent: " + config.groupOwnerIntent); 832 return null; 833 } 834 835 SupplicantResult<String> result = new SupplicantResult( 836 "connect(" + config.deviceAddress + ")"); 837 try { 838 mISupplicantP2pIface.connect( 839 peerAddress, provisionMethod, preSelectedPin, joinExistingGroup, 840 persistent, config.groupOwnerIntent, 841 (SupplicantStatus status, String generatedPin) -> { 842 result.setResult(status, generatedPin); 843 }); 844 } catch (RemoteException e) { 845 Log.e(TAG, "ISupplicantP2pIface exception: " + e); 846 supplicantServiceDiedHandler(); 847 } 848 return result.getResult(); 849 } 850 } 851 852 /** 853 * Cancel an ongoing P2P group formation and joining-a-group related 854 * operation. This operation unauthorizes the specific peer device (if any 855 * had been authorized to start group formation), stops P2P find (if in 856 * progress), stops pending operations for join-a-group, and removes the 857 * P2P group interface (if one was used) that is in the WPS provisioning 858 * step. If the WPS provisioning step has been completed, the group is not 859 * terminated. 860 * 861 * @return boolean value indicating whether operation was successful. 862 */ cancelConnect()863 public boolean cancelConnect() { 864 synchronized (mLock) { 865 if (!checkSupplicantP2pIfaceAndLogFailure("cancelConnect")) return false; 866 SupplicantResult<Void> result = new SupplicantResult("cancelConnect()"); 867 try { 868 result.setResult(mISupplicantP2pIface.cancelConnect()); 869 } catch (RemoteException e) { 870 Log.e(TAG, "ISupplicantP2pIface exception: " + e); 871 supplicantServiceDiedHandler(); 872 } 873 return result.isSuccess(); 874 } 875 } 876 877 878 /** 879 * Send P2P provision discovery request to the specified peer. The 880 * parameters for this command are the P2P device address of the peer and the 881 * desired configuration method. 882 * 883 * @param config Config class describing peer setup. 884 * 885 * @return boolean value indicating whether operation was successful. 886 */ provisionDiscovery(WifiP2pConfig config)887 public boolean provisionDiscovery(WifiP2pConfig config) { 888 if (config == null) return false; 889 synchronized (mLock) { 890 if (!checkSupplicantP2pIfaceAndLogFailure("provisionDiscovery")) return false; 891 892 int targetMethod = wpsInfoToConfigMethod(config.wps.setup); 893 if (targetMethod == RESULT_NOT_VALID) { 894 Log.e(TAG, "Unrecognized WPS configuration method: " + config.wps.setup); 895 return false; 896 } 897 if (targetMethod == ISupplicantP2pIface.WpsProvisionMethod.DISPLAY) { 898 // We are doing display, so provision discovery is keypad. 899 targetMethod = ISupplicantP2pIface.WpsProvisionMethod.KEYPAD; 900 } else if (targetMethod == ISupplicantP2pIface.WpsProvisionMethod.KEYPAD) { 901 // We are doing keypad, so provision discovery is display. 902 targetMethod = ISupplicantP2pIface.WpsProvisionMethod.DISPLAY; 903 } 904 905 if (config.deviceAddress == null) { 906 Log.e(TAG, "Cannot parse null mac address."); 907 return false; 908 } 909 byte[] macAddress = null; 910 try { 911 macAddress = NativeUtil.macAddressToByteArray(config.deviceAddress); 912 } catch (Exception e) { 913 Log.e(TAG, "Could not parse peer mac address.", e); 914 return false; 915 } 916 917 SupplicantResult<Void> result = new SupplicantResult( 918 "provisionDiscovery(" + config.deviceAddress + ", " + config.wps.setup + ")"); 919 try { 920 result.setResult(mISupplicantP2pIface.provisionDiscovery(macAddress, targetMethod)); 921 } catch (RemoteException e) { 922 Log.e(TAG, "ISupplicantP2pIface exception: " + e); 923 supplicantServiceDiedHandler(); 924 } 925 926 return result.isSuccess(); 927 } 928 } 929 930 931 /** 932 * Invite a device to a persistent group. 933 * If the peer device is the group owner of the persistent group, the peer 934 * parameter is not needed. Otherwise it is used to specify which 935 * device to invite. |goDeviceAddress| parameter may be used to override 936 * the group owner device address for Invitation Request should it not be 937 * known for some reason (this should not be needed in most cases). 938 * 939 * @param group Group object to use. 940 * @param peerAddress MAC address of the device to invite. 941 * 942 * @return boolean value indicating whether operation was successful. 943 */ invite(WifiP2pGroup group, String peerAddress)944 public boolean invite(WifiP2pGroup group, String peerAddress) { 945 if (TextUtils.isEmpty(peerAddress)) return false; 946 synchronized (mLock) { 947 if (!checkSupplicantP2pIfaceAndLogFailure("invite")) return false; 948 if (group == null) { 949 Log.e(TAG, "Cannot invite to null group."); 950 return false; 951 } 952 953 if (group.getOwner() == null) { 954 Log.e(TAG, "Cannot invite to group with null owner."); 955 return false; 956 } 957 958 if (group.getOwner().deviceAddress == null) { 959 Log.e(TAG, "Group owner has no mac address."); 960 return false; 961 } 962 963 byte[] ownerMacAddress = null; 964 try { 965 ownerMacAddress = NativeUtil.macAddressToByteArray(group.getOwner().deviceAddress); 966 } catch (Exception e) { 967 Log.e(TAG, "Group owner mac address parse error.", e); 968 return false; 969 } 970 971 if (peerAddress == null) { 972 Log.e(TAG, "Cannot parse peer mac address."); 973 return false; 974 } 975 976 byte[] peerMacAddress; 977 try { 978 peerMacAddress = NativeUtil.macAddressToByteArray(peerAddress); 979 } catch (Exception e) { 980 Log.e(TAG, "Peer mac address parse error.", e); 981 return false; 982 } 983 984 SupplicantResult<Void> result = new SupplicantResult( 985 "invite(" + group.getInterface() + ", " + group.getOwner().deviceAddress 986 + ", " + peerAddress + ")"); 987 try { 988 result.setResult(mISupplicantP2pIface.invite( 989 group.getInterface(), ownerMacAddress, peerMacAddress)); 990 } catch (RemoteException e) { 991 Log.e(TAG, "ISupplicantP2pIface exception: " + e); 992 supplicantServiceDiedHandler(); 993 } 994 return result.isSuccess(); 995 } 996 } 997 998 999 /** 1000 * Reject connection attempt from a peer (specified with a device 1001 * address). This is a mechanism to reject a pending group owner negotiation 1002 * with a peer and request to automatically block any further connection or 1003 * discovery of the peer. 1004 * 1005 * @param peerAddress MAC address of the device to reject. 1006 * 1007 * @return boolean value indicating whether operation was successful. 1008 */ reject(String peerAddress)1009 public boolean reject(String peerAddress) { 1010 synchronized (mLock) { 1011 if (!checkSupplicantP2pIfaceAndLogFailure("reject")) return false; 1012 1013 if (peerAddress == null) { 1014 Log.e(TAG, "Cannot parse rejected peer's mac address."); 1015 return false; 1016 } 1017 byte[] macAddress = null; 1018 try { 1019 macAddress = NativeUtil.macAddressToByteArray(peerAddress); 1020 } catch (Exception e) { 1021 Log.e(TAG, "Could not parse peer mac address.", e); 1022 return false; 1023 } 1024 1025 SupplicantResult<Void> result = 1026 new SupplicantResult("reject(" + peerAddress + ")"); 1027 try { 1028 result.setResult(mISupplicantP2pIface.reject(macAddress)); 1029 } catch (RemoteException e) { 1030 Log.e(TAG, "ISupplicantP2pIface exception: " + e); 1031 supplicantServiceDiedHandler(); 1032 } 1033 1034 return result.isSuccess(); 1035 } 1036 } 1037 1038 1039 /** 1040 * Gets the MAC address of the device. 1041 * 1042 * @return MAC address of the device. 1043 */ getDeviceAddress()1044 public String getDeviceAddress() { 1045 synchronized (mLock) { 1046 if (!checkSupplicantP2pIfaceAndLogFailure("getDeviceAddress")) return null; 1047 SupplicantResult<String> result = new SupplicantResult("getDeviceAddress()"); 1048 try { 1049 mISupplicantP2pIface.getDeviceAddress((SupplicantStatus status, byte[] address) -> { 1050 String parsedAddress = null; 1051 try { 1052 parsedAddress = NativeUtil.macAddressFromByteArray(address); 1053 } catch (Exception e) { 1054 Log.e(TAG, "Could not process reported address.", e); 1055 } 1056 result.setResult(status, parsedAddress); 1057 }); 1058 } catch (RemoteException e) { 1059 Log.e(TAG, "ISupplicantP2pIface exception: " + e); 1060 supplicantServiceDiedHandler(); 1061 return null; 1062 } 1063 1064 return result.getResult(); 1065 } 1066 } 1067 1068 1069 /** 1070 * Gets the operational SSID of the device. 1071 * 1072 * @param address MAC address of the peer. 1073 * 1074 * @return SSID of the device. 1075 */ getSsid(String address)1076 public String getSsid(String address) { 1077 synchronized (mLock) { 1078 if (!checkSupplicantP2pIfaceAndLogFailure("getSsid")) return null; 1079 1080 if (address == null) { 1081 Log.e(TAG, "Cannot parse peer mac address."); 1082 return null; 1083 } 1084 byte[] macAddress = null; 1085 try { 1086 macAddress = NativeUtil.macAddressToByteArray(address); 1087 } catch (Exception e) { 1088 Log.e(TAG, "Could not parse mac address.", e); 1089 return null; 1090 } 1091 1092 SupplicantResult<String> result = 1093 new SupplicantResult("getSsid(" + address + ")"); 1094 try { 1095 mISupplicantP2pIface.getSsid( 1096 macAddress, (SupplicantStatus status, ArrayList<Byte> ssid) -> { 1097 String ssidString = null; 1098 if (ssid != null) { 1099 try { 1100 ssidString = NativeUtil.removeEnclosingQuotes( 1101 NativeUtil.encodeSsid(ssid)); 1102 } catch (Exception e) { 1103 Log.e(TAG, "Could not encode SSID.", e); 1104 } 1105 } 1106 result.setResult(status, ssidString); 1107 }); 1108 } catch (RemoteException e) { 1109 Log.e(TAG, "ISupplicantP2pIface exception: " + e); 1110 supplicantServiceDiedHandler(); 1111 return null; 1112 } 1113 1114 return result.getResult(); 1115 } 1116 } 1117 1118 1119 /** 1120 * Reinvoke a device from a persistent group. 1121 * 1122 * @param networkId Used to specify the persistent group. 1123 * @param peerAddress MAC address of the device to reinvoke. 1124 * 1125 * @return true, if operation was successful. 1126 */ reinvoke(int networkId, String peerAddress)1127 public boolean reinvoke(int networkId, String peerAddress) { 1128 if (TextUtils.isEmpty(peerAddress) || networkId < 0) return false; 1129 synchronized (mLock) { 1130 if (!checkSupplicantP2pIfaceAndLogFailure("reinvoke")) return false; 1131 if (peerAddress == null) { 1132 Log.e(TAG, "Cannot parse peer mac address."); 1133 return false; 1134 } 1135 byte[] macAddress = null; 1136 try { 1137 macAddress = NativeUtil.macAddressToByteArray(peerAddress); 1138 } catch (Exception e) { 1139 Log.e(TAG, "Could not parse mac address.", e); 1140 return false; 1141 } 1142 1143 SupplicantResult<Void> result = new SupplicantResult( 1144 "reinvoke(" + networkId + ", " + peerAddress + ")"); 1145 try { 1146 result.setResult(mISupplicantP2pIface.reinvoke(networkId, macAddress)); 1147 } catch (RemoteException e) { 1148 Log.e(TAG, "ISupplicantP2pIface exception: " + e); 1149 supplicantServiceDiedHandler(); 1150 } 1151 1152 return result.isSuccess(); 1153 } 1154 } 1155 1156 1157 /** 1158 * Set up a P2P group owner manually (i.e., without group owner 1159 * negotiation with a specific peer). This is also known as autonomous 1160 * group owner. 1161 * 1162 * @param networkId Used to specify the restart of a persistent group. 1163 * @param isPersistent Used to request a persistent group to be formed. 1164 * 1165 * @return true, if operation was successful. 1166 */ groupAdd(int networkId, boolean isPersistent)1167 public boolean groupAdd(int networkId, boolean isPersistent) { 1168 synchronized (mLock) { 1169 if (!checkSupplicantP2pIfaceAndLogFailure("groupAdd")) return false; 1170 SupplicantResult<Void> result = 1171 new SupplicantResult("groupAdd(" + networkId + ", " + isPersistent + ")"); 1172 try { 1173 result.setResult(mISupplicantP2pIface.addGroup(isPersistent, networkId)); 1174 } catch (RemoteException e) { 1175 Log.e(TAG, "ISupplicantP2pIface exception: " + e); 1176 supplicantServiceDiedHandler(); 1177 } 1178 return result.isSuccess(); 1179 } 1180 } 1181 1182 /** 1183 * Set up a P2P group as Group Owner or join a group with a configuration. 1184 * 1185 * @param networkName SSID of the group to be formed 1186 * @param passphrase passphrase of the group to be formed 1187 * @param isPersistent Used to request a persistent group to be formed. 1188 * @param freq prefered frequencty or band of the group to be formed 1189 * @param peerAddress peerAddress Group Owner MAC address, only applied for Group Client. 1190 * If the MAC is "00:00:00:00:00:00", the device will try to find a peer 1191 * whose SSID matches ssid. 1192 * @param join join a group or create a group 1193 * 1194 * @return true, if operation was successful. 1195 */ groupAdd(String networkName, String passphrase, boolean isPersistent, int freq, String peerAddress, boolean join)1196 public boolean groupAdd(String networkName, String passphrase, 1197 boolean isPersistent, int freq, String peerAddress, boolean join) { 1198 synchronized (mLock) { 1199 android.hardware.wifi.supplicant.V1_2.ISupplicantP2pIface ifaceV12 = 1200 getSupplicantP2pIfaceAndLogFailureV1_2("groupAdd_1_2"); 1201 if (ifaceV12 == null) return false; 1202 1203 java.util.ArrayList<Byte> ssid = NativeUtil.decodeSsid("\"" + networkName + "\""); 1204 byte[] macAddress = null; 1205 try { 1206 macAddress = NativeUtil.macAddressToByteArray(peerAddress); 1207 } catch (Exception e) { 1208 Log.e(TAG, "Could not parse mac address.", e); 1209 return false; 1210 } 1211 1212 SupplicantResult<Void> result = 1213 new SupplicantResult("groupAdd(" + networkName + ", " 1214 + (TextUtils.isEmpty(passphrase) ? "<Empty>" : "<Non-Empty>") 1215 + ", " + isPersistent + ", " + freq 1216 + ", " + peerAddress + ", " + join + ")"); 1217 try { 1218 result.setResult(ifaceV12.addGroup_1_2( 1219 ssid, passphrase, isPersistent, freq, macAddress, join)); 1220 } catch (RemoteException e) { 1221 Log.e(TAG, "ISupplicantP2pIface exception: " + e); 1222 supplicantServiceDiedHandler(); 1223 } 1224 return result.isSuccess(); 1225 } 1226 } 1227 1228 /** 1229 * Set up a P2P group owner manually. 1230 * This is a helper method that invokes groupAdd(networkId, isPersistent) internally. 1231 * 1232 * @param isPersistent Used to request a persistent group to be formed. 1233 * 1234 * @return true, if operation was successful. 1235 */ groupAdd(boolean isPersistent)1236 public boolean groupAdd(boolean isPersistent) { 1237 // Supplicant expects networkId to be -1 if not supplied. 1238 return groupAdd(-1, isPersistent); 1239 } 1240 1241 1242 /** 1243 * Terminate a P2P group. If a new virtual network interface was used for 1244 * the group, it must also be removed. The network interface name of the 1245 * group interface is used as a parameter for this command. 1246 * 1247 * @param groupName Group interface name to use. 1248 * 1249 * @return true, if operation was successful. 1250 */ groupRemove(String groupName)1251 public boolean groupRemove(String groupName) { 1252 if (TextUtils.isEmpty(groupName)) return false; 1253 synchronized (mLock) { 1254 if (!checkSupplicantP2pIfaceAndLogFailure("groupRemove")) return false; 1255 SupplicantResult<Void> result = new SupplicantResult("groupRemove(" + groupName + ")"); 1256 try { 1257 result.setResult(mISupplicantP2pIface.removeGroup(groupName)); 1258 } catch (RemoteException e) { 1259 Log.e(TAG, "ISupplicantP2pIface exception: " + e); 1260 supplicantServiceDiedHandler(); 1261 } 1262 return result.isSuccess(); 1263 } 1264 } 1265 1266 1267 /** 1268 * Gets the capability of the group which the device is a 1269 * member of. 1270 * 1271 * @param peerAddress MAC address of the peer. 1272 * 1273 * @return combination of |GroupCapabilityMask| values. 1274 */ getGroupCapability(String peerAddress)1275 public int getGroupCapability(String peerAddress) { 1276 synchronized (mLock) { 1277 if (!checkSupplicantP2pIfaceAndLogFailure("getGroupCapability")) { 1278 return RESULT_NOT_VALID; 1279 } 1280 1281 if (peerAddress == null) { 1282 Log.e(TAG, "Cannot parse peer mac address."); 1283 return RESULT_NOT_VALID; 1284 } 1285 byte[] macAddress = null; 1286 try { 1287 macAddress = NativeUtil.macAddressToByteArray(peerAddress); 1288 } catch (Exception e) { 1289 Log.e(TAG, "Could not parse group address.", e); 1290 return RESULT_NOT_VALID; 1291 } 1292 1293 SupplicantResult<Integer> capability = new SupplicantResult( 1294 "getGroupCapability(" + peerAddress + ")"); 1295 try { 1296 mISupplicantP2pIface.getGroupCapability( 1297 macAddress, (SupplicantStatus status, int cap) -> { 1298 capability.setResult(status, cap); 1299 }); 1300 } catch (RemoteException e) { 1301 Log.e(TAG, "ISupplicantP2pIface exception: " + e); 1302 supplicantServiceDiedHandler(); 1303 } 1304 1305 if (!capability.isSuccess()) { 1306 return RESULT_NOT_VALID; 1307 } 1308 1309 return capability.getResult(); 1310 } 1311 } 1312 1313 1314 /** 1315 * Configure Extended Listen Timing. 1316 * 1317 * If enabled, listen state must be entered every |intervalInMillis| for at 1318 * least |periodInMillis|. Both values have acceptable range of 1-65535 1319 * (with interval obviously having to be larger than or equal to duration). 1320 * If the P2P module is not idle at the time the Extended Listen Timing 1321 * timeout occurs, the Listen State operation must be skipped. 1322 * 1323 * @param enable Enables or disables listening. 1324 * @param periodInMillis Period in milliseconds. 1325 * @param intervalInMillis Interval in milliseconds. 1326 * 1327 * @return true, if operation was successful. 1328 */ configureExtListen(boolean enable, int periodInMillis, int intervalInMillis)1329 public boolean configureExtListen(boolean enable, int periodInMillis, int intervalInMillis) { 1330 if (enable && intervalInMillis < periodInMillis) { 1331 return false; 1332 } 1333 synchronized (mLock) { 1334 if (!checkSupplicantP2pIfaceAndLogFailure("configureExtListen")) return false; 1335 1336 // If listening is disabled, wpa supplicant expects zeroes. 1337 if (!enable) { 1338 periodInMillis = 0; 1339 intervalInMillis = 0; 1340 } 1341 1342 // Verify that the integers are not negative. Leave actual parameter validation to 1343 // supplicant. 1344 if (periodInMillis < 0 || intervalInMillis < 0) { 1345 Log.e(TAG, "Invalid parameters supplied to configureExtListen: " + periodInMillis 1346 + ", " + intervalInMillis); 1347 return false; 1348 } 1349 1350 SupplicantResult<Void> result = new SupplicantResult( 1351 "configureExtListen(" + periodInMillis + ", " + intervalInMillis + ")"); 1352 try { 1353 result.setResult( 1354 mISupplicantP2pIface.configureExtListen(periodInMillis, intervalInMillis)); 1355 } catch (RemoteException e) { 1356 Log.e(TAG, "ISupplicantP2pIface exception: " + e); 1357 supplicantServiceDiedHandler(); 1358 } 1359 1360 return result.isSuccess(); 1361 } 1362 } 1363 1364 1365 /** 1366 * Set P2P Listen channel and operating chanel. 1367 * 1368 * @param listenChannel Wifi channel. eg, 1, 6, 11. 1369 * @param operatingChannel Wifi channel. eg, 1, 6, 11. 1370 * 1371 * @return true, if operation was successful. 1372 */ setListenChannel(int listenChannel, int operatingChannel)1373 public boolean setListenChannel(int listenChannel, int operatingChannel) { 1374 synchronized (mLock) { 1375 if (!checkSupplicantP2pIfaceAndLogFailure("setListenChannel")) return false; 1376 1377 if (listenChannel >= 1 && listenChannel <= 11) { 1378 SupplicantResult<Void> result = new SupplicantResult( 1379 "setListenChannel(" + listenChannel + ", " + DEFAULT_OPERATING_CLASS + ")"); 1380 try { 1381 result.setResult(mISupplicantP2pIface.setListenChannel( 1382 listenChannel, DEFAULT_OPERATING_CLASS)); 1383 } catch (RemoteException e) { 1384 Log.e(TAG, "ISupplicantP2pIface exception: " + e); 1385 supplicantServiceDiedHandler(); 1386 } 1387 if (!result.isSuccess()) { 1388 return false; 1389 } 1390 } else if (listenChannel != 0) { 1391 // listenChannel == 0 does not set any listen channel. 1392 return false; 1393 } 1394 1395 if (operatingChannel >= 0 && operatingChannel <= 165) { 1396 ArrayList<ISupplicantP2pIface.FreqRange> ranges = new ArrayList<>(); 1397 // operatingChannel == 0 enables all freqs. 1398 if (operatingChannel >= 1 && operatingChannel <= 165) { 1399 int freq = (operatingChannel <= 14 ? 2407 : 5000) + operatingChannel * 5; 1400 ISupplicantP2pIface.FreqRange range1 = new ISupplicantP2pIface.FreqRange(); 1401 range1.min = 1000; 1402 range1.max = freq - 5; 1403 ISupplicantP2pIface.FreqRange range2 = new ISupplicantP2pIface.FreqRange(); 1404 range2.min = freq + 5; 1405 range2.max = 6000; 1406 ranges.add(range1); 1407 ranges.add(range2); 1408 } 1409 SupplicantResult<Void> result = new SupplicantResult( 1410 "setDisallowedFrequencies(" + ranges + ")"); 1411 try { 1412 result.setResult(mISupplicantP2pIface.setDisallowedFrequencies(ranges)); 1413 } catch (RemoteException e) { 1414 Log.e(TAG, "ISupplicantP2pIface exception: " + e); 1415 supplicantServiceDiedHandler(); 1416 } 1417 return result.isSuccess(); 1418 } 1419 return false; 1420 } 1421 } 1422 1423 1424 /** 1425 * This command can be used to add a upnp/bonjour service. 1426 * 1427 * @param servInfo List of service queries. 1428 * 1429 * @return true, if operation was successful. 1430 */ serviceAdd(WifiP2pServiceInfo servInfo)1431 public boolean serviceAdd(WifiP2pServiceInfo servInfo) { 1432 synchronized (mLock) { 1433 if (!checkSupplicantP2pIfaceAndLogFailure("serviceAdd")) return false; 1434 1435 if (servInfo == null) { 1436 Log.e(TAG, "Null service info passed."); 1437 return false; 1438 } 1439 1440 for (String s : servInfo.getSupplicantQueryList()) { 1441 if (s == null) { 1442 Log.e(TAG, "Invalid service description (null)."); 1443 return false; 1444 } 1445 1446 String[] data = s.split(" "); 1447 if (data.length < 3) { 1448 Log.e(TAG, "Service specification invalid: " + s); 1449 return false; 1450 } 1451 1452 SupplicantResult<Void> result = null; 1453 try { 1454 if ("upnp".equals(data[0])) { 1455 int version = 0; 1456 try { 1457 version = Integer.parseInt(data[1], 16); 1458 } catch (NumberFormatException e) { 1459 Log.e(TAG, "UPnP Service specification invalid: " + s, e); 1460 return false; 1461 } 1462 1463 result = new SupplicantResult( 1464 "addUpnpService(" + data[1] + ", " + data[2] + ")"); 1465 result.setResult(mISupplicantP2pIface.addUpnpService(version, data[2])); 1466 } else if ("bonjour".equals(data[0])) { 1467 if (data[1] != null && data[2] != null) { 1468 ArrayList<Byte> request = null; 1469 ArrayList<Byte> response = null; 1470 try { 1471 request = NativeUtil.byteArrayToArrayList( 1472 NativeUtil.hexStringToByteArray(data[1])); 1473 response = NativeUtil.byteArrayToArrayList( 1474 NativeUtil.hexStringToByteArray(data[2])); 1475 } catch (Exception e) { 1476 Log.e(TAG, "Invalid bonjour service description."); 1477 return false; 1478 } 1479 result = new SupplicantResult( 1480 "addBonjourService(" + data[1] + ", " + data[2] + ")"); 1481 result.setResult( 1482 mISupplicantP2pIface.addBonjourService(request, response)); 1483 } 1484 } else { 1485 return false; 1486 } 1487 } catch (RemoteException e) { 1488 Log.e(TAG, "ISupplicantP2pIface exception: " + e); 1489 supplicantServiceDiedHandler(); 1490 } 1491 1492 if (result == null || !result.isSuccess()) return false; 1493 } 1494 1495 return true; 1496 } 1497 } 1498 1499 1500 /** 1501 * This command can be used to remove a upnp/bonjour service. 1502 * 1503 * @param servInfo List of service queries. 1504 * 1505 * @return true, if operation was successful. 1506 */ serviceRemove(WifiP2pServiceInfo servInfo)1507 public boolean serviceRemove(WifiP2pServiceInfo servInfo) { 1508 synchronized (mLock) { 1509 if (!checkSupplicantP2pIfaceAndLogFailure("serviceRemove")) return false; 1510 1511 if (servInfo == null) { 1512 Log.e(TAG, "Null service info passed."); 1513 return false; 1514 } 1515 1516 for (String s : servInfo.getSupplicantQueryList()) { 1517 if (s == null) { 1518 Log.e(TAG, "Invalid service description (null)."); 1519 return false; 1520 } 1521 1522 String[] data = s.split(" "); 1523 if (data.length < 3) { 1524 Log.e(TAG, "Service specification invalid: " + s); 1525 return false; 1526 } 1527 1528 SupplicantResult<Void> result = null; 1529 try { 1530 if ("upnp".equals(data[0])) { 1531 int version = 0; 1532 try { 1533 version = Integer.parseInt(data[1], 16); 1534 } catch (NumberFormatException e) { 1535 Log.e(TAG, "UPnP Service specification invalid: " + s, e); 1536 return false; 1537 } 1538 result = new SupplicantResult( 1539 "removeUpnpService(" + data[1] + ", " + data[2] + ")"); 1540 result.setResult(mISupplicantP2pIface.removeUpnpService(version, data[2])); 1541 } else if ("bonjour".equals(data[0])) { 1542 if (data[1] != null) { 1543 ArrayList<Byte> request = null; 1544 try { 1545 request = NativeUtil.byteArrayToArrayList( 1546 NativeUtil.hexStringToByteArray(data[1])); 1547 } catch (Exception e) { 1548 Log.e(TAG, "Invalid bonjour service description."); 1549 return false; 1550 } 1551 result = new SupplicantResult("removeBonjourService(" + data[1] + ")"); 1552 result.setResult(mISupplicantP2pIface.removeBonjourService(request)); 1553 } 1554 } else { 1555 Log.e(TAG, "Unknown / unsupported P2P service requested: " + data[0]); 1556 return false; 1557 } 1558 } catch (RemoteException e) { 1559 Log.e(TAG, "ISupplicantP2pIface exception: " + e); 1560 supplicantServiceDiedHandler(); 1561 } 1562 1563 if (result == null || !result.isSuccess()) return false; 1564 } 1565 1566 return true; 1567 } 1568 } 1569 1570 1571 /** 1572 * Schedule a P2P service discovery request. The parameters for this command 1573 * are the device address of the peer device (or 00:00:00:00:00:00 for 1574 * wildcard query that is sent to every discovered P2P peer that supports 1575 * service discovery) and P2P Service Query TLV(s) as hexdump. 1576 * 1577 * @param peerAddress MAC address of the device to discover. 1578 * @param query Hex dump of the query data. 1579 * @return identifier Identifier for the request. Can be used to cancel the 1580 * request. 1581 */ requestServiceDiscovery(String peerAddress, String query)1582 public String requestServiceDiscovery(String peerAddress, String query) { 1583 synchronized (mLock) { 1584 if (!checkSupplicantP2pIfaceAndLogFailure("requestServiceDiscovery")) return null; 1585 1586 if (peerAddress == null) { 1587 Log.e(TAG, "Cannot parse peer mac address."); 1588 return null; 1589 } 1590 byte[] macAddress = null; 1591 try { 1592 macAddress = NativeUtil.macAddressToByteArray(peerAddress); 1593 } catch (Exception e) { 1594 Log.e(TAG, "Could not process peer MAC address.", e); 1595 return null; 1596 } 1597 1598 if (query == null) { 1599 Log.e(TAG, "Cannot parse service discovery query: " + query); 1600 return null; 1601 } 1602 ArrayList<Byte> binQuery = null; 1603 try { 1604 binQuery = NativeUtil.byteArrayToArrayList(NativeUtil.hexStringToByteArray(query)); 1605 } catch (Exception e) { 1606 Log.e(TAG, "Could not parse service query.", e); 1607 return null; 1608 } 1609 1610 SupplicantResult<Long> result = new SupplicantResult( 1611 "requestServiceDiscovery(" + peerAddress + ", " + query + ")"); 1612 try { 1613 mISupplicantP2pIface.requestServiceDiscovery( 1614 macAddress, binQuery, 1615 (SupplicantStatus status, long identifier) -> { 1616 result.setResult(status, new Long(identifier)); 1617 }); 1618 } catch (RemoteException e) { 1619 Log.e(TAG, "ISupplicantP2pIface exception: " + e); 1620 supplicantServiceDiedHandler(); 1621 } 1622 1623 Long value = result.getResult(); 1624 if (value == null) return null; 1625 return value.toString(); 1626 } 1627 } 1628 1629 1630 /** 1631 * Cancel a previous service discovery request. 1632 * 1633 * @param identifier Identifier for the request to cancel. 1634 * @return true, if operation was successful. 1635 */ cancelServiceDiscovery(String identifier)1636 public boolean cancelServiceDiscovery(String identifier) { 1637 synchronized (mLock) { 1638 if (!checkSupplicantP2pIfaceAndLogFailure("cancelServiceDiscovery")) return false; 1639 if (identifier == null) { 1640 Log.e(TAG, "cancelServiceDiscovery requires a valid tag."); 1641 return false; 1642 } 1643 1644 long id = 0; 1645 try { 1646 id = Long.parseLong(identifier); 1647 } catch (NumberFormatException e) { 1648 Log.e(TAG, "Service discovery identifier invalid: " + identifier, e); 1649 return false; 1650 } 1651 1652 SupplicantResult<Void> result = new SupplicantResult( 1653 "cancelServiceDiscovery(" + identifier + ")"); 1654 try { 1655 result.setResult(mISupplicantP2pIface.cancelServiceDiscovery(id)); 1656 } catch (RemoteException e) { 1657 Log.e(TAG, "ISupplicantP2pIface exception: " + e); 1658 supplicantServiceDiedHandler(); 1659 } 1660 1661 return result.isSuccess(); 1662 } 1663 } 1664 1665 1666 /** 1667 * Send driver command to set Miracast mode. 1668 * 1669 * @param mode Mode of Miracast. 1670 * @return true, if operation was successful. 1671 */ setMiracastMode(int mode)1672 public boolean setMiracastMode(int mode) { 1673 synchronized (mLock) { 1674 if (!checkSupplicantP2pIfaceAndLogFailure("setMiracastMode")) return false; 1675 byte targetMode = ISupplicantP2pIface.MiracastMode.DISABLED; 1676 1677 switch (mode) { 1678 case WifiP2pManager.MIRACAST_SOURCE: 1679 targetMode = ISupplicantP2pIface.MiracastMode.SOURCE; 1680 break; 1681 1682 case WifiP2pManager.MIRACAST_SINK: 1683 targetMode = ISupplicantP2pIface.MiracastMode.SINK; 1684 break; 1685 } 1686 1687 SupplicantResult<Void> result = new SupplicantResult( 1688 "setMiracastMode(" + mode + ")"); 1689 try { 1690 result.setResult(mISupplicantP2pIface.setMiracastMode(targetMode)); 1691 } catch (RemoteException e) { 1692 Log.e(TAG, "ISupplicantP2pIface exception: " + e); 1693 supplicantServiceDiedHandler(); 1694 } 1695 1696 return result.isSuccess(); 1697 } 1698 } 1699 1700 1701 /** 1702 * Initiate WPS Push Button setup. 1703 * The PBC operation requires that a button is also pressed at the 1704 * AP/Registrar at about the same time (2 minute window). 1705 * 1706 * @param groupIfName Group interface name to use. 1707 * @param bssid BSSID of the AP. Use empty bssid to indicate wildcard. 1708 * @return true, if operation was successful. 1709 */ startWpsPbc(String groupIfName, String bssid)1710 public boolean startWpsPbc(String groupIfName, String bssid) { 1711 if (TextUtils.isEmpty(groupIfName)) { 1712 Log.e(TAG, "Group name required when requesting WPS PBC. Got (" + groupIfName + ")"); 1713 return false; 1714 } 1715 synchronized (mLock) { 1716 if (!checkSupplicantP2pIfaceAndLogFailure("startWpsPbc")) return false; 1717 // Null values should be fine, since bssid can be empty. 1718 byte[] macAddress = null; 1719 try { 1720 macAddress = NativeUtil.macAddressToByteArray(bssid); 1721 } catch (Exception e) { 1722 Log.e(TAG, "Could not parse BSSID.", e); 1723 return false; 1724 } 1725 1726 SupplicantResult<Void> result = new SupplicantResult( 1727 "startWpsPbc(" + groupIfName + ", " + bssid + ")"); 1728 try { 1729 result.setResult(mISupplicantP2pIface.startWpsPbc(groupIfName, macAddress)); 1730 } catch (RemoteException e) { 1731 Log.e(TAG, "ISupplicantP2pIface exception: " + e); 1732 supplicantServiceDiedHandler(); 1733 } 1734 1735 return result.isSuccess(); 1736 } 1737 } 1738 1739 1740 /** 1741 * Initiate WPS Pin Keypad setup. 1742 * 1743 * @param groupIfName Group interface name to use. 1744 * @param pin 8 digit pin to be used. 1745 * @return true, if operation was successful. 1746 */ startWpsPinKeypad(String groupIfName, String pin)1747 public boolean startWpsPinKeypad(String groupIfName, String pin) { 1748 if (TextUtils.isEmpty(groupIfName) || TextUtils.isEmpty(pin)) return false; 1749 synchronized (mLock) { 1750 if (!checkSupplicantP2pIfaceAndLogFailure("startWpsPinKeypad")) return false; 1751 if (groupIfName == null) { 1752 Log.e(TAG, "Group name required when requesting WPS KEYPAD."); 1753 return false; 1754 } 1755 if (pin == null) { 1756 Log.e(TAG, "PIN required when requesting WPS KEYPAD."); 1757 return false; 1758 } 1759 1760 SupplicantResult<Void> result = new SupplicantResult( 1761 "startWpsPinKeypad(" + groupIfName + ", " + pin + ")"); 1762 try { 1763 result.setResult(mISupplicantP2pIface.startWpsPinKeypad(groupIfName, pin)); 1764 } catch (RemoteException e) { 1765 Log.e(TAG, "ISupplicantP2pIface exception: " + e); 1766 supplicantServiceDiedHandler(); 1767 } 1768 1769 return result.isSuccess(); 1770 } 1771 } 1772 1773 1774 /** 1775 * Initiate WPS Pin Display setup. 1776 * 1777 * @param groupIfName Group interface name to use. 1778 * @param bssid BSSID of the AP. Use empty bssid to indicate wildcard. 1779 * @return generated pin if operation was successful, null otherwise. 1780 */ startWpsPinDisplay(String groupIfName, String bssid)1781 public String startWpsPinDisplay(String groupIfName, String bssid) { 1782 if (TextUtils.isEmpty(groupIfName)) return null; 1783 synchronized (mLock) { 1784 if (!checkSupplicantP2pIfaceAndLogFailure("startWpsPinDisplay")) return null; 1785 if (groupIfName == null) { 1786 Log.e(TAG, "Group name required when requesting WPS KEYPAD."); 1787 return null; 1788 } 1789 1790 // Null values should be fine, since bssid can be empty. 1791 byte[] macAddress = null; 1792 try { 1793 macAddress = NativeUtil.macAddressToByteArray(bssid); 1794 } catch (Exception e) { 1795 Log.e(TAG, "Could not parse BSSID.", e); 1796 return null; 1797 } 1798 1799 SupplicantResult<String> result = new SupplicantResult( 1800 "startWpsPinDisplay(" + groupIfName + ", " + bssid + ")"); 1801 try { 1802 mISupplicantP2pIface.startWpsPinDisplay( 1803 groupIfName, macAddress, 1804 (SupplicantStatus status, String generatedPin) -> { 1805 result.setResult(status, generatedPin); 1806 }); 1807 } catch (RemoteException e) { 1808 Log.e(TAG, "ISupplicantP2pIface exception: " + e); 1809 supplicantServiceDiedHandler(); 1810 } 1811 1812 return result.getResult(); 1813 } 1814 } 1815 1816 1817 /** 1818 * Cancel any ongoing WPS operations. 1819 * 1820 * @param groupIfName Group interface name to use. 1821 * @return true, if operation was successful. 1822 */ cancelWps(String groupIfName)1823 public boolean cancelWps(String groupIfName) { 1824 synchronized (mLock) { 1825 if (!checkSupplicantP2pIfaceAndLogFailure("cancelWps")) return false; 1826 if (groupIfName == null) { 1827 Log.e(TAG, "Group name required when requesting WPS KEYPAD."); 1828 return false; 1829 } 1830 1831 SupplicantResult<Void> result = new SupplicantResult( 1832 "cancelWps(" + groupIfName + ")"); 1833 try { 1834 result.setResult(mISupplicantP2pIface.cancelWps(groupIfName)); 1835 } catch (RemoteException e) { 1836 Log.e(TAG, "ISupplicantP2pIface exception: " + e); 1837 supplicantServiceDiedHandler(); 1838 } 1839 1840 return result.isSuccess(); 1841 } 1842 } 1843 1844 1845 /** 1846 * Enable/Disable Wifi Display. 1847 * 1848 * @param enable true to enable, false to disable. 1849 * @return true, if operation was successful. 1850 */ enableWfd(boolean enable)1851 public boolean enableWfd(boolean enable) { 1852 synchronized (mLock) { 1853 if (!checkSupplicantP2pIfaceAndLogFailure("enableWfd")) return false; 1854 1855 SupplicantResult<Void> result = new SupplicantResult( 1856 "enableWfd(" + enable + ")"); 1857 try { 1858 result.setResult(mISupplicantP2pIface.enableWfd(enable)); 1859 } catch (RemoteException e) { 1860 Log.e(TAG, "ISupplicantP2pIface exception: " + e); 1861 supplicantServiceDiedHandler(); 1862 } 1863 1864 return result.isSuccess(); 1865 } 1866 } 1867 1868 1869 /** 1870 * Set Wifi Display device info. 1871 * 1872 * @param info WFD device info as described in section 5.1.2 of WFD technical 1873 * specification v1.0.0. 1874 * @return true, if operation was successful. 1875 */ setWfdDeviceInfo(String info)1876 public boolean setWfdDeviceInfo(String info) { 1877 synchronized (mLock) { 1878 if (!checkSupplicantP2pIfaceAndLogFailure("setWfdDeviceInfo")) return false; 1879 1880 if (info == null) { 1881 Log.e(TAG, "Cannot parse null WFD info string."); 1882 return false; 1883 } 1884 byte[] wfdInfo = null; 1885 try { 1886 wfdInfo = NativeUtil.hexStringToByteArray(info); 1887 } catch (Exception e) { 1888 Log.e(TAG, "Could not parse WFD Device Info string."); 1889 return false; 1890 } 1891 1892 SupplicantResult<Void> result = new SupplicantResult( 1893 "setWfdDeviceInfo(" + info + ")"); 1894 try { 1895 result.setResult(mISupplicantP2pIface.setWfdDeviceInfo(wfdInfo)); 1896 } catch (RemoteException e) { 1897 Log.e(TAG, "ISupplicantP2pIface exception: " + e); 1898 supplicantServiceDiedHandler(); 1899 } 1900 1901 return result.isSuccess(); 1902 } 1903 } 1904 1905 /** 1906 * Remove network with provided id. 1907 * 1908 * @param networkId Id of the network to lookup. 1909 * @return true, if operation was successful. 1910 */ removeNetwork(int networkId)1911 public boolean removeNetwork(int networkId) { 1912 synchronized (mLock) { 1913 if (!checkSupplicantP2pIfaceAndLogFailure("removeNetwork")) return false; 1914 1915 SupplicantResult<Void> result = new SupplicantResult( 1916 "removeNetwork(" + networkId + ")"); 1917 try { 1918 result.setResult(mISupplicantP2pIface.removeNetwork(networkId)); 1919 } catch (RemoteException e) { 1920 Log.e(TAG, "ISupplicantP2pIface exception: " + e); 1921 supplicantServiceDiedHandler(); 1922 } 1923 1924 return result.isSuccess(); 1925 } 1926 } 1927 1928 /** 1929 * List the networks saved in wpa_supplicant. 1930 * 1931 * @return List of network ids. 1932 */ listNetworks()1933 private List<Integer> listNetworks() { 1934 synchronized (mLock) { 1935 if (!checkSupplicantP2pIfaceAndLogFailure("listNetworks")) return null; 1936 SupplicantResult<ArrayList> result = new SupplicantResult("listNetworks()"); 1937 try { 1938 mISupplicantP2pIface.listNetworks( 1939 (SupplicantStatus status, ArrayList<Integer> networkIds) -> { 1940 result.setResult(status, networkIds); 1941 }); 1942 } catch (RemoteException e) { 1943 Log.e(TAG, "ISupplicantP2pIface exception: " + e); 1944 supplicantServiceDiedHandler(); 1945 } 1946 return result.getResult(); 1947 } 1948 } 1949 1950 /** 1951 * Get the supplicant P2p network object for the specified network ID. 1952 * 1953 * @param networkId Id of the network to lookup. 1954 * @return ISupplicantP2pNetwork instance on success, null on failure. 1955 */ getNetwork(int networkId)1956 private ISupplicantP2pNetwork getNetwork(int networkId) { 1957 synchronized (mLock) { 1958 if (!checkSupplicantP2pIfaceAndLogFailure("getNetwork")) return null; 1959 SupplicantResult<ISupplicantNetwork> result = 1960 new SupplicantResult("getNetwork(" + networkId + ")"); 1961 try { 1962 mISupplicantP2pIface.getNetwork( 1963 networkId, 1964 (SupplicantStatus status, ISupplicantNetwork network) -> { 1965 result.setResult(status, network); 1966 }); 1967 } catch (RemoteException e) { 1968 Log.e(TAG, "ISupplicantP2pIface exception: " + e); 1969 supplicantServiceDiedHandler(); 1970 } 1971 if (result.getResult() == null) { 1972 Log.e(TAG, "getNetwork got null network"); 1973 return null; 1974 } 1975 return getP2pNetworkMockable(result.getResult()); 1976 } 1977 } 1978 1979 /** 1980 * Get the persistent group list from wpa_supplicant's p2p mgmt interface 1981 * 1982 * @param groups WifiP2pGroupList to store persistent groups in 1983 * @return true, if list has been modified. 1984 */ loadGroups(WifiP2pGroupList groups)1985 public boolean loadGroups(WifiP2pGroupList groups) { 1986 synchronized (mLock) { 1987 if (!checkSupplicantP2pIfaceAndLogFailure("loadGroups")) return false; 1988 List<Integer> networkIds = listNetworks(); 1989 if (networkIds == null || networkIds.isEmpty()) { 1990 return false; 1991 } 1992 for (Integer networkId : networkIds) { 1993 ISupplicantP2pNetwork network = getNetwork(networkId); 1994 if (network == null) { 1995 Log.e(TAG, "Failed to retrieve network object for " + networkId); 1996 continue; 1997 } 1998 SupplicantResult<Boolean> resultIsCurrent = 1999 new SupplicantResult("isCurrent(" + networkId + ")"); 2000 try { 2001 network.isCurrent( 2002 (SupplicantStatus status, boolean isCurrent) -> { 2003 resultIsCurrent.setResult(status, isCurrent); 2004 }); 2005 } catch (RemoteException e) { 2006 Log.e(TAG, "ISupplicantP2pIface exception: " + e); 2007 supplicantServiceDiedHandler(); 2008 } 2009 /** Skip the current network, if we're somehow getting networks from the p2p GO 2010 interface, instead of p2p mgmt interface*/ 2011 if (!resultIsCurrent.isSuccess() || resultIsCurrent.getResult()) { 2012 Log.i(TAG, "Skipping current network"); 2013 continue; 2014 } 2015 2016 WifiP2pGroup group = new WifiP2pGroup(); 2017 group.setNetworkId(networkId); 2018 2019 // Now get the ssid, bssid and other flags for this network. 2020 SupplicantResult<ArrayList> resultSsid = 2021 new SupplicantResult("getSsid(" + networkId + ")"); 2022 try { 2023 network.getSsid( 2024 (SupplicantStatus status, ArrayList<Byte> ssid) -> { 2025 resultSsid.setResult(status, ssid); 2026 }); 2027 } catch (RemoteException e) { 2028 Log.e(TAG, "ISupplicantP2pIface exception: " + e); 2029 supplicantServiceDiedHandler(); 2030 } 2031 if (resultSsid.isSuccess() && resultSsid.getResult() != null 2032 && !resultSsid.getResult().isEmpty()) { 2033 group.setNetworkName(NativeUtil.removeEnclosingQuotes( 2034 NativeUtil.encodeSsid(resultSsid.getResult()))); 2035 } 2036 2037 SupplicantResult<byte[]> resultBssid = 2038 new SupplicantResult("getBssid(" + networkId + ")"); 2039 try { 2040 network.getBssid( 2041 (SupplicantStatus status, byte[] bssid) -> { 2042 resultBssid.setResult(status, bssid); 2043 }); 2044 } catch (RemoteException e) { 2045 Log.e(TAG, "ISupplicantP2pIface exception: " + e); 2046 supplicantServiceDiedHandler(); 2047 } 2048 if (resultBssid.isSuccess() && !ArrayUtils.isEmpty(resultBssid.getResult())) { 2049 WifiP2pDevice device = new WifiP2pDevice(); 2050 device.deviceAddress = 2051 NativeUtil.macAddressFromByteArray(resultBssid.getResult()); 2052 group.setOwner(device); 2053 } 2054 2055 SupplicantResult<Boolean> resultIsGo = 2056 new SupplicantResult("isGo(" + networkId + ")"); 2057 try { 2058 network.isGo( 2059 (SupplicantStatus status, boolean isGo) -> { 2060 resultIsGo.setResult(status, isGo); 2061 }); 2062 } catch (RemoteException e) { 2063 Log.e(TAG, "ISupplicantP2pIface exception: " + e); 2064 supplicantServiceDiedHandler(); 2065 } 2066 if (resultIsGo.isSuccess()) { 2067 group.setIsGroupOwner(resultIsGo.getResult()); 2068 } 2069 groups.add(group); 2070 } 2071 } 2072 return true; 2073 } 2074 2075 /** 2076 * Set WPS device name. 2077 * 2078 * @param name String to be set. 2079 * @return true if request is sent successfully, false otherwise. 2080 */ setWpsDeviceName(String name)2081 public boolean setWpsDeviceName(String name) { 2082 if (name == null) { 2083 return false; 2084 } 2085 synchronized (mLock) { 2086 if (!checkSupplicantP2pIfaceAndLogFailure("setWpsDeviceName")) return false; 2087 SupplicantResult<Void> result = new SupplicantResult( 2088 "setWpsDeviceName(" + name + ")"); 2089 try { 2090 result.setResult(mISupplicantP2pIface.setWpsDeviceName(name)); 2091 } catch (RemoteException e) { 2092 Log.e(TAG, "ISupplicantP2pIface exception: " + e); 2093 supplicantServiceDiedHandler(); 2094 } 2095 return result.isSuccess(); 2096 } 2097 } 2098 2099 /** 2100 * Set WPS device type. 2101 * 2102 * @param typeStr Type specified as a string. Used format: <categ>-<OUI>-<subcateg> 2103 * @return true if request is sent successfully, false otherwise. 2104 */ setWpsDeviceType(String typeStr)2105 public boolean setWpsDeviceType(String typeStr) { 2106 try { 2107 Matcher match = WPS_DEVICE_TYPE_PATTERN.matcher(typeStr); 2108 if (!match.find() || match.groupCount() != 3) { 2109 Log.e(TAG, "Malformed WPS device type " + typeStr); 2110 return false; 2111 } 2112 short categ = Short.parseShort(match.group(1)); 2113 byte[] oui = NativeUtil.hexStringToByteArray(match.group(2)); 2114 short subCateg = Short.parseShort(match.group(3)); 2115 2116 byte[] bytes = new byte[8]; 2117 ByteBuffer byteBuffer = ByteBuffer.wrap(bytes).order(ByteOrder.BIG_ENDIAN); 2118 byteBuffer.putShort(categ); 2119 byteBuffer.put(oui); 2120 byteBuffer.putShort(subCateg); 2121 synchronized (mLock) { 2122 if (!checkSupplicantP2pIfaceAndLogFailure("setWpsDeviceType")) return false; 2123 SupplicantResult<Void> result = new SupplicantResult( 2124 "setWpsDeviceType(" + typeStr + ")"); 2125 try { 2126 result.setResult(mISupplicantP2pIface.setWpsDeviceType(bytes)); 2127 } catch (RemoteException e) { 2128 Log.e(TAG, "ISupplicantP2pIface exception: " + e); 2129 supplicantServiceDiedHandler(); 2130 } 2131 return result.isSuccess(); 2132 } 2133 } catch (IllegalArgumentException e) { 2134 Log.e(TAG, "Illegal argument " + typeStr, e); 2135 return false; 2136 } 2137 } 2138 2139 /** 2140 * Set WPS config methods 2141 * 2142 * @param configMethodsStr List of config methods. 2143 * @return true if request is sent successfully, false otherwise. 2144 */ setWpsConfigMethods(String configMethodsStr)2145 public boolean setWpsConfigMethods(String configMethodsStr) { 2146 synchronized (mLock) { 2147 if (!checkSupplicantP2pIfaceAndLogFailure("setWpsConfigMethods")) return false; 2148 SupplicantResult<Void> result = 2149 new SupplicantResult("setWpsConfigMethods(" + configMethodsStr + ")"); 2150 short configMethodsMask = 0; 2151 String[] configMethodsStrArr = configMethodsStr.split("\\s+"); 2152 for (int i = 0; i < configMethodsStrArr.length; i++) { 2153 configMethodsMask |= stringToWpsConfigMethod(configMethodsStrArr[i]); 2154 } 2155 try { 2156 result.setResult(mISupplicantP2pIface.setWpsConfigMethods(configMethodsMask)); 2157 } catch (RemoteException e) { 2158 Log.e(TAG, "ISupplicantP2pIface exception: " + e); 2159 supplicantServiceDiedHandler(); 2160 } 2161 return result.isSuccess(); 2162 } 2163 } 2164 2165 /** 2166 * Get NFC handover request message. 2167 * 2168 * @return select message if created successfully, null otherwise. 2169 */ getNfcHandoverRequest()2170 public String getNfcHandoverRequest() { 2171 synchronized (mLock) { 2172 if (!checkSupplicantP2pIfaceAndLogFailure("getNfcHandoverRequest")) return null; 2173 SupplicantResult<ArrayList> result = new SupplicantResult( 2174 "getNfcHandoverRequest()"); 2175 try { 2176 mISupplicantP2pIface.createNfcHandoverRequestMessage( 2177 (SupplicantStatus status, ArrayList<Byte> message) -> { 2178 result.setResult(status, message); 2179 }); 2180 } catch (RemoteException e) { 2181 Log.e(TAG, "ISupplicantP2pIface exception: " + e); 2182 supplicantServiceDiedHandler(); 2183 } 2184 if (!result.isSuccess()) { 2185 return null; 2186 2187 } 2188 return NativeUtil.hexStringFromByteArray( 2189 NativeUtil.byteArrayFromArrayList(result.getResult())); 2190 } 2191 } 2192 2193 /** 2194 * Get NFC handover select message. 2195 * 2196 * @return select message if created successfully, null otherwise. 2197 */ getNfcHandoverSelect()2198 public String getNfcHandoverSelect() { 2199 synchronized (mLock) { 2200 if (!checkSupplicantP2pIfaceAndLogFailure("getNfcHandoverSelect")) return null; 2201 SupplicantResult<ArrayList> result = new SupplicantResult( 2202 "getNfcHandoverSelect()"); 2203 try { 2204 mISupplicantP2pIface.createNfcHandoverSelectMessage( 2205 (SupplicantStatus status, ArrayList<Byte> message) -> { 2206 result.setResult(status, message); 2207 }); 2208 } catch (RemoteException e) { 2209 Log.e(TAG, "ISupplicantP2pIface exception: " + e); 2210 supplicantServiceDiedHandler(); 2211 } 2212 if (!result.isSuccess()) { 2213 return null; 2214 2215 } 2216 return NativeUtil.hexStringFromByteArray( 2217 NativeUtil.byteArrayFromArrayList(result.getResult())); 2218 } 2219 } 2220 2221 /** 2222 * Report NFC handover select message. 2223 * 2224 * @return true if reported successfully, false otherwise. 2225 */ initiatorReportNfcHandover(String selectMessage)2226 public boolean initiatorReportNfcHandover(String selectMessage) { 2227 if (selectMessage == null) return false; 2228 synchronized (mLock) { 2229 if (!checkSupplicantP2pIfaceAndLogFailure("initiatorReportNfcHandover")) return false; 2230 SupplicantResult<Void> result = new SupplicantResult( 2231 "initiatorReportNfcHandover(" + selectMessage + ")"); 2232 try { 2233 result.setResult(mISupplicantP2pIface.reportNfcHandoverInitiation( 2234 NativeUtil.byteArrayToArrayList(NativeUtil.hexStringToByteArray( 2235 selectMessage)))); 2236 } catch (RemoteException e) { 2237 Log.e(TAG, "ISupplicantP2pIface exception: " + e); 2238 supplicantServiceDiedHandler(); 2239 } catch (IllegalArgumentException e) { 2240 Log.e(TAG, "Illegal argument " + selectMessage, e); 2241 return false; 2242 } 2243 return result.isSuccess(); 2244 } 2245 } 2246 2247 /** 2248 * Report NFC handover request message. 2249 * 2250 * @return true if reported successfully, false otherwise. 2251 */ responderReportNfcHandover(String requestMessage)2252 public boolean responderReportNfcHandover(String requestMessage) { 2253 if (requestMessage == null) return false; 2254 synchronized (mLock) { 2255 if (!checkSupplicantP2pIfaceAndLogFailure("responderReportNfcHandover")) return false; 2256 SupplicantResult<Void> result = new SupplicantResult( 2257 "responderReportNfcHandover(" + requestMessage + ")"); 2258 try { 2259 result.setResult(mISupplicantP2pIface.reportNfcHandoverResponse( 2260 NativeUtil.byteArrayToArrayList(NativeUtil.hexStringToByteArray( 2261 requestMessage)))); 2262 } catch (RemoteException e) { 2263 Log.e(TAG, "ISupplicantP2pIface exception: " + e); 2264 supplicantServiceDiedHandler(); 2265 } catch (IllegalArgumentException e) { 2266 Log.e(TAG, "Illegal argument " + requestMessage, e); 2267 return false; 2268 } 2269 return result.isSuccess(); 2270 } 2271 } 2272 2273 /** 2274 * Set the client list for the provided network. 2275 * 2276 * @param networkId Id of the network. 2277 * @param clientListStr Space separated list of clients. 2278 * @return true, if operation was successful. 2279 */ setClientList(int networkId, String clientListStr)2280 public boolean setClientList(int networkId, String clientListStr) { 2281 synchronized (mLock) { 2282 if (!checkSupplicantP2pIfaceAndLogFailure("setClientList")) return false; 2283 if (TextUtils.isEmpty(clientListStr)) { 2284 Log.e(TAG, "Invalid client list"); 2285 return false; 2286 } 2287 ISupplicantP2pNetwork network = getNetwork(networkId); 2288 if (network == null) { 2289 Log.e(TAG, "Invalid network id "); 2290 return false; 2291 } 2292 SupplicantResult<Void> result = new SupplicantResult( 2293 "setClientList(" + networkId + ", " + clientListStr + ")"); 2294 try { 2295 ArrayList<byte[]> clients = new ArrayList<>(); 2296 for (String clientStr : Arrays.asList(clientListStr.split("\\s+"))) { 2297 clients.add(NativeUtil.macAddressToByteArray(clientStr)); 2298 } 2299 result.setResult(network.setClientList(clients)); 2300 } catch (RemoteException e) { 2301 Log.e(TAG, "ISupplicantP2pIface exception: " + e); 2302 supplicantServiceDiedHandler(); 2303 } catch (IllegalArgumentException e) { 2304 Log.e(TAG, "Illegal argument " + clientListStr, e); 2305 return false; 2306 } 2307 return result.isSuccess(); 2308 } 2309 } 2310 2311 /** 2312 * Set the client list for the provided network. 2313 * 2314 * @param networkId Id of the network. 2315 * @return Space separated list of clients if successfull, null otherwise. 2316 */ getClientList(int networkId)2317 public String getClientList(int networkId) { 2318 synchronized (mLock) { 2319 if (!checkSupplicantP2pIfaceAndLogFailure("getClientList")) return null; 2320 ISupplicantP2pNetwork network = getNetwork(networkId); 2321 if (network == null) { 2322 Log.e(TAG, "Invalid network id "); 2323 return null; 2324 } 2325 SupplicantResult<ArrayList> result = new SupplicantResult( 2326 "getClientList(" + networkId + ")"); 2327 try { 2328 network.getClientList( 2329 (SupplicantStatus status, ArrayList<byte[]> clients) -> { 2330 result.setResult(status, clients); 2331 }); 2332 } catch (RemoteException e) { 2333 Log.e(TAG, "ISupplicantP2pIface exception: " + e); 2334 supplicantServiceDiedHandler(); 2335 } 2336 if (!result.isSuccess()) { 2337 return null; 2338 } 2339 ArrayList<byte[]> clients = result.getResult(); 2340 return clients.stream() 2341 .map(NativeUtil::macAddressFromByteArray) 2342 .collect(Collectors.joining(" ")); 2343 } 2344 } 2345 2346 /** 2347 * Persist the current configurations to disk. 2348 * 2349 * @return true, if operation was successful. 2350 */ saveConfig()2351 public boolean saveConfig() { 2352 synchronized (mLock) { 2353 if (!checkSupplicantP2pIfaceAndLogFailure("saveConfig")) return false; 2354 SupplicantResult<Void> result = new SupplicantResult("saveConfig()"); 2355 try { 2356 result.setResult(mISupplicantP2pIface.saveConfig()); 2357 } catch (RemoteException e) { 2358 Log.e(TAG, "ISupplicantP2pIface exception: " + e); 2359 supplicantServiceDiedHandler(); 2360 } 2361 return result.isSuccess(); 2362 } 2363 } 2364 2365 2366 /** 2367 * Enable/Disable P2P MAC randomization. 2368 * 2369 * @param enable true to enable, false to disable. 2370 * @return true, if operation was successful. 2371 */ setMacRandomization(boolean enable)2372 public boolean setMacRandomization(boolean enable) { 2373 synchronized (mLock) { 2374 android.hardware.wifi.supplicant.V1_2.ISupplicantP2pIface ifaceV12 = 2375 getSupplicantP2pIfaceAndLogFailureV1_2("setMacRandomization"); 2376 if (ifaceV12 == null) return false; 2377 2378 SupplicantResult<Void> result = new SupplicantResult( 2379 "setMacRandomization(" + enable + ")"); 2380 try { 2381 result.setResult(ifaceV12.setMacRandomization(enable)); 2382 } catch (RemoteException e) { 2383 Log.e(TAG, "ISupplicantP2pIface exception: " + e); 2384 supplicantServiceDiedHandler(); 2385 } 2386 2387 return result.isSuccess(); 2388 } 2389 } 2390 2391 2392 /** 2393 * Converts the Wps config method string to the equivalent enum value. 2394 */ stringToWpsConfigMethod(String configMethod)2395 private static short stringToWpsConfigMethod(String configMethod) { 2396 switch (configMethod) { 2397 case "usba": 2398 return WpsConfigMethods.USBA; 2399 case "ethernet": 2400 return WpsConfigMethods.ETHERNET; 2401 case "label": 2402 return WpsConfigMethods.LABEL; 2403 case "display": 2404 return WpsConfigMethods.DISPLAY; 2405 case "int_nfc_token": 2406 return WpsConfigMethods.INT_NFC_TOKEN; 2407 case "ext_nfc_token": 2408 return WpsConfigMethods.EXT_NFC_TOKEN; 2409 case "nfc_interface": 2410 return WpsConfigMethods.NFC_INTERFACE; 2411 case "push_button": 2412 return WpsConfigMethods.PUSHBUTTON; 2413 case "keypad": 2414 return WpsConfigMethods.KEYPAD; 2415 case "virtual_push_button": 2416 return WpsConfigMethods.VIRT_PUSHBUTTON; 2417 case "physical_push_button": 2418 return WpsConfigMethods.PHY_PUSHBUTTON; 2419 case "p2ps": 2420 return WpsConfigMethods.P2PS; 2421 case "virtual_display": 2422 return WpsConfigMethods.VIRT_DISPLAY; 2423 case "physical_display": 2424 return WpsConfigMethods.PHY_DISPLAY; 2425 default: 2426 throw new IllegalArgumentException( 2427 "Invalid WPS config method: " + configMethod); 2428 } 2429 } 2430 2431 /** Container class allowing propagation of status and/or value 2432 * from callbacks. 2433 * 2434 * Primary purpose is to allow callback lambdas to provide results 2435 * to parent methods. 2436 */ 2437 private static class SupplicantResult<E> { 2438 private String mMethodName; 2439 private SupplicantStatus mStatus; 2440 private E mValue; 2441 SupplicantResult(String methodName)2442 SupplicantResult(String methodName) { 2443 mMethodName = methodName; 2444 mStatus = null; 2445 mValue = null; 2446 logd("entering " + mMethodName); 2447 } 2448 setResult(SupplicantStatus status, E value)2449 public void setResult(SupplicantStatus status, E value) { 2450 logCompletion(mMethodName, status); 2451 logd("leaving " + mMethodName + " with result = " + value); 2452 mStatus = status; 2453 mValue = value; 2454 } 2455 setResult(SupplicantStatus status)2456 public void setResult(SupplicantStatus status) { 2457 logCompletion(mMethodName, status); 2458 logd("leaving " + mMethodName); 2459 mStatus = status; 2460 } 2461 isSuccess()2462 public boolean isSuccess() { 2463 return (mStatus != null 2464 && (mStatus.code == SupplicantStatusCode.SUCCESS 2465 || mStatus.code == SupplicantStatusCode.FAILURE_IFACE_EXISTS)); 2466 } 2467 getResult()2468 public E getResult() { 2469 return (isSuccess() ? mValue : null); 2470 } 2471 } 2472 } 2473