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