• 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 static android.Manifest.permission.ACCESS_FINE_LOCATION;
20 import static android.Manifest.permission.ACCESS_WIFI_STATE;
21 import static android.Manifest.permission.CHANGE_WIFI_STATE;
22 import static android.Manifest.permission.MANAGE_WIFI_NETWORK_SELECTION;
23 import static android.Manifest.permission.NEARBY_WIFI_DEVICES;
24 import static android.Manifest.permission.OVERRIDE_WIFI_CONFIG;
25 import static android.net.wifi.ScanResult.WIFI_BAND_24_GHZ;
26 import static android.net.wifi.ScanResult.WIFI_BAND_5_GHZ;
27 
28 import android.annotation.CallbackExecutor;
29 import android.annotation.FlaggedApi;
30 import android.annotation.IntDef;
31 import android.annotation.NonNull;
32 import android.annotation.Nullable;
33 import android.annotation.RequiresPermission;
34 import android.annotation.SdkConstant;
35 import android.annotation.SdkConstant.SdkConstantType;
36 import android.annotation.SystemApi;
37 import android.annotation.SystemService;
38 import android.content.Context;
39 import android.net.ConnectivityManager;
40 import android.net.MacAddress;
41 import android.net.NetworkRequest;
42 import android.net.NetworkSpecifier;
43 import android.net.wifi.IBooleanListener;
44 import android.net.wifi.IIntegerListener;
45 import android.net.wifi.IListListener;
46 import android.net.wifi.OuiKeyedData;
47 import android.net.wifi.WifiManager;
48 import android.net.wifi.rtt.RangingResult;
49 import android.net.wifi.util.HexEncoding;
50 import android.os.Binder;
51 import android.os.Build;
52 import android.os.Bundle;
53 import android.os.Handler;
54 import android.os.Looper;
55 import android.os.RemoteException;
56 import android.util.Log;
57 
58 import androidx.annotation.RequiresApi;
59 
60 import com.android.modules.utils.HandlerExecutor;
61 import com.android.modules.utils.build.SdkLevel;
62 import com.android.wifi.flags.Flags;
63 
64 import java.lang.annotation.Retention;
65 import java.lang.annotation.RetentionPolicy;
66 import java.lang.ref.WeakReference;
67 import java.nio.BufferOverflowException;
68 import java.util.Collections;
69 import java.util.List;
70 import java.util.Objects;
71 import java.util.concurrent.Executor;
72 import java.util.function.Consumer;
73 
74 /**
75  * This class provides the primary API for managing Wi-Fi Aware operations:
76  * discovery and peer-to-peer data connections.
77  * <p>
78  * The class provides access to:
79  * <ul>
80  * <li>Initialize a Aware cluster (peer-to-peer synchronization). Refer to
81  * {@link #attach(AttachCallback, Handler)}.
82  * <li>Create discovery sessions (publish or subscribe sessions). Refer to
83  * {@link WifiAwareSession#publish(PublishConfig, DiscoverySessionCallback, Handler)} and
84  * {@link WifiAwareSession#subscribe(SubscribeConfig, DiscoverySessionCallback, Handler)}.
85  * <li>Create a Aware network specifier to be used with
86  * {@link ConnectivityManager#requestNetwork(NetworkRequest, ConnectivityManager.NetworkCallback)}
87  * to set-up a Aware connection with a peer. Refer to {@link WifiAwareNetworkSpecifier.Builder}.
88  * </ul>
89  * <p>
90  *     Aware may not be usable when Wi-Fi is disabled (and other conditions). To validate that
91  *     the functionality is available use the {@link #isAvailable()} function. To track
92  *     changes in Aware usability register for the {@link #ACTION_WIFI_AWARE_STATE_CHANGED}
93  *     broadcast. Note that this broadcast is not sticky - you should register for it and then
94  *     check the above API to avoid a race condition.
95  * <p>
96  *     An application must use {@link #attach(AttachCallback, Handler)} to initialize a
97  *     Aware cluster - before making any other Aware operation. Aware cluster membership is a
98  *     device-wide operation - the API guarantees that the device is in a cluster or joins a
99  *     Aware cluster (or starts one if none can be found). Information about attach success (or
100  *     failure) are returned in callbacks of {@link AttachCallback}. Proceed with Aware
101  *     discovery or connection setup only after receiving confirmation that Aware attach
102  *     succeeded - {@link AttachCallback#onAttached(WifiAwareSession)}. When an
103  *     application is finished using Aware it <b>must</b> use the
104  *     {@link WifiAwareSession#close()} API to indicate to the Aware service that the device
105  *     may detach from the Aware cluster. The device will actually disable Aware once the last
106  *     application detaches.
107  * <p>
108  *     Once a Aware attach is confirmed use the
109  *     {@link WifiAwareSession#publish(PublishConfig, DiscoverySessionCallback, Handler)}
110  *     or
111  *     {@link WifiAwareSession#subscribe(SubscribeConfig, DiscoverySessionCallback,
112  *     Handler)} to create publish or subscribe Aware discovery sessions. Events are called on the
113  *     provided callback object {@link DiscoverySessionCallback}. Specifically, the
114  *     {@link DiscoverySessionCallback#onPublishStarted(PublishDiscoverySession)}
115  *     and
116  *     {@link DiscoverySessionCallback#onSubscribeStarted(
117  *SubscribeDiscoverySession)}
118  *     return {@link PublishDiscoverySession} and
119  *     {@link SubscribeDiscoverySession}
120  *     objects respectively on which additional session operations can be performed, e.g. updating
121  *     the session {@link PublishDiscoverySession#updatePublish(PublishConfig)} and
122  *     {@link SubscribeDiscoverySession#updateSubscribe(SubscribeConfig)}. Sessions can
123  *     also be used to send messages using the
124  *     {@link DiscoverySession#sendMessage(PeerHandle, int, byte[])} APIs. When an
125  *     application is finished with a discovery session it <b>must</b> terminate it using the
126  *     {@link DiscoverySession#close()} API.
127  * <p>
128  *    Creating connections between Aware devices is managed by the standard
129  *    {@link ConnectivityManager#requestNetwork(NetworkRequest,
130  *    ConnectivityManager.NetworkCallback)}.
131  *    The {@link NetworkRequest} object should be constructed with:
132  *    <ul>
133  *        <li>{@link NetworkRequest.Builder#addTransportType(int)} of
134  *        {@link android.net.NetworkCapabilities#TRANSPORT_WIFI_AWARE}.
135  *        <li>{@link NetworkRequest.Builder#setNetworkSpecifier(String)} using
136  *        {@link WifiAwareNetworkSpecifier.Builder}.
137  *    </ul>
138  */
139 @SystemService(Context.WIFI_AWARE_SERVICE)
140 public class WifiAwareManager {
141     private static final String TAG = "WifiAwareManager";
142     private static final boolean DBG = false;
143     private static final boolean VDBG = false; // STOPSHIP if true
144 
145     /**
146      * Broadcast intent action to indicate that the state of Wi-Fi Aware availability has changed
147      * and all active Aware sessions are no longer usable. Use the {@link #isAvailable()} to query
148      * the current status.
149      * This broadcast is <b>not</b> sticky, use the {@link #isAvailable()} API after registering
150      * the broadcast to check the current state of Wi-Fi Aware.
151      * <p>Note: The broadcast is only delivered to registered receivers - no manifest registered
152      * components will be launched.
153      */
154     @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION)
155     public static final String ACTION_WIFI_AWARE_STATE_CHANGED =
156             "android.net.wifi.aware.action.WIFI_AWARE_STATE_CHANGED";
157     /**
158      * Intent broadcast sent whenever Wi-Fi Aware resource availability has changed. The resources
159      * are attached with the {@link #EXTRA_AWARE_RESOURCES} extra. The resources can also be
160      * obtained using the {@link #getAvailableAwareResources()} method. To receive this broadcast,
161      * apps must hold {@link android.Manifest.permission#ACCESS_WIFI_STATE}.
162      * <p>Note: The broadcast is only delivered to registered receivers - no manifest registered
163      * components will be launched.
164      */
165     @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION)
166     @RequiresApi(Build.VERSION_CODES.TIRAMISU)
167     @RequiresPermission(ACCESS_WIFI_STATE)
168     public static final String ACTION_WIFI_AWARE_RESOURCE_CHANGED =
169             "android.net.wifi.aware.action.WIFI_AWARE_RESOURCE_CHANGED";
170 
171     /**
172      * Sent as a part of {@link #ACTION_WIFI_AWARE_RESOURCE_CHANGED} that contains an instance of
173      * {@link AwareResources} representing the current Wi-Fi Aware resources.
174      */
175     public static final String EXTRA_AWARE_RESOURCES =
176             "android.net.wifi.aware.extra.AWARE_RESOURCES";
177 
178     /** @hide */
179     @IntDef({
180             WIFI_AWARE_DATA_PATH_ROLE_INITIATOR, WIFI_AWARE_DATA_PATH_ROLE_RESPONDER})
181     @Retention(RetentionPolicy.SOURCE)
182     public @interface DataPathRole {
183     }
184 
185     /**
186      * Connection creation role is that of INITIATOR. Used to create a network specifier string
187      * when requesting a Aware network.
188      *
189      * @see WifiAwareSession#createNetworkSpecifierOpen(int, byte[])
190      * @see WifiAwareSession#createNetworkSpecifierPassphrase(int, byte[], String)
191      */
192     public static final int WIFI_AWARE_DATA_PATH_ROLE_INITIATOR = 0;
193 
194     /**
195      * Connection creation role is that of RESPONDER. Used to create a network specifier string
196      * when requesting a Aware network.
197      *
198      * @see WifiAwareSession#createNetworkSpecifierOpen(int, byte[])
199      * @see WifiAwareSession#createNetworkSpecifierPassphrase(int, byte[], String)
200      */
201     public static final int WIFI_AWARE_DATA_PATH_ROLE_RESPONDER = 1;
202 
203     /** @hide */
204     @IntDef({
205             WIFI_AWARE_DISCOVERY_LOST_REASON_UNKNOWN,
206             WIFI_AWARE_DISCOVERY_LOST_REASON_PEER_NOT_VISIBLE})
207     @Retention(RetentionPolicy.SOURCE)
208     public @interface DiscoveryLostReasonCode {
209     }
210 
211     /**
212      * Reason code provided in {@link DiscoverySessionCallback#onServiceLost(PeerHandle, int)}
213      * indicating that the service was lost for unknown reason.
214      */
215     public static final int WIFI_AWARE_DISCOVERY_LOST_REASON_UNKNOWN = 0;
216 
217     /**
218      * Reason code provided in {@link DiscoverySessionCallback#onServiceLost(PeerHandle, int)}
219      * indicating that the service advertised by the peer is no longer visible. This may be because
220      * the peer is out of range or because the peer stopped advertising this service.
221      */
222     public static final int WIFI_AWARE_DISCOVERY_LOST_REASON_PEER_NOT_VISIBLE = 1;
223 
224     /** @hide */
225     @IntDef({
226             WIFI_AWARE_SUSPEND_REDUNDANT_REQUEST,
227             WIFI_AWARE_SUSPEND_INVALID_SESSION,
228             WIFI_AWARE_SUSPEND_CANNOT_SUSPEND,
229             WIFI_AWARE_SUSPEND_INTERNAL_ERROR})
230     @Retention(RetentionPolicy.SOURCE)
231     public @interface SessionSuspensionFailedReasonCode {}
232 
233     /**
234      * Reason code provided in {@link DiscoverySessionCallback#onSessionSuspendFailed(int)} when the
235      * session is already suspended.
236      * @hide
237      */
238     @SystemApi
239     public static final int WIFI_AWARE_SUSPEND_REDUNDANT_REQUEST = 0;
240 
241     /**
242      * Reason code provided in {@link DiscoverySessionCallback#onSessionSuspendFailed(int)} when the
243      * specified session does not support suspension.
244       @hide
245      */
246     @SystemApi
247     public static final int WIFI_AWARE_SUSPEND_INVALID_SESSION = 1;
248 
249     /**
250      * Reason code provided in {@link DiscoverySessionCallback#onSessionSuspendFailed(int)} when the
251      * session could not be suspended due to more than one app using it.
252       @hide
253      */
254     @SystemApi
255     public static final int WIFI_AWARE_SUSPEND_CANNOT_SUSPEND = 2;
256 
257     /**
258      * Reason code provided in {@link DiscoverySessionCallback#onSessionSuspendFailed(int)} when an
259      * error is encountered with the request.
260       @hide
261      */
262     @SystemApi
263     public static final int WIFI_AWARE_SUSPEND_INTERNAL_ERROR = 3;
264 
265     /** @hide */
266     @IntDef({
267             WIFI_AWARE_RESUME_REDUNDANT_REQUEST,
268             WIFI_AWARE_RESUME_INVALID_SESSION,
269             WIFI_AWARE_RESUME_INTERNAL_ERROR})
270     @Retention(RetentionPolicy.SOURCE)
271     public @interface SessionResumptionFailedReasonCode {}
272 
273     /**
274      * Reason code provided in {@link DiscoverySessionCallback#onSessionResumeFailed(int)} when the
275      * session is not suspended.
276      * @hide
277      */
278     @SystemApi
279     public static final int WIFI_AWARE_RESUME_REDUNDANT_REQUEST = 0;
280 
281     /**
282      * Reason code provided in {@link DiscoverySessionCallback#onSessionResumeFailed(int)} when the
283      * specified session does not support suspension.
284       @hide
285      */
286     @SystemApi
287     public static final int WIFI_AWARE_RESUME_INVALID_SESSION = 1;
288 
289     /**
290      * Reason code provided in {@link DiscoverySessionCallback#onSessionResumeFailed(int)} when an
291      * error is encountered with the request.
292       @hide
293      */
294     @SystemApi
295     public static final int WIFI_AWARE_RESUME_INTERNAL_ERROR = 2;
296 
297     /** @hide */
298     @Retention(RetentionPolicy.SOURCE)
299     @IntDef(
300             prefix = {"WIFI_BAND_"},
301             value = {WIFI_BAND_24_GHZ, WIFI_BAND_5_GHZ})
302     public @interface InstantModeBand {};
303 
304     private final Context mContext;
305     private final IWifiAwareManager mService;
306 
307     private final Object mLock = new Object(); // lock access to the following vars
308 
309     /** @hide */
WifiAwareManager(@onNull Context context, @NonNull IWifiAwareManager service)310     public WifiAwareManager(@NonNull Context context, @NonNull IWifiAwareManager service) {
311         mContext = context;
312         mService = service;
313     }
314 
315     /**
316      * Returns the current status of Aware API: whether or not Aware is available. To track
317      * changes in the state of Aware API register for the
318      * {@link #ACTION_WIFI_AWARE_STATE_CHANGED} broadcast.
319      *
320      * @return A boolean indicating whether the app can use the Aware API at this time (true) or
321      * not (false).
322      */
323     @RequiresPermission(ACCESS_WIFI_STATE)
isAvailable()324     public boolean isAvailable() {
325         try {
326             return mService.isUsageEnabled();
327         } catch (RemoteException e) {
328             throw e.rethrowFromSystemServer();
329         }
330     }
331 
332     /**
333      * Return the current status of the Aware service: whether or not the device is already attached
334      * to an Aware cluster. To attach to an Aware cluster, please use
335      * {@link #attach(AttachCallback, Handler)} or
336      * {@link #attach(AttachCallback, IdentityChangedListener, Handler)}.
337      * @return A boolean indicating whether the device is attached to a cluster at this time (true)
338      *         or not (false).
339      */
340     @RequiresPermission(ACCESS_WIFI_STATE)
isDeviceAttached()341     public boolean isDeviceAttached() {
342         try {
343             return mService.isDeviceAttached();
344         } catch (RemoteException e) {
345             throw e.rethrowFromSystemServer();
346         }
347     }
348 
349     /**
350      * Return the device support for setting a channel requirement in a data-path request. If true
351      * the channel set by
352      * {@link WifiAwareNetworkSpecifier.Builder#setChannelFrequencyMhz(int, boolean)} will be
353      * honored, otherwise it will be ignored.
354      * @return True is the device support set channel on data-path request, false otherwise.
355      */
356     @RequiresPermission(ACCESS_WIFI_STATE)
isSetChannelOnDataPathSupported()357     public boolean isSetChannelOnDataPathSupported() {
358         try {
359             return mService.isSetChannelOnDataPathSupported();
360         } catch (RemoteException e) {
361             throw e.rethrowFromSystemServer();
362         }
363     }
364 
365     /**
366      * Enable the Wifi Aware Instant communication mode. If the device doesn't support this feature
367      * calling this API will result no action.
368      * <p>
369      * Note: before {@link android.os.Build.VERSION_CODES#TIRAMISU}, only system app can use this
370      * API. Start with {@link android.os.Build.VERSION_CODES#TIRAMISU} apps hold
371      * {@link android.Manifest.permission#OVERRIDE_WIFI_CONFIG} are allowed to use it.
372      *
373      * @see Characteristics#isInstantCommunicationModeSupported()
374      * @param enable true for enable, false otherwise.
375      * @hide
376      */
377     @SystemApi
378     @RequiresApi(Build.VERSION_CODES.S)
379     @RequiresPermission(allOf = {CHANGE_WIFI_STATE, OVERRIDE_WIFI_CONFIG})
enableInstantCommunicationMode(boolean enable)380     public void enableInstantCommunicationMode(boolean enable) {
381         if (!SdkLevel.isAtLeastS()) {
382             throw new UnsupportedOperationException();
383         }
384         try {
385             mService.enableInstantCommunicationMode(mContext.getOpPackageName(), enable);
386         } catch (RemoteException e) {
387             throw e.rethrowFromSystemServer();
388         }
389     }
390 
391     /**
392      * Return the current status of the Wifi Aware instant communication mode.
393      * If the device doesn't support this feature, return will always be false.
394      * @see Characteristics#isInstantCommunicationModeSupported()
395      * @return true if it is enabled, false otherwise.
396      */
397     @RequiresApi(Build.VERSION_CODES.S)
398     @RequiresPermission(ACCESS_WIFI_STATE)
isInstantCommunicationModeEnabled()399     public boolean isInstantCommunicationModeEnabled() {
400         if (!SdkLevel.isAtLeastS()) {
401             throw new UnsupportedOperationException();
402         }
403         try {
404             return mService.isInstantCommunicationModeEnabled();
405         } catch (RemoteException e) {
406             throw e.rethrowFromSystemServer();
407         }
408     }
409 
410     /**
411      * Returns the characteristics of the Wi-Fi Aware interface: a set of parameters which specify
412      * limitations on configurations, e.g. the maximum service name length.
413      * <p>
414      * May return {@code null} if the Wi-Fi Aware service is not initialized. Use
415      * {@link #attach(AttachCallback, Handler)} or
416      * {@link #attach(AttachCallback, IdentityChangedListener, Handler)} to initialize the Wi-Fi
417      * Aware service.
418      *
419      * @return An object specifying configuration limitations of Aware.
420      */
421     @RequiresPermission(ACCESS_WIFI_STATE)
getCharacteristics()422     public @Nullable Characteristics getCharacteristics() {
423         try {
424             return mService.getCharacteristics();
425         } catch (RemoteException e) {
426             throw e.rethrowFromSystemServer();
427         }
428     }
429 
430     /**
431      * Return the available resources of the Wi-Fi aware service: a set of parameters which specify
432      * limitations on service usage, e.g the number of data-paths which could be created.
433      * <p>
434      * May return {@code null} if the Wi-Fi Aware service is not initialized. Use
435      * {@link #attach(AttachCallback, Handler)} or
436      * {@link #attach(AttachCallback, IdentityChangedListener, Handler)} to initialize the Wi-Fi
437      * Aware service.
438      *
439      * @return An object specifying the currently available resource of the Wi-Fi Aware service.
440      */
441     @RequiresPermission(ACCESS_WIFI_STATE)
getAvailableAwareResources()442     public @Nullable AwareResources getAvailableAwareResources() {
443         try {
444             return mService.getAvailableAwareResources();
445         } catch (RemoteException e) {
446             throw e.rethrowFromSystemServer();
447         }
448     }
449 
450     /**
451      * Attach to the Wi-Fi Aware service - enabling the application to create discovery sessions or
452      * create connections to peers. The device will attach to an existing cluster if it can find
453      * one or create a new cluster (if it is the first to enable Aware in its vicinity). Results
454      * (e.g. successful attach to a cluster) are provided to the {@code attachCallback} object.
455      * An application <b>must</b> call {@link WifiAwareSession#close()} when done with the
456      * Wi-Fi Aware object.
457      * <p>
458      * Note: a Aware cluster is a shared resource - if the device is already attached to a cluster
459      * then this function will simply indicate success immediately using the same {@code
460      * attachCallback}.
461      *
462      * @param attachCallback A callback for attach events, extended from
463      * {@link AttachCallback}.
464      * @param handler The Handler on whose thread to execute the callbacks of the {@code
465      * attachCallback} object. If a null is provided then the application's main thread will be
466      *                used.
467      */
468     @RequiresPermission(allOf = {
469             ACCESS_WIFI_STATE,
470             CHANGE_WIFI_STATE
471     })
attach(@onNull AttachCallback attachCallback, @Nullable Handler handler)472     public void attach(@NonNull AttachCallback attachCallback, @Nullable Handler handler) {
473         attach(handler, null, attachCallback, null, false, null);
474     }
475 
476     /**
477      * Attach to the Wi-Fi Aware service - enabling the application to create discovery sessions or
478      * create connections to peers. The device will attach to an existing cluster if it can find
479      * one or create a new cluster (if it is the first to enable Aware in its vicinity). Results
480      * (e.g. successful attach to a cluster) are provided to the {@code attachCallback} object.
481      * An application <b>must</b> call {@link WifiAwareSession#close()} when done with the
482      * Wi-Fi Aware object.
483      * <p>
484      * Note: a Aware cluster is a shared resource - if the device is already attached to a cluster
485      * then this function will simply indicate success immediately using the same {@code
486      * attachCallback}.
487      * <p>
488      * This version of the API attaches a listener to receive the MAC address of the Aware interface
489      * on startup and whenever it is updated (it is randomized at regular intervals for privacy).
490      *
491      * If targeting {@link android.os.Build.VERSION_CODES#TIRAMISU} or later, the application must
492      * have {@link android.Manifest.permission#NEARBY_WIFI_DEVICES} with
493      * android:usesPermissionFlags="neverForLocation". If the application does not declare
494      * android:usesPermissionFlags="neverForLocation", then it must also have
495      * {@link android.Manifest.permission#ACCESS_FINE_LOCATION}.
496      *
497      * If targeting an earlier release than {@link android.os.Build.VERSION_CODES#TIRAMISU}, the
498      * application must have {@link android.Manifest.permission#ACCESS_FINE_LOCATION}.
499      *
500      * Apps without {@link android.Manifest.permission#NEARBY_WIFI_DEVICES} or
501      * {@link android.Manifest.permission#ACCESS_FINE_LOCATION} can use the
502      * {@link #attach(AttachCallback, Handler)} version.
503      * Note that aside from permission requirements the {@link IdentityChangedListener} will wake up
504      * the host at regular intervals causing higher power consumption, do not use it unless the
505      * information is necessary (e.g. for out-of-band discovery).
506      *
507      * @param attachCallback A callback for attach events, extended from
508      * {@link AttachCallback}.
509      * @param identityChangedListener A callback for changed identity or cluster ID, extended from
510      * {@link IdentityChangedListener}.
511      * @param handler The Handler on whose thread to execute the callbacks of the {@code
512      * attachCallback} and {@code identityChangedListener} objects. If a null is provided then the
513      *                application's main thread will be used.
514      */
515     @RequiresPermission(allOf = {
516             ACCESS_WIFI_STATE,
517             CHANGE_WIFI_STATE,
518             ACCESS_FINE_LOCATION,
519             NEARBY_WIFI_DEVICES}, conditional = true)
attach(@onNull AttachCallback attachCallback, @NonNull IdentityChangedListener identityChangedListener, @Nullable Handler handler)520     public void attach(@NonNull AttachCallback attachCallback,
521             @NonNull IdentityChangedListener identityChangedListener,
522             @Nullable Handler handler) {
523         attach(handler, null, attachCallback, identityChangedListener, false, null);
524     }
525 
526     /**
527      * Attach to the Wi-Fi Aware service - enabling the application to create discovery sessions or
528      * create connections to peers. See {@link #attach(AttachCallback, IdentityChangedListener,
529      * Handler)} for more information.
530      *
531      * This version allows callers to provide an instance of {@link ConfigRequest}.
532      *
533      * @param configRequest Parameters for this request.
534      * @param executor The executor to execute the listener of the {@code attachCallback} object.
535      * @param attachCallback A callback for attach events, extended from {@link AttachCallback}.
536      * @param identityChangedListener A callback for changed identity or cluster ID, extended from
537      * {@link IdentityChangedListener}.
538      * @hide
539      */
540     @RequiresPermission(allOf = {
541             ACCESS_WIFI_STATE,
542             CHANGE_WIFI_STATE,
543             ACCESS_FINE_LOCATION,
544             NEARBY_WIFI_DEVICES,
545             MANAGE_WIFI_NETWORK_SELECTION}, conditional = true)
546     @RequiresApi(Build.VERSION_CODES.VANILLA_ICE_CREAM)
547     @FlaggedApi(Flags.FLAG_ANDROID_V_WIFI_API)
548     @SystemApi
attach(@onNull ConfigRequest configRequest, @NonNull @CallbackExecutor Executor executor, @NonNull AttachCallback attachCallback, @NonNull IdentityChangedListener identityChangedListener)549     public void attach(@NonNull ConfigRequest configRequest,
550             @NonNull @CallbackExecutor Executor executor, @NonNull AttachCallback attachCallback,
551             @NonNull IdentityChangedListener identityChangedListener) {
552         if (!SdkLevel.isAtLeastV()) {
553             throw new UnsupportedOperationException();
554         }
555         Objects.requireNonNull(configRequest);
556         Objects.requireNonNull(executor);
557         attach(null, configRequest, attachCallback, identityChangedListener, false, executor);
558     }
559 
560     /** @hide */
attach(Handler handler, ConfigRequest configRequest, AttachCallback attachCallback, IdentityChangedListener identityChangedListener, boolean forOffloading, Executor executor)561     public void attach(Handler handler, ConfigRequest configRequest,
562             AttachCallback attachCallback,
563             IdentityChangedListener identityChangedListener, boolean forOffloading,
564             Executor executor) {
565         if (VDBG) {
566             Log.v(TAG, "attach(): handler=" + handler + ", callback=" + attachCallback
567                     + ", configRequest=" + configRequest + ", identityChangedListener="
568                     + identityChangedListener + ", forOffloading" + forOffloading);
569         }
570 
571         if (attachCallback == null) {
572             throw new IllegalArgumentException("Null callback provided");
573         }
574 
575         synchronized (mLock) {
576             Executor localExecutor = executor;
577             if (localExecutor == null) {
578                 localExecutor = new HandlerExecutor((handler == null)
579                         ? new Handler(Looper.getMainLooper()) : handler);
580             }
581 
582             try {
583                 Binder binder = new Binder();
584                 Bundle extras = new Bundle();
585                 if (SdkLevel.isAtLeastS()) {
586                     extras.putParcelable(WifiManager.EXTRA_PARAM_KEY_ATTRIBUTION_SOURCE,
587                             mContext.getAttributionSource());
588                 }
589                 mService.connect(binder, mContext.getOpPackageName(), mContext.getAttributionTag(),
590                         new WifiAwareEventCallbackProxy(this, localExecutor, binder,
591                                 attachCallback, identityChangedListener), configRequest,
592                         identityChangedListener != null, extras, forOffloading);
593             } catch (RemoteException e) {
594                 throw e.rethrowFromSystemServer();
595             }
596         }
597     }
598 
599     /** @hide */
disconnect(int clientId, Binder binder)600     public void disconnect(int clientId, Binder binder) {
601         if (VDBG) Log.v(TAG, "disconnect()");
602 
603         try {
604             mService.disconnect(clientId, binder);
605         } catch (RemoteException e) {
606             throw e.rethrowFromSystemServer();
607         }
608     }
609 
610     /** @hide */
setMasterPreference(int clientId, Binder binder, int mp)611     public void setMasterPreference(int clientId, Binder binder, int mp) {
612         if (VDBG) Log.v(TAG, "setMasterPreference()");
613 
614         try {
615             mService.setMasterPreference(clientId, binder, mp);
616         } catch (RemoteException e) {
617             throw e.rethrowFromSystemServer();
618         }
619     }
620 
621     /**
622      * @hide
623      */
getMasterPreference(int clientId, Binder binder, @NonNull Executor executor, @NonNull Consumer<Integer> resultsCallback)624     public void getMasterPreference(int clientId, Binder binder, @NonNull Executor executor,
625             @NonNull Consumer<Integer> resultsCallback) {
626         Objects.requireNonNull(executor, "executor cannot be null");
627         Objects.requireNonNull(resultsCallback, "resultsCallback cannot be null");
628         try {
629             mService.getMasterPreference(clientId, binder,
630                     new IIntegerListener.Stub() {
631                         public void onResult(int value) {
632                             Binder.clearCallingIdentity();
633                             executor.execute(() -> resultsCallback.accept(value));
634                         }
635                     });
636         } catch (RemoteException e) {
637             throw e.rethrowFromSystemServer();
638         }
639     }
640 
641     /** @hide */
publish(int clientId, Looper looper, PublishConfig publishConfig, DiscoverySessionCallback callback)642     public void publish(int clientId, Looper looper, PublishConfig publishConfig,
643             DiscoverySessionCallback callback) {
644         if (VDBG) Log.v(TAG, "publish(): clientId=" + clientId + ", config=" + publishConfig);
645 
646         if (callback == null) {
647             throw new IllegalArgumentException("Null callback provided");
648         }
649 
650         try {
651             Bundle extras = new Bundle();
652             if (SdkLevel.isAtLeastS()) {
653                 extras.putParcelable(WifiManager.EXTRA_PARAM_KEY_ATTRIBUTION_SOURCE,
654                         mContext.getAttributionSource());
655             }
656             mService.publish(mContext.getOpPackageName(), mContext.getAttributionTag(), clientId,
657                     publishConfig,
658                     new WifiAwareDiscoverySessionCallbackProxy(this, looper, true, callback,
659                             clientId), extras);
660         } catch (RemoteException e) {
661             throw e.rethrowFromSystemServer();
662         }
663     }
664 
665     /** @hide */
updatePublish(int clientId, int sessionId, PublishConfig publishConfig)666     public void updatePublish(int clientId, int sessionId, PublishConfig publishConfig) {
667         if (VDBG) {
668             Log.v(TAG, "updatePublish(): clientId=" + clientId + ",sessionId=" + sessionId
669                     + ", config=" + publishConfig);
670         }
671 
672         try {
673             mService.updatePublish(clientId, sessionId, publishConfig);
674         } catch (RemoteException e) {
675             throw e.rethrowFromSystemServer();
676         }
677     }
678 
679     /** @hide */
subscribe(int clientId, Looper looper, SubscribeConfig subscribeConfig, DiscoverySessionCallback callback)680     public void subscribe(int clientId, Looper looper, SubscribeConfig subscribeConfig,
681             DiscoverySessionCallback callback) {
682         if (VDBG) {
683             if (VDBG) {
684                 Log.v(TAG,
685                         "subscribe(): clientId=" + clientId + ", config=" + subscribeConfig);
686             }
687         }
688 
689         if (callback == null) {
690             throw new IllegalArgumentException("Null callback provided");
691         }
692 
693         try {
694             Bundle extras = new Bundle();
695             if (SdkLevel.isAtLeastS()) {
696                 extras.putParcelable(WifiManager.EXTRA_PARAM_KEY_ATTRIBUTION_SOURCE,
697                         mContext.getAttributionSource());
698             }
699             mService.subscribe(mContext.getOpPackageName(), mContext.getAttributionTag(), clientId,
700                     subscribeConfig,
701                     new WifiAwareDiscoverySessionCallbackProxy(this, looper, false, callback,
702                             clientId), extras);
703         } catch (RemoteException e) {
704             throw e.rethrowFromSystemServer();
705         }
706     }
707 
708     /** @hide */
updateSubscribe(int clientId, int sessionId, SubscribeConfig subscribeConfig)709     public void updateSubscribe(int clientId, int sessionId, SubscribeConfig subscribeConfig) {
710         if (VDBG) {
711             Log.v(TAG, "updateSubscribe(): clientId=" + clientId + ",sessionId=" + sessionId
712                     + ", config=" + subscribeConfig);
713         }
714 
715         try {
716             mService.updateSubscribe(clientId, sessionId, subscribeConfig);
717         } catch (RemoteException e) {
718             throw e.rethrowFromSystemServer();
719         }
720     }
721 
722     /** @hide */
terminateSession(int clientId, int sessionId)723     public void terminateSession(int clientId, int sessionId) {
724         if (VDBG) {
725             Log.d(TAG,
726                     "terminateSession(): clientId=" + clientId + ", sessionId=" + sessionId);
727         }
728 
729         try {
730             mService.terminateSession(clientId, sessionId);
731         } catch (RemoteException e) {
732             throw e.rethrowFromSystemServer();
733         }
734     }
735 
736     /** @hide */
sendMessage(int clientId, int sessionId, PeerHandle peerHandle, byte[] message, int messageId, int retryCount)737     public void sendMessage(int clientId, int sessionId, PeerHandle peerHandle, byte[] message,
738             int messageId, int retryCount) {
739         if (peerHandle == null) {
740             throw new IllegalArgumentException(
741                     "sendMessage: invalid peerHandle - must be non-null");
742         }
743 
744         if (VDBG) {
745             Log.v(TAG, "sendMessage(): clientId=" + clientId + ", sessionId=" + sessionId
746                     + ", peerHandle=" + peerHandle.peerId + ", messageId="
747                     + messageId + ", retryCount=" + retryCount);
748         }
749 
750         try {
751             mService.sendMessage(clientId, sessionId, peerHandle.peerId, message, messageId,
752                     retryCount);
753         } catch (RemoteException e) {
754             throw e.rethrowFromSystemServer();
755         }
756     }
757 
758     /**
759      * @hide
760      */
initiateNanPairingSetupRequest(int clientId, int sessionId, PeerHandle peerHandle, String password, String pairingDeviceAlias, int cipherSuite)761     public void initiateNanPairingSetupRequest(int clientId, int sessionId, PeerHandle peerHandle,
762             String password, String pairingDeviceAlias, int cipherSuite) {
763         if (peerHandle == null) {
764             throw new IllegalArgumentException(
765                     "initiateNanPairingRequest: invalid peerHandle - must be non-null");
766         }
767         if (VDBG) {
768             Log.v(TAG, "initiateNanPairingRequest(): clientId=" + clientId
769                     + ", sessionId=" + sessionId + ", peerHandle=" + peerHandle.peerId);
770         }
771         try {
772             mService.initiateNanPairingSetupRequest(clientId, sessionId, peerHandle.peerId,
773                     password, pairingDeviceAlias, cipherSuite);
774         } catch (RemoteException e) {
775             throw e.rethrowFromSystemServer();
776         }
777     }
778 
779     /**
780      * @hide
781      */
responseNanPairingSetupRequest(int clientId, int sessionId, PeerHandle peerHandle, int requestId, String password, String pairingDeviceAlias, boolean accept, int cipherSuite)782     public void responseNanPairingSetupRequest(int clientId, int sessionId, PeerHandle peerHandle,
783             int requestId, String password, String pairingDeviceAlias, boolean accept,
784             int cipherSuite) {
785         if (peerHandle == null) {
786             throw new IllegalArgumentException(
787                     "initiateNanPairingRequest: invalid peerHandle - must be non-null");
788         }
789         if (VDBG) {
790             Log.v(TAG, "initiateNanPairingRequest(): clientId=" + clientId
791                     + ", sessionId=" + sessionId + ", peerHandle=" + peerHandle.peerId);
792         }
793         try {
794             mService.responseNanPairingSetupRequest(clientId, sessionId, peerHandle.peerId,
795                     requestId, password, pairingDeviceAlias, accept, cipherSuite);
796         } catch (RemoteException e) {
797             throw e.rethrowFromSystemServer();
798         }
799     }
800 
801     /**
802      * @hide
803      */
initiateBootStrappingSetupRequest(int clientId, int sessionId, PeerHandle peerHandle, int method)804     public void initiateBootStrappingSetupRequest(int clientId, int sessionId,
805             PeerHandle peerHandle, int method) {
806         if (peerHandle == null) {
807             throw new IllegalArgumentException(
808                     "initiateBootStrappingSetupRequest: invalid peerHandle - must be non-null");
809         }
810         if (VDBG) {
811             Log.v(TAG, "initiateBootStrappingSetupRequest(): clientId=" + clientId
812                     + ", sessionId=" + sessionId + ", peerHandle=" + peerHandle.peerId);
813         }
814         try {
815             mService.initiateBootStrappingSetupRequest(clientId, sessionId, peerHandle.peerId,
816                     method);
817         } catch (RemoteException e) {
818             throw e.rethrowFromSystemServer();
819         }
820     }
821 
822     /** @hide */
823     @RequiresPermission(android.Manifest.permission.NETWORK_STACK)
requestMacAddresses(int uid, int[] peerIds, IWifiAwareMacAddressProvider callback)824     public void requestMacAddresses(int uid, int[] peerIds,
825             IWifiAwareMacAddressProvider callback) {
826         try {
827             mService.requestMacAddresses(uid, peerIds, callback);
828         } catch (RemoteException e) {
829             throw e.rethrowFromSystemServer();
830         }
831     }
832 
833     /** @hide */
createNetworkSpecifier(int clientId, int role, int sessionId, @NonNull PeerHandle peerHandle, @Nullable byte[] pmk, @Nullable String passphrase)834     public NetworkSpecifier createNetworkSpecifier(int clientId, int role, int sessionId,
835             @NonNull PeerHandle peerHandle, @Nullable byte[] pmk, @Nullable String passphrase) {
836         if (VDBG) {
837             Log.v(TAG, "createNetworkSpecifier: role=" + role + ", sessionId=" + sessionId
838                     + ", peerHandle=" + ((peerHandle == null) ? peerHandle : peerHandle.peerId)
839                     + ", pmk=" + ((pmk == null) ? "null" : "non-null")
840                     + ", passphrase=" + ((passphrase == null) ? "null" : "non-null"));
841         }
842 
843         if (!WifiAwareUtils.isLegacyVersion(mContext, Build.VERSION_CODES.Q)) {
844             throw new UnsupportedOperationException(
845                     "API deprecated - use WifiAwareNetworkSpecifier.Builder");
846         }
847 
848         if (role != WIFI_AWARE_DATA_PATH_ROLE_INITIATOR
849                 && role != WIFI_AWARE_DATA_PATH_ROLE_RESPONDER) {
850             throw new IllegalArgumentException(
851                     "createNetworkSpecifier: Invalid 'role' argument when creating a network "
852                             + "specifier");
853         }
854         if (role == WIFI_AWARE_DATA_PATH_ROLE_INITIATOR || !WifiAwareUtils.isLegacyVersion(mContext,
855                 Build.VERSION_CODES.P)) {
856             if (peerHandle == null) {
857                 throw new IllegalArgumentException(
858                         "createNetworkSpecifier: Invalid peer handle - cannot be null");
859             }
860         }
861 
862         return new WifiAwareNetworkSpecifier(
863                 (peerHandle == null) ? WifiAwareNetworkSpecifier.NETWORK_SPECIFIER_TYPE_IB_ANY_PEER
864                         : WifiAwareNetworkSpecifier.NETWORK_SPECIFIER_TYPE_IB,
865                 role,
866                 clientId,
867                 sessionId,
868                 peerHandle != null ? peerHandle.peerId : 0, // 0 is an invalid peer ID
869                 null, // peerMac (not used in this method)
870                 pmk,
871                 passphrase,
872                 0, // no port info for deprecated IB APIs
873                 -1); // no transport info for deprecated IB APIs
874     }
875 
876     /** @hide */
createNetworkSpecifier(int clientId, @DataPathRole int role, @NonNull byte[] peer, @Nullable byte[] pmk, @Nullable String passphrase)877     public NetworkSpecifier createNetworkSpecifier(int clientId, @DataPathRole int role,
878             @NonNull byte[] peer, @Nullable byte[] pmk, @Nullable String passphrase) {
879         if (VDBG) {
880             Log.v(TAG, "createNetworkSpecifier: role=" + role
881                     + ", pmk=" + ((pmk == null) ? "null" : "non-null")
882                     + ", passphrase=" + ((passphrase == null) ? "null" : "non-null"));
883         }
884 
885         if (role != WIFI_AWARE_DATA_PATH_ROLE_INITIATOR
886                 && role != WIFI_AWARE_DATA_PATH_ROLE_RESPONDER) {
887             throw new IllegalArgumentException(
888                     "createNetworkSpecifier: Invalid 'role' argument when creating a network "
889                             + "specifier");
890         }
891         if (role == WIFI_AWARE_DATA_PATH_ROLE_INITIATOR || !WifiAwareUtils.isLegacyVersion(mContext,
892                 Build.VERSION_CODES.P)) {
893             if (peer == null) {
894                 throw new IllegalArgumentException(
895                         "createNetworkSpecifier: Invalid peer MAC - cannot be null");
896             }
897         }
898         if (peer != null && peer.length != 6) {
899             throw new IllegalArgumentException("createNetworkSpecifier: Invalid peer MAC address");
900         }
901 
902         return new WifiAwareNetworkSpecifier(
903                 (peer == null) ? WifiAwareNetworkSpecifier.NETWORK_SPECIFIER_TYPE_OOB_ANY_PEER
904                         : WifiAwareNetworkSpecifier.NETWORK_SPECIFIER_TYPE_OOB,
905                 role,
906                 clientId,
907                 0, // 0 is an invalid session ID
908                 0, // 0 is an invalid peer ID
909                 peer,
910                 pmk,
911                 passphrase,
912                 0, // no port info for OOB APIs
913                 -1); // no transport protocol info for OOB APIs
914     }
915 
916     private static class WifiAwareEventCallbackProxy extends IWifiAwareEventCallback.Stub {
917         private final WeakReference<WifiAwareManager> mAwareManager;
918         private final Binder mBinder;
919         private final Executor mExecutor;
920 
921         private final AttachCallback mAttachCallback;
922 
923         private final IdentityChangedListener mIdentityChangedListener;
924 
925         /**
926          * Constructs a {@link AttachCallback} using the specified looper.
927          * All callbacks will delivered on the thread of the specified looper.
928          *
929          * @param executor The executor to execute the callbacks.
930          */
WifiAwareEventCallbackProxy(WifiAwareManager mgr, Executor executor, Binder binder, final AttachCallback attachCallback, final IdentityChangedListener identityChangedListener)931         WifiAwareEventCallbackProxy(WifiAwareManager mgr, Executor executor, Binder binder,
932                 final AttachCallback attachCallback,
933                 final IdentityChangedListener identityChangedListener) {
934             mAwareManager = new WeakReference<>(mgr);
935             mExecutor = executor;
936             mBinder = binder;
937             mAttachCallback = attachCallback;
938             mIdentityChangedListener = identityChangedListener;
939         }
940 
941         @Override
onConnectSuccess(int clientId)942         public void onConnectSuccess(int clientId) {
943             if (VDBG) Log.v(TAG, "onConnectSuccess");
944             Binder.clearCallingIdentity();
945             mExecutor.execute(() -> {
946                 WifiAwareManager mgr = mAwareManager.get();
947                 if (mgr == null) {
948                     Log.w(TAG, "WifiAwareEventCallbackProxy: handleMessage post GC");
949                     return;
950                 }
951                 mAttachCallback.onAttached(new WifiAwareSession(mgr, mBinder, clientId));
952             });
953         }
954 
955         @Override
onConnectFail(int reason)956         public void onConnectFail(int reason) {
957             if (VDBG) Log.v(TAG, "onConnectFail: reason=" + reason);
958             Binder.clearCallingIdentity();
959             mExecutor.execute(() -> {
960                 WifiAwareManager mgr = mAwareManager.get();
961                 if (mgr == null) {
962                     Log.w(TAG, "WifiAwareEventCallbackProxy: handleMessage post GC");
963                     return;
964                 }
965                 mAwareManager.clear();
966                 mAttachCallback.onAttachFailed();
967             });
968         }
969 
970         @Override
onIdentityChanged(byte[] mac)971         public void onIdentityChanged(byte[] mac) {
972             if (VDBG) Log.v(TAG, "onIdentityChanged: mac=" + new String(HexEncoding.encode(mac)));
973             Binder.clearCallingIdentity();
974             mExecutor.execute(() -> {
975                 WifiAwareManager mgr = mAwareManager.get();
976                 if (mgr == null) {
977                     Log.w(TAG, "WifiAwareEventCallbackProxy: handleMessage post GC");
978                     return;
979                 }
980                 if (mIdentityChangedListener == null) {
981                     Log.e(TAG, "CALLBACK_IDENTITY_CHANGED: null listener.");
982                 } else {
983                     mIdentityChangedListener.onIdentityChanged(mac);
984                 }
985             });
986         }
987 
988         @Override
onAttachTerminate()989         public void onAttachTerminate() {
990             if (VDBG) Log.v(TAG, "onAwareSessionTerminated");
991             Binder.clearCallingIdentity();
992             mExecutor.execute(() -> {
993                 WifiAwareManager mgr = mAwareManager.get();
994                 if (mgr == null) {
995                     Log.w(TAG, "WifiAwareEventCallbackProxy: handleMessage post GC");
996                     return;
997                 }
998                 mAwareManager.clear();
999                 mAttachCallback.onAwareSessionTerminated();
1000             });
1001         }
1002 
1003         @Override
onClusterIdChanged( @dentityChangedListener.ClusterChangeEvent int clusterEventType, byte[] clusterId)1004         public void onClusterIdChanged(
1005                 @IdentityChangedListener.ClusterChangeEvent int clusterEventType,
1006                 byte[] clusterId) {
1007             Binder.clearCallingIdentity();
1008             mExecutor.execute(() -> {
1009                 WifiAwareManager mgr = mAwareManager.get();
1010                 if (mgr == null) {
1011                     Log.w(TAG, "WifiAwareEventCallbackProxy: handleMessage post GC");
1012                     return;
1013                 }
1014                 if (mIdentityChangedListener == null) {
1015                     Log.e(TAG, "CALLBACK_CLUSTER_ID_CHANGED: null listener.");
1016                 } else {
1017                     try {
1018                         mIdentityChangedListener.onClusterIdChanged(
1019                                 clusterEventType, MacAddress.fromBytes(clusterId));
1020                     } catch (IllegalArgumentException iae) {
1021                         Log.e(TAG, " Invalid MAC address, " + iae);
1022                     }
1023                 }
1024             });
1025         }
1026     }
1027 
1028     private static class WifiAwareDiscoverySessionCallbackProxy extends
1029             IWifiAwareDiscoverySessionCallback.Stub {
1030         private final WeakReference<WifiAwareManager> mAwareManager;
1031         private final boolean mIsPublish;
1032         private final DiscoverySessionCallback mOriginalCallback;
1033         private final int mClientId;
1034 
1035         private final Handler mHandler;
1036         private DiscoverySession mSession;
1037 
WifiAwareDiscoverySessionCallbackProxy(WifiAwareManager mgr, Looper looper, boolean isPublish, DiscoverySessionCallback originalCallback, int clientId)1038         WifiAwareDiscoverySessionCallbackProxy(WifiAwareManager mgr, Looper looper,
1039                 boolean isPublish, DiscoverySessionCallback originalCallback,
1040                 int clientId) {
1041             mAwareManager = new WeakReference<>(mgr);
1042             mIsPublish = isPublish;
1043             mOriginalCallback = originalCallback;
1044             mClientId = clientId;
1045 
1046             if (VDBG) {
1047                 Log.v(TAG, "WifiAwareDiscoverySessionCallbackProxy ctor: isPublish=" + isPublish);
1048             }
1049 
1050             mHandler = new Handler(looper);
1051         }
1052 
1053         @Override
onSessionStarted(int sessionId)1054         public void onSessionStarted(int sessionId) {
1055             if (VDBG) Log.v(TAG, "onSessionStarted: sessionId=" + sessionId);
1056             mHandler.post(() -> onProxySessionStarted(sessionId));
1057         }
1058 
1059         @Override
onSessionConfigSuccess()1060         public void onSessionConfigSuccess() {
1061             if (VDBG) Log.v(TAG, "onSessionConfigSuccess");
1062             mHandler.post(mOriginalCallback::onSessionConfigUpdated);
1063         }
1064 
1065         @Override
onSessionConfigFail(int reason)1066         public void onSessionConfigFail(int reason) {
1067             if (VDBG) Log.v(TAG, "onSessionConfigFail: reason=" + reason);
1068             mHandler.post(() -> {
1069                 mOriginalCallback.onSessionConfigFailed();
1070                 if (mSession == null) {
1071                     /* creation failed (as opposed to update failing) */
1072                     mAwareManager.clear();
1073                 }
1074             });
1075         }
1076 
1077         @Override
onSessionTerminated(int reason)1078         public void onSessionTerminated(int reason) {
1079             if (VDBG) Log.v(TAG, "onSessionTerminated: reason=" + reason);
1080             mHandler.post(() -> onProxySessionTerminated(reason));
1081         }
1082 
1083         @Override
onSessionSuspendSucceeded()1084         public void onSessionSuspendSucceeded() {
1085             if (VDBG) Log.v(TAG, "onSessionSuspendSucceeded");
1086             mHandler.post(mOriginalCallback::onSessionSuspendSucceeded);
1087         }
1088 
1089         @Override
onSessionSuspendFail(int reason)1090         public void onSessionSuspendFail(int reason) {
1091             if (VDBG) Log.v(TAG, "onSessionSuspendFail: reason=" + reason);
1092             mHandler.post(() -> mOriginalCallback.onSessionSuspendFailed(reason));
1093         }
1094 
1095         @Override
onSessionResumeSucceeded()1096         public void onSessionResumeSucceeded() {
1097             if (VDBG) Log.v(TAG, "onSessionResumeSucceeded");
1098             mHandler.post(mOriginalCallback::onSessionResumeSucceeded);
1099         }
1100 
1101         @Override
onSessionResumeFail(int reason)1102         public void onSessionResumeFail(int reason) {
1103             if (VDBG) Log.v(TAG, "onSessionResumeFail: reason=" + reason);
1104             mHandler.post(() -> mOriginalCallback.onSessionResumeFailed(reason));
1105         }
1106 
1107         @Override
onMatch(int peerId, byte[] serviceSpecificInfo, byte[] matchFilter, int peerCipherSuite, byte[] scid, String pairingAlias, AwarePairingConfig pairingConfig, @Nullable OuiKeyedData[] vendorData)1108         public void onMatch(int peerId, byte[] serviceSpecificInfo, byte[] matchFilter,
1109                 int peerCipherSuite, byte[] scid, String pairingAlias,
1110                 AwarePairingConfig pairingConfig, @Nullable OuiKeyedData[] vendorData) {
1111             if (VDBG) Log.v(TAG, "onMatch: peerId=" + peerId);
1112 
1113             mHandler.post(() -> {
1114                 List<byte[]> matchFilterList = getMatchFilterList(matchFilter);
1115                 mOriginalCallback.onServiceDiscovered(new PeerHandle(peerId), serviceSpecificInfo,
1116                         matchFilterList);
1117                 mOriginalCallback.onServiceDiscovered(
1118                         new ServiceDiscoveryInfo(new PeerHandle(peerId), peerCipherSuite,
1119                                 serviceSpecificInfo, matchFilterList, scid, pairingAlias,
1120                                 pairingConfig, vendorData));
1121             });
1122         }
1123 
getMatchFilterList(byte[] matchFilter)1124         private List<byte[]> getMatchFilterList(byte[] matchFilter) {
1125             List<byte[]> matchFilterList = null;
1126             try {
1127                 matchFilterList = new TlvBufferUtils.TlvIterable(0, 1, matchFilter).toList();
1128             } catch (BufferOverflowException e) {
1129                 matchFilterList = Collections.emptyList();
1130                 Log.e(TAG, "onServiceDiscovered: invalid match filter byte array '"
1131                         + new String(HexEncoding.encode(matchFilter))
1132                         + "' - cannot be parsed: e=" + e);
1133             }
1134             return matchFilterList;
1135         }
1136 
1137         @Override
onMatchWithDistance(int peerId, byte[] serviceSpecificInfo, byte[] matchFilter, int distanceMm, int peerCipherSuite, byte[] scid, String pairingAlias, AwarePairingConfig pairingConfig, @Nullable OuiKeyedData[] vendorData)1138         public void onMatchWithDistance(int peerId, byte[] serviceSpecificInfo, byte[] matchFilter,
1139                 int distanceMm, int peerCipherSuite, byte[] scid, String pairingAlias,
1140                 AwarePairingConfig pairingConfig, @Nullable OuiKeyedData[] vendorData) {
1141             if (VDBG) {
1142                 Log.v(TAG, "onMatchWithDistance: peerId=" + peerId + ", distanceMm=" + distanceMm);
1143             }
1144             mHandler.post(() -> {
1145                 List<byte[]> matchFilterList = getMatchFilterList(matchFilter);
1146                 mOriginalCallback.onServiceDiscoveredWithinRange(
1147                         new PeerHandle(peerId),
1148                         serviceSpecificInfo,
1149                         matchFilterList, distanceMm);
1150                 mOriginalCallback.onServiceDiscoveredWithinRange(
1151                         new ServiceDiscoveryInfo(
1152                                 new PeerHandle(peerId),
1153                                 peerCipherSuite,
1154                                 serviceSpecificInfo,
1155                                 matchFilterList,
1156                                 scid,
1157                                 pairingAlias,
1158                                 pairingConfig,
1159                                 vendorData),
1160                         distanceMm);
1161             });
1162         }
1163         @Override
onMatchExpired(int peerId)1164         public void onMatchExpired(int peerId) {
1165             if (VDBG) {
1166                 Log.v(TAG, "onMatchExpired: peerId=" + peerId);
1167             }
1168             mHandler.post(() ->
1169                     mOriginalCallback.onServiceLost(new PeerHandle(peerId),
1170                             WIFI_AWARE_DISCOVERY_LOST_REASON_PEER_NOT_VISIBLE));
1171         }
1172 
1173         @Override
onMessageSendSuccess(int messageId)1174         public void onMessageSendSuccess(int messageId) {
1175             if (VDBG) Log.v(TAG, "onMessageSendSuccess");
1176             mHandler.post(() -> mOriginalCallback.onMessageSendSucceeded(messageId));
1177         }
1178 
1179         @Override
onMessageSendFail(int messageId, int reason)1180         public void onMessageSendFail(int messageId, int reason) {
1181             if (VDBG) Log.v(TAG, "onMessageSendFail: reason=" + reason);
1182             mHandler.post(() -> mOriginalCallback.onMessageSendFailed(messageId));
1183         }
1184 
1185         @Override
onMessageReceived(int peerId, byte[] message)1186         public void onMessageReceived(int peerId, byte[] message) {
1187             if (VDBG) {
1188                 Log.v(TAG, "onMessageReceived: peerId=" + peerId);
1189             }
1190             mHandler.post(() -> mOriginalCallback.onMessageReceived(new PeerHandle(peerId),
1191                     message));
1192         }
1193 
1194         @Override
onPairingSetupRequestReceived(int peerId, int requestId)1195         public void onPairingSetupRequestReceived(int peerId, int requestId) {
1196             mHandler.post(() ->
1197                     mOriginalCallback.onPairingSetupRequestReceived(new PeerHandle(peerId),
1198                             requestId));
1199         }
1200         @Override
onPairingSetupConfirmed(int peerId, boolean accept, String alias)1201         public void onPairingSetupConfirmed(int peerId, boolean accept, String alias) {
1202             if (accept) {
1203                 mHandler.post(() -> mOriginalCallback
1204                         .onPairingSetupSucceeded(new PeerHandle(peerId), alias));
1205             } else {
1206                 mHandler.post(() -> mOriginalCallback
1207                         .onPairingSetupFailed(new PeerHandle(peerId)));
1208             }
1209         }
1210         @Override
onPairingVerificationConfirmed(int peerId, boolean accept, String alias)1211         public void onPairingVerificationConfirmed(int peerId, boolean accept, String alias) {
1212             if (accept) {
1213                 mHandler.post(() -> mOriginalCallback.onPairingVerificationSucceed(
1214                         new PeerHandle(peerId), alias));
1215             } else {
1216                 mHandler.post(() -> mOriginalCallback
1217                         .onPairingVerificationFailed(new PeerHandle(peerId)));
1218             }
1219         }
1220         @Override
onBootstrappingVerificationConfirmed(int peerId, boolean accept, int method)1221         public void onBootstrappingVerificationConfirmed(int peerId, boolean accept, int method) {
1222             if (accept) {
1223                 mHandler.post(() -> mOriginalCallback.onBootstrappingSucceeded(
1224                         new PeerHandle(peerId), method));
1225             } else {
1226                 mHandler.post(() -> mOriginalCallback.onBootstrappingFailed(
1227                         new PeerHandle(peerId)));
1228             }
1229         }
1230 
1231         @Override
onRangingResultsReceived(List<RangingResult> rangingResults)1232         public void onRangingResultsReceived(List<RangingResult> rangingResults) {
1233             mHandler.post(() -> mOriginalCallback.onRangingResultsReceived(rangingResults));
1234         }
1235 
1236         /*
1237          * Proxies methods
1238          */
onProxySessionStarted(int sessionId)1239         public void onProxySessionStarted(int sessionId) {
1240             if (VDBG) Log.v(TAG, "Proxy: onSessionStarted: sessionId=" + sessionId);
1241             if (mSession != null) {
1242                 Log.e(TAG,
1243                         "onSessionStarted: sessionId=" + sessionId + ": session already created!?");
1244                 throw new IllegalStateException(
1245                         "onSessionStarted: sessionId=" + sessionId + ": session already created!?");
1246             }
1247 
1248             WifiAwareManager mgr = mAwareManager.get();
1249             if (mgr == null) {
1250                 Log.w(TAG, "onProxySessionStarted: mgr GC'd");
1251                 return;
1252             }
1253 
1254             if (mIsPublish) {
1255                 PublishDiscoverySession session = new PublishDiscoverySession(mgr,
1256                         mClientId, sessionId);
1257                 mSession = session;
1258                 mOriginalCallback.onPublishStarted(session);
1259             } else {
1260                 SubscribeDiscoverySession
1261                         session = new SubscribeDiscoverySession(mgr, mClientId, sessionId);
1262                 mSession = session;
1263                 mOriginalCallback.onSubscribeStarted(session);
1264             }
1265         }
1266 
onProxySessionTerminated(int reason)1267         public void onProxySessionTerminated(int reason) {
1268             if (VDBG) Log.v(TAG, "Proxy: onSessionTerminated: reason=" + reason);
1269             if (mSession != null) {
1270                 mSession.setTerminated();
1271                 mSession = null;
1272             } else {
1273                 Log.w(TAG, "Proxy: onSessionTerminated called but mSession is null!?");
1274             }
1275             mAwareManager.clear();
1276             mOriginalCallback.onSessionTerminated();
1277         }
1278     }
1279 
1280     /**
1281      * Set Wi-Fi Aware protocol parameters.
1282      * @hide
1283      * @param params An object contain specified parameters. Use {@code null} to remove previously
1284      *               set configuration and restore default behavior.
1285      */
1286     @SystemApi
1287     @RequiresPermission(allOf = {OVERRIDE_WIFI_CONFIG,
1288             CHANGE_WIFI_STATE})
setAwareParams(@ullable AwareParams params)1289     public void setAwareParams(@Nullable AwareParams params) {
1290         try {
1291             mService.setAwareParams(params);
1292         } catch (RemoteException e) {
1293             throw e.rethrowFromSystemServer();
1294         }
1295     }
1296 
1297     /**
1298      *  Set all Wi-Fi Aware sessions created by the calling app to be opportunistic. Opportunistic
1299      *  Wi-Fi Aware sessions are considered low priority and may be torn down (the sessions or the
1300      *  Aware interface) if there are resource conflicts.
1301      *
1302      * @param enabled True to configure all Wi-Fi Aware sessions by the calling app as
1303      *                Opportunistic. False by default.
1304      */
1305     @RequiresPermission(CHANGE_WIFI_STATE)
setOpportunisticModeEnabled(boolean enabled)1306     public void setOpportunisticModeEnabled(boolean enabled) {
1307         try {
1308             mService.setOpportunisticModeEnabled(mContext.getOpPackageName(), enabled);
1309         } catch (RemoteException e) {
1310             throw e.rethrowFromSystemServer();
1311         }
1312     }
1313 
1314     /**
1315      * Indicate whether all Wi-Fi Aware sessions created by the calling app are opportunistic as
1316      * defined and configured by {@link #setOpportunisticModeEnabled(boolean)}
1317      *
1318      * @param executor The executor on which callback will be invoked.
1319      * @param resultsCallback An asynchronous callback that will return boolean
1320      */
1321     @RequiresPermission(ACCESS_WIFI_STATE)
isOpportunisticModeEnabled(@onNull @allbackExecutor Executor executor, @NonNull Consumer<Boolean> resultsCallback)1322     public void isOpportunisticModeEnabled(@NonNull @CallbackExecutor Executor executor,
1323             @NonNull Consumer<Boolean> resultsCallback) {
1324         Objects.requireNonNull(executor, "executor cannot be null");
1325         Objects.requireNonNull(resultsCallback, "resultsCallback cannot be null");
1326 
1327         try {
1328             mService.isOpportunisticModeEnabled(mContext.getOpPackageName(),
1329                     new IBooleanListener.Stub() {
1330                         @Override
1331                         public void onResult(boolean value) {
1332                             Binder.clearCallingIdentity();
1333                             executor.execute(() -> {
1334                                 resultsCallback.accept(value);
1335                             });
1336                         }
1337                     });
1338         } catch (RemoteException e) {
1339             throw e.rethrowFromSystemServer();
1340         }
1341     }
1342 
1343     /**
1344      * Reset all paired devices setup by the caller by
1345      * {@link DiscoverySession#initiatePairingRequest(PeerHandle, String, int, String)} and
1346      * {@link DiscoverySession#acceptPairingRequest(int, PeerHandle, String, int, String)}
1347      */
1348     @RequiresPermission(CHANGE_WIFI_STATE)
resetPairedDevices()1349     public void resetPairedDevices() {
1350         try {
1351             mService.resetPairedDevices(mContext.getOpPackageName());
1352         } catch (RemoteException e) {
1353             throw e.rethrowFromSystemServer();
1354         }
1355     }
1356 
1357     /**
1358      * Remove the target paired device setup by the caller by
1359      * {@link DiscoverySession#initiatePairingRequest(PeerHandle, String, int, String)} and
1360      * {@link DiscoverySession#acceptPairingRequest(int, PeerHandle, String, int, String)}
1361      * @param alias The alias set by the caller
1362      */
1363     @RequiresPermission(CHANGE_WIFI_STATE)
removePairedDevice(@onNull String alias)1364     public void removePairedDevice(@NonNull String alias) {
1365         try {
1366             mService.removePairedDevice(mContext.getOpPackageName(), alias);
1367         } catch (RemoteException e) {
1368             throw e.rethrowFromSystemServer();
1369         }
1370     }
1371 
1372     /**
1373      * Get all the paired devices configured by the calling app.
1374      * @param executor The executor on which callback will be invoked.
1375      * @param resultsCallback An asynchronous callback that will return a list of paired devices'
1376      *                        alias
1377      */
1378     @RequiresPermission(ACCESS_WIFI_STATE)
getPairedDevices(@onNull Executor executor, @NonNull Consumer<List<String>> resultsCallback)1379     public void getPairedDevices(@NonNull Executor executor,
1380             @NonNull Consumer<List<String>> resultsCallback) {
1381         Objects.requireNonNull(executor, "executor cannot be null");
1382         Objects.requireNonNull(resultsCallback, "resultsCallback cannot be null");
1383         try {
1384             mService.getPairedDevices(
1385                     mContext.getOpPackageName(),
1386                     new IListListener.Stub() {
1387                         public void onResult(List value) {
1388                             Binder.clearCallingIdentity();
1389                             executor.execute(() -> resultsCallback.accept(value));
1390                         }
1391                     });
1392         } catch (RemoteException e) {
1393             throw e.rethrowFromSystemServer();
1394         }
1395     }
1396 
1397     /**
1398      * @hide
1399      */
suspend(int clientId, int sessionId)1400     public void suspend(int clientId, int sessionId) {
1401         try {
1402             mService.suspend(clientId, sessionId);
1403         } catch (RemoteException e) {
1404             throw e.rethrowFromSystemServer();
1405         }
1406     }
1407 
1408     /**
1409      * @hide
1410      */
resume(int clientId, int sessionId)1411     public void resume(int clientId, int sessionId) {
1412         try {
1413             mService.resume(clientId, sessionId);
1414         } catch (RemoteException e) {
1415             throw e.rethrowFromSystemServer();
1416         }
1417     }
1418     /**
1419      * Attach to the Wi-Fi Aware service as an offload session. All discovery sessions and
1420      * connections will be handled via out-of-band connections.
1421      * The Aware session created by this attach method will have the lowest priority when resource
1422      * conflicts arise (e.g. Aware has to be torn down to create other WiFi interfaces).
1423      *
1424      * @param executor       The executor to execute the listener of the {@code attachCallback}
1425      *                       object.
1426      * @param attachCallback A callback for attach events, extended from
1427      *                       {@link AttachCallback}.
1428      * @hide
1429      * @see #attach(AttachCallback, Handler)
1430      */
1431     @SystemApi
1432     @RequiresPermission(allOf = {ACCESS_WIFI_STATE, CHANGE_WIFI_STATE, OVERRIDE_WIFI_CONFIG})
attachOffload(@onNull @allbackExecutor Executor executor, @NonNull AttachCallback attachCallback)1433     public void attachOffload(@NonNull @CallbackExecutor Executor executor,
1434             @NonNull AttachCallback attachCallback) {
1435         if (executor == null) {
1436             throw new IllegalArgumentException("Null executor provided");
1437         }
1438         attach(null, null, attachCallback, null, true, executor);
1439     }
1440 
1441 }
1442