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