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