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