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