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