| /* |
| * 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.nan; |
| |
| import android.annotation.IntDef; |
| import android.annotation.NonNull; |
| import android.annotation.Nullable; |
| import android.annotation.SdkConstant; |
| import android.annotation.SdkConstant.SdkConstantType; |
| import android.content.Context; |
| import android.net.ConnectivityManager; |
| import android.net.NetworkRequest; |
| import android.net.wifi.RttManager; |
| import android.os.Binder; |
| import android.os.Bundle; |
| import android.os.Handler; |
| import android.os.Looper; |
| import android.os.Message; |
| import android.os.RemoteException; |
| import android.util.Base64; |
| import android.util.Log; |
| import android.util.SparseArray; |
| |
| import com.android.internal.annotations.GuardedBy; |
| |
| import libcore.util.HexEncoding; |
| |
| import org.json.JSONException; |
| import org.json.JSONObject; |
| |
| import java.lang.annotation.Retention; |
| import java.lang.annotation.RetentionPolicy; |
| import java.lang.ref.WeakReference; |
| import java.util.Arrays; |
| |
| /** |
| * This class provides the primary API for managing Wi-Fi NAN operations: |
| * discovery and peer-to-peer data connections. Get an instance of this class by calling |
| * {@link android.content.Context#getSystemService(String) |
| * Context.getSystemService(Context.WIFI_NAN_SERVICE)}. |
| * <p> |
| * The class provides access to: |
| * <ul> |
| * <li>Initialize a NAN cluster (peer-to-peer synchronization). Refer to |
| * {@link #attach(Handler, WifiNanEventCallback)}. <li>Create discovery sessions (publish or |
| * subscribe sessions). Refer to |
| * {@link WifiNanSession#publish(PublishConfig, WifiNanDiscoverySessionCallback)} and |
| * {@link WifiNanSession#subscribe(SubscribeConfig, WifiNanDiscoverySessionCallback)}. <li>Create |
| * a NAN network specifier to be used with |
| * {@link ConnectivityManager#requestNetwork(NetworkRequest, ConnectivityManager.NetworkCallback)} |
| * to set-up a NAN connection with a peer. Refer to |
| * {@link WifiNanDiscoveryBaseSession#createNetworkSpecifier(int, int, byte[])} and |
| * {@link WifiNanSession#createNetworkSpecifier(int, byte[], byte[])}. |
| * </ul> |
| * <p> |
| * NAN may not be usable when Wi-Fi is disabled (and other conditions). To validate that |
| * the functionality is available use the {@link #isAvailable()} function. To track |
| * changes in NAN usability register for the {@link #ACTION_WIFI_NAN_STATE_CHANGED} broadcast. |
| * Note that this broadcast is not sticky - you should register for it and then check the |
| * above API to avoid a race condition. |
| * <p> |
| * An application must use {@link #attach(Handler, WifiNanEventCallback)} to initialize a NAN |
| * cluster - before making any other NAN operation. NAN cluster membership is a device-wide |
| * operation - the API guarantees that the device is in a cluster or joins a NAN cluster (or |
| * starts one if none can be found). Information about attach success (or failure) are |
| * returned in callbacks of {@link WifiNanEventCallback}. Proceed with NAN discovery or |
| * connection setup only after receiving confirmation that NAN attach succeeded - |
| * {@link WifiNanEventCallback#onAttached(WifiNanSession)}. When an application is |
| * finished using NAN it <b>must</b> use the {@link WifiNanSession#destroy()} API |
| * to indicate to the NAN service that the device may detach from the NAN cluster. The |
| * device will actually disable NAN once the last application detaches. |
| * <p> |
| * Once a NAN attach is confirmed use the |
| * {@link WifiNanSession#publish(PublishConfig, WifiNanDiscoverySessionCallback)} or |
| * {@link WifiNanSession#subscribe(SubscribeConfig, WifiNanDiscoverySessionCallback)} to |
| * create publish or subscribe NAN discovery sessions. Events are called on the provided |
| * callback object {@link WifiNanDiscoverySessionCallback}. Specifically, the |
| * {@link WifiNanDiscoverySessionCallback#onPublishStarted(WifiNanPublishDiscoverySession)} |
| * and |
| * {@link WifiNanDiscoverySessionCallback#onSubscribeStarted(WifiNanSubscribeDiscoverySession)} |
| * return {@link WifiNanPublishDiscoverySession} and {@link WifiNanSubscribeDiscoverySession} |
| * objects respectively on which additional session operations can be performed, e.g. updating |
| * the session {@link WifiNanPublishDiscoverySession#updatePublish(PublishConfig)} and |
| * {@link WifiNanSubscribeDiscoverySession#updateSubscribe(SubscribeConfig)}. Sessions can also |
| * be used to send messages using the |
| * {@link WifiNanDiscoveryBaseSession#sendMessage(int, byte[], int)} APIs. When an application |
| * is finished with a discovery session it <b>must</b> terminate it using the |
| * {@link WifiNanDiscoveryBaseSession#destroy()} API. |
| * <p> |
| * Creating connections between NAN devices is managed by the standard |
| * {@link ConnectivityManager#requestNetwork(NetworkRequest, ConnectivityManager.NetworkCallback)}. |
| * The {@link NetworkRequest} object should be constructed with: |
| * <ul> |
| * <li>{@link NetworkRequest.Builder#addTransportType(int)} of |
| * {@link android.net.NetworkCapabilities#TRANSPORT_WIFI_NAN}. |
| * <li>{@link NetworkRequest.Builder#setNetworkSpecifier(String)} using |
| * {@link WifiNanSession#createNetworkSpecifier(int, byte[], byte[])} or |
| * {@link WifiNanDiscoveryBaseSession#createNetworkSpecifier(int, int, byte[])}. |
| * </ul> |
| * |
| * @hide PROPOSED_NAN_API |
| */ |
| public class WifiNanManager { |
| private static final String TAG = "WifiNanManager"; |
| private static final boolean DBG = false; |
| private static final boolean VDBG = false; // STOPSHIP if true |
| |
| /** |
| * Keys used to generate a Network Specifier for the NAN network request. The network specifier |
| * is formatted as a JSON string. |
| */ |
| |
| /** |
| * TYPE_1A: role, client_id, session_id, peer_id, token |
| * @hide |
| */ |
| public static final int NETWORK_SPECIFIER_TYPE_1A = 0; |
| |
| /** |
| * TYPE_1B: role, client_id, session_id, peer_id [only permitted for RESPONDER] |
| * @hide |
| */ |
| public static final int NETWORK_SPECIFIER_TYPE_1B = 1; |
| |
| /** |
| * TYPE_1C: role, client_id, session_id, token [only permitted for RESPONDER] |
| * @hide |
| */ |
| public static final int NETWORK_SPECIFIER_TYPE_1C = 2; |
| |
| /** |
| * TYPE_1C: role, client_id, session_id [only permitted for RESPONDER] |
| * @hide |
| */ |
| public static final int NETWORK_SPECIFIER_TYPE_1D = 3; |
| |
| /** |
| * TYPE_2A: role, client_id, peer_mac, token |
| * @hide |
| */ |
| public static final int NETWORK_SPECIFIER_TYPE_2A = 4; |
| |
| /** |
| * TYPE_2B: role, client_id, peer_mac [only permitted for RESPONDER] |
| * @hide |
| */ |
| public static final int NETWORK_SPECIFIER_TYPE_2B = 5; |
| |
| /** |
| * TYPE_2C: role, client_id, token [only permitted for RESPONDER] |
| * @hide |
| */ |
| public static final int NETWORK_SPECIFIER_TYPE_2C = 6; |
| |
| /** |
| * TYPE_2D: role, client_id [only permitted for RESPONDER] |
| * @hide |
| */ |
| public static final int NETWORK_SPECIFIER_TYPE_2D = 7; |
| |
| /** @hide */ |
| public static final int NETWORK_SPECIFIER_TYPE_MAX_VALID = NETWORK_SPECIFIER_TYPE_2D; |
| |
| /** @hide */ |
| public static final String NETWORK_SPECIFIER_KEY_TYPE = "type"; |
| |
| /** @hide */ |
| public static final String NETWORK_SPECIFIER_KEY_ROLE = "role"; |
| |
| /** @hide */ |
| public static final String NETWORK_SPECIFIER_KEY_CLIENT_ID = "client_id"; |
| |
| /** @hide */ |
| public static final String NETWORK_SPECIFIER_KEY_SESSION_ID = "session_id"; |
| |
| /** @hide */ |
| public static final String NETWORK_SPECIFIER_KEY_PEER_ID = "peer_id"; |
| |
| /** @hide */ |
| public static final String NETWORK_SPECIFIER_KEY_PEER_MAC = "peer_mac"; |
| |
| /** @hide */ |
| public static final String NETWORK_SPECIFIER_KEY_TOKEN = "token"; |
| |
| /** |
| * Broadcast intent action to indicate whether Wi-Fi NAN is enabled or |
| * disabled. An extra {@link #EXTRA_WIFI_STATE} provides the state |
| * information as int using {@link #WIFI_NAN_STATE_DISABLED} and |
| * {@link #WIFI_NAN_STATE_ENABLED} constants. This broadcast is <b>not</b> sticky, |
| * use the {@link #isAvailable()} API after registering the broadcast to check the current |
| * state of Wi-Fi NAN. |
| * |
| * @see #EXTRA_WIFI_STATE |
| */ |
| @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION) |
| public static final String ACTION_WIFI_NAN_STATE_CHANGED = |
| "android.net.wifi.nan.action.WIFI_NAN_STATE_CHANGED"; |
| |
| /** |
| * The lookup key for an int value indicating whether Wi-Fi NAN is enabled or |
| * disabled. Retrieve it with |
| * {@link android.content.Intent#getIntExtra(String,int)}. |
| * |
| * @see #WIFI_NAN_STATE_DISABLED |
| * @see #WIFI_NAN_STATE_ENABLED |
| */ |
| public static final String EXTRA_WIFI_STATE = "android.net.wifi.nan.extra.WIFI_STATE"; |
| |
| /** |
| * Wi-Fi NAN is disabled. |
| * |
| * @see #ACTION_WIFI_NAN_STATE_CHANGED |
| */ |
| public static final int WIFI_NAN_STATE_DISABLED = 1; |
| |
| /** |
| * Wi-Fi NAN is enabled. |
| * |
| * @see #ACTION_WIFI_NAN_STATE_CHANGED |
| */ |
| public static final int WIFI_NAN_STATE_ENABLED = 2; |
| |
| /** @hide */ |
| @IntDef({ |
| WIFI_NAN_DATA_PATH_ROLE_INITIATOR, WIFI_NAN_DATA_PATH_ROLE_RESPONDER}) |
| @Retention(RetentionPolicy.SOURCE) |
| public @interface DataPathRole { |
| } |
| |
| /** |
| * Connection creation role is that of INITIATOR. Used to create a network specifier string |
| * when requesting a NAN network. |
| * |
| * @see WifiNanDiscoveryBaseSession#createNetworkSpecifier(int, int, byte[]) |
| * @see WifiNanSession#createNetworkSpecifier(int, byte[], byte[]) |
| */ |
| public static final int WIFI_NAN_DATA_PATH_ROLE_INITIATOR = 0; |
| |
| /** |
| * Connection creation role is that of RESPONDER. Used to create a network specifier string |
| * when requesting a NAN network. |
| * |
| * @see WifiNanDiscoveryBaseSession#createNetworkSpecifier(int, int, byte[]) |
| * @see WifiNanSession#createNetworkSpecifier(int, byte[], byte[]) |
| */ |
| public static final int WIFI_NAN_DATA_PATH_ROLE_RESPONDER = 1; |
| |
| private final Context mContext; |
| private final IWifiNanManager mService; |
| |
| private final Object mLock = new Object(); // lock access to the following vars |
| |
| @GuardedBy("mLock") |
| private SparseArray<RttManager.RttListener> mRangingListeners = new SparseArray<>(); |
| |
| /** @hide */ |
| public WifiNanManager(Context context, IWifiNanManager service) { |
| mContext = context; |
| mService = service; |
| } |
| |
| /** |
| * Enable the usage of the NAN API. Doesn't actually turn on NAN cluster formation - that |
| * only happens when an attach is attempted. {@link #ACTION_WIFI_NAN_STATE_CHANGED} broadcast |
| * will be triggered. |
| * |
| * @hide |
| */ |
| public void enableUsage() { |
| try { |
| mService.enableUsage(); |
| } catch (RemoteException e) { |
| throw e.rethrowFromSystemServer(); |
| } |
| } |
| |
| /** |
| * Disable the usage of the NAN API. All attempts to attach() will be rejected. All open |
| * connections and sessions will be terminated. {@link #ACTION_WIFI_NAN_STATE_CHANGED} broadcast |
| * will be triggered. |
| * |
| * @hide |
| */ |
| public void disableUsage() { |
| try { |
| mService.disableUsage(); |
| } catch (RemoteException e) { |
| throw e.rethrowFromSystemServer(); |
| } |
| } |
| |
| /** |
| * Returns the current status of NAN API: whether or not NAN is available. To track changes |
| * in the state of NAN API register for the {@link #ACTION_WIFI_NAN_STATE_CHANGED} broadcast. |
| * |
| * @return A boolean indicating whether the app can use the NAN API at this time (true) or |
| * not (false). |
| */ |
| public boolean isAvailable() { |
| try { |
| return mService.isUsageEnabled(); |
| } catch (RemoteException e) { |
| throw e.rethrowFromSystemServer(); |
| } |
| } |
| |
| /** |
| * Attach to the Wi-Fi NAN service - enabling the application to create discovery session or |
| * create connection to peers. The device will attach to an existing cluster if it can find |
| * one or create a new cluster (if it is the first to enable NAN in its vicinity). Results |
| * (e.g. successful attach to a cluster) are provided to the {@code callback} object. |
| * An application <b>must</b> call {@link WifiNanSession#destroy()} when done with the |
| * Wi-Fi NAN object. |
| * <p> |
| * Note: a NAN cluster is a shared resource - if the device is already attached to a cluster |
| * than this function will simply indicate success immediately. |
| * |
| * @param handler The Handler on whose thread to execute all callbacks related to the |
| * attach request - including all sessions opened as part of this |
| * attach. If a null is provided then the application's main thread will be used. |
| * @param callback A callback extended from {@link WifiNanEventCallback}. |
| */ |
| public void attach(@Nullable Handler handler, @NonNull WifiNanEventCallback callback) { |
| attach(handler, null, callback); |
| } |
| |
| /** |
| * Attach to the Wi-Fi NAN service - enabling the application to create discovery session or |
| * create connection to peers. The device will attach to an existing cluster if it can find |
| * one or create a new cluster (if it is the first to enable NAN in its vicinity). Results |
| * (e.g. successful attach to a cluster) are provided to the {@code callback} object. |
| * An application <b>must</b> call {@link WifiNanSession#destroy()} when done with the |
| * Wi-Fi NAN object. Allows requesting a specific configuration using |
| * {@link ConfigRequest}. If not necessary (default configuration should usually work) use |
| * the {@link #attach(Handler, WifiNanEventCallback)} method instead. |
| * <p> |
| * Note: a NAN cluster is a shared resource - if the device is already attached to a cluster |
| * than this function will simply indicate success immediately. |
| * |
| * @param handler The Handler on whose thread to execute all callbacks related to the |
| * attach request - including all sessions opened as part of this |
| * attach. If a null is provided then the application's main thread will be used. |
| * @param configRequest The requested NAN configuration. |
| * @param callback A callback extended from {@link WifiNanEventCallback}. |
| */ |
| public void attach(@Nullable Handler handler, @Nullable ConfigRequest configRequest, |
| @NonNull WifiNanEventCallback callback) { |
| if (VDBG) { |
| Log.v(TAG, |
| "attach(): handler=" + handler + ", callback=" + callback + ", configRequest=" |
| + configRequest); |
| } |
| |
| synchronized (mLock) { |
| Looper looper = (handler == null) ? Looper.getMainLooper() : handler.getLooper(); |
| |
| try { |
| Binder binder = new Binder(); |
| mService.connect(binder, mContext.getOpPackageName(), |
| new WifiNanEventCallbackProxy(this, looper, binder, callback), |
| configRequest); |
| } catch (RemoteException e) { |
| throw e.rethrowFromSystemServer(); |
| } |
| } |
| } |
| |
| /** @hide */ |
| public void disconnect(int clientId, Binder binder) { |
| if (VDBG) Log.v(TAG, "disconnect()"); |
| |
| try { |
| mService.disconnect(clientId, binder); |
| } catch (RemoteException e) { |
| throw e.rethrowFromSystemServer(); |
| } |
| } |
| |
| /** @hide */ |
| public void publish(int clientId, Looper looper, PublishConfig publishConfig, |
| WifiNanDiscoverySessionCallback callback) { |
| if (VDBG) Log.v(TAG, "publish(): clientId=" + clientId + ", config=" + publishConfig); |
| |
| try { |
| mService.publish(clientId, publishConfig, |
| new WifiNanDiscoverySessionCallbackProxy(this, looper, true, callback, |
| clientId)); |
| } catch (RemoteException e) { |
| throw e.rethrowFromSystemServer(); |
| } |
| } |
| |
| /** @hide */ |
| public void updatePublish(int clientId, int sessionId, PublishConfig publishConfig) { |
| if (VDBG) { |
| Log.v(TAG, "updatePublish(): clientId=" + clientId + ",sessionId=" + sessionId |
| + ", config=" + publishConfig); |
| } |
| |
| try { |
| mService.updatePublish(clientId, sessionId, publishConfig); |
| } catch (RemoteException e) { |
| throw e.rethrowFromSystemServer(); |
| } |
| } |
| |
| /** @hide */ |
| public void subscribe(int clientId, Looper looper, SubscribeConfig subscribeConfig, |
| WifiNanDiscoverySessionCallback callback) { |
| if (VDBG) { |
| if (VDBG) { |
| Log.v(TAG, |
| "subscribe(): clientId=" + clientId + ", config=" + subscribeConfig); |
| } |
| } |
| |
| try { |
| mService.subscribe(clientId, subscribeConfig, |
| new WifiNanDiscoverySessionCallbackProxy(this, looper, false, callback, |
| clientId)); |
| } catch (RemoteException e) { |
| throw e.rethrowFromSystemServer(); |
| } |
| } |
| |
| /** @hide */ |
| public void updateSubscribe(int clientId, int sessionId, SubscribeConfig subscribeConfig) { |
| if (VDBG) { |
| Log.v(TAG, "updateSubscribe(): clientId=" + clientId + ",sessionId=" + sessionId |
| + ", config=" + subscribeConfig); |
| } |
| |
| try { |
| mService.updateSubscribe(clientId, sessionId, subscribeConfig); |
| } catch (RemoteException e) { |
| throw e.rethrowFromSystemServer(); |
| } |
| } |
| |
| /** @hide */ |
| public void terminateSession(int clientId, int sessionId) { |
| if (VDBG) { |
| Log.d(TAG, |
| "terminateSession(): clientId=" + clientId + ", sessionId=" + sessionId); |
| } |
| |
| try { |
| mService.terminateSession(clientId, sessionId); |
| } catch (RemoteException e) { |
| throw e.rethrowFromSystemServer(); |
| } |
| } |
| |
| /** @hide */ |
| public void sendMessage(int clientId, int sessionId, int peerId, byte[] message, int messageId, |
| int retryCount) { |
| if (VDBG) { |
| Log.v(TAG, |
| "sendMessage(): clientId=" + clientId + ", sessionId=" + sessionId + ", peerId=" |
| + peerId + ", messageId=" + messageId + ", retryCount=" + retryCount); |
| } |
| |
| try { |
| mService.sendMessage(clientId, sessionId, peerId, message, messageId, retryCount); |
| } catch (RemoteException e) { |
| throw e.rethrowFromSystemServer(); |
| } |
| } |
| |
| /** @hide */ |
| public void startRanging(int clientId, int sessionId, RttManager.RttParams[] params, |
| RttManager.RttListener listener) { |
| if (VDBG) { |
| Log.v(TAG, "startRanging: clientId=" + clientId + ", sessionId=" + sessionId + ", " |
| + "params=" + Arrays.toString(params) + ", listener=" + listener); |
| } |
| |
| int rangingKey = 0; |
| try { |
| rangingKey = mService.startRanging(clientId, sessionId, |
| new RttManager.ParcelableRttParams(params)); |
| } catch (RemoteException e) { |
| throw e.rethrowFromSystemServer(); |
| } |
| |
| synchronized (mLock) { |
| mRangingListeners.put(rangingKey, listener); |
| } |
| } |
| |
| /** @hide */ |
| public String createNetworkSpecifier(int clientId, int role, int sessionId, int peerId, |
| byte[] token) { |
| if (VDBG) { |
| Log.v(TAG, "createNetworkSpecifier: role=" + role + ", sessionId=" + sessionId |
| + ", peerId=" + peerId + ", token=" + token); |
| } |
| |
| int type; |
| if (token != null && peerId != 0) { |
| type = NETWORK_SPECIFIER_TYPE_1A; |
| } else if (token == null && peerId != 0) { |
| type = NETWORK_SPECIFIER_TYPE_1B; |
| } else if (token != null && peerId == 0) { |
| type = NETWORK_SPECIFIER_TYPE_1C; |
| } else { |
| type = NETWORK_SPECIFIER_TYPE_1D; |
| } |
| |
| if (role != WIFI_NAN_DATA_PATH_ROLE_INITIATOR |
| && role != WIFI_NAN_DATA_PATH_ROLE_RESPONDER) { |
| throw new IllegalArgumentException( |
| "createNetworkSpecifier: Invalid 'role' argument when creating a network " |
| + "specifier"); |
| } |
| if (role == WIFI_NAN_DATA_PATH_ROLE_INITIATOR) { |
| if (token == null) { |
| throw new IllegalArgumentException( |
| "createNetworkSpecifier: Invalid null token - not permitted on INITIATOR"); |
| } |
| if (peerId == 0) { |
| throw new IllegalArgumentException( |
| "createNetworkSpecifier: Invalid peer ID (value of 0) - not permitted on " |
| + "INITIATOR"); |
| } |
| } |
| |
| JSONObject json; |
| try { |
| json = new JSONObject(); |
| json.put(NETWORK_SPECIFIER_KEY_TYPE, type); |
| json.put(NETWORK_SPECIFIER_KEY_ROLE, role); |
| json.put(NETWORK_SPECIFIER_KEY_CLIENT_ID, clientId); |
| json.put(NETWORK_SPECIFIER_KEY_SESSION_ID, sessionId); |
| if (peerId != 0) { |
| json.put(NETWORK_SPECIFIER_KEY_PEER_ID, peerId); |
| } |
| if (token != null) { |
| json.put(NETWORK_SPECIFIER_KEY_TOKEN, |
| Base64.encodeToString(token, 0, token.length, Base64.DEFAULT)); |
| } |
| } catch (JSONException e) { |
| return ""; |
| } |
| |
| return json.toString(); |
| } |
| |
| /** @hide */ |
| public String createNetworkSpecifier(int clientId, @DataPathRole int role, @Nullable byte[] peer, |
| @Nullable byte[] token) { |
| if (VDBG) { |
| Log.v(TAG, "createNetworkSpecifier: role=" + role + ", token=" + token); |
| } |
| |
| int type; |
| if (token != null && peer != null) { |
| type = NETWORK_SPECIFIER_TYPE_2A; |
| } else if (token == null && peer != null) { |
| type = NETWORK_SPECIFIER_TYPE_2B; |
| } else if (token != null && peer == null) { |
| type = NETWORK_SPECIFIER_TYPE_2C; |
| } else { // both are null |
| type = NETWORK_SPECIFIER_TYPE_2D; |
| } |
| |
| if (role != WIFI_NAN_DATA_PATH_ROLE_INITIATOR |
| && role != WIFI_NAN_DATA_PATH_ROLE_RESPONDER) { |
| throw new IllegalArgumentException( |
| "createNetworkSpecifier: Invalid 'role' argument when creating a network " |
| + "specifier"); |
| } |
| if (role == WIFI_NAN_DATA_PATH_ROLE_INITIATOR) { |
| if (peer == null || peer.length != 6) { |
| throw new IllegalArgumentException( |
| "createNetworkSpecifier: Invalid peer MAC address"); |
| } |
| if (token == null) { |
| throw new IllegalArgumentException( |
| "createNetworkSpecifier: Invalid null token - not permitted on INITIATOR"); |
| } |
| } else { |
| if (peer != null && peer.length != 6) { |
| throw new IllegalArgumentException( |
| "createNetworkSpecifier: Invalid peer MAC address"); |
| } |
| } |
| |
| JSONObject json; |
| try { |
| json = new JSONObject(); |
| json.put(NETWORK_SPECIFIER_KEY_TYPE, type); |
| json.put(NETWORK_SPECIFIER_KEY_ROLE, role); |
| json.put(NETWORK_SPECIFIER_KEY_CLIENT_ID, clientId); |
| if (peer != null) { |
| json.put(NETWORK_SPECIFIER_KEY_PEER_MAC, new String(HexEncoding.encode(peer))); |
| } |
| if (token != null) { |
| json.put(NETWORK_SPECIFIER_KEY_TOKEN, |
| Base64.encodeToString(token, 0, token.length, Base64.DEFAULT)); |
| } |
| } catch (JSONException e) { |
| return ""; |
| } |
| |
| return json.toString(); |
| } |
| |
| private static class WifiNanEventCallbackProxy extends IWifiNanEventCallback.Stub { |
| private static final int CALLBACK_CONNECT_SUCCESS = 0; |
| private static final int CALLBACK_CONNECT_FAIL = 1; |
| private static final int CALLBACK_IDENTITY_CHANGED = 2; |
| private static final int CALLBACK_RANGING_SUCCESS = 3; |
| private static final int CALLBACK_RANGING_FAILURE = 4; |
| private static final int CALLBACK_RANGING_ABORTED = 5; |
| |
| private final Handler mHandler; |
| private final WeakReference<WifiNanManager> mNanManager; |
| private final Binder mBinder; |
| private final Looper mLooper; |
| |
| RttManager.RttListener getAndRemoveRangingListener(int rangingId) { |
| WifiNanManager mgr = mNanManager.get(); |
| if (mgr == null) { |
| Log.w(TAG, "getAndRemoveRangingListener: called post GC"); |
| return null; |
| } |
| |
| synchronized (mgr.mLock) { |
| RttManager.RttListener listener = mgr.mRangingListeners.get(rangingId); |
| mgr.mRangingListeners.delete(rangingId); |
| return listener; |
| } |
| } |
| |
| /** |
| * Constructs a {@link WifiNanEventCallback} using the specified looper. |
| * All callbacks will delivered on the thread of the specified looper. |
| * |
| * @param looper The looper on which to execute the callbacks. |
| */ |
| WifiNanEventCallbackProxy(WifiNanManager mgr, Looper looper, Binder binder, |
| final WifiNanEventCallback originalCallback) { |
| mNanManager = new WeakReference<>(mgr); |
| mLooper = looper; |
| mBinder = binder; |
| |
| if (VDBG) Log.v(TAG, "WifiNanEventCallbackProxy ctor: looper=" + looper); |
| mHandler = new Handler(looper) { |
| @Override |
| public void handleMessage(Message msg) { |
| if (DBG) { |
| Log.d(TAG, "WifiNanEventCallbackProxy: What=" + msg.what + ", msg=" + msg); |
| } |
| |
| WifiNanManager mgr = mNanManager.get(); |
| if (mgr == null) { |
| Log.w(TAG, "WifiNanEventCallbackProxy: handleMessage post GC"); |
| return; |
| } |
| |
| switch (msg.what) { |
| case CALLBACK_CONNECT_SUCCESS: |
| originalCallback.onAttached( |
| new WifiNanSession(mgr, mBinder, mLooper, msg.arg1)); |
| break; |
| case CALLBACK_CONNECT_FAIL: |
| mNanManager.clear(); |
| originalCallback.onAttachFailed(msg.arg1); |
| break; |
| case CALLBACK_IDENTITY_CHANGED: |
| originalCallback.onIdentityChanged((byte[]) msg.obj); |
| break; |
| case CALLBACK_RANGING_SUCCESS: { |
| RttManager.RttListener listener = getAndRemoveRangingListener(msg.arg1); |
| if (listener == null) { |
| Log.e(TAG, "CALLBACK_RANGING_SUCCESS rangingId=" + msg.arg1 |
| + ": no listener registered (anymore)"); |
| } else { |
| listener.onSuccess( |
| ((RttManager.ParcelableRttResults) msg.obj).mResults); |
| } |
| break; |
| } |
| case CALLBACK_RANGING_FAILURE: { |
| RttManager.RttListener listener = getAndRemoveRangingListener(msg.arg1); |
| if (listener == null) { |
| Log.e(TAG, "CALLBACK_RANGING_SUCCESS rangingId=" + msg.arg1 |
| + ": no listener registered (anymore)"); |
| } else { |
| listener.onFailure(msg.arg2, (String) msg.obj); |
| } |
| break; |
| } |
| case CALLBACK_RANGING_ABORTED: { |
| RttManager.RttListener listener = getAndRemoveRangingListener(msg.arg1); |
| if (listener == null) { |
| Log.e(TAG, "CALLBACK_RANGING_SUCCESS rangingId=" + msg.arg1 |
| + ": no listener registered (anymore)"); |
| } else { |
| listener.onAborted(); |
| } |
| break; |
| } |
| } |
| } |
| }; |
| } |
| |
| @Override |
| public void onConnectSuccess(int clientId) { |
| if (VDBG) Log.v(TAG, "onConnectSuccess"); |
| |
| Message msg = mHandler.obtainMessage(CALLBACK_CONNECT_SUCCESS); |
| msg.arg1 = clientId; |
| mHandler.sendMessage(msg); |
| } |
| |
| @Override |
| public void onConnectFail(int reason) { |
| if (VDBG) Log.v(TAG, "onConfigFailed: reason=" + reason); |
| |
| Message msg = mHandler.obtainMessage(CALLBACK_CONNECT_FAIL); |
| msg.arg1 = reason; |
| mHandler.sendMessage(msg); |
| } |
| |
| @Override |
| public void onIdentityChanged(byte[] mac) { |
| if (VDBG) Log.v(TAG, "onIdentityChanged: mac=" + new String(HexEncoding.encode(mac))); |
| |
| Message msg = mHandler.obtainMessage(CALLBACK_IDENTITY_CHANGED); |
| msg.obj = mac; |
| mHandler.sendMessage(msg); |
| } |
| |
| @Override |
| public void onRangingSuccess(int rangingId, RttManager.ParcelableRttResults results) { |
| if (VDBG) { |
| Log.v(TAG, "onRangingSuccess: rangingId=" + rangingId + ", results=" + results); |
| } |
| |
| Message msg = mHandler.obtainMessage(CALLBACK_RANGING_SUCCESS); |
| msg.arg1 = rangingId; |
| msg.obj = results; |
| mHandler.sendMessage(msg); |
| } |
| |
| @Override |
| public void onRangingFailure(int rangingId, int reason, String description) { |
| if (VDBG) { |
| Log.v(TAG, "onRangingSuccess: rangingId=" + rangingId + ", reason=" + reason |
| + ", description=" + description); |
| } |
| |
| Message msg = mHandler.obtainMessage(CALLBACK_RANGING_FAILURE); |
| msg.arg1 = rangingId; |
| msg.arg2 = reason; |
| msg.obj = description; |
| mHandler.sendMessage(msg); |
| |
| } |
| |
| @Override |
| public void onRangingAborted(int rangingId) { |
| if (VDBG) Log.v(TAG, "onRangingAborted: rangingId=" + rangingId); |
| |
| Message msg = mHandler.obtainMessage(CALLBACK_RANGING_ABORTED); |
| msg.arg1 = rangingId; |
| mHandler.sendMessage(msg); |
| |
| } |
| } |
| |
| private static class WifiNanDiscoverySessionCallbackProxy extends |
| IWifiNanDiscoverySessionCallback.Stub { |
| private static final int CALLBACK_SESSION_STARTED = 0; |
| private static final int CALLBACK_SESSION_CONFIG_SUCCESS = 1; |
| private static final int CALLBACK_SESSION_CONFIG_FAIL = 2; |
| private static final int CALLBACK_SESSION_TERMINATED = 3; |
| private static final int CALLBACK_MATCH = 4; |
| private static final int CALLBACK_MESSAGE_SEND_SUCCESS = 5; |
| private static final int CALLBACK_MESSAGE_SEND_FAIL = 6; |
| private static final int CALLBACK_MESSAGE_RECEIVED = 7; |
| |
| private static final String MESSAGE_BUNDLE_KEY_MESSAGE = "message"; |
| private static final String MESSAGE_BUNDLE_KEY_MESSAGE2 = "message2"; |
| |
| private final WeakReference<WifiNanManager> mNanManager; |
| private final boolean mIsPublish; |
| private final WifiNanDiscoverySessionCallback mOriginalCallback; |
| private final int mClientId; |
| |
| private final Handler mHandler; |
| private WifiNanDiscoveryBaseSession mSession; |
| |
| WifiNanDiscoverySessionCallbackProxy(WifiNanManager mgr, Looper looper, boolean isPublish, |
| WifiNanDiscoverySessionCallback originalCallback, int clientId) { |
| mNanManager = new WeakReference<>(mgr); |
| mIsPublish = isPublish; |
| mOriginalCallback = originalCallback; |
| mClientId = clientId; |
| |
| if (VDBG) { |
| Log.v(TAG, "WifiNanDiscoverySessionCallbackProxy ctor: isPublish=" + isPublish); |
| } |
| |
| mHandler = new Handler(looper) { |
| @Override |
| public void handleMessage(Message msg) { |
| if (DBG) Log.d(TAG, "What=" + msg.what + ", msg=" + msg); |
| |
| if (mNanManager.get() == null) { |
| Log.w(TAG, "WifiNanDiscoverySessionCallbackProxy: handleMessage post GC"); |
| return; |
| } |
| |
| switch (msg.what) { |
| case CALLBACK_SESSION_STARTED: |
| onProxySessionStarted(msg.arg1); |
| break; |
| case CALLBACK_SESSION_CONFIG_SUCCESS: |
| mOriginalCallback.onSessionConfigUpdated(); |
| break; |
| case CALLBACK_SESSION_CONFIG_FAIL: |
| mOriginalCallback.onSessionConfigFailed(msg.arg1); |
| if (mSession == null) { |
| /* |
| * creation failed (as opposed to update |
| * failing) |
| */ |
| mNanManager.clear(); |
| } |
| break; |
| case CALLBACK_SESSION_TERMINATED: |
| onProxySessionTerminated(msg.arg1); |
| break; |
| case CALLBACK_MATCH: |
| mOriginalCallback.onServiceDiscovered( |
| msg.arg1, |
| msg.getData().getByteArray(MESSAGE_BUNDLE_KEY_MESSAGE), |
| msg.getData().getByteArray(MESSAGE_BUNDLE_KEY_MESSAGE2)); |
| break; |
| case CALLBACK_MESSAGE_SEND_SUCCESS: |
| mOriginalCallback.onMessageSent(msg.arg1); |
| break; |
| case CALLBACK_MESSAGE_SEND_FAIL: |
| mOriginalCallback.onMessageSendFailed(msg.arg1, msg.arg2); |
| break; |
| case CALLBACK_MESSAGE_RECEIVED: |
| mOriginalCallback.onMessageReceived(msg.arg1, (byte[]) msg.obj); |
| break; |
| } |
| } |
| }; |
| } |
| |
| @Override |
| public void onSessionStarted(int sessionId) { |
| if (VDBG) Log.v(TAG, "onSessionStarted: sessionId=" + sessionId); |
| |
| Message msg = mHandler.obtainMessage(CALLBACK_SESSION_STARTED); |
| msg.arg1 = sessionId; |
| mHandler.sendMessage(msg); |
| } |
| |
| @Override |
| public void onSessionConfigSuccess() { |
| if (VDBG) Log.v(TAG, "onSessionConfigSuccess"); |
| |
| Message msg = mHandler.obtainMessage(CALLBACK_SESSION_CONFIG_SUCCESS); |
| mHandler.sendMessage(msg); |
| } |
| |
| @Override |
| public void onSessionConfigFail(int reason) { |
| if (VDBG) Log.v(TAG, "onSessionConfigFail: reason=" + reason); |
| |
| Message msg = mHandler.obtainMessage(CALLBACK_SESSION_CONFIG_FAIL); |
| msg.arg1 = reason; |
| mHandler.sendMessage(msg); |
| } |
| |
| @Override |
| public void onSessionTerminated(int reason) { |
| if (VDBG) Log.v(TAG, "onSessionTerminated: reason=" + reason); |
| |
| Message msg = mHandler.obtainMessage(CALLBACK_SESSION_TERMINATED); |
| msg.arg1 = reason; |
| mHandler.sendMessage(msg); |
| } |
| |
| @Override |
| public void onMatch(int peerId, byte[] serviceSpecificInfo, byte[] matchFilter) { |
| if (VDBG) Log.v(TAG, "onMatch: peerId=" + peerId); |
| |
| Bundle data = new Bundle(); |
| data.putByteArray(MESSAGE_BUNDLE_KEY_MESSAGE, serviceSpecificInfo); |
| data.putByteArray(MESSAGE_BUNDLE_KEY_MESSAGE2, matchFilter); |
| |
| Message msg = mHandler.obtainMessage(CALLBACK_MATCH); |
| msg.arg1 = peerId; |
| msg.setData(data); |
| mHandler.sendMessage(msg); |
| } |
| |
| @Override |
| public void onMessageSendSuccess(int messageId) { |
| if (VDBG) Log.v(TAG, "onMessageSendSuccess"); |
| |
| Message msg = mHandler.obtainMessage(CALLBACK_MESSAGE_SEND_SUCCESS); |
| msg.arg1 = messageId; |
| mHandler.sendMessage(msg); |
| } |
| |
| @Override |
| public void onMessageSendFail(int messageId, int reason) { |
| if (VDBG) Log.v(TAG, "onMessageSendFail: reason=" + reason); |
| |
| Message msg = mHandler.obtainMessage(CALLBACK_MESSAGE_SEND_FAIL); |
| msg.arg1 = messageId; |
| msg.arg2 = reason; |
| mHandler.sendMessage(msg); |
| } |
| |
| @Override |
| public void onMessageReceived(int peerId, byte[] message) { |
| if (VDBG) { |
| Log.v(TAG, "onMessageReceived: peerId='" + peerId); |
| } |
| |
| Message msg = mHandler.obtainMessage(CALLBACK_MESSAGE_RECEIVED); |
| msg.arg1 = peerId; |
| msg.obj = message; |
| mHandler.sendMessage(msg); |
| } |
| |
| /* |
| * Proxied methods |
| */ |
| public void onProxySessionStarted(int sessionId) { |
| if (VDBG) Log.v(TAG, "Proxy: onSessionStarted: sessionId=" + sessionId); |
| if (mSession != null) { |
| Log.e(TAG, |
| "onSessionStarted: sessionId=" + sessionId + ": session already created!?"); |
| throw new IllegalStateException( |
| "onSessionStarted: sessionId=" + sessionId + ": session already created!?"); |
| } |
| |
| WifiNanManager mgr = mNanManager.get(); |
| if (mgr == null) { |
| Log.w(TAG, "onProxySessionStarted: mgr GC'd"); |
| return; |
| } |
| |
| if (mIsPublish) { |
| WifiNanPublishDiscoverySession session = new WifiNanPublishDiscoverySession(mgr, |
| mClientId, sessionId); |
| mSession = session; |
| mOriginalCallback.onPublishStarted(session); |
| } else { |
| WifiNanSubscribeDiscoverySession |
| session = new WifiNanSubscribeDiscoverySession(mgr, mClientId, sessionId); |
| mSession = session; |
| mOriginalCallback.onSubscribeStarted(session); |
| } |
| } |
| |
| public void onProxySessionTerminated(int reason) { |
| if (VDBG) Log.v(TAG, "Proxy: onSessionTerminated: reason=" + reason); |
| if (mSession != null) { |
| mSession.setTerminated(); |
| mSession = null; |
| } else { |
| Log.w(TAG, "Proxy: onSessionTerminated called but mSession is null!?"); |
| } |
| mNanManager.clear(); |
| mOriginalCallback.onSessionTerminated(reason); |
| } |
| } |
| } |