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