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