Home | History | Annotate | Download | only in aware
      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
     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)
    120 public 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