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