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