/*
 * Copyright (C) 2016 The Android Open Source Project
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package android.net.wifi.aware;

import android.annotation.NonNull;
import android.annotation.Nullable;
import android.annotation.SystemApi;
import android.net.NetworkSpecifier;
import android.util.Log;

import com.android.internal.annotations.VisibleForTesting;

import dalvik.system.CloseGuard;

import java.lang.ref.WeakReference;

A class representing a single publish or subscribe Aware session. This object will not be created directly - only its child classes are available: PublishDiscoverySession and SubscribeDiscoverySession. This class provides functionality common to both publish and subscribe discovery sessions: The close() method must be called to destroy discovery sessions once they are no longer needed.
/** * A class representing a single publish or subscribe Aware session. This object * will not be created directly - only its child classes are available: * {@link PublishDiscoverySession} and {@link SubscribeDiscoverySession}. This * class provides functionality common to both publish and subscribe discovery sessions: * <ul> * <li>Sending messages: {@link #sendMessage(PeerHandle, int, byte[])} method. * <li>Creating a network-specifier when requesting a Aware connection: * {@link #createNetworkSpecifierOpen(PeerHandle)} or * {@link #createNetworkSpecifierPassphrase(PeerHandle, String)}. * </ul> * The {@link #close()} method must be called to destroy discovery sessions once they are * no longer needed. */
public class DiscoverySession implements AutoCloseable { private static final String TAG = "DiscoverySession"; private static final boolean DBG = false; private static final boolean VDBG = false; // STOPSHIP if true private static final int MAX_SEND_RETRY_COUNT = 5;
@hide
/** @hide */
protected WeakReference<WifiAwareManager> mMgr;
@hide
/** @hide */
protected final int mClientId;
@hide
/** @hide */
protected final int mSessionId;
@hide
/** @hide */
protected boolean mTerminated = false; private final CloseGuard mCloseGuard = CloseGuard.get();
Return the maximum permitted retry count when sending messages using sendMessage(PeerHandle, int, byte[], int).
Returns:Maximum retry count when sending messages.
@hide
/** * Return the maximum permitted retry count when sending messages using * {@link #sendMessage(PeerHandle, int, byte[], int)}. * * @return Maximum retry count when sending messages. * * @hide */
public static int getMaxSendRetryCount() { return MAX_SEND_RETRY_COUNT; }
@hide
/** @hide */
public DiscoverySession(WifiAwareManager manager, int clientId, int sessionId) { if (VDBG) { Log.v(TAG, "New discovery session created: manager=" + manager + ", clientId=" + clientId + ", sessionId=" + sessionId); } mMgr = new WeakReference<>(manager); mClientId = clientId; mSessionId = sessionId; mCloseGuard.open("close"); }
Destroy the publish or subscribe session - free any resources, and stop transmitting packets on-air (for an active session) or listening for matches (for a passive session). The session may not be used for any additional operations after its destruction.

This operation must be done on a session which is no longer needed. Otherwise system resources will continue to be utilized until the application exits. The only exception is a session for which we received a termination callback, DiscoverySessionCallback.onSessionTerminated().

/** * Destroy the publish or subscribe session - free any resources, and stop * transmitting packets on-air (for an active session) or listening for * matches (for a passive session). The session may not be used for any * additional operations after its destruction. * <p> * This operation must be done on a session which is no longer needed. Otherwise system * resources will continue to be utilized until the application exits. The only * exception is a session for which we received a termination callback, * {@link DiscoverySessionCallback#onSessionTerminated()}. */
@Override public void close() { WifiAwareManager mgr = mMgr.get(); if (mgr == null) { Log.w(TAG, "destroy: called post GC on WifiAwareManager"); return; } mgr.terminateSession(mClientId, mSessionId); mTerminated = true; mMgr.clear(); mCloseGuard.close(); }
Sets the status of the session to terminated - i.e. an indication that already terminated rather than executing a termination.
@hide
/** * Sets the status of the session to terminated - i.e. an indication that * already terminated rather than executing a termination. * * @hide */
public void setTerminated() { if (mTerminated) { Log.w(TAG, "terminate: already terminated."); return; } mTerminated = true; mMgr.clear(); mCloseGuard.close(); }
@hide
/** @hide */
@Override protected void finalize() throws Throwable { try { if (mCloseGuard != null) { mCloseGuard.warnIfOpen(); } if (!mTerminated) { close(); } } finally { super.finalize(); } }
Access the client ID of the Aware session. Note: internal visibility for testing.
Returns:The internal client ID.
@hide
/** * Access the client ID of the Aware session. * * Note: internal visibility for testing. * * @return The internal client ID. * * @hide */
@VisibleForTesting public int getClientId() { return mClientId; }
Access the discovery session ID of the Aware session. Note: internal visibility for testing.
Returns:The internal discovery session ID.
@hide
/** * Access the discovery session ID of the Aware session. * * Note: internal visibility for testing. * * @return The internal discovery session ID. * * @hide */
@VisibleForTesting public int getSessionId() { return mSessionId; }
Sends a message to the specified destination. Aware messages are transmitted in the context of a discovery session - executed subsequent to a publish/subscribe DiscoverySessionCallback.onServiceDiscovered(PeerHandle, byte[], List<byte[]>) event.

Aware messages are not guaranteed delivery. Callbacks on DiscoverySessionCallback indicate message was transmitted successfully, DiscoverySessionCallback.onMessageSendSucceeded(int), or transmission failed (possibly after several retries) - DiscoverySessionCallback.onMessageSendFailed(int).

The peer will get a callback indicating a message was received using DiscoverySessionCallback.onMessageReceived(PeerHandle, byte[]).

Params:
  • peerHandle – The peer's handle for the message. Must be a result of an DiscoverySessionCallback.onServiceDiscovered(PeerHandle, byte[], List<byte[]>) or DiscoverySessionCallback.onMessageReceived(PeerHandle, byte[]) events.
  • messageId – An arbitrary integer used by the caller to identify the message. The same integer ID will be returned in the callbacks indicating message send success or failure. The messageId is not used internally by the Aware service - it can be arbitrary and non-unique.
  • message – The message to be transmitted.
  • retryCount – An integer specifying how many additional service-level (as opposed to PHY or MAC level) retries should be attempted if there is no ACK from the receiver (note: no retransmissions are attempted in other failure cases). A value of 0 indicates no retries. Max permitted value is getMaxSendRetryCount().
@hide
/** * Sends a message to the specified destination. Aware messages are transmitted in the context * of a discovery session - executed subsequent to a publish/subscribe * {@link DiscoverySessionCallback#onServiceDiscovered(PeerHandle, * byte[], java.util.List)} event. * <p> * Aware messages are not guaranteed delivery. Callbacks on * {@link DiscoverySessionCallback} indicate message was transmitted successfully, * {@link DiscoverySessionCallback#onMessageSendSucceeded(int)}, or transmission * failed (possibly after several retries) - * {@link DiscoverySessionCallback#onMessageSendFailed(int)}. * <p> * The peer will get a callback indicating a message was received using * {@link DiscoverySessionCallback#onMessageReceived(PeerHandle, * byte[])}. * * @param peerHandle The peer's handle for the message. Must be a result of an * {@link DiscoverySessionCallback#onServiceDiscovered(PeerHandle, * byte[], java.util.List)} or * {@link DiscoverySessionCallback#onMessageReceived(PeerHandle, * byte[])} events. * @param messageId An arbitrary integer used by the caller to identify the message. The same * integer ID will be returned in the callbacks indicating message send success or * failure. The {@code messageId} is not used internally by the Aware service - it * can be arbitrary and non-unique. * @param message The message to be transmitted. * @param retryCount An integer specifying how many additional service-level (as opposed to PHY * or MAC level) retries should be attempted if there is no ACK from the receiver * (note: no retransmissions are attempted in other failure cases). A value of 0 * indicates no retries. Max permitted value is {@link #getMaxSendRetryCount()}. * * @hide */
public void sendMessage(@NonNull PeerHandle peerHandle, int messageId, @Nullable byte[] message, int retryCount) { if (mTerminated) { Log.w(TAG, "sendMessage: called on terminated session"); return; } WifiAwareManager mgr = mMgr.get(); if (mgr == null) { Log.w(TAG, "sendMessage: called post GC on WifiAwareManager"); return; } mgr.sendMessage(mClientId, mSessionId, peerHandle, message, messageId, retryCount); }
Sends a message to the specified destination. Aware messages are transmitted in the context of a discovery session - executed subsequent to a publish/subscribe DiscoverySessionCallback.onServiceDiscovered(PeerHandle, byte[], List<byte[]>) event.

Aware messages are not guaranteed delivery. Callbacks on DiscoverySessionCallback indicate message was transmitted successfully, DiscoverySessionCallback.onMessageSendSucceeded(int), or transmission failed (possibly after several retries) - DiscoverySessionCallback.onMessageSendFailed(int).

The peer will get a callback indicating a message was received using DiscoverySessionCallback.onMessageReceived(PeerHandle, byte[]).

Params:
/** * Sends a message to the specified destination. Aware messages are transmitted in the context * of a discovery session - executed subsequent to a publish/subscribe * {@link DiscoverySessionCallback#onServiceDiscovered(PeerHandle, * byte[], java.util.List)} event. * <p> * Aware messages are not guaranteed delivery. Callbacks on * {@link DiscoverySessionCallback} indicate message was transmitted successfully, * {@link DiscoverySessionCallback#onMessageSendSucceeded(int)}, or transmission * failed (possibly after several retries) - * {@link DiscoverySessionCallback#onMessageSendFailed(int)}. * <p> * The peer will get a callback indicating a message was received using * {@link DiscoverySessionCallback#onMessageReceived(PeerHandle, * byte[])}. * * @param peerHandle The peer's handle for the message. Must be a result of an * {@link DiscoverySessionCallback#onServiceDiscovered(PeerHandle, * byte[], java.util.List)} or * {@link DiscoverySessionCallback#onMessageReceived(PeerHandle, * byte[])} events. * @param messageId An arbitrary integer used by the caller to identify the message. The same * integer ID will be returned in the callbacks indicating message send success or * failure. The {@code messageId} is not used internally by the Aware service - it * can be arbitrary and non-unique. * @param message The message to be transmitted. */
public void sendMessage(@NonNull PeerHandle peerHandle, int messageId, @Nullable byte[] message) { sendMessage(peerHandle, messageId, message, 0); }
Create a Builder.setNetworkSpecifier(NetworkSpecifier) for an unencrypted WiFi Aware connection (link) to the specified peer. The Builder.addTransportType(int) should be set to NetworkCapabilities.TRANSPORT_WIFI_AWARE.

This method should be used when setting up a connection with a peer discovered through Aware discovery or communication (in such scenarios the MAC address of the peer is shielded by an opaque peer ID handle). If an Aware connection is needed to a peer discovered using other OOB (out-of-band) mechanism then use the alternative WifiAwareSession.createNetworkSpecifierOpen(int, byte[]) method - which uses the peer's MAC address.

Note: per the Wi-Fi Aware specification the roles are fixed - a Subscriber is an INITIATOR and a Publisher is a RESPONDER.

To set up an encrypted link use the createNetworkSpecifierPassphrase(PeerHandle, String) API.

Params:
Returns:A NetworkSpecifier to be used to construct Builder.setNetworkSpecifier(NetworkSpecifier) to pass to ConnectivityManager.requestNetwork(NetworkRequest, NetworkCallback) [or other varieties of that API].
/** * Create a {@link android.net.NetworkRequest.Builder#setNetworkSpecifier(NetworkSpecifier)} for * an unencrypted WiFi Aware connection (link) to the specified peer. The * {@link android.net.NetworkRequest.Builder#addTransportType(int)} should be set to * {@link android.net.NetworkCapabilities#TRANSPORT_WIFI_AWARE}. * <p> * This method should be used when setting up a connection with a peer discovered through Aware * discovery or communication (in such scenarios the MAC address of the peer is shielded by * an opaque peer ID handle). If an Aware connection is needed to a peer discovered using other * OOB (out-of-band) mechanism then use the alternative * {@link WifiAwareSession#createNetworkSpecifierOpen(int, byte[])} method - which uses the * peer's MAC address. * <p> * Note: per the Wi-Fi Aware specification the roles are fixed - a Subscriber is an INITIATOR * and a Publisher is a RESPONDER. * <p> * To set up an encrypted link use the * {@link #createNetworkSpecifierPassphrase(PeerHandle, String)} API. * * @param peerHandle The peer's handle obtained through * {@link DiscoverySessionCallback#onServiceDiscovered(PeerHandle, byte[], java.util.List)} * or * {@link DiscoverySessionCallback#onMessageReceived(PeerHandle, byte[])}. * On a RESPONDER this value is used to gate the acceptance of a connection * request from only that peer. * * @return A {@link NetworkSpecifier} to be used to construct * {@link android.net.NetworkRequest.Builder#setNetworkSpecifier(NetworkSpecifier)} to pass to * {@link android.net.ConnectivityManager#requestNetwork(android.net.NetworkRequest, * android.net.ConnectivityManager.NetworkCallback)} * [or other varieties of that API]. */
public NetworkSpecifier createNetworkSpecifierOpen(@NonNull PeerHandle peerHandle) { if (mTerminated) { Log.w(TAG, "createNetworkSpecifierOpen: called on terminated session"); return null; } WifiAwareManager mgr = mMgr.get(); if (mgr == null) { Log.w(TAG, "createNetworkSpecifierOpen: called post GC on WifiAwareManager"); return null; } int role = this instanceof SubscribeDiscoverySession ? WifiAwareManager.WIFI_AWARE_DATA_PATH_ROLE_INITIATOR : WifiAwareManager.WIFI_AWARE_DATA_PATH_ROLE_RESPONDER; return mgr.createNetworkSpecifier(mClientId, role, mSessionId, peerHandle, null, null); }
Create a Builder.setNetworkSpecifier(NetworkSpecifier) for an encrypted WiFi Aware connection (link) to the specified peer. The Builder.addTransportType(int) should be set to NetworkCapabilities.TRANSPORT_WIFI_AWARE.

This method should be used when setting up a connection with a peer discovered through Aware discovery or communication (in such scenarios the MAC address of the peer is shielded by an opaque peer ID handle). If an Aware connection is needed to a peer discovered using other OOB (out-of-band) mechanism then use the alternative WifiAwareSession.createNetworkSpecifierPassphrase(int, byte[], String) method - which uses the peer's MAC address.

Note: per the Wi-Fi Aware specification the roles are fixed - a Subscriber is an INITIATOR and a Publisher is a RESPONDER.

Params:
Returns:A NetworkSpecifier to be used to construct Builder.setNetworkSpecifier(NetworkSpecifier) to pass to ConnectivityManager.requestNetwork(NetworkRequest, NetworkCallback) [or other varieties of that API].
/** * Create a {@link android.net.NetworkRequest.Builder#setNetworkSpecifier(NetworkSpecifier)} for * an encrypted WiFi Aware connection (link) to the specified peer. The * {@link android.net.NetworkRequest.Builder#addTransportType(int)} should be set to * {@link android.net.NetworkCapabilities#TRANSPORT_WIFI_AWARE}. * <p> * This method should be used when setting up a connection with a peer discovered through Aware * discovery or communication (in such scenarios the MAC address of the peer is shielded by * an opaque peer ID handle). If an Aware connection is needed to a peer discovered using other * OOB (out-of-band) mechanism then use the alternative * {@link WifiAwareSession#createNetworkSpecifierPassphrase(int, byte[], String)} method - * which uses the peer's MAC address. * <p> * Note: per the Wi-Fi Aware specification the roles are fixed - a Subscriber is an INITIATOR * and a Publisher is a RESPONDER. * * @param peerHandle The peer's handle obtained through * {@link DiscoverySessionCallback#onServiceDiscovered(PeerHandle, * byte[], java.util.List)} or * {@link DiscoverySessionCallback#onMessageReceived(PeerHandle, * byte[])}. On a RESPONDER this value is used to gate the acceptance of a connection request * from only that peer. * @param passphrase The passphrase to be used to encrypt the link. The PMK is generated from * the passphrase. Use the * {@link #createNetworkSpecifierOpen(PeerHandle)} API to * specify an open (unencrypted) link. * * @return A {@link NetworkSpecifier} to be used to construct * {@link android.net.NetworkRequest.Builder#setNetworkSpecifier(NetworkSpecifier)} to pass to * {@link android.net.ConnectivityManager#requestNetwork(android.net.NetworkRequest, * android.net.ConnectivityManager.NetworkCallback)} * [or other varieties of that API]. */
public NetworkSpecifier createNetworkSpecifierPassphrase( @NonNull PeerHandle peerHandle, @NonNull String passphrase) { if (!WifiAwareUtils.validatePassphrase(passphrase)) { throw new IllegalArgumentException("Passphrase must meet length requirements"); } if (mTerminated) { Log.w(TAG, "createNetworkSpecifierPassphrase: called on terminated session"); return null; } WifiAwareManager mgr = mMgr.get(); if (mgr == null) { Log.w(TAG, "createNetworkSpecifierPassphrase: called post GC on WifiAwareManager"); return null; } int role = this instanceof SubscribeDiscoverySession ? WifiAwareManager.WIFI_AWARE_DATA_PATH_ROLE_INITIATOR : WifiAwareManager.WIFI_AWARE_DATA_PATH_ROLE_RESPONDER; return mgr.createNetworkSpecifier(mClientId, role, mSessionId, peerHandle, null, passphrase); }
Create a Builder.setNetworkSpecifier(NetworkSpecifier) for an encrypted WiFi Aware connection (link) to the specified peer. The Builder.addTransportType(int) should be set to NetworkCapabilities.TRANSPORT_WIFI_AWARE.

This method should be used when setting up a connection with a peer discovered through Aware discovery or communication (in such scenarios the MAC address of the peer is shielded by an opaque peer ID handle). If an Aware connection is needed to a peer discovered using other OOB (out-of-band) mechanism then use the alternative WifiAwareSession.createNetworkSpecifierPmk(int, byte[], byte[]) method - which uses the peer's MAC address.

Note: per the Wi-Fi Aware specification the roles are fixed - a Subscriber is an INITIATOR and a Publisher is a RESPONDER.

Params:
Returns:A NetworkSpecifier to be used to construct Builder.setNetworkSpecifier(NetworkSpecifier) to pass to ConnectivityManager.requestNetwork(NetworkRequest, NetworkCallback) [or other varieties of that API].
@hide
/** * Create a {@link android.net.NetworkRequest.Builder#setNetworkSpecifier(NetworkSpecifier)} for * an encrypted WiFi Aware connection (link) to the specified peer. The * {@link android.net.NetworkRequest.Builder#addTransportType(int)} should be set to * {@link android.net.NetworkCapabilities#TRANSPORT_WIFI_AWARE}. * <p> * This method should be used when setting up a connection with a peer discovered through Aware * discovery or communication (in such scenarios the MAC address of the peer is shielded by * an opaque peer ID handle). If an Aware connection is needed to a peer discovered using other * OOB (out-of-band) mechanism then use the alternative * {@link WifiAwareSession#createNetworkSpecifierPmk(int, byte[], byte[])} method - which uses * the peer's MAC address. * <p> * Note: per the Wi-Fi Aware specification the roles are fixed - a Subscriber is an INITIATOR * and a Publisher is a RESPONDER. * * @param peerHandle The peer's handle obtained through * {@link DiscoverySessionCallback#onServiceDiscovered(PeerHandle, * byte[], java.util.List)} or * {@link DiscoverySessionCallback#onMessageReceived(PeerHandle, * byte[])}. On a RESPONDER this value is used to gate the acceptance of a connection request * from only that peer. * @param pmk A PMK (pairwise master key, see IEEE 802.11i) specifying the key to use for * encrypting the data-path. Use the * {@link #createNetworkSpecifierPassphrase(PeerHandle, String)} to specify a * Passphrase or {@link #createNetworkSpecifierOpen(PeerHandle)} to specify an * open (unencrypted) link. * * @return A {@link NetworkSpecifier} to be used to construct * {@link android.net.NetworkRequest.Builder#setNetworkSpecifier(NetworkSpecifier)} to pass to * {@link android.net.ConnectivityManager#requestNetwork(android.net.NetworkRequest, * android.net.ConnectivityManager.NetworkCallback)} * [or other varieties of that API]. * * @hide */
@SystemApi public NetworkSpecifier createNetworkSpecifierPmk(@NonNull PeerHandle peerHandle, @NonNull byte[] pmk) { if (!WifiAwareUtils.validatePmk(pmk)) { throw new IllegalArgumentException("PMK must 32 bytes"); } if (mTerminated) { Log.w(TAG, "createNetworkSpecifierPmk: called on terminated session"); return null; } WifiAwareManager mgr = mMgr.get(); if (mgr == null) { Log.w(TAG, "createNetworkSpecifierPmk: called post GC on WifiAwareManager"); return null; } int role = this instanceof SubscribeDiscoverySession ? WifiAwareManager.WIFI_AWARE_DATA_PATH_ROLE_INITIATOR : WifiAwareManager.WIFI_AWARE_DATA_PATH_ROLE_RESPONDER; return mgr.createNetworkSpecifier(mClientId, role, mSessionId, peerHandle, pmk, null); } }