WifiAwareNativeApi.java revision ff05cbcaccc03d1f64d1cb232a3c40a664c4be57
1/*
2 * Copyright (C) 2016 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 *      http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17package com.android.server.wifi.aware;
18
19import android.hardware.wifi.V1_0.IWifiNanIface;
20import android.hardware.wifi.V1_0.NanBandIndex;
21import android.hardware.wifi.V1_0.NanBandSpecificConfig;
22import android.hardware.wifi.V1_0.NanCipherSuiteType;
23import android.hardware.wifi.V1_0.NanConfigRequest;
24import android.hardware.wifi.V1_0.NanDataPathSecurityType;
25import android.hardware.wifi.V1_0.NanEnableRequest;
26import android.hardware.wifi.V1_0.NanInitiateDataPathRequest;
27import android.hardware.wifi.V1_0.NanMatchAlg;
28import android.hardware.wifi.V1_0.NanPublishRequest;
29import android.hardware.wifi.V1_0.NanRespondToDataPathIndicationRequest;
30import android.hardware.wifi.V1_0.NanSubscribeRequest;
31import android.hardware.wifi.V1_0.NanTransmitFollowupRequest;
32import android.hardware.wifi.V1_0.NanTxType;
33import android.hardware.wifi.V1_0.WifiStatus;
34import android.hardware.wifi.V1_0.WifiStatusCode;
35import android.net.wifi.aware.ConfigRequest;
36import android.net.wifi.aware.PublishConfig;
37import android.net.wifi.aware.SubscribeConfig;
38import android.os.RemoteException;
39import android.util.Log;
40
41import libcore.util.HexEncoding;
42
43import java.io.FileDescriptor;
44import java.io.PrintWriter;
45import java.nio.charset.StandardCharsets;
46import java.util.ArrayList;
47
48/**
49 * Translates Wi-Fi Aware requests from the framework to the HAL (HIDL).
50 *
51 * Delegates the management of the NAN interface to WifiAwareNativeManager.
52 */
53public class WifiAwareNativeApi {
54    private static final String TAG = "WifiAwareNativeApi";
55    private static final boolean DBG = false;
56    private static final boolean VDBG = false; // STOPSHIP if true
57
58    private static final String SERVICE_NAME_FOR_OOB_DATA_PATH = "Wi-Fi Aware Data Path";
59
60    private final WifiAwareNativeManager mHal;
61
62    public WifiAwareNativeApi(WifiAwareNativeManager wifiAwareNativeManager) {
63        mHal = wifiAwareNativeManager;
64    }
65
66    /**
67     * Query the firmware's capabilities.
68     *
69     * @param transactionId Transaction ID for the transaction - used in the async callback to
70     *                      match with the original request.
71     */
72    public boolean getCapabilities(short transactionId) {
73        if (VDBG) Log.v(TAG, "getCapabilities: transactionId=" + transactionId);
74
75        IWifiNanIface iface = mHal.getWifiNanIface();
76        if (iface == null) {
77            Log.e(TAG, "getCapabilities: null interface");
78            return false;
79        }
80
81        try {
82            WifiStatus status = iface.getCapabilitiesRequest(transactionId);
83            if (status.code == WifiStatusCode.SUCCESS) {
84                return true;
85            } else {
86                Log.e(TAG, "getCapabilities: error: " + statusString(status));
87                return false;
88            }
89        } catch (RemoteException e) {
90            Log.e(TAG, "getCapabilities: exception: " + e);
91            return false;
92        }
93    }
94
95    /**
96     * Enable and configure Aware.
97     *
98     * @param transactionId Transaction ID for the transaction - used in the
99     *            async callback to match with the original request.
100     * @param configRequest Requested Aware configuration.
101     * @param notifyIdentityChange Indicates whether or not to get address change callbacks.
102     * @param initialConfiguration Specifies whether initial configuration
103     *            (true) or an update (false) to the configuration.
104     */
105    public boolean enableAndConfigure(short transactionId, ConfigRequest configRequest,
106            boolean notifyIdentityChange, boolean initialConfiguration) {
107        if (VDBG) {
108            Log.v(TAG, "enableAndConfigure: transactionId=" + transactionId + ", configRequest="
109                    + configRequest + ", notifyIdentityChange=" + notifyIdentityChange
110                    + ", initialConfiguration=" + initialConfiguration);
111        }
112
113        IWifiNanIface iface = mHal.getWifiNanIface();
114        if (iface == null) {
115            Log.e(TAG, "enableAndConfigure: null interface");
116            return false;
117        }
118
119        try {
120            WifiStatus status;
121            if (initialConfiguration) {
122                // translate framework to HIDL configuration
123                NanEnableRequest req = new NanEnableRequest();
124
125                req.operateInBand[NanBandIndex.NAN_BAND_24GHZ] = true;
126                req.operateInBand[NanBandIndex.NAN_BAND_5GHZ] = configRequest.mSupport5gBand;
127                req.hopCountMax = 2;
128                req.configParams.masterPref = (byte) configRequest.mMasterPreference;
129                req.configParams.disableDiscoveryAddressChangeIndication = !notifyIdentityChange;
130                req.configParams.disableStartedClusterIndication = !notifyIdentityChange;
131                req.configParams.disableJoinedClusterIndication = !notifyIdentityChange;
132                req.configParams.includePublishServiceIdsInBeacon = true;
133                req.configParams.numberOfPublishServiceIdsInBeacon = 0;
134                req.configParams.includeSubscribeServiceIdsInBeacon = true;
135                req.configParams.numberOfSubscribeServiceIdsInBeacon = 0;
136                req.configParams.rssiWindowSize = 8;
137                req.configParams.macAddressRandomizationIntervalSec = 1800;
138
139                NanBandSpecificConfig config24 = new NanBandSpecificConfig();
140                config24.rssiClose = 60;
141                config24.rssiMiddle = 70;
142                config24.rssiCloseProximity = 60;
143                config24.dwellTimeMs = (byte) 200;
144                config24.scanPeriodSec = 20;
145                if (configRequest.mDiscoveryWindowInterval[ConfigRequest.NAN_BAND_24GHZ]
146                        == ConfigRequest.DW_INTERVAL_NOT_INIT) {
147                    config24.validDiscoveryWindowIntervalVal = false;
148                } else {
149                    config24.validDiscoveryWindowIntervalVal = true;
150                    config24.discoveryWindowIntervalVal =
151                            (byte) configRequest.mDiscoveryWindowInterval[ConfigRequest
152                                    .NAN_BAND_24GHZ];
153                }
154                req.configParams.bandSpecificConfig[NanBandIndex.NAN_BAND_24GHZ] = config24;
155
156                NanBandSpecificConfig config5 = new NanBandSpecificConfig();
157                config5.rssiClose = 60;
158                config5.rssiMiddle = 75;
159                config5.rssiCloseProximity = 60;
160                config5.dwellTimeMs = (byte) 200;
161                config5.scanPeriodSec = 20;
162                if (configRequest.mDiscoveryWindowInterval[ConfigRequest.NAN_BAND_5GHZ]
163                        == ConfigRequest.DW_INTERVAL_NOT_INIT) {
164                    config5.validDiscoveryWindowIntervalVal = false;
165                } else {
166                    config5.validDiscoveryWindowIntervalVal = true;
167                    config5.discoveryWindowIntervalVal =
168                            (byte) configRequest.mDiscoveryWindowInterval[ConfigRequest
169                                    .NAN_BAND_5GHZ];
170                }
171                req.configParams.bandSpecificConfig[NanBandIndex.NAN_BAND_5GHZ] = config5;
172
173                req.debugConfigs.validClusterIdVals = true;
174                req.debugConfigs.clusterIdTopRangeVal = (short) configRequest.mClusterHigh;
175                req.debugConfigs.clusterIdBottomRangeVal = (short) configRequest.mClusterLow;
176                req.debugConfigs.validIntfAddrVal = false;
177                req.debugConfigs.validOuiVal = false;
178                req.debugConfigs.ouiVal = 0;
179                req.debugConfigs.validRandomFactorForceVal = false;
180                req.debugConfigs.randomFactorForceVal = 0;
181                req.debugConfigs.validHopCountForceVal = false;
182                req.debugConfigs.hopCountForceVal = 0;
183                req.debugConfigs.validDiscoveryChannelVal = false;
184                req.debugConfigs.discoveryChannelMhzVal[NanBandIndex.NAN_BAND_24GHZ] = 0;
185                req.debugConfigs.discoveryChannelMhzVal[NanBandIndex.NAN_BAND_5GHZ] = 0;
186                req.debugConfigs.validUseBeaconsInBandVal = false;
187                req.debugConfigs.useBeaconsInBandVal[NanBandIndex.NAN_BAND_24GHZ] = true;
188                req.debugConfigs.useBeaconsInBandVal[NanBandIndex.NAN_BAND_5GHZ] = true;
189                req.debugConfigs.validUseSdfInBandVal = false;
190                req.debugConfigs.useSdfInBandVal[NanBandIndex.NAN_BAND_24GHZ] = true;
191                req.debugConfigs.useSdfInBandVal[NanBandIndex.NAN_BAND_5GHZ] = true;
192
193                status = iface.enableRequest(transactionId, req);
194            } else {
195                NanConfigRequest req = new NanConfigRequest();
196                req.masterPref = (byte) configRequest.mMasterPreference;
197                req.disableDiscoveryAddressChangeIndication = !notifyIdentityChange;
198                req.disableStartedClusterIndication = !notifyIdentityChange;
199                req.disableJoinedClusterIndication = !notifyIdentityChange;
200                req.includePublishServiceIdsInBeacon = true;
201                req.numberOfPublishServiceIdsInBeacon = 0;
202                req.includeSubscribeServiceIdsInBeacon = true;
203                req.numberOfSubscribeServiceIdsInBeacon = 0;
204                req.rssiWindowSize = 8;
205                req.macAddressRandomizationIntervalSec = 1800;
206
207                NanBandSpecificConfig config24 = new NanBandSpecificConfig();
208                config24.rssiClose = 60;
209                config24.rssiMiddle = 70;
210                config24.rssiCloseProximity = 60;
211                config24.dwellTimeMs = (byte) 200;
212                config24.scanPeriodSec = 20;
213                if (configRequest.mDiscoveryWindowInterval[ConfigRequest.NAN_BAND_24GHZ]
214                        == ConfigRequest.DW_INTERVAL_NOT_INIT) {
215                    config24.validDiscoveryWindowIntervalVal = false;
216                } else {
217                    config24.validDiscoveryWindowIntervalVal = true;
218                    config24.discoveryWindowIntervalVal =
219                            (byte) configRequest.mDiscoveryWindowInterval[ConfigRequest
220                                    .NAN_BAND_24GHZ];
221                }
222                req.bandSpecificConfig[NanBandIndex.NAN_BAND_24GHZ] = config24;
223
224                NanBandSpecificConfig config5 = new NanBandSpecificConfig();
225                config5.rssiClose = 60;
226                config5.rssiMiddle = 75;
227                config5.rssiCloseProximity = 60;
228                config5.dwellTimeMs = (byte) 200;
229                config5.scanPeriodSec = 20;
230                if (configRequest.mDiscoveryWindowInterval[ConfigRequest.NAN_BAND_5GHZ]
231                        == ConfigRequest.DW_INTERVAL_NOT_INIT) {
232                    config5.validDiscoveryWindowIntervalVal = false;
233                } else {
234                    config5.validDiscoveryWindowIntervalVal = true;
235                    config5.discoveryWindowIntervalVal =
236                            (byte) configRequest.mDiscoveryWindowInterval[ConfigRequest
237                                    .NAN_BAND_5GHZ];
238                }
239                req.bandSpecificConfig[NanBandIndex.NAN_BAND_5GHZ] = config5;
240
241                status = iface.configRequest(transactionId, req);
242            }
243            if (status.code == WifiStatusCode.SUCCESS) {
244                return true;
245            } else {
246                Log.e(TAG, "enableAndConfigure: error: " + statusString(status));
247                return false;
248            }
249        } catch (RemoteException e) {
250            Log.e(TAG, "enableAndConfigure: exception: " + e);
251            return false;
252        }
253    }
254
255    /**
256     * Disable Aware.
257     *
258     * @param transactionId transactionId Transaction ID for the transaction -
259     *            used in the async callback to match with the original request.
260     */
261    public boolean disable(short transactionId) {
262        if (VDBG) Log.d(TAG, "disable");
263
264        IWifiNanIface iface = mHal.getWifiNanIface();
265        if (iface == null) {
266            Log.e(TAG, "disable: null interface");
267            return false;
268        }
269
270        try {
271            WifiStatus status = iface.disableRequest(transactionId);
272            if (status.code == WifiStatusCode.SUCCESS) {
273                return true;
274            } else {
275                Log.e(TAG, "disable: error: " + statusString(status));
276                return false;
277            }
278        } catch (RemoteException e) {
279            Log.e(TAG, "disable: exception: " + e);
280            return false;
281        }
282    }
283
284    /**
285     * Start or modify a service publish session.
286     *
287     * @param transactionId transactionId Transaction ID for the transaction -
288     *            used in the async callback to match with the original request.
289     * @param publishId ID of the requested session - 0 to request a new publish
290     *            session.
291     * @param publishConfig Configuration of the discovery session.
292     */
293    public boolean publish(short transactionId, int publishId, PublishConfig publishConfig) {
294        if (VDBG) {
295            Log.d(TAG, "publish: transactionId=" + transactionId + ", config=" + publishConfig);
296        }
297
298        IWifiNanIface iface = mHal.getWifiNanIface();
299        if (iface == null) {
300            Log.e(TAG, "publish: null interface");
301            return false;
302        }
303
304        NanPublishRequest req = new NanPublishRequest();
305        req.baseConfigs.sessionId = 0;
306        req.baseConfigs.ttlSec = (short) publishConfig.mTtlSec;
307        req.baseConfigs.discoveryWindowPeriod = 1;
308        req.baseConfigs.discoveryCount = 0;
309        convertNativeByteArrayToArrayList(publishConfig.mServiceName, req.baseConfigs.serviceName);
310        // TODO: what's the right value on publish?
311        req.baseConfigs.discoveryMatchIndicator = NanMatchAlg.MATCH_ONCE;
312        convertNativeByteArrayToArrayList(publishConfig.mServiceSpecificInfo,
313                req.baseConfigs.serviceSpecificInfo);
314        convertNativeByteArrayToArrayList(publishConfig.mMatchFilter,
315                publishConfig.mPublishType == PublishConfig.PUBLISH_TYPE_UNSOLICITED
316                        ? req.baseConfigs.txMatchFilter : req.baseConfigs.rxMatchFilter);
317        req.baseConfigs.useRssiThreshold = false;
318        req.baseConfigs.disableDiscoveryTerminationIndication =
319                !publishConfig.mEnableTerminateNotification;
320        req.baseConfigs.disableMatchExpirationIndication = true;
321        req.baseConfigs.disableFollowupReceivedIndication = false;
322
323        // TODO: configure ranging and security
324        req.baseConfigs.securityConfig.securityType = NanDataPathSecurityType.OPEN;
325        req.baseConfigs.rangingRequired = false;
326        req.autoAcceptDataPathRequests = false;
327
328        req.publishType = publishConfig.mPublishType;
329        req.txType = NanTxType.BROADCAST;
330
331        try {
332            WifiStatus status = iface.startPublishRequest(transactionId, req);
333            if (status.code == WifiStatusCode.SUCCESS) {
334                return true;
335            } else {
336                Log.e(TAG, "publish: error: " + statusString(status));
337                return false;
338            }
339        } catch (RemoteException e) {
340            Log.e(TAG, "publish: exception: " + e);
341            return false;
342        }
343    }
344
345    /**
346     * Start or modify a service subscription session.
347     *
348     * @param transactionId transactionId Transaction ID for the transaction -
349     *            used in the async callback to match with the original request.
350     * @param subscribeId ID of the requested session - 0 to request a new
351     *            subscribe session.
352     * @param subscribeConfig Configuration of the discovery session.
353     */
354    public boolean subscribe(short transactionId, int subscribeId,
355            SubscribeConfig subscribeConfig) {
356        if (VDBG) {
357            Log.d(TAG, "subscribe: transactionId=" + transactionId + ", config=" + subscribeConfig);
358        }
359
360        IWifiNanIface iface = mHal.getWifiNanIface();
361        if (iface == null) {
362            Log.e(TAG, "subscribe: null interface");
363            return false;
364        }
365
366        NanSubscribeRequest req = new NanSubscribeRequest();
367        req.baseConfigs.sessionId = 0;
368        req.baseConfigs.ttlSec = (short) subscribeConfig.mTtlSec;
369        req.baseConfigs.discoveryWindowPeriod = 1;
370        req.baseConfigs.discoveryCount = 0;
371        convertNativeByteArrayToArrayList(subscribeConfig.mServiceName,
372                req.baseConfigs.serviceName);
373        req.baseConfigs.discoveryMatchIndicator = NanMatchAlg.MATCH_ONCE;
374        convertNativeByteArrayToArrayList(subscribeConfig.mServiceSpecificInfo,
375                req.baseConfigs.serviceSpecificInfo);
376        convertNativeByteArrayToArrayList(subscribeConfig.mMatchFilter,
377                subscribeConfig.mSubscribeType == SubscribeConfig.SUBSCRIBE_TYPE_ACTIVE
378                        ? req.baseConfigs.txMatchFilter : req.baseConfigs.rxMatchFilter);
379        req.baseConfigs.useRssiThreshold = false;
380        req.baseConfigs.disableDiscoveryTerminationIndication =
381                !subscribeConfig.mEnableTerminateNotification;
382        req.baseConfigs.disableMatchExpirationIndication = true;
383        req.baseConfigs.disableFollowupReceivedIndication = false;
384
385        // TODO: configure ranging and security
386        req.baseConfigs.securityConfig.securityType = NanDataPathSecurityType.OPEN;
387        req.baseConfigs.rangingRequired = false;
388
389        req.subscribeType = subscribeConfig.mSubscribeType;
390
391        try {
392            WifiStatus status = iface.startSubscribeRequest(transactionId, req);
393            if (status.code == WifiStatusCode.SUCCESS) {
394                return true;
395            } else {
396                Log.e(TAG, "subscribe: error: " + statusString(status));
397                return false;
398            }
399        } catch (RemoteException e) {
400            Log.e(TAG, "subscribe: exception: " + e);
401            return false;
402        }
403    }
404
405    /**
406     * Send a message through an existing discovery session.
407     *
408     * @param transactionId transactionId Transaction ID for the transaction -
409     *            used in the async callback to match with the original request.
410     * @param pubSubId ID of the existing publish/subscribe session.
411     * @param requestorInstanceId ID of the peer to communicate with - obtained
412     *            through a previous discovery (match) operation with that peer.
413     * @param dest MAC address of the peer to communicate with - obtained
414     *            together with requestorInstanceId.
415     * @param message Message.
416     * @param messageId Arbitary integer from host (not sent to HAL - useful for
417     *                  testing/debugging at this level)
418     */
419    public boolean sendMessage(short transactionId, int pubSubId, int requestorInstanceId,
420            byte[] dest, byte[] message, int messageId) {
421        if (VDBG) {
422            Log.d(TAG,
423                    "sendMessage: transactionId=" + transactionId + ", pubSubId=" + pubSubId
424                            + ", requestorInstanceId=" + requestorInstanceId + ", dest="
425                            + String.valueOf(HexEncoding.encode(dest)) + ", messageId="
426                            + messageId);
427        }
428
429        IWifiNanIface iface = mHal.getWifiNanIface();
430        if (iface == null) {
431            Log.e(TAG, "sendMessage: null interface");
432            return false;
433        }
434
435        NanTransmitFollowupRequest req = new NanTransmitFollowupRequest();
436        req.discoverySessionId = (byte) pubSubId;
437        req.peerId = requestorInstanceId;
438        copyArray(dest, req.addr);
439        req.isHighPriority = false;
440        req.shouldUseDiscoveryWindow = true;
441        convertNativeByteArrayToArrayList(message, req.serviceSpecificInfo);
442        req.disableFollowupResultIndication = false;
443
444        try {
445            WifiStatus status = iface.transmitFollowupRequest(transactionId, req);
446            if (status.code == WifiStatusCode.SUCCESS) {
447                return true;
448            } else {
449                Log.e(TAG, "sendMessage: error: " + statusString(status));
450                return false;
451            }
452        } catch (RemoteException e) {
453            Log.e(TAG, "sendMessage: exception: " + e);
454            return false;
455        }
456    }
457
458    /**
459     * Terminate a publish discovery session.
460     *
461     * @param transactionId transactionId Transaction ID for the transaction -
462     *            used in the async callback to match with the original request.
463     * @param pubSubId ID of the publish/subscribe session - obtained when
464     *            creating a session.
465     */
466    public boolean stopPublish(short transactionId, int pubSubId) {
467        if (VDBG) {
468            Log.d(TAG, "stopPublish: transactionId=" + transactionId + ", pubSubId=" + pubSubId);
469        }
470
471        IWifiNanIface iface = mHal.getWifiNanIface();
472        if (iface == null) {
473            Log.e(TAG, "stopPublish: null interface");
474            return false;
475        }
476
477        try {
478            WifiStatus status = iface.stopPublishRequest(transactionId, (byte) pubSubId);
479            if (status.code == WifiStatusCode.SUCCESS) {
480                return true;
481            } else {
482                Log.e(TAG, "stopPublish: error: " + statusString(status));
483                return false;
484            }
485        } catch (RemoteException e) {
486            Log.e(TAG, "stopPublish: exception: " + e);
487            return false;
488        }
489    }
490
491    /**
492     * Terminate a subscribe discovery session.
493     *
494     * @param transactionId transactionId Transaction ID for the transaction -
495     *            used in the async callback to match with the original request.
496     * @param pubSubId ID of the publish/subscribe session - obtained when
497     *            creating a session.
498     */
499    public boolean stopSubscribe(short transactionId, int pubSubId) {
500        if (VDBG) {
501            Log.d(TAG, "stopSubscribe: transactionId=" + transactionId + ", pubSubId=" + pubSubId);
502        }
503
504        IWifiNanIface iface = mHal.getWifiNanIface();
505        if (iface == null) {
506            Log.e(TAG, "stopSubscribe: null interface");
507            return false;
508        }
509
510        try {
511            WifiStatus status = iface.stopSubscribeRequest(transactionId, (byte) pubSubId);
512            if (status.code == WifiStatusCode.SUCCESS) {
513                return true;
514            } else {
515                Log.e(TAG, "stopSubscribe: error: " + statusString(status));
516                return false;
517            }
518        } catch (RemoteException e) {
519            Log.e(TAG, "stopSubscribe: exception: " + e);
520            return false;
521        }
522    }
523
524    /**
525     * Create a Aware network interface. This only creates the Linux interface - it doesn't actually
526     * create the data connection.
527     *
528     * @param transactionId Transaction ID for the transaction - used in the async callback to
529     *                      match with the original request.
530     * @param interfaceName The name of the interface, e.g. "aware0".
531     */
532    public boolean createAwareNetworkInterface(short transactionId, String interfaceName) {
533        if (VDBG) {
534            Log.v(TAG, "createAwareNetworkInterface: transactionId=" + transactionId + ", "
535                    + "interfaceName=" + interfaceName);
536        }
537
538        IWifiNanIface iface = mHal.getWifiNanIface();
539        if (iface == null) {
540            Log.e(TAG, "createAwareNetworkInterface: null interface");
541            return false;
542        }
543
544        try {
545            WifiStatus status = iface.createDataInterfaceRequest(transactionId, interfaceName);
546            if (status.code == WifiStatusCode.SUCCESS) {
547                return true;
548            } else {
549                Log.e(TAG, "createAwareNetworkInterface: error: " + statusString(status));
550                return false;
551            }
552        } catch (RemoteException e) {
553            Log.e(TAG, "createAwareNetworkInterface: exception: " + e);
554            return false;
555        }
556    }
557
558    /**
559     * Deletes a Aware network interface. The data connection can (should?) be torn down previously.
560     *
561     * @param transactionId Transaction ID for the transaction - used in the async callback to
562     *                      match with the original request.
563     * @param interfaceName The name of the interface, e.g. "aware0".
564     */
565    public boolean deleteAwareNetworkInterface(short transactionId, String interfaceName) {
566        if (VDBG) {
567            Log.v(TAG, "deleteAwareNetworkInterface: transactionId=" + transactionId + ", "
568                    + "interfaceName=" + interfaceName);
569        }
570
571        IWifiNanIface iface = mHal.getWifiNanIface();
572        if (iface == null) {
573            Log.e(TAG, "deleteAwareNetworkInterface: null interface");
574            return false;
575        }
576
577        try {
578            WifiStatus status = iface.deleteDataInterfaceRequest(transactionId, interfaceName);
579            if (status.code == WifiStatusCode.SUCCESS) {
580                return true;
581            } else {
582                Log.e(TAG, "deleteAwareNetworkInterface: error: " + statusString(status));
583                return false;
584            }
585        } catch (RemoteException e) {
586            Log.e(TAG, "deleteAwareNetworkInterface: exception: " + e);
587            return false;
588        }
589    }
590
591    /**
592     * Initiates setting up a data-path between device and peer. Security is provided by either
593     * PMK or Passphrase (not both) - if both are null then an open (unencrypted) link is set up.
594     *
595     * @param transactionId      Transaction ID for the transaction - used in the async callback to
596     *                           match with the original request.
597     * @param peerId             ID of the peer ID to associate the data path with. A value of 0
598     *                           indicates that not associated with an existing session.
599     * @param channelRequestType Indicates whether the specified channel is available, if available
600     *                           requested or forced (resulting in failure if cannot be
601     *                           accommodated).
602     * @param channel            The channel on which to set up the data-path.
603     * @param peer               The MAC address of the peer to create a connection with.
604     * @param interfaceName      The interface on which to create the data connection.
605     * @param pmk Pairwise master key (PMK - see IEEE 802.11i) for the data-path.
606     * @param passphrase  Passphrase for the data-path.
607     * @param capabilities The capabilities of the firmware.
608     */
609    public boolean initiateDataPath(short transactionId, int peerId, int channelRequestType,
610            int channel, byte[] peer, String interfaceName, byte[] pmk, String passphrase,
611            boolean isOutOfBand, Capabilities capabilities) {
612        if (VDBG) {
613            Log.v(TAG, "initiateDataPath: transactionId=" + transactionId + ", peerId=" + peerId
614                    + ", channelRequestType=" + channelRequestType + ", channel=" + channel
615                    + ", peer=" + String.valueOf(HexEncoding.encode(peer)) + ", interfaceName="
616                    + interfaceName);
617        }
618
619        IWifiNanIface iface = mHal.getWifiNanIface();
620        if (iface == null) {
621            Log.e(TAG, "initiateDataPath: null interface");
622            return false;
623        }
624
625        if (capabilities == null) {
626            Log.e(TAG, "initiateDataPath: null capabilities");
627            return false;
628        }
629
630        NanInitiateDataPathRequest req = new NanInitiateDataPathRequest();
631        req.peerId = peerId;
632        copyArray(peer, req.peerDiscMacAddr);
633        req.channelRequestType = channelRequestType;
634        req.channel = channel;
635        req.ifaceName = interfaceName;
636        req.securityConfig.securityType = NanDataPathSecurityType.OPEN;
637        if (pmk != null && pmk.length != 0) {
638            req.securityConfig.cipherType = getStrongestCipherSuiteType(
639                    capabilities.supportedCipherSuites);
640            req.securityConfig.securityType = NanDataPathSecurityType.PMK;
641            copyArray(pmk, req.securityConfig.pmk);
642        }
643        if (passphrase != null && passphrase.length() != 0) {
644            req.securityConfig.cipherType = getStrongestCipherSuiteType(
645                    capabilities.supportedCipherSuites);
646            req.securityConfig.securityType = NanDataPathSecurityType.PASSPHRASE;
647            convertNativeByteArrayToArrayList(passphrase.getBytes(), req.securityConfig.passphrase);
648
649            if (isOutOfBand) { // only relevant when using passphrase
650                convertNativeByteArrayToArrayList(
651                        SERVICE_NAME_FOR_OOB_DATA_PATH.getBytes(StandardCharsets.UTF_8),
652                        req.serviceNameOutOfBand);
653            }
654        }
655
656        try {
657            WifiStatus status = iface.initiateDataPathRequest(transactionId, req);
658            if (status.code == WifiStatusCode.SUCCESS) {
659                return true;
660            } else {
661                Log.e(TAG, "initiateDataPath: error: " + statusString(status));
662                return false;
663            }
664        } catch (RemoteException e) {
665            Log.e(TAG, "initiateDataPath: exception: " + e);
666            return false;
667        }
668    }
669
670    /**
671     * Responds to a data request from a peer. Security is provided by either PMK or Passphrase (not
672     * both) - if both are null then an open (unencrypted) link is set up.
673     *
674     * @param transactionId Transaction ID for the transaction - used in the async callback to
675     *                      match with the original request.
676     * @param accept Accept (true) or reject (false) the original call.
677     * @param ndpId The NDP (Aware data path) ID. Obtained from the request callback.
678     * @param interfaceName The interface on which the data path will be setup. Obtained from the
679     *                      request callback.
680     * @param pmk Pairwise master key (PMK - see IEEE 802.11i) for the data-path.
681     * @param passphrase  Passphrase for the data-path.
682     * @param capabilities The capabilities of the firmware.
683     */
684    public boolean respondToDataPathRequest(short transactionId, boolean accept, int ndpId,
685            String interfaceName, byte[] pmk, String passphrase, Capabilities capabilities) {
686        if (VDBG) {
687            Log.v(TAG, "respondToDataPathRequest: transactionId=" + transactionId + ", accept="
688                    + accept + ", int ndpId=" + ndpId + ", interfaceName=" + interfaceName);
689        }
690
691        IWifiNanIface iface = mHal.getWifiNanIface();
692        if (iface == null) {
693            Log.e(TAG, "respondToDataPathRequest: null interface");
694            return false;
695        }
696
697        if (capabilities == null) {
698            Log.e(TAG, "initiateDataPath: null capabilities");
699            return false;
700        }
701
702        NanRespondToDataPathIndicationRequest req = new NanRespondToDataPathIndicationRequest();
703        req.acceptRequest = accept;
704        req.ndpInstanceId = ndpId;
705        req.ifaceName = interfaceName;
706        req.securityConfig.securityType = NanDataPathSecurityType.OPEN;
707        if (pmk != null && pmk.length != 0) {
708            req.securityConfig.cipherType = getStrongestCipherSuiteType(
709                    capabilities.supportedCipherSuites);
710            req.securityConfig.securityType = NanDataPathSecurityType.PMK;
711            copyArray(pmk, req.securityConfig.pmk);
712        }
713        if (passphrase != null && passphrase.length() != 0) {
714            req.securityConfig.cipherType = getStrongestCipherSuiteType(
715                    capabilities.supportedCipherSuites);
716            req.securityConfig.securityType = NanDataPathSecurityType.PASSPHRASE;
717            convertNativeByteArrayToArrayList(passphrase.getBytes(), req.securityConfig.passphrase);
718        }
719
720        try {
721            WifiStatus status = iface.respondToDataPathIndicationRequest(transactionId, req);
722            if (status.code == WifiStatusCode.SUCCESS) {
723                return true;
724            } else {
725                Log.e(TAG, "respondToDataPathRequest: error: " + statusString(status));
726                return false;
727            }
728        } catch (RemoteException e) {
729            Log.e(TAG, "respondToDataPathRequest: exception: " + e);
730            return false;
731        }
732    }
733
734    /**
735     * Terminate an existing data-path (does not delete the interface).
736     *
737     * @param transactionId Transaction ID for the transaction - used in the async callback to
738     *                      match with the original request.
739     * @param ndpId The NDP (Aware data path) ID to be terminated.
740     */
741    public boolean endDataPath(short transactionId, int ndpId) {
742        if (VDBG) {
743            Log.v(TAG, "endDataPath: transactionId=" + transactionId + ", ndpId=" + ndpId);
744        }
745
746        IWifiNanIface iface = mHal.getWifiNanIface();
747        if (iface == null) {
748            Log.e(TAG, "endDataPath: null interface");
749            return false;
750        }
751
752        try {
753            WifiStatus status = iface.terminateDataPathRequest(transactionId, ndpId);
754            if (status.code == WifiStatusCode.SUCCESS) {
755                return true;
756            } else {
757                Log.e(TAG, "endDataPath: error: " + statusString(status));
758                return false;
759            }
760        } catch (RemoteException e) {
761            Log.e(TAG, "endDataPath: exception: " + e);
762            return false;
763        }
764    }
765
766
767    // utilities
768
769    /**
770     * Returns the strongest supported cipher suite.
771     *
772     * Baseline is very simple: 256 > 128 > 0.
773     */
774    private int getStrongestCipherSuiteType(int supportedCipherSuites) {
775        if ((supportedCipherSuites & NanCipherSuiteType.SHARED_KEY_256_MASK) != 0) {
776            return NanCipherSuiteType.SHARED_KEY_256_MASK;
777        }
778        if ((supportedCipherSuites & NanCipherSuiteType.SHARED_KEY_128_MASK) != 0) {
779            return NanCipherSuiteType.SHARED_KEY_128_MASK;
780        }
781        return NanCipherSuiteType.NONE;
782    }
783
784    /**
785     * Converts a byte[] to an ArrayList<Byte>. Fills in the entries of the 'to' array if
786     * provided (non-null), otherwise creates and returns a new ArrayList<>.
787     *
788     * @param from The input byte[] to convert from.
789     * @param to An optional ArrayList<> to fill in from 'from'.
790     *
791     * @return A newly allocated ArrayList<> if 'to' is null, otherwise null.
792     */
793    private ArrayList<Byte> convertNativeByteArrayToArrayList(byte[] from, ArrayList<Byte> to) {
794        if (from == null) {
795            from = new byte[0];
796        }
797
798        if (to == null) {
799            to = new ArrayList<>(from.length);
800        } else {
801            to.ensureCapacity(from.length);
802        }
803        for (int i = 0; i < from.length; ++i) {
804            to.add(from[i]);
805        }
806        return to;
807    }
808
809    private void copyArray(byte[] from, byte[] to) {
810        if (from == null || to == null || from.length != to.length) {
811            Log.e(TAG, "copyArray error: from=" + from + ", to=" + to);
812            return;
813        }
814        for (int i = 0; i < from.length; ++i) {
815            to[i] = from[i];
816        }
817    }
818
819    private static String statusString(WifiStatus status) {
820        if (status == null) {
821            return "status=null";
822        }
823        StringBuilder sb = new StringBuilder();
824        sb.append(status.code).append(" (").append(status.description).append(")");
825        return sb.toString();
826    }
827
828    /**
829     * Dump the internal state of the class.
830     */
831    public void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
832        mHal.dump(fd, pw, args);
833    }
834}
835