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