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