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.net.NetworkSpecifier;
25 import android.net.wifi.RttManager;
26 import android.net.wifi.RttManager.RttResult;
27 import android.net.wifi.aware.AttachCallback;
28 import android.net.wifi.aware.ConfigRequest;
29 import android.net.wifi.aware.DiscoverySession;
30 import android.net.wifi.aware.DiscoverySessionCallback;
31 import android.net.wifi.aware.IdentityChangedListener;
32 import android.net.wifi.aware.PeerHandle;
33 import android.net.wifi.aware.PublishConfig;
34 import android.net.wifi.aware.PublishDiscoverySession;
35 import android.net.wifi.aware.SubscribeConfig;
36 import android.net.wifi.aware.SubscribeDiscoverySession;
37 import android.net.wifi.aware.TlvBufferUtils;
38 import android.net.wifi.aware.WifiAwareManager;
39 import android.net.wifi.aware.WifiAwareNetworkSpecifier;
40 import android.net.wifi.aware.WifiAwareSession;
41 import android.os.Bundle;
42 import android.os.Parcelable;
43 import android.os.RemoteException;
44 import android.util.Base64;
45 import android.util.SparseArray;
46 
47 import com.android.internal.annotations.GuardedBy;
48 
49 import libcore.util.HexEncoding;
50 
51 import com.googlecode.android_scripting.facade.EventFacade;
52 import com.googlecode.android_scripting.facade.FacadeManager;
53 import com.googlecode.android_scripting.jsonrpc.RpcReceiver;
54 import com.googlecode.android_scripting.rpc.Rpc;
55 import com.googlecode.android_scripting.rpc.RpcOptional;
56 import com.googlecode.android_scripting.rpc.RpcParameter;
57 
58 import org.json.JSONArray;
59 import org.json.JSONException;
60 import org.json.JSONObject;
61 
62 import java.util.List;
63 
64 /**
65  * WifiAwareManager functions.
66  */
67 public class WifiAwareManagerFacade extends RpcReceiver {
68     private final Service mService;
69     private final EventFacade mEventFacade;
70     private final WifiAwareStateChangedReceiver mStateChangedReceiver;
71 
72     private final Object mLock = new Object(); // lock access to the following vars
73 
74     @GuardedBy("mLock")
75     private WifiAwareManager mMgr;
76 
77     @GuardedBy("mLock")
78     private int mNextDiscoverySessionId = 1;
79     @GuardedBy("mLock")
80     private SparseArray<DiscoverySession> mDiscoverySessions = new SparseArray<>();
getNextDiscoverySessionId()81     private int getNextDiscoverySessionId() {
82         synchronized (mLock) {
83             return mNextDiscoverySessionId++;
84         }
85     }
86 
87     @GuardedBy("mLock")
88     private int mNextSessionId = 1;
89     @GuardedBy("mLock")
90     private SparseArray<WifiAwareSession> mSessions = new SparseArray<>();
getNextSessionId()91     private int getNextSessionId() {
92         synchronized (mLock) {
93             return mNextSessionId++;
94         }
95     }
96 
97     @GuardedBy("mLock")
98     private SparseArray<Long> mMessageStartTime = new SparseArray<>();
99 
100     private static final String NS_KEY_TYPE = "type";
101     private static final String NS_KEY_ROLE = "role";
102     private static final String NS_KEY_CLIENT_ID = "client_id";
103     private static final String NS_KEY_SESSION_ID = "session_id";
104     private static final String NS_KEY_PEER_ID = "peer_id";
105     private static final String NS_KEY_PEER_MAC = "peer_mac";
106     private static final String NS_KEY_PMK = "pmk";
107     private static final String NS_KEY_PASSPHRASE = "passphrase";
108 
getJsonString(WifiAwareNetworkSpecifier ns)109     private static String getJsonString(WifiAwareNetworkSpecifier ns) throws JSONException {
110         JSONObject j = new JSONObject();
111 
112         j.put(NS_KEY_TYPE, ns.type);
113         j.put(NS_KEY_ROLE, ns.role);
114         j.put(NS_KEY_CLIENT_ID, ns.clientId);
115         j.put(NS_KEY_SESSION_ID, ns.sessionId);
116         j.put(NS_KEY_PEER_ID, ns.peerId);
117         if (ns.peerMac != null) {
118             j.put(NS_KEY_PEER_MAC, Base64.encodeToString(ns.peerMac, Base64.DEFAULT));
119         }
120         if (ns.pmk != null) {
121             j.put(NS_KEY_PMK, Base64.encodeToString(ns.pmk, Base64.DEFAULT));
122         }
123         if (ns.passphrase != null) {
124             j.put(NS_KEY_PASSPHRASE, ns.passphrase);
125         }
126 
127         return j.toString();
128     }
129 
getNetworkSpecifier(JSONObject j)130     public static NetworkSpecifier getNetworkSpecifier(JSONObject j) throws JSONException {
131         if (j == null) {
132             return null;
133         }
134 
135         int type = 0, role = 0, clientId = 0, sessionId = 0, peerId = 0;
136         byte[] peerMac = null;
137         byte[] pmk = null;
138         String passphrase = null;
139 
140         if (j.has(NS_KEY_TYPE)) {
141             type = j.getInt((NS_KEY_TYPE));
142         }
143         if (j.has(NS_KEY_ROLE)) {
144             role = j.getInt((NS_KEY_ROLE));
145         }
146         if (j.has(NS_KEY_CLIENT_ID)) {
147             clientId = j.getInt((NS_KEY_CLIENT_ID));
148         }
149         if (j.has(NS_KEY_SESSION_ID)) {
150             sessionId = j.getInt((NS_KEY_SESSION_ID));
151         }
152         if (j.has(NS_KEY_PEER_ID)) {
153             peerId = j.getInt((NS_KEY_PEER_ID));
154         }
155         if (j.has(NS_KEY_PEER_MAC)) {
156             peerMac = Base64.decode(j.getString(NS_KEY_PEER_MAC), Base64.DEFAULT);
157         }
158         if (j.has(NS_KEY_PMK)) {
159             pmk = Base64.decode(j.getString(NS_KEY_PMK), Base64.DEFAULT);
160         }
161         if (j.has(NS_KEY_PASSPHRASE)) {
162             passphrase = j.getString((NS_KEY_PASSPHRASE));
163         }
164 
165         return new WifiAwareNetworkSpecifier(type, role, clientId, sessionId, peerId, peerMac, pmk,
166                 passphrase);
167     }
168 
getFilterData(JSONObject j)169     private static TlvBufferUtils.TlvConstructor getFilterData(JSONObject j) throws JSONException {
170         if (j == null) {
171             return null;
172         }
173 
174         TlvBufferUtils.TlvConstructor constructor = new TlvBufferUtils.TlvConstructor(0, 1);
175         constructor.allocate(255);
176 
177         if (j.has("int0")) {
178             constructor.putShort(0, (short) j.getInt("int0"));
179         }
180 
181         if (j.has("int1")) {
182             constructor.putShort(0, (short) j.getInt("int1"));
183         }
184 
185         if (j.has("data0")) {
186             constructor.putString(0, j.getString("data0"));
187         }
188 
189         if (j.has("data1")) {
190             constructor.putString(0, j.getString("data1"));
191         }
192 
193         return constructor;
194     }
195 
getConfigRequest(JSONObject j)196     private static ConfigRequest getConfigRequest(JSONObject j) throws JSONException {
197         if (j == null) {
198             return null;
199         }
200 
201         ConfigRequest.Builder builder = new ConfigRequest.Builder();
202 
203         if (j.has("Support5gBand")) {
204             builder.setSupport5gBand(j.getBoolean("Support5gBand"));
205         }
206         if (j.has("MasterPreference")) {
207             builder.setMasterPreference(j.getInt("MasterPreference"));
208         }
209         if (j.has("ClusterLow")) {
210             builder.setClusterLow(j.getInt("ClusterLow"));
211         }
212         if (j.has("ClusterHigh")) {
213             builder.setClusterHigh(j.getInt("ClusterHigh"));
214         }
215         if (j.has("DiscoveryWindowInterval")) {
216             JSONArray interval = j.getJSONArray("DiscoveryWindowInterval");
217             if (interval.length() != 2) {
218                 throw new JSONException(
219                         "Expect 'DiscoveryWindowInterval' to be an array with 2 elements!");
220             }
221             int intervalValue = interval.getInt(ConfigRequest.NAN_BAND_24GHZ);
222             if (intervalValue != ConfigRequest.DW_INTERVAL_NOT_INIT) {
223                 builder.setDiscoveryWindowInterval(ConfigRequest.NAN_BAND_24GHZ, intervalValue);
224             }
225             intervalValue = interval.getInt(ConfigRequest.NAN_BAND_5GHZ);
226             if (intervalValue != ConfigRequest.DW_INTERVAL_NOT_INIT) {
227                 builder.setDiscoveryWindowInterval(ConfigRequest.NAN_BAND_5GHZ, intervalValue);
228             }
229         }
230 
231         return builder.build();
232     }
233 
getPublishConfig(JSONObject j)234     private static PublishConfig getPublishConfig(JSONObject j) throws JSONException {
235         if (j == null) {
236             return null;
237         }
238 
239         PublishConfig.Builder builder = new PublishConfig.Builder();
240 
241         if (j.has("ServiceName")) {
242             builder.setServiceName(j.getString("ServiceName"));
243         }
244 
245         if (j.has("ServiceSpecificInfo")) {
246             String ssi = j.getString("ServiceSpecificInfo");
247             byte[] bytes = ssi.getBytes();
248             builder.setServiceSpecificInfo(bytes);
249         }
250 
251         if (j.has("MatchFilter")) {
252             TlvBufferUtils.TlvConstructor constructor = getFilterData(
253                     j.getJSONObject("MatchFilter"));
254             builder.setMatchFilter(
255                     new TlvBufferUtils.TlvIterable(0, 1, constructor.getArray()).toList());
256         }
257 
258         if (j.has("PublishType")) {
259             builder.setPublishType(j.getInt("PublishType"));
260         }
261         if (j.has("TtlSec")) {
262             builder.setTtlSec(j.getInt("TtlSec"));
263         }
264         if (j.has("EnableTerminateNotification")) {
265             builder.setTerminateNotificationEnabled(j.getBoolean("TerminateNotificationEnabled"));
266         }
267 
268         return builder.build();
269     }
270 
getSubscribeConfig(JSONObject j)271     private static SubscribeConfig getSubscribeConfig(JSONObject j) throws JSONException {
272         if (j == null) {
273             return null;
274         }
275 
276         SubscribeConfig.Builder builder = new SubscribeConfig.Builder();
277 
278         if (j.has("ServiceName")) {
279             builder.setServiceName(j.getString("ServiceName"));
280         }
281 
282         if (j.has("ServiceSpecificInfo")) {
283             String ssi = j.getString("ServiceSpecificInfo");
284             builder.setServiceSpecificInfo(ssi.getBytes());
285         }
286 
287         if (j.has("MatchFilter")) {
288             TlvBufferUtils.TlvConstructor constructor = getFilterData(
289                     j.getJSONObject("MatchFilter"));
290             builder.setMatchFilter(
291                     new TlvBufferUtils.TlvIterable(0, 1, constructor.getArray()).toList());
292         }
293 
294         if (j.has("SubscribeType")) {
295             builder.setSubscribeType(j.getInt("SubscribeType"));
296         }
297         if (j.has("TtlSec")) {
298             builder.setTtlSec(j.getInt("TtlSec"));
299         }
300         if (j.has("EnableTerminateNotification")) {
301             builder.setTerminateNotificationEnabled(j.getBoolean("TerminateNotificationEnabled"));
302         }
303 
304         return builder.build();
305     }
306 
WifiAwareManagerFacade(FacadeManager manager)307     public WifiAwareManagerFacade(FacadeManager manager) {
308         super(manager);
309         mService = manager.getService();
310 
311         mMgr = (WifiAwareManager) mService.getSystemService(Context.WIFI_AWARE_SERVICE);
312 
313         mEventFacade = manager.getReceiver(EventFacade.class);
314 
315         mStateChangedReceiver = new WifiAwareStateChangedReceiver();
316         IntentFilter filter = new IntentFilter(WifiAwareManager.ACTION_WIFI_AWARE_STATE_CHANGED);
317         mService.registerReceiver(mStateChangedReceiver, filter);
318     }
319 
320     @Override
shutdown()321     public void shutdown() {
322         wifiAwareDestroyAll();
323         mService.unregisterReceiver(mStateChangedReceiver);
324     }
325 
326     @Rpc(description = "Is Aware Usage Enabled?")
wifiIsAwareAvailable()327     public Boolean wifiIsAwareAvailable() throws RemoteException {
328         synchronized (mLock) {
329             return mMgr.isAvailable();
330         }
331     }
332 
333     @Rpc(description = "Destroy all Aware sessions and discovery sessions")
wifiAwareDestroyAll()334     public void wifiAwareDestroyAll() {
335         synchronized (mLock) {
336             for (int i = 0; i < mSessions.size(); ++i) {
337                 mSessions.valueAt(i).close();
338             }
339             mSessions.clear();
340 
341             /* discovery sessions automatically destroyed when containing Aware sessions
342              * destroyed */
343             mDiscoverySessions.clear();
344 
345             mMessageStartTime.clear();
346         }
347     }
348 
349     @Rpc(description = "Attach to Aware.")
wifiAwareAttach( @pcParametername = "awareConfig") @pcOptional JSONObject awareConfig)350     public Integer wifiAwareAttach(
351             @RpcParameter(name = "awareConfig") @RpcOptional JSONObject awareConfig)
352             throws RemoteException, JSONException {
353         synchronized (mLock) {
354             int sessionId = getNextSessionId();
355             mMgr.attach(null, getConfigRequest(awareConfig),
356                     new AwareAttachCallbackPostsEvents(sessionId),
357                     new AwareIdentityChangeListenerPostsEvents(sessionId));
358             return sessionId;
359         }
360     }
361 
362     @Rpc(description = "Destroy a Aware session.")
wifiAwareDestroy( @pcParametername = "clientId", description = "The client ID returned when a connection was created") Integer clientId)363     public void wifiAwareDestroy(
364             @RpcParameter(name = "clientId", description = "The client ID returned when a connection was created") Integer clientId)
365             throws RemoteException, JSONException {
366         WifiAwareSession session;
367         synchronized (mLock) {
368             session = mSessions.get(clientId);
369         }
370         if (session == null) {
371             throw new IllegalStateException(
372                     "Calling WifiAwareDisconnect before session (client ID " + clientId
373                             + ") is ready/or already disconnected");
374         }
375         session.close();
376     }
377 
378     @Rpc(description = "Publish.")
wifiAwarePublish( @pcParametername = "clientId", description = "The client ID returned when a connection was created") Integer clientId, @RpcParameter(name = "publishConfig") JSONObject publishConfig)379     public Integer wifiAwarePublish(
380             @RpcParameter(name = "clientId", description = "The client ID returned when a connection was created") Integer clientId,
381             @RpcParameter(name = "publishConfig") JSONObject publishConfig)
382             throws RemoteException, JSONException {
383         synchronized (mLock) {
384             WifiAwareSession session = mSessions.get(clientId);
385             if (session == null) {
386                 throw new IllegalStateException(
387                         "Calling WifiAwarePublish before session (client ID " + clientId
388                                 + ") is ready/or already disconnected");
389             }
390 
391             int discoverySessionId = getNextDiscoverySessionId();
392             session.publish(getPublishConfig(publishConfig),
393                     new AwareDiscoverySessionCallbackPostsEvents(discoverySessionId), null);
394             return discoverySessionId;
395         }
396     }
397 
398     @Rpc(description = "Subscribe.")
wifiAwareSubscribe( @pcParametername = "clientId", description = "The client ID returned when a connection was created") Integer clientId, @RpcParameter(name = "subscribeConfig") JSONObject subscribeConfig)399     public Integer wifiAwareSubscribe(
400             @RpcParameter(name = "clientId", description = "The client ID returned when a connection was created") Integer clientId,
401             @RpcParameter(name = "subscribeConfig") JSONObject subscribeConfig)
402             throws RemoteException, JSONException {
403         synchronized (mLock) {
404             WifiAwareSession session = mSessions.get(clientId);
405             if (session == null) {
406                 throw new IllegalStateException(
407                         "Calling WifiAwareSubscribe before session (client ID " + clientId
408                                 + ") is ready/or already disconnected");
409             }
410 
411             int discoverySessionId = getNextDiscoverySessionId();
412             session.subscribe(getSubscribeConfig(subscribeConfig),
413                     new AwareDiscoverySessionCallbackPostsEvents(discoverySessionId), null);
414             return discoverySessionId;
415         }
416     }
417 
418     @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)419     public void wifiAwareDestroyDiscoverySession(
420             @RpcParameter(name = "sessionId", description = "The discovery session ID returned when session was created using publish or subscribe") Integer sessionId)
421             throws RemoteException {
422         synchronized (mLock) {
423             DiscoverySession session = mDiscoverySessions.get(sessionId);
424             if (session == null) {
425                 throw new IllegalStateException(
426                         "Calling WifiAwareTerminateSession before session (session ID "
427                                 + sessionId + ") is ready");
428             }
429             session.close();
430             mDiscoverySessions.remove(sessionId);
431         }
432     }
433 
434     @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)435     public void wifiAwareSendMessage(
436             @RpcParameter(name = "sessionId", description = "The session ID returned when session"
437                     + " was created using publish or subscribe") Integer sessionId,
438             @RpcParameter(name = "peerId", description = "The ID of the peer being communicated "
439                     + "with. Obtained from a previous message or match session.") Integer peerId,
440             @RpcParameter(name = "messageId", description = "Arbitrary handle used for "
441                     + "identification of the message in the message status callbacks")
442                     Integer messageId,
443             @RpcParameter(name = "message") String message,
444             @RpcParameter(name = "retryCount", description = "Number of retries (0 for none) if "
445                     + "transmission fails due to no ACK reception") Integer retryCount)
446                     throws RemoteException {
447         DiscoverySession session;
448         synchronized (mLock) {
449             session = mDiscoverySessions.get(sessionId);
450         }
451         if (session == null) {
452             throw new IllegalStateException(
453                     "Calling WifiAwareSendMessage before session (session ID " + sessionId
454                             + " is ready");
455         }
456         byte[] bytes = null;
457         if (message != null) {
458             bytes = message.getBytes();
459         }
460 
461         synchronized (mLock) {
462             mMessageStartTime.put(messageId, System.currentTimeMillis());
463         }
464         session.sendMessage(new PeerHandle(peerId), messageId, bytes, retryCount);
465     }
466 
467     @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)468     public void wifiAwareStartRanging(
469             @RpcParameter(name = "callbackId") Integer callbackId,
470             @RpcParameter(name = "sessionId", description = "The session ID returned when session was created using publish or subscribe") Integer sessionId,
471             @RpcParameter(name = "rttParams", description = "RTT session parameters.") JSONArray rttParams) throws RemoteException, JSONException {
472         DiscoverySession session;
473         synchronized (mLock) {
474             session = mDiscoverySessions.get(sessionId);
475         }
476         if (session == null) {
477             throw new IllegalStateException(
478                     "Calling WifiAwareStartRanging before session (session ID "
479                             + sessionId + " is ready");
480         }
481         RttManager.RttParams[] rParams = new RttManager.RttParams[rttParams.length()];
482         for (int i = 0; i < rttParams.length(); i++) {
483             rParams[i] = WifiRttManagerFacade.parseRttParam(rttParams.getJSONObject(i));
484         }
485         session.startRanging(rParams, new WifiAwareRangingListener(callbackId, sessionId));
486     }
487 
488     @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)489     public String wifiAwareCreateNetworkSpecifier(
490             @RpcParameter(name = "sessionId", description = "The session ID returned when session was created using publish or subscribe")
491                     Integer sessionId,
492             @RpcParameter(name = "peerId", description = "The ID of the peer (obtained through OnMatch or OnMessageReceived")
493                     Integer peerId,
494             @RpcParameter(name = "passphrase", description = "Passphrase of the data-path. Optional, can be empty/null.")
495             @RpcOptional String passphrase) throws JSONException {
496         DiscoverySession session;
497         synchronized (mLock) {
498             session = mDiscoverySessions.get(sessionId);
499         }
500         if (session == null) {
501             throw new IllegalStateException(
502                     "Calling WifiAwareStartRanging before session (session ID "
503                             + sessionId + " is ready");
504         }
505         NetworkSpecifier ns = null;
506         if (passphrase == null || passphrase.length() == 0) {
507             ns = session.createNetworkSpecifierOpen(new PeerHandle(peerId));
508         } else {
509             ns = session.createNetworkSpecifierPassphrase(new PeerHandle(peerId), passphrase);
510         }
511 
512         return getJsonString((WifiAwareNetworkSpecifier) ns);
513     }
514 
515     private class AwareAttachCallbackPostsEvents extends AttachCallback {
516         private int mSessionId;
517         private long mCreateTimestampMs;
518 
AwareAttachCallbackPostsEvents(int sessionId)519         public AwareAttachCallbackPostsEvents(int sessionId) {
520             mSessionId = sessionId;
521             mCreateTimestampMs = System.currentTimeMillis();
522         }
523 
524         @Override
onAttached(WifiAwareSession session)525         public void onAttached(WifiAwareSession session) {
526             synchronized (mLock) {
527                 mSessions.put(mSessionId, session);
528             }
529 
530             Bundle mResults = new Bundle();
531             mResults.putInt("sessionId", mSessionId);
532             mResults.putLong("latencyMs", System.currentTimeMillis() - mCreateTimestampMs);
533             mResults.putLong("timestampMs", System.currentTimeMillis());
534             mEventFacade.postEvent("WifiAwareOnAttached", mResults);
535         }
536 
537         @Override
onAttachFailed()538         public void onAttachFailed() {
539             Bundle mResults = new Bundle();
540             mResults.putInt("sessionId", mSessionId);
541             mResults.putLong("latencyMs", System.currentTimeMillis() - mCreateTimestampMs);
542             mEventFacade.postEvent("WifiAwareOnAttachFailed", mResults);
543         }
544     }
545 
546     private class AwareIdentityChangeListenerPostsEvents extends IdentityChangedListener {
547         private int mSessionId;
548 
AwareIdentityChangeListenerPostsEvents(int sessionId)549         public AwareIdentityChangeListenerPostsEvents(int sessionId) {
550             mSessionId = sessionId;
551         }
552 
553         @Override
onIdentityChanged(byte[] mac)554         public void onIdentityChanged(byte[] mac) {
555             Bundle mResults = new Bundle();
556             mResults.putInt("sessionId", mSessionId);
557             mResults.putString("mac", String.valueOf(HexEncoding.encode(mac)));
558             mResults.putLong("timestampMs", System.currentTimeMillis());
559             mEventFacade.postEvent("WifiAwareOnIdentityChanged", mResults);
560         }
561     }
562 
563     private class AwareDiscoverySessionCallbackPostsEvents extends
564             DiscoverySessionCallback {
565         private int mDiscoverySessionId;
566         private long mCreateTimestampMs;
567 
AwareDiscoverySessionCallbackPostsEvents(int discoverySessionId)568         public AwareDiscoverySessionCallbackPostsEvents(int discoverySessionId) {
569             mDiscoverySessionId = discoverySessionId;
570             mCreateTimestampMs = System.currentTimeMillis();
571         }
572 
573         @Override
onPublishStarted(PublishDiscoverySession discoverySession)574         public void onPublishStarted(PublishDiscoverySession discoverySession) {
575             synchronized (mLock) {
576                 mDiscoverySessions.put(mDiscoverySessionId, discoverySession);
577             }
578 
579             Bundle mResults = new Bundle();
580             mResults.putInt("discoverySessionId", mDiscoverySessionId);
581             mResults.putLong("latencyMs", System.currentTimeMillis() - mCreateTimestampMs);
582             mResults.putLong("timestampMs", System.currentTimeMillis());
583             mEventFacade.postEvent("WifiAwareSessionOnPublishStarted", mResults);
584         }
585 
586         @Override
onSubscribeStarted(SubscribeDiscoverySession discoverySession)587         public void onSubscribeStarted(SubscribeDiscoverySession discoverySession) {
588             synchronized (mLock) {
589                 mDiscoverySessions.put(mDiscoverySessionId, discoverySession);
590             }
591 
592             Bundle mResults = new Bundle();
593             mResults.putInt("discoverySessionId", mDiscoverySessionId);
594             mResults.putLong("latencyMs", System.currentTimeMillis() - mCreateTimestampMs);
595             mResults.putLong("timestampMs", System.currentTimeMillis());
596             mEventFacade.postEvent("WifiAwareSessionOnSubscribeStarted", mResults);
597         }
598 
599         @Override
onSessionConfigUpdated()600         public void onSessionConfigUpdated() {
601             Bundle mResults = new Bundle();
602             mResults.putInt("discoverySessionId", mDiscoverySessionId);
603             mEventFacade.postEvent("WifiAwareSessionOnSessionConfigUpdated", mResults);
604         }
605 
606         @Override
onSessionConfigFailed()607         public void onSessionConfigFailed() {
608             Bundle mResults = new Bundle();
609             mResults.putInt("discoverySessionId", mDiscoverySessionId);
610             mEventFacade.postEvent("WifiAwareSessionOnSessionConfigFailed", mResults);
611         }
612 
613         @Override
onSessionTerminated()614         public void onSessionTerminated() {
615             Bundle mResults = new Bundle();
616             mResults.putInt("discoverySessionId", mDiscoverySessionId);
617             mEventFacade.postEvent("WifiAwareSessionOnSessionTerminated", mResults);
618         }
619 
620         @Override
onServiceDiscovered(PeerHandle peerHandle, byte[] serviceSpecificInfo, List<byte[]> matchFilter)621         public void onServiceDiscovered(PeerHandle peerHandle,
622                 byte[] serviceSpecificInfo, List<byte[]> matchFilter) {
623             Bundle mResults = new Bundle();
624             mResults.putInt("discoverySessionId", mDiscoverySessionId);
625             mResults.putInt("peerId", peerHandle.peerId);
626             mResults.putByteArray("serviceSpecificInfo", serviceSpecificInfo); // TODO: base64
627             mResults.putByteArray("matchFilter", new TlvBufferUtils.TlvConstructor(0,
628                     1).allocateAndPut(matchFilter).getArray()); // TODO: base64
629             mResults.putLong("timestampMs", System.currentTimeMillis());
630             mEventFacade.postEvent("WifiAwareSessionOnServiceDiscovered", mResults);
631         }
632 
633         @Override
onMessageSendSucceeded(int messageId)634         public void onMessageSendSucceeded(int messageId) {
635             Bundle mResults = new Bundle();
636             mResults.putInt("discoverySessionId", mDiscoverySessionId);
637             mResults.putInt("messageId", messageId);
638             synchronized (mLock) {
639                 Long startTime = mMessageStartTime.get(messageId);
640                 if (startTime != null) {
641                     mResults.putLong("latencyMs",
642                             System.currentTimeMillis() - startTime.longValue());
643                     mMessageStartTime.remove(messageId);
644                 }
645             }
646             mEventFacade.postEvent("WifiAwareSessionOnMessageSent", mResults);
647         }
648 
649         @Override
onMessageSendFailed(int messageId)650         public void onMessageSendFailed(int messageId) {
651             Bundle mResults = new Bundle();
652             mResults.putInt("discoverySessionId", mDiscoverySessionId);
653             mResults.putInt("messageId", messageId);
654             synchronized (mLock) {
655                 Long startTime = mMessageStartTime.get(messageId);
656                 if (startTime != null) {
657                     mResults.putLong("latencyMs",
658                             System.currentTimeMillis() - startTime.longValue());
659                     mMessageStartTime.remove(messageId);
660                 }
661             }
662             mEventFacade.postEvent("WifiAwareSessionOnMessageSendFailed", mResults);
663         }
664 
665         @Override
onMessageReceived(PeerHandle peerHandle, byte[] message)666         public void onMessageReceived(PeerHandle peerHandle, byte[] message) {
667             Bundle mResults = new Bundle();
668             mResults.putInt("discoverySessionId", mDiscoverySessionId);
669             mResults.putInt("peerId", peerHandle.peerId);
670             mResults.putByteArray("message", message); // TODO: base64
671             mResults.putString("messageAsString", new String(message));
672             mEventFacade.postEvent("WifiAwareSessionOnMessageReceived", mResults);
673         }
674     }
675 
676     class WifiAwareRangingListener implements RttManager.RttListener {
677         private int mCallbackId;
678         private int mSessionId;
679 
WifiAwareRangingListener(int callbackId, int sessionId)680         public WifiAwareRangingListener(int callbackId, int sessionId) {
681             mCallbackId = callbackId;
682             mSessionId = sessionId;
683         }
684 
685         @Override
onSuccess(RttResult[] results)686         public void onSuccess(RttResult[] results) {
687             Bundle bundle = new Bundle();
688             bundle.putInt("callbackId", mCallbackId);
689             bundle.putInt("sessionId", mSessionId);
690 
691             Parcelable[] resultBundles = new Parcelable[results.length];
692             for (int i = 0; i < results.length; i++) {
693                 resultBundles[i] = WifiRttManagerFacade.RangingListener.packRttResult(results[i]);
694             }
695             bundle.putParcelableArray("Results", resultBundles);
696 
697             mEventFacade.postEvent("WifiAwareRangingListenerOnSuccess", bundle);
698         }
699 
700         @Override
onFailure(int reason, String description)701         public void onFailure(int reason, String description) {
702             Bundle bundle = new Bundle();
703             bundle.putInt("callbackId", mCallbackId);
704             bundle.putInt("sessionId", mSessionId);
705             bundle.putInt("reason", reason);
706             bundle.putString("description", description);
707             mEventFacade.postEvent("WifiAwareRangingListenerOnFailure", bundle);
708         }
709 
710         @Override
onAborted()711         public void onAborted() {
712             Bundle bundle = new Bundle();
713             bundle.putInt("callbackId", mCallbackId);
714             bundle.putInt("sessionId", mSessionId);
715             mEventFacade.postEvent("WifiAwareRangingListenerOnAborted", bundle);
716         }
717 
718     }
719 
720     class WifiAwareStateChangedReceiver extends BroadcastReceiver {
721         @Override
onReceive(Context c, Intent intent)722         public void onReceive(Context c, Intent intent) {
723             boolean isAvailable = mMgr.isAvailable();
724             if (!isAvailable) {
725                 wifiAwareDestroyAll();
726             }
727             mEventFacade.postEvent(isAvailable ? "WifiAwareAvailable" : "WifiAwareNotAvailable",
728                     new Bundle());
729         }
730     }
731 }
732