blob: 4d92ae174e6d03d0c0c4e3b87cd6b7acc15be582 [file] [log] [blame]
/*
* 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.CloseGuard;
import android.util.Log;
import com.android.internal.annotations.VisibleForTesting;
import java.lang.ref.Reference;
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:
* {@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 using
* {@link WifiAwareNetworkSpecifier.Builder}.
* </ul>
* <p>
* 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 */
protected WeakReference<WifiAwareManager> mMgr;
/** @hide */
protected final int mClientId;
/** @hide */
protected final int mSessionId;
/** @hide */
protected boolean mTerminated = false;
private final CloseGuard mCloseGuard = new CloseGuard();
/**
* 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 */
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.
* <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();
Reference.reachabilityFence(this);
}
/**
* 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 */
@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.
*
* @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.
*
* @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
* {@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
* {@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 {@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.
* @deprecated Use the replacement {@link WifiAwareNetworkSpecifier.Builder}.
*
* @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].
*/
@Deprecated
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 {@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.
* @deprecated Use the replacement {@link WifiAwareNetworkSpecifier.Builder}.
*
* @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].
*/
@Deprecated
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 {@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.
* @deprecated Use the replacement {@link WifiAwareNetworkSpecifier.Builder}.
*
* @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
*/
@Deprecated
@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);
}
}