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