WifiAwareNativeApi.java revision 9a4b9d97dc46509222e6192596fbdb37a2b8fca6
1/* 2 * Copyright (C) 2016 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 17package com.android.server.wifi.aware; 18 19import android.hardware.wifi.V1_0.IWifiNanIface; 20import android.hardware.wifi.V1_0.NanBandIndex; 21import android.hardware.wifi.V1_0.NanBandSpecificConfig; 22import android.hardware.wifi.V1_0.NanCipherSuiteType; 23import android.hardware.wifi.V1_0.NanConfigRequest; 24import android.hardware.wifi.V1_0.NanDataPathSecurityType; 25import android.hardware.wifi.V1_0.NanEnableRequest; 26import android.hardware.wifi.V1_0.NanInitiateDataPathRequest; 27import android.hardware.wifi.V1_0.NanMatchAlg; 28import android.hardware.wifi.V1_0.NanPublishRequest; 29import android.hardware.wifi.V1_0.NanRespondToDataPathIndicationRequest; 30import android.hardware.wifi.V1_0.NanSubscribeRequest; 31import android.hardware.wifi.V1_0.NanTransmitFollowupRequest; 32import android.hardware.wifi.V1_0.NanTxType; 33import android.hardware.wifi.V1_0.WifiStatus; 34import android.hardware.wifi.V1_0.WifiStatusCode; 35import android.net.wifi.aware.ConfigRequest; 36import android.net.wifi.aware.PublishConfig; 37import android.net.wifi.aware.SubscribeConfig; 38import android.os.RemoteException; 39import android.os.ShellCommand; 40import android.util.Log; 41 42import libcore.util.HexEncoding; 43 44import java.io.FileDescriptor; 45import java.io.PrintWriter; 46import java.nio.charset.StandardCharsets; 47import java.util.ArrayList; 48import java.util.HashMap; 49import java.util.Map; 50 51/** 52 * Translates Wi-Fi Aware requests from the framework to the HAL (HIDL). 53 * 54 * Delegates the management of the NAN interface to WifiAwareNativeManager. 55 */ 56public class WifiAwareNativeApi implements WifiAwareShellCommand.DelegatedShellCommand { 57 private static final String TAG = "WifiAwareNativeApi"; 58 private static final boolean DBG = false; 59 private static final boolean VDBG = false; // STOPSHIP if true 60 61 private static final String SERVICE_NAME_FOR_OOB_DATA_PATH = "Wi-Fi Aware Data Path"; 62 63 private final WifiAwareNativeManager mHal; 64 65 public WifiAwareNativeApi(WifiAwareNativeManager wifiAwareNativeManager) { 66 mHal = wifiAwareNativeManager; 67 onReset(); 68 } 69 70 /* 71 * Parameters settable through the shell command. 72 * see wifi/1.0/types.hal NanBandSpecificConfig.discoveryWindowIntervalVal for description 73 */ 74 public static final String PARAM_DW_DEFAULT_24GHZ = "dw_default_24ghz"; 75 public static final int PARAM_DW_DEFAULT_24GHZ_DEFAULT = -1; // Firmware default 76 public static final String PARAM_DW_DEFAULT_5GHZ = "dw_default_5ghz"; 77 public static final int PARAM_DW_DEFAULT_5GHZ_DEFAULT = -1; // Firmware default 78 public static final String PARAM_DW_ON_INACTIVE_24GHZ = "dw_on_inactive_24ghz"; 79 public static final int PARAM_DW_ON_INACTIVE_24GHZ_DEFAULT = 4; // 4 -> DW=8, latency=4s 80 public static final String PARAM_DW_ON_INACTIVE_5GHZ = "dw_on_inactive_5ghz"; 81 public static final int PARAM_DW_ON_INACTIVE_5GHZ_DEFAULT = 0; // 0 = disabled 82 public static final String PARAM_DW_ON_IDLE_24GHZ = "dw_on_idle_24ghz"; 83 public static final int PARAM_DW_ON_IDLE_24GHZ_DEFAULT = -1; // NOP (but disabling on IDLE) 84 public static final String PARAM_DW_ON_IDLE_5GHZ = "dw_on_idle_5ghz"; 85 public static final int PARAM_DW_ON_IDLE_5GHZ_DEFAULT = -1; // NOP (but disabling on IDLE) 86 87 public static final String PARAM_MAC_RANDOM_INTERVAL_SEC = "mac_random_interval_sec"; 88 public static final int PARAM_MAC_RANDOM_INTERVAL_SEC_DEFAULT = 1800; // 30 minutes 89 90 private Map<String, Integer> mSettableParameters = new HashMap<>(); 91 92 /** 93 * Interpreter of adb shell command 'adb shell wifiaware native_api ...'. 94 * 95 * @return -1 if parameter not recognized or invalid value, 0 otherwise. 96 */ 97 @Override 98 public int onCommand(ShellCommand parentShell) { 99 final PrintWriter pw = parentShell.getErrPrintWriter(); 100 101 String subCmd = parentShell.getNextArgRequired(); 102 if (VDBG) Log.v(TAG, "onCommand: subCmd='" + subCmd + "'"); 103 switch (subCmd) { 104 case "set": { 105 String name = parentShell.getNextArgRequired(); 106 if (VDBG) Log.v(TAG, "onCommand: name='" + name + "'"); 107 if (!mSettableParameters.containsKey(name)) { 108 pw.println("Unknown parameter name -- '" + name + "'"); 109 return -1; 110 } 111 112 String valueStr = parentShell.getNextArgRequired(); 113 if (VDBG) Log.v(TAG, "onCommand: valueStr='" + valueStr + "'"); 114 int value; 115 try { 116 value = Integer.valueOf(valueStr); 117 } catch (NumberFormatException e) { 118 pw.println("Can't convert value to integer -- '" + valueStr + "'"); 119 return -1; 120 } 121 mSettableParameters.put(name, value); 122 return 0; 123 } 124 case "get": { 125 String name = parentShell.getNextArgRequired(); 126 if (VDBG) Log.v(TAG, "onCommand: name='" + name + "'"); 127 if (!mSettableParameters.containsKey(name)) { 128 pw.println("Unknown parameter name -- '" + name + "'"); 129 return -1; 130 } 131 132 parentShell.getOutPrintWriter().println((int) mSettableParameters.get(name)); 133 return 0; 134 } 135 default: 136 pw.println("Unknown 'wifiaware native_api <cmd>'"); 137 } 138 139 return -1; 140 } 141 142 @Override 143 public void onReset() { 144 mSettableParameters.put(PARAM_DW_DEFAULT_24GHZ, PARAM_DW_DEFAULT_24GHZ_DEFAULT); 145 mSettableParameters.put(PARAM_DW_DEFAULT_5GHZ, PARAM_DW_DEFAULT_5GHZ_DEFAULT); 146 mSettableParameters.put(PARAM_DW_ON_INACTIVE_24GHZ, PARAM_DW_ON_INACTIVE_24GHZ_DEFAULT); 147 mSettableParameters.put(PARAM_DW_ON_INACTIVE_5GHZ, PARAM_DW_ON_INACTIVE_5GHZ_DEFAULT); 148 mSettableParameters.put(PARAM_DW_ON_IDLE_24GHZ, PARAM_DW_ON_IDLE_24GHZ_DEFAULT); 149 mSettableParameters.put(PARAM_DW_ON_IDLE_5GHZ, PARAM_DW_ON_IDLE_5GHZ_DEFAULT); 150 151 mSettableParameters.put(PARAM_MAC_RANDOM_INTERVAL_SEC, 152 PARAM_MAC_RANDOM_INTERVAL_SEC_DEFAULT); 153 } 154 155 @Override 156 public void onHelp(String command, ShellCommand parentShell) { 157 final PrintWriter pw = parentShell.getOutPrintWriter(); 158 159 pw.println(" " + command); 160 pw.println(" set <name> <value>: sets named parameter to value. Names: " 161 + mSettableParameters.keySet()); 162 pw.println(" get <name>: gets named parameter value. Names: " 163 + mSettableParameters.keySet()); 164 } 165 166 /** 167 * Query the firmware's capabilities. 168 * 169 * @param transactionId Transaction ID for the transaction - used in the async callback to 170 * match with the original request. 171 */ 172 public boolean getCapabilities(short transactionId) { 173 if (VDBG) Log.v(TAG, "getCapabilities: transactionId=" + transactionId); 174 175 IWifiNanIface iface = mHal.getWifiNanIface(); 176 if (iface == null) { 177 Log.e(TAG, "getCapabilities: null interface"); 178 return false; 179 } 180 181 try { 182 WifiStatus status = iface.getCapabilitiesRequest(transactionId); 183 if (status.code == WifiStatusCode.SUCCESS) { 184 return true; 185 } else { 186 Log.e(TAG, "getCapabilities: error: " + statusString(status)); 187 return false; 188 } 189 } catch (RemoteException e) { 190 Log.e(TAG, "getCapabilities: exception: " + e); 191 return false; 192 } 193 } 194 195 /** 196 * Enable and configure Aware. 197 * 198 * @param transactionId Transaction ID for the transaction - used in the 199 * async callback to match with the original request. 200 * @param configRequest Requested Aware configuration. 201 * @param notifyIdentityChange Indicates whether or not to get address change callbacks. 202 * @param initialConfiguration Specifies whether initial configuration 203 * (true) or an update (false) to the configuration. 204 * @param isInteractive PowerManager.isInteractive 205 * @param isIdle PowerManager.isIdle 206 */ 207 public boolean enableAndConfigure(short transactionId, ConfigRequest configRequest, 208 boolean notifyIdentityChange, boolean initialConfiguration, boolean isInteractive, 209 boolean isIdle) { 210 if (VDBG) { 211 Log.v(TAG, "enableAndConfigure: transactionId=" + transactionId + ", configRequest=" 212 + configRequest + ", notifyIdentityChange=" + notifyIdentityChange 213 + ", initialConfiguration=" + initialConfiguration 214 + ", isInteractive=" + isInteractive + ", isIdle=" + isIdle); 215 } 216 217 IWifiNanIface iface = mHal.getWifiNanIface(); 218 if (iface == null) { 219 Log.e(TAG, "enableAndConfigure: null interface"); 220 return false; 221 } 222 223 try { 224 WifiStatus status; 225 if (initialConfiguration) { 226 // translate framework to HIDL configuration 227 NanEnableRequest req = new NanEnableRequest(); 228 229 req.operateInBand[NanBandIndex.NAN_BAND_24GHZ] = true; 230 req.operateInBand[NanBandIndex.NAN_BAND_5GHZ] = configRequest.mSupport5gBand; 231 req.hopCountMax = 2; 232 req.configParams.masterPref = (byte) configRequest.mMasterPreference; 233 req.configParams.disableDiscoveryAddressChangeIndication = !notifyIdentityChange; 234 req.configParams.disableStartedClusterIndication = !notifyIdentityChange; 235 req.configParams.disableJoinedClusterIndication = !notifyIdentityChange; 236 req.configParams.includePublishServiceIdsInBeacon = true; 237 req.configParams.numberOfPublishServiceIdsInBeacon = 0; 238 req.configParams.includeSubscribeServiceIdsInBeacon = true; 239 req.configParams.numberOfSubscribeServiceIdsInBeacon = 0; 240 req.configParams.rssiWindowSize = 8; 241 req.configParams.macAddressRandomizationIntervalSec = mSettableParameters.get( 242 PARAM_MAC_RANDOM_INTERVAL_SEC); 243 244 NanBandSpecificConfig config24 = new NanBandSpecificConfig(); 245 config24.rssiClose = 60; 246 config24.rssiMiddle = 70; 247 config24.rssiCloseProximity = 60; 248 config24.dwellTimeMs = (byte) 200; 249 config24.scanPeriodSec = 20; 250 if (configRequest.mDiscoveryWindowInterval[ConfigRequest.NAN_BAND_24GHZ] 251 == ConfigRequest.DW_INTERVAL_NOT_INIT) { 252 config24.validDiscoveryWindowIntervalVal = false; 253 } else { 254 config24.validDiscoveryWindowIntervalVal = true; 255 config24.discoveryWindowIntervalVal = 256 (byte) configRequest.mDiscoveryWindowInterval[ConfigRequest 257 .NAN_BAND_24GHZ]; 258 } 259 req.configParams.bandSpecificConfig[NanBandIndex.NAN_BAND_24GHZ] = config24; 260 261 NanBandSpecificConfig config5 = new NanBandSpecificConfig(); 262 config5.rssiClose = 60; 263 config5.rssiMiddle = 75; 264 config5.rssiCloseProximity = 60; 265 config5.dwellTimeMs = (byte) 200; 266 config5.scanPeriodSec = 20; 267 if (configRequest.mDiscoveryWindowInterval[ConfigRequest.NAN_BAND_5GHZ] 268 == ConfigRequest.DW_INTERVAL_NOT_INIT) { 269 config5.validDiscoveryWindowIntervalVal = false; 270 } else { 271 config5.validDiscoveryWindowIntervalVal = true; 272 config5.discoveryWindowIntervalVal = 273 (byte) configRequest.mDiscoveryWindowInterval[ConfigRequest 274 .NAN_BAND_5GHZ]; 275 } 276 req.configParams.bandSpecificConfig[NanBandIndex.NAN_BAND_5GHZ] = config5; 277 278 req.debugConfigs.validClusterIdVals = true; 279 req.debugConfigs.clusterIdTopRangeVal = (short) configRequest.mClusterHigh; 280 req.debugConfigs.clusterIdBottomRangeVal = (short) configRequest.mClusterLow; 281 req.debugConfigs.validIntfAddrVal = false; 282 req.debugConfigs.validOuiVal = false; 283 req.debugConfigs.ouiVal = 0; 284 req.debugConfigs.validRandomFactorForceVal = false; 285 req.debugConfigs.randomFactorForceVal = 0; 286 req.debugConfigs.validHopCountForceVal = false; 287 req.debugConfigs.hopCountForceVal = 0; 288 req.debugConfigs.validDiscoveryChannelVal = false; 289 req.debugConfigs.discoveryChannelMhzVal[NanBandIndex.NAN_BAND_24GHZ] = 0; 290 req.debugConfigs.discoveryChannelMhzVal[NanBandIndex.NAN_BAND_5GHZ] = 0; 291 req.debugConfigs.validUseBeaconsInBandVal = false; 292 req.debugConfigs.useBeaconsInBandVal[NanBandIndex.NAN_BAND_24GHZ] = true; 293 req.debugConfigs.useBeaconsInBandVal[NanBandIndex.NAN_BAND_5GHZ] = true; 294 req.debugConfigs.validUseSdfInBandVal = false; 295 req.debugConfigs.useSdfInBandVal[NanBandIndex.NAN_BAND_24GHZ] = true; 296 req.debugConfigs.useSdfInBandVal[NanBandIndex.NAN_BAND_5GHZ] = true; 297 298 updateConfigForPowerSettings(req.configParams, isInteractive, isIdle); 299 300 status = iface.enableRequest(transactionId, req); 301 } else { 302 NanConfigRequest req = new NanConfigRequest(); 303 req.masterPref = (byte) configRequest.mMasterPreference; 304 req.disableDiscoveryAddressChangeIndication = !notifyIdentityChange; 305 req.disableStartedClusterIndication = !notifyIdentityChange; 306 req.disableJoinedClusterIndication = !notifyIdentityChange; 307 req.includePublishServiceIdsInBeacon = true; 308 req.numberOfPublishServiceIdsInBeacon = 0; 309 req.includeSubscribeServiceIdsInBeacon = true; 310 req.numberOfSubscribeServiceIdsInBeacon = 0; 311 req.rssiWindowSize = 8; 312 req.macAddressRandomizationIntervalSec = mSettableParameters.get( 313 PARAM_MAC_RANDOM_INTERVAL_SEC); 314 315 NanBandSpecificConfig config24 = new NanBandSpecificConfig(); 316 config24.rssiClose = 60; 317 config24.rssiMiddle = 70; 318 config24.rssiCloseProximity = 60; 319 config24.dwellTimeMs = (byte) 200; 320 config24.scanPeriodSec = 20; 321 if (configRequest.mDiscoveryWindowInterval[ConfigRequest.NAN_BAND_24GHZ] 322 == ConfigRequest.DW_INTERVAL_NOT_INIT) { 323 config24.validDiscoveryWindowIntervalVal = false; 324 } else { 325 config24.validDiscoveryWindowIntervalVal = true; 326 config24.discoveryWindowIntervalVal = 327 (byte) configRequest.mDiscoveryWindowInterval[ConfigRequest 328 .NAN_BAND_24GHZ]; 329 } 330 req.bandSpecificConfig[NanBandIndex.NAN_BAND_24GHZ] = config24; 331 332 NanBandSpecificConfig config5 = new NanBandSpecificConfig(); 333 config5.rssiClose = 60; 334 config5.rssiMiddle = 75; 335 config5.rssiCloseProximity = 60; 336 config5.dwellTimeMs = (byte) 200; 337 config5.scanPeriodSec = 20; 338 if (configRequest.mDiscoveryWindowInterval[ConfigRequest.NAN_BAND_5GHZ] 339 == ConfigRequest.DW_INTERVAL_NOT_INIT) { 340 config5.validDiscoveryWindowIntervalVal = false; 341 } else { 342 config5.validDiscoveryWindowIntervalVal = true; 343 config5.discoveryWindowIntervalVal = 344 (byte) configRequest.mDiscoveryWindowInterval[ConfigRequest 345 .NAN_BAND_5GHZ]; 346 } 347 req.bandSpecificConfig[NanBandIndex.NAN_BAND_5GHZ] = config5; 348 349 updateConfigForPowerSettings(req, isInteractive, isIdle); 350 351 status = iface.configRequest(transactionId, req); 352 } 353 if (status.code == WifiStatusCode.SUCCESS) { 354 return true; 355 } else { 356 Log.e(TAG, "enableAndConfigure: error: " + statusString(status)); 357 return false; 358 } 359 } catch (RemoteException e) { 360 Log.e(TAG, "enableAndConfigure: exception: " + e); 361 return false; 362 } 363 } 364 365 /** 366 * Disable Aware. 367 * 368 * @param transactionId transactionId Transaction ID for the transaction - 369 * used in the async callback to match with the original request. 370 */ 371 public boolean disable(short transactionId) { 372 if (VDBG) Log.d(TAG, "disable"); 373 374 IWifiNanIface iface = mHal.getWifiNanIface(); 375 if (iface == null) { 376 Log.e(TAG, "disable: null interface"); 377 return false; 378 } 379 380 try { 381 WifiStatus status = iface.disableRequest(transactionId); 382 if (status.code == WifiStatusCode.SUCCESS) { 383 return true; 384 } else { 385 Log.e(TAG, "disable: error: " + statusString(status)); 386 return false; 387 } 388 } catch (RemoteException e) { 389 Log.e(TAG, "disable: exception: " + e); 390 return false; 391 } 392 } 393 394 /** 395 * Start or modify a service publish session. 396 * 397 * @param transactionId transactionId Transaction ID for the transaction - 398 * used in the async callback to match with the original request. 399 * @param publishId ID of the requested session - 0 to request a new publish 400 * session. 401 * @param publishConfig Configuration of the discovery session. 402 */ 403 public boolean publish(short transactionId, byte publishId, PublishConfig publishConfig) { 404 if (VDBG) { 405 Log.d(TAG, "publish: transactionId=" + transactionId + ", publishId=" + publishId 406 + ", config=" + publishConfig); 407 } 408 409 IWifiNanIface iface = mHal.getWifiNanIface(); 410 if (iface == null) { 411 Log.e(TAG, "publish: null interface"); 412 return false; 413 } 414 415 NanPublishRequest req = new NanPublishRequest(); 416 req.baseConfigs.sessionId = publishId; 417 req.baseConfigs.ttlSec = (short) publishConfig.mTtlSec; 418 req.baseConfigs.discoveryWindowPeriod = 1; 419 req.baseConfigs.discoveryCount = 0; 420 convertNativeByteArrayToArrayList(publishConfig.mServiceName, req.baseConfigs.serviceName); 421 req.baseConfigs.discoveryMatchIndicator = NanMatchAlg.MATCH_NEVER; 422 convertNativeByteArrayToArrayList(publishConfig.mServiceSpecificInfo, 423 req.baseConfigs.serviceSpecificInfo); 424 convertNativeByteArrayToArrayList(publishConfig.mMatchFilter, 425 publishConfig.mPublishType == PublishConfig.PUBLISH_TYPE_UNSOLICITED 426 ? req.baseConfigs.txMatchFilter : req.baseConfigs.rxMatchFilter); 427 req.baseConfigs.useRssiThreshold = false; 428 req.baseConfigs.disableDiscoveryTerminationIndication = 429 !publishConfig.mEnableTerminateNotification; 430 req.baseConfigs.disableMatchExpirationIndication = true; 431 req.baseConfigs.disableFollowupReceivedIndication = false; 432 433 // TODO: configure ranging and security 434 req.baseConfigs.securityConfig.securityType = NanDataPathSecurityType.OPEN; 435 req.baseConfigs.rangingRequired = false; 436 req.autoAcceptDataPathRequests = false; 437 438 req.publishType = publishConfig.mPublishType; 439 req.txType = NanTxType.BROADCAST; 440 441 try { 442 WifiStatus status = iface.startPublishRequest(transactionId, req); 443 if (status.code == WifiStatusCode.SUCCESS) { 444 return true; 445 } else { 446 Log.e(TAG, "publish: error: " + statusString(status)); 447 return false; 448 } 449 } catch (RemoteException e) { 450 Log.e(TAG, "publish: exception: " + e); 451 return false; 452 } 453 } 454 455 /** 456 * Start or modify a service subscription session. 457 * 458 * @param transactionId transactionId Transaction ID for the transaction - 459 * used in the async callback to match with the original request. 460 * @param subscribeId ID of the requested session - 0 to request a new 461 * subscribe session. 462 * @param subscribeConfig Configuration of the discovery session. 463 */ 464 public boolean subscribe(short transactionId, byte subscribeId, 465 SubscribeConfig subscribeConfig) { 466 if (VDBG) { 467 Log.d(TAG, "subscribe: transactionId=" + transactionId + ", subscribeId=" + subscribeId 468 + ", config=" + subscribeConfig); 469 } 470 471 IWifiNanIface iface = mHal.getWifiNanIface(); 472 if (iface == null) { 473 Log.e(TAG, "subscribe: null interface"); 474 return false; 475 } 476 477 NanSubscribeRequest req = new NanSubscribeRequest(); 478 req.baseConfigs.sessionId = subscribeId; 479 req.baseConfigs.ttlSec = (short) subscribeConfig.mTtlSec; 480 req.baseConfigs.discoveryWindowPeriod = 1; 481 req.baseConfigs.discoveryCount = 0; 482 convertNativeByteArrayToArrayList(subscribeConfig.mServiceName, 483 req.baseConfigs.serviceName); 484 req.baseConfigs.discoveryMatchIndicator = NanMatchAlg.MATCH_ONCE; 485 convertNativeByteArrayToArrayList(subscribeConfig.mServiceSpecificInfo, 486 req.baseConfigs.serviceSpecificInfo); 487 convertNativeByteArrayToArrayList(subscribeConfig.mMatchFilter, 488 subscribeConfig.mSubscribeType == SubscribeConfig.SUBSCRIBE_TYPE_ACTIVE 489 ? req.baseConfigs.txMatchFilter : req.baseConfigs.rxMatchFilter); 490 req.baseConfigs.useRssiThreshold = false; 491 req.baseConfigs.disableDiscoveryTerminationIndication = 492 !subscribeConfig.mEnableTerminateNotification; 493 req.baseConfigs.disableMatchExpirationIndication = true; 494 req.baseConfigs.disableFollowupReceivedIndication = false; 495 496 // TODO: configure ranging and security 497 req.baseConfigs.securityConfig.securityType = NanDataPathSecurityType.OPEN; 498 req.baseConfigs.rangingRequired = false; 499 500 req.subscribeType = subscribeConfig.mSubscribeType; 501 502 try { 503 WifiStatus status = iface.startSubscribeRequest(transactionId, req); 504 if (status.code == WifiStatusCode.SUCCESS) { 505 return true; 506 } else { 507 Log.e(TAG, "subscribe: error: " + statusString(status)); 508 return false; 509 } 510 } catch (RemoteException e) { 511 Log.e(TAG, "subscribe: exception: " + e); 512 return false; 513 } 514 } 515 516 /** 517 * Send a message through an existing discovery session. 518 * 519 * @param transactionId transactionId Transaction ID for the transaction - 520 * used in the async callback to match with the original request. 521 * @param pubSubId ID of the existing publish/subscribe session. 522 * @param requestorInstanceId ID of the peer to communicate with - obtained 523 * through a previous discovery (match) operation with that peer. 524 * @param dest MAC address of the peer to communicate with - obtained 525 * together with requestorInstanceId. 526 * @param message Message. 527 * @param messageId Arbitary integer from host (not sent to HAL - useful for 528 * testing/debugging at this level) 529 */ 530 public boolean sendMessage(short transactionId, byte pubSubId, int requestorInstanceId, 531 byte[] dest, byte[] message, int messageId) { 532 if (VDBG) { 533 Log.d(TAG, 534 "sendMessage: transactionId=" + transactionId + ", pubSubId=" + pubSubId 535 + ", requestorInstanceId=" + requestorInstanceId + ", dest=" 536 + String.valueOf(HexEncoding.encode(dest)) + ", messageId=" + messageId 537 + ", message=" + (message == null ? "<null>" 538 : HexEncoding.encode(message)) + ", message.length=" + (message == null 539 ? 0 : message.length)); 540 } 541 542 IWifiNanIface iface = mHal.getWifiNanIface(); 543 if (iface == null) { 544 Log.e(TAG, "sendMessage: null interface"); 545 return false; 546 } 547 548 NanTransmitFollowupRequest req = new NanTransmitFollowupRequest(); 549 req.discoverySessionId = pubSubId; 550 req.peerId = requestorInstanceId; 551 copyArray(dest, req.addr); 552 req.isHighPriority = false; 553 req.shouldUseDiscoveryWindow = true; 554 convertNativeByteArrayToArrayList(message, req.serviceSpecificInfo); 555 req.disableFollowupResultIndication = false; 556 557 try { 558 WifiStatus status = iface.transmitFollowupRequest(transactionId, req); 559 if (status.code == WifiStatusCode.SUCCESS) { 560 return true; 561 } else { 562 Log.e(TAG, "sendMessage: error: " + statusString(status)); 563 return false; 564 } 565 } catch (RemoteException e) { 566 Log.e(TAG, "sendMessage: exception: " + e); 567 return false; 568 } 569 } 570 571 /** 572 * Terminate a publish discovery session. 573 * 574 * @param transactionId transactionId Transaction ID for the transaction - 575 * used in the async callback to match with the original request. 576 * @param pubSubId ID of the publish/subscribe session - obtained when 577 * creating a session. 578 */ 579 public boolean stopPublish(short transactionId, byte pubSubId) { 580 if (VDBG) { 581 Log.d(TAG, "stopPublish: transactionId=" + transactionId + ", pubSubId=" + pubSubId); 582 } 583 584 IWifiNanIface iface = mHal.getWifiNanIface(); 585 if (iface == null) { 586 Log.e(TAG, "stopPublish: null interface"); 587 return false; 588 } 589 590 try { 591 WifiStatus status = iface.stopPublishRequest(transactionId, pubSubId); 592 if (status.code == WifiStatusCode.SUCCESS) { 593 return true; 594 } else { 595 Log.e(TAG, "stopPublish: error: " + statusString(status)); 596 return false; 597 } 598 } catch (RemoteException e) { 599 Log.e(TAG, "stopPublish: exception: " + e); 600 return false; 601 } 602 } 603 604 /** 605 * Terminate a subscribe discovery session. 606 * 607 * @param transactionId transactionId Transaction ID for the transaction - 608 * used in the async callback to match with the original request. 609 * @param pubSubId ID of the publish/subscribe session - obtained when 610 * creating a session. 611 */ 612 public boolean stopSubscribe(short transactionId, byte pubSubId) { 613 if (VDBG) { 614 Log.d(TAG, "stopSubscribe: transactionId=" + transactionId + ", pubSubId=" + pubSubId); 615 } 616 617 IWifiNanIface iface = mHal.getWifiNanIface(); 618 if (iface == null) { 619 Log.e(TAG, "stopSubscribe: null interface"); 620 return false; 621 } 622 623 try { 624 WifiStatus status = iface.stopSubscribeRequest(transactionId, pubSubId); 625 if (status.code == WifiStatusCode.SUCCESS) { 626 return true; 627 } else { 628 Log.e(TAG, "stopSubscribe: error: " + statusString(status)); 629 return false; 630 } 631 } catch (RemoteException e) { 632 Log.e(TAG, "stopSubscribe: exception: " + e); 633 return false; 634 } 635 } 636 637 /** 638 * Create a Aware network interface. This only creates the Linux interface - it doesn't actually 639 * create the data connection. 640 * 641 * @param transactionId Transaction ID for the transaction - used in the async callback to 642 * match with the original request. 643 * @param interfaceName The name of the interface, e.g. "aware0". 644 */ 645 public boolean createAwareNetworkInterface(short transactionId, String interfaceName) { 646 if (VDBG) { 647 Log.v(TAG, "createAwareNetworkInterface: transactionId=" + transactionId + ", " 648 + "interfaceName=" + interfaceName); 649 } 650 651 IWifiNanIface iface = mHal.getWifiNanIface(); 652 if (iface == null) { 653 Log.e(TAG, "createAwareNetworkInterface: null interface"); 654 return false; 655 } 656 657 try { 658 WifiStatus status = iface.createDataInterfaceRequest(transactionId, interfaceName); 659 if (status.code == WifiStatusCode.SUCCESS) { 660 return true; 661 } else { 662 Log.e(TAG, "createAwareNetworkInterface: error: " + statusString(status)); 663 return false; 664 } 665 } catch (RemoteException e) { 666 Log.e(TAG, "createAwareNetworkInterface: exception: " + e); 667 return false; 668 } 669 } 670 671 /** 672 * Deletes a Aware network interface. The data connection can (should?) be torn down previously. 673 * 674 * @param transactionId Transaction ID for the transaction - used in the async callback to 675 * match with the original request. 676 * @param interfaceName The name of the interface, e.g. "aware0". 677 */ 678 public boolean deleteAwareNetworkInterface(short transactionId, String interfaceName) { 679 if (VDBG) { 680 Log.v(TAG, "deleteAwareNetworkInterface: transactionId=" + transactionId + ", " 681 + "interfaceName=" + interfaceName); 682 } 683 684 IWifiNanIface iface = mHal.getWifiNanIface(); 685 if (iface == null) { 686 Log.e(TAG, "deleteAwareNetworkInterface: null interface"); 687 return false; 688 } 689 690 try { 691 WifiStatus status = iface.deleteDataInterfaceRequest(transactionId, interfaceName); 692 if (status.code == WifiStatusCode.SUCCESS) { 693 return true; 694 } else { 695 Log.e(TAG, "deleteAwareNetworkInterface: error: " + statusString(status)); 696 return false; 697 } 698 } catch (RemoteException e) { 699 Log.e(TAG, "deleteAwareNetworkInterface: exception: " + e); 700 return false; 701 } 702 } 703 704 /** 705 * Initiates setting up a data-path between device and peer. Security is provided by either 706 * PMK or Passphrase (not both) - if both are null then an open (unencrypted) link is set up. 707 * 708 * @param transactionId Transaction ID for the transaction - used in the async callback to 709 * match with the original request. 710 * @param peerId ID of the peer ID to associate the data path with. A value of 0 711 * indicates that not associated with an existing session. 712 * @param channelRequestType Indicates whether the specified channel is available, if available 713 * requested or forced (resulting in failure if cannot be 714 * accommodated). 715 * @param channel The channel on which to set up the data-path. 716 * @param peer The MAC address of the peer to create a connection with. 717 * @param interfaceName The interface on which to create the data connection. 718 * @param pmk Pairwise master key (PMK - see IEEE 802.11i) for the data-path. 719 * @param passphrase Passphrase for the data-path. 720 * @param capabilities The capabilities of the firmware. 721 */ 722 public boolean initiateDataPath(short transactionId, int peerId, int channelRequestType, 723 int channel, byte[] peer, String interfaceName, byte[] pmk, String passphrase, 724 boolean isOutOfBand, Capabilities capabilities) { 725 if (VDBG) { 726 Log.v(TAG, "initiateDataPath: transactionId=" + transactionId + ", peerId=" + peerId 727 + ", channelRequestType=" + channelRequestType + ", channel=" + channel 728 + ", peer=" + String.valueOf(HexEncoding.encode(peer)) + ", interfaceName=" 729 + interfaceName); 730 } 731 732 IWifiNanIface iface = mHal.getWifiNanIface(); 733 if (iface == null) { 734 Log.e(TAG, "initiateDataPath: null interface"); 735 return false; 736 } 737 738 if (capabilities == null) { 739 Log.e(TAG, "initiateDataPath: null capabilities"); 740 return false; 741 } 742 743 NanInitiateDataPathRequest req = new NanInitiateDataPathRequest(); 744 req.peerId = peerId; 745 copyArray(peer, req.peerDiscMacAddr); 746 req.channelRequestType = channelRequestType; 747 req.channel = channel; 748 req.ifaceName = interfaceName; 749 req.securityConfig.securityType = NanDataPathSecurityType.OPEN; 750 if (pmk != null && pmk.length != 0) { 751 req.securityConfig.cipherType = getStrongestCipherSuiteType( 752 capabilities.supportedCipherSuites); 753 req.securityConfig.securityType = NanDataPathSecurityType.PMK; 754 copyArray(pmk, req.securityConfig.pmk); 755 } 756 if (passphrase != null && passphrase.length() != 0) { 757 req.securityConfig.cipherType = getStrongestCipherSuiteType( 758 capabilities.supportedCipherSuites); 759 req.securityConfig.securityType = NanDataPathSecurityType.PASSPHRASE; 760 convertNativeByteArrayToArrayList(passphrase.getBytes(), req.securityConfig.passphrase); 761 762 if (isOutOfBand) { // only relevant when using passphrase 763 convertNativeByteArrayToArrayList( 764 SERVICE_NAME_FOR_OOB_DATA_PATH.getBytes(StandardCharsets.UTF_8), 765 req.serviceNameOutOfBand); 766 } 767 } 768 769 try { 770 WifiStatus status = iface.initiateDataPathRequest(transactionId, req); 771 if (status.code == WifiStatusCode.SUCCESS) { 772 return true; 773 } else { 774 Log.e(TAG, "initiateDataPath: error: " + statusString(status)); 775 return false; 776 } 777 } catch (RemoteException e) { 778 Log.e(TAG, "initiateDataPath: exception: " + e); 779 return false; 780 } 781 } 782 783 /** 784 * Responds to a data request from a peer. Security is provided by either PMK or Passphrase (not 785 * both) - if both are null then an open (unencrypted) link is set up. 786 * 787 * @param transactionId Transaction ID for the transaction - used in the async callback to 788 * match with the original request. 789 * @param accept Accept (true) or reject (false) the original call. 790 * @param ndpId The NDP (Aware data path) ID. Obtained from the request callback. 791 * @param interfaceName The interface on which the data path will be setup. Obtained from the 792 * request callback. 793 * @param pmk Pairwise master key (PMK - see IEEE 802.11i) for the data-path. 794 * @param passphrase Passphrase for the data-path. 795 * @param isOutOfBand Is the data-path out-of-band (i.e. without a corresponding Aware discovery 796 * session). 797 * @param capabilities The capabilities of the firmware. 798 */ 799 public boolean respondToDataPathRequest(short transactionId, boolean accept, int ndpId, 800 String interfaceName, byte[] pmk, String passphrase, boolean isOutOfBand, 801 Capabilities capabilities) { 802 if (VDBG) { 803 Log.v(TAG, "respondToDataPathRequest: transactionId=" + transactionId + ", accept=" 804 + accept + ", int ndpId=" + ndpId + ", interfaceName=" + interfaceName); 805 } 806 807 IWifiNanIface iface = mHal.getWifiNanIface(); 808 if (iface == null) { 809 Log.e(TAG, "respondToDataPathRequest: null interface"); 810 return false; 811 } 812 813 if (capabilities == null) { 814 Log.e(TAG, "initiateDataPath: null capabilities"); 815 return false; 816 } 817 818 NanRespondToDataPathIndicationRequest req = new NanRespondToDataPathIndicationRequest(); 819 req.acceptRequest = accept; 820 req.ndpInstanceId = ndpId; 821 req.ifaceName = interfaceName; 822 req.securityConfig.securityType = NanDataPathSecurityType.OPEN; 823 if (pmk != null && pmk.length != 0) { 824 req.securityConfig.cipherType = getStrongestCipherSuiteType( 825 capabilities.supportedCipherSuites); 826 req.securityConfig.securityType = NanDataPathSecurityType.PMK; 827 copyArray(pmk, req.securityConfig.pmk); 828 } 829 if (passphrase != null && passphrase.length() != 0) { 830 req.securityConfig.cipherType = getStrongestCipherSuiteType( 831 capabilities.supportedCipherSuites); 832 req.securityConfig.securityType = NanDataPathSecurityType.PASSPHRASE; 833 convertNativeByteArrayToArrayList(passphrase.getBytes(), req.securityConfig.passphrase); 834 835 if (isOutOfBand) { // only relevant when using passphrase 836 convertNativeByteArrayToArrayList( 837 SERVICE_NAME_FOR_OOB_DATA_PATH.getBytes(StandardCharsets.UTF_8), 838 req.serviceNameOutOfBand); 839 } 840 } 841 842 try { 843 WifiStatus status = iface.respondToDataPathIndicationRequest(transactionId, req); 844 if (status.code == WifiStatusCode.SUCCESS) { 845 return true; 846 } else { 847 Log.e(TAG, "respondToDataPathRequest: error: " + statusString(status)); 848 return false; 849 } 850 } catch (RemoteException e) { 851 Log.e(TAG, "respondToDataPathRequest: exception: " + e); 852 return false; 853 } 854 } 855 856 /** 857 * Terminate an existing data-path (does not delete the interface). 858 * 859 * @param transactionId Transaction ID for the transaction - used in the async callback to 860 * match with the original request. 861 * @param ndpId The NDP (Aware data path) ID to be terminated. 862 */ 863 public boolean endDataPath(short transactionId, int ndpId) { 864 if (VDBG) { 865 Log.v(TAG, "endDataPath: transactionId=" + transactionId + ", ndpId=" + ndpId); 866 } 867 868 IWifiNanIface iface = mHal.getWifiNanIface(); 869 if (iface == null) { 870 Log.e(TAG, "endDataPath: null interface"); 871 return false; 872 } 873 874 try { 875 WifiStatus status = iface.terminateDataPathRequest(transactionId, ndpId); 876 if (status.code == WifiStatusCode.SUCCESS) { 877 return true; 878 } else { 879 Log.e(TAG, "endDataPath: error: " + statusString(status)); 880 return false; 881 } 882 } catch (RemoteException e) { 883 Log.e(TAG, "endDataPath: exception: " + e); 884 return false; 885 } 886 } 887 888 889 // utilities 890 891 /** 892 * Update the NAN configuration to reflect the current power settings. 893 */ 894 private void updateConfigForPowerSettings(NanConfigRequest req, boolean isInteractive, 895 boolean isIdle) { 896 if (isIdle) { // lowest power state: doze 897 updateSingleConfigForPowerSettings(req.bandSpecificConfig[NanBandIndex.NAN_BAND_5GHZ], 898 mSettableParameters.get(PARAM_DW_ON_IDLE_5GHZ)); 899 updateSingleConfigForPowerSettings(req.bandSpecificConfig[NanBandIndex.NAN_BAND_24GHZ], 900 mSettableParameters.get(PARAM_DW_ON_IDLE_24GHZ)); 901 } else if (!isInteractive) { // intermediate power state: inactive 902 updateSingleConfigForPowerSettings(req.bandSpecificConfig[NanBandIndex.NAN_BAND_5GHZ], 903 mSettableParameters.get(PARAM_DW_ON_INACTIVE_5GHZ)); 904 updateSingleConfigForPowerSettings(req.bandSpecificConfig[NanBandIndex.NAN_BAND_24GHZ], 905 mSettableParameters.get(PARAM_DW_ON_INACTIVE_24GHZ)); 906 } else { // the default state 907 updateSingleConfigForPowerSettings(req.bandSpecificConfig[NanBandIndex.NAN_BAND_5GHZ], 908 mSettableParameters.get(PARAM_DW_DEFAULT_5GHZ)); 909 updateSingleConfigForPowerSettings(req.bandSpecificConfig[NanBandIndex.NAN_BAND_24GHZ], 910 mSettableParameters.get(PARAM_DW_DEFAULT_24GHZ)); 911 } 912 913 // else do nothing - normal power state 914 } 915 916 private void updateSingleConfigForPowerSettings(NanBandSpecificConfig cfg, int override) { 917 if (override != -1) { 918 cfg.validDiscoveryWindowIntervalVal = true; 919 cfg.discoveryWindowIntervalVal = (byte) override; 920 } 921 } 922 923 /** 924 * Returns the strongest supported cipher suite. 925 * 926 * Baseline is very simple: 256 > 128 > 0. 927 */ 928 private int getStrongestCipherSuiteType(int supportedCipherSuites) { 929 if ((supportedCipherSuites & NanCipherSuiteType.SHARED_KEY_256_MASK) != 0) { 930 return NanCipherSuiteType.SHARED_KEY_256_MASK; 931 } 932 if ((supportedCipherSuites & NanCipherSuiteType.SHARED_KEY_128_MASK) != 0) { 933 return NanCipherSuiteType.SHARED_KEY_128_MASK; 934 } 935 return NanCipherSuiteType.NONE; 936 } 937 938 /** 939 * Converts a byte[] to an ArrayList<Byte>. Fills in the entries of the 'to' array if 940 * provided (non-null), otherwise creates and returns a new ArrayList<>. 941 * 942 * @param from The input byte[] to convert from. 943 * @param to An optional ArrayList<> to fill in from 'from'. 944 * 945 * @return A newly allocated ArrayList<> if 'to' is null, otherwise null. 946 */ 947 private ArrayList<Byte> convertNativeByteArrayToArrayList(byte[] from, ArrayList<Byte> to) { 948 if (from == null) { 949 from = new byte[0]; 950 } 951 952 if (to == null) { 953 to = new ArrayList<>(from.length); 954 } else { 955 to.ensureCapacity(from.length); 956 } 957 for (int i = 0; i < from.length; ++i) { 958 to.add(from[i]); 959 } 960 return to; 961 } 962 963 private void copyArray(byte[] from, byte[] to) { 964 if (from == null || to == null || from.length != to.length) { 965 Log.e(TAG, "copyArray error: from=" + from + ", to=" + to); 966 return; 967 } 968 for (int i = 0; i < from.length; ++i) { 969 to[i] = from[i]; 970 } 971 } 972 973 private static String statusString(WifiStatus status) { 974 if (status == null) { 975 return "status=null"; 976 } 977 StringBuilder sb = new StringBuilder(); 978 sb.append(status.code).append(" (").append(status.description).append(")"); 979 return sb.toString(); 980 } 981 982 /** 983 * Dump the internal state of the class. 984 */ 985 public void dump(FileDescriptor fd, PrintWriter pw, String[] args) { 986 pw.println("WifiAwareNativeApi:"); 987 pw.println(" mSettableParameters: " + mSettableParameters); 988 mHal.dump(fd, pw, args); 989 } 990} 991