WifiAwareManager.java revision b7abd810ba407ac72dbb1b539925450b4ed2ad23
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.Bundle; 31import android.os.Handler; 32import android.os.Looper; 33import android.os.Message; 34import android.os.Process; 35import android.os.RemoteException; 36import android.util.Log; 37 38import libcore.util.HexEncoding; 39 40import java.lang.annotation.Retention; 41import java.lang.annotation.RetentionPolicy; 42import java.lang.ref.WeakReference; 43import java.nio.BufferOverflowException; 44import java.util.List; 45 46/** 47 * This class provides the primary API for managing Wi-Fi Aware operations: 48 * discovery and peer-to-peer data connections. 49 * <p> 50 * The class provides access to: 51 * <ul> 52 * <li>Initialize a Aware cluster (peer-to-peer synchronization). Refer to 53 * {@link #attach(AttachCallback, Handler)}. 54 * <li>Create discovery sessions (publish or subscribe sessions). Refer to 55 * {@link WifiAwareSession#publish(PublishConfig, DiscoverySessionCallback, Handler)} and 56 * {@link WifiAwareSession#subscribe(SubscribeConfig, DiscoverySessionCallback, Handler)}. 57 * <li>Create a Aware network specifier to be used with 58 * {@link ConnectivityManager#requestNetwork(NetworkRequest, ConnectivityManager.NetworkCallback)} 59 * to set-up a Aware connection with a peer. Refer to 60 * {@link DiscoverySession#createNetworkSpecifierOpen(PeerHandle)}, 61 * {@link DiscoverySession#createNetworkSpecifierPassphrase(PeerHandle, String)}, 62 * {@link WifiAwareSession#createNetworkSpecifierOpen(int, byte[])}, and 63 * {@link WifiAwareSession#createNetworkSpecifierPassphrase(int, byte[], String)}. 64 * </ul> 65 * <p> 66 * Aware may not be usable when Wi-Fi is disabled (and other conditions). To validate that 67 * the functionality is available use the {@link #isAvailable()} function. To track 68 * changes in Aware usability register for the {@link #ACTION_WIFI_AWARE_STATE_CHANGED} 69 * broadcast. Note that this broadcast is not sticky - you should register for it and then 70 * check the above API to avoid a race condition. 71 * <p> 72 * An application must use {@link #attach(AttachCallback, Handler)} to initialize a 73 * Aware cluster - before making any other Aware operation. Aware cluster membership is a 74 * device-wide operation - the API guarantees that the device is in a cluster or joins a 75 * Aware cluster (or starts one if none can be found). Information about attach success (or 76 * failure) are returned in callbacks of {@link AttachCallback}. Proceed with Aware 77 * discovery or connection setup only after receiving confirmation that Aware attach 78 * succeeded - {@link AttachCallback#onAttached(WifiAwareSession)}. When an 79 * application is finished using Aware it <b>must</b> use the 80 * {@link WifiAwareSession#close()} API to indicate to the Aware service that the device 81 * may detach from the Aware cluster. The device will actually disable Aware once the last 82 * application detaches. 83 * <p> 84 * Once a Aware attach is confirmed use the 85 * {@link WifiAwareSession#publish(PublishConfig, DiscoverySessionCallback, Handler)} 86 * or 87 * {@link WifiAwareSession#subscribe(SubscribeConfig, DiscoverySessionCallback, 88 * Handler)} to create publish or subscribe Aware discovery sessions. Events are called on the 89 * provided callback object {@link DiscoverySessionCallback}. Specifically, the 90 * {@link DiscoverySessionCallback#onPublishStarted(PublishDiscoverySession)} 91 * and 92 * {@link DiscoverySessionCallback#onSubscribeStarted( 93 *SubscribeDiscoverySession)} 94 * return {@link PublishDiscoverySession} and 95 * {@link SubscribeDiscoverySession} 96 * objects respectively on which additional session operations can be performed, e.g. updating 97 * the session {@link PublishDiscoverySession#updatePublish(PublishConfig)} and 98 * {@link SubscribeDiscoverySession#updateSubscribe(SubscribeConfig)}. Sessions can 99 * also be used to send messages using the 100 * {@link DiscoverySession#sendMessage(PeerHandle, int, byte[])} APIs. When an 101 * application is finished with a discovery session it <b>must</b> terminate it using the 102 * {@link DiscoverySession#close()} API. 103 * <p> 104 * Creating connections between Aware devices is managed by the standard 105 * {@link ConnectivityManager#requestNetwork(NetworkRequest, 106 * ConnectivityManager.NetworkCallback)}. 107 * The {@link NetworkRequest} object should be constructed with: 108 * <ul> 109 * <li>{@link NetworkRequest.Builder#addTransportType(int)} of 110 * {@link android.net.NetworkCapabilities#TRANSPORT_WIFI_AWARE}. 111 * <li>{@link NetworkRequest.Builder#setNetworkSpecifier(String)} using 112 * {@link WifiAwareSession#createNetworkSpecifierOpen(int, byte[])}, 113 * {@link WifiAwareSession#createNetworkSpecifierPassphrase(int, byte[], String)}, 114 * {@link DiscoverySession#createNetworkSpecifierOpen(PeerHandle)}, or 115 * {@link DiscoverySession#createNetworkSpecifierPassphrase(PeerHandle, String)}. 116 * </ul> 117 */ 118@SystemService(Context.WIFI_AWARE_SERVICE) 119public class WifiAwareManager { 120 private static final String TAG = "WifiAwareManager"; 121 private static final boolean DBG = false; 122 private static final boolean VDBG = false; // STOPSHIP if true 123 124 /** 125 * Broadcast intent action to indicate that the state of Wi-Fi Aware availability has changed. 126 * Use the {@link #isAvailable()} to query the current status. 127 * This broadcast is <b>not</b> sticky, use the {@link #isAvailable()} API after registering 128 * the broadcast to check the current state of Wi-Fi Aware. 129 * <p>Note: The broadcast is only delivered to registered receivers - no manifest registered 130 * components will be launched. 131 */ 132 @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION) 133 public static final String ACTION_WIFI_AWARE_STATE_CHANGED = 134 "android.net.wifi.aware.action.WIFI_AWARE_STATE_CHANGED"; 135 136 /** @hide */ 137 @IntDef({ 138 WIFI_AWARE_DATA_PATH_ROLE_INITIATOR, WIFI_AWARE_DATA_PATH_ROLE_RESPONDER}) 139 @Retention(RetentionPolicy.SOURCE) 140 public @interface DataPathRole { 141 } 142 143 /** 144 * Connection creation role is that of INITIATOR. Used to create a network specifier string 145 * when requesting a Aware network. 146 * 147 * @see DiscoverySession#createNetworkSpecifierOpen(PeerHandle) 148 * @see DiscoverySession#createNetworkSpecifierPassphrase(PeerHandle, String) 149 * @see WifiAwareSession#createNetworkSpecifierOpen(int, byte[]) 150 * @see WifiAwareSession#createNetworkSpecifierPassphrase(int, byte[], String) 151 */ 152 public static final int WIFI_AWARE_DATA_PATH_ROLE_INITIATOR = 0; 153 154 /** 155 * Connection creation role is that of RESPONDER. Used to create a network specifier string 156 * when requesting a Aware network. 157 * 158 * @see DiscoverySession#createNetworkSpecifierOpen(PeerHandle) 159 * @see DiscoverySession#createNetworkSpecifierPassphrase(PeerHandle, String) 160 * @see WifiAwareSession#createNetworkSpecifierOpen(int, byte[]) 161 * @see WifiAwareSession#createNetworkSpecifierPassphrase(int, byte[], String) 162 */ 163 public static final int WIFI_AWARE_DATA_PATH_ROLE_RESPONDER = 1; 164 165 private final Context mContext; 166 private final IWifiAwareManager mService; 167 168 private final Object mLock = new Object(); // lock access to the following vars 169 170 /** @hide */ 171 public WifiAwareManager(Context context, IWifiAwareManager service) { 172 mContext = context; 173 mService = service; 174 } 175 176 /** 177 * Returns the current status of Aware API: whether or not Aware is available. To track 178 * changes in the state of Aware API register for the 179 * {@link #ACTION_WIFI_AWARE_STATE_CHANGED} broadcast. 180 * 181 * @return A boolean indicating whether the app can use the Aware API at this time (true) or 182 * not (false). 183 */ 184 public boolean isAvailable() { 185 try { 186 return mService.isUsageEnabled(); 187 } catch (RemoteException e) { 188 throw e.rethrowFromSystemServer(); 189 } 190 } 191 192 /** 193 * Returns the characteristics of the Wi-Fi Aware interface: a set of parameters which specify 194 * limitations on configurations, e.g. the maximum service name length. 195 * 196 * @return An object specifying configuration limitations of Aware. 197 */ 198 public Characteristics getCharacteristics() { 199 try { 200 return mService.getCharacteristics(); 201 } catch (RemoteException e) { 202 throw e.rethrowFromSystemServer(); 203 } 204 } 205 206 /** 207 * Attach to the Wi-Fi Aware service - enabling the application to create discovery sessions or 208 * create connections to peers. The device will attach to an existing cluster if it can find 209 * one or create a new cluster (if it is the first to enable Aware in its vicinity). Results 210 * (e.g. successful attach to a cluster) are provided to the {@code attachCallback} object. 211 * An application <b>must</b> call {@link WifiAwareSession#close()} when done with the 212 * Wi-Fi Aware object. 213 * <p> 214 * Note: a Aware cluster is a shared resource - if the device is already attached to a cluster 215 * then this function will simply indicate success immediately using the same {@code 216 * attachCallback}. 217 * 218 * @param attachCallback A callback for attach events, extended from 219 * {@link AttachCallback}. 220 * @param handler The Handler on whose thread to execute the callbacks of the {@code 221 * attachCallback} object. If a null is provided then the application's main thread will be 222 * used. 223 */ 224 public void attach(@NonNull AttachCallback attachCallback, @Nullable Handler handler) { 225 attach(handler, null, attachCallback, null); 226 } 227 228 /** 229 * Attach to the Wi-Fi Aware service - enabling the application to create discovery sessions or 230 * create connections to peers. The device will attach to an existing cluster if it can find 231 * one or create a new cluster (if it is the first to enable Aware in its vicinity). Results 232 * (e.g. successful attach to a cluster) are provided to the {@code attachCallback} object. 233 * An application <b>must</b> call {@link WifiAwareSession#close()} when done with the 234 * Wi-Fi Aware object. 235 * <p> 236 * Note: a Aware cluster is a shared resource - if the device is already attached to a cluster 237 * then this function will simply indicate success immediately using the same {@code 238 * attachCallback}. 239 * <p> 240 * This version of the API attaches a listener to receive the MAC address of the Aware interface 241 * on startup and whenever it is updated (it is randomized at regular intervals for privacy). 242 * The application must have the {@link android.Manifest.permission#ACCESS_COARSE_LOCATION} 243 * permission to execute this attach request. Otherwise, use the 244 * {@link #attach(AttachCallback, Handler)} version. Note that aside from permission 245 * requirements this listener will wake up the host at regular intervals causing higher power 246 * consumption, do not use it unless the information is necessary (e.g. for OOB discovery). 247 * 248 * @param attachCallback A callback for attach events, extended from 249 * {@link AttachCallback}. 250 * @param identityChangedListener A listener for changed identity, extended from 251 * {@link IdentityChangedListener}. 252 * @param handler The Handler on whose thread to execute the callbacks of the {@code 253 * attachCallback} and {@code identityChangedListener} objects. If a null is provided then the 254 * application's main thread will be used. 255 */ 256 public void attach(@NonNull AttachCallback attachCallback, 257 @NonNull IdentityChangedListener identityChangedListener, 258 @Nullable Handler handler) { 259 attach(handler, null, attachCallback, identityChangedListener); 260 } 261 262 /** @hide */ 263 public void attach(Handler handler, ConfigRequest configRequest, 264 AttachCallback attachCallback, 265 IdentityChangedListener identityChangedListener) { 266 if (VDBG) { 267 Log.v(TAG, "attach(): handler=" + handler + ", callback=" + attachCallback 268 + ", configRequest=" + configRequest + ", identityChangedListener=" 269 + identityChangedListener); 270 } 271 272 if (attachCallback == null) { 273 throw new IllegalArgumentException("Null callback provided"); 274 } 275 276 synchronized (mLock) { 277 Looper looper = (handler == null) ? Looper.getMainLooper() : handler.getLooper(); 278 279 try { 280 Binder binder = new Binder(); 281 mService.connect(binder, mContext.getOpPackageName(), 282 new WifiAwareEventCallbackProxy(this, looper, binder, attachCallback, 283 identityChangedListener), configRequest, 284 identityChangedListener != null); 285 } catch (RemoteException e) { 286 throw e.rethrowFromSystemServer(); 287 } 288 } 289 } 290 291 /** @hide */ 292 public void disconnect(int clientId, Binder binder) { 293 if (VDBG) Log.v(TAG, "disconnect()"); 294 295 try { 296 mService.disconnect(clientId, binder); 297 } catch (RemoteException e) { 298 throw e.rethrowFromSystemServer(); 299 } 300 } 301 302 /** @hide */ 303 public void publish(int clientId, Looper looper, PublishConfig publishConfig, 304 DiscoverySessionCallback callback) { 305 if (VDBG) Log.v(TAG, "publish(): clientId=" + clientId + ", config=" + publishConfig); 306 307 if (callback == null) { 308 throw new IllegalArgumentException("Null callback provided"); 309 } 310 311 try { 312 mService.publish(mContext.getOpPackageName(), clientId, publishConfig, 313 new WifiAwareDiscoverySessionCallbackProxy(this, looper, true, callback, 314 clientId)); 315 } catch (RemoteException e) { 316 throw e.rethrowFromSystemServer(); 317 } 318 } 319 320 /** @hide */ 321 public void updatePublish(int clientId, int sessionId, PublishConfig publishConfig) { 322 if (VDBG) { 323 Log.v(TAG, "updatePublish(): clientId=" + clientId + ",sessionId=" + sessionId 324 + ", config=" + publishConfig); 325 } 326 327 try { 328 mService.updatePublish(clientId, sessionId, publishConfig); 329 } catch (RemoteException e) { 330 throw e.rethrowFromSystemServer(); 331 } 332 } 333 334 /** @hide */ 335 public void subscribe(int clientId, Looper looper, SubscribeConfig subscribeConfig, 336 DiscoverySessionCallback callback) { 337 if (VDBG) { 338 if (VDBG) { 339 Log.v(TAG, 340 "subscribe(): clientId=" + clientId + ", config=" + subscribeConfig); 341 } 342 } 343 344 if (callback == null) { 345 throw new IllegalArgumentException("Null callback provided"); 346 } 347 348 try { 349 mService.subscribe(mContext.getOpPackageName(), clientId, subscribeConfig, 350 new WifiAwareDiscoverySessionCallbackProxy(this, looper, false, callback, 351 clientId)); 352 } catch (RemoteException e) { 353 throw e.rethrowFromSystemServer(); 354 } 355 } 356 357 /** @hide */ 358 public void updateSubscribe(int clientId, int sessionId, SubscribeConfig subscribeConfig) { 359 if (VDBG) { 360 Log.v(TAG, "updateSubscribe(): clientId=" + clientId + ",sessionId=" + sessionId 361 + ", config=" + subscribeConfig); 362 } 363 364 try { 365 mService.updateSubscribe(clientId, sessionId, subscribeConfig); 366 } catch (RemoteException e) { 367 throw e.rethrowFromSystemServer(); 368 } 369 } 370 371 /** @hide */ 372 public void terminateSession(int clientId, int sessionId) { 373 if (VDBG) { 374 Log.d(TAG, 375 "terminateSession(): clientId=" + clientId + ", sessionId=" + sessionId); 376 } 377 378 try { 379 mService.terminateSession(clientId, sessionId); 380 } catch (RemoteException e) { 381 throw e.rethrowFromSystemServer(); 382 } 383 } 384 385 /** @hide */ 386 public void sendMessage(int clientId, int sessionId, PeerHandle peerHandle, byte[] message, 387 int messageId, int retryCount) { 388 if (peerHandle == null) { 389 throw new IllegalArgumentException( 390 "sendMessage: invalid peerHandle - must be non-null"); 391 } 392 393 if (VDBG) { 394 Log.v(TAG, "sendMessage(): clientId=" + clientId + ", sessionId=" + sessionId 395 + ", peerHandle=" + peerHandle.peerId + ", messageId=" 396 + messageId + ", retryCount=" + retryCount); 397 } 398 399 try { 400 mService.sendMessage(clientId, sessionId, peerHandle.peerId, message, messageId, 401 retryCount); 402 } catch (RemoteException e) { 403 throw e.rethrowFromSystemServer(); 404 } 405 } 406 407 /** @hide */ 408 public NetworkSpecifier createNetworkSpecifier(int clientId, int role, int sessionId, 409 PeerHandle peerHandle, @Nullable byte[] pmk, @Nullable String passphrase) { 410 if (VDBG) { 411 Log.v(TAG, "createNetworkSpecifier: role=" + role + ", sessionId=" + sessionId 412 + ", peerHandle=" + ((peerHandle == null) ? peerHandle : peerHandle.peerId) 413 + ", pmk=" + ((pmk == null) ? "null" : "non-null") 414 + ", passphrase=" + ((passphrase == null) ? "null" : "non-null")); 415 } 416 417 if (role != WIFI_AWARE_DATA_PATH_ROLE_INITIATOR 418 && role != WIFI_AWARE_DATA_PATH_ROLE_RESPONDER) { 419 throw new IllegalArgumentException( 420 "createNetworkSpecifier: Invalid 'role' argument when creating a network " 421 + "specifier"); 422 } 423 if (role == WIFI_AWARE_DATA_PATH_ROLE_INITIATOR) { 424 if (peerHandle == null) { 425 throw new IllegalArgumentException( 426 "createNetworkSpecifier: Invalid peer handle (value of null) - not " 427 + "permitted on INITIATOR"); 428 } 429 } 430 431 return new WifiAwareNetworkSpecifier( 432 (peerHandle == null) ? WifiAwareNetworkSpecifier.NETWORK_SPECIFIER_TYPE_IB_ANY_PEER 433 : WifiAwareNetworkSpecifier.NETWORK_SPECIFIER_TYPE_IB, 434 role, 435 clientId, 436 sessionId, 437 peerHandle != null ? peerHandle.peerId : 0, // 0 is an invalid peer ID 438 null, // peerMac (not used in this method) 439 pmk, 440 passphrase, 441 Process.myUid()); 442 } 443 444 /** @hide */ 445 public NetworkSpecifier createNetworkSpecifier(int clientId, @DataPathRole int role, 446 @Nullable byte[] peer, @Nullable byte[] pmk, @Nullable String passphrase) { 447 if (VDBG) { 448 Log.v(TAG, "createNetworkSpecifier: role=" + role 449 + ", pmk=" + ((pmk == null) ? "null" : "non-null") 450 + ", passphrase=" + ((passphrase == null) ? "null" : "non-null")); 451 } 452 453 if (role != WIFI_AWARE_DATA_PATH_ROLE_INITIATOR 454 && role != WIFI_AWARE_DATA_PATH_ROLE_RESPONDER) { 455 throw new IllegalArgumentException( 456 "createNetworkSpecifier: Invalid 'role' argument when creating a network " 457 + "specifier"); 458 } 459 if (role == WIFI_AWARE_DATA_PATH_ROLE_INITIATOR) { 460 if (peer == null) { 461 throw new IllegalArgumentException("createNetworkSpecifier: Invalid peer MAC " 462 + "address - null not permitted on INITIATOR"); 463 } 464 } 465 if (peer != null && peer.length != 6) { 466 throw new IllegalArgumentException("createNetworkSpecifier: Invalid peer MAC address"); 467 } 468 469 return new WifiAwareNetworkSpecifier( 470 (peer == null) ? WifiAwareNetworkSpecifier.NETWORK_SPECIFIER_TYPE_OOB_ANY_PEER 471 : WifiAwareNetworkSpecifier.NETWORK_SPECIFIER_TYPE_OOB, 472 role, 473 clientId, 474 0, // 0 is an invalid session ID 475 0, // 0 is an invalid peer ID 476 peer, 477 pmk, 478 passphrase, 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 */ 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 */ 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 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