• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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<>();
getNextDiscoverySessionId()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<>();
getNextSessionId()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 
getJsonString(WifiAwareNetworkSpecifier ns)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 
getNetworkSpecifier(JSONObject j)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 
getStringOrNull(JSONObject j, String name)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 
getConfigRequest(JSONObject j)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 
getMatchFilter(JSONArray ja)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 
getPublishConfig(JSONObject j)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 
267         return builder.build();
268     }
269 
getSubscribeConfig(JSONObject j)270     private static SubscribeConfig getSubscribeConfig(JSONObject j) throws JSONException {
271         if (j == null) {
272             return null;
273         }
274 
275         SubscribeConfig.Builder builder = new SubscribeConfig.Builder();
276 
277         if (j.has("ServiceName")) {
278             builder.setServiceName(j.getString("ServiceName"));
279         }
280 
281         if (j.has("ServiceSpecificInfo")) {
282             String ssi = getStringOrNull(j, "ServiceSpecificInfo");
283             if (ssi != null) {
284                 builder.setServiceSpecificInfo(ssi.getBytes());
285             }
286         }
287 
288         if (j.has("MatchFilter")) {
289             byte[] bytes = Base64.decode(
290                     j.getString("MatchFilter").getBytes(StandardCharsets.UTF_8), Base64.DEFAULT);
291             List<byte[]> mf = new TlvBufferUtils.TlvIterable(0, 1, bytes).toList();
292             builder.setMatchFilter(mf);
293         }
294 
295         if (!j.isNull("MatchFilterList")) {
296             builder.setMatchFilter(getMatchFilter(j.getJSONArray("MatchFilterList")));
297         }
298 
299         if (j.has("DiscoveryType")) {
300             builder.setSubscribeType(j.getInt("DiscoveryType"));
301         }
302         if (j.has("TtlSec")) {
303             builder.setTtlSec(j.getInt("TtlSec"));
304         }
305         if (j.has("TerminateNotificationEnabled")) {
306             builder.setTerminateNotificationEnabled(j.getBoolean("TerminateNotificationEnabled"));
307         }
308 
309         return builder.build();
310     }
311 
WifiAwareManagerFacade(FacadeManager manager)312     public WifiAwareManagerFacade(FacadeManager manager) {
313         super(manager);
314         mService = manager.getService();
315 
316         mMgr = (WifiAwareManager) mService.getSystemService(Context.WIFI_AWARE_SERVICE);
317 
318         mEventFacade = manager.getReceiver(EventFacade.class);
319 
320         mStateChangedReceiver = new WifiAwareStateChangedReceiver();
321         IntentFilter filter = new IntentFilter(WifiAwareManager.ACTION_WIFI_AWARE_STATE_CHANGED);
322         mService.registerReceiver(mStateChangedReceiver, filter);
323     }
324 
325     @Override
shutdown()326     public void shutdown() {
327         wifiAwareDestroyAll();
328         mService.unregisterReceiver(mStateChangedReceiver);
329     }
330 
331     @Rpc(description = "Does the device support the Wi-Fi Aware feature?")
doesDeviceSupportWifiAwareFeature()332     public Boolean doesDeviceSupportWifiAwareFeature() {
333         return mService.getPackageManager().hasSystemFeature(PackageManager.FEATURE_WIFI_AWARE);
334     }
335 
336     @Rpc(description = "Is Aware Usage Enabled?")
wifiIsAwareAvailable()337     public Boolean wifiIsAwareAvailable() throws RemoteException {
338         synchronized (mLock) {
339             return mMgr.isAvailable();
340         }
341     }
342 
343     @Rpc(description = "Destroy all Aware sessions and discovery sessions")
wifiAwareDestroyAll()344     public void wifiAwareDestroyAll() {
345         synchronized (mLock) {
346             for (int i = 0; i < mSessions.size(); ++i) {
347                 mSessions.valueAt(i).close();
348             }
349             mSessions.clear();
350 
351             /* discovery sessions automatically destroyed when containing Aware sessions
352              * destroyed */
353             mDiscoverySessions.clear();
354 
355             mMessageStartTime.clear();
356         }
357     }
358 
359     @Rpc(description = "Attach to Aware.")
wifiAwareAttach( @pcParametername = "identityCb", description = "Controls whether an identity callback is provided") @pcOptional 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)360     public Integer wifiAwareAttach(
361             @RpcParameter(name = "identityCb",
362                 description = "Controls whether an identity callback is provided")
363                 @RpcOptional Boolean identityCb,
364             @RpcParameter(name = "awareConfig",
365                 description = "The session configuration, or null for default config")
366                 @RpcOptional JSONObject awareConfig,
367             @RpcParameter(name = "useIdInCallbackEvent",
368                 description =
369                     "Specifies whether the callback events should be decorated with session Id")
370                 @RpcOptional Boolean useIdInCallbackEvent)
371             throws RemoteException, JSONException {
372         synchronized (mLock) {
373             int sessionId = getNextSessionId();
374             boolean useIdInCallbackEventName =
375                 (useIdInCallbackEvent != null) ? useIdInCallbackEvent : false;
376             mMgr.attach(null, getConfigRequest(awareConfig),
377                     new AwareAttachCallbackPostsEvents(sessionId, useIdInCallbackEventName),
378                     (identityCb != null && identityCb.booleanValue())
379                         ? new AwareIdentityChangeListenerPostsEvents(sessionId,
380                         useIdInCallbackEventName) : null);
381             return sessionId;
382         }
383     }
384 
385     @Rpc(description = "Destroy a Aware session.")
wifiAwareDestroy( @pcParametername = "clientId", description = "The client ID returned when a connection was created") Integer clientId)386     public void wifiAwareDestroy(
387             @RpcParameter(name = "clientId", description = "The client ID returned when a connection was created") Integer clientId)
388             throws RemoteException, JSONException {
389         WifiAwareSession session;
390         synchronized (mLock) {
391             session = mSessions.get(clientId);
392         }
393         if (session == null) {
394             throw new IllegalStateException(
395                     "Calling WifiAwareDisconnect before session (client ID " + clientId
396                             + ") is ready/or already disconnected");
397         }
398         session.close();
399     }
400 
401     @Rpc(description = "Publish.")
wifiAwarePublish( @pcParametername = "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)402     public Integer wifiAwarePublish(
403             @RpcParameter(name = "clientId", description = "The client ID returned when a connection was created") Integer clientId,
404             @RpcParameter(name = "publishConfig") JSONObject publishConfig,
405             @RpcParameter(name = "useIdInCallbackEvent",
406             description =
407                 "Specifies whether the callback events should be decorated with session Id")
408                 @RpcOptional Boolean useIdInCallbackEvent)
409             throws RemoteException, JSONException {
410         synchronized (mLock) {
411             WifiAwareSession session = mSessions.get(clientId);
412             if (session == null) {
413                 throw new IllegalStateException(
414                         "Calling WifiAwarePublish before session (client ID " + clientId
415                                 + ") is ready/or already disconnected");
416             }
417             boolean useIdInCallbackEventName =
418                 (useIdInCallbackEvent != null) ? useIdInCallbackEvent : false;
419 
420             int discoverySessionId = getNextDiscoverySessionId();
421             session.publish(getPublishConfig(publishConfig),
422                 new AwareDiscoverySessionCallbackPostsEvents(discoverySessionId,
423                     useIdInCallbackEventName), null);
424             return discoverySessionId;
425         }
426     }
427 
428     @Rpc(description = "Update Publish.")
wifiAwareUpdatePublish( @pcParametername = "sessionId", description = "The discovery session ID") Integer sessionId, @RpcParameter(name = "publishConfig", description = "Publish configuration") JSONObject publishConfig)429     public void wifiAwareUpdatePublish(
430         @RpcParameter(name = "sessionId", description = "The discovery session ID")
431             Integer sessionId,
432         @RpcParameter(name = "publishConfig", description = "Publish configuration")
433             JSONObject publishConfig)
434         throws RemoteException, JSONException {
435         synchronized (mLock) {
436             DiscoverySession session = mDiscoverySessions.get(sessionId);
437             if (session == null) {
438                 throw new IllegalStateException(
439                     "Calling wifiAwareUpdatePublish before session (session ID "
440                         + sessionId + ") is ready");
441             }
442             if (!(session instanceof PublishDiscoverySession)) {
443                 throw new IllegalArgumentException(
444                     "Calling wifiAwareUpdatePublish with a subscribe session ID");
445             }
446             ((PublishDiscoverySession) session).updatePublish(getPublishConfig(publishConfig));
447         }
448     }
449 
450     @Rpc(description = "Subscribe.")
wifiAwareSubscribe( @pcParametername = "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)451     public Integer wifiAwareSubscribe(
452             @RpcParameter(name = "clientId", description = "The client ID returned when a connection was created") Integer clientId,
453             @RpcParameter(name = "subscribeConfig") JSONObject subscribeConfig,
454             @RpcParameter(name = "useIdInCallbackEvent",
455                 description =
456                 "Specifies whether the callback events should be decorated with session Id")
457                 @RpcOptional Boolean useIdInCallbackEvent)
458             throws RemoteException, JSONException {
459         synchronized (mLock) {
460             WifiAwareSession session = mSessions.get(clientId);
461             if (session == null) {
462                 throw new IllegalStateException(
463                         "Calling WifiAwareSubscribe before session (client ID " + clientId
464                                 + ") is ready/or already disconnected");
465             }
466             boolean useIdInCallbackEventName =
467                 (useIdInCallbackEvent != null) ? useIdInCallbackEvent : false;
468 
469             int discoverySessionId = getNextDiscoverySessionId();
470             session.subscribe(getSubscribeConfig(subscribeConfig),
471                 new AwareDiscoverySessionCallbackPostsEvents(discoverySessionId,
472                     useIdInCallbackEventName), null);
473             return discoverySessionId;
474         }
475     }
476 
477     @Rpc(description = "Update Subscribe.")
wifiAwareUpdateSubscribe( @pcParametername = "sessionId", description = "The discovery session ID") Integer sessionId, @RpcParameter(name = "subscribeConfig", description = "Subscribe configuration") JSONObject subscribeConfig)478     public void wifiAwareUpdateSubscribe(
479         @RpcParameter(name = "sessionId", description = "The discovery session ID")
480             Integer sessionId,
481         @RpcParameter(name = "subscribeConfig", description = "Subscribe configuration")
482             JSONObject subscribeConfig)
483         throws RemoteException, JSONException {
484         synchronized (mLock) {
485             DiscoverySession session = mDiscoverySessions.get(sessionId);
486             if (session == null) {
487                 throw new IllegalStateException(
488                     "Calling wifiAwareUpdateSubscribe before session (session ID "
489                         + sessionId + ") is ready");
490             }
491             if (!(session instanceof SubscribeDiscoverySession)) {
492                 throw new IllegalArgumentException(
493                     "Calling wifiAwareUpdateSubscribe with a publish session ID");
494             }
495             ((SubscribeDiscoverySession) session)
496                 .updateSubscribe(getSubscribeConfig(subscribeConfig));
497         }
498     }
499 
500     @Rpc(description = "Destroy a discovery Session.")
wifiAwareDestroyDiscoverySession( @pcParametername = "sessionId", description = "The discovery session ID returned when session was created using publish or subscribe") Integer sessionId)501     public void wifiAwareDestroyDiscoverySession(
502             @RpcParameter(name = "sessionId", description = "The discovery session ID returned when session was created using publish or subscribe") Integer sessionId)
503             throws RemoteException {
504         synchronized (mLock) {
505             DiscoverySession session = mDiscoverySessions.get(sessionId);
506             if (session == null) {
507                 throw new IllegalStateException(
508                         "Calling WifiAwareTerminateSession before session (session ID "
509                                 + sessionId + ") is ready");
510             }
511             session.close();
512             mDiscoverySessions.remove(sessionId);
513         }
514     }
515 
516     @Rpc(description = "Send peer-to-peer Aware message")
wifiAwareSendMessage( @pcParametername = "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)517     public void wifiAwareSendMessage(
518             @RpcParameter(name = "sessionId", description = "The session ID returned when session"
519                     + " was created using publish or subscribe") Integer sessionId,
520             @RpcParameter(name = "peerId", description = "The ID of the peer being communicated "
521                     + "with. Obtained from a previous message or match session.") Integer peerId,
522             @RpcParameter(name = "messageId", description = "Arbitrary handle used for "
523                     + "identification of the message in the message status callbacks")
524                     Integer messageId,
525             @RpcParameter(name = "message") String message,
526             @RpcParameter(name = "retryCount", description = "Number of retries (0 for none) if "
527                     + "transmission fails due to no ACK reception") Integer retryCount)
528                     throws RemoteException {
529         DiscoverySession session;
530         synchronized (mLock) {
531             session = mDiscoverySessions.get(sessionId);
532         }
533         if (session == null) {
534             throw new IllegalStateException(
535                     "Calling WifiAwareSendMessage before session (session ID " + sessionId
536                             + " is ready");
537         }
538         byte[] bytes = null;
539         if (message != null) {
540             bytes = message.getBytes();
541         }
542 
543         synchronized (mLock) {
544             mMessageStartTime.put(messageId, System.currentTimeMillis());
545         }
546         session.sendMessage(new PeerHandle(peerId), messageId, bytes, retryCount);
547     }
548 
549     @Rpc(description = "Start peer-to-peer Aware ranging")
wifiAwareStartRanging( @pcParametername = "callbackId") Integer callbackId, @RpcParameter(name = "sessionId", description = "The session ID returned when session was created using publish or subscribe") Integer sessionId, @RpcParameter(name = "rttParams", description = "RTT session parameters.") JSONArray rttParams)550     public void wifiAwareStartRanging(
551             @RpcParameter(name = "callbackId") Integer callbackId,
552             @RpcParameter(name = "sessionId", description = "The session ID returned when session was created using publish or subscribe") Integer sessionId,
553             @RpcParameter(name = "rttParams", description = "RTT session parameters.") JSONArray rttParams) throws RemoteException, JSONException {
554         DiscoverySession session;
555         synchronized (mLock) {
556             session = mDiscoverySessions.get(sessionId);
557         }
558         if (session == null) {
559             throw new IllegalStateException(
560                     "Calling WifiAwareStartRanging before session (session ID "
561                             + sessionId + " is ready");
562         }
563         RttManager.RttParams[] rParams = new RttManager.RttParams[rttParams.length()];
564         for (int i = 0; i < rttParams.length(); i++) {
565             rParams[i] = WifiRttManagerFacade.parseRttParam(rttParams.getJSONObject(i));
566         }
567         session.startRanging(rParams, new WifiAwareRangingListener(callbackId, sessionId));
568     }
569 
570     @Rpc(description = "Create a network specifier to be used when specifying a Aware network request")
wifiAwareCreateNetworkSpecifier( @pcParametername = "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)571     public String wifiAwareCreateNetworkSpecifier(
572             @RpcParameter(name = "sessionId", description = "The session ID returned when session was created using publish or subscribe")
573                     Integer sessionId,
574             @RpcParameter(name = "peerId", description = "The ID of the peer (obtained through OnMatch or OnMessageReceived")
575                     Integer peerId,
576             @RpcParameter(name = "passphrase",
577                 description = "Passphrase of the data-path. Optional, can be empty/null.")
578                 @RpcOptional String passphrase,
579             @RpcParameter(name = "pmk",
580                 description = "PMK of the data-path (base64 encoded). Optional, can be empty/null.")
581                 @RpcOptional String pmk)
582         throws JSONException {
583         DiscoverySession session;
584         synchronized (mLock) {
585             session = mDiscoverySessions.get(sessionId);
586         }
587         if (session == null) {
588             throw new IllegalStateException(
589                     "Calling wifiAwareCreateNetworkSpecifier before session (session ID "
590                             + sessionId + " is ready");
591         }
592         NetworkSpecifier ns = null;
593         PeerHandle peerHandle = null;
594         if (peerId != null) {
595             peerHandle = new PeerHandle(peerId);
596         }
597         if (TextUtils.isEmpty(passphrase) && TextUtils.isEmpty(pmk)) {
598             ns = session.createNetworkSpecifierOpen(peerHandle);
599         } else if (TextUtils.isEmpty(pmk)){
600             ns = session.createNetworkSpecifierPassphrase(peerHandle, passphrase);
601         } else {
602             byte[] pmkDecoded = Base64.decode(pmk, Base64.DEFAULT);
603             ns = session.createNetworkSpecifierPmk(peerHandle, pmkDecoded);
604         }
605 
606         return getJsonString((WifiAwareNetworkSpecifier) ns);
607     }
608 
609     @Rpc(description = "Create a network specifier to be used when specifying an OOB Aware network request")
wifiAwareCreateNetworkSpecifierOob( @pcParametername = "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)610     public String wifiAwareCreateNetworkSpecifierOob(
611             @RpcParameter(name = "clientId",
612                     description = "The client ID")
613                     Integer clientId,
614             @RpcParameter(name = "role", description = "The role: INITIATOR(0), RESPONDER(1)")
615                     Integer role,
616             @RpcParameter(name = "peerMac",
617                     description = "The MAC address of the peer")
618                     String peerMac,
619             @RpcParameter(name = "passphrase",
620                     description = "Passphrase of the data-path. Optional, can be empty/null.")
621             @RpcOptional String passphrase,
622             @RpcParameter(name = "pmk",
623                     description = "PMK of the data-path (base64). Optional, can be empty/null.")
624             @RpcOptional String pmk) throws JSONException {
625         WifiAwareSession session;
626         synchronized (mLock) {
627             session = mSessions.get(clientId);
628         }
629         if (session == null) {
630             throw new IllegalStateException(
631                     "Calling wifiAwareCreateNetworkSpecifierOob before session (client ID "
632                             + clientId + " is ready");
633         }
634         NetworkSpecifier ns = null;
635         byte[] peerMacBytes = null;
636         if (peerMac != null) {
637             peerMacBytes = HexEncoding.decode(peerMac.toCharArray(), false);
638         }
639         if (TextUtils.isEmpty(passphrase) && TextUtils.isEmpty(pmk)) {
640             ns = session.createNetworkSpecifierOpen(role, peerMacBytes);
641         } else if (TextUtils.isEmpty(pmk)){
642             ns = session.createNetworkSpecifierPassphrase(role, peerMacBytes, passphrase);
643         } else {
644             byte[] pmkDecoded = Base64.decode(pmk, Base64.DEFAULT);
645             ns = session.createNetworkSpecifierPmk(role, peerMacBytes, pmkDecoded);
646         }
647 
648         return getJsonString((WifiAwareNetworkSpecifier) ns);
649     }
650 
651     private class AwareAttachCallbackPostsEvents extends AttachCallback {
652         private int mSessionId;
653         private long mCreateTimestampMs;
654         private boolean mUseIdInCallbackEventName;
655 
AwareAttachCallbackPostsEvents(int sessionId, boolean useIdInCallbackEventName)656         public AwareAttachCallbackPostsEvents(int sessionId, boolean useIdInCallbackEventName) {
657             mSessionId = sessionId;
658             mCreateTimestampMs = System.currentTimeMillis();
659             mUseIdInCallbackEventName = useIdInCallbackEventName;
660         }
661 
662         @Override
onAttached(WifiAwareSession session)663         public void onAttached(WifiAwareSession session) {
664             synchronized (mLock) {
665                 mSessions.put(mSessionId, session);
666             }
667 
668             Bundle mResults = new Bundle();
669             mResults.putInt("sessionId", mSessionId);
670             mResults.putLong("latencyMs", System.currentTimeMillis() - mCreateTimestampMs);
671             mResults.putLong("timestampMs", System.currentTimeMillis());
672             if (mUseIdInCallbackEventName) {
673                 mEventFacade.postEvent("WifiAwareOnAttached_" + mSessionId, mResults);
674             } else {
675                 mEventFacade.postEvent("WifiAwareOnAttached", mResults);
676             }
677         }
678 
679         @Override
onAttachFailed()680         public void onAttachFailed() {
681             Bundle mResults = new Bundle();
682             mResults.putInt("sessionId", mSessionId);
683             mResults.putLong("latencyMs", System.currentTimeMillis() - mCreateTimestampMs);
684             if (mUseIdInCallbackEventName) {
685                 mEventFacade.postEvent("WifiAwareOnAttachFailed_" + mSessionId, mResults);
686             } else {
687                 mEventFacade.postEvent("WifiAwareOnAttachFailed", mResults);
688             }
689         }
690     }
691 
692     private class AwareIdentityChangeListenerPostsEvents extends IdentityChangedListener {
693         private int mSessionId;
694         private boolean mUseIdInCallbackEventName;
695 
AwareIdentityChangeListenerPostsEvents(int sessionId, boolean useIdInCallbackEventName)696         public AwareIdentityChangeListenerPostsEvents(int sessionId,
697             boolean useIdInCallbackEventName) {
698             mSessionId = sessionId;
699             mUseIdInCallbackEventName = useIdInCallbackEventName;
700         }
701 
702         @Override
onIdentityChanged(byte[] mac)703         public void onIdentityChanged(byte[] mac) {
704             Bundle mResults = new Bundle();
705             mResults.putInt("sessionId", mSessionId);
706             mResults.putString("mac", String.valueOf(HexEncoding.encode(mac)));
707             mResults.putLong("timestampMs", System.currentTimeMillis());
708             if (mUseIdInCallbackEventName) {
709                 mEventFacade.postEvent("WifiAwareOnIdentityChanged_" + mSessionId, mResults);
710             } else {
711                 mEventFacade.postEvent("WifiAwareOnIdentityChanged", mResults);
712             }
713         }
714     }
715 
716     private class AwareDiscoverySessionCallbackPostsEvents extends
717             DiscoverySessionCallback {
718         private int mDiscoverySessionId;
719         private boolean mUseIdInCallbackEventName;
720         private long mCreateTimestampMs;
721 
AwareDiscoverySessionCallbackPostsEvents(int discoverySessionId, boolean useIdInCallbackEventName)722         public AwareDiscoverySessionCallbackPostsEvents(int discoverySessionId,
723                 boolean useIdInCallbackEventName) {
724             mDiscoverySessionId = discoverySessionId;
725             mUseIdInCallbackEventName = useIdInCallbackEventName;
726             mCreateTimestampMs = System.currentTimeMillis();
727         }
728 
postEvent(String eventName, Bundle results)729         private void postEvent(String eventName, Bundle results) {
730             String finalEventName = eventName;
731             if (mUseIdInCallbackEventName) {
732                 finalEventName += "_" + mDiscoverySessionId;
733             }
734 
735             mEventFacade.postEvent(finalEventName, results);
736         }
737 
738         @Override
onPublishStarted(PublishDiscoverySession discoverySession)739         public void onPublishStarted(PublishDiscoverySession discoverySession) {
740             synchronized (mLock) {
741                 mDiscoverySessions.put(mDiscoverySessionId, discoverySession);
742             }
743 
744             Bundle mResults = new Bundle();
745             mResults.putInt("discoverySessionId", mDiscoverySessionId);
746             mResults.putLong("latencyMs", System.currentTimeMillis() - mCreateTimestampMs);
747             mResults.putLong("timestampMs", System.currentTimeMillis());
748             postEvent("WifiAwareSessionOnPublishStarted", mResults);
749         }
750 
751         @Override
onSubscribeStarted(SubscribeDiscoverySession discoverySession)752         public void onSubscribeStarted(SubscribeDiscoverySession discoverySession) {
753             synchronized (mLock) {
754                 mDiscoverySessions.put(mDiscoverySessionId, discoverySession);
755             }
756 
757             Bundle mResults = new Bundle();
758             mResults.putInt("discoverySessionId", mDiscoverySessionId);
759             mResults.putLong("latencyMs", System.currentTimeMillis() - mCreateTimestampMs);
760             mResults.putLong("timestampMs", System.currentTimeMillis());
761             postEvent("WifiAwareSessionOnSubscribeStarted", mResults);
762         }
763 
764         @Override
onSessionConfigUpdated()765         public void onSessionConfigUpdated() {
766             Bundle mResults = new Bundle();
767             mResults.putInt("discoverySessionId", mDiscoverySessionId);
768             postEvent("WifiAwareSessionOnSessionConfigUpdated", mResults);
769         }
770 
771         @Override
onSessionConfigFailed()772         public void onSessionConfigFailed() {
773             Bundle mResults = new Bundle();
774             mResults.putInt("discoverySessionId", mDiscoverySessionId);
775             postEvent("WifiAwareSessionOnSessionConfigFailed", mResults);
776         }
777 
778         @Override
onSessionTerminated()779         public void onSessionTerminated() {
780             Bundle mResults = new Bundle();
781             mResults.putInt("discoverySessionId", mDiscoverySessionId);
782             postEvent("WifiAwareSessionOnSessionTerminated", mResults);
783         }
784 
785         @Override
onServiceDiscovered(PeerHandle peerHandle, byte[] serviceSpecificInfo, List<byte[]> matchFilter)786         public void onServiceDiscovered(PeerHandle peerHandle,
787                 byte[] serviceSpecificInfo, List<byte[]> matchFilter) {
788             Bundle mResults = new Bundle();
789             mResults.putInt("discoverySessionId", mDiscoverySessionId);
790             mResults.putInt("peerId", peerHandle.peerId);
791             mResults.putByteArray("serviceSpecificInfo", serviceSpecificInfo);
792             mResults.putByteArray("matchFilter", new TlvBufferUtils.TlvConstructor(0,
793                     1).allocateAndPut(matchFilter).getArray());
794             ArrayList<String> matchFilterStrings = new ArrayList<>(matchFilter.size());
795             for (byte[] be: matchFilter) {
796                 matchFilterStrings.add(Base64.encodeToString(be, Base64.DEFAULT));
797             }
798             mResults.putStringArrayList("matchFilterList", matchFilterStrings);
799             mResults.putLong("timestampMs", System.currentTimeMillis());
800             postEvent("WifiAwareSessionOnServiceDiscovered", mResults);
801         }
802 
803         @Override
onMessageSendSucceeded(int messageId)804         public void onMessageSendSucceeded(int messageId) {
805             Bundle mResults = new Bundle();
806             mResults.putInt("discoverySessionId", mDiscoverySessionId);
807             mResults.putInt("messageId", messageId);
808             synchronized (mLock) {
809                 Long startTime = mMessageStartTime.get(messageId);
810                 if (startTime != null) {
811                     mResults.putLong("latencyMs",
812                             System.currentTimeMillis() - startTime.longValue());
813                     mMessageStartTime.remove(messageId);
814                 }
815             }
816             postEvent("WifiAwareSessionOnMessageSent", mResults);
817         }
818 
819         @Override
onMessageSendFailed(int messageId)820         public void onMessageSendFailed(int messageId) {
821             Bundle mResults = new Bundle();
822             mResults.putInt("discoverySessionId", mDiscoverySessionId);
823             mResults.putInt("messageId", messageId);
824             synchronized (mLock) {
825                 Long startTime = mMessageStartTime.get(messageId);
826                 if (startTime != null) {
827                     mResults.putLong("latencyMs",
828                             System.currentTimeMillis() - startTime.longValue());
829                     mMessageStartTime.remove(messageId);
830                 }
831             }
832             postEvent("WifiAwareSessionOnMessageSendFailed", mResults);
833         }
834 
835         @Override
onMessageReceived(PeerHandle peerHandle, byte[] message)836         public void onMessageReceived(PeerHandle peerHandle, byte[] message) {
837             Bundle mResults = new Bundle();
838             mResults.putInt("discoverySessionId", mDiscoverySessionId);
839             mResults.putInt("peerId", peerHandle.peerId);
840             mResults.putByteArray("message", message); // TODO: base64
841             mResults.putString("messageAsString", new String(message));
842             postEvent("WifiAwareSessionOnMessageReceived", mResults);
843         }
844     }
845 
846     class WifiAwareRangingListener implements RttManager.RttListener {
847         private int mCallbackId;
848         private int mSessionId;
849 
WifiAwareRangingListener(int callbackId, int sessionId)850         public WifiAwareRangingListener(int callbackId, int sessionId) {
851             mCallbackId = callbackId;
852             mSessionId = sessionId;
853         }
854 
855         @Override
onSuccess(RttResult[] results)856         public void onSuccess(RttResult[] results) {
857             Bundle bundle = new Bundle();
858             bundle.putInt("callbackId", mCallbackId);
859             bundle.putInt("sessionId", mSessionId);
860 
861             Parcelable[] resultBundles = new Parcelable[results.length];
862             for (int i = 0; i < results.length; i++) {
863                 resultBundles[i] = WifiRttManagerFacade.RangingListener.packRttResult(results[i]);
864             }
865             bundle.putParcelableArray("Results", resultBundles);
866 
867             mEventFacade.postEvent("WifiAwareRangingListenerOnSuccess", bundle);
868         }
869 
870         @Override
onFailure(int reason, String description)871         public void onFailure(int reason, String description) {
872             Bundle bundle = new Bundle();
873             bundle.putInt("callbackId", mCallbackId);
874             bundle.putInt("sessionId", mSessionId);
875             bundle.putInt("reason", reason);
876             bundle.putString("description", description);
877             mEventFacade.postEvent("WifiAwareRangingListenerOnFailure", bundle);
878         }
879 
880         @Override
onAborted()881         public void onAborted() {
882             Bundle bundle = new Bundle();
883             bundle.putInt("callbackId", mCallbackId);
884             bundle.putInt("sessionId", mSessionId);
885             mEventFacade.postEvent("WifiAwareRangingListenerOnAborted", bundle);
886         }
887 
888     }
889 
890     class WifiAwareStateChangedReceiver extends BroadcastReceiver {
891         @Override
onReceive(Context c, Intent intent)892         public void onReceive(Context c, Intent intent) {
893             boolean isAvailable = mMgr.isAvailable();
894             if (!isAvailable) {
895                 wifiAwareDestroyAll();
896             }
897             mEventFacade.postEvent(isAvailable ? "WifiAwareAvailable" : "WifiAwareNotAvailable",
898                     new Bundle());
899         }
900     }
901 }
902