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