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.Log; 24 25 import com.android.internal.annotations.VisibleForTesting; 26 27 import dalvik.system.CloseGuard; 28 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: 39 * {@link #createNetworkSpecifierOpen(PeerHandle)} or 40 * {@link #createNetworkSpecifierPassphrase(PeerHandle, String)}. 41 * </ul> 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 = CloseGuard.get(); 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 */ 71 public static int getMaxSendRetryCount() { 72 return MAX_SEND_RETRY_COUNT; 73 } 74 75 /** @hide */ 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 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 } 112 113 /** 114 * Sets the status of the session to terminated - i.e. an indication that 115 * already terminated rather than executing a termination. 116 * 117 * @hide 118 */ 119 public void setTerminated() { 120 if (mTerminated) { 121 Log.w(TAG, "terminate: already terminated."); 122 return; 123 } 124 125 mTerminated = true; 126 mMgr.clear(); 127 mCloseGuard.close(); 128 } 129 130 /** @hide */ 131 @Override 132 protected void finalize() throws Throwable { 133 try { 134 if (mCloseGuard != null) { 135 mCloseGuard.warnIfOpen(); 136 } 137 138 if (!mTerminated) { 139 close(); 140 } 141 } finally { 142 super.finalize(); 143 } 144 } 145 146 /** 147 * Access the client ID of the Aware session. 148 * 149 * Note: internal visibility for testing. 150 * 151 * @return The internal client ID. 152 * 153 * @hide 154 */ 155 @VisibleForTesting 156 public int getClientId() { 157 return mClientId; 158 } 159 160 /** 161 * Access the discovery session ID of the Aware session. 162 * 163 * Note: internal visibility for testing. 164 * 165 * @return The internal discovery session ID. 166 * 167 * @hide 168 */ 169 @VisibleForTesting 170 public int getSessionId() { 171 return mSessionId; 172 } 173 174 /** 175 * Sends a message to the specified destination. Aware messages are transmitted in the context 176 * of a discovery session - executed subsequent to a publish/subscribe 177 * {@link DiscoverySessionCallback#onServiceDiscovered(PeerHandle, 178 * byte[], java.util.List)} event. 179 * <p> 180 * Aware messages are not guaranteed delivery. Callbacks on 181 * {@link DiscoverySessionCallback} indicate message was transmitted successfully, 182 * {@link DiscoverySessionCallback#onMessageSendSucceeded(int)}, or transmission 183 * failed (possibly after several retries) - 184 * {@link DiscoverySessionCallback#onMessageSendFailed(int)}. 185 * <p> 186 * The peer will get a callback indicating a message was received using 187 * {@link DiscoverySessionCallback#onMessageReceived(PeerHandle, 188 * byte[])}. 189 * 190 * @param peerHandle The peer's handle for the message. Must be a result of an 191 * {@link DiscoverySessionCallback#onServiceDiscovered(PeerHandle, 192 * byte[], java.util.List)} or 193 * {@link DiscoverySessionCallback#onMessageReceived(PeerHandle, 194 * byte[])} events. 195 * @param messageId An arbitrary integer used by the caller to identify the message. The same 196 * integer ID will be returned in the callbacks indicating message send success or 197 * failure. The {@code messageId} is not used internally by the Aware service - it 198 * can be arbitrary and non-unique. 199 * @param message The message to be transmitted. 200 * @param retryCount An integer specifying how many additional service-level (as opposed to PHY 201 * or MAC level) retries should be attempted if there is no ACK from the receiver 202 * (note: no retransmissions are attempted in other failure cases). A value of 0 203 * indicates no retries. Max permitted value is {@link #getMaxSendRetryCount()}. 204 * 205 * @hide 206 */ 207 public void sendMessage(@NonNull PeerHandle peerHandle, int messageId, 208 @Nullable byte[] message, int retryCount) { 209 if (mTerminated) { 210 Log.w(TAG, "sendMessage: called on terminated session"); 211 return; 212 } 213 214 WifiAwareManager mgr = mMgr.get(); 215 if (mgr == null) { 216 Log.w(TAG, "sendMessage: called post GC on WifiAwareManager"); 217 return; 218 } 219 220 mgr.sendMessage(mClientId, mSessionId, peerHandle, message, messageId, retryCount); 221 } 222 223 /** 224 * Sends a message to the specified destination. Aware messages are transmitted in the context 225 * of a discovery session - executed subsequent to a publish/subscribe 226 * {@link DiscoverySessionCallback#onServiceDiscovered(PeerHandle, 227 * byte[], java.util.List)} event. 228 * <p> 229 * Aware messages are not guaranteed delivery. Callbacks on 230 * {@link DiscoverySessionCallback} indicate message was transmitted successfully, 231 * {@link DiscoverySessionCallback#onMessageSendSucceeded(int)}, or transmission 232 * failed (possibly after several retries) - 233 * {@link DiscoverySessionCallback#onMessageSendFailed(int)}. 234 * <p> 235 * The peer will get a callback indicating a message was received using 236 * {@link DiscoverySessionCallback#onMessageReceived(PeerHandle, 237 * byte[])}. 238 * 239 * @param peerHandle The peer's handle for the message. Must be a result of an 240 * {@link DiscoverySessionCallback#onServiceDiscovered(PeerHandle, 241 * byte[], java.util.List)} or 242 * {@link DiscoverySessionCallback#onMessageReceived(PeerHandle, 243 * byte[])} events. 244 * @param messageId An arbitrary integer used by the caller to identify the message. The same 245 * integer ID will be returned in the callbacks indicating message send success or 246 * failure. The {@code messageId} is not used internally by the Aware service - it 247 * can be arbitrary and non-unique. 248 * @param message The message to be transmitted. 249 */ 250 public void sendMessage(@NonNull PeerHandle peerHandle, int messageId, 251 @Nullable byte[] message) { 252 sendMessage(peerHandle, messageId, message, 0); 253 } 254 255 /** 256 * Create a {@link android.net.NetworkRequest.Builder#setNetworkSpecifier(NetworkSpecifier)} for 257 * an unencrypted WiFi Aware connection (link) to the specified peer. The 258 * {@link android.net.NetworkRequest.Builder#addTransportType(int)} should be set to 259 * {@link android.net.NetworkCapabilities#TRANSPORT_WIFI_AWARE}. 260 * <p> 261 * This method should be used when setting up a connection with a peer discovered through Aware 262 * discovery or communication (in such scenarios the MAC address of the peer is shielded by 263 * an opaque peer ID handle). If an Aware connection is needed to a peer discovered using other 264 * OOB (out-of-band) mechanism then use the alternative 265 * {@link WifiAwareSession#createNetworkSpecifierOpen(int, byte[])} method - which uses the 266 * peer's MAC address. 267 * <p> 268 * Note: per the Wi-Fi Aware specification the roles are fixed - a Subscriber is an INITIATOR 269 * and a Publisher is a RESPONDER. 270 * <p> 271 * To set up an encrypted link use the 272 * {@link #createNetworkSpecifierPassphrase(PeerHandle, String)} API. 273 * 274 * @param peerHandle The peer's handle obtained through 275 * {@link DiscoverySessionCallback#onServiceDiscovered(PeerHandle, byte[], java.util.List)} 276 * or 277 * {@link DiscoverySessionCallback#onMessageReceived(PeerHandle, byte[])}. 278 * On a RESPONDER this value is used to gate the acceptance of a connection 279 * request from only that peer. 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(@NonNull 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. 328 * @param passphrase The passphrase to be used to encrypt the link. The PMK is generated from 329 * the passphrase. Use the 330 * {@link #createNetworkSpecifierOpen(PeerHandle)} API to 331 * specify an open (unencrypted) link. 332 * 333 * @return A {@link NetworkSpecifier} to be used to construct 334 * {@link android.net.NetworkRequest.Builder#setNetworkSpecifier(NetworkSpecifier)} to pass to 335 * {@link android.net.ConnectivityManager#requestNetwork(android.net.NetworkRequest, 336 * android.net.ConnectivityManager.NetworkCallback)} 337 * [or other varieties of that API]. 338 */ 339 public NetworkSpecifier createNetworkSpecifierPassphrase( 340 @NonNull PeerHandle peerHandle, @NonNull String passphrase) { 341 if (!WifiAwareUtils.validatePassphrase(passphrase)) { 342 throw new IllegalArgumentException("Passphrase must meet length requirements"); 343 } 344 345 if (mTerminated) { 346 Log.w(TAG, "createNetworkSpecifierPassphrase: called on terminated session"); 347 return null; 348 } 349 350 WifiAwareManager mgr = mMgr.get(); 351 if (mgr == null) { 352 Log.w(TAG, "createNetworkSpecifierPassphrase: called post GC on WifiAwareManager"); 353 return null; 354 } 355 356 int role = this instanceof SubscribeDiscoverySession 357 ? WifiAwareManager.WIFI_AWARE_DATA_PATH_ROLE_INITIATOR 358 : WifiAwareManager.WIFI_AWARE_DATA_PATH_ROLE_RESPONDER; 359 360 return mgr.createNetworkSpecifier(mClientId, role, mSessionId, peerHandle, null, 361 passphrase); 362 } 363 364 /** 365 * Create a {@link android.net.NetworkRequest.Builder#setNetworkSpecifier(NetworkSpecifier)} for 366 * an encrypted WiFi Aware connection (link) to the specified peer. The 367 * {@link android.net.NetworkRequest.Builder#addTransportType(int)} should be set to 368 * {@link android.net.NetworkCapabilities#TRANSPORT_WIFI_AWARE}. 369 * <p> 370 * This method should be used when setting up a connection with a peer discovered through Aware 371 * discovery or communication (in such scenarios the MAC address of the peer is shielded by 372 * an opaque peer ID handle). If an Aware connection is needed to a peer discovered using other 373 * OOB (out-of-band) mechanism then use the alternative 374 * {@link WifiAwareSession#createNetworkSpecifierPmk(int, byte[], byte[])} method - which uses 375 * the peer's MAC address. 376 * <p> 377 * Note: per the Wi-Fi Aware specification the roles are fixed - a Subscriber is an INITIATOR 378 * and a Publisher is a RESPONDER. 379 * 380 * @param peerHandle The peer's handle obtained through 381 * {@link DiscoverySessionCallback#onServiceDiscovered(PeerHandle, 382 * byte[], java.util.List)} or 383 * {@link DiscoverySessionCallback#onMessageReceived(PeerHandle, 384 * byte[])}. On a RESPONDER this value is used to gate the acceptance of a connection request 385 * from only that peer. 386 * @param pmk A PMK (pairwise master key, see IEEE 802.11i) specifying the key to use for 387 * encrypting the data-path. Use the 388 * {@link #createNetworkSpecifierPassphrase(PeerHandle, String)} to specify a 389 * Passphrase or {@link #createNetworkSpecifierOpen(PeerHandle)} to specify an 390 * open (unencrypted) link. 391 * 392 * @return A {@link NetworkSpecifier} to be used to construct 393 * {@link android.net.NetworkRequest.Builder#setNetworkSpecifier(NetworkSpecifier)} to pass to 394 * {@link android.net.ConnectivityManager#requestNetwork(android.net.NetworkRequest, 395 * android.net.ConnectivityManager.NetworkCallback)} 396 * [or other varieties of that API]. 397 * 398 * @hide 399 */ 400 @SystemApi 401 public NetworkSpecifier createNetworkSpecifierPmk(@NonNull PeerHandle peerHandle, 402 @NonNull byte[] pmk) { 403 if (!WifiAwareUtils.validatePmk(pmk)) { 404 throw new IllegalArgumentException("PMK must 32 bytes"); 405 } 406 407 if (mTerminated) { 408 Log.w(TAG, "createNetworkSpecifierPmk: called on terminated session"); 409 return null; 410 } 411 412 WifiAwareManager mgr = mMgr.get(); 413 if (mgr == null) { 414 Log.w(TAG, "createNetworkSpecifierPmk: called post GC on WifiAwareManager"); 415 return null; 416 } 417 418 int role = this instanceof SubscribeDiscoverySession 419 ? WifiAwareManager.WIFI_AWARE_DATA_PATH_ROLE_INITIATOR 420 : WifiAwareManager.WIFI_AWARE_DATA_PATH_ROLE_RESPONDER; 421 422 return mgr.createNetworkSpecifier(mClientId, role, mSessionId, peerHandle, pmk, null); 423 } 424 } 425