• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2016 The Android Open Source Project
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 
17 package android.net.wifi.aware;
18 
19 import android.annotation.NonNull;
20 import android.annotation.Nullable;
21 import android.annotation.SystemApi;
22 import android.net.NetworkSpecifier;
23 import android.net.wifi.RttManager;
24 import android.util.Log;
25 
26 import dalvik.system.CloseGuard;
27 
28 import java.lang.ref.WeakReference;
29 
30 /**
31  * A class representing a single publish or subscribe Aware session. This object
32  * will not be created directly - only its child classes are available:
33  * {@link PublishDiscoverySession} and {@link SubscribeDiscoverySession}. This
34  * class provides functionality common to both publish and subscribe discovery sessions:
35  * <ul>
36  *     <li>Sending messages: {@link #sendMessage(PeerHandle, int, byte[])} method.
37  *     <li>Creating a network-specifier when requesting a Aware connection:
38  *     {@link #createNetworkSpecifierOpen(PeerHandle)} or
39  *     {@link #createNetworkSpecifierPassphrase(PeerHandle, String)}.
40  * </ul>
41  * The {@link #close()} method must be called to destroy discovery sessions once they are
42  * no longer needed.
43  */
44 public class DiscoverySession implements AutoCloseable {
45     private static final String TAG = "DiscoverySession";
46     private static final boolean DBG = false;
47     private static final boolean VDBG = false; // STOPSHIP if true
48 
49     private static final int MAX_SEND_RETRY_COUNT = 5;
50 
51     /** @hide */
52     protected WeakReference<WifiAwareManager> mMgr;
53     /** @hide */
54     protected final int mClientId;
55     /** @hide */
56     protected final int mSessionId;
57     /** @hide */
58     protected boolean mTerminated = false;
59 
60     private final CloseGuard mCloseGuard = CloseGuard.get();
61 
62     /**
63      * Return the maximum permitted retry count when sending messages using
64      * {@link #sendMessage(PeerHandle, int, byte[], int)}.
65      *
66      * @return Maximum retry count when sending messages.
67      *
68      * @hide
69      */
getMaxSendRetryCount()70     public static int getMaxSendRetryCount() {
71         return MAX_SEND_RETRY_COUNT;
72     }
73 
74     /** @hide */
DiscoverySession(WifiAwareManager manager, int clientId, int sessionId)75     public DiscoverySession(WifiAwareManager manager, int clientId, int sessionId) {
76         if (VDBG) {
77             Log.v(TAG, "New discovery session created: manager=" + manager + ", clientId="
78                     + clientId + ", sessionId=" + sessionId);
79         }
80 
81         mMgr = new WeakReference<>(manager);
82         mClientId = clientId;
83         mSessionId = sessionId;
84 
85         mCloseGuard.open("destroy");
86     }
87 
88     /**
89      * Destroy the publish or subscribe session - free any resources, and stop
90      * transmitting packets on-air (for an active session) or listening for
91      * matches (for a passive session). The session may not be used for any
92      * additional operations after its destruction.
93      * <p>
94      *     This operation must be done on a session which is no longer needed. Otherwise system
95      *     resources will continue to be utilized until the application exits. The only
96      *     exception is a session for which we received a termination callback,
97      *     {@link DiscoverySessionCallback#onSessionTerminated()}.
98      */
99     @Override
close()100     public void close() {
101         WifiAwareManager mgr = mMgr.get();
102         if (mgr == null) {
103             Log.w(TAG, "destroy: called post GC on WifiAwareManager");
104             return;
105         }
106         mgr.terminateSession(mClientId, mSessionId);
107         mTerminated = true;
108         mMgr.clear();
109         mCloseGuard.close();
110     }
111 
112     /**
113      * Sets the status of the session to terminated - i.e. an indication that
114      * already terminated rather than executing a termination.
115      *
116      * @hide
117      */
setTerminated()118     public void setTerminated() {
119         if (mTerminated) {
120             Log.w(TAG, "terminate: already terminated.");
121             return;
122         }
123 
124         mTerminated = true;
125         mMgr.clear();
126         mCloseGuard.close();
127     }
128 
129     /** @hide */
130     @Override
finalize()131     protected void finalize() throws Throwable {
132         try {
133             if (!mTerminated) {
134                 mCloseGuard.warnIfOpen();
135                 close();
136             }
137         } finally {
138             super.finalize();
139         }
140     }
141 
142     /**
143      * Sends a message to the specified destination. Aware messages are transmitted in the context
144      * of a discovery session - executed subsequent to a publish/subscribe
145      * {@link DiscoverySessionCallback#onServiceDiscovered(PeerHandle,
146      * byte[], java.util.List)} event.
147      * <p>
148      *     Aware messages are not guaranteed delivery. Callbacks on
149      *     {@link DiscoverySessionCallback} indicate message was transmitted successfully,
150      *     {@link DiscoverySessionCallback#onMessageSendSucceeded(int)}, or transmission
151      *     failed (possibly after several retries) -
152      *     {@link DiscoverySessionCallback#onMessageSendFailed(int)}.
153      * <p>
154      *     The peer will get a callback indicating a message was received using
155      *     {@link DiscoverySessionCallback#onMessageReceived(PeerHandle,
156      *     byte[])}.
157      *
158      * @param peerHandle The peer's handle for the message. Must be a result of an
159      * {@link DiscoverySessionCallback#onServiceDiscovered(PeerHandle,
160      * byte[], java.util.List)} or
161      * {@link DiscoverySessionCallback#onMessageReceived(PeerHandle,
162      * byte[])} events.
163      * @param messageId An arbitrary integer used by the caller to identify the message. The same
164      *            integer ID will be returned in the callbacks indicating message send success or
165      *            failure. The {@code messageId} is not used internally by the Aware service - it
166      *                  can be arbitrary and non-unique.
167      * @param message The message to be transmitted.
168      * @param retryCount An integer specifying how many additional service-level (as opposed to PHY
169      *            or MAC level) retries should be attempted if there is no ACK from the receiver
170      *            (note: no retransmissions are attempted in other failure cases). A value of 0
171      *            indicates no retries. Max permitted value is {@link #getMaxSendRetryCount()}.
172      *
173      * @hide
174      */
sendMessage(@onNull PeerHandle peerHandle, int messageId, @Nullable byte[] message, int retryCount)175     public void sendMessage(@NonNull PeerHandle peerHandle, int messageId,
176             @Nullable byte[] message, int retryCount) {
177         if (mTerminated) {
178             Log.w(TAG, "sendMessage: called on terminated session");
179             return;
180         }
181 
182         WifiAwareManager mgr = mMgr.get();
183         if (mgr == null) {
184             Log.w(TAG, "sendMessage: called post GC on WifiAwareManager");
185             return;
186         }
187 
188         mgr.sendMessage(mClientId, mSessionId, peerHandle, message, messageId, retryCount);
189     }
190 
191     /**
192      * Sends a message to the specified destination. Aware messages are transmitted in the context
193      * of a discovery session - executed subsequent to a publish/subscribe
194      * {@link DiscoverySessionCallback#onServiceDiscovered(PeerHandle,
195      * byte[], java.util.List)} event.
196      * <p>
197      *     Aware messages are not guaranteed delivery. Callbacks on
198      *     {@link DiscoverySessionCallback} indicate message was transmitted successfully,
199      *     {@link DiscoverySessionCallback#onMessageSendSucceeded(int)}, or transmission
200      *     failed (possibly after several retries) -
201      *     {@link DiscoverySessionCallback#onMessageSendFailed(int)}.
202      * <p>
203      * The peer will get a callback indicating a message was received using
204      * {@link DiscoverySessionCallback#onMessageReceived(PeerHandle,
205      * byte[])}.
206      *
207      * @param peerHandle The peer's handle for the message. Must be a result of an
208      * {@link DiscoverySessionCallback#onServiceDiscovered(PeerHandle,
209      * byte[], java.util.List)} or
210      * {@link DiscoverySessionCallback#onMessageReceived(PeerHandle,
211      * byte[])} events.
212      * @param messageId An arbitrary integer used by the caller to identify the message. The same
213      *            integer ID will be returned in the callbacks indicating message send success or
214      *            failure. The {@code messageId} is not used internally by the Aware service - it
215      *                  can be arbitrary and non-unique.
216      * @param message The message to be transmitted.
217      */
sendMessage(@onNull PeerHandle peerHandle, int messageId, @Nullable byte[] message)218     public void sendMessage(@NonNull PeerHandle peerHandle, int messageId,
219             @Nullable byte[] message) {
220         sendMessage(peerHandle, messageId, message, 0);
221     }
222 
223     /**
224      * Start a ranging operation with the specified peers. The peer IDs are obtained from an
225      * {@link DiscoverySessionCallback#onServiceDiscovered(PeerHandle,
226      * byte[], java.util.List)} or
227      * {@link DiscoverySessionCallback#onMessageReceived(PeerHandle,
228      * byte[])} operation - can
229      * only range devices which are part of an ongoing discovery session.
230      *
231      * @param params   RTT parameters - each corresponding to a specific peer ID (the array sizes
232      *                 must be identical). The
233      *                 {@link android.net.wifi.RttManager.RttParams#bssid} member must be set to
234      *                 a peer ID - not to a MAC address.
235      * @param listener The listener to receive the results of the ranging session.
236      * @hide
237      * [TODO: b/28847998 - track RTT API & visilibity]
238      */
startRanging(RttManager.RttParams[] params, RttManager.RttListener listener)239     public void startRanging(RttManager.RttParams[] params, RttManager.RttListener listener) {
240         if (mTerminated) {
241             Log.w(TAG, "startRanging: called on terminated session");
242             return;
243         }
244 
245         WifiAwareManager mgr = mMgr.get();
246         if (mgr == null) {
247             Log.w(TAG, "startRanging: called post GC on WifiAwareManager");
248             return;
249         }
250 
251         mgr.startRanging(mClientId, mSessionId, params, listener);
252     }
253 
254     /**
255      * Create a {@link android.net.NetworkRequest.Builder#setNetworkSpecifier(NetworkSpecifier)} for
256      * an unencrypted WiFi Aware connection (link) to the specified peer. The
257      * {@link android.net.NetworkRequest.Builder#addTransportType(int)} should be set to
258      * {@link android.net.NetworkCapabilities#TRANSPORT_WIFI_AWARE}.
259      * <p>
260      * This method should be used when setting up a connection with a peer discovered through Aware
261      * discovery or communication (in such scenarios the MAC address of the peer is shielded by
262      * an opaque peer ID handle). If an Aware connection is needed to a peer discovered using other
263      * OOB (out-of-band) mechanism then use the alternative
264      * {@link WifiAwareSession#createNetworkSpecifierOpen(int, byte[])} method - which uses the
265      * peer's MAC address.
266      * <p>
267      * Note: per the Wi-Fi Aware specification the roles are fixed - a Subscriber is an INITIATOR
268      * and a Publisher is a RESPONDER.
269      * <p>
270      * To set up an encrypted link use the
271      * {@link #createNetworkSpecifierPassphrase(PeerHandle, String)} API.
272      *
273      * @param peerHandle The peer's handle obtained through
274      * {@link DiscoverySessionCallback#onServiceDiscovered(PeerHandle, byte[], java.util.List)}
275      *                   or
276      *                   {@link DiscoverySessionCallback#onMessageReceived(PeerHandle, byte[])}.
277      *                   On a RESPONDER this value is used to gate the acceptance of a connection
278      *                   request from only that peer. A RESPONDER may specify a {@code null} -
279      *                   indicating that it will accept connection requests from any device.
280      *
281      * @return A {@link NetworkSpecifier} to be used to construct
282      * {@link android.net.NetworkRequest.Builder#setNetworkSpecifier(NetworkSpecifier)} to pass to
283      * {@link android.net.ConnectivityManager#requestNetwork(android.net.NetworkRequest,
284      * android.net.ConnectivityManager.NetworkCallback)}
285      * [or other varieties of that API].
286      */
createNetworkSpecifierOpen(@ullable PeerHandle peerHandle)287     public NetworkSpecifier createNetworkSpecifierOpen(@Nullable PeerHandle peerHandle) {
288         if (mTerminated) {
289             Log.w(TAG, "createNetworkSpecifierOpen: called on terminated session");
290             return null;
291         }
292 
293         WifiAwareManager mgr = mMgr.get();
294         if (mgr == null) {
295             Log.w(TAG, "createNetworkSpecifierOpen: called post GC on WifiAwareManager");
296             return null;
297         }
298 
299         int role = this instanceof SubscribeDiscoverySession
300                 ? WifiAwareManager.WIFI_AWARE_DATA_PATH_ROLE_INITIATOR
301                 : WifiAwareManager.WIFI_AWARE_DATA_PATH_ROLE_RESPONDER;
302 
303         return mgr.createNetworkSpecifier(mClientId, role, mSessionId, peerHandle, null, null);
304     }
305 
306     /**
307      * Create a {@link android.net.NetworkRequest.Builder#setNetworkSpecifier(NetworkSpecifier)} for
308      * an encrypted WiFi Aware connection (link) to the specified peer. The
309      * {@link android.net.NetworkRequest.Builder#addTransportType(int)} should be set to
310      * {@link android.net.NetworkCapabilities#TRANSPORT_WIFI_AWARE}.
311      * <p>
312      * This method should be used when setting up a connection with a peer discovered through Aware
313      * discovery or communication (in such scenarios the MAC address of the peer is shielded by
314      * an opaque peer ID handle). If an Aware connection is needed to a peer discovered using other
315      * OOB (out-of-band) mechanism then use the alternative
316      * {@link WifiAwareSession#createNetworkSpecifierPassphrase(int, byte[], String)} method -
317      * which uses the peer's MAC address.
318      * <p>
319      * Note: per the Wi-Fi Aware specification the roles are fixed - a Subscriber is an INITIATOR
320      * and a Publisher is a RESPONDER.
321      *
322      * @param peerHandle The peer's handle obtained through
323      * {@link DiscoverySessionCallback#onServiceDiscovered(PeerHandle,
324      * byte[], java.util.List)} or
325      * {@link DiscoverySessionCallback#onMessageReceived(PeerHandle,
326      * byte[])}. On a RESPONDER this value is used to gate the acceptance of a connection request
327      *                   from only that peer. A RESPONDER may specify a {@code null} - indicating
328      *                   that it will accept connection requests from any device.
329      * @param passphrase The passphrase to be used to encrypt the link. The PMK is generated from
330      *                   the passphrase. Use the
331      *                   {@link #createNetworkSpecifierOpen(PeerHandle)} API to
332      *                   specify an open (unencrypted) link.
333      *
334      * @return A {@link NetworkSpecifier} to be used to construct
335      * {@link android.net.NetworkRequest.Builder#setNetworkSpecifier(NetworkSpecifier)} to pass to
336      * {@link android.net.ConnectivityManager#requestNetwork(android.net.NetworkRequest,
337      * android.net.ConnectivityManager.NetworkCallback)}
338      * [or other varieties of that API].
339      */
createNetworkSpecifierPassphrase( @ullable PeerHandle peerHandle, @NonNull String passphrase)340     public NetworkSpecifier createNetworkSpecifierPassphrase(
341             @Nullable PeerHandle peerHandle, @NonNull String passphrase) {
342         if (passphrase == null || passphrase.length() == 0) {
343             throw new IllegalArgumentException("Passphrase must not be null or empty");
344         }
345 
346         if (mTerminated) {
347             Log.w(TAG, "createNetworkSpecifierPassphrase: called on terminated session");
348             return null;
349         }
350 
351         WifiAwareManager mgr = mMgr.get();
352         if (mgr == null) {
353             Log.w(TAG, "createNetworkSpecifierPassphrase: called post GC on WifiAwareManager");
354             return null;
355         }
356 
357         int role = this instanceof SubscribeDiscoverySession
358                 ? WifiAwareManager.WIFI_AWARE_DATA_PATH_ROLE_INITIATOR
359                 : WifiAwareManager.WIFI_AWARE_DATA_PATH_ROLE_RESPONDER;
360 
361         return mgr.createNetworkSpecifier(mClientId, role, mSessionId, peerHandle, null,
362                 passphrase);
363     }
364 
365     /**
366      * Create a {@link android.net.NetworkRequest.Builder#setNetworkSpecifier(NetworkSpecifier)} for
367      * an encrypted WiFi Aware connection (link) to the specified peer. The
368      * {@link android.net.NetworkRequest.Builder#addTransportType(int)} should be set to
369      * {@link android.net.NetworkCapabilities#TRANSPORT_WIFI_AWARE}.
370      * <p>
371      * This method should be used when setting up a connection with a peer discovered through Aware
372      * discovery or communication (in such scenarios the MAC address of the peer is shielded by
373      * an opaque peer ID handle). If an Aware connection is needed to a peer discovered using other
374      * OOB (out-of-band) mechanism then use the alternative
375      * {@link WifiAwareSession#createNetworkSpecifierPmk(int, byte[], byte[])} method - which uses
376      * the peer's MAC address.
377      * <p>
378      * Note: per the Wi-Fi Aware specification the roles are fixed - a Subscriber is an INITIATOR
379      * and a Publisher is a RESPONDER.
380      *
381      * @param peerHandle The peer's handle obtained through
382      * {@link DiscoverySessionCallback#onServiceDiscovered(PeerHandle,
383      * byte[], java.util.List)} or
384      * {@link DiscoverySessionCallback#onMessageReceived(PeerHandle,
385      * byte[])}. On a RESPONDER this value is used to gate the acceptance of a connection request
386      *                   from only that peer. A RESPONDER may specify a null - indicating that
387      *                   it will accept connection requests from any device.
388      * @param pmk A PMK (pairwise master key, see IEEE 802.11i) specifying the key to use for
389      *            encrypting the data-path. Use the
390      *            {@link #createNetworkSpecifierPassphrase(PeerHandle, String)} to specify a
391      *            Passphrase or {@link #createNetworkSpecifierOpen(PeerHandle)} to specify an
392      *            open (unencrypted) link.
393      *
394      * @return A {@link NetworkSpecifier} to be used to construct
395      * {@link android.net.NetworkRequest.Builder#setNetworkSpecifier(NetworkSpecifier)} to pass to
396      * {@link android.net.ConnectivityManager#requestNetwork(android.net.NetworkRequest,
397      * android.net.ConnectivityManager.NetworkCallback)}
398      * [or other varieties of that API].
399      *
400      * @hide
401      */
402     @SystemApi
createNetworkSpecifierPmk(@ullable PeerHandle peerHandle, @NonNull byte[] pmk)403     public NetworkSpecifier createNetworkSpecifierPmk(@Nullable PeerHandle peerHandle,
404             @NonNull byte[] pmk) {
405         if (pmk == null || pmk.length == 0) {
406             throw new IllegalArgumentException("PMK must not be null or empty");
407         }
408 
409         if (mTerminated) {
410             Log.w(TAG, "createNetworkSpecifierPmk: called on terminated session");
411             return null;
412         }
413 
414         WifiAwareManager mgr = mMgr.get();
415         if (mgr == null) {
416             Log.w(TAG, "createNetworkSpecifierPmk: called post GC on WifiAwareManager");
417             return null;
418         }
419 
420         int role = this instanceof SubscribeDiscoverySession
421                 ? WifiAwareManager.WIFI_AWARE_DATA_PATH_ROLE_INITIATOR
422                 : WifiAwareManager.WIFI_AWARE_DATA_PATH_ROLE_RESPONDER;
423 
424         return mgr.createNetworkSpecifier(mClientId, role, mSessionId, peerHandle, pmk, null);
425     }
426 }
427