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