| /* |
| * Copyright (C) 2017 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 com.googlecode.android_scripting.facade.wifi; |
| |
| import android.app.Service; |
| import android.content.BroadcastReceiver; |
| import android.content.Context; |
| import android.content.Intent; |
| import android.content.IntentFilter; |
| import android.content.pm.PackageManager; |
| import android.net.NetworkSpecifier; |
| import android.net.wifi.RttManager; |
| import android.net.wifi.RttManager.RttResult; |
| import android.net.wifi.WifiScanner; |
| import android.net.wifi.aware.AttachCallback; |
| import android.net.wifi.aware.ConfigRequest; |
| import android.net.wifi.aware.DiscoverySession; |
| import android.net.wifi.aware.DiscoverySessionCallback; |
| import android.net.wifi.aware.IdentityChangedListener; |
| import android.net.wifi.aware.PeerHandle; |
| import android.net.wifi.aware.PublishConfig; |
| import android.net.wifi.aware.PublishDiscoverySession; |
| import android.net.wifi.aware.SubscribeConfig; |
| import android.net.wifi.aware.SubscribeDiscoverySession; |
| import android.net.wifi.aware.TlvBufferUtils; |
| import android.net.wifi.aware.WifiAwareManager; |
| import android.net.wifi.aware.WifiAwareNetworkSpecifier; |
| import android.net.wifi.aware.WifiAwareSession; |
| import android.os.Bundle; |
| import android.os.Parcelable; |
| import android.os.RemoteException; |
| import android.text.TextUtils; |
| import android.util.Base64; |
| import android.util.SparseArray; |
| |
| import com.android.internal.annotations.GuardedBy; |
| import com.android.modules.utils.build.SdkLevel; |
| |
| import libcore.util.HexEncoding; |
| |
| import com.googlecode.android_scripting.facade.EventFacade; |
| import com.googlecode.android_scripting.facade.FacadeManager; |
| import com.googlecode.android_scripting.jsonrpc.RpcReceiver; |
| import com.googlecode.android_scripting.rpc.Rpc; |
| import com.googlecode.android_scripting.rpc.RpcOptional; |
| import com.googlecode.android_scripting.rpc.RpcParameter; |
| |
| import org.json.JSONArray; |
| import org.json.JSONException; |
| import org.json.JSONObject; |
| |
| import java.nio.charset.StandardCharsets; |
| import java.util.ArrayList; |
| import java.util.List; |
| import java.util.Objects; |
| |
| /** |
| * WifiAwareManager functions. |
| */ |
| public class WifiAwareManagerFacade extends RpcReceiver { |
| private final Service mService; |
| private final EventFacade mEventFacade; |
| private final WifiAwareStateChangedReceiver mStateChangedReceiver; |
| |
| private final Object mLock = new Object(); // lock access to the following vars |
| |
| @GuardedBy("mLock") |
| private WifiAwareManager mMgr; |
| |
| @GuardedBy("mLock") |
| private int mNextDiscoverySessionId = 1; |
| @GuardedBy("mLock") |
| private SparseArray<DiscoverySession> mDiscoverySessions = new SparseArray<>(); |
| private int getNextDiscoverySessionId() { |
| synchronized (mLock) { |
| return mNextDiscoverySessionId++; |
| } |
| } |
| |
| @GuardedBy("mLock") |
| private int mNextSessionId = 1; |
| @GuardedBy("mLock") |
| private SparseArray<WifiAwareSession> mSessions = new SparseArray<>(); |
| private int getNextSessionId() { |
| synchronized (mLock) { |
| return mNextSessionId++; |
| } |
| } |
| |
| @GuardedBy("mLock") |
| private SparseArray<Long> mMessageStartTime = new SparseArray<>(); |
| |
| private static final String NS_KEY_TYPE = "type"; |
| private static final String NS_KEY_ROLE = "role"; |
| private static final String NS_KEY_CLIENT_ID = "client_id"; |
| private static final String NS_KEY_SESSION_ID = "session_id"; |
| private static final String NS_KEY_PEER_ID = "peer_id"; |
| private static final String NS_KEY_PEER_MAC = "peer_mac"; |
| private static final String NS_KEY_PMK = "pmk"; |
| private static final String NS_KEY_PASSPHRASE = "passphrase"; |
| private static final String NS_KEY_PORT = "port"; |
| private static final String NS_KEY_TRANSPORT_PROTOCOL = "transport_protocol"; |
| |
| private static String getJsonString(WifiAwareNetworkSpecifier ns) throws JSONException { |
| JSONObject j = new JSONObject(); |
| |
| j.put(NS_KEY_TYPE, ns.type); |
| j.put(NS_KEY_ROLE, ns.role); |
| j.put(NS_KEY_CLIENT_ID, ns.clientId); |
| j.put(NS_KEY_SESSION_ID, ns.sessionId); |
| j.put(NS_KEY_PEER_ID, ns.peerId); |
| if (ns.peerMac != null) { |
| j.put(NS_KEY_PEER_MAC, Base64.encodeToString(ns.peerMac, Base64.DEFAULT)); |
| } |
| if (ns.pmk != null) { |
| j.put(NS_KEY_PMK, Base64.encodeToString(ns.pmk, Base64.DEFAULT)); |
| } |
| if (ns.passphrase != null) { |
| j.put(NS_KEY_PASSPHRASE, ns.passphrase); |
| } |
| if (ns.port != 0) { |
| j.put(NS_KEY_PORT, ns.port); |
| } |
| if (ns.transportProtocol != -1) { |
| j.put(NS_KEY_TRANSPORT_PROTOCOL, ns.transportProtocol); |
| } |
| |
| return j.toString(); |
| } |
| |
| public static NetworkSpecifier getNetworkSpecifier(JSONObject j) throws JSONException { |
| if (j == null) { |
| return null; |
| } |
| |
| int type = 0, role = 0, clientId = 0, sessionId = 0, peerId = 0; |
| byte[] peerMac = null; |
| byte[] pmk = null; |
| String passphrase = null; |
| int port = 0, transportProtocol = -1; |
| |
| if (j.has(NS_KEY_TYPE)) { |
| type = j.getInt((NS_KEY_TYPE)); |
| } |
| if (j.has(NS_KEY_ROLE)) { |
| role = j.getInt((NS_KEY_ROLE)); |
| } |
| if (j.has(NS_KEY_CLIENT_ID)) { |
| clientId = j.getInt((NS_KEY_CLIENT_ID)); |
| } |
| if (j.has(NS_KEY_SESSION_ID)) { |
| sessionId = j.getInt((NS_KEY_SESSION_ID)); |
| } |
| if (j.has(NS_KEY_PEER_ID)) { |
| peerId = j.getInt((NS_KEY_PEER_ID)); |
| } |
| if (j.has(NS_KEY_PEER_MAC)) { |
| peerMac = Base64.decode(j.getString(NS_KEY_PEER_MAC), Base64.DEFAULT); |
| } |
| if (j.has(NS_KEY_PMK)) { |
| pmk = Base64.decode(j.getString(NS_KEY_PMK), Base64.DEFAULT); |
| } |
| if (j.has(NS_KEY_PASSPHRASE)) { |
| passphrase = j.getString(NS_KEY_PASSPHRASE); |
| } |
| if (j.has(NS_KEY_PORT)) { |
| port = j.getInt(NS_KEY_PORT); |
| } |
| if (j.has(NS_KEY_TRANSPORT_PROTOCOL)) { |
| transportProtocol = j.getInt(NS_KEY_TRANSPORT_PROTOCOL); |
| } |
| |
| return new WifiAwareNetworkSpecifier(type, role, clientId, sessionId, peerId, peerMac, pmk, |
| passphrase, port, transportProtocol); |
| } |
| |
| private static String getStringOrNull(JSONObject j, String name) throws JSONException { |
| if (j.isNull(name)) { |
| return null; |
| } |
| return j.getString(name); |
| } |
| |
| private static ConfigRequest getConfigRequest(JSONObject j) throws JSONException { |
| if (j == null) { |
| return null; |
| } |
| |
| ConfigRequest.Builder builder = new ConfigRequest.Builder(); |
| |
| if (j.has("Support5gBand")) { |
| builder.setSupport5gBand(j.getBoolean("Support5gBand")); |
| } |
| if (j.has("MasterPreference")) { |
| builder.setMasterPreference(j.getInt("MasterPreference")); |
| } |
| if (j.has("ClusterLow")) { |
| builder.setClusterLow(j.getInt("ClusterLow")); |
| } |
| if (j.has("ClusterHigh")) { |
| builder.setClusterHigh(j.getInt("ClusterHigh")); |
| } |
| if (j.has("DiscoveryWindowInterval")) { |
| JSONArray interval = j.getJSONArray("DiscoveryWindowInterval"); |
| if (interval.length() != 2) { |
| throw new JSONException( |
| "Expect 'DiscoveryWindowInterval' to be an array with 2 elements!"); |
| } |
| int intervalValue = interval.getInt(ConfigRequest.NAN_BAND_24GHZ); |
| if (intervalValue != ConfigRequest.DW_INTERVAL_NOT_INIT) { |
| builder.setDiscoveryWindowInterval(ConfigRequest.NAN_BAND_24GHZ, intervalValue); |
| } |
| intervalValue = interval.getInt(ConfigRequest.NAN_BAND_5GHZ); |
| if (intervalValue != ConfigRequest.DW_INTERVAL_NOT_INIT) { |
| builder.setDiscoveryWindowInterval(ConfigRequest.NAN_BAND_5GHZ, intervalValue); |
| } |
| } |
| |
| return builder.build(); |
| } |
| |
| private static List<byte[]> getMatchFilter(JSONArray ja) throws JSONException { |
| List<byte[]> la = new ArrayList<>(); |
| for (int i = 0; i < ja.length(); ++i) { |
| la.add(Base64.decode(ja.getString(i).getBytes(StandardCharsets.UTF_8), Base64.DEFAULT)); |
| } |
| return la; |
| } |
| |
| private static PublishConfig getPublishConfig(JSONObject j) throws JSONException { |
| if (j == null) { |
| return null; |
| } |
| |
| PublishConfig.Builder builder = new PublishConfig.Builder(); |
| |
| if (j.has("ServiceName")) { |
| builder.setServiceName(getStringOrNull(j, "ServiceName")); |
| } |
| |
| if (j.has("ServiceSpecificInfo")) { |
| String ssi = getStringOrNull(j, "ServiceSpecificInfo"); |
| if (ssi != null) { |
| builder.setServiceSpecificInfo(ssi.getBytes()); |
| } |
| } |
| |
| if (j.has("MatchFilter")) { |
| byte[] bytes = Base64.decode( |
| j.getString("MatchFilter").getBytes(StandardCharsets.UTF_8), Base64.DEFAULT); |
| List<byte[]> mf = new TlvBufferUtils.TlvIterable(0, 1, bytes).toList(); |
| builder.setMatchFilter(mf); |
| |
| } |
| |
| if (!j.isNull("MatchFilterList")) { |
| builder.setMatchFilter(getMatchFilter(j.getJSONArray("MatchFilterList"))); |
| } |
| |
| if (j.has("DiscoveryType")) { |
| builder.setPublishType(j.getInt("DiscoveryType")); |
| } |
| if (j.has("TtlSec")) { |
| builder.setTtlSec(j.getInt("TtlSec")); |
| } |
| if (j.has("TerminateNotificationEnabled")) { |
| builder.setTerminateNotificationEnabled(j.getBoolean("TerminateNotificationEnabled")); |
| } |
| if (j.has("RangingEnabled")) { |
| builder.setRangingEnabled(j.getBoolean("RangingEnabled")); |
| } |
| if (SdkLevel.isAtLeastT() && j.has("InstantModeEnabled")) { |
| builder.setInstantCommunicationModeEnabled(true, |
| Objects.equals(j.getString("InstantModeEnabled"), "5G") |
| ? WifiScanner.WIFI_BAND_5_GHZ : WifiScanner.WIFI_BAND_24_GHZ); |
| } |
| |
| return builder.build(); |
| } |
| |
| private static SubscribeConfig getSubscribeConfig(JSONObject j) throws JSONException { |
| if (j == null) { |
| return null; |
| } |
| |
| SubscribeConfig.Builder builder = new SubscribeConfig.Builder(); |
| |
| if (j.has("ServiceName")) { |
| builder.setServiceName(j.getString("ServiceName")); |
| } |
| |
| if (j.has("ServiceSpecificInfo")) { |
| String ssi = getStringOrNull(j, "ServiceSpecificInfo"); |
| if (ssi != null) { |
| builder.setServiceSpecificInfo(ssi.getBytes()); |
| } |
| } |
| |
| if (j.has("MatchFilter")) { |
| byte[] bytes = Base64.decode( |
| j.getString("MatchFilter").getBytes(StandardCharsets.UTF_8), Base64.DEFAULT); |
| List<byte[]> mf = new TlvBufferUtils.TlvIterable(0, 1, bytes).toList(); |
| builder.setMatchFilter(mf); |
| } |
| |
| if (!j.isNull("MatchFilterList")) { |
| builder.setMatchFilter(getMatchFilter(j.getJSONArray("MatchFilterList"))); |
| } |
| |
| if (j.has("DiscoveryType")) { |
| builder.setSubscribeType(j.getInt("DiscoveryType")); |
| } |
| if (j.has("TtlSec")) { |
| builder.setTtlSec(j.getInt("TtlSec")); |
| } |
| if (j.has("TerminateNotificationEnabled")) { |
| builder.setTerminateNotificationEnabled(j.getBoolean("TerminateNotificationEnabled")); |
| } |
| if (j.has("MinDistanceMm")) { |
| builder.setMinDistanceMm(j.getInt("MinDistanceMm")); |
| } |
| if (j.has("MaxDistanceMm")) { |
| builder.setMaxDistanceMm(j.getInt("MaxDistanceMm")); |
| } |
| if (SdkLevel.isAtLeastT() && j.has("InstantModeEnabled")) { |
| builder.setInstantCommunicationModeEnabled(true, |
| Objects.equals(j.getString("InstantModeEnabled"), "5G") |
| ? WifiScanner.WIFI_BAND_5_GHZ : WifiScanner.WIFI_BAND_24_GHZ); |
| } |
| |
| return builder.build(); |
| } |
| |
| public WifiAwareManagerFacade(FacadeManager manager) { |
| super(manager); |
| mService = manager.getService(); |
| |
| mMgr = (WifiAwareManager) mService.getSystemService(Context.WIFI_AWARE_SERVICE); |
| |
| mEventFacade = manager.getReceiver(EventFacade.class); |
| |
| mStateChangedReceiver = new WifiAwareStateChangedReceiver(); |
| IntentFilter filter = new IntentFilter(WifiAwareManager.ACTION_WIFI_AWARE_STATE_CHANGED); |
| mService.registerReceiver(mStateChangedReceiver, filter); |
| } |
| |
| @Override |
| public void shutdown() { |
| wifiAwareDestroyAll(); |
| mService.unregisterReceiver(mStateChangedReceiver); |
| } |
| |
| @Rpc(description = "Does the device support the Wi-Fi Aware feature?") |
| public Boolean doesDeviceSupportWifiAwareFeature() { |
| return mService.getPackageManager().hasSystemFeature(PackageManager.FEATURE_WIFI_AWARE); |
| } |
| |
| @Rpc(description = "Is Aware Usage Enabled?") |
| public Boolean wifiIsAwareAvailable() throws RemoteException { |
| synchronized (mLock) { |
| return mMgr.isAvailable(); |
| } |
| } |
| |
| @Rpc(description = "Destroy all Aware sessions and discovery sessions") |
| public void wifiAwareDestroyAll() { |
| synchronized (mLock) { |
| for (int i = 0; i < mSessions.size(); ++i) { |
| mSessions.valueAt(i).close(); |
| } |
| mSessions.clear(); |
| |
| /* discovery sessions automatically destroyed when containing Aware sessions |
| * destroyed */ |
| mDiscoverySessions.clear(); |
| |
| mMessageStartTime.clear(); |
| } |
| } |
| |
| @Rpc(description = "Attach to Aware.") |
| public Integer wifiAwareAttach( |
| @RpcParameter(name = "identityCb", |
| description = "Controls whether an identity callback is provided") |
| @RpcOptional Boolean identityCb, |
| @RpcParameter(name = "awareConfig", |
| description = "The session configuration, or null for default config") |
| @RpcOptional JSONObject awareConfig, |
| @RpcParameter(name = "useIdInCallbackEvent", |
| description = |
| "Specifies whether the callback events should be decorated with session Id") |
| @RpcOptional Boolean useIdInCallbackEvent) |
| throws RemoteException, JSONException { |
| synchronized (mLock) { |
| int sessionId = getNextSessionId(); |
| boolean useIdInCallbackEventName = |
| (useIdInCallbackEvent != null) ? useIdInCallbackEvent : false; |
| mMgr.attach(null, getConfigRequest(awareConfig), |
| new AwareAttachCallbackPostsEvents(sessionId, useIdInCallbackEventName), |
| (identityCb != null && identityCb.booleanValue()) |
| ? new AwareIdentityChangeListenerPostsEvents(sessionId, |
| useIdInCallbackEventName) : null); |
| return sessionId; |
| } |
| } |
| |
| @Rpc(description = "Destroy a Aware session.") |
| public void wifiAwareDestroy( |
| @RpcParameter(name = "clientId", description = "The client ID returned when a connection was created") Integer clientId) |
| throws RemoteException, JSONException { |
| WifiAwareSession session; |
| synchronized (mLock) { |
| session = mSessions.get(clientId); |
| } |
| if (session == null) { |
| throw new IllegalStateException( |
| "Calling WifiAwareDisconnect before session (client ID " + clientId |
| + ") is ready/or already disconnected"); |
| } |
| session.close(); |
| } |
| |
| @Rpc(description = "Publish.") |
| public Integer wifiAwarePublish( |
| @RpcParameter(name = "clientId", description = "The client ID returned when a connection was created") Integer clientId, |
| @RpcParameter(name = "publishConfig") JSONObject publishConfig, |
| @RpcParameter(name = "useIdInCallbackEvent", |
| description = |
| "Specifies whether the callback events should be decorated with session Id") |
| @RpcOptional Boolean useIdInCallbackEvent) |
| throws RemoteException, JSONException { |
| synchronized (mLock) { |
| WifiAwareSession session = mSessions.get(clientId); |
| if (session == null) { |
| throw new IllegalStateException( |
| "Calling WifiAwarePublish before session (client ID " + clientId |
| + ") is ready/or already disconnected"); |
| } |
| boolean useIdInCallbackEventName = |
| (useIdInCallbackEvent != null) ? useIdInCallbackEvent : false; |
| |
| int discoverySessionId = getNextDiscoverySessionId(); |
| session.publish(getPublishConfig(publishConfig), |
| new AwareDiscoverySessionCallbackPostsEvents(discoverySessionId, |
| useIdInCallbackEventName), null); |
| return discoverySessionId; |
| } |
| } |
| |
| @Rpc(description = "Update Publish.") |
| public void wifiAwareUpdatePublish( |
| @RpcParameter(name = "sessionId", description = "The discovery session ID") |
| Integer sessionId, |
| @RpcParameter(name = "publishConfig", description = "Publish configuration") |
| JSONObject publishConfig) |
| throws RemoteException, JSONException { |
| synchronized (mLock) { |
| DiscoverySession session = mDiscoverySessions.get(sessionId); |
| if (session == null) { |
| throw new IllegalStateException( |
| "Calling wifiAwareUpdatePublish before session (session ID " |
| + sessionId + ") is ready"); |
| } |
| if (!(session instanceof PublishDiscoverySession)) { |
| throw new IllegalArgumentException( |
| "Calling wifiAwareUpdatePublish with a subscribe session ID"); |
| } |
| ((PublishDiscoverySession) session).updatePublish(getPublishConfig(publishConfig)); |
| } |
| } |
| |
| @Rpc(description = "Subscribe.") |
| public Integer wifiAwareSubscribe( |
| @RpcParameter(name = "clientId", description = "The client ID returned when a connection was created") Integer clientId, |
| @RpcParameter(name = "subscribeConfig") JSONObject subscribeConfig, |
| @RpcParameter(name = "useIdInCallbackEvent", |
| description = |
| "Specifies whether the callback events should be decorated with session Id") |
| @RpcOptional Boolean useIdInCallbackEvent) |
| throws RemoteException, JSONException { |
| synchronized (mLock) { |
| WifiAwareSession session = mSessions.get(clientId); |
| if (session == null) { |
| throw new IllegalStateException( |
| "Calling WifiAwareSubscribe before session (client ID " + clientId |
| + ") is ready/or already disconnected"); |
| } |
| boolean useIdInCallbackEventName = |
| (useIdInCallbackEvent != null) ? useIdInCallbackEvent : false; |
| |
| int discoverySessionId = getNextDiscoverySessionId(); |
| session.subscribe(getSubscribeConfig(subscribeConfig), |
| new AwareDiscoverySessionCallbackPostsEvents(discoverySessionId, |
| useIdInCallbackEventName), null); |
| return discoverySessionId; |
| } |
| } |
| |
| @Rpc(description = "Update Subscribe.") |
| public void wifiAwareUpdateSubscribe( |
| @RpcParameter(name = "sessionId", description = "The discovery session ID") |
| Integer sessionId, |
| @RpcParameter(name = "subscribeConfig", description = "Subscribe configuration") |
| JSONObject subscribeConfig) |
| throws RemoteException, JSONException { |
| synchronized (mLock) { |
| DiscoverySession session = mDiscoverySessions.get(sessionId); |
| if (session == null) { |
| throw new IllegalStateException( |
| "Calling wifiAwareUpdateSubscribe before session (session ID " |
| + sessionId + ") is ready"); |
| } |
| if (!(session instanceof SubscribeDiscoverySession)) { |
| throw new IllegalArgumentException( |
| "Calling wifiAwareUpdateSubscribe with a publish session ID"); |
| } |
| ((SubscribeDiscoverySession) session) |
| .updateSubscribe(getSubscribeConfig(subscribeConfig)); |
| } |
| } |
| |
| @Rpc(description = "Destroy a discovery Session.") |
| public void wifiAwareDestroyDiscoverySession( |
| @RpcParameter(name = "sessionId", description = "The discovery session ID returned when session was created using publish or subscribe") Integer sessionId) |
| throws RemoteException { |
| synchronized (mLock) { |
| DiscoverySession session = mDiscoverySessions.get(sessionId); |
| if (session == null) { |
| throw new IllegalStateException( |
| "Calling WifiAwareTerminateSession before session (session ID " |
| + sessionId + ") is ready"); |
| } |
| session.close(); |
| mDiscoverySessions.remove(sessionId); |
| } |
| } |
| |
| @Rpc(description = "Send peer-to-peer Aware message") |
| public void wifiAwareSendMessage( |
| @RpcParameter(name = "sessionId", description = "The session ID returned when session" |
| + " was created using publish or subscribe") Integer sessionId, |
| @RpcParameter(name = "peerId", description = "The ID of the peer being communicated " |
| + "with. Obtained from a previous message or match session.") Integer peerId, |
| @RpcParameter(name = "messageId", description = "Arbitrary handle used for " |
| + "identification of the message in the message status callbacks") |
| Integer messageId, |
| @RpcParameter(name = "message") String message, |
| @RpcParameter(name = "retryCount", description = "Number of retries (0 for none) if " |
| + "transmission fails due to no ACK reception") Integer retryCount) |
| throws RemoteException { |
| DiscoverySession session; |
| synchronized (mLock) { |
| session = mDiscoverySessions.get(sessionId); |
| } |
| if (session == null) { |
| throw new IllegalStateException( |
| "Calling WifiAwareSendMessage before session (session ID " + sessionId |
| + " is ready"); |
| } |
| byte[] bytes = null; |
| if (message != null) { |
| bytes = message.getBytes(); |
| } |
| |
| synchronized (mLock) { |
| mMessageStartTime.put(messageId, System.currentTimeMillis()); |
| } |
| session.sendMessage(new PeerHandle(peerId), messageId, bytes, retryCount); |
| } |
| |
| @Rpc(description = "Create a network specifier to be used when specifying a Aware network request") |
| public String wifiAwareCreateNetworkSpecifier( |
| @RpcParameter(name = "sessionId", description = "The session ID returned when session was created using publish or subscribe") |
| Integer sessionId, |
| @RpcParameter(name = "peerId", description = "The ID of the peer (obtained through OnMatch or OnMessageReceived") |
| Integer peerId, |
| @RpcParameter(name = "passphrase", |
| description = "Passphrase of the data-path. Optional, can be empty/null.") |
| @RpcOptional String passphrase, |
| @RpcParameter(name = "pmk", |
| description = "PMK of the data-path (base64 encoded). Optional, can be empty/null.") |
| @RpcOptional String pmk, |
| @RpcParameter(name = "port", description = "Port") @RpcOptional Integer port, |
| @RpcParameter(name = "transportProtocol", description = "Transport protocol") |
| @RpcOptional Integer transportProtocol) throws JSONException { |
| DiscoverySession session; |
| synchronized (mLock) { |
| session = mDiscoverySessions.get(sessionId); |
| } |
| if (session == null) { |
| throw new IllegalStateException( |
| "Calling wifiAwareCreateNetworkSpecifier before session (session ID " |
| + sessionId + " is ready"); |
| } |
| PeerHandle peerHandle = null; |
| if (peerId != null) { |
| peerHandle = new PeerHandle(peerId); |
| } |
| byte[] pmkDecoded = null; |
| if (!TextUtils.isEmpty(pmk)) { |
| pmkDecoded = Base64.decode(pmk, Base64.DEFAULT); |
| } |
| |
| WifiAwareNetworkSpecifier ns = new WifiAwareNetworkSpecifier( |
| (peerHandle == null) ? WifiAwareNetworkSpecifier.NETWORK_SPECIFIER_TYPE_IB_ANY_PEER |
| : WifiAwareNetworkSpecifier.NETWORK_SPECIFIER_TYPE_IB, |
| session instanceof SubscribeDiscoverySession |
| ? WifiAwareManager.WIFI_AWARE_DATA_PATH_ROLE_INITIATOR |
| : WifiAwareManager.WIFI_AWARE_DATA_PATH_ROLE_RESPONDER, |
| session.getClientId(), |
| session.getSessionId(), |
| peerHandle != null ? peerHandle.peerId : 0, // 0 is an invalid peer ID |
| null, // peerMac (not used in this method) |
| pmkDecoded, |
| passphrase, |
| port == null ? 0 : port.intValue(), |
| transportProtocol == null ? -1 : transportProtocol.intValue()); |
| |
| return getJsonString(ns); |
| } |
| |
| @Rpc(description = "Create a network specifier to be used when specifying an OOB Aware network request") |
| public String wifiAwareCreateNetworkSpecifierOob( |
| @RpcParameter(name = "clientId", |
| description = "The client ID") |
| Integer clientId, |
| @RpcParameter(name = "role", description = "The role: INITIATOR(0), RESPONDER(1)") |
| Integer role, |
| @RpcParameter(name = "peerMac", |
| description = "The MAC address of the peer") |
| String peerMac, |
| @RpcParameter(name = "passphrase", |
| description = "Passphrase of the data-path. Optional, can be empty/null.") |
| @RpcOptional String passphrase, |
| @RpcParameter(name = "pmk", |
| description = "PMK of the data-path (base64). Optional, can be empty/null.") |
| @RpcOptional String pmk) throws JSONException { |
| WifiAwareSession session; |
| synchronized (mLock) { |
| session = mSessions.get(clientId); |
| } |
| if (session == null) { |
| throw new IllegalStateException( |
| "Calling wifiAwareCreateNetworkSpecifierOob before session (client ID " |
| + clientId + " is ready"); |
| } |
| byte[] peerMacBytes = null; |
| if (peerMac != null) { |
| peerMacBytes = HexEncoding.decode(peerMac.toCharArray(), false); |
| } |
| byte[] pmkDecoded = null; |
| if (!TextUtils.isEmpty(pmk)) { |
| pmkDecoded = Base64.decode(pmk, Base64.DEFAULT); |
| } |
| |
| WifiAwareNetworkSpecifier ns = new WifiAwareNetworkSpecifier( |
| (peerMacBytes == null) ? |
| WifiAwareNetworkSpecifier.NETWORK_SPECIFIER_TYPE_OOB_ANY_PEER |
| : WifiAwareNetworkSpecifier.NETWORK_SPECIFIER_TYPE_OOB, |
| role, |
| session.getClientId(), |
| 0, // 0 is an invalid session ID |
| 0, // 0 is an invalid peer ID |
| peerMacBytes, |
| pmkDecoded, |
| passphrase, |
| 0, // no port for OOB |
| -1); // no transport protocol for OOB |
| |
| return getJsonString(ns); |
| } |
| |
| private class AwareAttachCallbackPostsEvents extends AttachCallback { |
| private int mSessionId; |
| private long mCreateTimestampMs; |
| private boolean mUseIdInCallbackEventName; |
| |
| public AwareAttachCallbackPostsEvents(int sessionId, boolean useIdInCallbackEventName) { |
| mSessionId = sessionId; |
| mCreateTimestampMs = System.currentTimeMillis(); |
| mUseIdInCallbackEventName = useIdInCallbackEventName; |
| } |
| |
| @Override |
| public void onAttached(WifiAwareSession session) { |
| synchronized (mLock) { |
| mSessions.put(mSessionId, session); |
| } |
| |
| Bundle mResults = new Bundle(); |
| mResults.putInt("sessionId", mSessionId); |
| mResults.putLong("latencyMs", System.currentTimeMillis() - mCreateTimestampMs); |
| mResults.putLong("timestampMs", System.currentTimeMillis()); |
| if (mUseIdInCallbackEventName) { |
| mEventFacade.postEvent("WifiAwareOnAttached_" + mSessionId, mResults); |
| } else { |
| mEventFacade.postEvent("WifiAwareOnAttached", mResults); |
| } |
| } |
| |
| @Override |
| public void onAttachFailed() { |
| Bundle mResults = new Bundle(); |
| mResults.putInt("sessionId", mSessionId); |
| mResults.putLong("latencyMs", System.currentTimeMillis() - mCreateTimestampMs); |
| if (mUseIdInCallbackEventName) { |
| mEventFacade.postEvent("WifiAwareOnAttachFailed_" + mSessionId, mResults); |
| } else { |
| mEventFacade.postEvent("WifiAwareOnAttachFailed", mResults); |
| } |
| } |
| } |
| |
| private class AwareIdentityChangeListenerPostsEvents extends IdentityChangedListener { |
| private int mSessionId; |
| private boolean mUseIdInCallbackEventName; |
| |
| public AwareIdentityChangeListenerPostsEvents(int sessionId, |
| boolean useIdInCallbackEventName) { |
| mSessionId = sessionId; |
| mUseIdInCallbackEventName = useIdInCallbackEventName; |
| } |
| |
| @Override |
| public void onIdentityChanged(byte[] mac) { |
| Bundle mResults = new Bundle(); |
| mResults.putInt("sessionId", mSessionId); |
| mResults.putString("mac", String.valueOf(HexEncoding.encode(mac))); |
| mResults.putLong("timestampMs", System.currentTimeMillis()); |
| if (mUseIdInCallbackEventName) { |
| mEventFacade.postEvent("WifiAwareOnIdentityChanged_" + mSessionId, mResults); |
| } else { |
| mEventFacade.postEvent("WifiAwareOnIdentityChanged", mResults); |
| } |
| } |
| } |
| |
| private class AwareDiscoverySessionCallbackPostsEvents extends |
| DiscoverySessionCallback { |
| private int mDiscoverySessionId; |
| private boolean mUseIdInCallbackEventName; |
| private long mCreateTimestampMs; |
| |
| public AwareDiscoverySessionCallbackPostsEvents(int discoverySessionId, |
| boolean useIdInCallbackEventName) { |
| mDiscoverySessionId = discoverySessionId; |
| mUseIdInCallbackEventName = useIdInCallbackEventName; |
| mCreateTimestampMs = System.currentTimeMillis(); |
| } |
| |
| private void postEvent(String eventName, Bundle results) { |
| String finalEventName = eventName; |
| if (mUseIdInCallbackEventName) { |
| finalEventName += "_" + mDiscoverySessionId; |
| } |
| |
| mEventFacade.postEvent(finalEventName, results); |
| } |
| |
| @Override |
| public void onPublishStarted(PublishDiscoverySession discoverySession) { |
| synchronized (mLock) { |
| mDiscoverySessions.put(mDiscoverySessionId, discoverySession); |
| } |
| |
| Bundle mResults = new Bundle(); |
| mResults.putInt("discoverySessionId", mDiscoverySessionId); |
| mResults.putLong("latencyMs", System.currentTimeMillis() - mCreateTimestampMs); |
| mResults.putLong("timestampMs", System.currentTimeMillis()); |
| postEvent("WifiAwareSessionOnPublishStarted", mResults); |
| } |
| |
| @Override |
| public void onSubscribeStarted(SubscribeDiscoverySession discoverySession) { |
| synchronized (mLock) { |
| mDiscoverySessions.put(mDiscoverySessionId, discoverySession); |
| } |
| |
| Bundle mResults = new Bundle(); |
| mResults.putInt("discoverySessionId", mDiscoverySessionId); |
| mResults.putLong("latencyMs", System.currentTimeMillis() - mCreateTimestampMs); |
| mResults.putLong("timestampMs", System.currentTimeMillis()); |
| postEvent("WifiAwareSessionOnSubscribeStarted", mResults); |
| } |
| |
| @Override |
| public void onSessionConfigUpdated() { |
| Bundle mResults = new Bundle(); |
| mResults.putInt("discoverySessionId", mDiscoverySessionId); |
| postEvent("WifiAwareSessionOnSessionConfigUpdated", mResults); |
| } |
| |
| @Override |
| public void onSessionConfigFailed() { |
| Bundle mResults = new Bundle(); |
| mResults.putInt("discoverySessionId", mDiscoverySessionId); |
| postEvent("WifiAwareSessionOnSessionConfigFailed", mResults); |
| } |
| |
| @Override |
| public void onSessionTerminated() { |
| Bundle mResults = new Bundle(); |
| mResults.putInt("discoverySessionId", mDiscoverySessionId); |
| postEvent("WifiAwareSessionOnSessionTerminated", mResults); |
| } |
| |
| private Bundle createServiceDiscoveredBaseBundle(PeerHandle peerHandle, |
| byte[] serviceSpecificInfo, List<byte[]> matchFilter) { |
| Bundle mResults = new Bundle(); |
| mResults.putInt("discoverySessionId", mDiscoverySessionId); |
| mResults.putInt("peerId", peerHandle.peerId); |
| mResults.putByteArray("serviceSpecificInfo", serviceSpecificInfo); |
| mResults.putByteArray("matchFilter", new TlvBufferUtils.TlvConstructor(0, |
| 1).allocateAndPut(matchFilter).getArray()); |
| ArrayList<String> matchFilterStrings = new ArrayList<>(matchFilter.size()); |
| for (byte[] be: matchFilter) { |
| matchFilterStrings.add(Base64.encodeToString(be, Base64.DEFAULT)); |
| } |
| mResults.putStringArrayList("matchFilterList", matchFilterStrings); |
| mResults.putLong("timestampMs", System.currentTimeMillis()); |
| return mResults; |
| } |
| |
| @Override |
| public void onServiceDiscovered(PeerHandle peerHandle, |
| byte[] serviceSpecificInfo, List<byte[]> matchFilter) { |
| Bundle mResults = createServiceDiscoveredBaseBundle(peerHandle, serviceSpecificInfo, |
| matchFilter); |
| postEvent("WifiAwareSessionOnServiceDiscovered", mResults); |
| } |
| |
| @Override |
| public void onServiceDiscoveredWithinRange(PeerHandle peerHandle, |
| byte[] serviceSpecificInfo, |
| List<byte[]> matchFilter, int distanceMm) { |
| Bundle mResults = createServiceDiscoveredBaseBundle(peerHandle, serviceSpecificInfo, |
| matchFilter); |
| mResults.putInt("distanceMm", distanceMm); |
| postEvent("WifiAwareSessionOnServiceDiscovered", mResults); |
| } |
| |
| @Override |
| public void onMessageSendSucceeded(int messageId) { |
| Bundle mResults = new Bundle(); |
| mResults.putInt("discoverySessionId", mDiscoverySessionId); |
| mResults.putInt("messageId", messageId); |
| synchronized (mLock) { |
| Long startTime = mMessageStartTime.get(messageId); |
| if (startTime != null) { |
| mResults.putLong("latencyMs", |
| System.currentTimeMillis() - startTime.longValue()); |
| mMessageStartTime.remove(messageId); |
| } |
| } |
| postEvent("WifiAwareSessionOnMessageSent", mResults); |
| } |
| |
| @Override |
| public void onMessageSendFailed(int messageId) { |
| Bundle mResults = new Bundle(); |
| mResults.putInt("discoverySessionId", mDiscoverySessionId); |
| mResults.putInt("messageId", messageId); |
| synchronized (mLock) { |
| Long startTime = mMessageStartTime.get(messageId); |
| if (startTime != null) { |
| mResults.putLong("latencyMs", |
| System.currentTimeMillis() - startTime.longValue()); |
| mMessageStartTime.remove(messageId); |
| } |
| } |
| postEvent("WifiAwareSessionOnMessageSendFailed", mResults); |
| } |
| |
| @Override |
| public void onMessageReceived(PeerHandle peerHandle, byte[] message) { |
| Bundle mResults = new Bundle(); |
| mResults.putInt("discoverySessionId", mDiscoverySessionId); |
| mResults.putInt("peerId", peerHandle.peerId); |
| mResults.putByteArray("message", message); // TODO: base64 |
| mResults.putString("messageAsString", new String(message)); |
| postEvent("WifiAwareSessionOnMessageReceived", mResults); |
| } |
| |
| @Override |
| public void onServiceLost(PeerHandle peerHandle, @WifiAwareManager.DiscoveryLostReasonCode |
| int reason) { |
| Bundle mResults = new Bundle(); |
| mResults.putInt("discoverySessionId", mDiscoverySessionId); |
| mResults.putInt("peerId", peerHandle.peerId); |
| mResults.putInt("lostReason", reason); |
| postEvent("WifiAwareSessionOnServiceLost", mResults); |
| } |
| } |
| |
| class WifiAwareRangingListener implements RttManager.RttListener { |
| private int mCallbackId; |
| private int mSessionId; |
| |
| public WifiAwareRangingListener(int callbackId, int sessionId) { |
| mCallbackId = callbackId; |
| mSessionId = sessionId; |
| } |
| |
| @Override |
| public void onSuccess(RttResult[] results) { |
| Bundle bundle = new Bundle(); |
| bundle.putInt("callbackId", mCallbackId); |
| bundle.putInt("sessionId", mSessionId); |
| |
| Parcelable[] resultBundles = new Parcelable[results.length]; |
| for (int i = 0; i < results.length; i++) { |
| resultBundles[i] = WifiRttManagerFacade.RangingListener.packRttResult(results[i]); |
| } |
| bundle.putParcelableArray("Results", resultBundles); |
| |
| mEventFacade.postEvent("WifiAwareRangingListenerOnSuccess", bundle); |
| } |
| |
| @Override |
| public void onFailure(int reason, String description) { |
| Bundle bundle = new Bundle(); |
| bundle.putInt("callbackId", mCallbackId); |
| bundle.putInt("sessionId", mSessionId); |
| bundle.putInt("reason", reason); |
| bundle.putString("description", description); |
| mEventFacade.postEvent("WifiAwareRangingListenerOnFailure", bundle); |
| } |
| |
| @Override |
| public void onAborted() { |
| Bundle bundle = new Bundle(); |
| bundle.putInt("callbackId", mCallbackId); |
| bundle.putInt("sessionId", mSessionId); |
| mEventFacade.postEvent("WifiAwareRangingListenerOnAborted", bundle); |
| } |
| |
| } |
| |
| class WifiAwareStateChangedReceiver extends BroadcastReceiver { |
| @Override |
| public void onReceive(Context c, Intent intent) { |
| boolean isAvailable = mMgr.isAvailable(); |
| if (!isAvailable) { |
| wifiAwareDestroyAll(); |
| } |
| mEventFacade.postEvent(isAvailable ? "WifiAwareAvailable" : "WifiAwareNotAvailable", |
| new Bundle()); |
| } |
| } |
| } |