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.annotation.Nullable; 21 import android.hardware.wifi.supplicant.V1_0.ISupplicant; 22 import android.hardware.wifi.supplicant.V1_0.ISupplicantIface; 23 import android.hardware.wifi.supplicant.V1_0.ISupplicantNetwork; 24 import android.hardware.wifi.supplicant.V1_0.ISupplicantP2pIface; 25 import android.hardware.wifi.supplicant.V1_0.ISupplicantP2pIfaceCallback; 26 import android.hardware.wifi.supplicant.V1_0.ISupplicantP2pNetwork; 27 import android.hardware.wifi.supplicant.V1_0.IfaceType; 28 import android.hardware.wifi.supplicant.V1_0.SupplicantStatus; 29 import android.hardware.wifi.supplicant.V1_0.SupplicantStatusCode; 30 import android.hardware.wifi.supplicant.V1_0.WpsConfigMethods; 31 import android.hidl.manager.V1_0.IServiceManager; 32 import android.hidl.manager.V1_0.IServiceNotification; 33 import android.net.wifi.CoexUnsafeChannel; 34 import android.net.wifi.ScanResult; 35 import android.net.wifi.WpsInfo; 36 import android.net.wifi.p2p.WifiP2pConfig; 37 import android.net.wifi.p2p.WifiP2pDevice; 38 import android.net.wifi.p2p.WifiP2pExtListenParams; 39 import android.net.wifi.p2p.WifiP2pGroup; 40 import android.net.wifi.p2p.WifiP2pGroupList; 41 import android.net.wifi.p2p.WifiP2pManager; 42 import android.net.wifi.p2p.nsd.WifiP2pServiceInfo; 43 import android.os.IHwBinder.DeathRecipient; 44 import android.os.RemoteException; 45 import android.text.TextUtils; 46 import android.util.Log; 47 48 import com.android.internal.annotations.VisibleForTesting; 49 import com.android.modules.utils.build.SdkLevel; 50 import com.android.server.wifi.util.ArrayUtils; 51 import com.android.server.wifi.util.NativeUtil; 52 53 import java.nio.ByteBuffer; 54 import java.nio.ByteOrder; 55 import java.util.ArrayList; 56 import java.util.Arrays; 57 import java.util.List; 58 import java.util.NoSuchElementException; 59 import java.util.Set; 60 import java.util.regex.Matcher; 61 import java.util.regex.Pattern; 62 import java.util.stream.Collectors; 63 64 /** 65 * Native calls sending requests to the P2P Hals, and callbacks for receiving P2P events 66 */ 67 public class SupplicantP2pIfaceHalHidlImpl implements ISupplicantP2pIfaceHal { 68 private static final String TAG = "SupplicantP2pIfaceHalHidlImpl"; 69 @VisibleForTesting 70 public static final String HAL_INSTANCE_NAME = "default"; 71 private static boolean sVerboseLoggingEnabled = true; 72 private static boolean sHalVerboseLoggingEnabled = true; 73 private static final int RESULT_NOT_VALID = -1; 74 private static final int DEFAULT_OPERATING_CLASS = 81; 75 /** 76 * Regex pattern for extracting the wps device type bytes. 77 * Matches a strings like the following: "<categ>-<OUI>-<subcateg>"; 78 */ 79 private static final Pattern WPS_DEVICE_TYPE_PATTERN = 80 Pattern.compile("^(\\d{1,2})-([0-9a-fA-F]{8})-(\\d{1,2})$"); 81 82 private final Object mLock = new Object(); 83 84 // Supplicant HAL HIDL interface objects 85 private IServiceManager mIServiceManager = null; 86 private ISupplicant mISupplicant = null; 87 private ISupplicantIface mHidlSupplicantIface = null; 88 private ISupplicantP2pIface mISupplicantP2pIface = null; 89 private final IServiceNotification mServiceNotificationCallback = 90 new IServiceNotification.Stub() { 91 public void onRegistration(String fqName, String name, boolean preexisting) { 92 synchronized (mLock) { 93 if (sVerboseLoggingEnabled) { 94 Log.i(TAG, "IServiceNotification.onRegistration for: " + fqName 95 + ", " + name + " preexisting=" + preexisting); 96 } 97 if (!initSupplicantService()) { 98 Log.e(TAG, "Initializing ISupplicant failed."); 99 supplicantServiceDiedHandler(); 100 } else { 101 Log.i(TAG, "Completed initialization of ISupplicant interfaces."); 102 } 103 } 104 } 105 }; 106 private final DeathRecipient mServiceManagerDeathRecipient = 107 cookie -> { 108 Log.w(TAG, "IServiceManager died: cookie=" + cookie); 109 synchronized (mLock) { 110 supplicantServiceDiedHandler(); 111 mIServiceManager = null; // Will need to register a new ServiceNotification 112 } 113 }; 114 private final DeathRecipient mSupplicantDeathRecipient = 115 cookie -> { 116 Log.w(TAG, "ISupplicant/ISupplicantP2pIface died: cookie=" + cookie); 117 synchronized (mLock) { 118 supplicantServiceDiedHandler(); 119 } 120 }; 121 122 private final WifiP2pMonitor mMonitor; 123 private ISupplicantP2pIfaceCallback mCallback = null; 124 SupplicantP2pIfaceHalHidlImpl(WifiP2pMonitor monitor)125 public SupplicantP2pIfaceHalHidlImpl(WifiP2pMonitor monitor) { 126 mMonitor = monitor; 127 } 128 linkToServiceManagerDeath()129 private boolean linkToServiceManagerDeath() { 130 if (mIServiceManager == null) return false; 131 try { 132 if (!mIServiceManager.linkToDeath(mServiceManagerDeathRecipient, 0)) { 133 Log.wtf(TAG, "Error on linkToDeath on IServiceManager"); 134 supplicantServiceDiedHandler(); 135 mIServiceManager = null; // Will need to register a new ServiceNotification 136 return false; 137 } 138 } catch (RemoteException e) { 139 Log.e(TAG, "IServiceManager.linkToDeath exception", e); 140 return false; 141 } 142 return true; 143 } 144 145 /** 146 * Enable verbose logging for all sub modules. 147 * 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, null); 592 } 593 logw(String s)594 protected static void logw(String s) { 595 Log.w(TAG, s, null); 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. See comments for 1478 * {@link ISupplicantP2pIfaceHal#configureExtListen(boolean, int, int, WifiP2pExtListenParams)} 1479 * 1480 * @return true, if operation was successful. 1481 */ configureExtListen(boolean enable, int periodInMillis, int intervalInMillis, @Nullable WifiP2pExtListenParams extListenParams)1482 public boolean configureExtListen(boolean enable, int periodInMillis, int intervalInMillis, 1483 @Nullable WifiP2pExtListenParams extListenParams) { 1484 if (enable && intervalInMillis < periodInMillis) { 1485 return false; 1486 } 1487 synchronized (mLock) { 1488 if (!checkSupplicantP2pIfaceAndLogFailure("configureExtListen")) return false; 1489 1490 // If listening is disabled, wpa supplicant expects zeroes. 1491 if (!enable) { 1492 periodInMillis = 0; 1493 intervalInMillis = 0; 1494 } 1495 1496 // Verify that the integers are not negative. Leave actual parameter validation to 1497 // supplicant. 1498 if (periodInMillis < 0 || intervalInMillis < 0) { 1499 Log.e(TAG, "Invalid parameters supplied to configureExtListen: " + periodInMillis 1500 + ", " + intervalInMillis); 1501 return false; 1502 } 1503 1504 SupplicantResult<Void> result = new SupplicantResult( 1505 "configureExtListen(" + periodInMillis + ", " + intervalInMillis + ")"); 1506 try { 1507 result.setResult( 1508 mISupplicantP2pIface.configureExtListen(periodInMillis, intervalInMillis)); 1509 } catch (RemoteException e) { 1510 Log.e(TAG, "ISupplicantP2pIface exception: " + e); 1511 supplicantServiceDiedHandler(); 1512 } 1513 1514 return result.isSuccess(); 1515 } 1516 } 1517 1518 1519 /** 1520 * Set P2P Listen channel. 1521 * 1522 * @param listenChannel Wifi channel. eg, 1, 6, 11. 1523 * 1524 * @return true, if operation was successful. 1525 */ setListenChannel(int listenChannel)1526 public boolean setListenChannel(int listenChannel) { 1527 synchronized (mLock) { 1528 if (!checkSupplicantP2pIfaceAndLogFailure("setListenChannel")) return false; 1529 1530 // There is no original channel recorded in supplicant, so just return true. 1531 if (0 == listenChannel) return true; 1532 1533 // Using channels other than 1, 6, and 11 would result in discovery issue. 1534 if (listenChannel != 1 && listenChannel != 6 && listenChannel != 11) { 1535 return false; 1536 } 1537 1538 SupplicantResult<Void> result = new SupplicantResult( 1539 "setListenChannel(" + listenChannel + ", " + DEFAULT_OPERATING_CLASS + ")"); 1540 try { 1541 result.setResult(mISupplicantP2pIface.setListenChannel( 1542 listenChannel, DEFAULT_OPERATING_CLASS)); 1543 } catch (RemoteException e) { 1544 Log.e(TAG, "ISupplicantP2pIface exception: " + e); 1545 supplicantServiceDiedHandler(); 1546 } 1547 return result.isSuccess(); 1548 } 1549 } 1550 1551 /** 1552 * Set P2P operating channel. 1553 * 1554 * @param operatingChannel the desired operating channel. 1555 * @param unsafeChannels channels which p2p cannot use. 1556 * 1557 * @return true, if operation was successful. 1558 */ setOperatingChannel(int operatingChannel, @NonNull List<CoexUnsafeChannel> unsafeChannels)1559 public boolean setOperatingChannel(int operatingChannel, 1560 @NonNull List<CoexUnsafeChannel> unsafeChannels) { 1561 synchronized (mLock) { 1562 if (!checkSupplicantP2pIfaceAndLogFailure("setOperatingChannel")) return false; 1563 if (null == unsafeChannels) return false; 1564 1565 ArrayList<ISupplicantP2pIface.FreqRange> ranges = new ArrayList<>(); 1566 if (operatingChannel >= 1 && operatingChannel <= 165) { 1567 int freq = (operatingChannel <= 14 ? 2407 : 5000) + operatingChannel * 5; 1568 ISupplicantP2pIface.FreqRange range1 = new ISupplicantP2pIface.FreqRange(); 1569 range1.min = 1000; 1570 range1.max = freq - 5; 1571 ISupplicantP2pIface.FreqRange range2 = new ISupplicantP2pIface.FreqRange(); 1572 range2.min = freq + 5; 1573 range2.max = 6000; 1574 ranges.add(range1); 1575 ranges.add(range2); 1576 } 1577 if (SdkLevel.isAtLeastS()) { 1578 for (CoexUnsafeChannel cuc: unsafeChannels) { 1579 int centerFreq = ScanResult.convertChannelToFrequencyMhzIfSupported( 1580 cuc.getChannel(), cuc.getBand()); 1581 ISupplicantP2pIface.FreqRange range = new ISupplicantP2pIface.FreqRange(); 1582 // The range boundaries are inclusive in native frequency inclusion check. 1583 // Minusing one to avoid affecting neighbors. 1584 range.min = centerFreq - 5 - 1; 1585 range.max = centerFreq + 5 - 1; 1586 ranges.add(range); 1587 } 1588 } 1589 SupplicantResult<Void> result = new SupplicantResult( 1590 "setDisallowedFrequencies(" + ranges + ")"); 1591 try { 1592 result.setResult(mISupplicantP2pIface.setDisallowedFrequencies(ranges)); 1593 } catch (RemoteException e) { 1594 Log.e(TAG, "ISupplicantP2pIface exception: " + e); 1595 supplicantServiceDiedHandler(); 1596 } 1597 return result.isSuccess(); 1598 } 1599 } 1600 1601 /** 1602 * This command can be used to add a upnp/bonjour service. 1603 * 1604 * @param servInfo List of service queries. 1605 * 1606 * @return true, if operation was successful. 1607 */ serviceAdd(WifiP2pServiceInfo servInfo)1608 public boolean serviceAdd(WifiP2pServiceInfo servInfo) { 1609 synchronized (mLock) { 1610 if (!checkSupplicantP2pIfaceAndLogFailure("serviceAdd")) return false; 1611 1612 if (servInfo == null) { 1613 Log.e(TAG, "Null service info passed."); 1614 return false; 1615 } 1616 1617 for (String s : servInfo.getSupplicantQueryList()) { 1618 if (s == null) { 1619 Log.e(TAG, "Invalid service description (null)."); 1620 return false; 1621 } 1622 1623 String[] data = s.split(" "); 1624 if (data.length < 3) { 1625 Log.e(TAG, "Service specification invalid: " + s); 1626 return false; 1627 } 1628 1629 SupplicantResult<Void> result = null; 1630 try { 1631 if ("upnp".equals(data[0])) { 1632 int version = 0; 1633 try { 1634 version = Integer.parseInt(data[1], 16); 1635 } catch (NumberFormatException e) { 1636 Log.e(TAG, "UPnP Service specification invalid: " + s, e); 1637 return false; 1638 } 1639 1640 result = new SupplicantResult( 1641 "addUpnpService(" + data[1] + ", " + data[2] + ")"); 1642 result.setResult(mISupplicantP2pIface.addUpnpService(version, data[2])); 1643 } else if ("bonjour".equals(data[0])) { 1644 if (data[1] != null && data[2] != null) { 1645 ArrayList<Byte> request = null; 1646 ArrayList<Byte> response = null; 1647 try { 1648 request = NativeUtil.byteArrayToArrayList( 1649 NativeUtil.hexStringToByteArray(data[1])); 1650 response = NativeUtil.byteArrayToArrayList( 1651 NativeUtil.hexStringToByteArray(data[2])); 1652 } catch (Exception e) { 1653 Log.e(TAG, "Invalid bonjour service description."); 1654 return false; 1655 } 1656 result = new SupplicantResult( 1657 "addBonjourService(" + data[1] + ", " + data[2] + ")"); 1658 result.setResult( 1659 mISupplicantP2pIface.addBonjourService(request, response)); 1660 } 1661 } else { 1662 return false; 1663 } 1664 } catch (RemoteException e) { 1665 Log.e(TAG, "ISupplicantP2pIface exception: " + e); 1666 supplicantServiceDiedHandler(); 1667 } 1668 1669 if (result == null || !result.isSuccess()) return false; 1670 } 1671 1672 return true; 1673 } 1674 } 1675 1676 1677 /** 1678 * This command can be used to remove a upnp/bonjour service. 1679 * 1680 * @param servInfo List of service queries. 1681 * 1682 * @return true, if operation was successful. 1683 */ serviceRemove(WifiP2pServiceInfo servInfo)1684 public boolean serviceRemove(WifiP2pServiceInfo servInfo) { 1685 synchronized (mLock) { 1686 if (!checkSupplicantP2pIfaceAndLogFailure("serviceRemove")) return false; 1687 1688 if (servInfo == null) { 1689 Log.e(TAG, "Null service info passed."); 1690 return false; 1691 } 1692 1693 for (String s : servInfo.getSupplicantQueryList()) { 1694 if (s == null) { 1695 Log.e(TAG, "Invalid service description (null)."); 1696 return false; 1697 } 1698 1699 String[] data = s.split(" "); 1700 if (data.length < 3) { 1701 Log.e(TAG, "Service specification invalid: " + s); 1702 return false; 1703 } 1704 1705 SupplicantResult<Void> result = null; 1706 try { 1707 if ("upnp".equals(data[0])) { 1708 int version = 0; 1709 try { 1710 version = Integer.parseInt(data[1], 16); 1711 } catch (NumberFormatException e) { 1712 Log.e(TAG, "UPnP Service specification invalid: " + s, e); 1713 return false; 1714 } 1715 result = new SupplicantResult( 1716 "removeUpnpService(" + data[1] + ", " + data[2] + ")"); 1717 result.setResult(mISupplicantP2pIface.removeUpnpService(version, data[2])); 1718 } else if ("bonjour".equals(data[0])) { 1719 if (data[1] != null) { 1720 ArrayList<Byte> request = null; 1721 try { 1722 request = NativeUtil.byteArrayToArrayList( 1723 NativeUtil.hexStringToByteArray(data[1])); 1724 } catch (Exception e) { 1725 Log.e(TAG, "Invalid bonjour service description."); 1726 return false; 1727 } 1728 result = new SupplicantResult("removeBonjourService(" + data[1] + ")"); 1729 result.setResult(mISupplicantP2pIface.removeBonjourService(request)); 1730 } 1731 } else { 1732 Log.e(TAG, "Unknown / unsupported P2P service requested: " + data[0]); 1733 return false; 1734 } 1735 } catch (RemoteException e) { 1736 Log.e(TAG, "ISupplicantP2pIface exception: " + e); 1737 supplicantServiceDiedHandler(); 1738 } 1739 1740 if (result == null || !result.isSuccess()) return false; 1741 } 1742 1743 return true; 1744 } 1745 } 1746 1747 1748 /** 1749 * Schedule a P2P service discovery request. The parameters for this command 1750 * are the device address of the peer device (or 00:00:00:00:00:00 for 1751 * wildcard query that is sent to every discovered P2P peer that supports 1752 * service discovery) and P2P Service Query TLV(s) as hexdump. 1753 * 1754 * @param peerAddress MAC address of the device to discover. 1755 * @param query Hex dump of the query data. 1756 * @return identifier Identifier for the request. Can be used to cancel the 1757 * request. 1758 */ requestServiceDiscovery(String peerAddress, String query)1759 public String requestServiceDiscovery(String peerAddress, String query) { 1760 synchronized (mLock) { 1761 if (!checkSupplicantP2pIfaceAndLogFailure("requestServiceDiscovery")) return null; 1762 1763 if (peerAddress == null) { 1764 Log.e(TAG, "Cannot parse peer mac address."); 1765 return null; 1766 } 1767 byte[] macAddress = null; 1768 try { 1769 macAddress = NativeUtil.macAddressToByteArray(peerAddress); 1770 } catch (Exception e) { 1771 Log.e(TAG, "Could not process peer MAC address.", e); 1772 return null; 1773 } 1774 1775 if (query == null) { 1776 Log.e(TAG, "Cannot parse service discovery query: " + query); 1777 return null; 1778 } 1779 ArrayList<Byte> binQuery = null; 1780 try { 1781 binQuery = NativeUtil.byteArrayToArrayList(NativeUtil.hexStringToByteArray(query)); 1782 } catch (Exception e) { 1783 Log.e(TAG, "Could not parse service query.", e); 1784 return null; 1785 } 1786 1787 SupplicantResult<Long> result = new SupplicantResult( 1788 "requestServiceDiscovery(" + peerAddress + ", " + query + ")"); 1789 try { 1790 mISupplicantP2pIface.requestServiceDiscovery( 1791 macAddress, binQuery, 1792 (SupplicantStatus status, long identifier) -> { 1793 result.setResult(status, new Long(identifier)); 1794 }); 1795 } catch (RemoteException e) { 1796 Log.e(TAG, "ISupplicantP2pIface exception: " + e); 1797 supplicantServiceDiedHandler(); 1798 } 1799 1800 Long value = result.getResult(); 1801 if (value == null) return null; 1802 return value.toString(); 1803 } 1804 } 1805 1806 1807 /** 1808 * Cancel a previous service discovery request. 1809 * 1810 * @param identifier Identifier for the request to cancel. 1811 * @return true, if operation was successful. 1812 */ cancelServiceDiscovery(String identifier)1813 public boolean cancelServiceDiscovery(String identifier) { 1814 synchronized (mLock) { 1815 if (!checkSupplicantP2pIfaceAndLogFailure("cancelServiceDiscovery")) return false; 1816 if (identifier == null) { 1817 Log.e(TAG, "cancelServiceDiscovery requires a valid tag."); 1818 return false; 1819 } 1820 1821 long id = 0; 1822 try { 1823 id = Long.parseLong(identifier); 1824 } catch (NumberFormatException e) { 1825 Log.e(TAG, "Service discovery identifier invalid: " + identifier, e); 1826 return false; 1827 } 1828 1829 SupplicantResult<Void> result = new SupplicantResult( 1830 "cancelServiceDiscovery(" + identifier + ")"); 1831 try { 1832 result.setResult(mISupplicantP2pIface.cancelServiceDiscovery(id)); 1833 } catch (RemoteException e) { 1834 Log.e(TAG, "ISupplicantP2pIface exception: " + e); 1835 supplicantServiceDiedHandler(); 1836 } 1837 1838 return result.isSuccess(); 1839 } 1840 } 1841 1842 1843 /** 1844 * Send driver command to set Miracast mode. 1845 * 1846 * @param mode Mode of Miracast. 1847 * @return true, if operation was successful. 1848 */ setMiracastMode(int mode)1849 public boolean setMiracastMode(int mode) { 1850 synchronized (mLock) { 1851 if (!checkSupplicantP2pIfaceAndLogFailure("setMiracastMode")) return false; 1852 byte targetMode = ISupplicantP2pIface.MiracastMode.DISABLED; 1853 1854 switch (mode) { 1855 case WifiP2pManager.MIRACAST_SOURCE: 1856 targetMode = ISupplicantP2pIface.MiracastMode.SOURCE; 1857 break; 1858 1859 case WifiP2pManager.MIRACAST_SINK: 1860 targetMode = ISupplicantP2pIface.MiracastMode.SINK; 1861 break; 1862 } 1863 1864 SupplicantResult<Void> result = new SupplicantResult( 1865 "setMiracastMode(" + mode + ")"); 1866 try { 1867 result.setResult(mISupplicantP2pIface.setMiracastMode(targetMode)); 1868 } catch (RemoteException e) { 1869 Log.e(TAG, "ISupplicantP2pIface exception: " + e); 1870 supplicantServiceDiedHandler(); 1871 } 1872 1873 return result.isSuccess(); 1874 } 1875 } 1876 1877 1878 /** 1879 * Initiate WPS Push Button setup. 1880 * The PBC operation requires that a button is also pressed at the 1881 * AP/Registrar at about the same time (2 minute window). 1882 * 1883 * @param groupIfName Group interface name to use. 1884 * @param bssid BSSID of the AP. Use empty bssid to indicate wildcard. 1885 * @return true, if operation was successful. 1886 */ startWpsPbc(String groupIfName, String bssid)1887 public boolean startWpsPbc(String groupIfName, String bssid) { 1888 if (TextUtils.isEmpty(groupIfName)) { 1889 Log.e(TAG, "Group name required when requesting WPS PBC. Got (" + groupIfName + ")"); 1890 return false; 1891 } 1892 synchronized (mLock) { 1893 if (!checkSupplicantP2pIfaceAndLogFailure("startWpsPbc")) return false; 1894 // Null values should be fine, since bssid can be empty. 1895 byte[] macAddress = null; 1896 try { 1897 macAddress = NativeUtil.macAddressToByteArray(bssid); 1898 } catch (Exception e) { 1899 Log.e(TAG, "Could not parse BSSID.", e); 1900 return false; 1901 } 1902 1903 SupplicantResult<Void> result = new SupplicantResult( 1904 "startWpsPbc(" + groupIfName + ", " + bssid + ")"); 1905 try { 1906 result.setResult(mISupplicantP2pIface.startWpsPbc(groupIfName, macAddress)); 1907 } catch (RemoteException e) { 1908 Log.e(TAG, "ISupplicantP2pIface exception: " + e); 1909 supplicantServiceDiedHandler(); 1910 } 1911 1912 return result.isSuccess(); 1913 } 1914 } 1915 1916 1917 /** 1918 * Initiate WPS Pin Keypad setup. 1919 * 1920 * @param groupIfName Group interface name to use. 1921 * @param pin 8 digit pin to be used. 1922 * @return true, if operation was successful. 1923 */ startWpsPinKeypad(String groupIfName, String pin)1924 public boolean startWpsPinKeypad(String groupIfName, String pin) { 1925 if (TextUtils.isEmpty(groupIfName) || TextUtils.isEmpty(pin)) return false; 1926 synchronized (mLock) { 1927 if (!checkSupplicantP2pIfaceAndLogFailure("startWpsPinKeypad")) return false; 1928 if (groupIfName == null) { 1929 Log.e(TAG, "Group name required when requesting WPS KEYPAD."); 1930 return false; 1931 } 1932 if (pin == null) { 1933 Log.e(TAG, "PIN required when requesting WPS KEYPAD."); 1934 return false; 1935 } 1936 1937 SupplicantResult<Void> result = new SupplicantResult( 1938 "startWpsPinKeypad(" + groupIfName + ", " + pin + ")"); 1939 try { 1940 result.setResult(mISupplicantP2pIface.startWpsPinKeypad(groupIfName, pin)); 1941 } catch (RemoteException e) { 1942 Log.e(TAG, "ISupplicantP2pIface exception: " + e); 1943 supplicantServiceDiedHandler(); 1944 } 1945 1946 return result.isSuccess(); 1947 } 1948 } 1949 1950 1951 /** 1952 * Initiate WPS Pin Display setup. 1953 * 1954 * @param groupIfName Group interface name to use. 1955 * @param bssid BSSID of the AP. Use empty bssid to indicate wildcard. 1956 * @return generated pin if operation was successful, null otherwise. 1957 */ startWpsPinDisplay(String groupIfName, String bssid)1958 public String startWpsPinDisplay(String groupIfName, String bssid) { 1959 if (TextUtils.isEmpty(groupIfName)) return null; 1960 synchronized (mLock) { 1961 if (!checkSupplicantP2pIfaceAndLogFailure("startWpsPinDisplay")) return null; 1962 if (groupIfName == null) { 1963 Log.e(TAG, "Group name required when requesting WPS KEYPAD."); 1964 return null; 1965 } 1966 1967 // Null values should be fine, since bssid can be empty. 1968 byte[] macAddress = null; 1969 try { 1970 macAddress = NativeUtil.macAddressToByteArray(bssid); 1971 } catch (Exception e) { 1972 Log.e(TAG, "Could not parse BSSID.", e); 1973 return null; 1974 } 1975 1976 SupplicantResult<String> result = new SupplicantResult( 1977 "startWpsPinDisplay(" + groupIfName + ", " + bssid + ")"); 1978 try { 1979 mISupplicantP2pIface.startWpsPinDisplay( 1980 groupIfName, macAddress, 1981 (SupplicantStatus status, String generatedPin) -> { 1982 result.setResult(status, generatedPin); 1983 }); 1984 } catch (RemoteException e) { 1985 Log.e(TAG, "ISupplicantP2pIface exception: " + e); 1986 supplicantServiceDiedHandler(); 1987 } 1988 1989 return result.getResult(); 1990 } 1991 } 1992 1993 1994 /** 1995 * Cancel any ongoing WPS operations. 1996 * 1997 * @param groupIfName Group interface name to use. 1998 * @return true, if operation was successful. 1999 */ cancelWps(String groupIfName)2000 public boolean cancelWps(String groupIfName) { 2001 synchronized (mLock) { 2002 if (!checkSupplicantP2pIfaceAndLogFailure("cancelWps")) return false; 2003 if (groupIfName == null) { 2004 Log.e(TAG, "Group name required when requesting WPS KEYPAD."); 2005 return false; 2006 } 2007 2008 SupplicantResult<Void> result = new SupplicantResult( 2009 "cancelWps(" + groupIfName + ")"); 2010 try { 2011 result.setResult(mISupplicantP2pIface.cancelWps(groupIfName)); 2012 } catch (RemoteException e) { 2013 Log.e(TAG, "ISupplicantP2pIface exception: " + e); 2014 supplicantServiceDiedHandler(); 2015 } 2016 2017 return result.isSuccess(); 2018 } 2019 } 2020 2021 2022 /** 2023 * Enable/Disable Wifi Display. 2024 * 2025 * @param enable true to enable, false to disable. 2026 * @return true, if operation was successful. 2027 */ enableWfd(boolean enable)2028 public boolean enableWfd(boolean enable) { 2029 synchronized (mLock) { 2030 if (!checkSupplicantP2pIfaceAndLogFailure("enableWfd")) return false; 2031 2032 SupplicantResult<Void> result = new SupplicantResult( 2033 "enableWfd(" + enable + ")"); 2034 try { 2035 result.setResult(mISupplicantP2pIface.enableWfd(enable)); 2036 } catch (RemoteException e) { 2037 Log.e(TAG, "ISupplicantP2pIface exception: " + e); 2038 supplicantServiceDiedHandler(); 2039 } 2040 2041 return result.isSuccess(); 2042 } 2043 } 2044 2045 2046 /** 2047 * Set Wifi Display device info. 2048 * 2049 * @param info WFD device info as described in section 5.1.2 of WFD technical 2050 * specification v1.0.0. 2051 * @return true, if operation was successful. 2052 */ setWfdDeviceInfo(String info)2053 public boolean setWfdDeviceInfo(String info) { 2054 synchronized (mLock) { 2055 if (!checkSupplicantP2pIfaceAndLogFailure("setWfdDeviceInfo")) return false; 2056 2057 if (info == null) { 2058 Log.e(TAG, "Cannot parse null WFD info string."); 2059 return false; 2060 } 2061 byte[] wfdInfo = null; 2062 try { 2063 wfdInfo = NativeUtil.hexStringToByteArray(info); 2064 } catch (Exception e) { 2065 Log.e(TAG, "Could not parse WFD Device Info string."); 2066 return false; 2067 } 2068 2069 SupplicantResult<Void> result = new SupplicantResult( 2070 "setWfdDeviceInfo(" + info + ")"); 2071 try { 2072 result.setResult(mISupplicantP2pIface.setWfdDeviceInfo(wfdInfo)); 2073 } catch (RemoteException e) { 2074 Log.e(TAG, "ISupplicantP2pIface exception: " + e); 2075 supplicantServiceDiedHandler(); 2076 } 2077 2078 return result.isSuccess(); 2079 } 2080 } 2081 2082 /** 2083 * Remove network with provided id. 2084 * 2085 * @param networkId Id of the network to lookup. 2086 * @return true, if operation was successful. 2087 */ removeNetwork(int networkId)2088 public boolean removeNetwork(int networkId) { 2089 synchronized (mLock) { 2090 if (!checkSupplicantP2pIfaceAndLogFailure("removeNetwork")) return false; 2091 2092 SupplicantResult<Void> result = new SupplicantResult( 2093 "removeNetwork(" + networkId + ")"); 2094 try { 2095 result.setResult(mISupplicantP2pIface.removeNetwork(networkId)); 2096 } catch (RemoteException e) { 2097 Log.e(TAG, "ISupplicantP2pIface exception: " + e); 2098 supplicantServiceDiedHandler(); 2099 } 2100 2101 return result.isSuccess(); 2102 } 2103 } 2104 2105 /** 2106 * List the networks saved in wpa_supplicant. 2107 * 2108 * @return List of network ids. 2109 */ listNetworks()2110 private List<Integer> listNetworks() { 2111 synchronized (mLock) { 2112 if (!checkSupplicantP2pIfaceAndLogFailure("listNetworks")) return null; 2113 SupplicantResult<ArrayList> result = new SupplicantResult("listNetworks()"); 2114 try { 2115 mISupplicantP2pIface.listNetworks( 2116 (SupplicantStatus status, ArrayList<Integer> networkIds) -> { 2117 result.setResult(status, networkIds); 2118 }); 2119 } catch (RemoteException e) { 2120 Log.e(TAG, "ISupplicantP2pIface exception: " + e); 2121 supplicantServiceDiedHandler(); 2122 } 2123 return result.getResult(); 2124 } 2125 } 2126 2127 /** 2128 * Get the supplicant P2p network object for the specified network ID. 2129 * 2130 * @param networkId Id of the network to lookup. 2131 * @return ISupplicantP2pNetwork instance on success, null on failure. 2132 */ getNetwork(int networkId)2133 private ISupplicantP2pNetwork getNetwork(int networkId) { 2134 synchronized (mLock) { 2135 if (!checkSupplicantP2pIfaceAndLogFailure("getNetwork")) return null; 2136 SupplicantResult<ISupplicantNetwork> result = 2137 new SupplicantResult("getNetwork(" + networkId + ")"); 2138 try { 2139 mISupplicantP2pIface.getNetwork( 2140 networkId, 2141 (SupplicantStatus status, ISupplicantNetwork network) -> { 2142 result.setResult(status, network); 2143 }); 2144 } catch (RemoteException e) { 2145 Log.e(TAG, "ISupplicantP2pIface exception: " + e); 2146 supplicantServiceDiedHandler(); 2147 } 2148 if (result.getResult() == null) { 2149 Log.e(TAG, "getNetwork got null network"); 2150 return null; 2151 } 2152 return getP2pNetworkMockable(result.getResult()); 2153 } 2154 } 2155 2156 /** 2157 * Get the persistent group list from wpa_supplicant's p2p mgmt interface 2158 * 2159 * @param groups WifiP2pGroupList to store persistent groups in 2160 * @return true, if list has been modified. 2161 */ loadGroups(WifiP2pGroupList groups)2162 public boolean loadGroups(WifiP2pGroupList groups) { 2163 synchronized (mLock) { 2164 if (!checkSupplicantP2pIfaceAndLogFailure("loadGroups")) return false; 2165 List<Integer> networkIds = listNetworks(); 2166 if (networkIds == null || networkIds.isEmpty()) { 2167 return false; 2168 } 2169 for (Integer networkId : networkIds) { 2170 ISupplicantP2pNetwork network = getNetwork(networkId); 2171 if (network == null) { 2172 Log.e(TAG, "Failed to retrieve network object for " + networkId); 2173 continue; 2174 } 2175 SupplicantResult<Boolean> resultIsCurrent = 2176 new SupplicantResult("isCurrent(" + networkId + ")"); 2177 try { 2178 network.isCurrent( 2179 (SupplicantStatus status, boolean isCurrent) -> { 2180 resultIsCurrent.setResult(status, isCurrent); 2181 }); 2182 } catch (RemoteException e) { 2183 Log.e(TAG, "ISupplicantP2pIface exception: " + e); 2184 supplicantServiceDiedHandler(); 2185 } 2186 /** Skip the current network, if we're somehow getting networks from the p2p GO 2187 interface, instead of p2p mgmt interface*/ 2188 if (!resultIsCurrent.isSuccess() || resultIsCurrent.getResult()) { 2189 Log.i(TAG, "Skipping current network"); 2190 continue; 2191 } 2192 2193 WifiP2pGroup group = new WifiP2pGroup(); 2194 group.setNetworkId(networkId); 2195 2196 // Now get the ssid, bssid and other flags for this network. 2197 SupplicantResult<ArrayList> resultSsid = 2198 new SupplicantResult("getSsid(" + networkId + ")"); 2199 try { 2200 network.getSsid( 2201 (SupplicantStatus status, ArrayList<Byte> ssid) -> { 2202 resultSsid.setResult(status, ssid); 2203 }); 2204 } catch (RemoteException e) { 2205 Log.e(TAG, "ISupplicantP2pIface exception: " + e); 2206 supplicantServiceDiedHandler(); 2207 } 2208 if (resultSsid.isSuccess() && resultSsid.getResult() != null 2209 && !resultSsid.getResult().isEmpty()) { 2210 group.setNetworkName(NativeUtil.removeEnclosingQuotes( 2211 NativeUtil.encodeSsid(resultSsid.getResult()))); 2212 } 2213 2214 SupplicantResult<byte[]> resultBssid = 2215 new SupplicantResult("getBssid(" + networkId + ")"); 2216 try { 2217 network.getBssid( 2218 (SupplicantStatus status, byte[] bssid) -> { 2219 resultBssid.setResult(status, bssid); 2220 }); 2221 } catch (RemoteException e) { 2222 Log.e(TAG, "ISupplicantP2pIface exception: " + e); 2223 supplicantServiceDiedHandler(); 2224 } 2225 if (resultBssid.isSuccess() && !ArrayUtils.isEmpty(resultBssid.getResult())) { 2226 WifiP2pDevice device = new WifiP2pDevice(); 2227 device.deviceAddress = 2228 NativeUtil.macAddressFromByteArray(resultBssid.getResult()); 2229 group.setOwner(device); 2230 } 2231 2232 SupplicantResult<Boolean> resultIsGo = 2233 new SupplicantResult("isGo(" + networkId + ")"); 2234 try { 2235 network.isGo( 2236 (SupplicantStatus status, boolean isGo) -> { 2237 resultIsGo.setResult(status, isGo); 2238 }); 2239 } catch (RemoteException e) { 2240 Log.e(TAG, "ISupplicantP2pIface exception: " + e); 2241 supplicantServiceDiedHandler(); 2242 } 2243 if (resultIsGo.isSuccess()) { 2244 group.setIsGroupOwner(resultIsGo.getResult()); 2245 } 2246 groups.add(group); 2247 } 2248 } 2249 return true; 2250 } 2251 2252 /** 2253 * Set WPS device name. 2254 * 2255 * @param name String to be set. 2256 * @return true if request is sent successfully, false otherwise. 2257 */ setWpsDeviceName(String name)2258 public boolean setWpsDeviceName(String name) { 2259 if (name == null) { 2260 return false; 2261 } 2262 synchronized (mLock) { 2263 if (!checkSupplicantP2pIfaceAndLogFailure("setWpsDeviceName")) return false; 2264 SupplicantResult<Void> result = new SupplicantResult( 2265 "setWpsDeviceName(" + name + ")"); 2266 try { 2267 result.setResult(mISupplicantP2pIface.setWpsDeviceName(name)); 2268 } catch (RemoteException e) { 2269 Log.e(TAG, "ISupplicantP2pIface exception: " + e); 2270 supplicantServiceDiedHandler(); 2271 } 2272 return result.isSuccess(); 2273 } 2274 } 2275 2276 /** 2277 * Set WPS device type. 2278 * 2279 * @param typeStr Type specified as a string. Used format: <categ>-<OUI>-<subcateg> 2280 * @return true if request is sent successfully, false otherwise. 2281 */ setWpsDeviceType(String typeStr)2282 public boolean setWpsDeviceType(String typeStr) { 2283 try { 2284 Matcher match = WPS_DEVICE_TYPE_PATTERN.matcher(typeStr); 2285 if (!match.find() || match.groupCount() != 3) { 2286 Log.e(TAG, "Malformed WPS device type " + typeStr); 2287 return false; 2288 } 2289 short categ = Short.parseShort(match.group(1)); 2290 byte[] oui = NativeUtil.hexStringToByteArray(match.group(2)); 2291 short subCateg = Short.parseShort(match.group(3)); 2292 2293 byte[] bytes = new byte[8]; 2294 ByteBuffer byteBuffer = ByteBuffer.wrap(bytes).order(ByteOrder.BIG_ENDIAN); 2295 byteBuffer.putShort(categ); 2296 byteBuffer.put(oui); 2297 byteBuffer.putShort(subCateg); 2298 synchronized (mLock) { 2299 if (!checkSupplicantP2pIfaceAndLogFailure("setWpsDeviceType")) return false; 2300 SupplicantResult<Void> result = new SupplicantResult( 2301 "setWpsDeviceType(" + typeStr + ")"); 2302 try { 2303 result.setResult(mISupplicantP2pIface.setWpsDeviceType(bytes)); 2304 } catch (RemoteException e) { 2305 Log.e(TAG, "ISupplicantP2pIface exception: " + e); 2306 supplicantServiceDiedHandler(); 2307 } 2308 return result.isSuccess(); 2309 } 2310 } catch (IllegalArgumentException e) { 2311 Log.e(TAG, "Illegal argument " + typeStr, e); 2312 return false; 2313 } 2314 } 2315 2316 /** 2317 * Set WPS config methods 2318 * 2319 * @param configMethodsStr List of config methods. 2320 * @return true if request is sent successfully, false otherwise. 2321 */ setWpsConfigMethods(String configMethodsStr)2322 public boolean setWpsConfigMethods(String configMethodsStr) { 2323 synchronized (mLock) { 2324 if (!checkSupplicantP2pIfaceAndLogFailure("setWpsConfigMethods")) return false; 2325 SupplicantResult<Void> result = 2326 new SupplicantResult("setWpsConfigMethods(" + configMethodsStr + ")"); 2327 short configMethodsMask = 0; 2328 String[] configMethodsStrArr = configMethodsStr.split("\\s+"); 2329 for (int i = 0; i < configMethodsStrArr.length; i++) { 2330 configMethodsMask |= stringToWpsConfigMethod(configMethodsStrArr[i]); 2331 } 2332 try { 2333 result.setResult(mISupplicantP2pIface.setWpsConfigMethods(configMethodsMask)); 2334 } catch (RemoteException e) { 2335 Log.e(TAG, "ISupplicantP2pIface exception: " + e); 2336 supplicantServiceDiedHandler(); 2337 } 2338 return result.isSuccess(); 2339 } 2340 } 2341 2342 /** 2343 * Get NFC handover request message. 2344 * 2345 * @return select message if created successfully, null otherwise. 2346 */ getNfcHandoverRequest()2347 public String getNfcHandoverRequest() { 2348 synchronized (mLock) { 2349 if (!checkSupplicantP2pIfaceAndLogFailure("getNfcHandoverRequest")) return null; 2350 SupplicantResult<ArrayList> result = new SupplicantResult( 2351 "getNfcHandoverRequest()"); 2352 try { 2353 mISupplicantP2pIface.createNfcHandoverRequestMessage( 2354 (SupplicantStatus status, ArrayList<Byte> message) -> { 2355 result.setResult(status, message); 2356 }); 2357 } catch (RemoteException e) { 2358 Log.e(TAG, "ISupplicantP2pIface exception: " + e); 2359 supplicantServiceDiedHandler(); 2360 } 2361 if (!result.isSuccess()) { 2362 return null; 2363 2364 } 2365 return NativeUtil.hexStringFromByteArray( 2366 NativeUtil.byteArrayFromArrayList(result.getResult())); 2367 } 2368 } 2369 2370 /** 2371 * Get NFC handover select message. 2372 * 2373 * @return select message if created successfully, null otherwise. 2374 */ getNfcHandoverSelect()2375 public String getNfcHandoverSelect() { 2376 synchronized (mLock) { 2377 if (!checkSupplicantP2pIfaceAndLogFailure("getNfcHandoverSelect")) return null; 2378 SupplicantResult<ArrayList> result = new SupplicantResult( 2379 "getNfcHandoverSelect()"); 2380 try { 2381 mISupplicantP2pIface.createNfcHandoverSelectMessage( 2382 (SupplicantStatus status, ArrayList<Byte> message) -> { 2383 result.setResult(status, message); 2384 }); 2385 } catch (RemoteException e) { 2386 Log.e(TAG, "ISupplicantP2pIface exception: " + e); 2387 supplicantServiceDiedHandler(); 2388 } 2389 if (!result.isSuccess()) { 2390 return null; 2391 2392 } 2393 return NativeUtil.hexStringFromByteArray( 2394 NativeUtil.byteArrayFromArrayList(result.getResult())); 2395 } 2396 } 2397 2398 /** 2399 * Report NFC handover select message. 2400 * 2401 * @return true if reported successfully, false otherwise. 2402 */ initiatorReportNfcHandover(String selectMessage)2403 public boolean initiatorReportNfcHandover(String selectMessage) { 2404 if (selectMessage == null) return false; 2405 synchronized (mLock) { 2406 if (!checkSupplicantP2pIfaceAndLogFailure("initiatorReportNfcHandover")) return false; 2407 SupplicantResult<Void> result = new SupplicantResult( 2408 "initiatorReportNfcHandover(" + selectMessage + ")"); 2409 try { 2410 result.setResult(mISupplicantP2pIface.reportNfcHandoverInitiation( 2411 NativeUtil.byteArrayToArrayList(NativeUtil.hexStringToByteArray( 2412 selectMessage)))); 2413 } catch (RemoteException e) { 2414 Log.e(TAG, "ISupplicantP2pIface exception: " + e); 2415 supplicantServiceDiedHandler(); 2416 } catch (IllegalArgumentException e) { 2417 Log.e(TAG, "Illegal argument " + selectMessage, e); 2418 return false; 2419 } 2420 return result.isSuccess(); 2421 } 2422 } 2423 2424 /** 2425 * Report NFC handover request message. 2426 * 2427 * @return true if reported successfully, false otherwise. 2428 */ responderReportNfcHandover(String requestMessage)2429 public boolean responderReportNfcHandover(String requestMessage) { 2430 if (requestMessage == null) return false; 2431 synchronized (mLock) { 2432 if (!checkSupplicantP2pIfaceAndLogFailure("responderReportNfcHandover")) return false; 2433 SupplicantResult<Void> result = new SupplicantResult( 2434 "responderReportNfcHandover(" + requestMessage + ")"); 2435 try { 2436 result.setResult(mISupplicantP2pIface.reportNfcHandoverResponse( 2437 NativeUtil.byteArrayToArrayList(NativeUtil.hexStringToByteArray( 2438 requestMessage)))); 2439 } catch (RemoteException e) { 2440 Log.e(TAG, "ISupplicantP2pIface exception: " + e); 2441 supplicantServiceDiedHandler(); 2442 } catch (IllegalArgumentException e) { 2443 Log.e(TAG, "Illegal argument " + requestMessage, e); 2444 return false; 2445 } 2446 return result.isSuccess(); 2447 } 2448 } 2449 2450 /** 2451 * Set the client list for the provided network. 2452 * 2453 * @param networkId Id of the network. 2454 * @param clientListStr Space separated list of clients. 2455 * @return true, if operation was successful. 2456 */ setClientList(int networkId, String clientListStr)2457 public boolean setClientList(int networkId, String clientListStr) { 2458 synchronized (mLock) { 2459 if (!checkSupplicantP2pIfaceAndLogFailure("setClientList")) return false; 2460 if (TextUtils.isEmpty(clientListStr)) { 2461 Log.e(TAG, "Invalid client list"); 2462 return false; 2463 } 2464 ISupplicantP2pNetwork network = getNetwork(networkId); 2465 if (network == null) { 2466 Log.e(TAG, "Invalid network id "); 2467 return false; 2468 } 2469 SupplicantResult<Void> result = new SupplicantResult( 2470 "setClientList(" + networkId + ", " + clientListStr + ")"); 2471 try { 2472 ArrayList<byte[]> clients = new ArrayList<>(); 2473 for (String clientStr : Arrays.asList(clientListStr.split("\\s+"))) { 2474 clients.add(NativeUtil.macAddressToByteArray(clientStr)); 2475 } 2476 result.setResult(network.setClientList(clients)); 2477 } catch (RemoteException e) { 2478 Log.e(TAG, "ISupplicantP2pIface exception: " + e); 2479 supplicantServiceDiedHandler(); 2480 } catch (IllegalArgumentException e) { 2481 Log.e(TAG, "Illegal argument " + clientListStr, e); 2482 return false; 2483 } 2484 return result.isSuccess(); 2485 } 2486 } 2487 2488 /** 2489 * Set the client list for the provided network. 2490 * 2491 * @param networkId Id of the network. 2492 * @return Space separated list of clients if successful, null otherwise. 2493 */ getClientList(int networkId)2494 public String getClientList(int networkId) { 2495 synchronized (mLock) { 2496 if (!checkSupplicantP2pIfaceAndLogFailure("getClientList")) return null; 2497 ISupplicantP2pNetwork network = getNetwork(networkId); 2498 if (network == null) { 2499 Log.e(TAG, "Invalid network id "); 2500 return null; 2501 } 2502 SupplicantResult<ArrayList> result = new SupplicantResult( 2503 "getClientList(" + networkId + ")"); 2504 try { 2505 network.getClientList( 2506 (SupplicantStatus status, ArrayList<byte[]> clients) -> { 2507 result.setResult(status, clients); 2508 }); 2509 } catch (RemoteException e) { 2510 Log.e(TAG, "ISupplicantP2pIface exception: " + e); 2511 supplicantServiceDiedHandler(); 2512 } 2513 if (!result.isSuccess()) { 2514 return null; 2515 } 2516 ArrayList<byte[]> clients = result.getResult(); 2517 return clients.stream() 2518 .map(NativeUtil::macAddressFromByteArray) 2519 .collect(Collectors.joining(" ")); 2520 } 2521 } 2522 2523 /** 2524 * Persist the current configurations to disk. 2525 * 2526 * @return true, if operation was successful. 2527 */ saveConfig()2528 public boolean saveConfig() { 2529 synchronized (mLock) { 2530 if (!checkSupplicantP2pIfaceAndLogFailure("saveConfig")) return false; 2531 SupplicantResult<Void> result = new SupplicantResult("saveConfig()"); 2532 try { 2533 result.setResult(mISupplicantP2pIface.saveConfig()); 2534 } catch (RemoteException e) { 2535 Log.e(TAG, "ISupplicantP2pIface exception: " + e); 2536 supplicantServiceDiedHandler(); 2537 } 2538 return result.isSuccess(); 2539 } 2540 } 2541 2542 2543 /** 2544 * Enable/Disable P2P MAC randomization. 2545 * 2546 * @param enable true to enable, false to disable. 2547 * @return true, if operation was successful. 2548 */ setMacRandomization(boolean enable)2549 public boolean setMacRandomization(boolean enable) { 2550 synchronized (mLock) { 2551 android.hardware.wifi.supplicant.V1_2.ISupplicantP2pIface ifaceV12 = 2552 getSupplicantP2pIfaceAndLogFailureV1_2("setMacRandomization"); 2553 if (ifaceV12 == null) return false; 2554 2555 SupplicantResult<Void> result = new SupplicantResult( 2556 "setMacRandomization(" + enable + ")"); 2557 try { 2558 result.setResult(ifaceV12.setMacRandomization(enable)); 2559 } catch (RemoteException e) { 2560 Log.e(TAG, "ISupplicantP2pIface exception: " + e); 2561 supplicantServiceDiedHandler(); 2562 } 2563 2564 return result.isSuccess(); 2565 } 2566 } 2567 2568 /** 2569 * Set Wifi Display R2 device info. 2570 * 2571 * @param info WFD R2 device info as described in section 5.1.12 of WFD technical 2572 * specification v2.1. 2573 * @return true, if operation was successful. 2574 */ setWfdR2DeviceInfo(String info)2575 public boolean setWfdR2DeviceInfo(String info) { 2576 synchronized (mLock) { 2577 if (info == null) { 2578 Log.e(TAG, "Cannot parse null WFD info string."); 2579 return false; 2580 } 2581 byte[] wfdR2Info = null; 2582 try { 2583 wfdR2Info = NativeUtil.hexStringToByteArray(info); 2584 } catch (Exception e) { 2585 Log.e(TAG, "Could not parse WFD R2 Device Info string."); 2586 return false; 2587 } 2588 2589 android.hardware.wifi.supplicant.V1_4.ISupplicantP2pIface ifaceV14 = 2590 getSupplicantP2pIfaceAndLogFailureV1_4("setWfdR2DeviceInfo"); 2591 if (ifaceV14 == null) return false; 2592 SupplicantResultV1_4<Void> result = new SupplicantResultV1_4( 2593 "setWfdR2DeviceInfo(" + info + ")"); 2594 try { 2595 result.setResult(ifaceV14.setWfdR2DeviceInfo(wfdR2Info)); 2596 } catch (RemoteException e) { 2597 Log.e(TAG, "ISupplicantP2pIface exception: " + e); 2598 supplicantServiceDiedHandler(); 2599 } 2600 return result.isSuccess(); 2601 } 2602 } 2603 2604 /** 2605 * Remove the client with the MAC address from the group. 2606 * 2607 * @param peerAddress Mac address of the client. 2608 * @param isLegacyClient Indicate if client is a legacy client or not. 2609 * @return true if success 2610 */ removeClient(String peerAddress, boolean isLegacyClient)2611 public boolean removeClient(String peerAddress, boolean isLegacyClient) { 2612 Log.d(TAG, "removeClient() is not supported."); 2613 return false; 2614 } 2615 2616 /** 2617 * Set vendor-specific information elements to wpa_supplicant. 2618 * 2619 * @param vendorElements The list of vendor-specific information elements. 2620 * 2621 * @return boolean The value indicating whether operation was successful. 2622 */ setVendorElements(Set<ScanResult.InformationElement> vendorElements)2623 public boolean setVendorElements(Set<ScanResult.InformationElement> vendorElements) { 2624 Log.d(TAG, "setVendorElements() is not supported."); 2625 return false; 2626 } 2627 2628 /** 2629 * Configure the IP addresses in supplicant for P2P GO to provide the IP address to 2630 * client in EAPOL handshake. Refer Wi-Fi P2P Technical Specification v1.7 - Section 4.2.8 2631 * IP Address Allocation in EAPOL-Key Frames (4-Way Handshake) for more details. 2632 * The IP addresses are IPV4 addresses and higher-order address bytes are in the 2633 * lower-order int bytes (e.g. 1.2.3.4 is represented as 0x04030201) 2634 * 2635 * @param ipAddressGo The P2P Group Owner IP address. 2636 * @param ipAddressMask The P2P Group owner subnet mask. 2637 * @param ipAddressStart The starting address in the IP address pool. 2638 * @param ipAddressEnd The ending address in the IP address pool. 2639 * @return boolean value indicating whether operation was successful. 2640 */ configureEapolIpAddressAllocationParams(int ipAddressGo, int ipAddressMask, int ipAddressStart, int ipAddressEnd)2641 public boolean configureEapolIpAddressAllocationParams(int ipAddressGo, int ipAddressMask, 2642 int ipAddressStart, int ipAddressEnd) { 2643 Log.d(TAG, "configureEapolIpAddressAllocationParams() is not supported."); 2644 return false; 2645 } 2646 2647 /** 2648 * Converts the Wps config method string to the equivalent enum value. 2649 */ stringToWpsConfigMethod(String configMethod)2650 private static short stringToWpsConfigMethod(String configMethod) { 2651 switch (configMethod) { 2652 case "usba": 2653 return WpsConfigMethods.USBA; 2654 case "ethernet": 2655 return WpsConfigMethods.ETHERNET; 2656 case "label": 2657 return WpsConfigMethods.LABEL; 2658 case "display": 2659 return WpsConfigMethods.DISPLAY; 2660 case "int_nfc_token": 2661 return WpsConfigMethods.INT_NFC_TOKEN; 2662 case "ext_nfc_token": 2663 return WpsConfigMethods.EXT_NFC_TOKEN; 2664 case "nfc_interface": 2665 return WpsConfigMethods.NFC_INTERFACE; 2666 case "push_button": 2667 return WpsConfigMethods.PUSHBUTTON; 2668 case "keypad": 2669 return WpsConfigMethods.KEYPAD; 2670 case "virtual_push_button": 2671 return WpsConfigMethods.VIRT_PUSHBUTTON; 2672 case "physical_push_button": 2673 return WpsConfigMethods.PHY_PUSHBUTTON; 2674 case "p2ps": 2675 return WpsConfigMethods.P2PS; 2676 case "virtual_display": 2677 return WpsConfigMethods.VIRT_DISPLAY; 2678 case "physical_display": 2679 return WpsConfigMethods.PHY_DISPLAY; 2680 default: 2681 throw new IllegalArgumentException( 2682 "Invalid WPS config method: " + configMethod); 2683 } 2684 } 2685 2686 /** Container class allowing propagation of status and/or value 2687 * from callbacks. 2688 * 2689 * Primary purpose is to allow callback lambdas to provide results 2690 * to parent methods. 2691 */ 2692 private static class SupplicantResultBase<S, E> { 2693 private String mMethodName; 2694 private S mStatus; 2695 private E mValue; 2696 SupplicantResultBase(String methodName)2697 SupplicantResultBase(String methodName) { 2698 mMethodName = methodName; 2699 mStatus = null; 2700 mValue = null; 2701 logd("entering " + mMethodName); 2702 } 2703 setResult(S status, E value)2704 public void setResult(S status, E value) { 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 + " with result = " + value); 2711 mStatus = status; 2712 mValue = value; 2713 } 2714 setResult(S status)2715 public void setResult(S status) { 2716 if (status == null) { 2717 logw(mMethodName + " failed: no status code returned."); 2718 } else { 2719 logCompletion(mMethodName, getCode(status), getDebugMessage(status)); 2720 } 2721 logd("leaving " + mMethodName); 2722 mStatus = status; 2723 } 2724 isSuccess()2725 public boolean isSuccess() { 2726 return (mStatus != null 2727 && (getCode(mStatus) == SupplicantStatusCode.SUCCESS 2728 || getCode(mStatus) == SupplicantStatusCode.FAILURE_IFACE_EXISTS)); 2729 } 2730 getResult()2731 public E getResult() { 2732 return (isSuccess() ? mValue : null); 2733 } 2734 getCode(Object obj)2735 protected int getCode(Object obj) { 2736 SupplicantStatus status = (SupplicantStatus) obj; 2737 return status.code; 2738 } 2739 getDebugMessage(Object obj)2740 protected String getDebugMessage(Object obj) { 2741 SupplicantStatus status = (SupplicantStatus) obj; 2742 return status.debugMessage; 2743 } 2744 } 2745 2746 private static class SupplicantResult<E> 2747 extends SupplicantResultBase<SupplicantStatus, E> { SupplicantResult(String iface)2748 SupplicantResult(String iface) { 2749 super(iface); 2750 } 2751 } 2752 2753 private static class SupplicantResultV1_4<E> 2754 extends SupplicantResultBase< 2755 android.hardware.wifi.supplicant.V1_4.SupplicantStatus, E> { SupplicantResultV1_4(String iface)2756 SupplicantResultV1_4(String iface) { 2757 super(iface); 2758 } 2759 getCode(Object obj)2760 protected int getCode(Object obj) { 2761 android.hardware.wifi.supplicant.V1_4.SupplicantStatus status = 2762 (android.hardware.wifi.supplicant.V1_4.SupplicantStatus) obj; 2763 return status.code; 2764 } 2765 getDebugMessage(Object obj)2766 protected String getDebugMessage(Object obj) { 2767 android.hardware.wifi.supplicant.V1_4.SupplicantStatus status = 2768 (android.hardware.wifi.supplicant.V1_4.SupplicantStatus) obj; 2769 return status.debugMessage; 2770 } 2771 } 2772 } 2773