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