Home | History | Annotate | Download | only in aware
      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