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 android.annotation.IntDef; 20 import android.annotation.NonNull; 21 import android.annotation.Nullable; 22 import android.annotation.RequiresPermission; 23 import android.annotation.SdkConstant; 24 import android.annotation.SdkConstant.SdkConstantType; 25 import android.annotation.SystemApi; 26 import android.annotation.SystemService; 27 import android.content.Context; 28 import android.net.ConnectivityManager; 29 import android.net.NetworkRequest; 30 import android.net.NetworkSpecifier; 31 import android.net.wifi.util.HexEncoding; 32 import android.os.Binder; 33 import android.os.Build; 34 import android.os.Bundle; 35 import android.os.Handler; 36 import android.os.Looper; 37 import android.os.Message; 38 import android.os.RemoteException; 39 import android.util.Log; 40 41 import androidx.annotation.RequiresApi; 42 43 import com.android.modules.utils.build.SdkLevel; 44 45 import java.lang.annotation.Retention; 46 import java.lang.annotation.RetentionPolicy; 47 import java.lang.ref.WeakReference; 48 import java.nio.BufferOverflowException; 49 import java.util.List; 50 51 /** 52 * This class provides the primary API for managing Wi-Fi Aware operations: 53 * discovery and peer-to-peer data connections. 54 * <p> 55 * The class provides access to: 56 * <ul> 57 * <li>Initialize a Aware cluster (peer-to-peer synchronization). Refer to 58 * {@link #attach(AttachCallback, Handler)}. 59 * <li>Create discovery sessions (publish or subscribe sessions). Refer to 60 * {@link WifiAwareSession#publish(PublishConfig, DiscoverySessionCallback, Handler)} and 61 * {@link WifiAwareSession#subscribe(SubscribeConfig, DiscoverySessionCallback, Handler)}. 62 * <li>Create a Aware network specifier to be used with 63 * {@link ConnectivityManager#requestNetwork(NetworkRequest, ConnectivityManager.NetworkCallback)} 64 * to set-up a Aware connection with a peer. Refer to {@link WifiAwareNetworkSpecifier.Builder}. 65 * </ul> 66 * <p> 67 * Aware may not be usable when Wi-Fi is disabled (and other conditions). To validate that 68 * the functionality is available use the {@link #isAvailable()} function. To track 69 * changes in Aware usability register for the {@link #ACTION_WIFI_AWARE_STATE_CHANGED} 70 * broadcast. Note that this broadcast is not sticky - you should register for it and then 71 * check the above API to avoid a race condition. 72 * <p> 73 * An application must use {@link #attach(AttachCallback, Handler)} to initialize a 74 * Aware cluster - before making any other Aware operation. Aware cluster membership is a 75 * device-wide operation - the API guarantees that the device is in a cluster or joins a 76 * Aware cluster (or starts one if none can be found). Information about attach success (or 77 * failure) are returned in callbacks of {@link AttachCallback}. Proceed with Aware 78 * discovery or connection setup only after receiving confirmation that Aware attach 79 * succeeded - {@link AttachCallback#onAttached(WifiAwareSession)}. When an 80 * application is finished using Aware it <b>must</b> use the 81 * {@link WifiAwareSession#close()} API to indicate to the Aware service that the device 82 * may detach from the Aware cluster. The device will actually disable Aware once the last 83 * application detaches. 84 * <p> 85 * Once a Aware attach is confirmed use the 86 * {@link WifiAwareSession#publish(PublishConfig, DiscoverySessionCallback, Handler)} 87 * or 88 * {@link WifiAwareSession#subscribe(SubscribeConfig, DiscoverySessionCallback, 89 * Handler)} to create publish or subscribe Aware discovery sessions. Events are called on the 90 * provided callback object {@link DiscoverySessionCallback}. Specifically, the 91 * {@link DiscoverySessionCallback#onPublishStarted(PublishDiscoverySession)} 92 * and 93 * {@link DiscoverySessionCallback#onSubscribeStarted( 94 *SubscribeDiscoverySession)} 95 * return {@link PublishDiscoverySession} and 96 * {@link SubscribeDiscoverySession} 97 * objects respectively on which additional session operations can be performed, e.g. updating 98 * the session {@link PublishDiscoverySession#updatePublish(PublishConfig)} and 99 * {@link SubscribeDiscoverySession#updateSubscribe(SubscribeConfig)}. Sessions can 100 * also be used to send messages using the 101 * {@link DiscoverySession#sendMessage(PeerHandle, int, byte[])} APIs. When an 102 * application is finished with a discovery session it <b>must</b> terminate it using the 103 * {@link DiscoverySession#close()} API. 104 * <p> 105 * Creating connections between Aware devices is managed by the standard 106 * {@link ConnectivityManager#requestNetwork(NetworkRequest, 107 * ConnectivityManager.NetworkCallback)}. 108 * The {@link NetworkRequest} object should be constructed with: 109 * <ul> 110 * <li>{@link NetworkRequest.Builder#addTransportType(int)} of 111 * {@link android.net.NetworkCapabilities#TRANSPORT_WIFI_AWARE}. 112 * <li>{@link NetworkRequest.Builder#setNetworkSpecifier(String)} using 113 * {@link WifiAwareNetworkSpecifier.Builder}. 114 * </ul> 115 */ 116 @SystemService(Context.WIFI_AWARE_SERVICE) 117 public class WifiAwareManager { 118 private static final String TAG = "WifiAwareManager"; 119 private static final boolean DBG = false; 120 private static final boolean VDBG = false; // STOPSHIP if true 121 122 /** 123 * Broadcast intent action to indicate that the state of Wi-Fi Aware availability has changed 124 * and all active Aware sessions are no longer usable. Use the {@link #isAvailable()} to query 125 * the current status. 126 * This broadcast is <b>not</b> sticky, use the {@link #isAvailable()} API after registering 127 * the broadcast to check the current state of Wi-Fi Aware. 128 * <p>Note: The broadcast is only delivered to registered receivers - no manifest registered 129 * components will be launched. 130 */ 131 @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION) 132 public static final String ACTION_WIFI_AWARE_STATE_CHANGED = 133 "android.net.wifi.aware.action.WIFI_AWARE_STATE_CHANGED"; 134 135 /** @hide */ 136 @IntDef({ 137 WIFI_AWARE_DATA_PATH_ROLE_INITIATOR, WIFI_AWARE_DATA_PATH_ROLE_RESPONDER}) 138 @Retention(RetentionPolicy.SOURCE) 139 public @interface DataPathRole { 140 } 141 142 /** 143 * Connection creation role is that of INITIATOR. Used to create a network specifier string 144 * when requesting a Aware network. 145 * 146 * @see WifiAwareSession#createNetworkSpecifierOpen(int, byte[]) 147 * @see WifiAwareSession#createNetworkSpecifierPassphrase(int, byte[], String) 148 */ 149 public static final int WIFI_AWARE_DATA_PATH_ROLE_INITIATOR = 0; 150 151 /** 152 * Connection creation role is that of RESPONDER. Used to create a network specifier string 153 * when requesting a Aware network. 154 * 155 * @see WifiAwareSession#createNetworkSpecifierOpen(int, byte[]) 156 * @see WifiAwareSession#createNetworkSpecifierPassphrase(int, byte[], String) 157 */ 158 public static final int WIFI_AWARE_DATA_PATH_ROLE_RESPONDER = 1; 159 160 /** @hide */ 161 @IntDef({ 162 WIFI_AWARE_DISCOVERY_LOST_REASON_UNKNOWN, 163 WIFI_AWARE_DISCOVERY_LOST_REASON_PEER_NOT_VISIBLE}) 164 @Retention(RetentionPolicy.SOURCE) 165 public @interface DiscoveryLostReasonCode { 166 } 167 168 /** 169 * Reason code provided in {@link DiscoverySessionCallback#onServiceLost(PeerHandle, int)} 170 * indicating that the service was lost for unknown reason. 171 */ 172 public static final int WIFI_AWARE_DISCOVERY_LOST_REASON_UNKNOWN = 0; 173 174 /** 175 * Reason code provided in {@link DiscoverySessionCallback#onServiceLost(PeerHandle, int)} 176 * indicating that the service advertised by the peer is no longer visible. This may be because 177 * the peer is out of range or because the peer stopped advertising this service. 178 */ 179 public static final int WIFI_AWARE_DISCOVERY_LOST_REASON_PEER_NOT_VISIBLE = 1; 180 181 private final Context mContext; 182 private final IWifiAwareManager mService; 183 184 private final Object mLock = new Object(); // lock access to the following vars 185 186 /** @hide */ WifiAwareManager(@onNull Context context, @NonNull IWifiAwareManager service)187 public WifiAwareManager(@NonNull Context context, @NonNull IWifiAwareManager service) { 188 mContext = context; 189 mService = service; 190 } 191 192 /** 193 * Returns the current status of Aware API: whether or not Aware is available. To track 194 * changes in the state of Aware API register for the 195 * {@link #ACTION_WIFI_AWARE_STATE_CHANGED} broadcast. 196 * 197 * @return A boolean indicating whether the app can use the Aware API at this time (true) or 198 * not (false). 199 */ isAvailable()200 public boolean isAvailable() { 201 try { 202 return mService.isUsageEnabled(); 203 } catch (RemoteException e) { 204 throw e.rethrowFromSystemServer(); 205 } 206 } 207 208 /** 209 * Return the current status of the Aware service: whether or not the device is already attached 210 * to an Aware cluster. To attach to an Aware cluster, please use 211 * {@link #attach(AttachCallback, Handler)} or 212 * {@link #attach(AttachCallback, IdentityChangedListener, Handler)}. 213 * @return A boolean indicating whether the device is attached to a cluster at this time (true) 214 * or not (false). 215 */ isDeviceAttached()216 public boolean isDeviceAttached() { 217 try { 218 return mService.isDeviceAttached(); 219 } catch (RemoteException e) { 220 throw e.rethrowFromSystemServer(); 221 } 222 } 223 224 /** 225 * Enable the Wifi Aware Instant communication mode. If the device doesn't support this feature 226 * calling this API will result no action. 227 * @see Characteristics#isInstantCommunicationModeSupported() 228 * @param enable true for enable, false otherwise. 229 * @hide 230 */ 231 @SystemApi 232 @RequiresApi(Build.VERSION_CODES.S) enableInstantCommunicationMode(boolean enable)233 public void enableInstantCommunicationMode(boolean enable) { 234 if (!SdkLevel.isAtLeastS()) { 235 throw new UnsupportedOperationException(); 236 } 237 try { 238 mService.enableInstantCommunicationMode(mContext.getOpPackageName(), enable); 239 } catch (RemoteException e) { 240 throw e.rethrowFromSystemServer(); 241 } 242 } 243 244 /** 245 * Return the current status of the Wifi Aware instant communication mode. 246 * If the device doesn't support this feature, return will always be false. 247 * @see Characteristics#isInstantCommunicationModeSupported() 248 * @return true if it is enabled, false otherwise. 249 */ 250 @RequiresApi(Build.VERSION_CODES.S) isInstantCommunicationModeEnabled()251 public boolean isInstantCommunicationModeEnabled() { 252 if (!SdkLevel.isAtLeastS()) { 253 throw new UnsupportedOperationException(); 254 } 255 try { 256 return mService.isInstantCommunicationModeEnabled(); 257 } catch (RemoteException e) { 258 throw e.rethrowFromSystemServer(); 259 } 260 } 261 262 /** 263 * Returns the characteristics of the Wi-Fi Aware interface: a set of parameters which specify 264 * limitations on configurations, e.g. the maximum service name length. 265 * <p> 266 * May return {@code null} if the Wi-Fi Aware service is not initialized. Use 267 * {@link #attach(AttachCallback, Handler)} or 268 * {@link #attach(AttachCallback, IdentityChangedListener, Handler)} to initialize the Wi-Fi 269 * Aware service. 270 * 271 * @return An object specifying configuration limitations of Aware. 272 */ getCharacteristics()273 public @Nullable Characteristics getCharacteristics() { 274 try { 275 return mService.getCharacteristics(); 276 } catch (RemoteException e) { 277 throw e.rethrowFromSystemServer(); 278 } 279 } 280 281 /** 282 * Return the available resources of the Wi-Fi aware service: a set of parameters which specify 283 * limitations on service usage, e.g the number of data-paths which could be created. 284 * <p> 285 * May return {@code null} if the Wi-Fi Aware service is not initialized. Use 286 * {@link #attach(AttachCallback, Handler)} or 287 * {@link #attach(AttachCallback, IdentityChangedListener, Handler)} to initialize the Wi-Fi 288 * Aware service. 289 * 290 * @return An object specifying the currently available resource of the Wi-Fi Aware service. 291 */ getAvailableAwareResources()292 public @Nullable AwareResources getAvailableAwareResources() { 293 try { 294 return mService.getAvailableAwareResources(); 295 } catch (RemoteException e) { 296 throw e.rethrowFromSystemServer(); 297 } 298 } 299 300 /** 301 * Attach to the Wi-Fi Aware service - enabling the application to create discovery sessions or 302 * create connections to peers. The device will attach to an existing cluster if it can find 303 * one or create a new cluster (if it is the first to enable Aware in its vicinity). Results 304 * (e.g. successful attach to a cluster) are provided to the {@code attachCallback} object. 305 * An application <b>must</b> call {@link WifiAwareSession#close()} when done with the 306 * Wi-Fi Aware object. 307 * <p> 308 * Note: a Aware cluster is a shared resource - if the device is already attached to a cluster 309 * then this function will simply indicate success immediately using the same {@code 310 * attachCallback}. 311 * 312 * @param attachCallback A callback for attach events, extended from 313 * {@link AttachCallback}. 314 * @param handler The Handler on whose thread to execute the callbacks of the {@code 315 * attachCallback} object. If a null is provided then the application's main thread will be 316 * used. 317 */ attach(@onNull AttachCallback attachCallback, @Nullable Handler handler)318 public void attach(@NonNull AttachCallback attachCallback, @Nullable Handler handler) { 319 attach(handler, null, attachCallback, null); 320 } 321 322 /** 323 * Attach to the Wi-Fi Aware service - enabling the application to create discovery sessions or 324 * create connections to peers. The device will attach to an existing cluster if it can find 325 * one or create a new cluster (if it is the first to enable Aware in its vicinity). Results 326 * (e.g. successful attach to a cluster) are provided to the {@code attachCallback} object. 327 * An application <b>must</b> call {@link WifiAwareSession#close()} when done with the 328 * Wi-Fi Aware object. 329 * <p> 330 * Note: a Aware cluster is a shared resource - if the device is already attached to a cluster 331 * then this function will simply indicate success immediately using the same {@code 332 * attachCallback}. 333 * <p> 334 * This version of the API attaches a listener to receive the MAC address of the Aware interface 335 * on startup and whenever it is updated (it is randomized at regular intervals for privacy). 336 * The application must have the {@link android.Manifest.permission#ACCESS_FINE_LOCATION} 337 * permission to execute this attach request. Otherwise, use the 338 * {@link #attach(AttachCallback, Handler)} version. Note that aside from permission 339 * requirements this listener will wake up the host at regular intervals causing higher power 340 * consumption, do not use it unless the information is necessary (e.g. for OOB discovery). 341 * 342 * @param attachCallback A callback for attach events, extended from 343 * {@link AttachCallback}. 344 * @param identityChangedListener A listener for changed identity, extended from 345 * {@link IdentityChangedListener}. 346 * @param handler The Handler on whose thread to execute the callbacks of the {@code 347 * attachCallback} and {@code identityChangedListener} objects. If a null is provided then the 348 * application's main thread will be used. 349 */ attach(@onNull AttachCallback attachCallback, @NonNull IdentityChangedListener identityChangedListener, @Nullable Handler handler)350 public void attach(@NonNull AttachCallback attachCallback, 351 @NonNull IdentityChangedListener identityChangedListener, 352 @Nullable Handler handler) { 353 attach(handler, null, attachCallback, identityChangedListener); 354 } 355 356 /** @hide */ attach(Handler handler, ConfigRequest configRequest, AttachCallback attachCallback, IdentityChangedListener identityChangedListener)357 public void attach(Handler handler, ConfigRequest configRequest, 358 AttachCallback attachCallback, 359 IdentityChangedListener identityChangedListener) { 360 if (VDBG) { 361 Log.v(TAG, "attach(): handler=" + handler + ", callback=" + attachCallback 362 + ", configRequest=" + configRequest + ", identityChangedListener=" 363 + identityChangedListener); 364 } 365 366 if (attachCallback == null) { 367 throw new IllegalArgumentException("Null callback provided"); 368 } 369 370 synchronized (mLock) { 371 Looper looper = (handler == null) ? Looper.getMainLooper() : handler.getLooper(); 372 373 try { 374 Binder binder = new Binder(); 375 mService.connect(binder, mContext.getOpPackageName(), mContext.getAttributionTag(), 376 new WifiAwareEventCallbackProxy(this, looper, binder, attachCallback, 377 identityChangedListener), configRequest, 378 identityChangedListener != null); 379 } catch (RemoteException e) { 380 throw e.rethrowFromSystemServer(); 381 } 382 } 383 } 384 385 /** @hide */ disconnect(int clientId, Binder binder)386 public void disconnect(int clientId, Binder binder) { 387 if (VDBG) Log.v(TAG, "disconnect()"); 388 389 try { 390 mService.disconnect(clientId, binder); 391 } catch (RemoteException e) { 392 throw e.rethrowFromSystemServer(); 393 } 394 } 395 396 /** @hide */ publish(int clientId, Looper looper, PublishConfig publishConfig, DiscoverySessionCallback callback)397 public void publish(int clientId, Looper looper, PublishConfig publishConfig, 398 DiscoverySessionCallback callback) { 399 if (VDBG) Log.v(TAG, "publish(): clientId=" + clientId + ", config=" + publishConfig); 400 401 if (callback == null) { 402 throw new IllegalArgumentException("Null callback provided"); 403 } 404 405 try { 406 mService.publish(mContext.getOpPackageName(), mContext.getAttributionTag(), clientId, 407 publishConfig, 408 new WifiAwareDiscoverySessionCallbackProxy(this, looper, true, callback, 409 clientId)); 410 } catch (RemoteException e) { 411 throw e.rethrowFromSystemServer(); 412 } 413 } 414 415 /** @hide */ updatePublish(int clientId, int sessionId, PublishConfig publishConfig)416 public void updatePublish(int clientId, int sessionId, PublishConfig publishConfig) { 417 if (VDBG) { 418 Log.v(TAG, "updatePublish(): clientId=" + clientId + ",sessionId=" + sessionId 419 + ", config=" + publishConfig); 420 } 421 422 try { 423 mService.updatePublish(clientId, sessionId, publishConfig); 424 } catch (RemoteException e) { 425 throw e.rethrowFromSystemServer(); 426 } 427 } 428 429 /** @hide */ subscribe(int clientId, Looper looper, SubscribeConfig subscribeConfig, DiscoverySessionCallback callback)430 public void subscribe(int clientId, Looper looper, SubscribeConfig subscribeConfig, 431 DiscoverySessionCallback callback) { 432 if (VDBG) { 433 if (VDBG) { 434 Log.v(TAG, 435 "subscribe(): clientId=" + clientId + ", config=" + subscribeConfig); 436 } 437 } 438 439 if (callback == null) { 440 throw new IllegalArgumentException("Null callback provided"); 441 } 442 443 try { 444 mService.subscribe(mContext.getOpPackageName(), mContext.getAttributionTag(), clientId, 445 subscribeConfig, 446 new WifiAwareDiscoverySessionCallbackProxy(this, looper, false, callback, 447 clientId)); 448 } catch (RemoteException e) { 449 throw e.rethrowFromSystemServer(); 450 } 451 } 452 453 /** @hide */ updateSubscribe(int clientId, int sessionId, SubscribeConfig subscribeConfig)454 public void updateSubscribe(int clientId, int sessionId, SubscribeConfig subscribeConfig) { 455 if (VDBG) { 456 Log.v(TAG, "updateSubscribe(): clientId=" + clientId + ",sessionId=" + sessionId 457 + ", config=" + subscribeConfig); 458 } 459 460 try { 461 mService.updateSubscribe(clientId, sessionId, subscribeConfig); 462 } catch (RemoteException e) { 463 throw e.rethrowFromSystemServer(); 464 } 465 } 466 467 /** @hide */ terminateSession(int clientId, int sessionId)468 public void terminateSession(int clientId, int sessionId) { 469 if (VDBG) { 470 Log.d(TAG, 471 "terminateSession(): clientId=" + clientId + ", sessionId=" + sessionId); 472 } 473 474 try { 475 mService.terminateSession(clientId, sessionId); 476 } catch (RemoteException e) { 477 throw e.rethrowFromSystemServer(); 478 } 479 } 480 481 /** @hide */ sendMessage(int clientId, int sessionId, PeerHandle peerHandle, byte[] message, int messageId, int retryCount)482 public void sendMessage(int clientId, int sessionId, PeerHandle peerHandle, byte[] message, 483 int messageId, int retryCount) { 484 if (peerHandle == null) { 485 throw new IllegalArgumentException( 486 "sendMessage: invalid peerHandle - must be non-null"); 487 } 488 489 if (VDBG) { 490 Log.v(TAG, "sendMessage(): clientId=" + clientId + ", sessionId=" + sessionId 491 + ", peerHandle=" + peerHandle.peerId + ", messageId=" 492 + messageId + ", retryCount=" + retryCount); 493 } 494 495 try { 496 mService.sendMessage(clientId, sessionId, peerHandle.peerId, message, messageId, 497 retryCount); 498 } catch (RemoteException e) { 499 throw e.rethrowFromSystemServer(); 500 } 501 } 502 503 /** @hide */ 504 @RequiresPermission(android.Manifest.permission.NETWORK_STACK) requestMacAddresses(int uid, List<Integer> peerIds, IWifiAwareMacAddressProvider callback)505 public void requestMacAddresses(int uid, List<Integer> peerIds, 506 IWifiAwareMacAddressProvider callback) { 507 try { 508 mService.requestMacAddresses(uid, peerIds, callback); 509 } catch (RemoteException e) { 510 throw e.rethrowFromSystemServer(); 511 } 512 } 513 514 /** @hide */ createNetworkSpecifier(int clientId, int role, int sessionId, @NonNull PeerHandle peerHandle, @Nullable byte[] pmk, @Nullable String passphrase)515 public NetworkSpecifier createNetworkSpecifier(int clientId, int role, int sessionId, 516 @NonNull PeerHandle peerHandle, @Nullable byte[] pmk, @Nullable String passphrase) { 517 if (VDBG) { 518 Log.v(TAG, "createNetworkSpecifier: role=" + role + ", sessionId=" + sessionId 519 + ", peerHandle=" + ((peerHandle == null) ? peerHandle : peerHandle.peerId) 520 + ", pmk=" + ((pmk == null) ? "null" : "non-null") 521 + ", passphrase=" + ((passphrase == null) ? "null" : "non-null")); 522 } 523 524 if (!WifiAwareUtils.isLegacyVersion(mContext, Build.VERSION_CODES.Q)) { 525 throw new UnsupportedOperationException( 526 "API deprecated - use WifiAwareNetworkSpecifier.Builder"); 527 } 528 529 if (role != WIFI_AWARE_DATA_PATH_ROLE_INITIATOR 530 && role != WIFI_AWARE_DATA_PATH_ROLE_RESPONDER) { 531 throw new IllegalArgumentException( 532 "createNetworkSpecifier: Invalid 'role' argument when creating a network " 533 + "specifier"); 534 } 535 if (role == WIFI_AWARE_DATA_PATH_ROLE_INITIATOR || !WifiAwareUtils.isLegacyVersion(mContext, 536 Build.VERSION_CODES.P)) { 537 if (peerHandle == null) { 538 throw new IllegalArgumentException( 539 "createNetworkSpecifier: Invalid peer handle - cannot be null"); 540 } 541 } 542 543 return new WifiAwareNetworkSpecifier( 544 (peerHandle == null) ? WifiAwareNetworkSpecifier.NETWORK_SPECIFIER_TYPE_IB_ANY_PEER 545 : WifiAwareNetworkSpecifier.NETWORK_SPECIFIER_TYPE_IB, 546 role, 547 clientId, 548 sessionId, 549 peerHandle != null ? peerHandle.peerId : 0, // 0 is an invalid peer ID 550 null, // peerMac (not used in this method) 551 pmk, 552 passphrase, 553 0, // no port info for deprecated IB APIs 554 -1); // no transport info for deprecated IB APIs 555 } 556 557 /** @hide */ createNetworkSpecifier(int clientId, @DataPathRole int role, @NonNull byte[] peer, @Nullable byte[] pmk, @Nullable String passphrase)558 public NetworkSpecifier createNetworkSpecifier(int clientId, @DataPathRole int role, 559 @NonNull byte[] peer, @Nullable byte[] pmk, @Nullable String passphrase) { 560 if (VDBG) { 561 Log.v(TAG, "createNetworkSpecifier: role=" + role 562 + ", pmk=" + ((pmk == null) ? "null" : "non-null") 563 + ", passphrase=" + ((passphrase == null) ? "null" : "non-null")); 564 } 565 566 if (role != WIFI_AWARE_DATA_PATH_ROLE_INITIATOR 567 && role != WIFI_AWARE_DATA_PATH_ROLE_RESPONDER) { 568 throw new IllegalArgumentException( 569 "createNetworkSpecifier: Invalid 'role' argument when creating a network " 570 + "specifier"); 571 } 572 if (role == WIFI_AWARE_DATA_PATH_ROLE_INITIATOR || !WifiAwareUtils.isLegacyVersion(mContext, 573 Build.VERSION_CODES.P)) { 574 if (peer == null) { 575 throw new IllegalArgumentException( 576 "createNetworkSpecifier: Invalid peer MAC - cannot be null"); 577 } 578 } 579 if (peer != null && peer.length != 6) { 580 throw new IllegalArgumentException("createNetworkSpecifier: Invalid peer MAC address"); 581 } 582 583 return new WifiAwareNetworkSpecifier( 584 (peer == null) ? WifiAwareNetworkSpecifier.NETWORK_SPECIFIER_TYPE_OOB_ANY_PEER 585 : WifiAwareNetworkSpecifier.NETWORK_SPECIFIER_TYPE_OOB, 586 role, 587 clientId, 588 0, // 0 is an invalid session ID 589 0, // 0 is an invalid peer ID 590 peer, 591 pmk, 592 passphrase, 593 0, // no port info for OOB APIs 594 -1); // no transport protocol info for OOB APIs 595 } 596 597 private static class WifiAwareEventCallbackProxy extends IWifiAwareEventCallback.Stub { 598 private static final int CALLBACK_CONNECT_SUCCESS = 0; 599 private static final int CALLBACK_CONNECT_FAIL = 1; 600 private static final int CALLBACK_IDENTITY_CHANGED = 2; 601 602 private final Handler mHandler; 603 private final WeakReference<WifiAwareManager> mAwareManager; 604 private final Binder mBinder; 605 private final Looper mLooper; 606 607 /** 608 * Constructs a {@link AttachCallback} using the specified looper. 609 * All callbacks will delivered on the thread of the specified looper. 610 * 611 * @param looper The looper on which to execute the callbacks. 612 */ WifiAwareEventCallbackProxy(WifiAwareManager mgr, Looper looper, Binder binder, final AttachCallback attachCallback, final IdentityChangedListener identityChangedListener)613 WifiAwareEventCallbackProxy(WifiAwareManager mgr, Looper looper, Binder binder, 614 final AttachCallback attachCallback, 615 final IdentityChangedListener identityChangedListener) { 616 mAwareManager = new WeakReference<>(mgr); 617 mLooper = looper; 618 mBinder = binder; 619 620 if (VDBG) Log.v(TAG, "WifiAwareEventCallbackProxy ctor: looper=" + looper); 621 mHandler = new Handler(looper) { 622 @Override 623 public void handleMessage(Message msg) { 624 if (DBG) { 625 Log.d(TAG, "WifiAwareEventCallbackProxy: What=" + msg.what + ", msg=" 626 + msg); 627 } 628 629 WifiAwareManager mgr = mAwareManager.get(); 630 if (mgr == null) { 631 Log.w(TAG, "WifiAwareEventCallbackProxy: handleMessage post GC"); 632 return; 633 } 634 635 switch (msg.what) { 636 case CALLBACK_CONNECT_SUCCESS: 637 attachCallback.onAttached( 638 new WifiAwareSession(mgr, mBinder, msg.arg1)); 639 break; 640 case CALLBACK_CONNECT_FAIL: 641 mAwareManager.clear(); 642 attachCallback.onAttachFailed(); 643 break; 644 case CALLBACK_IDENTITY_CHANGED: 645 if (identityChangedListener == null) { 646 Log.e(TAG, "CALLBACK_IDENTITY_CHANGED: null listener."); 647 } else { 648 identityChangedListener.onIdentityChanged((byte[]) msg.obj); 649 } 650 break; 651 } 652 } 653 }; 654 } 655 656 @Override onConnectSuccess(int clientId)657 public void onConnectSuccess(int clientId) { 658 if (VDBG) Log.v(TAG, "onConnectSuccess"); 659 660 Message msg = mHandler.obtainMessage(CALLBACK_CONNECT_SUCCESS); 661 msg.arg1 = clientId; 662 mHandler.sendMessage(msg); 663 } 664 665 @Override onConnectFail(int reason)666 public void onConnectFail(int reason) { 667 if (VDBG) Log.v(TAG, "onConnectFail: reason=" + reason); 668 669 Message msg = mHandler.obtainMessage(CALLBACK_CONNECT_FAIL); 670 msg.arg1 = reason; 671 mHandler.sendMessage(msg); 672 } 673 674 @Override onIdentityChanged(byte[] mac)675 public void onIdentityChanged(byte[] mac) { 676 if (VDBG) Log.v(TAG, "onIdentityChanged: mac=" + new String(HexEncoding.encode(mac))); 677 678 Message msg = mHandler.obtainMessage(CALLBACK_IDENTITY_CHANGED); 679 msg.obj = mac; 680 mHandler.sendMessage(msg); 681 } 682 } 683 684 private static class WifiAwareDiscoverySessionCallbackProxy extends 685 IWifiAwareDiscoverySessionCallback.Stub { 686 private static final int CALLBACK_SESSION_STARTED = 0; 687 private static final int CALLBACK_SESSION_CONFIG_SUCCESS = 1; 688 private static final int CALLBACK_SESSION_CONFIG_FAIL = 2; 689 private static final int CALLBACK_SESSION_TERMINATED = 3; 690 private static final int CALLBACK_MATCH = 4; 691 private static final int CALLBACK_MESSAGE_SEND_SUCCESS = 5; 692 private static final int CALLBACK_MESSAGE_SEND_FAIL = 6; 693 private static final int CALLBACK_MESSAGE_RECEIVED = 7; 694 private static final int CALLBACK_MATCH_WITH_DISTANCE = 8; 695 private static final int CALLBACK_MATCH_EXPIRED = 9; 696 697 private static final String MESSAGE_BUNDLE_KEY_MESSAGE = "message"; 698 private static final String MESSAGE_BUNDLE_KEY_MESSAGE2 = "message2"; 699 700 private final WeakReference<WifiAwareManager> mAwareManager; 701 private final boolean mIsPublish; 702 private final DiscoverySessionCallback mOriginalCallback; 703 private final int mClientId; 704 705 private final Handler mHandler; 706 private DiscoverySession mSession; 707 WifiAwareDiscoverySessionCallbackProxy(WifiAwareManager mgr, Looper looper, boolean isPublish, DiscoverySessionCallback originalCallback, int clientId)708 WifiAwareDiscoverySessionCallbackProxy(WifiAwareManager mgr, Looper looper, 709 boolean isPublish, DiscoverySessionCallback originalCallback, 710 int clientId) { 711 mAwareManager = new WeakReference<>(mgr); 712 mIsPublish = isPublish; 713 mOriginalCallback = originalCallback; 714 mClientId = clientId; 715 716 if (VDBG) { 717 Log.v(TAG, "WifiAwareDiscoverySessionCallbackProxy ctor: isPublish=" + isPublish); 718 } 719 720 mHandler = new Handler(looper) { 721 @Override 722 public void handleMessage(Message msg) { 723 if (DBG) Log.d(TAG, "What=" + msg.what + ", msg=" + msg); 724 725 if (mAwareManager.get() == null) { 726 Log.w(TAG, "WifiAwareDiscoverySessionCallbackProxy: handleMessage post GC"); 727 return; 728 } 729 730 switch (msg.what) { 731 case CALLBACK_SESSION_STARTED: 732 onProxySessionStarted(msg.arg1); 733 break; 734 case CALLBACK_SESSION_CONFIG_SUCCESS: 735 mOriginalCallback.onSessionConfigUpdated(); 736 break; 737 case CALLBACK_SESSION_CONFIG_FAIL: 738 mOriginalCallback.onSessionConfigFailed(); 739 if (mSession == null) { 740 /* 741 * creation failed (as opposed to update 742 * failing) 743 */ 744 mAwareManager.clear(); 745 } 746 break; 747 case CALLBACK_SESSION_TERMINATED: 748 onProxySessionTerminated(msg.arg1); 749 break; 750 case CALLBACK_MATCH: 751 case CALLBACK_MATCH_WITH_DISTANCE: 752 { 753 List<byte[]> matchFilter = null; 754 byte[] arg = msg.getData().getByteArray(MESSAGE_BUNDLE_KEY_MESSAGE2); 755 try { 756 matchFilter = new TlvBufferUtils.TlvIterable(0, 1, arg).toList(); 757 } catch (BufferOverflowException e) { 758 matchFilter = null; 759 Log.e(TAG, "onServiceDiscovered: invalid match filter byte array '" 760 + new String(HexEncoding.encode(arg)) 761 + "' - cannot be parsed: e=" + e); 762 } 763 if (msg.what == CALLBACK_MATCH) { 764 mOriginalCallback.onServiceDiscovered(new PeerHandle(msg.arg1), 765 msg.getData().getByteArray(MESSAGE_BUNDLE_KEY_MESSAGE), 766 matchFilter); 767 } else { 768 mOriginalCallback.onServiceDiscoveredWithinRange( 769 new PeerHandle(msg.arg1), 770 msg.getData().getByteArray(MESSAGE_BUNDLE_KEY_MESSAGE), 771 matchFilter, msg.arg2); 772 } 773 break; 774 } 775 case CALLBACK_MESSAGE_SEND_SUCCESS: 776 mOriginalCallback.onMessageSendSucceeded(msg.arg1); 777 break; 778 case CALLBACK_MESSAGE_SEND_FAIL: 779 mOriginalCallback.onMessageSendFailed(msg.arg1); 780 break; 781 case CALLBACK_MESSAGE_RECEIVED: 782 mOriginalCallback.onMessageReceived(new PeerHandle(msg.arg1), 783 (byte[]) msg.obj); 784 break; 785 case CALLBACK_MATCH_EXPIRED: 786 mOriginalCallback 787 .onServiceLost(new PeerHandle(msg.arg1), 788 WIFI_AWARE_DISCOVERY_LOST_REASON_PEER_NOT_VISIBLE); 789 break; 790 } 791 } 792 }; 793 } 794 795 @Override onSessionStarted(int sessionId)796 public void onSessionStarted(int sessionId) { 797 if (VDBG) Log.v(TAG, "onSessionStarted: sessionId=" + sessionId); 798 799 Message msg = mHandler.obtainMessage(CALLBACK_SESSION_STARTED); 800 msg.arg1 = sessionId; 801 mHandler.sendMessage(msg); 802 } 803 804 @Override onSessionConfigSuccess()805 public void onSessionConfigSuccess() { 806 if (VDBG) Log.v(TAG, "onSessionConfigSuccess"); 807 808 Message msg = mHandler.obtainMessage(CALLBACK_SESSION_CONFIG_SUCCESS); 809 mHandler.sendMessage(msg); 810 } 811 812 @Override onSessionConfigFail(int reason)813 public void onSessionConfigFail(int reason) { 814 if (VDBG) Log.v(TAG, "onSessionConfigFail: reason=" + reason); 815 816 Message msg = mHandler.obtainMessage(CALLBACK_SESSION_CONFIG_FAIL); 817 msg.arg1 = reason; 818 mHandler.sendMessage(msg); 819 } 820 821 @Override onSessionTerminated(int reason)822 public void onSessionTerminated(int reason) { 823 if (VDBG) Log.v(TAG, "onSessionTerminated: reason=" + reason); 824 825 Message msg = mHandler.obtainMessage(CALLBACK_SESSION_TERMINATED); 826 msg.arg1 = reason; 827 mHandler.sendMessage(msg); 828 } 829 onMatchCommon(int messageType, int peerId, byte[] serviceSpecificInfo, byte[] matchFilter, int distanceMm)830 private void onMatchCommon(int messageType, int peerId, byte[] serviceSpecificInfo, 831 byte[] matchFilter, int distanceMm) { 832 Bundle data = new Bundle(); 833 data.putByteArray(MESSAGE_BUNDLE_KEY_MESSAGE, serviceSpecificInfo); 834 data.putByteArray(MESSAGE_BUNDLE_KEY_MESSAGE2, matchFilter); 835 836 Message msg = mHandler.obtainMessage(messageType); 837 msg.arg1 = peerId; 838 msg.arg2 = distanceMm; 839 msg.setData(data); 840 mHandler.sendMessage(msg); 841 } 842 843 @Override onMatch(int peerId, byte[] serviceSpecificInfo, byte[] matchFilter)844 public void onMatch(int peerId, byte[] serviceSpecificInfo, byte[] matchFilter) { 845 if (VDBG) Log.v(TAG, "onMatch: peerId=" + peerId); 846 847 onMatchCommon(CALLBACK_MATCH, peerId, serviceSpecificInfo, matchFilter, 0); 848 } 849 850 @Override onMatchWithDistance(int peerId, byte[] serviceSpecificInfo, byte[] matchFilter, int distanceMm)851 public void onMatchWithDistance(int peerId, byte[] serviceSpecificInfo, byte[] matchFilter, 852 int distanceMm) { 853 if (VDBG) { 854 Log.v(TAG, "onMatchWithDistance: peerId=" + peerId + ", distanceMm=" + distanceMm); 855 } 856 857 onMatchCommon(CALLBACK_MATCH_WITH_DISTANCE, peerId, serviceSpecificInfo, matchFilter, 858 distanceMm); 859 } 860 @Override onMatchExpired(int peerId)861 public void onMatchExpired(int peerId) { 862 if (VDBG) { 863 Log.v(TAG, "onMatchExpired: peerId=" + peerId); 864 } 865 Message msg = mHandler.obtainMessage(CALLBACK_MATCH_EXPIRED); 866 msg.arg1 = peerId; 867 mHandler.sendMessage(msg); 868 } 869 870 @Override onMessageSendSuccess(int messageId)871 public void onMessageSendSuccess(int messageId) { 872 if (VDBG) Log.v(TAG, "onMessageSendSuccess"); 873 874 Message msg = mHandler.obtainMessage(CALLBACK_MESSAGE_SEND_SUCCESS); 875 msg.arg1 = messageId; 876 mHandler.sendMessage(msg); 877 } 878 879 @Override onMessageSendFail(int messageId, int reason)880 public void onMessageSendFail(int messageId, int reason) { 881 if (VDBG) Log.v(TAG, "onMessageSendFail: reason=" + reason); 882 883 Message msg = mHandler.obtainMessage(CALLBACK_MESSAGE_SEND_FAIL); 884 msg.arg1 = messageId; 885 msg.arg2 = reason; 886 mHandler.sendMessage(msg); 887 } 888 889 @Override onMessageReceived(int peerId, byte[] message)890 public void onMessageReceived(int peerId, byte[] message) { 891 if (VDBG) { 892 Log.v(TAG, "onMessageReceived: peerId=" + peerId); 893 } 894 895 Message msg = mHandler.obtainMessage(CALLBACK_MESSAGE_RECEIVED); 896 msg.arg1 = peerId; 897 msg.obj = message; 898 mHandler.sendMessage(msg); 899 } 900 901 /* 902 * Proxied methods 903 */ onProxySessionStarted(int sessionId)904 public void onProxySessionStarted(int sessionId) { 905 if (VDBG) Log.v(TAG, "Proxy: onSessionStarted: sessionId=" + sessionId); 906 if (mSession != null) { 907 Log.e(TAG, 908 "onSessionStarted: sessionId=" + sessionId + ": session already created!?"); 909 throw new IllegalStateException( 910 "onSessionStarted: sessionId=" + sessionId + ": session already created!?"); 911 } 912 913 WifiAwareManager mgr = mAwareManager.get(); 914 if (mgr == null) { 915 Log.w(TAG, "onProxySessionStarted: mgr GC'd"); 916 return; 917 } 918 919 if (mIsPublish) { 920 PublishDiscoverySession session = new PublishDiscoverySession(mgr, 921 mClientId, sessionId); 922 mSession = session; 923 mOriginalCallback.onPublishStarted(session); 924 } else { 925 SubscribeDiscoverySession 926 session = new SubscribeDiscoverySession(mgr, mClientId, sessionId); 927 mSession = session; 928 mOriginalCallback.onSubscribeStarted(session); 929 } 930 } 931 onProxySessionTerminated(int reason)932 public void onProxySessionTerminated(int reason) { 933 if (VDBG) Log.v(TAG, "Proxy: onSessionTerminated: reason=" + reason); 934 if (mSession != null) { 935 mSession.setTerminated(); 936 mSession = null; 937 } else { 938 Log.w(TAG, "Proxy: onSessionTerminated called but mSession is null!?"); 939 } 940 mAwareManager.clear(); 941 mOriginalCallback.onSessionTerminated(); 942 } 943 } 944 } 945