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 */ 70 public static int getMaxSendRetryCount() { 71 return MAX_SEND_RETRY_COUNT; 72 } 73 74 /** @hide */ 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 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 */ 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 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 */ 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 */ 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 */ 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 */ 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 */ 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 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