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