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