1 /* 2 * Copyright (C) 2016 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17 package android.net.wifi.aware; 18 19 import static android.Manifest.permission.ACCESS_FINE_LOCATION; 20 import static android.Manifest.permission.ACCESS_WIFI_STATE; 21 import static android.Manifest.permission.CHANGE_WIFI_STATE; 22 import static android.Manifest.permission.MANAGE_WIFI_NETWORK_SELECTION; 23 import static android.Manifest.permission.NEARBY_WIFI_DEVICES; 24 import static android.Manifest.permission.OVERRIDE_WIFI_CONFIG; 25 import static android.net.wifi.ScanResult.WIFI_BAND_24_GHZ; 26 import static android.net.wifi.ScanResult.WIFI_BAND_5_GHZ; 27 28 import android.annotation.CallbackExecutor; 29 import android.annotation.FlaggedApi; 30 import android.annotation.IntDef; 31 import android.annotation.NonNull; 32 import android.annotation.Nullable; 33 import android.annotation.RequiresPermission; 34 import android.annotation.SdkConstant; 35 import android.annotation.SdkConstant.SdkConstantType; 36 import android.annotation.SystemApi; 37 import android.annotation.SystemService; 38 import android.content.Context; 39 import android.net.ConnectivityManager; 40 import android.net.MacAddress; 41 import android.net.NetworkRequest; 42 import android.net.NetworkSpecifier; 43 import android.net.wifi.IBooleanListener; 44 import android.net.wifi.IIntegerListener; 45 import android.net.wifi.IListListener; 46 import android.net.wifi.OuiKeyedData; 47 import android.net.wifi.WifiManager; 48 import android.net.wifi.util.HexEncoding; 49 import android.os.Binder; 50 import android.os.Build; 51 import android.os.Bundle; 52 import android.os.Handler; 53 import android.os.Looper; 54 import android.os.RemoteException; 55 import android.util.Log; 56 57 import androidx.annotation.RequiresApi; 58 59 import com.android.modules.utils.HandlerExecutor; 60 import com.android.modules.utils.build.SdkLevel; 61 import com.android.wifi.flags.Flags; 62 63 import java.lang.annotation.Retention; 64 import java.lang.annotation.RetentionPolicy; 65 import java.lang.ref.WeakReference; 66 import java.nio.BufferOverflowException; 67 import java.util.Collections; 68 import java.util.List; 69 import java.util.Objects; 70 import java.util.concurrent.Executor; 71 import java.util.function.Consumer; 72 73 /** 74 * This class provides the primary API for managing Wi-Fi Aware operations: 75 * discovery and peer-to-peer data connections. 76 * <p> 77 * The class provides access to: 78 * <ul> 79 * <li>Initialize a Aware cluster (peer-to-peer synchronization). Refer to 80 * {@link #attach(AttachCallback, Handler)}. 81 * <li>Create discovery sessions (publish or subscribe sessions). Refer to 82 * {@link WifiAwareSession#publish(PublishConfig, DiscoverySessionCallback, Handler)} and 83 * {@link WifiAwareSession#subscribe(SubscribeConfig, DiscoverySessionCallback, Handler)}. 84 * <li>Create a Aware network specifier to be used with 85 * {@link ConnectivityManager#requestNetwork(NetworkRequest, ConnectivityManager.NetworkCallback)} 86 * to set-up a Aware connection with a peer. Refer to {@link WifiAwareNetworkSpecifier.Builder}. 87 * </ul> 88 * <p> 89 * Aware may not be usable when Wi-Fi is disabled (and other conditions). To validate that 90 * the functionality is available use the {@link #isAvailable()} function. To track 91 * changes in Aware usability register for the {@link #ACTION_WIFI_AWARE_STATE_CHANGED} 92 * broadcast. Note that this broadcast is not sticky - you should register for it and then 93 * check the above API to avoid a race condition. 94 * <p> 95 * An application must use {@link #attach(AttachCallback, Handler)} to initialize a 96 * Aware cluster - before making any other Aware operation. Aware cluster membership is a 97 * device-wide operation - the API guarantees that the device is in a cluster or joins a 98 * Aware cluster (or starts one if none can be found). Information about attach success (or 99 * failure) are returned in callbacks of {@link AttachCallback}. Proceed with Aware 100 * discovery or connection setup only after receiving confirmation that Aware attach 101 * succeeded - {@link AttachCallback#onAttached(WifiAwareSession)}. When an 102 * application is finished using Aware it <b>must</b> use the 103 * {@link WifiAwareSession#close()} API to indicate to the Aware service that the device 104 * may detach from the Aware cluster. The device will actually disable Aware once the last 105 * application detaches. 106 * <p> 107 * Once a Aware attach is confirmed use the 108 * {@link WifiAwareSession#publish(PublishConfig, DiscoverySessionCallback, Handler)} 109 * or 110 * {@link WifiAwareSession#subscribe(SubscribeConfig, DiscoverySessionCallback, 111 * Handler)} to create publish or subscribe Aware discovery sessions. Events are called on the 112 * provided callback object {@link DiscoverySessionCallback}. Specifically, the 113 * {@link DiscoverySessionCallback#onPublishStarted(PublishDiscoverySession)} 114 * and 115 * {@link DiscoverySessionCallback#onSubscribeStarted( 116 *SubscribeDiscoverySession)} 117 * return {@link PublishDiscoverySession} and 118 * {@link SubscribeDiscoverySession} 119 * objects respectively on which additional session operations can be performed, e.g. updating 120 * the session {@link PublishDiscoverySession#updatePublish(PublishConfig)} and 121 * {@link SubscribeDiscoverySession#updateSubscribe(SubscribeConfig)}. Sessions can 122 * also be used to send messages using the 123 * {@link DiscoverySession#sendMessage(PeerHandle, int, byte[])} APIs. When an 124 * application is finished with a discovery session it <b>must</b> terminate it using the 125 * {@link DiscoverySession#close()} API. 126 * <p> 127 * Creating connections between Aware devices is managed by the standard 128 * {@link ConnectivityManager#requestNetwork(NetworkRequest, 129 * ConnectivityManager.NetworkCallback)}. 130 * The {@link NetworkRequest} object should be constructed with: 131 * <ul> 132 * <li>{@link NetworkRequest.Builder#addTransportType(int)} of 133 * {@link android.net.NetworkCapabilities#TRANSPORT_WIFI_AWARE}. 134 * <li>{@link NetworkRequest.Builder#setNetworkSpecifier(String)} using 135 * {@link WifiAwareNetworkSpecifier.Builder}. 136 * </ul> 137 */ 138 @SystemService(Context.WIFI_AWARE_SERVICE) 139 public class WifiAwareManager { 140 private static final String TAG = "WifiAwareManager"; 141 private static final boolean DBG = false; 142 private static final boolean VDBG = false; // STOPSHIP if true 143 144 /** 145 * Broadcast intent action to indicate that the state of Wi-Fi Aware availability has changed 146 * and all active Aware sessions are no longer usable. Use the {@link #isAvailable()} to query 147 * the current status. 148 * This broadcast is <b>not</b> sticky, use the {@link #isAvailable()} API after registering 149 * the broadcast to check the current state of Wi-Fi Aware. 150 * <p>Note: The broadcast is only delivered to registered receivers - no manifest registered 151 * components will be launched. 152 */ 153 @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION) 154 public static final String ACTION_WIFI_AWARE_STATE_CHANGED = 155 "android.net.wifi.aware.action.WIFI_AWARE_STATE_CHANGED"; 156 /** 157 * Intent broadcast sent whenever Wi-Fi Aware resource availability has changed. The resources 158 * are attached with the {@link #EXTRA_AWARE_RESOURCES} extra. The resources can also be 159 * obtained using the {@link #getAvailableAwareResources()} method. To receive this broadcast, 160 * apps must hold {@link android.Manifest.permission#ACCESS_WIFI_STATE}. 161 * <p>Note: The broadcast is only delivered to registered receivers - no manifest registered 162 * components will be launched. 163 */ 164 @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION) 165 @RequiresApi(Build.VERSION_CODES.TIRAMISU) 166 @RequiresPermission(ACCESS_WIFI_STATE) 167 public static final String ACTION_WIFI_AWARE_RESOURCE_CHANGED = 168 "android.net.wifi.aware.action.WIFI_AWARE_RESOURCE_CHANGED"; 169 170 /** 171 * Sent as a part of {@link #ACTION_WIFI_AWARE_RESOURCE_CHANGED} that contains an instance of 172 * {@link AwareResources} representing the current Wi-Fi Aware resources. 173 */ 174 public static final String EXTRA_AWARE_RESOURCES = 175 "android.net.wifi.aware.extra.AWARE_RESOURCES"; 176 177 /** @hide */ 178 @IntDef({ 179 WIFI_AWARE_DATA_PATH_ROLE_INITIATOR, WIFI_AWARE_DATA_PATH_ROLE_RESPONDER}) 180 @Retention(RetentionPolicy.SOURCE) 181 public @interface DataPathRole { 182 } 183 184 /** 185 * Connection creation role is that of INITIATOR. Used to create a network specifier string 186 * when requesting a Aware network. 187 * 188 * @see WifiAwareSession#createNetworkSpecifierOpen(int, byte[]) 189 * @see WifiAwareSession#createNetworkSpecifierPassphrase(int, byte[], String) 190 */ 191 public static final int WIFI_AWARE_DATA_PATH_ROLE_INITIATOR = 0; 192 193 /** 194 * Connection creation role is that of RESPONDER. Used to create a network specifier string 195 * when requesting a Aware network. 196 * 197 * @see WifiAwareSession#createNetworkSpecifierOpen(int, byte[]) 198 * @see WifiAwareSession#createNetworkSpecifierPassphrase(int, byte[], String) 199 */ 200 public static final int WIFI_AWARE_DATA_PATH_ROLE_RESPONDER = 1; 201 202 /** @hide */ 203 @IntDef({ 204 WIFI_AWARE_DISCOVERY_LOST_REASON_UNKNOWN, 205 WIFI_AWARE_DISCOVERY_LOST_REASON_PEER_NOT_VISIBLE}) 206 @Retention(RetentionPolicy.SOURCE) 207 public @interface DiscoveryLostReasonCode { 208 } 209 210 /** 211 * Reason code provided in {@link DiscoverySessionCallback#onServiceLost(PeerHandle, int)} 212 * indicating that the service was lost for unknown reason. 213 */ 214 public static final int WIFI_AWARE_DISCOVERY_LOST_REASON_UNKNOWN = 0; 215 216 /** 217 * Reason code provided in {@link DiscoverySessionCallback#onServiceLost(PeerHandle, int)} 218 * indicating that the service advertised by the peer is no longer visible. This may be because 219 * the peer is out of range or because the peer stopped advertising this service. 220 */ 221 public static final int WIFI_AWARE_DISCOVERY_LOST_REASON_PEER_NOT_VISIBLE = 1; 222 223 /** @hide */ 224 @IntDef({ 225 WIFI_AWARE_SUSPEND_REDUNDANT_REQUEST, 226 WIFI_AWARE_SUSPEND_INVALID_SESSION, 227 WIFI_AWARE_SUSPEND_CANNOT_SUSPEND, 228 WIFI_AWARE_SUSPEND_INTERNAL_ERROR}) 229 @Retention(RetentionPolicy.SOURCE) 230 public @interface SessionSuspensionFailedReasonCode {} 231 232 /** 233 * Reason code provided in {@link DiscoverySessionCallback#onSessionSuspendFailed(int)} when the 234 * session is already suspended. 235 * @hide 236 */ 237 @SystemApi 238 public static final int WIFI_AWARE_SUSPEND_REDUNDANT_REQUEST = 0; 239 240 /** 241 * Reason code provided in {@link DiscoverySessionCallback#onSessionSuspendFailed(int)} when the 242 * specified session does not support suspension. 243 @hide 244 */ 245 @SystemApi 246 public static final int WIFI_AWARE_SUSPEND_INVALID_SESSION = 1; 247 248 /** 249 * Reason code provided in {@link DiscoverySessionCallback#onSessionSuspendFailed(int)} when the 250 * session could not be suspended due to more than one app using it. 251 @hide 252 */ 253 @SystemApi 254 public static final int WIFI_AWARE_SUSPEND_CANNOT_SUSPEND = 2; 255 256 /** 257 * Reason code provided in {@link DiscoverySessionCallback#onSessionSuspendFailed(int)} when an 258 * error is encountered with the request. 259 @hide 260 */ 261 @SystemApi 262 public static final int WIFI_AWARE_SUSPEND_INTERNAL_ERROR = 3; 263 264 /** @hide */ 265 @IntDef({ 266 WIFI_AWARE_RESUME_REDUNDANT_REQUEST, 267 WIFI_AWARE_RESUME_INVALID_SESSION, 268 WIFI_AWARE_RESUME_INTERNAL_ERROR}) 269 @Retention(RetentionPolicy.SOURCE) 270 public @interface SessionResumptionFailedReasonCode {} 271 272 /** 273 * Reason code provided in {@link DiscoverySessionCallback#onSessionResumeFailed(int)} when the 274 * session is not suspended. 275 * @hide 276 */ 277 @SystemApi 278 public static final int WIFI_AWARE_RESUME_REDUNDANT_REQUEST = 0; 279 280 /** 281 * Reason code provided in {@link DiscoverySessionCallback#onSessionResumeFailed(int)} when the 282 * specified session does not support suspension. 283 @hide 284 */ 285 @SystemApi 286 public static final int WIFI_AWARE_RESUME_INVALID_SESSION = 1; 287 288 /** 289 * Reason code provided in {@link DiscoverySessionCallback#onSessionResumeFailed(int)} when an 290 * error is encountered with the request. 291 @hide 292 */ 293 @SystemApi 294 public static final int WIFI_AWARE_RESUME_INTERNAL_ERROR = 2; 295 296 /** @hide */ 297 @Retention(RetentionPolicy.SOURCE) 298 @IntDef( 299 prefix = {"WIFI_BAND_"}, 300 value = {WIFI_BAND_24_GHZ, WIFI_BAND_5_GHZ}) 301 public @interface InstantModeBand {}; 302 303 private final Context mContext; 304 private final IWifiAwareManager mService; 305 306 private final Object mLock = new Object(); // lock access to the following vars 307 308 /** @hide */ WifiAwareManager(@onNull Context context, @NonNull IWifiAwareManager service)309 public WifiAwareManager(@NonNull Context context, @NonNull IWifiAwareManager service) { 310 mContext = context; 311 mService = service; 312 } 313 314 /** 315 * Returns the current status of Aware API: whether or not Aware is available. To track 316 * changes in the state of Aware API register for the 317 * {@link #ACTION_WIFI_AWARE_STATE_CHANGED} broadcast. 318 * 319 * @return A boolean indicating whether the app can use the Aware API at this time (true) or 320 * not (false). 321 */ 322 @RequiresPermission(ACCESS_WIFI_STATE) isAvailable()323 public boolean isAvailable() { 324 try { 325 return mService.isUsageEnabled(); 326 } catch (RemoteException e) { 327 throw e.rethrowFromSystemServer(); 328 } 329 } 330 331 /** 332 * Return the current status of the Aware service: whether or not the device is already attached 333 * to an Aware cluster. To attach to an Aware cluster, please use 334 * {@link #attach(AttachCallback, Handler)} or 335 * {@link #attach(AttachCallback, IdentityChangedListener, Handler)}. 336 * @return A boolean indicating whether the device is attached to a cluster at this time (true) 337 * or not (false). 338 */ 339 @RequiresPermission(ACCESS_WIFI_STATE) isDeviceAttached()340 public boolean isDeviceAttached() { 341 try { 342 return mService.isDeviceAttached(); 343 } catch (RemoteException e) { 344 throw e.rethrowFromSystemServer(); 345 } 346 } 347 348 /** 349 * Return the device support for setting a channel requirement in a data-path request. If true 350 * the channel set by 351 * {@link WifiAwareNetworkSpecifier.Builder#setChannelFrequencyMhz(int, boolean)} will be 352 * honored, otherwise it will be ignored. 353 * @return True is the device support set channel on data-path request, false otherwise. 354 */ 355 @RequiresPermission(ACCESS_WIFI_STATE) isSetChannelOnDataPathSupported()356 public boolean isSetChannelOnDataPathSupported() { 357 try { 358 return mService.isSetChannelOnDataPathSupported(); 359 } catch (RemoteException e) { 360 throw e.rethrowFromSystemServer(); 361 } 362 } 363 364 /** 365 * Enable the Wifi Aware Instant communication mode. If the device doesn't support this feature 366 * calling this API will result no action. 367 * <p> 368 * Note: before {@link android.os.Build.VERSION_CODES#TIRAMISU}, only system app can use this 369 * API. Start with {@link android.os.Build.VERSION_CODES#TIRAMISU} apps hold 370 * {@link android.Manifest.permission#OVERRIDE_WIFI_CONFIG} are allowed to use it. 371 * 372 * @see Characteristics#isInstantCommunicationModeSupported() 373 * @param enable true for enable, false otherwise. 374 * @hide 375 */ 376 @SystemApi 377 @RequiresApi(Build.VERSION_CODES.S) 378 @RequiresPermission(allOf = {CHANGE_WIFI_STATE, OVERRIDE_WIFI_CONFIG}) enableInstantCommunicationMode(boolean enable)379 public void enableInstantCommunicationMode(boolean enable) { 380 if (!SdkLevel.isAtLeastS()) { 381 throw new UnsupportedOperationException(); 382 } 383 try { 384 mService.enableInstantCommunicationMode(mContext.getOpPackageName(), enable); 385 } catch (RemoteException e) { 386 throw e.rethrowFromSystemServer(); 387 } 388 } 389 390 /** 391 * Return the current status of the Wifi Aware instant communication mode. 392 * If the device doesn't support this feature, return will always be false. 393 * @see Characteristics#isInstantCommunicationModeSupported() 394 * @return true if it is enabled, false otherwise. 395 */ 396 @RequiresApi(Build.VERSION_CODES.S) 397 @RequiresPermission(ACCESS_WIFI_STATE) isInstantCommunicationModeEnabled()398 public boolean isInstantCommunicationModeEnabled() { 399 if (!SdkLevel.isAtLeastS()) { 400 throw new UnsupportedOperationException(); 401 } 402 try { 403 return mService.isInstantCommunicationModeEnabled(); 404 } catch (RemoteException e) { 405 throw e.rethrowFromSystemServer(); 406 } 407 } 408 409 /** 410 * Returns the characteristics of the Wi-Fi Aware interface: a set of parameters which specify 411 * limitations on configurations, e.g. the maximum service name length. 412 * <p> 413 * May return {@code null} if the Wi-Fi Aware service is not initialized. Use 414 * {@link #attach(AttachCallback, Handler)} or 415 * {@link #attach(AttachCallback, IdentityChangedListener, Handler)} to initialize the Wi-Fi 416 * Aware service. 417 * 418 * @return An object specifying configuration limitations of Aware. 419 */ 420 @RequiresPermission(ACCESS_WIFI_STATE) getCharacteristics()421 public @Nullable Characteristics getCharacteristics() { 422 try { 423 return mService.getCharacteristics(); 424 } catch (RemoteException e) { 425 throw e.rethrowFromSystemServer(); 426 } 427 } 428 429 /** 430 * Return the available resources of the Wi-Fi aware service: a set of parameters which specify 431 * limitations on service usage, e.g the number of data-paths which could be created. 432 * <p> 433 * May return {@code null} if the Wi-Fi Aware service is not initialized. Use 434 * {@link #attach(AttachCallback, Handler)} or 435 * {@link #attach(AttachCallback, IdentityChangedListener, Handler)} to initialize the Wi-Fi 436 * Aware service. 437 * 438 * @return An object specifying the currently available resource of the Wi-Fi Aware service. 439 */ 440 @RequiresPermission(ACCESS_WIFI_STATE) getAvailableAwareResources()441 public @Nullable AwareResources getAvailableAwareResources() { 442 try { 443 return mService.getAvailableAwareResources(); 444 } catch (RemoteException e) { 445 throw e.rethrowFromSystemServer(); 446 } 447 } 448 449 /** 450 * Attach to the Wi-Fi Aware service - enabling the application to create discovery sessions or 451 * create connections to peers. The device will attach to an existing cluster if it can find 452 * one or create a new cluster (if it is the first to enable Aware in its vicinity). Results 453 * (e.g. successful attach to a cluster) are provided to the {@code attachCallback} object. 454 * An application <b>must</b> call {@link WifiAwareSession#close()} when done with the 455 * Wi-Fi Aware object. 456 * <p> 457 * Note: a Aware cluster is a shared resource - if the device is already attached to a cluster 458 * then this function will simply indicate success immediately using the same {@code 459 * attachCallback}. 460 * 461 * @param attachCallback A callback for attach events, extended from 462 * {@link AttachCallback}. 463 * @param handler The Handler on whose thread to execute the callbacks of the {@code 464 * attachCallback} object. If a null is provided then the application's main thread will be 465 * used. 466 */ 467 @RequiresPermission(allOf = { 468 ACCESS_WIFI_STATE, 469 CHANGE_WIFI_STATE 470 }) attach(@onNull AttachCallback attachCallback, @Nullable Handler handler)471 public void attach(@NonNull AttachCallback attachCallback, @Nullable Handler handler) { 472 attach(handler, null, attachCallback, null, false, null); 473 } 474 475 /** 476 * Attach to the Wi-Fi Aware service - enabling the application to create discovery sessions or 477 * create connections to peers. The device will attach to an existing cluster if it can find 478 * one or create a new cluster (if it is the first to enable Aware in its vicinity). Results 479 * (e.g. successful attach to a cluster) are provided to the {@code attachCallback} object. 480 * An application <b>must</b> call {@link WifiAwareSession#close()} when done with the 481 * Wi-Fi Aware object. 482 * <p> 483 * Note: a Aware cluster is a shared resource - if the device is already attached to a cluster 484 * then this function will simply indicate success immediately using the same {@code 485 * attachCallback}. 486 * <p> 487 * This version of the API attaches a listener to receive the MAC address of the Aware interface 488 * on startup and whenever it is updated (it is randomized at regular intervals for privacy). 489 * 490 * If targeting {@link android.os.Build.VERSION_CODES#TIRAMISU} or later, the application must 491 * have {@link android.Manifest.permission#NEARBY_WIFI_DEVICES} with 492 * android:usesPermissionFlags="neverForLocation". If the application does not declare 493 * android:usesPermissionFlags="neverForLocation", then it must also have 494 * {@link android.Manifest.permission#ACCESS_FINE_LOCATION}. 495 * 496 * If targeting an earlier release than {@link android.os.Build.VERSION_CODES#TIRAMISU}, the 497 * application must have {@link android.Manifest.permission#ACCESS_FINE_LOCATION}. 498 * 499 * Apps without {@link android.Manifest.permission#NEARBY_WIFI_DEVICES} or 500 * {@link android.Manifest.permission#ACCESS_FINE_LOCATION} can use the 501 * {@link #attach(AttachCallback, Handler)} version. 502 * Note that aside from permission requirements the {@link IdentityChangedListener} will wake up 503 * the host at regular intervals causing higher power consumption, do not use it unless the 504 * information is necessary (e.g. for out-of-band discovery). 505 * 506 * @param attachCallback A callback for attach events, extended from 507 * {@link AttachCallback}. 508 * @param identityChangedListener A callback for changed identity or cluster ID, extended from 509 * {@link IdentityChangedListener}. 510 * @param handler The Handler on whose thread to execute the callbacks of the {@code 511 * attachCallback} and {@code identityChangedListener} objects. If a null is provided then the 512 * application's main thread will be used. 513 */ 514 @RequiresPermission(allOf = { 515 ACCESS_WIFI_STATE, 516 CHANGE_WIFI_STATE, 517 ACCESS_FINE_LOCATION, 518 NEARBY_WIFI_DEVICES}, conditional = true) attach(@onNull AttachCallback attachCallback, @NonNull IdentityChangedListener identityChangedListener, @Nullable Handler handler)519 public void attach(@NonNull AttachCallback attachCallback, 520 @NonNull IdentityChangedListener identityChangedListener, 521 @Nullable Handler handler) { 522 attach(handler, null, attachCallback, identityChangedListener, false, null); 523 } 524 525 /** 526 * Attach to the Wi-Fi Aware service - enabling the application to create discovery sessions or 527 * create connections to peers. See {@link #attach(AttachCallback, IdentityChangedListener, 528 * Handler)} for more information. 529 * 530 * This version allows callers to provide an instance of {@link ConfigRequest}. 531 * 532 * @param configRequest Parameters for this request. 533 * @param executor The executor to execute the listener of the {@code attachCallback} object. 534 * @param attachCallback A callback for attach events, extended from {@link AttachCallback}. 535 * @param identityChangedListener A callback for changed identity or cluster ID, extended from 536 * {@link IdentityChangedListener}. 537 * @hide 538 */ 539 @RequiresPermission(allOf = { 540 ACCESS_WIFI_STATE, 541 CHANGE_WIFI_STATE, 542 ACCESS_FINE_LOCATION, 543 NEARBY_WIFI_DEVICES, 544 MANAGE_WIFI_NETWORK_SELECTION}, conditional = true) 545 @RequiresApi(Build.VERSION_CODES.VANILLA_ICE_CREAM) 546 @FlaggedApi(Flags.FLAG_ANDROID_V_WIFI_API) 547 @SystemApi attach(@onNull ConfigRequest configRequest, @NonNull @CallbackExecutor Executor executor, @NonNull AttachCallback attachCallback, @NonNull IdentityChangedListener identityChangedListener)548 public void attach(@NonNull ConfigRequest configRequest, 549 @NonNull @CallbackExecutor Executor executor, @NonNull AttachCallback attachCallback, 550 @NonNull IdentityChangedListener identityChangedListener) { 551 if (!SdkLevel.isAtLeastV()) { 552 throw new UnsupportedOperationException(); 553 } 554 Objects.requireNonNull(configRequest); 555 Objects.requireNonNull(executor); 556 attach(null, configRequest, attachCallback, identityChangedListener, false, executor); 557 } 558 559 /** @hide */ attach(Handler handler, ConfigRequest configRequest, AttachCallback attachCallback, IdentityChangedListener identityChangedListener, boolean forOffloading, Executor executor)560 public void attach(Handler handler, ConfigRequest configRequest, 561 AttachCallback attachCallback, 562 IdentityChangedListener identityChangedListener, boolean forOffloading, 563 Executor executor) { 564 if (VDBG) { 565 Log.v(TAG, "attach(): handler=" + handler + ", callback=" + attachCallback 566 + ", configRequest=" + configRequest + ", identityChangedListener=" 567 + identityChangedListener + ", forOffloading" + forOffloading); 568 } 569 570 if (attachCallback == null) { 571 throw new IllegalArgumentException("Null callback provided"); 572 } 573 574 synchronized (mLock) { 575 Executor localExecutor = executor; 576 if (localExecutor == null) { 577 localExecutor = new HandlerExecutor((handler == null) 578 ? new Handler(Looper.getMainLooper()) : handler); 579 } 580 581 try { 582 Binder binder = new Binder(); 583 Bundle extras = new Bundle(); 584 if (SdkLevel.isAtLeastS()) { 585 extras.putParcelable(WifiManager.EXTRA_PARAM_KEY_ATTRIBUTION_SOURCE, 586 mContext.getAttributionSource()); 587 } 588 mService.connect(binder, mContext.getOpPackageName(), mContext.getAttributionTag(), 589 new WifiAwareEventCallbackProxy(this, localExecutor, binder, 590 attachCallback, identityChangedListener), configRequest, 591 identityChangedListener != null, extras, forOffloading); 592 } catch (RemoteException e) { 593 throw e.rethrowFromSystemServer(); 594 } 595 } 596 } 597 598 /** @hide */ disconnect(int clientId, Binder binder)599 public void disconnect(int clientId, Binder binder) { 600 if (VDBG) Log.v(TAG, "disconnect()"); 601 602 try { 603 mService.disconnect(clientId, binder); 604 } catch (RemoteException e) { 605 throw e.rethrowFromSystemServer(); 606 } 607 } 608 609 /** @hide */ setMasterPreference(int clientId, Binder binder, int mp)610 public void setMasterPreference(int clientId, Binder binder, int mp) { 611 if (VDBG) Log.v(TAG, "setMasterPreference()"); 612 613 try { 614 mService.setMasterPreference(clientId, binder, mp); 615 } catch (RemoteException e) { 616 throw e.rethrowFromSystemServer(); 617 } 618 } 619 620 /** 621 * @hide 622 */ getMasterPreference(int clientId, Binder binder, @NonNull Executor executor, @NonNull Consumer<Integer> resultsCallback)623 public void getMasterPreference(int clientId, Binder binder, @NonNull Executor executor, 624 @NonNull Consumer<Integer> resultsCallback) { 625 Objects.requireNonNull(executor, "executor cannot be null"); 626 Objects.requireNonNull(resultsCallback, "resultsCallback cannot be null"); 627 try { 628 mService.getMasterPreference(clientId, binder, 629 new IIntegerListener.Stub() { 630 public void onResult(int value) { 631 Binder.clearCallingIdentity(); 632 executor.execute(() -> resultsCallback.accept(value)); 633 } 634 }); 635 } catch (RemoteException e) { 636 throw e.rethrowFromSystemServer(); 637 } 638 } 639 640 /** @hide */ publish(int clientId, Looper looper, PublishConfig publishConfig, DiscoverySessionCallback callback)641 public void publish(int clientId, Looper looper, PublishConfig publishConfig, 642 DiscoverySessionCallback callback) { 643 if (VDBG) Log.v(TAG, "publish(): clientId=" + clientId + ", config=" + publishConfig); 644 645 if (callback == null) { 646 throw new IllegalArgumentException("Null callback provided"); 647 } 648 649 try { 650 Bundle extras = new Bundle(); 651 if (SdkLevel.isAtLeastS()) { 652 extras.putParcelable(WifiManager.EXTRA_PARAM_KEY_ATTRIBUTION_SOURCE, 653 mContext.getAttributionSource()); 654 } 655 mService.publish(mContext.getOpPackageName(), mContext.getAttributionTag(), clientId, 656 publishConfig, 657 new WifiAwareDiscoverySessionCallbackProxy(this, looper, true, callback, 658 clientId), extras); 659 } catch (RemoteException e) { 660 throw e.rethrowFromSystemServer(); 661 } 662 } 663 664 /** @hide */ updatePublish(int clientId, int sessionId, PublishConfig publishConfig)665 public void updatePublish(int clientId, int sessionId, PublishConfig publishConfig) { 666 if (VDBG) { 667 Log.v(TAG, "updatePublish(): clientId=" + clientId + ",sessionId=" + sessionId 668 + ", config=" + publishConfig); 669 } 670 671 try { 672 mService.updatePublish(clientId, sessionId, publishConfig); 673 } catch (RemoteException e) { 674 throw e.rethrowFromSystemServer(); 675 } 676 } 677 678 /** @hide */ subscribe(int clientId, Looper looper, SubscribeConfig subscribeConfig, DiscoverySessionCallback callback)679 public void subscribe(int clientId, Looper looper, SubscribeConfig subscribeConfig, 680 DiscoverySessionCallback callback) { 681 if (VDBG) { 682 if (VDBG) { 683 Log.v(TAG, 684 "subscribe(): clientId=" + clientId + ", config=" + subscribeConfig); 685 } 686 } 687 688 if (callback == null) { 689 throw new IllegalArgumentException("Null callback provided"); 690 } 691 692 try { 693 Bundle extras = new Bundle(); 694 if (SdkLevel.isAtLeastS()) { 695 extras.putParcelable(WifiManager.EXTRA_PARAM_KEY_ATTRIBUTION_SOURCE, 696 mContext.getAttributionSource()); 697 } 698 mService.subscribe(mContext.getOpPackageName(), mContext.getAttributionTag(), clientId, 699 subscribeConfig, 700 new WifiAwareDiscoverySessionCallbackProxy(this, looper, false, callback, 701 clientId), extras); 702 } catch (RemoteException e) { 703 throw e.rethrowFromSystemServer(); 704 } 705 } 706 707 /** @hide */ updateSubscribe(int clientId, int sessionId, SubscribeConfig subscribeConfig)708 public void updateSubscribe(int clientId, int sessionId, SubscribeConfig subscribeConfig) { 709 if (VDBG) { 710 Log.v(TAG, "updateSubscribe(): clientId=" + clientId + ",sessionId=" + sessionId 711 + ", config=" + subscribeConfig); 712 } 713 714 try { 715 mService.updateSubscribe(clientId, sessionId, subscribeConfig); 716 } catch (RemoteException e) { 717 throw e.rethrowFromSystemServer(); 718 } 719 } 720 721 /** @hide */ terminateSession(int clientId, int sessionId)722 public void terminateSession(int clientId, int sessionId) { 723 if (VDBG) { 724 Log.d(TAG, 725 "terminateSession(): clientId=" + clientId + ", sessionId=" + sessionId); 726 } 727 728 try { 729 mService.terminateSession(clientId, sessionId); 730 } catch (RemoteException e) { 731 throw e.rethrowFromSystemServer(); 732 } 733 } 734 735 /** @hide */ sendMessage(int clientId, int sessionId, PeerHandle peerHandle, byte[] message, int messageId, int retryCount)736 public void sendMessage(int clientId, int sessionId, PeerHandle peerHandle, byte[] message, 737 int messageId, int retryCount) { 738 if (peerHandle == null) { 739 throw new IllegalArgumentException( 740 "sendMessage: invalid peerHandle - must be non-null"); 741 } 742 743 if (VDBG) { 744 Log.v(TAG, "sendMessage(): clientId=" + clientId + ", sessionId=" + sessionId 745 + ", peerHandle=" + peerHandle.peerId + ", messageId=" 746 + messageId + ", retryCount=" + retryCount); 747 } 748 749 try { 750 mService.sendMessage(clientId, sessionId, peerHandle.peerId, message, messageId, 751 retryCount); 752 } catch (RemoteException e) { 753 throw e.rethrowFromSystemServer(); 754 } 755 } 756 757 /** 758 * @hide 759 */ initiateNanPairingSetupRequest(int clientId, int sessionId, PeerHandle peerHandle, String password, String pairingDeviceAlias, int cipherSuite)760 public void initiateNanPairingSetupRequest(int clientId, int sessionId, PeerHandle peerHandle, 761 String password, String pairingDeviceAlias, int cipherSuite) { 762 if (peerHandle == null) { 763 throw new IllegalArgumentException( 764 "initiateNanPairingRequest: invalid peerHandle - must be non-null"); 765 } 766 if (VDBG) { 767 Log.v(TAG, "initiateNanPairingRequest(): clientId=" + clientId 768 + ", sessionId=" + sessionId + ", peerHandle=" + peerHandle.peerId); 769 } 770 try { 771 mService.initiateNanPairingSetupRequest(clientId, sessionId, peerHandle.peerId, 772 password, pairingDeviceAlias, cipherSuite); 773 } catch (RemoteException e) { 774 throw e.rethrowFromSystemServer(); 775 } 776 } 777 778 /** 779 * @hide 780 */ responseNanPairingSetupRequest(int clientId, int sessionId, PeerHandle peerHandle, int requestId, String password, String pairingDeviceAlias, boolean accept, int cipherSuite)781 public void responseNanPairingSetupRequest(int clientId, int sessionId, PeerHandle peerHandle, 782 int requestId, String password, String pairingDeviceAlias, boolean accept, 783 int cipherSuite) { 784 if (peerHandle == null) { 785 throw new IllegalArgumentException( 786 "initiateNanPairingRequest: invalid peerHandle - must be non-null"); 787 } 788 if (VDBG) { 789 Log.v(TAG, "initiateNanPairingRequest(): clientId=" + clientId 790 + ", sessionId=" + sessionId + ", peerHandle=" + peerHandle.peerId); 791 } 792 try { 793 mService.responseNanPairingSetupRequest(clientId, sessionId, peerHandle.peerId, 794 requestId, password, pairingDeviceAlias, accept, cipherSuite); 795 } catch (RemoteException e) { 796 throw e.rethrowFromSystemServer(); 797 } 798 } 799 800 /** 801 * @hide 802 */ initiateBootStrappingSetupRequest(int clientId, int sessionId, PeerHandle peerHandle, int method)803 public void initiateBootStrappingSetupRequest(int clientId, int sessionId, 804 PeerHandle peerHandle, int method) { 805 if (peerHandle == null) { 806 throw new IllegalArgumentException( 807 "initiateBootStrappingSetupRequest: invalid peerHandle - must be non-null"); 808 } 809 if (VDBG) { 810 Log.v(TAG, "initiateBootStrappingSetupRequest(): clientId=" + clientId 811 + ", sessionId=" + sessionId + ", peerHandle=" + peerHandle.peerId); 812 } 813 try { 814 mService.initiateBootStrappingSetupRequest(clientId, sessionId, peerHandle.peerId, 815 method); 816 } catch (RemoteException e) { 817 throw e.rethrowFromSystemServer(); 818 } 819 } 820 821 /** @hide */ 822 @RequiresPermission(android.Manifest.permission.NETWORK_STACK) requestMacAddresses(int uid, int[] peerIds, IWifiAwareMacAddressProvider callback)823 public void requestMacAddresses(int uid, int[] peerIds, 824 IWifiAwareMacAddressProvider callback) { 825 try { 826 mService.requestMacAddresses(uid, peerIds, callback); 827 } catch (RemoteException e) { 828 throw e.rethrowFromSystemServer(); 829 } 830 } 831 832 /** @hide */ createNetworkSpecifier(int clientId, int role, int sessionId, @NonNull PeerHandle peerHandle, @Nullable byte[] pmk, @Nullable String passphrase)833 public NetworkSpecifier createNetworkSpecifier(int clientId, int role, int sessionId, 834 @NonNull PeerHandle peerHandle, @Nullable byte[] pmk, @Nullable String passphrase) { 835 if (VDBG) { 836 Log.v(TAG, "createNetworkSpecifier: role=" + role + ", sessionId=" + sessionId 837 + ", peerHandle=" + ((peerHandle == null) ? peerHandle : peerHandle.peerId) 838 + ", pmk=" + ((pmk == null) ? "null" : "non-null") 839 + ", passphrase=" + ((passphrase == null) ? "null" : "non-null")); 840 } 841 842 if (!WifiAwareUtils.isLegacyVersion(mContext, Build.VERSION_CODES.Q)) { 843 throw new UnsupportedOperationException( 844 "API deprecated - use WifiAwareNetworkSpecifier.Builder"); 845 } 846 847 if (role != WIFI_AWARE_DATA_PATH_ROLE_INITIATOR 848 && role != WIFI_AWARE_DATA_PATH_ROLE_RESPONDER) { 849 throw new IllegalArgumentException( 850 "createNetworkSpecifier: Invalid 'role' argument when creating a network " 851 + "specifier"); 852 } 853 if (role == WIFI_AWARE_DATA_PATH_ROLE_INITIATOR || !WifiAwareUtils.isLegacyVersion(mContext, 854 Build.VERSION_CODES.P)) { 855 if (peerHandle == null) { 856 throw new IllegalArgumentException( 857 "createNetworkSpecifier: Invalid peer handle - cannot be null"); 858 } 859 } 860 861 return new WifiAwareNetworkSpecifier( 862 (peerHandle == null) ? WifiAwareNetworkSpecifier.NETWORK_SPECIFIER_TYPE_IB_ANY_PEER 863 : WifiAwareNetworkSpecifier.NETWORK_SPECIFIER_TYPE_IB, 864 role, 865 clientId, 866 sessionId, 867 peerHandle != null ? peerHandle.peerId : 0, // 0 is an invalid peer ID 868 null, // peerMac (not used in this method) 869 pmk, 870 passphrase, 871 0, // no port info for deprecated IB APIs 872 -1); // no transport info for deprecated IB APIs 873 } 874 875 /** @hide */ createNetworkSpecifier(int clientId, @DataPathRole int role, @NonNull byte[] peer, @Nullable byte[] pmk, @Nullable String passphrase)876 public NetworkSpecifier createNetworkSpecifier(int clientId, @DataPathRole int role, 877 @NonNull byte[] peer, @Nullable byte[] pmk, @Nullable String passphrase) { 878 if (VDBG) { 879 Log.v(TAG, "createNetworkSpecifier: role=" + role 880 + ", pmk=" + ((pmk == null) ? "null" : "non-null") 881 + ", passphrase=" + ((passphrase == null) ? "null" : "non-null")); 882 } 883 884 if (role != WIFI_AWARE_DATA_PATH_ROLE_INITIATOR 885 && role != WIFI_AWARE_DATA_PATH_ROLE_RESPONDER) { 886 throw new IllegalArgumentException( 887 "createNetworkSpecifier: Invalid 'role' argument when creating a network " 888 + "specifier"); 889 } 890 if (role == WIFI_AWARE_DATA_PATH_ROLE_INITIATOR || !WifiAwareUtils.isLegacyVersion(mContext, 891 Build.VERSION_CODES.P)) { 892 if (peer == null) { 893 throw new IllegalArgumentException( 894 "createNetworkSpecifier: Invalid peer MAC - cannot be null"); 895 } 896 } 897 if (peer != null && peer.length != 6) { 898 throw new IllegalArgumentException("createNetworkSpecifier: Invalid peer MAC address"); 899 } 900 901 return new WifiAwareNetworkSpecifier( 902 (peer == null) ? WifiAwareNetworkSpecifier.NETWORK_SPECIFIER_TYPE_OOB_ANY_PEER 903 : WifiAwareNetworkSpecifier.NETWORK_SPECIFIER_TYPE_OOB, 904 role, 905 clientId, 906 0, // 0 is an invalid session ID 907 0, // 0 is an invalid peer ID 908 peer, 909 pmk, 910 passphrase, 911 0, // no port info for OOB APIs 912 -1); // no transport protocol info for OOB APIs 913 } 914 915 private static class WifiAwareEventCallbackProxy extends IWifiAwareEventCallback.Stub { 916 private final WeakReference<WifiAwareManager> mAwareManager; 917 private final Binder mBinder; 918 private final Executor mExecutor; 919 920 private final AttachCallback mAttachCallback; 921 922 private final IdentityChangedListener mIdentityChangedListener; 923 924 /** 925 * Constructs a {@link AttachCallback} using the specified looper. 926 * All callbacks will delivered on the thread of the specified looper. 927 * 928 * @param executor The executor to execute the callbacks. 929 */ WifiAwareEventCallbackProxy(WifiAwareManager mgr, Executor executor, Binder binder, final AttachCallback attachCallback, final IdentityChangedListener identityChangedListener)930 WifiAwareEventCallbackProxy(WifiAwareManager mgr, Executor executor, Binder binder, 931 final AttachCallback attachCallback, 932 final IdentityChangedListener identityChangedListener) { 933 mAwareManager = new WeakReference<>(mgr); 934 mExecutor = executor; 935 mBinder = binder; 936 mAttachCallback = attachCallback; 937 mIdentityChangedListener = identityChangedListener; 938 } 939 940 @Override onConnectSuccess(int clientId)941 public void onConnectSuccess(int clientId) { 942 if (VDBG) Log.v(TAG, "onConnectSuccess"); 943 Binder.clearCallingIdentity(); 944 mExecutor.execute(() -> { 945 WifiAwareManager mgr = mAwareManager.get(); 946 if (mgr == null) { 947 Log.w(TAG, "WifiAwareEventCallbackProxy: handleMessage post GC"); 948 return; 949 } 950 mAttachCallback.onAttached(new WifiAwareSession(mgr, mBinder, clientId)); 951 }); 952 } 953 954 @Override onConnectFail(int reason)955 public void onConnectFail(int reason) { 956 if (VDBG) Log.v(TAG, "onConnectFail: reason=" + reason); 957 Binder.clearCallingIdentity(); 958 mExecutor.execute(() -> { 959 WifiAwareManager mgr = mAwareManager.get(); 960 if (mgr == null) { 961 Log.w(TAG, "WifiAwareEventCallbackProxy: handleMessage post GC"); 962 return; 963 } 964 mAwareManager.clear(); 965 mAttachCallback.onAttachFailed(); 966 }); 967 } 968 969 @Override onIdentityChanged(byte[] mac)970 public void onIdentityChanged(byte[] mac) { 971 if (VDBG) Log.v(TAG, "onIdentityChanged: mac=" + new String(HexEncoding.encode(mac))); 972 Binder.clearCallingIdentity(); 973 mExecutor.execute(() -> { 974 WifiAwareManager mgr = mAwareManager.get(); 975 if (mgr == null) { 976 Log.w(TAG, "WifiAwareEventCallbackProxy: handleMessage post GC"); 977 return; 978 } 979 if (mIdentityChangedListener == null) { 980 Log.e(TAG, "CALLBACK_IDENTITY_CHANGED: null listener."); 981 } else { 982 mIdentityChangedListener.onIdentityChanged(mac); 983 } 984 }); 985 } 986 987 @Override onAttachTerminate()988 public void onAttachTerminate() { 989 if (VDBG) Log.v(TAG, "onAwareSessionTerminated"); 990 Binder.clearCallingIdentity(); 991 mExecutor.execute(() -> { 992 WifiAwareManager mgr = mAwareManager.get(); 993 if (mgr == null) { 994 Log.w(TAG, "WifiAwareEventCallbackProxy: handleMessage post GC"); 995 return; 996 } 997 mAwareManager.clear(); 998 mAttachCallback.onAwareSessionTerminated(); 999 }); 1000 } 1001 1002 @Override onClusterIdChanged( @dentityChangedListener.ClusterChangeEvent int clusterEventType, byte[] clusterId)1003 public void onClusterIdChanged( 1004 @IdentityChangedListener.ClusterChangeEvent int clusterEventType, 1005 byte[] clusterId) { 1006 Binder.clearCallingIdentity(); 1007 mExecutor.execute(() -> { 1008 WifiAwareManager mgr = mAwareManager.get(); 1009 if (mgr == null) { 1010 Log.w(TAG, "WifiAwareEventCallbackProxy: handleMessage post GC"); 1011 return; 1012 } 1013 if (mIdentityChangedListener == null) { 1014 Log.e(TAG, "CALLBACK_CLUSTER_ID_CHANGED: null listener."); 1015 } else { 1016 try { 1017 mIdentityChangedListener.onClusterIdChanged( 1018 clusterEventType, MacAddress.fromBytes(clusterId)); 1019 } catch (IllegalArgumentException iae) { 1020 Log.e(TAG, " Invalid MAC address, " + iae); 1021 } 1022 } 1023 }); 1024 } 1025 } 1026 1027 private static class WifiAwareDiscoverySessionCallbackProxy extends 1028 IWifiAwareDiscoverySessionCallback.Stub { 1029 private final WeakReference<WifiAwareManager> mAwareManager; 1030 private final boolean mIsPublish; 1031 private final DiscoverySessionCallback mOriginalCallback; 1032 private final int mClientId; 1033 1034 private final Handler mHandler; 1035 private DiscoverySession mSession; 1036 WifiAwareDiscoverySessionCallbackProxy(WifiAwareManager mgr, Looper looper, boolean isPublish, DiscoverySessionCallback originalCallback, int clientId)1037 WifiAwareDiscoverySessionCallbackProxy(WifiAwareManager mgr, Looper looper, 1038 boolean isPublish, DiscoverySessionCallback originalCallback, 1039 int clientId) { 1040 mAwareManager = new WeakReference<>(mgr); 1041 mIsPublish = isPublish; 1042 mOriginalCallback = originalCallback; 1043 mClientId = clientId; 1044 1045 if (VDBG) { 1046 Log.v(TAG, "WifiAwareDiscoverySessionCallbackProxy ctor: isPublish=" + isPublish); 1047 } 1048 1049 mHandler = new Handler(looper); 1050 } 1051 1052 @Override onSessionStarted(int sessionId)1053 public void onSessionStarted(int sessionId) { 1054 if (VDBG) Log.v(TAG, "onSessionStarted: sessionId=" + sessionId); 1055 mHandler.post(() -> onProxySessionStarted(sessionId)); 1056 } 1057 1058 @Override onSessionConfigSuccess()1059 public void onSessionConfigSuccess() { 1060 if (VDBG) Log.v(TAG, "onSessionConfigSuccess"); 1061 mHandler.post(mOriginalCallback::onSessionConfigUpdated); 1062 } 1063 1064 @Override onSessionConfigFail(int reason)1065 public void onSessionConfigFail(int reason) { 1066 if (VDBG) Log.v(TAG, "onSessionConfigFail: reason=" + reason); 1067 mHandler.post(() -> { 1068 mOriginalCallback.onSessionConfigFailed(); 1069 if (mSession == null) { 1070 /* creation failed (as opposed to update failing) */ 1071 mAwareManager.clear(); 1072 } 1073 }); 1074 } 1075 1076 @Override onSessionTerminated(int reason)1077 public void onSessionTerminated(int reason) { 1078 if (VDBG) Log.v(TAG, "onSessionTerminated: reason=" + reason); 1079 mHandler.post(() -> onProxySessionTerminated(reason)); 1080 } 1081 1082 @Override onSessionSuspendSucceeded()1083 public void onSessionSuspendSucceeded() { 1084 if (VDBG) Log.v(TAG, "onSessionSuspendSucceeded"); 1085 mHandler.post(mOriginalCallback::onSessionSuspendSucceeded); 1086 } 1087 1088 @Override onSessionSuspendFail(int reason)1089 public void onSessionSuspendFail(int reason) { 1090 if (VDBG) Log.v(TAG, "onSessionSuspendFail: reason=" + reason); 1091 mHandler.post(() -> mOriginalCallback.onSessionSuspendFailed(reason)); 1092 } 1093 1094 @Override onSessionResumeSucceeded()1095 public void onSessionResumeSucceeded() { 1096 if (VDBG) Log.v(TAG, "onSessionResumeSucceeded"); 1097 mHandler.post(mOriginalCallback::onSessionResumeSucceeded); 1098 } 1099 1100 @Override onSessionResumeFail(int reason)1101 public void onSessionResumeFail(int reason) { 1102 if (VDBG) Log.v(TAG, "onSessionResumeFail: reason=" + reason); 1103 mHandler.post(() -> mOriginalCallback.onSessionResumeFailed(reason)); 1104 } 1105 1106 @Override onMatch(int peerId, byte[] serviceSpecificInfo, byte[] matchFilter, int peerCipherSuite, byte[] scid, String pairingAlias, AwarePairingConfig pairingConfig, @Nullable OuiKeyedData[] vendorData)1107 public void onMatch(int peerId, byte[] serviceSpecificInfo, byte[] matchFilter, 1108 int peerCipherSuite, byte[] scid, String pairingAlias, 1109 AwarePairingConfig pairingConfig, @Nullable OuiKeyedData[] vendorData) { 1110 if (VDBG) Log.v(TAG, "onMatch: peerId=" + peerId); 1111 1112 mHandler.post(() -> { 1113 List<byte[]> matchFilterList = getMatchFilterList(matchFilter); 1114 mOriginalCallback.onServiceDiscovered(new PeerHandle(peerId), serviceSpecificInfo, 1115 matchFilterList); 1116 mOriginalCallback.onServiceDiscovered( 1117 new ServiceDiscoveryInfo(new PeerHandle(peerId), peerCipherSuite, 1118 serviceSpecificInfo, matchFilterList, scid, pairingAlias, 1119 pairingConfig, vendorData)); 1120 }); 1121 } 1122 getMatchFilterList(byte[] matchFilter)1123 private List<byte[]> getMatchFilterList(byte[] matchFilter) { 1124 List<byte[]> matchFilterList = null; 1125 try { 1126 matchFilterList = new TlvBufferUtils.TlvIterable(0, 1, matchFilter).toList(); 1127 } catch (BufferOverflowException e) { 1128 matchFilterList = Collections.emptyList(); 1129 Log.e(TAG, "onServiceDiscovered: invalid match filter byte array '" 1130 + new String(HexEncoding.encode(matchFilter)) 1131 + "' - cannot be parsed: e=" + e); 1132 } 1133 return matchFilterList; 1134 } 1135 1136 @Override onMatchWithDistance(int peerId, byte[] serviceSpecificInfo, byte[] matchFilter, int distanceMm, int peerCipherSuite, byte[] scid, String pairingAlias, AwarePairingConfig pairingConfig, @Nullable OuiKeyedData[] vendorData)1137 public void onMatchWithDistance(int peerId, byte[] serviceSpecificInfo, byte[] matchFilter, 1138 int distanceMm, int peerCipherSuite, byte[] scid, String pairingAlias, 1139 AwarePairingConfig pairingConfig, @Nullable OuiKeyedData[] vendorData) { 1140 if (VDBG) { 1141 Log.v(TAG, "onMatchWithDistance: peerId=" + peerId + ", distanceMm=" + distanceMm); 1142 } 1143 mHandler.post(() -> { 1144 List<byte[]> matchFilterList = getMatchFilterList(matchFilter); 1145 mOriginalCallback.onServiceDiscoveredWithinRange( 1146 new PeerHandle(peerId), 1147 serviceSpecificInfo, 1148 matchFilterList, distanceMm); 1149 mOriginalCallback.onServiceDiscoveredWithinRange( 1150 new ServiceDiscoveryInfo( 1151 new PeerHandle(peerId), 1152 peerCipherSuite, 1153 serviceSpecificInfo, 1154 matchFilterList, 1155 scid, 1156 pairingAlias, 1157 pairingConfig, 1158 vendorData), 1159 distanceMm); 1160 }); 1161 } 1162 @Override onMatchExpired(int peerId)1163 public void onMatchExpired(int peerId) { 1164 if (VDBG) { 1165 Log.v(TAG, "onMatchExpired: peerId=" + peerId); 1166 } 1167 mHandler.post(() -> 1168 mOriginalCallback.onServiceLost(new PeerHandle(peerId), 1169 WIFI_AWARE_DISCOVERY_LOST_REASON_PEER_NOT_VISIBLE)); 1170 } 1171 1172 @Override onMessageSendSuccess(int messageId)1173 public void onMessageSendSuccess(int messageId) { 1174 if (VDBG) Log.v(TAG, "onMessageSendSuccess"); 1175 mHandler.post(() -> mOriginalCallback.onMessageSendSucceeded(messageId)); 1176 } 1177 1178 @Override onMessageSendFail(int messageId, int reason)1179 public void onMessageSendFail(int messageId, int reason) { 1180 if (VDBG) Log.v(TAG, "onMessageSendFail: reason=" + reason); 1181 mHandler.post(() -> mOriginalCallback.onMessageSendFailed(messageId)); 1182 } 1183 1184 @Override onMessageReceived(int peerId, byte[] message)1185 public void onMessageReceived(int peerId, byte[] message) { 1186 if (VDBG) { 1187 Log.v(TAG, "onMessageReceived: peerId=" + peerId); 1188 } 1189 mHandler.post(() -> mOriginalCallback.onMessageReceived(new PeerHandle(peerId), 1190 message)); 1191 } 1192 1193 @Override onPairingSetupRequestReceived(int peerId, int requestId)1194 public void onPairingSetupRequestReceived(int peerId, int requestId) { 1195 mHandler.post(() -> 1196 mOriginalCallback.onPairingSetupRequestReceived(new PeerHandle(peerId), 1197 requestId)); 1198 } 1199 @Override onPairingSetupConfirmed(int peerId, boolean accept, String alias)1200 public void onPairingSetupConfirmed(int peerId, boolean accept, String alias) { 1201 if (accept) { 1202 mHandler.post(() -> mOriginalCallback 1203 .onPairingSetupSucceeded(new PeerHandle(peerId), alias)); 1204 } else { 1205 mHandler.post(() -> mOriginalCallback 1206 .onPairingSetupFailed(new PeerHandle(peerId))); 1207 } 1208 } 1209 @Override onPairingVerificationConfirmed(int peerId, boolean accept, String alias)1210 public void onPairingVerificationConfirmed(int peerId, boolean accept, String alias) { 1211 if (accept) { 1212 mHandler.post(() -> mOriginalCallback.onPairingVerificationSucceed( 1213 new PeerHandle(peerId), alias)); 1214 } else { 1215 mHandler.post(() -> mOriginalCallback 1216 .onPairingVerificationFailed(new PeerHandle(peerId))); 1217 } 1218 } 1219 @Override onBootstrappingVerificationConfirmed(int peerId, boolean accept, int method)1220 public void onBootstrappingVerificationConfirmed(int peerId, boolean accept, int method) { 1221 if (accept) { 1222 mHandler.post(() -> mOriginalCallback.onBootstrappingSucceeded( 1223 new PeerHandle(peerId), method)); 1224 } else { 1225 mHandler.post(() -> mOriginalCallback.onBootstrappingFailed( 1226 new PeerHandle(peerId))); 1227 } 1228 } 1229 1230 /* 1231 * Proxies methods 1232 */ onProxySessionStarted(int sessionId)1233 public void onProxySessionStarted(int sessionId) { 1234 if (VDBG) Log.v(TAG, "Proxy: onSessionStarted: sessionId=" + sessionId); 1235 if (mSession != null) { 1236 Log.e(TAG, 1237 "onSessionStarted: sessionId=" + sessionId + ": session already created!?"); 1238 throw new IllegalStateException( 1239 "onSessionStarted: sessionId=" + sessionId + ": session already created!?"); 1240 } 1241 1242 WifiAwareManager mgr = mAwareManager.get(); 1243 if (mgr == null) { 1244 Log.w(TAG, "onProxySessionStarted: mgr GC'd"); 1245 return; 1246 } 1247 1248 if (mIsPublish) { 1249 PublishDiscoverySession session = new PublishDiscoverySession(mgr, 1250 mClientId, sessionId); 1251 mSession = session; 1252 mOriginalCallback.onPublishStarted(session); 1253 } else { 1254 SubscribeDiscoverySession 1255 session = new SubscribeDiscoverySession(mgr, mClientId, sessionId); 1256 mSession = session; 1257 mOriginalCallback.onSubscribeStarted(session); 1258 } 1259 } 1260 onProxySessionTerminated(int reason)1261 public void onProxySessionTerminated(int reason) { 1262 if (VDBG) Log.v(TAG, "Proxy: onSessionTerminated: reason=" + reason); 1263 if (mSession != null) { 1264 mSession.setTerminated(); 1265 mSession = null; 1266 } else { 1267 Log.w(TAG, "Proxy: onSessionTerminated called but mSession is null!?"); 1268 } 1269 mAwareManager.clear(); 1270 mOriginalCallback.onSessionTerminated(); 1271 } 1272 } 1273 1274 /** 1275 * Set Wi-Fi Aware protocol parameters. 1276 * @hide 1277 * @param params An object contain specified parameters. Use {@code null} to remove previously 1278 * set configuration and restore default behavior. 1279 */ 1280 @SystemApi 1281 @RequiresPermission(allOf = {OVERRIDE_WIFI_CONFIG, 1282 CHANGE_WIFI_STATE}) setAwareParams(@ullable AwareParams params)1283 public void setAwareParams(@Nullable AwareParams params) { 1284 try { 1285 mService.setAwareParams(params); 1286 } catch (RemoteException e) { 1287 throw e.rethrowFromSystemServer(); 1288 } 1289 } 1290 1291 /** 1292 * Set all Wi-Fi Aware sessions created by the calling app to be opportunistic. Opportunistic 1293 * Wi-Fi Aware sessions are considered low priority and may be torn down (the sessions or the 1294 * Aware interface) if there are resource conflicts. 1295 * 1296 * @param enabled True to configure all Wi-Fi Aware sessions by the calling app as 1297 * Opportunistic. False by default. 1298 */ 1299 @RequiresPermission(CHANGE_WIFI_STATE) setOpportunisticModeEnabled(boolean enabled)1300 public void setOpportunisticModeEnabled(boolean enabled) { 1301 try { 1302 mService.setOpportunisticModeEnabled(mContext.getOpPackageName(), enabled); 1303 } catch (RemoteException e) { 1304 throw e.rethrowFromSystemServer(); 1305 } 1306 } 1307 1308 /** 1309 * Indicate whether all Wi-Fi Aware sessions created by the calling app are opportunistic as 1310 * defined and configured by {@link #setOpportunisticModeEnabled(boolean)} 1311 * 1312 * @param executor The executor on which callback will be invoked. 1313 * @param resultsCallback An asynchronous callback that will return boolean 1314 */ 1315 @RequiresPermission(ACCESS_WIFI_STATE) isOpportunisticModeEnabled(@onNull @allbackExecutor Executor executor, @NonNull Consumer<Boolean> resultsCallback)1316 public void isOpportunisticModeEnabled(@NonNull @CallbackExecutor Executor executor, 1317 @NonNull Consumer<Boolean> resultsCallback) { 1318 Objects.requireNonNull(executor, "executor cannot be null"); 1319 Objects.requireNonNull(resultsCallback, "resultsCallback cannot be null"); 1320 1321 try { 1322 mService.isOpportunisticModeEnabled(mContext.getOpPackageName(), 1323 new IBooleanListener.Stub() { 1324 @Override 1325 public void onResult(boolean value) { 1326 Binder.clearCallingIdentity(); 1327 executor.execute(() -> { 1328 resultsCallback.accept(value); 1329 }); 1330 } 1331 }); 1332 } catch (RemoteException e) { 1333 throw e.rethrowFromSystemServer(); 1334 } 1335 } 1336 1337 /** 1338 * Reset all paired devices setup by the caller by 1339 * {@link DiscoverySession#initiatePairingRequest(PeerHandle, String, int, String)} and 1340 * {@link DiscoverySession#acceptPairingRequest(int, PeerHandle, String, int, String)} 1341 */ 1342 @RequiresPermission(CHANGE_WIFI_STATE) resetPairedDevices()1343 public void resetPairedDevices() { 1344 try { 1345 mService.resetPairedDevices(mContext.getOpPackageName()); 1346 } catch (RemoteException e) { 1347 throw e.rethrowFromSystemServer(); 1348 } 1349 } 1350 1351 /** 1352 * Remove the target paired device setup by the caller by 1353 * {@link DiscoverySession#initiatePairingRequest(PeerHandle, String, int, String)} and 1354 * {@link DiscoverySession#acceptPairingRequest(int, PeerHandle, String, int, String)} 1355 * @param alias The alias set by the caller 1356 */ 1357 @RequiresPermission(CHANGE_WIFI_STATE) removePairedDevice(@onNull String alias)1358 public void removePairedDevice(@NonNull String alias) { 1359 try { 1360 mService.removePairedDevice(mContext.getOpPackageName(), alias); 1361 } catch (RemoteException e) { 1362 throw e.rethrowFromSystemServer(); 1363 } 1364 } 1365 1366 /** 1367 * Get all the paired devices configured by the calling app. 1368 * @param executor The executor on which callback will be invoked. 1369 * @param resultsCallback An asynchronous callback that will return a list of paired devices' 1370 * alias 1371 */ 1372 @RequiresPermission(ACCESS_WIFI_STATE) getPairedDevices(@onNull Executor executor, @NonNull Consumer<List<String>> resultsCallback)1373 public void getPairedDevices(@NonNull Executor executor, 1374 @NonNull Consumer<List<String>> resultsCallback) { 1375 Objects.requireNonNull(executor, "executor cannot be null"); 1376 Objects.requireNonNull(resultsCallback, "resultsCallback cannot be null"); 1377 try { 1378 mService.getPairedDevices( 1379 mContext.getOpPackageName(), 1380 new IListListener.Stub() { 1381 public void onResult(List value) { 1382 Binder.clearCallingIdentity(); 1383 executor.execute(() -> resultsCallback.accept(value)); 1384 } 1385 }); 1386 } catch (RemoteException e) { 1387 throw e.rethrowFromSystemServer(); 1388 } 1389 } 1390 1391 /** 1392 * @hide 1393 */ suspend(int clientId, int sessionId)1394 public void suspend(int clientId, int sessionId) { 1395 try { 1396 mService.suspend(clientId, sessionId); 1397 } catch (RemoteException e) { 1398 throw e.rethrowFromSystemServer(); 1399 } 1400 } 1401 1402 /** 1403 * @hide 1404 */ resume(int clientId, int sessionId)1405 public void resume(int clientId, int sessionId) { 1406 try { 1407 mService.resume(clientId, sessionId); 1408 } catch (RemoteException e) { 1409 throw e.rethrowFromSystemServer(); 1410 } 1411 } 1412 /** 1413 * Attach to the Wi-Fi Aware service as an offload session. All discovery sessions and 1414 * connections will be handled via out-of-band connections. 1415 * The Aware session created by this attach method will have the lowest priority when resource 1416 * conflicts arise (e.g. Aware has to be torn down to create other WiFi interfaces). 1417 * 1418 * @param executor The executor to execute the listener of the {@code attachCallback} 1419 * object. 1420 * @param attachCallback A callback for attach events, extended from 1421 * {@link AttachCallback}. 1422 * @hide 1423 * @see #attach(AttachCallback, Handler) 1424 */ 1425 @SystemApi 1426 @RequiresPermission(allOf = {ACCESS_WIFI_STATE, CHANGE_WIFI_STATE, OVERRIDE_WIFI_CONFIG}) attachOffload(@onNull @allbackExecutor Executor executor, @NonNull AttachCallback attachCallback)1427 public void attachOffload(@NonNull @CallbackExecutor Executor executor, 1428 @NonNull AttachCallback attachCallback) { 1429 if (executor == null) { 1430 throw new IllegalArgumentException("Null executor provided"); 1431 } 1432 attach(null, null, attachCallback, null, true, executor); 1433 } 1434 1435 } 1436