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