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