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