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