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