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