Home | History | Annotate | Download | only in wifi
      1 /*
      2  * Copyright (C) 2017 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 com.googlecode.android_scripting.facade.wifi;
     18 
     19 import android.app.Service;
     20 import android.content.BroadcastReceiver;
     21 import android.content.Context;
     22 import android.content.Intent;
     23 import android.content.IntentFilter;
     24 import android.content.pm.PackageManager;
     25 import android.net.NetworkSpecifier;
     26 import android.net.wifi.RttManager;
     27 import android.net.wifi.RttManager.RttResult;
     28 import android.net.wifi.aware.AttachCallback;
     29 import android.net.wifi.aware.ConfigRequest;
     30 import android.net.wifi.aware.DiscoverySession;
     31 import android.net.wifi.aware.DiscoverySessionCallback;
     32 import android.net.wifi.aware.IdentityChangedListener;
     33 import android.net.wifi.aware.PeerHandle;
     34 import android.net.wifi.aware.PublishConfig;
     35 import android.net.wifi.aware.PublishDiscoverySession;
     36 import android.net.wifi.aware.SubscribeConfig;
     37 import android.net.wifi.aware.SubscribeDiscoverySession;
     38 import android.net.wifi.aware.TlvBufferUtils;
     39 import android.net.wifi.aware.WifiAwareManager;
     40 import android.net.wifi.aware.WifiAwareNetworkSpecifier;
     41 import android.net.wifi.aware.WifiAwareSession;
     42 import android.os.Bundle;
     43 import android.os.Parcelable;
     44 import android.os.Process;
     45 import android.os.RemoteException;
     46 import android.text.TextUtils;
     47 import android.util.Base64;
     48 import android.util.SparseArray;
     49 
     50 import com.android.internal.annotations.GuardedBy;
     51 
     52 import libcore.util.HexEncoding;
     53 
     54 import com.googlecode.android_scripting.facade.EventFacade;
     55 import com.googlecode.android_scripting.facade.FacadeManager;
     56 import com.googlecode.android_scripting.jsonrpc.RpcReceiver;
     57 import com.googlecode.android_scripting.rpc.Rpc;
     58 import com.googlecode.android_scripting.rpc.RpcOptional;
     59 import com.googlecode.android_scripting.rpc.RpcParameter;
     60 
     61 import org.json.JSONArray;
     62 import org.json.JSONException;
     63 import org.json.JSONObject;
     64 
     65 import java.nio.charset.StandardCharsets;
     66 import java.util.ArrayList;
     67 import java.util.List;
     68 
     69 /**
     70  * WifiAwareManager functions.
     71  */
     72 public class WifiAwareManagerFacade extends RpcReceiver {
     73     private final Service mService;
     74     private final EventFacade mEventFacade;
     75     private final WifiAwareStateChangedReceiver mStateChangedReceiver;
     76 
     77     private final Object mLock = new Object(); // lock access to the following vars
     78 
     79     @GuardedBy("mLock")
     80     private WifiAwareManager mMgr;
     81 
     82     @GuardedBy("mLock")
     83     private int mNextDiscoverySessionId = 1;
     84     @GuardedBy("mLock")
     85     private SparseArray<DiscoverySession> mDiscoverySessions = new SparseArray<>();
     86     private int getNextDiscoverySessionId() {
     87         synchronized (mLock) {
     88             return mNextDiscoverySessionId++;
     89         }
     90     }
     91 
     92     @GuardedBy("mLock")
     93     private int mNextSessionId = 1;
     94     @GuardedBy("mLock")
     95     private SparseArray<WifiAwareSession> mSessions = new SparseArray<>();
     96     private int getNextSessionId() {
     97         synchronized (mLock) {
     98             return mNextSessionId++;
     99         }
    100     }
    101 
    102     @GuardedBy("mLock")
    103     private SparseArray<Long> mMessageStartTime = new SparseArray<>();
    104 
    105     private static final String NS_KEY_TYPE = "type";
    106     private static final String NS_KEY_ROLE = "role";
    107     private static final String NS_KEY_CLIENT_ID = "client_id";
    108     private static final String NS_KEY_SESSION_ID = "session_id";
    109     private static final String NS_KEY_PEER_ID = "peer_id";
    110     private static final String NS_KEY_PEER_MAC = "peer_mac";
    111     private static final String NS_KEY_PMK = "pmk";
    112     private static final String NS_KEY_PASSPHRASE = "passphrase";
    113 
    114     private static String getJsonString(WifiAwareNetworkSpecifier ns) throws JSONException {
    115         JSONObject j = new JSONObject();
    116 
    117         j.put(NS_KEY_TYPE, ns.type);
    118         j.put(NS_KEY_ROLE, ns.role);
    119         j.put(NS_KEY_CLIENT_ID, ns.clientId);
    120         j.put(NS_KEY_SESSION_ID, ns.sessionId);
    121         j.put(NS_KEY_PEER_ID, ns.peerId);
    122         if (ns.peerMac != null) {
    123             j.put(NS_KEY_PEER_MAC, Base64.encodeToString(ns.peerMac, Base64.DEFAULT));
    124         }
    125         if (ns.pmk != null) {
    126             j.put(NS_KEY_PMK, Base64.encodeToString(ns.pmk, Base64.DEFAULT));
    127         }
    128         if (ns.passphrase != null) {
    129             j.put(NS_KEY_PASSPHRASE, ns.passphrase);
    130         }
    131 
    132         return j.toString();
    133     }
    134 
    135     public static NetworkSpecifier getNetworkSpecifier(JSONObject j) throws JSONException {
    136         if (j == null) {
    137             return null;
    138         }
    139 
    140         int type = 0, role = 0, clientId = 0, sessionId = 0, peerId = 0;
    141         byte[] peerMac = null;
    142         byte[] pmk = null;
    143         String passphrase = null;
    144 
    145         if (j.has(NS_KEY_TYPE)) {
    146             type = j.getInt((NS_KEY_TYPE));
    147         }
    148         if (j.has(NS_KEY_ROLE)) {
    149             role = j.getInt((NS_KEY_ROLE));
    150         }
    151         if (j.has(NS_KEY_CLIENT_ID)) {
    152             clientId = j.getInt((NS_KEY_CLIENT_ID));
    153         }
    154         if (j.has(NS_KEY_SESSION_ID)) {
    155             sessionId = j.getInt((NS_KEY_SESSION_ID));
    156         }
    157         if (j.has(NS_KEY_PEER_ID)) {
    158             peerId = j.getInt((NS_KEY_PEER_ID));
    159         }
    160         if (j.has(NS_KEY_PEER_MAC)) {
    161             peerMac = Base64.decode(j.getString(NS_KEY_PEER_MAC), Base64.DEFAULT);
    162         }
    163         if (j.has(NS_KEY_PMK)) {
    164             pmk = Base64.decode(j.getString(NS_KEY_PMK), Base64.DEFAULT);
    165         }
    166         if (j.has(NS_KEY_PASSPHRASE)) {
    167             passphrase = j.getString((NS_KEY_PASSPHRASE));
    168         }
    169 
    170         return new WifiAwareNetworkSpecifier(type, role, clientId, sessionId, peerId, peerMac, pmk,
    171                 passphrase, Process.myUid());
    172     }
    173 
    174     private static String getStringOrNull(JSONObject j, String name) throws JSONException {
    175         if (j.isNull(name)) {
    176             return null;
    177         }
    178         return j.getString(name);
    179     }
    180 
    181     private static ConfigRequest getConfigRequest(JSONObject j) throws JSONException {
    182         if (j == null) {
    183             return null;
    184         }
    185 
    186         ConfigRequest.Builder builder = new ConfigRequest.Builder();
    187 
    188         if (j.has("Support5gBand")) {
    189             builder.setSupport5gBand(j.getBoolean("Support5gBand"));
    190         }
    191         if (j.has("MasterPreference")) {
    192             builder.setMasterPreference(j.getInt("MasterPreference"));
    193         }
    194         if (j.has("ClusterLow")) {
    195             builder.setClusterLow(j.getInt("ClusterLow"));
    196         }
    197         if (j.has("ClusterHigh")) {
    198             builder.setClusterHigh(j.getInt("ClusterHigh"));
    199         }
    200         if (j.has("DiscoveryWindowInterval")) {
    201             JSONArray interval = j.getJSONArray("DiscoveryWindowInterval");
    202             if (interval.length() != 2) {
    203                 throw new JSONException(
    204                         "Expect 'DiscoveryWindowInterval' to be an array with 2 elements!");
    205             }
    206             int intervalValue = interval.getInt(ConfigRequest.NAN_BAND_24GHZ);
    207             if (intervalValue != ConfigRequest.DW_INTERVAL_NOT_INIT) {
    208                 builder.setDiscoveryWindowInterval(ConfigRequest.NAN_BAND_24GHZ, intervalValue);
    209             }
    210             intervalValue = interval.getInt(ConfigRequest.NAN_BAND_5GHZ);
    211             if (intervalValue != ConfigRequest.DW_INTERVAL_NOT_INIT) {
    212                 builder.setDiscoveryWindowInterval(ConfigRequest.NAN_BAND_5GHZ, intervalValue);
    213             }
    214         }
    215 
    216         return builder.build();
    217     }
    218 
    219     private static List<byte[]> getMatchFilter(JSONArray ja) throws JSONException {
    220         List<byte[]> la = new ArrayList<>();
    221         for (int i = 0; i < ja.length(); ++i) {
    222             la.add(Base64.decode(ja.getString(i).getBytes(StandardCharsets.UTF_8), Base64.DEFAULT));
    223         }
    224         return la;
    225     }
    226 
    227     private static PublishConfig getPublishConfig(JSONObject j) throws JSONException {
    228         if (j == null) {
    229             return null;
    230         }
    231 
    232         PublishConfig.Builder builder = new PublishConfig.Builder();
    233 
    234         if (j.has("ServiceName")) {
    235             builder.setServiceName(getStringOrNull(j, "ServiceName"));
    236         }
    237 
    238         if (j.has("ServiceSpecificInfo")) {
    239             String ssi = getStringOrNull(j, "ServiceSpecificInfo");
    240             if (ssi != null) {
    241                 builder.setServiceSpecificInfo(ssi.getBytes());
    242             }
    243         }
    244 
    245         if (j.has("MatchFilter")) {
    246             byte[] bytes = Base64.decode(
    247                     j.getString("MatchFilter").getBytes(StandardCharsets.UTF_8), Base64.DEFAULT);
    248             List<byte[]> mf = new TlvBufferUtils.TlvIterable(0, 1, bytes).toList();
    249             builder.setMatchFilter(mf);
    250 
    251         }
    252 
    253         if (!j.isNull("MatchFilterList")) {
    254             builder.setMatchFilter(getMatchFilter(j.getJSONArray("MatchFilterList")));
    255         }
    256 
    257         if (j.has("DiscoveryType")) {
    258             builder.setPublishType(j.getInt("DiscoveryType"));
    259         }
    260         if (j.has("TtlSec")) {
    261             builder.setTtlSec(j.getInt("TtlSec"));
    262         }
    263         if (j.has("TerminateNotificationEnabled")) {
    264             builder.setTerminateNotificationEnabled(j.getBoolean("TerminateNotificationEnabled"));
    265         }
    266         if (j.has("RangingEnabled")) {
    267             builder.setRangingEnabled(j.getBoolean("RangingEnabled"));
    268         }
    269 
    270 
    271         return builder.build();
    272     }
    273 
    274     private static SubscribeConfig getSubscribeConfig(JSONObject j) throws JSONException {
    275         if (j == null) {
    276             return null;
    277         }
    278 
    279         SubscribeConfig.Builder builder = new SubscribeConfig.Builder();
    280 
    281         if (j.has("ServiceName")) {
    282             builder.setServiceName(j.getString("ServiceName"));
    283         }
    284 
    285         if (j.has("ServiceSpecificInfo")) {
    286             String ssi = getStringOrNull(j, "ServiceSpecificInfo");
    287             if (ssi != null) {
    288                 builder.setServiceSpecificInfo(ssi.getBytes());
    289             }
    290         }
    291 
    292         if (j.has("MatchFilter")) {
    293             byte[] bytes = Base64.decode(
    294                     j.getString("MatchFilter").getBytes(StandardCharsets.UTF_8), Base64.DEFAULT);
    295             List<byte[]> mf = new TlvBufferUtils.TlvIterable(0, 1, bytes).toList();
    296             builder.setMatchFilter(mf);
    297         }
    298 
    299         if (!j.isNull("MatchFilterList")) {
    300             builder.setMatchFilter(getMatchFilter(j.getJSONArray("MatchFilterList")));
    301         }
    302 
    303         if (j.has("DiscoveryType")) {
    304             builder.setSubscribeType(j.getInt("DiscoveryType"));
    305         }
    306         if (j.has("TtlSec")) {
    307             builder.setTtlSec(j.getInt("TtlSec"));
    308         }
    309         if (j.has("TerminateNotificationEnabled")) {
    310             builder.setTerminateNotificationEnabled(j.getBoolean("TerminateNotificationEnabled"));
    311         }
    312         if (j.has("MinDistanceMm")) {
    313             builder.setMinDistanceMm(j.getInt("MinDistanceMm"));
    314         }
    315         if (j.has("MaxDistanceMm")) {
    316             builder.setMaxDistanceMm(j.getInt("MaxDistanceMm"));
    317         }
    318 
    319         return builder.build();
    320     }
    321 
    322     public WifiAwareManagerFacade(FacadeManager manager) {
    323         super(manager);
    324         mService = manager.getService();
    325 
    326         mMgr = (WifiAwareManager) mService.getSystemService(Context.WIFI_AWARE_SERVICE);
    327 
    328         mEventFacade = manager.getReceiver(EventFacade.class);
    329 
    330         mStateChangedReceiver = new WifiAwareStateChangedReceiver();
    331         IntentFilter filter = new IntentFilter(WifiAwareManager.ACTION_WIFI_AWARE_STATE_CHANGED);
    332         mService.registerReceiver(mStateChangedReceiver, filter);
    333     }
    334 
    335     @Override
    336     public void shutdown() {
    337         wifiAwareDestroyAll();
    338         mService.unregisterReceiver(mStateChangedReceiver);
    339     }
    340 
    341     @Rpc(description = "Does the device support the Wi-Fi Aware feature?")
    342     public Boolean doesDeviceSupportWifiAwareFeature() {
    343         return mService.getPackageManager().hasSystemFeature(PackageManager.FEATURE_WIFI_AWARE);
    344     }
    345 
    346     @Rpc(description = "Is Aware Usage Enabled?")
    347     public Boolean wifiIsAwareAvailable() throws RemoteException {
    348         synchronized (mLock) {
    349             return mMgr.isAvailable();
    350         }
    351     }
    352 
    353     @Rpc(description = "Destroy all Aware sessions and discovery sessions")
    354     public void wifiAwareDestroyAll() {
    355         synchronized (mLock) {
    356             for (int i = 0; i < mSessions.size(); ++i) {
    357                 mSessions.valueAt(i).close();
    358             }
    359             mSessions.clear();
    360 
    361             /* discovery sessions automatically destroyed when containing Aware sessions
    362              * destroyed */
    363             mDiscoverySessions.clear();
    364 
    365             mMessageStartTime.clear();
    366         }
    367     }
    368 
    369     @Rpc(description = "Attach to Aware.")
    370     public Integer wifiAwareAttach(
    371             @RpcParameter(name = "identityCb",
    372                 description = "Controls whether an identity callback is provided")
    373                 @RpcOptional Boolean identityCb,
    374             @RpcParameter(name = "awareConfig",
    375                 description = "The session configuration, or null for default config")
    376                 @RpcOptional JSONObject awareConfig,
    377             @RpcParameter(name = "useIdInCallbackEvent",
    378                 description =
    379                     "Specifies whether the callback events should be decorated with session Id")
    380                 @RpcOptional Boolean useIdInCallbackEvent)
    381             throws RemoteException, JSONException {
    382         synchronized (mLock) {
    383             int sessionId = getNextSessionId();
    384             boolean useIdInCallbackEventName =
    385                 (useIdInCallbackEvent != null) ? useIdInCallbackEvent : false;
    386             mMgr.attach(null, getConfigRequest(awareConfig),
    387                     new AwareAttachCallbackPostsEvents(sessionId, useIdInCallbackEventName),
    388                     (identityCb != null && identityCb.booleanValue())
    389                         ? new AwareIdentityChangeListenerPostsEvents(sessionId,
    390                         useIdInCallbackEventName) : null);
    391             return sessionId;
    392         }
    393     }
    394 
    395     @Rpc(description = "Destroy a Aware session.")
    396     public void wifiAwareDestroy(
    397             @RpcParameter(name = "clientId", description = "The client ID returned when a connection was created") Integer clientId)
    398             throws RemoteException, JSONException {
    399         WifiAwareSession session;
    400         synchronized (mLock) {
    401             session = mSessions.get(clientId);
    402         }
    403         if (session == null) {
    404             throw new IllegalStateException(
    405                     "Calling WifiAwareDisconnect before session (client ID " + clientId
    406                             + ") is ready/or already disconnected");
    407         }
    408         session.close();
    409     }
    410 
    411     @Rpc(description = "Publish.")
    412     public Integer wifiAwarePublish(
    413             @RpcParameter(name = "clientId", description = "The client ID returned when a connection was created") Integer clientId,
    414             @RpcParameter(name = "publishConfig") JSONObject publishConfig,
    415             @RpcParameter(name = "useIdInCallbackEvent",
    416             description =
    417                 "Specifies whether the callback events should be decorated with session Id")
    418                 @RpcOptional Boolean useIdInCallbackEvent)
    419             throws RemoteException, JSONException {
    420         synchronized (mLock) {
    421             WifiAwareSession session = mSessions.get(clientId);
    422             if (session == null) {
    423                 throw new IllegalStateException(
    424                         "Calling WifiAwarePublish before session (client ID " + clientId
    425                                 + ") is ready/or already disconnected");
    426             }
    427             boolean useIdInCallbackEventName =
    428                 (useIdInCallbackEvent != null) ? useIdInCallbackEvent : false;
    429 
    430             int discoverySessionId = getNextDiscoverySessionId();
    431             session.publish(getPublishConfig(publishConfig),
    432                 new AwareDiscoverySessionCallbackPostsEvents(discoverySessionId,
    433                     useIdInCallbackEventName), null);
    434             return discoverySessionId;
    435         }
    436     }
    437 
    438     @Rpc(description = "Update Publish.")
    439     public void wifiAwareUpdatePublish(
    440         @RpcParameter(name = "sessionId", description = "The discovery session ID")
    441             Integer sessionId,
    442         @RpcParameter(name = "publishConfig", description = "Publish configuration")
    443             JSONObject publishConfig)
    444         throws RemoteException, JSONException {
    445         synchronized (mLock) {
    446             DiscoverySession session = mDiscoverySessions.get(sessionId);
    447             if (session == null) {
    448                 throw new IllegalStateException(
    449                     "Calling wifiAwareUpdatePublish before session (session ID "
    450                         + sessionId + ") is ready");
    451             }
    452             if (!(session instanceof PublishDiscoverySession)) {
    453                 throw new IllegalArgumentException(
    454                     "Calling wifiAwareUpdatePublish with a subscribe session ID");
    455             }
    456             ((PublishDiscoverySession) session).updatePublish(getPublishConfig(publishConfig));
    457         }
    458     }
    459 
    460     @Rpc(description = "Subscribe.")
    461     public Integer wifiAwareSubscribe(
    462             @RpcParameter(name = "clientId", description = "The client ID returned when a connection was created") Integer clientId,
    463             @RpcParameter(name = "subscribeConfig") JSONObject subscribeConfig,
    464             @RpcParameter(name = "useIdInCallbackEvent",
    465                 description =
    466                 "Specifies whether the callback events should be decorated with session Id")
    467                 @RpcOptional Boolean useIdInCallbackEvent)
    468             throws RemoteException, JSONException {
    469         synchronized (mLock) {
    470             WifiAwareSession session = mSessions.get(clientId);
    471             if (session == null) {
    472                 throw new IllegalStateException(
    473                         "Calling WifiAwareSubscribe before session (client ID " + clientId
    474                                 + ") is ready/or already disconnected");
    475             }
    476             boolean useIdInCallbackEventName =
    477                 (useIdInCallbackEvent != null) ? useIdInCallbackEvent : false;
    478 
    479             int discoverySessionId = getNextDiscoverySessionId();
    480             session.subscribe(getSubscribeConfig(subscribeConfig),
    481                 new AwareDiscoverySessionCallbackPostsEvents(discoverySessionId,
    482                     useIdInCallbackEventName), null);
    483             return discoverySessionId;
    484         }
    485     }
    486 
    487     @Rpc(description = "Update Subscribe.")
    488     public void wifiAwareUpdateSubscribe(
    489         @RpcParameter(name = "sessionId", description = "The discovery session ID")
    490             Integer sessionId,
    491         @RpcParameter(name = "subscribeConfig", description = "Subscribe configuration")
    492             JSONObject subscribeConfig)
    493         throws RemoteException, JSONException {
    494         synchronized (mLock) {
    495             DiscoverySession session = mDiscoverySessions.get(sessionId);
    496             if (session == null) {
    497                 throw new IllegalStateException(
    498                     "Calling wifiAwareUpdateSubscribe before session (session ID "
    499                         + sessionId + ") is ready");
    500             }
    501             if (!(session instanceof SubscribeDiscoverySession)) {
    502                 throw new IllegalArgumentException(
    503                     "Calling wifiAwareUpdateSubscribe with a publish session ID");
    504             }
    505             ((SubscribeDiscoverySession) session)
    506                 .updateSubscribe(getSubscribeConfig(subscribeConfig));
    507         }
    508     }
    509 
    510     @Rpc(description = "Destroy a discovery Session.")
    511     public void wifiAwareDestroyDiscoverySession(
    512             @RpcParameter(name = "sessionId", description = "The discovery session ID returned when session was created using publish or subscribe") Integer sessionId)
    513             throws RemoteException {
    514         synchronized (mLock) {
    515             DiscoverySession session = mDiscoverySessions.get(sessionId);
    516             if (session == null) {
    517                 throw new IllegalStateException(
    518                         "Calling WifiAwareTerminateSession before session (session ID "
    519                                 + sessionId + ") is ready");
    520             }
    521             session.close();
    522             mDiscoverySessions.remove(sessionId);
    523         }
    524     }
    525 
    526     @Rpc(description = "Send peer-to-peer Aware message")
    527     public void wifiAwareSendMessage(
    528             @RpcParameter(name = "sessionId", description = "The session ID returned when session"
    529                     + " was created using publish or subscribe") Integer sessionId,
    530             @RpcParameter(name = "peerId", description = "The ID of the peer being communicated "
    531                     + "with. Obtained from a previous message or match session.") Integer peerId,
    532             @RpcParameter(name = "messageId", description = "Arbitrary handle used for "
    533                     + "identification of the message in the message status callbacks")
    534                     Integer messageId,
    535             @RpcParameter(name = "message") String message,
    536             @RpcParameter(name = "retryCount", description = "Number of retries (0 for none) if "
    537                     + "transmission fails due to no ACK reception") Integer retryCount)
    538                     throws RemoteException {
    539         DiscoverySession session;
    540         synchronized (mLock) {
    541             session = mDiscoverySessions.get(sessionId);
    542         }
    543         if (session == null) {
    544             throw new IllegalStateException(
    545                     "Calling WifiAwareSendMessage before session (session ID " + sessionId
    546                             + " is ready");
    547         }
    548         byte[] bytes = null;
    549         if (message != null) {
    550             bytes = message.getBytes();
    551         }
    552 
    553         synchronized (mLock) {
    554             mMessageStartTime.put(messageId, System.currentTimeMillis());
    555         }
    556         session.sendMessage(new PeerHandle(peerId), messageId, bytes, retryCount);
    557     }
    558 
    559     @Rpc(description = "Create a network specifier to be used when specifying a Aware network request")
    560     public String wifiAwareCreateNetworkSpecifier(
    561             @RpcParameter(name = "sessionId", description = "The session ID returned when session was created using publish or subscribe")
    562                     Integer sessionId,
    563             @RpcParameter(name = "peerId", description = "The ID of the peer (obtained through OnMatch or OnMessageReceived")
    564                     Integer peerId,
    565             @RpcParameter(name = "passphrase",
    566                 description = "Passphrase of the data-path. Optional, can be empty/null.")
    567                 @RpcOptional String passphrase,
    568             @RpcParameter(name = "pmk",
    569                 description = "PMK of the data-path (base64 encoded). Optional, can be empty/null.")
    570                 @RpcOptional String pmk)
    571         throws JSONException {
    572         DiscoverySession session;
    573         synchronized (mLock) {
    574             session = mDiscoverySessions.get(sessionId);
    575         }
    576         if (session == null) {
    577             throw new IllegalStateException(
    578                     "Calling wifiAwareCreateNetworkSpecifier before session (session ID "
    579                             + sessionId + " is ready");
    580         }
    581         PeerHandle peerHandle = null;
    582         if (peerId != null) {
    583             peerHandle = new PeerHandle(peerId);
    584         }
    585         byte[] pmkDecoded = null;
    586         if (!TextUtils.isEmpty(pmk)) {
    587             pmkDecoded = Base64.decode(pmk, Base64.DEFAULT);
    588         }
    589 
    590         WifiAwareNetworkSpecifier ns = new WifiAwareNetworkSpecifier(
    591                 (peerHandle == null) ? WifiAwareNetworkSpecifier.NETWORK_SPECIFIER_TYPE_IB_ANY_PEER
    592                         : WifiAwareNetworkSpecifier.NETWORK_SPECIFIER_TYPE_IB,
    593                 session instanceof SubscribeDiscoverySession
    594                         ? WifiAwareManager.WIFI_AWARE_DATA_PATH_ROLE_INITIATOR
    595                         : WifiAwareManager.WIFI_AWARE_DATA_PATH_ROLE_RESPONDER,
    596                 session.getClientId(),
    597                 session.getSessionId(),
    598                 peerHandle != null ? peerHandle.peerId : 0, // 0 is an invalid peer ID
    599                 null, // peerMac (not used in this method)
    600                 pmkDecoded,
    601                 passphrase,
    602                 Process.myUid());
    603 
    604         return getJsonString(ns);
    605     }
    606 
    607     @Rpc(description = "Create a network specifier to be used when specifying an OOB Aware network request")
    608     public String wifiAwareCreateNetworkSpecifierOob(
    609             @RpcParameter(name = "clientId",
    610                     description = "The client ID")
    611                     Integer clientId,
    612             @RpcParameter(name = "role", description = "The role: INITIATOR(0), RESPONDER(1)")
    613                     Integer role,
    614             @RpcParameter(name = "peerMac",
    615                     description = "The MAC address of the peer")
    616                     String peerMac,
    617             @RpcParameter(name = "passphrase",
    618                     description = "Passphrase of the data-path. Optional, can be empty/null.")
    619             @RpcOptional String passphrase,
    620             @RpcParameter(name = "pmk",
    621                     description = "PMK of the data-path (base64). Optional, can be empty/null.")
    622             @RpcOptional String pmk) throws JSONException {
    623         WifiAwareSession session;
    624         synchronized (mLock) {
    625             session = mSessions.get(clientId);
    626         }
    627         if (session == null) {
    628             throw new IllegalStateException(
    629                     "Calling wifiAwareCreateNetworkSpecifierOob before session (client ID "
    630                             + clientId + " is ready");
    631         }
    632         byte[] peerMacBytes = null;
    633         if (peerMac != null) {
    634             peerMacBytes = HexEncoding.decode(peerMac.toCharArray(), false);
    635         }
    636         byte[] pmkDecoded = null;
    637         if (!TextUtils.isEmpty(pmk)) {
    638             pmkDecoded = Base64.decode(pmk, Base64.DEFAULT);
    639         }
    640 
    641         WifiAwareNetworkSpecifier ns = new WifiAwareNetworkSpecifier(
    642                 (peerMacBytes == null) ?
    643                         WifiAwareNetworkSpecifier.NETWORK_SPECIFIER_TYPE_OOB_ANY_PEER
    644                         : WifiAwareNetworkSpecifier.NETWORK_SPECIFIER_TYPE_OOB,
    645                 role,
    646                 session.getClientId(),
    647                 0, // 0 is an invalid session ID
    648                 0, // 0 is an invalid peer ID
    649                 peerMacBytes,
    650                 pmkDecoded,
    651                 passphrase,
    652                 Process.myUid());
    653 
    654         return getJsonString(ns);
    655     }
    656 
    657     private class AwareAttachCallbackPostsEvents extends AttachCallback {
    658         private int mSessionId;
    659         private long mCreateTimestampMs;
    660         private boolean mUseIdInCallbackEventName;
    661 
    662         public AwareAttachCallbackPostsEvents(int sessionId, boolean useIdInCallbackEventName) {
    663             mSessionId = sessionId;
    664             mCreateTimestampMs = System.currentTimeMillis();
    665             mUseIdInCallbackEventName = useIdInCallbackEventName;
    666         }
    667 
    668         @Override
    669         public void onAttached(WifiAwareSession session) {
    670             synchronized (mLock) {
    671                 mSessions.put(mSessionId, session);
    672             }
    673 
    674             Bundle mResults = new Bundle();
    675             mResults.putInt("sessionId", mSessionId);
    676             mResults.putLong("latencyMs", System.currentTimeMillis() - mCreateTimestampMs);
    677             mResults.putLong("timestampMs", System.currentTimeMillis());
    678             if (mUseIdInCallbackEventName) {
    679                 mEventFacade.postEvent("WifiAwareOnAttached_" + mSessionId, mResults);
    680             } else {
    681                 mEventFacade.postEvent("WifiAwareOnAttached", mResults);
    682             }
    683         }
    684 
    685         @Override
    686         public void onAttachFailed() {
    687             Bundle mResults = new Bundle();
    688             mResults.putInt("sessionId", mSessionId);
    689             mResults.putLong("latencyMs", System.currentTimeMillis() - mCreateTimestampMs);
    690             if (mUseIdInCallbackEventName) {
    691                 mEventFacade.postEvent("WifiAwareOnAttachFailed_" + mSessionId, mResults);
    692             } else {
    693                 mEventFacade.postEvent("WifiAwareOnAttachFailed", mResults);
    694             }
    695         }
    696     }
    697 
    698     private class AwareIdentityChangeListenerPostsEvents extends IdentityChangedListener {
    699         private int mSessionId;
    700         private boolean mUseIdInCallbackEventName;
    701 
    702         public AwareIdentityChangeListenerPostsEvents(int sessionId,
    703             boolean useIdInCallbackEventName) {
    704             mSessionId = sessionId;
    705             mUseIdInCallbackEventName = useIdInCallbackEventName;
    706         }
    707 
    708         @Override
    709         public void onIdentityChanged(byte[] mac) {
    710             Bundle mResults = new Bundle();
    711             mResults.putInt("sessionId", mSessionId);
    712             mResults.putString("mac", String.valueOf(HexEncoding.encode(mac)));
    713             mResults.putLong("timestampMs", System.currentTimeMillis());
    714             if (mUseIdInCallbackEventName) {
    715                 mEventFacade.postEvent("WifiAwareOnIdentityChanged_" + mSessionId, mResults);
    716             } else {
    717                 mEventFacade.postEvent("WifiAwareOnIdentityChanged", mResults);
    718             }
    719         }
    720     }
    721 
    722     private class AwareDiscoverySessionCallbackPostsEvents extends
    723             DiscoverySessionCallback {
    724         private int mDiscoverySessionId;
    725         private boolean mUseIdInCallbackEventName;
    726         private long mCreateTimestampMs;
    727 
    728         public AwareDiscoverySessionCallbackPostsEvents(int discoverySessionId,
    729                 boolean useIdInCallbackEventName) {
    730             mDiscoverySessionId = discoverySessionId;
    731             mUseIdInCallbackEventName = useIdInCallbackEventName;
    732             mCreateTimestampMs = System.currentTimeMillis();
    733         }
    734 
    735         private void postEvent(String eventName, Bundle results) {
    736             String finalEventName = eventName;
    737             if (mUseIdInCallbackEventName) {
    738                 finalEventName += "_" + mDiscoverySessionId;
    739             }
    740 
    741             mEventFacade.postEvent(finalEventName, results);
    742         }
    743 
    744         @Override
    745         public void onPublishStarted(PublishDiscoverySession discoverySession) {
    746             synchronized (mLock) {
    747                 mDiscoverySessions.put(mDiscoverySessionId, discoverySession);
    748             }
    749 
    750             Bundle mResults = new Bundle();
    751             mResults.putInt("discoverySessionId", mDiscoverySessionId);
    752             mResults.putLong("latencyMs", System.currentTimeMillis() - mCreateTimestampMs);
    753             mResults.putLong("timestampMs", System.currentTimeMillis());
    754             postEvent("WifiAwareSessionOnPublishStarted", mResults);
    755         }
    756 
    757         @Override
    758         public void onSubscribeStarted(SubscribeDiscoverySession discoverySession) {
    759             synchronized (mLock) {
    760                 mDiscoverySessions.put(mDiscoverySessionId, discoverySession);
    761             }
    762 
    763             Bundle mResults = new Bundle();
    764             mResults.putInt("discoverySessionId", mDiscoverySessionId);
    765             mResults.putLong("latencyMs", System.currentTimeMillis() - mCreateTimestampMs);
    766             mResults.putLong("timestampMs", System.currentTimeMillis());
    767             postEvent("WifiAwareSessionOnSubscribeStarted", mResults);
    768         }
    769 
    770         @Override
    771         public void onSessionConfigUpdated() {
    772             Bundle mResults = new Bundle();
    773             mResults.putInt("discoverySessionId", mDiscoverySessionId);
    774             postEvent("WifiAwareSessionOnSessionConfigUpdated", mResults);
    775         }
    776 
    777         @Override
    778         public void onSessionConfigFailed() {
    779             Bundle mResults = new Bundle();
    780             mResults.putInt("discoverySessionId", mDiscoverySessionId);
    781             postEvent("WifiAwareSessionOnSessionConfigFailed", mResults);
    782         }
    783 
    784         @Override
    785         public void onSessionTerminated() {
    786             Bundle mResults = new Bundle();
    787             mResults.putInt("discoverySessionId", mDiscoverySessionId);
    788             postEvent("WifiAwareSessionOnSessionTerminated", mResults);
    789         }
    790 
    791         private Bundle createServiceDiscoveredBaseBundle(PeerHandle peerHandle,
    792                 byte[] serviceSpecificInfo, List<byte[]> matchFilter) {
    793             Bundle mResults = new Bundle();
    794             mResults.putInt("discoverySessionId", mDiscoverySessionId);
    795             mResults.putInt("peerId", peerHandle.peerId);
    796             mResults.putByteArray("serviceSpecificInfo", serviceSpecificInfo);
    797             mResults.putByteArray("matchFilter", new TlvBufferUtils.TlvConstructor(0,
    798                     1).allocateAndPut(matchFilter).getArray());
    799             ArrayList<String> matchFilterStrings = new ArrayList<>(matchFilter.size());
    800             for (byte[] be: matchFilter) {
    801                 matchFilterStrings.add(Base64.encodeToString(be, Base64.DEFAULT));
    802             }
    803             mResults.putStringArrayList("matchFilterList", matchFilterStrings);
    804             mResults.putLong("timestampMs", System.currentTimeMillis());
    805             return mResults;
    806         }
    807 
    808         @Override
    809         public void onServiceDiscovered(PeerHandle peerHandle,
    810                 byte[] serviceSpecificInfo, List<byte[]> matchFilter) {
    811             Bundle mResults = createServiceDiscoveredBaseBundle(peerHandle, serviceSpecificInfo,
    812                     matchFilter);
    813             postEvent("WifiAwareSessionOnServiceDiscovered", mResults);
    814         }
    815 
    816         @Override
    817         public void onServiceDiscoveredWithinRange(PeerHandle peerHandle,
    818                 byte[] serviceSpecificInfo,
    819                 List<byte[]> matchFilter, int distanceMm) {
    820             Bundle mResults = createServiceDiscoveredBaseBundle(peerHandle, serviceSpecificInfo,
    821                     matchFilter);
    822             mResults.putInt("distanceMm", distanceMm);
    823             postEvent("WifiAwareSessionOnServiceDiscovered", mResults);
    824         }
    825 
    826         @Override
    827         public void onMessageSendSucceeded(int messageId) {
    828             Bundle mResults = new Bundle();
    829             mResults.putInt("discoverySessionId", mDiscoverySessionId);
    830             mResults.putInt("messageId", messageId);
    831             synchronized (mLock) {
    832                 Long startTime = mMessageStartTime.get(messageId);
    833                 if (startTime != null) {
    834                     mResults.putLong("latencyMs",
    835                             System.currentTimeMillis() - startTime.longValue());
    836                     mMessageStartTime.remove(messageId);
    837                 }
    838             }
    839             postEvent("WifiAwareSessionOnMessageSent", mResults);
    840         }
    841 
    842         @Override
    843         public void onMessageSendFailed(int messageId) {
    844             Bundle mResults = new Bundle();
    845             mResults.putInt("discoverySessionId", mDiscoverySessionId);
    846             mResults.putInt("messageId", messageId);
    847             synchronized (mLock) {
    848                 Long startTime = mMessageStartTime.get(messageId);
    849                 if (startTime != null) {
    850                     mResults.putLong("latencyMs",
    851                             System.currentTimeMillis() - startTime.longValue());
    852                     mMessageStartTime.remove(messageId);
    853                 }
    854             }
    855             postEvent("WifiAwareSessionOnMessageSendFailed", mResults);
    856         }
    857 
    858         @Override
    859         public void onMessageReceived(PeerHandle peerHandle, byte[] message) {
    860             Bundle mResults = new Bundle();
    861             mResults.putInt("discoverySessionId", mDiscoverySessionId);
    862             mResults.putInt("peerId", peerHandle.peerId);
    863             mResults.putByteArray("message", message); // TODO: base64
    864             mResults.putString("messageAsString", new String(message));
    865             postEvent("WifiAwareSessionOnMessageReceived", mResults);
    866         }
    867     }
    868 
    869     class WifiAwareRangingListener implements RttManager.RttListener {
    870         private int mCallbackId;
    871         private int mSessionId;
    872 
    873         public WifiAwareRangingListener(int callbackId, int sessionId) {
    874             mCallbackId = callbackId;
    875             mSessionId = sessionId;
    876         }
    877 
    878         @Override
    879         public void onSuccess(RttResult[] results) {
    880             Bundle bundle = new Bundle();
    881             bundle.putInt("callbackId", mCallbackId);
    882             bundle.putInt("sessionId", mSessionId);
    883 
    884             Parcelable[] resultBundles = new Parcelable[results.length];
    885             for (int i = 0; i < results.length; i++) {
    886                 resultBundles[i] = WifiRttManagerFacade.RangingListener.packRttResult(results[i]);
    887             }
    888             bundle.putParcelableArray("Results", resultBundles);
    889 
    890             mEventFacade.postEvent("WifiAwareRangingListenerOnSuccess", bundle);
    891         }
    892 
    893         @Override
    894         public void onFailure(int reason, String description) {
    895             Bundle bundle = new Bundle();
    896             bundle.putInt("callbackId", mCallbackId);
    897             bundle.putInt("sessionId", mSessionId);
    898             bundle.putInt("reason", reason);
    899             bundle.putString("description", description);
    900             mEventFacade.postEvent("WifiAwareRangingListenerOnFailure", bundle);
    901         }
    902 
    903         @Override
    904         public void onAborted() {
    905             Bundle bundle = new Bundle();
    906             bundle.putInt("callbackId", mCallbackId);
    907             bundle.putInt("sessionId", mSessionId);
    908             mEventFacade.postEvent("WifiAwareRangingListenerOnAborted", bundle);
    909         }
    910 
    911     }
    912 
    913     class WifiAwareStateChangedReceiver extends BroadcastReceiver {
    914         @Override
    915         public void onReceive(Context c, Intent intent) {
    916             boolean isAvailable = mMgr.isAvailable();
    917             if (!isAvailable) {
    918                 wifiAwareDestroyAll();
    919             }
    920             mEventFacade.postEvent(isAvailable ? "WifiAwareAvailable" : "WifiAwareNotAvailable",
    921                     new Bundle());
    922         }
    923     }
    924 }
    925