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