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