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