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