SupplicantP2pIfaceHal.java revision d72d2f4c0feb70c5f088e3109c2d940c05482d06
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 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.removeEnclosingQuotes( 941 NativeUtil.encodeSsid(ssid)); 942 } catch (Exception e) { 943 Log.e(TAG, "Could not encode SSID.", e); 944 } 945 } 946 result.setResult(status, ssidString); 947 }); 948 } catch (RemoteException e) { 949 Log.e(TAG, "ISupplicantP2pIface exception: " + e); 950 supplicantServiceDiedHandler(); 951 return null; 952 } 953 954 return result.getResult(); 955 } 956 } 957 958 959 /** 960 * Reinvoke a device from a persistent group. 961 * 962 * @param networkId Used to specify the persistent group. 963 * @param peerAddress MAC address of the device to reinvoke. 964 * 965 * @return true, if operation was successful. 966 */ 967 public boolean reinvoke(int networkId, String peerAddress) { 968 if (TextUtils.isEmpty(peerAddress) || networkId < 0) return false; 969 synchronized (mLock) { 970 if (!checkSupplicantP2pIfaceAndLogFailure("reinvoke")) return false; 971 if (peerAddress == null) { 972 Log.e(TAG, "Cannot parse peer mac address."); 973 return false; 974 } 975 byte[] macAddress = null; 976 try { 977 macAddress = NativeUtil.macAddressToByteArray(peerAddress); 978 } catch (Exception e) { 979 Log.e(TAG, "Could not parse mac address.", e); 980 return false; 981 } 982 983 SupplicantResult<Void> result = new SupplicantResult( 984 "reinvoke(" + networkId + ", " + peerAddress + ")"); 985 try { 986 result.setResult(mISupplicantP2pIface.reinvoke(networkId, macAddress)); 987 } catch (RemoteException e) { 988 Log.e(TAG, "ISupplicantP2pIface exception: " + e); 989 supplicantServiceDiedHandler(); 990 } 991 992 return result.isSuccess(); 993 } 994 } 995 996 997 /** 998 * Set up a P2P group owner manually (i.e., without group owner 999 * negotiation with a specific peer). This is also known as autonomous 1000 * group owner. 1001 * 1002 * @param networkId Used to specify the restart of a persistent group. 1003 * @param isPersistent Used to request a persistent group to be formed. 1004 * 1005 * @return true, if operation was successful. 1006 */ 1007 public boolean groupAdd(int networkId, boolean isPersistent) { 1008 synchronized (mLock) { 1009 if (!checkSupplicantP2pIfaceAndLogFailure("groupAdd")) return false; 1010 SupplicantResult<Void> result = 1011 new SupplicantResult("groupAdd(" + networkId + ", " + isPersistent + ")"); 1012 try { 1013 result.setResult(mISupplicantP2pIface.addGroup(isPersistent, networkId)); 1014 } catch (RemoteException e) { 1015 Log.e(TAG, "ISupplicantP2pIface exception: " + e); 1016 supplicantServiceDiedHandler(); 1017 } 1018 return result.isSuccess(); 1019 } 1020 } 1021 1022 /** 1023 * Set up a P2P group owner manually. 1024 * This is a helper method that invokes groupAdd(networkId, isPersistent) internally. 1025 * 1026 * @param isPersistent Used to request a persistent group to be formed. 1027 * 1028 * @return true, if operation was successful. 1029 */ 1030 public boolean groupAdd(boolean isPersistent) { 1031 // Supplicant expects networkId to be -1 if not supplied. 1032 return groupAdd(-1, isPersistent); 1033 } 1034 1035 1036 /** 1037 * Terminate a P2P group. If a new virtual network interface was used for 1038 * the group, it must also be removed. The network interface name of the 1039 * group interface is used as a parameter for this command. 1040 * 1041 * @param groupName Group interface name to use. 1042 * 1043 * @return true, if operation was successful. 1044 */ 1045 public boolean groupRemove(String groupName) { 1046 if (TextUtils.isEmpty(groupName)) return false; 1047 synchronized (mLock) { 1048 if (!checkSupplicantP2pIfaceAndLogFailure("groupRemove")) return false; 1049 SupplicantResult<Void> result = new SupplicantResult("groupRemove(" + groupName + ")"); 1050 try { 1051 result.setResult(mISupplicantP2pIface.removeGroup(groupName)); 1052 } catch (RemoteException e) { 1053 Log.e(TAG, "ISupplicantP2pIface exception: " + e); 1054 supplicantServiceDiedHandler(); 1055 } 1056 return result.isSuccess(); 1057 } 1058 } 1059 1060 1061 /** 1062 * Gets the capability of the group which the device is a 1063 * member of. 1064 * 1065 * @param peerAddress MAC address of the peer. 1066 * 1067 * @return combination of |GroupCapabilityMask| values. 1068 */ 1069 public int getGroupCapability(String peerAddress) { 1070 synchronized (mLock) { 1071 if (!checkSupplicantP2pIfaceAndLogFailure("getGroupCapability")) { 1072 return RESULT_NOT_VALID; 1073 } 1074 1075 if (peerAddress == null) { 1076 Log.e(TAG, "Cannot parse peer mac address."); 1077 return RESULT_NOT_VALID; 1078 } 1079 byte[] macAddress = null; 1080 try { 1081 macAddress = NativeUtil.macAddressToByteArray(peerAddress); 1082 } catch (Exception e) { 1083 Log.e(TAG, "Could not parse group address.", e); 1084 return RESULT_NOT_VALID; 1085 } 1086 1087 SupplicantResult<Integer> capability = new SupplicantResult( 1088 "getGroupCapability(" + peerAddress + ")"); 1089 try { 1090 mISupplicantP2pIface.getGroupCapability( 1091 macAddress, (SupplicantStatus status, int cap) -> { 1092 capability.setResult(status, cap); 1093 }); 1094 } catch (RemoteException e) { 1095 Log.e(TAG, "ISupplicantP2pIface exception: " + e); 1096 supplicantServiceDiedHandler(); 1097 } 1098 1099 if (!capability.isSuccess()) { 1100 return RESULT_NOT_VALID; 1101 } 1102 1103 return capability.getResult(); 1104 } 1105 } 1106 1107 1108 /** 1109 * Configure Extended Listen Timing. 1110 * 1111 * If enabled, listen state must be entered every |intervalInMillis| for at 1112 * least |periodInMillis|. Both values have acceptable range of 1-65535 1113 * (with interval obviously having to be larger than or equal to duration). 1114 * If the P2P module is not idle at the time the Extended Listen Timing 1115 * timeout occurs, the Listen State operation must be skipped. 1116 * 1117 * @param enable Enables or disables listening. 1118 * @param periodInMillis Period in milliseconds. 1119 * @param intervalInMillis Interval in milliseconds. 1120 * 1121 * @return true, if operation was successful. 1122 */ 1123 public boolean configureExtListen(boolean enable, int periodInMillis, int intervalInMillis) { 1124 if (enable && intervalInMillis < periodInMillis) { 1125 return false; 1126 } 1127 synchronized (mLock) { 1128 if (!checkSupplicantP2pIfaceAndLogFailure("configureExtListen")) return false; 1129 1130 // If listening is disabled, wpa supplicant expects zeroes. 1131 if (!enable) { 1132 periodInMillis = 0; 1133 intervalInMillis = 0; 1134 } 1135 1136 // Verify that the integers are not negative. Leave actual parameter validation to 1137 // supplicant. 1138 if (periodInMillis < 0 || intervalInMillis < 0) { 1139 Log.e(TAG, "Invalid parameters supplied to configureExtListen: " + periodInMillis 1140 + ", " + intervalInMillis); 1141 return false; 1142 } 1143 1144 SupplicantResult<Void> result = new SupplicantResult( 1145 "configureExtListen(" + periodInMillis + ", " + intervalInMillis + ")"); 1146 try { 1147 result.setResult( 1148 mISupplicantP2pIface.configureExtListen(periodInMillis, intervalInMillis)); 1149 } catch (RemoteException e) { 1150 Log.e(TAG, "ISupplicantP2pIface exception: " + e); 1151 supplicantServiceDiedHandler(); 1152 } 1153 1154 return result.isSuccess(); 1155 } 1156 } 1157 1158 1159 /** 1160 * Set P2P Listen channel and operating chanel. 1161 * 1162 * @param listenChannel Wifi channel. eg, 1, 6, 11. 1163 * @param operatingChannel Wifi channel. eg, 1, 6, 11. 1164 * 1165 * @return true, if operation was successful. 1166 */ 1167 public boolean setListenChannel(int listenChannel, int operatingChannel) { 1168 synchronized (mLock) { 1169 if (!checkSupplicantP2pIfaceAndLogFailure("setListenChannel")) return false; 1170 1171 if (listenChannel >= 1 && listenChannel <= 11) { 1172 SupplicantResult<Void> result = new SupplicantResult( 1173 "setListenChannel(" + listenChannel + ", " + DEFAULT_OPERATING_CLASS + ")"); 1174 try { 1175 result.setResult(mISupplicantP2pIface.setListenChannel( 1176 listenChannel, DEFAULT_OPERATING_CLASS)); 1177 } catch (RemoteException e) { 1178 Log.e(TAG, "ISupplicantP2pIface exception: " + e); 1179 supplicantServiceDiedHandler(); 1180 } 1181 if (!result.isSuccess()) { 1182 return false; 1183 } 1184 } else if (listenChannel != 0) { 1185 // listenChannel == 0 does not set any listen channel. 1186 return false; 1187 } 1188 1189 if (operatingChannel >= 0 && operatingChannel <= 165) { 1190 ArrayList<ISupplicantP2pIface.FreqRange> ranges = new ArrayList<>(); 1191 // operatingChannel == 0 enables all freqs. 1192 if (operatingChannel >= 1 && operatingChannel <= 165) { 1193 int freq = (operatingChannel <= 14 ? 2407 : 5000) + operatingChannel * 5; 1194 ISupplicantP2pIface.FreqRange range1 = new ISupplicantP2pIface.FreqRange(); 1195 range1.min = 1000; 1196 range1.max = freq - 5; 1197 ISupplicantP2pIface.FreqRange range2 = new ISupplicantP2pIface.FreqRange(); 1198 range2.min = freq + 5; 1199 range2.max = 6000; 1200 ranges.add(range1); 1201 ranges.add(range2); 1202 } 1203 SupplicantResult<Void> result = new SupplicantResult( 1204 "setDisallowedFrequencies(" + ranges + ")"); 1205 try { 1206 result.setResult(mISupplicantP2pIface.setDisallowedFrequencies(ranges)); 1207 } catch (RemoteException e) { 1208 Log.e(TAG, "ISupplicantP2pIface exception: " + e); 1209 supplicantServiceDiedHandler(); 1210 } 1211 return result.isSuccess(); 1212 } 1213 return false; 1214 } 1215 } 1216 1217 1218 /** 1219 * This command can be used to add a upnp/bonjour service. 1220 * 1221 * @param servInfo List of service queries. 1222 * 1223 * @return true, if operation was successful. 1224 */ 1225 public boolean serviceAdd(WifiP2pServiceInfo servInfo) { 1226 synchronized (mLock) { 1227 if (!checkSupplicantP2pIfaceAndLogFailure("serviceAdd")) return false; 1228 1229 if (servInfo == null) { 1230 Log.e(TAG, "Null service info passed."); 1231 return false; 1232 } 1233 1234 for (String s : servInfo.getSupplicantQueryList()) { 1235 if (s == null) { 1236 Log.e(TAG, "Invalid service description (null)."); 1237 return false; 1238 } 1239 1240 String[] data = s.split(" "); 1241 if (data.length < 3) { 1242 Log.e(TAG, "Service specification invalid: " + s); 1243 return false; 1244 } 1245 1246 SupplicantResult<Void> result = null; 1247 try { 1248 if ("upnp".equals(data[0])) { 1249 int version = 0; 1250 try { 1251 version = Integer.parseInt(data[1], 16); 1252 } catch (NumberFormatException e) { 1253 Log.e(TAG, "UPnP Service specification invalid: " + s, e); 1254 return false; 1255 } 1256 1257 result = new SupplicantResult( 1258 "addUpnpService(" + data[1] + ", " + data[2] + ")"); 1259 result.setResult(mISupplicantP2pIface.addUpnpService(version, data[2])); 1260 } else if ("bonjour".equals(data[0])) { 1261 if (data[1] != null && data[2] != null) { 1262 ArrayList<Byte> request = null; 1263 ArrayList<Byte> response = null; 1264 try { 1265 request = NativeUtil.byteArrayToArrayList( 1266 NativeUtil.hexStringToByteArray(data[1])); 1267 response = NativeUtil.byteArrayToArrayList( 1268 NativeUtil.hexStringToByteArray(data[2])); 1269 } catch (Exception e) { 1270 Log.e(TAG, "Invalid bonjour service description."); 1271 return false; 1272 } 1273 result = new SupplicantResult( 1274 "addBonjourService(" + data[1] + ", " + data[2] + ")"); 1275 result.setResult( 1276 mISupplicantP2pIface.addBonjourService(request, response)); 1277 } 1278 } else { 1279 return false; 1280 } 1281 } catch (RemoteException e) { 1282 Log.e(TAG, "ISupplicantP2pIface exception: " + e); 1283 supplicantServiceDiedHandler(); 1284 } 1285 1286 if (result == null || !result.isSuccess()) return false; 1287 } 1288 1289 return true; 1290 } 1291 } 1292 1293 1294 /** 1295 * This command can be used to remove a upnp/bonjour service. 1296 * 1297 * @param servInfo List of service queries. 1298 * 1299 * @return true, if operation was successful. 1300 */ 1301 public boolean serviceRemove(WifiP2pServiceInfo servInfo) { 1302 synchronized (mLock) { 1303 if (!checkSupplicantP2pIfaceAndLogFailure("serviceRemove")) return false; 1304 1305 if (servInfo == null) { 1306 Log.e(TAG, "Null service info passed."); 1307 return false; 1308 } 1309 1310 for (String s : servInfo.getSupplicantQueryList()) { 1311 if (s == null) { 1312 Log.e(TAG, "Invalid service description (null)."); 1313 return false; 1314 } 1315 1316 String[] data = s.split(" "); 1317 if (data.length < 3) { 1318 Log.e(TAG, "Service specification invalid: " + s); 1319 return false; 1320 } 1321 1322 SupplicantResult<Void> result = null; 1323 try { 1324 if ("upnp".equals(data[0])) { 1325 int version = 0; 1326 try { 1327 version = Integer.parseInt(data[1], 16); 1328 } catch (NumberFormatException e) { 1329 Log.e(TAG, "UPnP Service specification invalid: " + s, e); 1330 return false; 1331 } 1332 result = new SupplicantResult( 1333 "removeUpnpService(" + data[1] + ", " + data[2] + ")"); 1334 result.setResult(mISupplicantP2pIface.removeUpnpService(version, data[2])); 1335 } else if ("bonjour".equals(data[0])) { 1336 if (data[1] != null) { 1337 ArrayList<Byte> request = null; 1338 try { 1339 request = NativeUtil.byteArrayToArrayList( 1340 NativeUtil.hexStringToByteArray(data[1])); 1341 } catch (Exception e) { 1342 Log.e(TAG, "Invalid bonjour service description."); 1343 return false; 1344 } 1345 result = new SupplicantResult("removeBonjourService(" + data[1] + ")"); 1346 result.setResult(mISupplicantP2pIface.removeBonjourService(request)); 1347 } 1348 } else { 1349 Log.e(TAG, "Unknown / unsupported P2P service requested: " + data[0]); 1350 return false; 1351 } 1352 } catch (RemoteException e) { 1353 Log.e(TAG, "ISupplicantP2pIface exception: " + e); 1354 supplicantServiceDiedHandler(); 1355 } 1356 1357 if (result == null || !result.isSuccess()) return false; 1358 } 1359 1360 return true; 1361 } 1362 } 1363 1364 1365 /** 1366 * Schedule a P2P service discovery request. The parameters for this command 1367 * are the device address of the peer device (or 00:00:00:00:00:00 for 1368 * wildcard query that is sent to every discovered P2P peer that supports 1369 * service discovery) and P2P Service Query TLV(s) as hexdump. 1370 * 1371 * @param peerAddress MAC address of the device to discover. 1372 * @param query Hex dump of the query data. 1373 * @return identifier Identifier for the request. Can be used to cancel the 1374 * request. 1375 */ 1376 public String requestServiceDiscovery(String peerAddress, String query) { 1377 synchronized (mLock) { 1378 if (!checkSupplicantP2pIfaceAndLogFailure("requestServiceDiscovery")) return null; 1379 1380 if (peerAddress == null) { 1381 Log.e(TAG, "Cannot parse peer mac address."); 1382 return null; 1383 } 1384 byte[] macAddress = null; 1385 try { 1386 macAddress = NativeUtil.macAddressToByteArray(peerAddress); 1387 } catch (Exception e) { 1388 Log.e(TAG, "Could not process peer MAC address.", e); 1389 return null; 1390 } 1391 1392 if (query == null) { 1393 Log.e(TAG, "Cannot parse service discovery query: " + query); 1394 return null; 1395 } 1396 ArrayList<Byte> binQuery = null; 1397 try { 1398 binQuery = NativeUtil.byteArrayToArrayList(NativeUtil.hexStringToByteArray(query)); 1399 } catch (Exception e) { 1400 Log.e(TAG, "Could not parse service query.", e); 1401 return null; 1402 } 1403 1404 SupplicantResult<Long> result = new SupplicantResult( 1405 "requestServiceDiscovery(" + peerAddress + ", " + query + ")"); 1406 try { 1407 mISupplicantP2pIface.requestServiceDiscovery( 1408 macAddress, binQuery, 1409 (SupplicantStatus status, long identifier) -> { 1410 result.setResult(status, new Long(identifier)); 1411 }); 1412 } catch (RemoteException e) { 1413 Log.e(TAG, "ISupplicantP2pIface exception: " + e); 1414 supplicantServiceDiedHandler(); 1415 } 1416 1417 Long value = result.getResult(); 1418 if (value == null) return null; 1419 return value.toString(); 1420 } 1421 } 1422 1423 1424 /** 1425 * Cancel a previous service discovery request. 1426 * 1427 * @param identifier Identifier for the request to cancel. 1428 * @return true, if operation was successful. 1429 */ 1430 public boolean cancelServiceDiscovery(String identifier) { 1431 synchronized (mLock) { 1432 if (!checkSupplicantP2pIfaceAndLogFailure("cancelServiceDiscovery")) return false; 1433 if (identifier == null) { 1434 Log.e(TAG, "cancelServiceDiscovery requires a valid tag."); 1435 return false; 1436 } 1437 1438 long id = 0; 1439 try { 1440 id = Long.parseLong(identifier); 1441 } catch (NumberFormatException e) { 1442 Log.e(TAG, "Service discovery identifier invalid: " + identifier, e); 1443 return false; 1444 } 1445 1446 SupplicantResult<Void> result = new SupplicantResult( 1447 "cancelServiceDiscovery(" + identifier + ")"); 1448 try { 1449 result.setResult(mISupplicantP2pIface.cancelServiceDiscovery(id)); 1450 } catch (RemoteException e) { 1451 Log.e(TAG, "ISupplicantP2pIface exception: " + e); 1452 supplicantServiceDiedHandler(); 1453 } 1454 1455 return result.isSuccess(); 1456 } 1457 } 1458 1459 1460 /** 1461 * Send driver command to set Miracast mode. 1462 * 1463 * @param mode Mode of Miracast. 1464 * @return true, if operation was successful. 1465 */ 1466 public boolean setMiracastMode(int mode) { 1467 synchronized (mLock) { 1468 if (!checkSupplicantP2pIfaceAndLogFailure("setMiracastMode")) return false; 1469 byte targetMode = ISupplicantP2pIface.MiracastMode.DISABLED; 1470 1471 switch (mode) { 1472 case WifiP2pManager.MIRACAST_SOURCE: 1473 targetMode = ISupplicantP2pIface.MiracastMode.SOURCE; 1474 break; 1475 1476 case WifiP2pManager.MIRACAST_SINK: 1477 targetMode = ISupplicantP2pIface.MiracastMode.SINK; 1478 break; 1479 } 1480 1481 SupplicantResult<Void> result = new SupplicantResult( 1482 "setMiracastMode(" + mode + ")"); 1483 try { 1484 result.setResult(mISupplicantP2pIface.setMiracastMode(targetMode)); 1485 } catch (RemoteException e) { 1486 Log.e(TAG, "ISupplicantP2pIface exception: " + e); 1487 supplicantServiceDiedHandler(); 1488 } 1489 1490 return result.isSuccess(); 1491 } 1492 } 1493 1494 1495 /** 1496 * Initiate WPS Push Button setup. 1497 * The PBC operation requires that a button is also pressed at the 1498 * AP/Registrar at about the same time (2 minute window). 1499 * 1500 * @param groupIfName Group interface name to use. 1501 * @param bssid BSSID of the AP. Use empty bssid to indicate wildcard. 1502 * @return true, if operation was successful. 1503 */ 1504 public boolean startWpsPbc(String groupIfName, String bssid) { 1505 if (TextUtils.isEmpty(groupIfName)) { 1506 Log.e(TAG, "Group name required when requesting WPS PBC. Got (" + groupIfName + ")"); 1507 return false; 1508 } 1509 synchronized (mLock) { 1510 if (!checkSupplicantP2pIfaceAndLogFailure("startWpsPbc")) return false; 1511 // Null values should be fine, since bssid can be empty. 1512 byte[] macAddress = null; 1513 try { 1514 macAddress = NativeUtil.macAddressToByteArray(bssid); 1515 } catch (Exception e) { 1516 Log.e(TAG, "Could not parse BSSID.", e); 1517 return false; 1518 } 1519 1520 SupplicantResult<Void> result = new SupplicantResult( 1521 "startWpsPbc(" + groupIfName + ", " + bssid + ")"); 1522 try { 1523 result.setResult(mISupplicantP2pIface.startWpsPbc(groupIfName, macAddress)); 1524 } catch (RemoteException e) { 1525 Log.e(TAG, "ISupplicantP2pIface exception: " + e); 1526 supplicantServiceDiedHandler(); 1527 } 1528 1529 return result.isSuccess(); 1530 } 1531 } 1532 1533 1534 /** 1535 * Initiate WPS Pin Keypad setup. 1536 * 1537 * @param groupIfName Group interface name to use. 1538 * @param pin 8 digit pin to be used. 1539 * @return true, if operation was successful. 1540 */ 1541 public boolean startWpsPinKeypad(String groupIfName, String pin) { 1542 if (TextUtils.isEmpty(groupIfName) || TextUtils.isEmpty(pin)) return false; 1543 synchronized (mLock) { 1544 if (!checkSupplicantP2pIfaceAndLogFailure("startWpsPinKeypad")) return false; 1545 if (groupIfName == null) { 1546 Log.e(TAG, "Group name required when requesting WPS KEYPAD."); 1547 return false; 1548 } 1549 if (pin == null) { 1550 Log.e(TAG, "PIN required when requesting WPS KEYPAD."); 1551 return false; 1552 } 1553 1554 SupplicantResult<Void> result = new SupplicantResult( 1555 "startWpsPinKeypad(" + groupIfName + ", " + pin + ")"); 1556 try { 1557 result.setResult(mISupplicantP2pIface.startWpsPinKeypad(groupIfName, pin)); 1558 } catch (RemoteException e) { 1559 Log.e(TAG, "ISupplicantP2pIface exception: " + e); 1560 supplicantServiceDiedHandler(); 1561 } 1562 1563 return result.isSuccess(); 1564 } 1565 } 1566 1567 1568 /** 1569 * Initiate WPS Pin Display setup. 1570 * 1571 * @param groupIfName Group interface name to use. 1572 * @param bssid BSSID of the AP. Use empty bssid to indicate wildcard. 1573 * @return generated pin if operation was successful, null otherwise. 1574 */ 1575 public String startWpsPinDisplay(String groupIfName, String bssid) { 1576 if (TextUtils.isEmpty(groupIfName)) return null; 1577 synchronized (mLock) { 1578 if (!checkSupplicantP2pIfaceAndLogFailure("startWpsPinDisplay")) return null; 1579 if (groupIfName == null) { 1580 Log.e(TAG, "Group name required when requesting WPS KEYPAD."); 1581 return null; 1582 } 1583 1584 // Null values should be fine, since bssid can be empty. 1585 byte[] macAddress = null; 1586 try { 1587 macAddress = NativeUtil.macAddressToByteArray(bssid); 1588 } catch (Exception e) { 1589 Log.e(TAG, "Could not parse BSSID.", e); 1590 return null; 1591 } 1592 1593 SupplicantResult<String> result = new SupplicantResult( 1594 "startWpsPinDisplay(" + groupIfName + ", " + bssid + ")"); 1595 try { 1596 mISupplicantP2pIface.startWpsPinDisplay( 1597 groupIfName, macAddress, 1598 (SupplicantStatus status, String generatedPin) -> { 1599 result.setResult(status, generatedPin); 1600 }); 1601 } catch (RemoteException e) { 1602 Log.e(TAG, "ISupplicantP2pIface exception: " + e); 1603 supplicantServiceDiedHandler(); 1604 } 1605 1606 return result.getResult(); 1607 } 1608 } 1609 1610 1611 /** 1612 * Cancel any ongoing WPS operations. 1613 * 1614 * @param groupIfName Group interface name to use. 1615 * @return true, if operation was successful. 1616 */ 1617 public boolean cancelWps(String groupIfName) { 1618 synchronized (mLock) { 1619 if (!checkSupplicantP2pIfaceAndLogFailure("cancelWps")) return false; 1620 if (groupIfName == null) { 1621 Log.e(TAG, "Group name required when requesting WPS KEYPAD."); 1622 return false; 1623 } 1624 1625 SupplicantResult<Void> result = new SupplicantResult( 1626 "cancelWps(" + groupIfName + ")"); 1627 try { 1628 result.setResult(mISupplicantP2pIface.cancelWps(groupIfName)); 1629 } catch (RemoteException e) { 1630 Log.e(TAG, "ISupplicantP2pIface exception: " + e); 1631 supplicantServiceDiedHandler(); 1632 } 1633 1634 return result.isSuccess(); 1635 } 1636 } 1637 1638 1639 /** 1640 * Enable/Disable Wifi Display. 1641 * 1642 * @param enable true to enable, false to disable. 1643 * @return true, if operation was successful. 1644 */ 1645 public boolean enableWfd(boolean enable) { 1646 synchronized (mLock) { 1647 if (!checkSupplicantP2pIfaceAndLogFailure("enableWfd")) return false; 1648 1649 SupplicantResult<Void> result = new SupplicantResult( 1650 "enableWfd(" + enable + ")"); 1651 try { 1652 result.setResult(mISupplicantP2pIface.enableWfd(enable)); 1653 } catch (RemoteException e) { 1654 Log.e(TAG, "ISupplicantP2pIface exception: " + e); 1655 supplicantServiceDiedHandler(); 1656 } 1657 1658 return result.isSuccess(); 1659 } 1660 } 1661 1662 1663 /** 1664 * Set Wifi Display device info. 1665 * 1666 * @param info WFD device info as described in section 5.1.2 of WFD technical 1667 * specification v1.0.0. 1668 * @return true, if operation was successful. 1669 */ 1670 public boolean setWfdDeviceInfo(String info) { 1671 synchronized (mLock) { 1672 if (!checkSupplicantP2pIfaceAndLogFailure("setWfdDeviceInfo")) return false; 1673 1674 if (info == null) { 1675 Log.e(TAG, "Cannot parse null WFD info string."); 1676 return false; 1677 } 1678 byte[] wfdInfo = null; 1679 try { 1680 wfdInfo = NativeUtil.hexStringToByteArray(info); 1681 } catch (Exception e) { 1682 Log.e(TAG, "Could not parse WFD Device Info string."); 1683 return false; 1684 } 1685 1686 SupplicantResult<Void> result = new SupplicantResult( 1687 "setWfdDeviceInfo(" + info + ")"); 1688 try { 1689 result.setResult(mISupplicantP2pIface.setWfdDeviceInfo(wfdInfo)); 1690 } catch (RemoteException e) { 1691 Log.e(TAG, "ISupplicantP2pIface exception: " + e); 1692 supplicantServiceDiedHandler(); 1693 } 1694 1695 return result.isSuccess(); 1696 } 1697 } 1698 1699 /** 1700 * Remove network with provided id. 1701 * 1702 * @param networkId Id of the network to lookup. 1703 * @return true, if operation was successful. 1704 */ 1705 public boolean removeNetwork(int networkId) { 1706 synchronized (mLock) { 1707 if (!checkSupplicantP2pIfaceAndLogFailure("removeNetwork")) return false; 1708 1709 SupplicantResult<Void> result = new SupplicantResult( 1710 "removeNetwork(" + networkId + ")"); 1711 try { 1712 result.setResult(mISupplicantP2pIface.removeNetwork(networkId)); 1713 } catch (RemoteException e) { 1714 Log.e(TAG, "ISupplicantP2pIface exception: " + e); 1715 supplicantServiceDiedHandler(); 1716 } 1717 1718 return result.isSuccess(); 1719 } 1720 } 1721 1722 /** 1723 * List the networks saved in wpa_supplicant. 1724 * 1725 * @return List of network ids. 1726 */ 1727 private List<Integer> listNetworks() { 1728 synchronized (mLock) { 1729 if (!checkSupplicantP2pIfaceAndLogFailure("listNetworks")) return null; 1730 SupplicantResult<ArrayList> result = new SupplicantResult("listNetworks()"); 1731 try { 1732 mISupplicantP2pIface.listNetworks( 1733 (SupplicantStatus status, ArrayList<Integer> networkIds) -> { 1734 result.setResult(status, networkIds); 1735 }); 1736 } catch (RemoteException e) { 1737 Log.e(TAG, "ISupplicantP2pIface exception: " + e); 1738 supplicantServiceDiedHandler(); 1739 } 1740 return result.getResult(); 1741 } 1742 } 1743 1744 /** 1745 * Get the supplicant P2p network object for the specified network ID. 1746 * 1747 * @param networkId Id of the network to lookup. 1748 * @return ISupplicantP2pNetwork instance on success, null on failure. 1749 */ 1750 private ISupplicantP2pNetwork getNetwork(int networkId) { 1751 synchronized (mLock) { 1752 if (!checkSupplicantP2pIfaceAndLogFailure("getNetwork")) return null; 1753 SupplicantResult<ISupplicantNetwork> result = 1754 new SupplicantResult("getNetwork(" + networkId + ")"); 1755 try { 1756 mISupplicantP2pIface.getNetwork( 1757 networkId, 1758 (SupplicantStatus status, ISupplicantNetwork network) -> { 1759 result.setResult(status, network); 1760 }); 1761 } catch (RemoteException e) { 1762 Log.e(TAG, "ISupplicantP2pIface exception: " + e); 1763 supplicantServiceDiedHandler(); 1764 } 1765 if (result.getResult() == null) { 1766 Log.e(TAG, "getNetwork got null network"); 1767 return null; 1768 } 1769 return getP2pNetworkMockable(result.getResult()); 1770 } 1771 } 1772 1773 /** 1774 * Get the persistent group list from wpa_supplicant's p2p mgmt interface 1775 * 1776 * @param groups WifiP2pGroupList to store persistent groups in 1777 * @return true, if list has been modified. 1778 */ 1779 public boolean loadGroups(WifiP2pGroupList groups) { 1780 synchronized (mLock) { 1781 if (!checkSupplicantP2pIfaceAndLogFailure("loadGroups")) return false; 1782 List<Integer> networkIds = listNetworks(); 1783 if (networkIds == null || networkIds.isEmpty()) { 1784 return false; 1785 } 1786 for (Integer networkId : networkIds) { 1787 ISupplicantP2pNetwork network = getNetwork(networkId); 1788 if (network == null) { 1789 Log.e(TAG, "Failed to retrieve network object for " + networkId); 1790 continue; 1791 } 1792 SupplicantResult<Boolean> resultIsCurrent = 1793 new SupplicantResult("isCurrent(" + networkId + ")"); 1794 try { 1795 network.isCurrent( 1796 (SupplicantStatus status, boolean isCurrent) -> { 1797 resultIsCurrent.setResult(status, isCurrent); 1798 }); 1799 } catch (RemoteException e) { 1800 Log.e(TAG, "ISupplicantP2pIface exception: " + e); 1801 supplicantServiceDiedHandler(); 1802 } 1803 /** Skip the current network, if we're somehow getting networks from the p2p GO 1804 interface, instead of p2p mgmt interface*/ 1805 if (!resultIsCurrent.isSuccess() || resultIsCurrent.getResult()) { 1806 Log.i(TAG, "Skipping current network"); 1807 continue; 1808 } 1809 1810 WifiP2pGroup group = new WifiP2pGroup(); 1811 group.setNetworkId(networkId); 1812 1813 // Now get the ssid, bssid and other flags for this network. 1814 SupplicantResult<ArrayList> resultSsid = 1815 new SupplicantResult("getSsid(" + networkId + ")"); 1816 try { 1817 network.getSsid( 1818 (SupplicantStatus status, ArrayList<Byte> ssid) -> { 1819 resultSsid.setResult(status, ssid); 1820 }); 1821 } catch (RemoteException e) { 1822 Log.e(TAG, "ISupplicantP2pIface exception: " + e); 1823 supplicantServiceDiedHandler(); 1824 } 1825 if (resultSsid.isSuccess() && resultSsid.getResult() != null 1826 && !resultSsid.getResult().isEmpty()) { 1827 group.setNetworkName(NativeUtil.removeEnclosingQuotes( 1828 NativeUtil.encodeSsid(resultSsid.getResult()))); 1829 } 1830 1831 SupplicantResult<byte[]> resultBssid = 1832 new SupplicantResult("getBssid(" + networkId + ")"); 1833 try { 1834 network.getBssid( 1835 (SupplicantStatus status, byte[] bssid) -> { 1836 resultBssid.setResult(status, bssid); 1837 }); 1838 } catch (RemoteException e) { 1839 Log.e(TAG, "ISupplicantP2pIface exception: " + e); 1840 supplicantServiceDiedHandler(); 1841 } 1842 if (resultBssid.isSuccess() && !ArrayUtils.isEmpty(resultBssid.getResult())) { 1843 WifiP2pDevice device = new WifiP2pDevice(); 1844 device.deviceAddress = 1845 NativeUtil.macAddressFromByteArray(resultBssid.getResult()); 1846 group.setOwner(device); 1847 } 1848 1849 SupplicantResult<Boolean> resultIsGo = 1850 new SupplicantResult("isGo(" + networkId + ")"); 1851 try { 1852 network.isGo( 1853 (SupplicantStatus status, boolean isGo) -> { 1854 resultIsGo.setResult(status, isGo); 1855 }); 1856 } catch (RemoteException e) { 1857 Log.e(TAG, "ISupplicantP2pIface exception: " + e); 1858 supplicantServiceDiedHandler(); 1859 } 1860 if (resultIsGo.isSuccess()) { 1861 group.setIsGroupOwner(resultIsGo.getResult()); 1862 } 1863 groups.add(group); 1864 } 1865 } 1866 return true; 1867 } 1868 1869 /** 1870 * Set WPS device name. 1871 * 1872 * @param name String to be set. 1873 * @return true if request is sent successfully, false otherwise. 1874 */ 1875 public boolean setWpsDeviceName(String name) { 1876 if (name == null) { 1877 return false; 1878 } 1879 synchronized (mLock) { 1880 if (!checkSupplicantP2pIfaceAndLogFailure("setWpsDeviceName")) return false; 1881 SupplicantResult<Void> result = new SupplicantResult( 1882 "setWpsDeviceName(" + name + ")"); 1883 try { 1884 result.setResult(mISupplicantP2pIface.setWpsDeviceName(name)); 1885 } catch (RemoteException e) { 1886 Log.e(TAG, "ISupplicantP2pIface exception: " + e); 1887 supplicantServiceDiedHandler(); 1888 } 1889 return result.isSuccess(); 1890 } 1891 } 1892 1893 /** 1894 * Set WPS device type. 1895 * 1896 * @param typeStr Type specified as a string. Used format: <categ>-<OUI>-<subcateg> 1897 * @return true if request is sent successfully, false otherwise. 1898 */ 1899 public boolean setWpsDeviceType(String typeStr) { 1900 try { 1901 Matcher match = WPS_DEVICE_TYPE_PATTERN.matcher(typeStr); 1902 if (!match.find() || match.groupCount() != 3) { 1903 Log.e(TAG, "Malformed WPS device type " + typeStr); 1904 return false; 1905 } 1906 short categ = Short.parseShort(match.group(1)); 1907 byte[] oui = NativeUtil.hexStringToByteArray(match.group(2)); 1908 short subCateg = Short.parseShort(match.group(3)); 1909 1910 byte[] bytes = new byte[8]; 1911 ByteBuffer byteBuffer = ByteBuffer.wrap(bytes).order(ByteOrder.BIG_ENDIAN); 1912 byteBuffer.putShort(categ); 1913 byteBuffer.put(oui); 1914 byteBuffer.putShort(subCateg); 1915 synchronized (mLock) { 1916 if (!checkSupplicantP2pIfaceAndLogFailure("setWpsDeviceType")) return false; 1917 SupplicantResult<Void> result = new SupplicantResult( 1918 "setWpsDeviceType(" + typeStr + ")"); 1919 try { 1920 result.setResult(mISupplicantP2pIface.setWpsDeviceType(bytes)); 1921 } catch (RemoteException e) { 1922 Log.e(TAG, "ISupplicantP2pIface exception: " + e); 1923 supplicantServiceDiedHandler(); 1924 } 1925 return result.isSuccess(); 1926 } 1927 } catch (IllegalArgumentException e) { 1928 Log.e(TAG, "Illegal argument " + typeStr, e); 1929 return false; 1930 } 1931 } 1932 1933 /** 1934 * Set WPS config methods 1935 * 1936 * @param configMethodsStr List of config methods. 1937 * @return true if request is sent successfully, false otherwise. 1938 */ 1939 public boolean setWpsConfigMethods(String configMethodsStr) { 1940 synchronized (mLock) { 1941 if (!checkSupplicantP2pIfaceAndLogFailure("setWpsConfigMethods")) return false; 1942 SupplicantResult<Void> result = 1943 new SupplicantResult("setWpsConfigMethods(" + configMethodsStr + ")"); 1944 short configMethodsMask = 0; 1945 String[] configMethodsStrArr = configMethodsStr.split("\\s+"); 1946 for (int i = 0; i < configMethodsStrArr.length; i++) { 1947 configMethodsMask |= stringToWpsConfigMethod(configMethodsStrArr[i]); 1948 } 1949 try { 1950 result.setResult(mISupplicantP2pIface.setWpsConfigMethods(configMethodsMask)); 1951 } catch (RemoteException e) { 1952 Log.e(TAG, "ISupplicantP2pIface exception: " + e); 1953 supplicantServiceDiedHandler(); 1954 } 1955 return result.isSuccess(); 1956 } 1957 } 1958 1959 /** 1960 * Get NFC handover request message. 1961 * 1962 * @return select message if created successfully, null otherwise. 1963 */ 1964 public String getNfcHandoverRequest() { 1965 synchronized (mLock) { 1966 if (!checkSupplicantP2pIfaceAndLogFailure("getNfcHandoverRequest")) return null; 1967 SupplicantResult<ArrayList> result = new SupplicantResult( 1968 "getNfcHandoverRequest()"); 1969 try { 1970 mISupplicantP2pIface.createNfcHandoverRequestMessage( 1971 (SupplicantStatus status, ArrayList<Byte> message) -> { 1972 result.setResult(status, message); 1973 }); 1974 } catch (RemoteException e) { 1975 Log.e(TAG, "ISupplicantP2pIface exception: " + e); 1976 supplicantServiceDiedHandler(); 1977 } 1978 if (!result.isSuccess()) { 1979 return null; 1980 1981 } 1982 return NativeUtil.hexStringFromByteArray( 1983 NativeUtil.byteArrayFromArrayList(result.getResult())); 1984 } 1985 } 1986 1987 /** 1988 * Get NFC handover select message. 1989 * 1990 * @return select message if created successfully, null otherwise. 1991 */ 1992 public String getNfcHandoverSelect() { 1993 synchronized (mLock) { 1994 if (!checkSupplicantP2pIfaceAndLogFailure("getNfcHandoverSelect")) return null; 1995 SupplicantResult<ArrayList> result = new SupplicantResult( 1996 "getNfcHandoverSelect()"); 1997 try { 1998 mISupplicantP2pIface.createNfcHandoverSelectMessage( 1999 (SupplicantStatus status, ArrayList<Byte> message) -> { 2000 result.setResult(status, message); 2001 }); 2002 } catch (RemoteException e) { 2003 Log.e(TAG, "ISupplicantP2pIface exception: " + e); 2004 supplicantServiceDiedHandler(); 2005 } 2006 if (!result.isSuccess()) { 2007 return null; 2008 2009 } 2010 return NativeUtil.hexStringFromByteArray( 2011 NativeUtil.byteArrayFromArrayList(result.getResult())); 2012 } 2013 } 2014 2015 /** 2016 * Report NFC handover select message. 2017 * 2018 * @return true if reported successfully, false otherwise. 2019 */ 2020 public boolean initiatorReportNfcHandover(String selectMessage) { 2021 if (selectMessage == null) return false; 2022 synchronized (mLock) { 2023 if (!checkSupplicantP2pIfaceAndLogFailure("initiatorReportNfcHandover")) return false; 2024 SupplicantResult<Void> result = new SupplicantResult( 2025 "initiatorReportNfcHandover(" + selectMessage + ")"); 2026 try { 2027 result.setResult(mISupplicantP2pIface.reportNfcHandoverInitiation( 2028 NativeUtil.byteArrayToArrayList(NativeUtil.hexStringToByteArray( 2029 selectMessage)))); 2030 } catch (RemoteException e) { 2031 Log.e(TAG, "ISupplicantP2pIface exception: " + e); 2032 supplicantServiceDiedHandler(); 2033 } catch (IllegalArgumentException e) { 2034 Log.e(TAG, "Illegal argument " + selectMessage, e); 2035 return false; 2036 } 2037 return result.isSuccess(); 2038 } 2039 } 2040 2041 /** 2042 * Report NFC handover request message. 2043 * 2044 * @return true if reported successfully, false otherwise. 2045 */ 2046 public boolean responderReportNfcHandover(String requestMessage) { 2047 if (requestMessage == null) return false; 2048 synchronized (mLock) { 2049 if (!checkSupplicantP2pIfaceAndLogFailure("responderReportNfcHandover")) return false; 2050 SupplicantResult<Void> result = new SupplicantResult( 2051 "responderReportNfcHandover(" + requestMessage + ")"); 2052 try { 2053 result.setResult(mISupplicantP2pIface.reportNfcHandoverResponse( 2054 NativeUtil.byteArrayToArrayList(NativeUtil.hexStringToByteArray( 2055 requestMessage)))); 2056 } catch (RemoteException e) { 2057 Log.e(TAG, "ISupplicantP2pIface exception: " + e); 2058 supplicantServiceDiedHandler(); 2059 } catch (IllegalArgumentException e) { 2060 Log.e(TAG, "Illegal argument " + requestMessage, e); 2061 return false; 2062 } 2063 return result.isSuccess(); 2064 } 2065 } 2066 2067 /** 2068 * Set the client list for the provided network. 2069 * 2070 * @param networkId Id of the network. 2071 * @param clientListStr Space separated list of clients. 2072 * @return true, if operation was successful. 2073 */ 2074 public boolean setClientList(int networkId, String clientListStr) { 2075 synchronized (mLock) { 2076 if (!checkSupplicantP2pIfaceAndLogFailure("setClientList")) return false; 2077 if (TextUtils.isEmpty(clientListStr)) { 2078 Log.e(TAG, "Invalid client list"); 2079 return false; 2080 } 2081 ISupplicantP2pNetwork network = getNetwork(networkId); 2082 if (network == null) { 2083 Log.e(TAG, "Invalid network id "); 2084 return false; 2085 } 2086 SupplicantResult<Void> result = new SupplicantResult( 2087 "setClientList(" + networkId + ", " + clientListStr + ")"); 2088 try { 2089 ArrayList<byte[]> clients = new ArrayList<>(); 2090 for (String clientStr : Arrays.asList(clientListStr.split("\\s+"))) { 2091 clients.add(NativeUtil.macAddressToByteArray(clientStr)); 2092 } 2093 result.setResult(network.setClientList(clients)); 2094 } catch (RemoteException e) { 2095 Log.e(TAG, "ISupplicantP2pIface exception: " + e); 2096 supplicantServiceDiedHandler(); 2097 } catch (IllegalArgumentException e) { 2098 Log.e(TAG, "Illegal argument " + clientListStr, e); 2099 return false; 2100 } 2101 return result.isSuccess(); 2102 } 2103 } 2104 2105 /** 2106 * Set the client list for the provided network. 2107 * 2108 * @param networkId Id of the network. 2109 * @return Space separated list of clients if successfull, null otherwise. 2110 */ 2111 public String getClientList(int networkId) { 2112 synchronized (mLock) { 2113 if (!checkSupplicantP2pIfaceAndLogFailure("getClientList")) return null; 2114 ISupplicantP2pNetwork network = getNetwork(networkId); 2115 if (network == null) { 2116 Log.e(TAG, "Invalid network id "); 2117 return null; 2118 } 2119 SupplicantResult<ArrayList> result = new SupplicantResult( 2120 "getClientList(" + networkId + ")"); 2121 try { 2122 network.getClientList( 2123 (SupplicantStatus status, ArrayList<byte[]> clients) -> { 2124 result.setResult(status, clients); 2125 }); 2126 } catch (RemoteException e) { 2127 Log.e(TAG, "ISupplicantP2pIface exception: " + e); 2128 supplicantServiceDiedHandler(); 2129 } 2130 if (!result.isSuccess()) { 2131 return null; 2132 } 2133 ArrayList<byte[]> clients = result.getResult(); 2134 return clients.stream() 2135 .map(NativeUtil::macAddressFromByteArray) 2136 .collect(Collectors.joining(" ")); 2137 } 2138 } 2139 2140 /** 2141 * Persist the current configurations to disk. 2142 * 2143 * @return true, if operation was successful. 2144 */ 2145 public boolean saveConfig() { 2146 synchronized (mLock) { 2147 if (!checkSupplicantP2pIfaceAndLogFailure("saveConfig")) return false; 2148 SupplicantResult<Void> result = new SupplicantResult("saveConfig()"); 2149 try { 2150 result.setResult(mISupplicantP2pIface.saveConfig()); 2151 } catch (RemoteException e) { 2152 Log.e(TAG, "ISupplicantP2pIface exception: " + e); 2153 supplicantServiceDiedHandler(); 2154 } 2155 return result.isSuccess(); 2156 } 2157 } 2158 2159 /** 2160 * Converts the Wps config method string to the equivalent enum value. 2161 */ 2162 private static short stringToWpsConfigMethod(String configMethod) { 2163 switch (configMethod) { 2164 case "usba": 2165 return WpsConfigMethods.USBA; 2166 case "ethernet": 2167 return WpsConfigMethods.ETHERNET; 2168 case "label": 2169 return WpsConfigMethods.LABEL; 2170 case "display": 2171 return WpsConfigMethods.DISPLAY; 2172 case "int_nfc_token": 2173 return WpsConfigMethods.INT_NFC_TOKEN; 2174 case "ext_nfc_token": 2175 return WpsConfigMethods.EXT_NFC_TOKEN; 2176 case "nfc_interface": 2177 return WpsConfigMethods.NFC_INTERFACE; 2178 case "push_button": 2179 return WpsConfigMethods.PUSHBUTTON; 2180 case "keypad": 2181 return WpsConfigMethods.KEYPAD; 2182 case "virtual_push_button": 2183 return WpsConfigMethods.VIRT_PUSHBUTTON; 2184 case "physical_push_button": 2185 return WpsConfigMethods.PHY_PUSHBUTTON; 2186 case "p2ps": 2187 return WpsConfigMethods.P2PS; 2188 case "virtual_display": 2189 return WpsConfigMethods.VIRT_DISPLAY; 2190 case "physical_display": 2191 return WpsConfigMethods.PHY_DISPLAY; 2192 default: 2193 throw new IllegalArgumentException( 2194 "Invalid WPS config method: " + configMethod); 2195 } 2196 } 2197 2198 /** Container class allowing propagation of status and/or value 2199 * from callbacks. 2200 * 2201 * Primary purpose is to allow callback lambdas to provide results 2202 * to parent methods. 2203 */ 2204 private static class SupplicantResult<E> { 2205 private String mMethodName; 2206 private SupplicantStatus mStatus; 2207 private E mValue; 2208 2209 SupplicantResult(String methodName) { 2210 mMethodName = methodName; 2211 mStatus = null; 2212 mValue = null; 2213 logd("entering " + mMethodName); 2214 } 2215 2216 public void setResult(SupplicantStatus status, E value) { 2217 logCompletion(mMethodName, status); 2218 logd("leaving " + mMethodName + " with result = " + value); 2219 mStatus = status; 2220 mValue = value; 2221 } 2222 2223 public void setResult(SupplicantStatus status) { 2224 logCompletion(mMethodName, status); 2225 logd("leaving " + mMethodName); 2226 mStatus = status; 2227 } 2228 2229 public boolean isSuccess() { 2230 return (mStatus != null && mStatus.code == SupplicantStatusCode.SUCCESS); 2231 } 2232 2233 public E getResult() { 2234 return (isSuccess() ? mValue : null); 2235 } 2236 } 2237} 2238