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