| /* |
| * 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.os.Binder; |
| import android.os.Handler; |
| import android.os.Looper; |
| import android.util.CloseGuard; |
| import android.util.Log; |
| |
| import com.android.internal.annotations.VisibleForTesting; |
| |
| import java.lang.ref.Reference; |
| import java.lang.ref.WeakReference; |
| |
| /** |
| * This class represents a Wi-Fi Aware session - an attachment to the Wi-Fi Aware service through |
| * which the app can execute discovery operations. |
| */ |
| public class WifiAwareSession implements AutoCloseable { |
| private static final String TAG = "WifiAwareSession"; |
| private static final boolean DBG = false; |
| private static final boolean VDBG = false; // STOPSHIP if true |
| |
| private final WeakReference<WifiAwareManager> mMgr; |
| private final Binder mBinder; |
| private final int mClientId; |
| |
| private boolean mTerminated = true; |
| private final CloseGuard mCloseGuard = new CloseGuard(); |
| |
| /** @hide */ |
| public WifiAwareSession(WifiAwareManager manager, Binder binder, int clientId) { |
| if (VDBG) Log.v(TAG, "New session created: manager=" + manager + ", clientId=" + clientId); |
| |
| mMgr = new WeakReference<>(manager); |
| mBinder = binder; |
| mClientId = clientId; |
| mTerminated = false; |
| |
| mCloseGuard.open("close"); |
| } |
| |
| /** |
| * Destroy the Wi-Fi Aware service session and, if no other applications are attached to Aware, |
| * also disable Aware. This method destroys all outstanding operations - i.e. all publish and |
| * subscribes are terminated, and any outstanding data-links are shut-down. However, it is |
| * good practice to destroy these discovery sessions and connections explicitly before a |
| * session-wide destroy. |
| * <p> |
| * An application may re-attach after a destroy using |
| * {@link WifiAwareManager#attach(AttachCallback, Handler)} . |
| */ |
| @Override |
| public void close() { |
| WifiAwareManager mgr = mMgr.get(); |
| if (mgr == null) { |
| Log.w(TAG, "destroy: called post GC on WifiAwareManager"); |
| return; |
| } |
| mgr.disconnect(mClientId, mBinder); |
| mTerminated = true; |
| mMgr.clear(); |
| mCloseGuard.close(); |
| Reference.reachabilityFence(this); |
| } |
| |
| /** @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; |
| } |
| |
| /** |
| * Issue a request to the Aware service to create a new Aware publish discovery session, using |
| * the specified {@code publishConfig} configuration. The results of the publish operation |
| * are routed to the callbacks of {@link DiscoverySessionCallback}: |
| * <ul> |
| * <li> |
| * {@link DiscoverySessionCallback#onPublishStarted( |
| *PublishDiscoverySession)} |
| * is called when the publish session is created and provides a handle to the session. |
| * Further operations on the publish session can be executed on that object. |
| * <li>{@link DiscoverySessionCallback#onSessionConfigFailed()} is called if the |
| * publish operation failed. |
| * </ul> |
| * <p> |
| * Other results of the publish session operations will also be routed to callbacks |
| * on the {@code callback} object. The resulting publish session can be modified using |
| * {@link PublishDiscoverySession#updatePublish(PublishConfig)}. |
| * <p> |
| * An application must use the {@link DiscoverySession#close()} to |
| * terminate the publish discovery session once it isn't needed. This will free |
| * resources as well terminate any on-air transmissions. |
| * <p>The application must have the {@link android.Manifest.permission#ACCESS_FINE_LOCATION} |
| * permission to start a publish discovery session. |
| * |
| * @param publishConfig The {@link PublishConfig} specifying the |
| * configuration of the requested publish session. |
| * @param callback A {@link DiscoverySessionCallback} derived object to be used for |
| * session event callbacks. |
| * @param handler The Handler on whose thread to execute the callbacks of the {@code |
| * callback} object. If a null is provided then the application's main thread will be used. |
| */ |
| public void publish(@NonNull PublishConfig publishConfig, |
| @NonNull DiscoverySessionCallback callback, @Nullable Handler handler) { |
| WifiAwareManager mgr = mMgr.get(); |
| if (mgr == null) { |
| Log.e(TAG, "publish: called post GC on WifiAwareManager"); |
| return; |
| } |
| if (mTerminated) { |
| Log.e(TAG, "publish: called after termination"); |
| return; |
| } |
| mgr.publish(mClientId, (handler == null) ? Looper.getMainLooper() : handler.getLooper(), |
| publishConfig, callback); |
| } |
| |
| /** |
| * Issue a request to the Aware service to create a new Aware subscribe discovery session, using |
| * the specified {@code subscribeConfig} configuration. The results of the subscribe |
| * operation are routed to the callbacks of {@link DiscoverySessionCallback}: |
| * <ul> |
| * <li> |
| * {@link DiscoverySessionCallback#onSubscribeStarted( |
| *SubscribeDiscoverySession)} |
| * is called when the subscribe session is created and provides a handle to the session. |
| * Further operations on the subscribe session can be executed on that object. |
| * <li>{@link DiscoverySessionCallback#onSessionConfigFailed()} is called if the |
| * subscribe operation failed. |
| * </ul> |
| * <p> |
| * Other results of the subscribe session operations will also be routed to callbacks |
| * on the {@code callback} object. The resulting subscribe session can be modified using |
| * {@link SubscribeDiscoverySession#updateSubscribe(SubscribeConfig)}. |
| * <p> |
| * An application must use the {@link DiscoverySession#close()} to |
| * terminate the subscribe discovery session once it isn't needed. This will free |
| * resources as well terminate any on-air transmissions. |
| * <p>The application must have the {@link android.Manifest.permission#ACCESS_FINE_LOCATION} |
| * permission to start a subscribe discovery session. |
| * |
| * @param subscribeConfig The {@link SubscribeConfig} specifying the |
| * configuration of the requested subscribe session. |
| * @param callback A {@link DiscoverySessionCallback} derived object to be used for |
| * session event callbacks. |
| * @param handler The Handler on whose thread to execute the callbacks of the {@code |
| * callback} object. If a null is provided then the application's main thread will be used. |
| */ |
| public void subscribe(@NonNull SubscribeConfig subscribeConfig, |
| @NonNull DiscoverySessionCallback callback, @Nullable Handler handler) { |
| WifiAwareManager mgr = mMgr.get(); |
| if (mgr == null) { |
| Log.e(TAG, "publish: called post GC on WifiAwareManager"); |
| return; |
| } |
| if (mTerminated) { |
| Log.e(TAG, "publish: called after termination"); |
| return; |
| } |
| mgr.subscribe(mClientId, (handler == null) ? Looper.getMainLooper() : handler.getLooper(), |
| subscribeConfig, callback); |
| } |
| |
| /** |
| * 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 API is targeted for applications which can obtain the peer MAC address using OOB |
| * (out-of-band) discovery. Aware discovery does not provide the MAC address of the peer - |
| * when using Aware discovery use the alternative network specifier method - |
| * {@link android.net.wifi.aware.WifiAwareNetworkSpecifier.Builder}. |
| * <p> |
| * To set up an encrypted link use the |
| * {@link #createNetworkSpecifierPassphrase(int, byte[], String)} API. |
| * |
| * @param role The role of this device: |
| * {@link WifiAwareManager#WIFI_AWARE_DATA_PATH_ROLE_INITIATOR} or |
| * {@link WifiAwareManager#WIFI_AWARE_DATA_PATH_ROLE_RESPONDER} |
| * @param peer The MAC address of the peer's Aware discovery interface. 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( |
| @WifiAwareManager.DataPathRole int role, @NonNull byte[] peer) { |
| WifiAwareManager mgr = mMgr.get(); |
| if (mgr == null) { |
| Log.e(TAG, "createNetworkSpecifierOpen: called post GC on WifiAwareManager"); |
| return null; |
| } |
| if (mTerminated) { |
| Log.e(TAG, "createNetworkSpecifierOpen: called after termination"); |
| return null; |
| } |
| return mgr.createNetworkSpecifier(mClientId, role, peer, 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 API is targeted for applications which can obtain the peer MAC address using OOB |
| * (out-of-band) discovery. Aware discovery does not provide the MAC address of the peer - |
| * when using Aware discovery use the alternative network specifier method - |
| * {@link android.net.wifi.aware.WifiAwareNetworkSpecifier.Builder}. |
| * |
| * @param role The role of this device: |
| * {@link WifiAwareManager#WIFI_AWARE_DATA_PATH_ROLE_INITIATOR} or |
| * {@link WifiAwareManager#WIFI_AWARE_DATA_PATH_ROLE_RESPONDER} |
| * @param peer The MAC address of the peer's Aware discovery interface. 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 {@link #createNetworkSpecifierOpen(int, byte[])} 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( |
| @WifiAwareManager.DataPathRole int role, @NonNull byte[] peer, |
| @NonNull String passphrase) { |
| WifiAwareManager mgr = mMgr.get(); |
| if (mgr == null) { |
| Log.e(TAG, "createNetworkSpecifierPassphrase: called post GC on WifiAwareManager"); |
| return null; |
| } |
| if (mTerminated) { |
| Log.e(TAG, "createNetworkSpecifierPassphrase: called after termination"); |
| return null; |
| } |
| if (!WifiAwareUtils.validatePassphrase(passphrase)) { |
| throw new IllegalArgumentException("Passphrase must meet length requirements"); |
| } |
| |
| return mgr.createNetworkSpecifier(mClientId, role, peer, 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 API is targeted for applications which can obtain the peer MAC address using OOB |
| * (out-of-band) discovery. Aware discovery does not provide the MAC address of the peer - |
| * when using Aware discovery use the alternative network specifier method - |
| * {@link android.net.wifi.aware.WifiAwareNetworkSpecifier.Builder}. |
| * |
| * @param role The role of this device: |
| * {@link WifiAwareManager#WIFI_AWARE_DATA_PATH_ROLE_INITIATOR} or |
| * {@link WifiAwareManager#WIFI_AWARE_DATA_PATH_ROLE_RESPONDER} |
| * @param peer The MAC address of the peer's Aware discovery interface. 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(int, byte[], String)} to specify a |
| * Passphrase or {@link #createNetworkSpecifierOpen(int, byte[])} 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( |
| @WifiAwareManager.DataPathRole int role, @NonNull byte[] peer, @NonNull byte[] pmk) { |
| WifiAwareManager mgr = mMgr.get(); |
| if (mgr == null) { |
| Log.e(TAG, "createNetworkSpecifierPmk: called post GC on WifiAwareManager"); |
| return null; |
| } |
| if (mTerminated) { |
| Log.e(TAG, "createNetworkSpecifierPmk: called after termination"); |
| return null; |
| } |
| if (!WifiAwareUtils.validatePmk(pmk)) { |
| throw new IllegalArgumentException("PMK must 32 bytes"); |
| } |
| return mgr.createNetworkSpecifier(mClientId, role, peer, pmk, null); |
| } |
| } |