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