WifiNative.java revision 38a6c1ba5d461b8c7b11685c5dd2e98d9e106b55
1/*
2 * Copyright (C) 2008 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;
18
19import android.annotation.NonNull;
20import android.annotation.Nullable;
21import android.content.Context;
22import android.net.apf.ApfCapabilities;
23import android.net.wifi.IApInterface;
24import android.net.wifi.IClientInterface;
25import android.net.wifi.RttManager;
26import android.net.wifi.RttManager.ResponderConfig;
27import android.net.wifi.ScanResult;
28import android.net.wifi.WifiConfiguration;
29import android.net.wifi.WifiEnterpriseConfig;
30import android.net.wifi.WifiLinkLayerStats;
31import android.net.wifi.WifiScanner;
32import android.net.wifi.WifiSsid;
33import android.net.wifi.WifiWakeReasonAndCounts;
34import android.net.wifi.WpsInfo;
35import android.net.wifi.p2p.WifiP2pConfig;
36import android.net.wifi.p2p.WifiP2pGroup;
37import android.net.wifi.p2p.nsd.WifiP2pServiceInfo;
38import android.os.SystemClock;
39import android.os.SystemProperties;
40import android.text.TextUtils;
41import android.util.LocalLog;
42import android.util.Log;
43import android.util.SparseArray;
44
45import com.android.internal.annotations.Immutable;
46import com.android.internal.annotations.VisibleForTesting;
47import com.android.internal.util.HexDump;
48import com.android.server.connectivity.KeepalivePacketData;
49import com.android.server.wifi.hotspot2.Utils;
50import com.android.server.wifi.util.FrameParser;
51import com.android.server.wifi.util.InformationElementUtil;
52import com.android.server.wifi.util.NativeUtil;
53
54import libcore.util.HexEncoding;
55
56import org.json.JSONException;
57import org.json.JSONObject;
58
59import java.io.PrintWriter;
60import java.io.StringWriter;
61import java.io.UnsupportedEncodingException;
62import java.net.URLDecoder;
63import java.net.URLEncoder;
64import java.nio.ByteBuffer;
65import java.nio.CharBuffer;
66import java.nio.charset.CharacterCodingException;
67import java.nio.charset.CharsetDecoder;
68import java.nio.charset.StandardCharsets;
69import java.text.SimpleDateFormat;
70import java.util.ArrayList;
71import java.util.BitSet;
72import java.util.Date;
73import java.util.HashMap;
74import java.util.Iterator;
75import java.util.List;
76import java.util.Locale;
77import java.util.Map;
78import java.util.Objects;
79import java.util.Set;
80import java.util.TimeZone;
81
82
83/**
84 * Native calls for bring up/shut down of the supplicant daemon and for
85 * sending requests to the supplicant daemon
86 *
87 * waitForEvent() is called on the monitor thread for events. All other methods
88 * must be serialized from the framework.
89 *
90 * {@hide}
91 */
92public class WifiNative {
93    private static boolean DBG = false;
94
95    // Must match wifi_hal.h
96    public static final int WIFI_SUCCESS = 0;
97
98    /**
99     * Hold this lock before calling supplicant or HAL methods
100     * it is required to mutually exclude access to the driver
101     */
102    public static final Object sLock = new Object();
103
104    private static final LocalLog sLocalLog = new LocalLog(8192);
105
106    public @NonNull LocalLog getLocalLog() {
107        return sLocalLog;
108    }
109
110    /* Register native functions */
111    static {
112        /* Native functions are defined in libwifi-service.so */
113        System.loadLibrary("wifi-service");
114        registerNatives();
115    }
116
117    private static native int registerNatives();
118
119    /*
120     * Singleton WifiNative instances
121     */
122    private static WifiNative wlanNativeInterface =
123            new WifiNative(SystemProperties.get("wifi.interface", "wlan0"), true);
124    public static WifiNative getWlanNativeInterface() {
125        return wlanNativeInterface;
126    }
127
128    private static WifiNative p2pNativeInterface =
129            // commands for p2p0 interface don't need prefix
130            new WifiNative(SystemProperties.get("wifi.direct.interface", "p2p0"), false);
131    public static WifiNative getP2pNativeInterface() {
132        return p2pNativeInterface;
133    }
134
135
136    // TODO(b/34884202): Set this to true to enable HIDL once we're fully ready.
137    private static final boolean HIDL_ENABLE = false;
138    public static final boolean HIDL_SUP_ENABLE = false;
139    private final String mTAG;
140    private final String mInterfaceName;
141    private final String mInterfacePrefix;
142    private SupplicantStaIfaceHal mSupplicantStaIfaceHal;
143    private SupplicantP2pIfaceHal mSupplicantP2pIfaceHal;
144    private WifiVendorHal mWifiVendorHal;
145    private WificondControl mWificondControl;
146    private WifiSupplicantControl mWifiSupplicantControl;
147
148    private Context mContext = null;
149    public void initContext(Context context) {
150        if (mContext == null && context != null) {
151            mContext = context;
152        }
153    }
154
155    /**
156     * Explicitly sets the SupplicantStaIfaceHal instance
157     * TODO(b/34722734): move this into the constructor of WifiNative when I clean up the awful
158     * double singleton pattern
159     */
160    public void setSupplicantStaIfaceHal(SupplicantStaIfaceHal wifiSupplicantHal) {
161        mSupplicantStaIfaceHal = wifiSupplicantHal;
162    }
163
164    /**
165     * Explicitly sets the WificondControl instance
166     * TODO(b/34722734): move this into the constructor of WifiNative when I clean up the awful
167     * double singleton pattern
168     */
169    public void setWificondControl(WificondControl wificondControl) {
170        mWificondControl = wificondControl;
171    }
172
173    /** Explicitly sets the SupplicantP2pIfaceHal instance
174     * TODO(b/34722734): move this into the constructor of WifiNative when I clean up the awful
175     * double singleton pattern
176     */
177    public void setSupplicantP2pIfaceHal(SupplicantP2pIfaceHal wifiSupplicantHal) {
178        mSupplicantP2pIfaceHal = wifiSupplicantHal;
179    }
180
181
182    /** Explicitly sets the WifiSupplicantControl instance
183     * TODO(b/34722734): move this into the constructor of WifiNative when I clean up the awful
184     * double singleton pattern
185     */
186    public void setWifiSupplicantControl(WifiSupplicantControl wifiSupplicantControl) {
187        mWifiSupplicantControl = wifiSupplicantControl;
188    }
189
190    /**
191     * Explicitly sets the WifiVendorHal instance
192     * TODO(b/34722734): move this into the constructor of WifiNative when I clean up the awful
193     * double singleton pattern
194     */
195    public void setWifiVendorHal(WifiVendorHal wifiVendorHal) {
196        mWifiVendorHal = wifiVendorHal;
197    }
198
199    private WifiNative(String interfaceName,
200                       boolean requiresPrefix) {
201        mInterfaceName = interfaceName;
202        mTAG = "WifiNative-" + interfaceName;
203
204        if (requiresPrefix) {
205            mInterfacePrefix = "IFNAME=" + interfaceName + " ";
206        } else {
207            mInterfacePrefix = "";
208        }
209    }
210
211    /**
212     * Initializes the vendor HAL. This is just used to initialize the {@link HalDeviceManager}.
213     */
214    public boolean initializeVendorHal() {
215        if (!HIDL_ENABLE) {
216            return true;
217        }
218        return mWifiVendorHal.initialize();
219    }
220
221    public String getInterfaceName() {
222        return mInterfaceName;
223    }
224
225    // Note this affects logging on for all interfaces
226    void enableVerboseLogging(int verbose) {
227        if (verbose > 0) {
228            DBG = true;
229        } else {
230            DBG = false;
231        }
232        if (mWificondControl != null) {
233            mWificondControl.enableVerboseLogging(verbose > 0 ? true : false);
234        }
235        if (mSupplicantStaIfaceHal != null) {
236            mSupplicantStaIfaceHal.enableVerboseLogging(verbose > 0);
237        }
238    }
239
240    private void localLog(String s) {
241        if (sLocalLog != null) sLocalLog.log(mInterfaceName + ": " + s);
242    }
243
244   /**
245    * Setup driver for client mode via wificond.
246    * @return An IClientInterface as wificond client interface binder handler.
247    * Returns null on failure.
248    */
249    public IClientInterface setupDriverForClientMode() {
250        IClientInterface clientInterface = mWificondControl.setupDriverForClientMode();
251        if (!startHal(true)) {
252            // TODO(b/34859006): Handle failures.
253            Log.e(TAG, "Failed to start HAL for client mode");
254        }
255        return clientInterface;
256    }
257
258    /**
259    * Setup driver for softAp mode via wificond.
260    * @return An IApInterface as wificond Ap interface binder handler.
261    * Returns null on failure.
262    */
263    public IApInterface setupDriverForSoftApMode() {
264        IApInterface apInterface = mWificondControl.setupDriverForSoftApMode();
265
266        if (!startHal(false)) {
267            // TODO(b/34859006): Handle failures.
268            Log.e(TAG, "Failed to start HAL for AP mode");
269        }
270        return apInterface;
271    }
272
273    /**
274    * Teardown all interfaces configured in wificond.
275    * @return Returns true on success.
276    */
277    public boolean tearDownInterfaces() {
278        return mWificondControl.tearDownInterfaces();
279    }
280
281    /**
282    * Disable wpa_supplicant via wificond.
283    * @return Returns true on success.
284    */
285    public boolean disableSupplicant() {
286        return mWificondControl.disableSupplicant();
287    }
288
289    /**
290    * Enable wpa_supplicant via wificond.
291    * @return Returns true on success.
292    */
293    public boolean enableSupplicant() {
294        return mWificondControl.enableSupplicant();
295    }
296
297    /**
298    * Request signal polling to wificond.
299    * Returns an SignalPollResult object.
300    * Returns null on failure.
301    */
302    public SignalPollResult signalPoll() {
303        return mWificondControl.signalPoll();
304    }
305
306    /**
307     * Fetch TX packet counters on current connection from wificond.
308    * Returns an TxPacketCounters object.
309    * Returns null on failure.
310    */
311    public TxPacketCounters getTxPacketCounters() {
312        return mWificondControl.getTxPacketCounters();
313    }
314
315    /*
316     * Supplicant management
317     */
318    private native static boolean connectToSupplicantNative();
319    /**
320     * This method is called repeatedly until the connection to wpa_supplicant is established.
321     *
322     * @return true if connection is established, false otherwise.
323     */
324    public boolean connectToSupplicant() {
325        if (HIDL_SUP_ENABLE) {
326            // Start initialization if not already started.
327            if (!mSupplicantP2pIfaceHal.isInitializationStarted()
328                    && !mSupplicantP2pIfaceHal.initialize()) {
329                return false;
330            }
331            if (!mSupplicantStaIfaceHal.isInitializationStarted()
332                    && !mSupplicantStaIfaceHal.initialize()) {
333                return false;
334            }
335            // Check if the initialization is complete.
336            return (mSupplicantP2pIfaceHal.isInitializationComplete()
337                    && mSupplicantStaIfaceHal.isInitializationComplete());
338        } else {
339            synchronized (sLock) {
340                localLog(mInterfacePrefix + "connectToSupplicant");
341                return connectToSupplicantNative();
342            }
343        }
344    }
345
346    private native static void closeSupplicantConnectionNative();
347    public void closeSupplicantConnection() {
348        if (HIDL_SUP_ENABLE) {
349            // Nothing to do for HIDL.
350        } else {
351            synchronized (sLock) {
352                localLog(mInterfacePrefix + "closeSupplicantConnection");
353                closeSupplicantConnectionNative();
354            }
355        }
356    }
357
358    /**
359     * Wait for the supplicant to send an event, returning the event string.
360     * @return the event string sent by the supplicant.
361     */
362    private native static String waitForEventNative();
363    public String waitForEvent() {
364        // No synchronization necessary .. it is implemented in WifiMonitor
365        return waitForEventNative();
366    }
367
368
369    /*
370     * Supplicant Command Primitives
371     */
372    private native boolean doBooleanCommandNative(String command);
373
374    private native int doIntCommandNative(String command);
375
376    private native String doStringCommandNative(String command);
377
378    private boolean doBooleanCommand(String command) {
379        if (DBG) Log.d(mTAG, "doBoolean: " + command);
380        synchronized (sLock) {
381            String toLog = mInterfacePrefix + command;
382            boolean result = doBooleanCommandNative(mInterfacePrefix + command);
383            localLog(toLog + " -> " + result);
384            if (DBG) Log.d(mTAG, command + ": returned " + result);
385            return result;
386        }
387    }
388
389    private boolean doBooleanCommandWithoutLogging(String command) {
390        if (DBG) Log.d(mTAG, "doBooleanCommandWithoutLogging: " + command);
391        synchronized (sLock) {
392            boolean result = doBooleanCommandNative(mInterfacePrefix + command);
393            if (DBG) Log.d(mTAG, command + ": returned " + result);
394            return result;
395        }
396    }
397
398    private int doIntCommand(String command) {
399        if (DBG) Log.d(mTAG, "doInt: " + command);
400        synchronized (sLock) {
401            String toLog = mInterfacePrefix + command;
402            int result = doIntCommandNative(mInterfacePrefix + command);
403            localLog(toLog + " -> " + result);
404            if (DBG) Log.d(mTAG, "   returned " + result);
405            return result;
406        }
407    }
408
409    private String doStringCommand(String command) {
410        if (DBG) {
411            //GET_NETWORK commands flood the logs
412            if (!command.startsWith("GET_NETWORK")) {
413                Log.d(mTAG, "doString: [" + command + "]");
414            }
415        }
416        synchronized (sLock) {
417            String toLog = mInterfacePrefix + command;
418            String result = doStringCommandNative(mInterfacePrefix + command);
419            if (result == null) {
420                if (DBG) Log.d(mTAG, "doStringCommandNative no result");
421            } else {
422                if (!command.startsWith("STATUS-")) {
423                    localLog(toLog + " -> " + result);
424                }
425                if (DBG) Log.d(mTAG, "   returned " + result.replace("\n", " "));
426            }
427            return result;
428        }
429    }
430
431    private String doStringCommandWithoutLogging(String command) {
432        if (DBG) {
433            //GET_NETWORK commands flood the logs
434            if (!command.startsWith("GET_NETWORK")) {
435                Log.d(mTAG, "doString: [" + command + "]");
436            }
437        }
438        synchronized (sLock) {
439            return doStringCommandNative(mInterfacePrefix + command);
440        }
441    }
442
443    public String doCustomSupplicantCommand(String command) {
444        return doStringCommand(command);
445    }
446
447
448    /*
449     * Wrappers for supplicant commands
450     */
451    /**
452     * Set supplicant log level
453     *
454     * @param turnOnVerbose Whether to turn on verbose logging or not.
455     */
456    public void setSupplicantLogLevel(boolean turnOnVerbose) {
457        if (HIDL_SUP_ENABLE) {
458            int logLevel = turnOnVerbose
459                    ? SupplicantStaIfaceHal.LOG_LEVEL_DEBUG
460                    : SupplicantStaIfaceHal.LOG_LEVEL_INFO;
461            mSupplicantStaIfaceHal.setLogLevel(logLevel);
462        } else {
463            doStringCommand("LOG_LEVEL " + (turnOnVerbose ? "DEBUG" : "INFO"));
464        }
465    }
466
467    /*
468     * Convert string to Hexadecimal before passing to wifi native layer
469     * In native function "doCommand()" have trouble in converting Unicode character string to UTF8
470     * conversion to hex is required because SSIDs can have space characters in them;
471     * and that can confuses the supplicant because it uses space charaters as delimiters
472     */
473    public static String encodeSSID(String ssid) {
474        int length = ssid.length();
475        if ((length > 1) && (ssid.charAt(0) == '"')
476                && (ssid.charAt(length - 1) == '"')) {
477            ssid = ssid.substring(1, length - 1);
478        }
479        return Utils.toHex(ssid.getBytes(StandardCharsets.UTF_8));
480    }
481
482    /**
483     * Start a scan using wpa_supplicant for the given frequencies.
484     * @param freqs list of frequencies to scan for, if null scan all supported channels.
485     * @param hiddenNetworkSSIDs List of hidden networks to be scanned for.
486     */
487    public boolean scan(Set<Integer> freqs, Set<String> hiddenNetworkSSIDs) {
488        String freqList = null;
489        String hiddenNetworkSSIDList = null;
490        if (freqs != null && freqs.size() != 0) {
491            freqList = TextUtils.join(",", freqs);
492        }
493        if (hiddenNetworkSSIDs != null && hiddenNetworkSSIDs.size() != 0) {
494            StringBuilder ssidList = new StringBuilder();
495            for (String ssid : hiddenNetworkSSIDs) {
496                ssidList.append(encodeSSID(ssid)).append(" ");
497            }
498            hiddenNetworkSSIDList = ssidList.toString();
499        }
500        return scanWithParams(freqList, hiddenNetworkSSIDList);
501    }
502
503    private boolean scanWithParams(String freqList, String hiddenNetworkSSIDList) {
504        StringBuilder scanCommand = new StringBuilder();
505        scanCommand.append("SCAN TYPE=ONLY");
506        if (freqList != null) {
507            scanCommand.append(" freq=" + freqList);
508        }
509        if (hiddenNetworkSSIDList != null) {
510            scanCommand.append(" ssid " + hiddenNetworkSSIDList);
511        }
512        return doBooleanCommand(scanCommand.toString());
513    }
514
515    /* Does a graceful shutdown of supplicant. Is a common stop function for both p2p and sta.
516     *
517     * Note that underneath we use a harsh-sounding "terminate" supplicant command
518     * for a graceful stop and a mild-sounding "stop" interface
519     * to kill the process
520     */
521    public boolean stopSupplicant() {
522        return doBooleanCommand("TERMINATE");
523    }
524
525    public String listNetworks() {
526        return doStringCommand("LIST_NETWORKS");
527    }
528
529    public String listNetworks(int last_id) {
530        return doStringCommand("LIST_NETWORKS LAST_ID=" + last_id);
531    }
532
533    public int addNetwork() {
534        return doIntCommand("ADD_NETWORK");
535    }
536
537    public boolean setNetworkExtra(int netId, String name, Map<String, String> values) {
538        String encoded = createNetworkExtra(values);
539        if (encoded == null) {
540            return false;
541        }
542        return setNetworkVariable(netId, name, "\"" + encoded + "\"");
543    }
544
545    @VisibleForTesting
546    public static String createNetworkExtra(Map<String, String> values) {
547        final String encoded;
548        try {
549            encoded = URLEncoder.encode(new JSONObject(values).toString(), "UTF-8");
550        } catch (NullPointerException e) {
551            Log.e(TAG, "Unable to serialize networkExtra: " + e.toString());
552            return null;
553        } catch (UnsupportedEncodingException e) {
554            Log.e(TAG, "Unable to serialize networkExtra: " + e.toString());
555            return null;
556        }
557        return encoded;
558    }
559
560    public boolean setNetworkVariable(int netId, String name, String value) {
561        if (TextUtils.isEmpty(name) || TextUtils.isEmpty(value)) return false;
562        if (name.equals(WifiConfiguration.pskVarName)
563                || name.equals(WifiEnterpriseConfig.PASSWORD_KEY)
564                || name.equals(WifiEnterpriseConfig.IDENTITY_KEY)
565                || name.equals(WifiEnterpriseConfig.ANON_IDENTITY_KEY)) {
566            return doBooleanCommandWithoutLogging("SET_NETWORK " + netId + " " + name + " " + value);
567        } else {
568            return doBooleanCommand("SET_NETWORK " + netId + " " + name + " " + value);
569        }
570    }
571
572    public Map<String, String> getNetworkExtra(int netId, String name) {
573        final String extraString = getNetworkVariable(netId, name);
574        if (extraString == null || !extraString.startsWith("\"") || !extraString.endsWith("\"")) {
575            return null;
576        }
577        return parseNetworkExtra(NativeUtil.removeEnclosingQuotes(extraString));
578    }
579
580    /**
581     * Parse the network extra JSON encoded string to a map of string key, value pairs.
582     */
583    public static Map<String, String> parseNetworkExtra(String encoded) {
584        if (TextUtils.isEmpty(encoded)) {
585            return null;
586        }
587        try {
588            // This method reads a JSON dictionary that was written by setNetworkExtra(). However,
589            // on devices that upgraded from Marshmallow, it may encounter a legacy value instead -
590            // an FQDN stored as a plain string. If such a value is encountered, the JSONObject
591            // constructor will thrown a JSONException and the method will return null.
592            final JSONObject json = new JSONObject(URLDecoder.decode(encoded, "UTF-8"));
593            final Map<String, String> values = new HashMap<>();
594            final Iterator<?> it = json.keys();
595            while (it.hasNext()) {
596                final String key = (String) it.next();
597                final Object value = json.get(key);
598                if (value instanceof String) {
599                    values.put(key, (String) value);
600                }
601            }
602            return values;
603        } catch (UnsupportedEncodingException e) {
604            Log.e(TAG, "Unable to deserialize networkExtra: " + e.toString());
605            return null;
606        } catch (JSONException e) {
607            // This is not necessarily an error. This exception will also occur if we encounter a
608            // legacy FQDN stored as a plain string. We want to return null in this case as no JSON
609            // dictionary of extras was found.
610            return null;
611        }
612    }
613
614    public String getNetworkVariable(int netId, String name) {
615        if (TextUtils.isEmpty(name)) return null;
616
617        // GET_NETWORK will likely flood the logs ...
618        return doStringCommandWithoutLogging("GET_NETWORK " + netId + " " + name);
619    }
620
621    public boolean removeNetwork(int netId) {
622        return doBooleanCommand("REMOVE_NETWORK " + netId);
623    }
624
625    private void logDbg(String debug) {
626        long now = SystemClock.elapsedRealtimeNanos();
627        String ts = String.format("[%,d us] ", now/1000);
628        Log.e("WifiNative: ", ts+debug+ " stack:"
629                + Thread.currentThread().getStackTrace()[2].getMethodName() +" - "
630                + Thread.currentThread().getStackTrace()[3].getMethodName() +" - "
631                + Thread.currentThread().getStackTrace()[4].getMethodName() +" - "
632                + Thread.currentThread().getStackTrace()[5].getMethodName()+" - "
633                + Thread.currentThread().getStackTrace()[6].getMethodName());
634
635    }
636
637    /**
638     * Select a network in wpa_supplicant (Disables all others).
639     * @param netId - Network ID of the network to be selected.
640     * @return true if command succeeded, false otherwise.
641     */
642    public boolean selectNetwork(int netId) {
643        if (DBG) logDbg("selectNetwork nid=" + Integer.toString(netId));
644        return doBooleanCommand("SELECT_NETWORK " + netId);
645    }
646
647    public boolean reconnect() {
648        if (DBG) logDbg("RECONNECT ");
649        if (HIDL_SUP_ENABLE) {
650            return mSupplicantStaIfaceHal.reconnect();
651        } else {
652            return doBooleanCommand("RECONNECT");
653        }
654    }
655
656    public boolean reassociate() {
657        if (DBG) logDbg("REASSOCIATE ");
658        if (HIDL_SUP_ENABLE) {
659            return mSupplicantStaIfaceHal.reassociate();
660        } else {
661            return doBooleanCommand("REASSOCIATE");
662        }
663    }
664
665    public boolean disconnect() {
666        if (DBG) logDbg("DISCONNECT ");
667        if (HIDL_SUP_ENABLE) {
668            return mSupplicantStaIfaceHal.disconnect();
669        } else {
670            return doBooleanCommand("DISCONNECT");
671        }
672    }
673
674    public String status() {
675        return status(false);
676    }
677
678    public String status(boolean noEvents) {
679        if (noEvents) {
680            return doStringCommand("STATUS-NO_EVENTS");
681        } else {
682            return doStringCommand("STATUS");
683        }
684    }
685
686    public String getMacAddress() {
687        if (HIDL_SUP_ENABLE) {
688            return mSupplicantStaIfaceHal.getMacAddress();
689        } else {
690            //Macaddr = XX.XX.XX.XX.XX.XX
691            String ret = doStringCommand("DRIVER MACADDR");
692            if (!TextUtils.isEmpty(ret)) {
693                String[] tokens = ret.split(" = ");
694                if (tokens.length == 2) return tokens[1];
695            }
696            return null;
697        }
698    }
699
700    /**
701    * Fetch the latest scan result from kernel via wificond.
702    * @return Returns an ArrayList of ScanDetail.
703    * Returns an empty ArrayList on failure.
704    */
705    public ArrayList<ScanDetail> getScanResults() {
706        return mWificondControl.getScanResults();
707    }
708
709    /**
710     * Format of result:
711     * id=1016
712     * bssid=00:03:7f:40:84:10
713     * freq=2462
714     * beacon_int=200
715     * capabilities=0x0431
716     * qual=0
717     * noise=0
718     * level=-46
719     * tsf=0000002669008476
720     * age=5
721     * ie=00105143412d485332302d52322d54455354010882848b960c12182403010b0706555...
722     * flags=[WPA2-EAP-CCMP][ESS][P2P][HS20]
723     * ssid=QCA-HS20-R2-TEST
724     * p2p_device_name=
725     * p2p_config_methods=0x0SET_NE
726     * anqp_venue_name=02083d656e6757692d466920416c6c69616e63650a3239383920436f...
727     * anqp_network_auth_type=010000
728     * anqp_roaming_consortium=03506f9a05001bc504bd
729     * anqp_ip_addr_type_availability=0c
730     * anqp_nai_realm=0200300000246d61696c2e6578616d706c652e636f6d3b636973636f2...
731     * anqp_3gpp=000600040132f465
732     * anqp_domain_name=0b65786d61706c652e636f6d
733     * hs20_operator_friendly_name=11656e6757692d466920416c6c69616e63650e636869...
734     * hs20_wan_metrics=01c40900008001000000000a00
735     * hs20_connection_capability=0100000006140001061600000650000106bb010106bb0...
736     * hs20_osu_providers_list=0b5143412d4f53552d425353010901310015656e6757692d...
737     */
738    public String scanResult(String bssid) {
739        return doStringCommand("BSS " + bssid);
740    }
741
742    /**
743     * Start filtering out Multicast V4 packets
744     * @return {@code true} if the operation succeeded, {@code false} otherwise
745     *
746     * Multicast filtering rules work as follows:
747     *
748     * The driver can filter multicast (v4 and/or v6) and broadcast packets when in
749     * a power optimized mode (typically when screen goes off).
750     *
751     * In order to prevent the driver from filtering the multicast/broadcast packets, we have to
752     * add a DRIVER RXFILTER-ADD rule followed by DRIVER RXFILTER-START to make the rule effective
753     *
754     * DRIVER RXFILTER-ADD Num
755     *   where Num = 0 - Unicast, 1 - Broadcast, 2 - Mutil4 or 3 - Multi6
756     *
757     * and DRIVER RXFILTER-START
758     * In order to stop the usage of these rules, we do
759     *
760     * DRIVER RXFILTER-STOP
761     * DRIVER RXFILTER-REMOVE Num
762     *   where Num is as described for RXFILTER-ADD
763     *
764     * The  SETSUSPENDOPT driver command overrides the filtering rules
765     */
766    public boolean startFilteringMulticastV4Packets() {
767        if (HIDL_SUP_ENABLE) {
768            return mSupplicantStaIfaceHal.stopRxFilter()
769                    && mSupplicantStaIfaceHal.removeRxFilter(
770                    SupplicantStaIfaceHal.RX_FILTER_TYPE_V4_MULTICAST)
771                    && mSupplicantStaIfaceHal.stopRxFilter();
772        } else {
773            return doBooleanCommand("DRIVER RXFILTER-STOP")
774                    && doBooleanCommand("DRIVER RXFILTER-REMOVE 2")
775                    && doBooleanCommand("DRIVER RXFILTER-START");
776        }
777    }
778
779    /**
780     * Stop filtering out Multicast V4 packets.
781     * @return {@code true} if the operation succeeded, {@code false} otherwise
782     */
783    public boolean stopFilteringMulticastV4Packets() {
784        if (HIDL_SUP_ENABLE) {
785            return mSupplicantStaIfaceHal.stopRxFilter()
786                    && mSupplicantStaIfaceHal.addRxFilter(
787                    SupplicantStaIfaceHal.RX_FILTER_TYPE_V4_MULTICAST)
788                    && mSupplicantStaIfaceHal.stopRxFilter();
789        } else {
790            return doBooleanCommand("DRIVER RXFILTER-STOP")
791                    && doBooleanCommand("DRIVER RXFILTER-ADD 2")
792                    && doBooleanCommand("DRIVER RXFILTER-START");
793        }
794    }
795
796    /**
797     * Start filtering out Multicast V6 packets
798     * @return {@code true} if the operation succeeded, {@code false} otherwise
799     */
800    public boolean startFilteringMulticastV6Packets() {
801        if (HIDL_SUP_ENABLE) {
802            return mSupplicantStaIfaceHal.stopRxFilter()
803                    && mSupplicantStaIfaceHal.removeRxFilter(
804                    SupplicantStaIfaceHal.RX_FILTER_TYPE_V6_MULTICAST)
805                    && mSupplicantStaIfaceHal.stopRxFilter();
806        } else {
807            return doBooleanCommand("DRIVER RXFILTER-STOP")
808                    && doBooleanCommand("DRIVER RXFILTER-REMOVE 3")
809                    && doBooleanCommand("DRIVER RXFILTER-START");
810        }
811    }
812
813    /**
814     * Stop filtering out Multicast V6 packets.
815     * @return {@code true} if the operation succeeded, {@code false} otherwise
816     */
817    public boolean stopFilteringMulticastV6Packets() {
818        if (HIDL_SUP_ENABLE) {
819            return mSupplicantStaIfaceHal.stopRxFilter()
820                    && mSupplicantStaIfaceHal.addRxFilter(
821                    SupplicantStaIfaceHal.RX_FILTER_TYPE_V6_MULTICAST)
822                    && mSupplicantStaIfaceHal.stopRxFilter();
823        } else {
824            return doBooleanCommand("DRIVER RXFILTER-STOP")
825                    && doBooleanCommand("DRIVER RXFILTER-ADD 3")
826                    && doBooleanCommand("DRIVER RXFILTER-START");
827        }
828    }
829
830    public static final int BLUETOOTH_COEXISTENCE_MODE_ENABLED     = 0;
831    public static final int BLUETOOTH_COEXISTENCE_MODE_DISABLED    = 1;
832    public static final int BLUETOOTH_COEXISTENCE_MODE_SENSE       = 2;
833    /**
834      * Sets the bluetooth coexistence mode.
835      *
836      * @param mode One of {@link #BLUETOOTH_COEXISTENCE_MODE_DISABLED},
837      *            {@link #BLUETOOTH_COEXISTENCE_MODE_ENABLED}, or
838      *            {@link #BLUETOOTH_COEXISTENCE_MODE_SENSE}.
839      * @return Whether the mode was successfully set.
840      */
841    public boolean setBluetoothCoexistenceMode(int mode) {
842        if (HIDL_SUP_ENABLE) {
843            return mSupplicantStaIfaceHal.setBtCoexistenceMode((byte) mode);
844        } else {
845            return doBooleanCommand("DRIVER BTCOEXMODE " + mode);
846        }
847    }
848
849    /**
850     * Enable or disable Bluetooth coexistence scan mode. When this mode is on,
851     * some of the low-level scan parameters used by the driver are changed to
852     * reduce interference with A2DP streaming.
853     *
854     * @param setCoexScanMode whether to enable or disable this mode
855     * @return {@code true} if the command succeeded, {@code false} otherwise.
856     */
857    public boolean setBluetoothCoexistenceScanMode(boolean setCoexScanMode) {
858        if (HIDL_SUP_ENABLE) {
859            return mSupplicantStaIfaceHal.setBtCoexistenceScanModeEnabled(setCoexScanMode);
860        } else {
861            if (setCoexScanMode) {
862                return doBooleanCommand("DRIVER BTCOEXSCAN-START");
863            } else {
864                return doBooleanCommand("DRIVER BTCOEXSCAN-STOP");
865            }
866        }
867    }
868
869    public boolean setSuspendOptimizations(boolean enabled) {
870        if (HIDL_SUP_ENABLE) {
871            return mSupplicantStaIfaceHal.setSuspendModeEnabled(enabled);
872        } else {
873            if (enabled) {
874                return doBooleanCommand("DRIVER SETSUSPENDMODE 1");
875            } else {
876                return doBooleanCommand("DRIVER SETSUSPENDMODE 0");
877            }
878        }
879    }
880
881    public boolean setCountryCode(String countryCode) {
882        if (HIDL_SUP_ENABLE) {
883            return mSupplicantStaIfaceHal.setCountryCode(countryCode);
884        } else {
885            if (countryCode != null) {
886                return doBooleanCommand("DRIVER COUNTRY " + countryCode.toUpperCase(Locale.ROOT));
887            } else {
888                return doBooleanCommand("DRIVER COUNTRY");
889            }
890        }
891    }
892
893    /**
894     * Start/Stop PNO scan.
895     * @param enable boolean indicating whether PNO is being enabled or disabled.
896     */
897    public boolean setPnoScan(boolean enable) {
898        String cmd = enable ? "SET pno 1" : "SET pno 0";
899        return doBooleanCommand(cmd);
900    }
901
902    public void startTdls(String macAddr, boolean enable) {
903        if (HIDL_SUP_ENABLE) {
904            if (enable) {
905                mSupplicantStaIfaceHal.initiateTdlsDiscover(macAddr);
906                mSupplicantStaIfaceHal.initiateTdlsSetup(macAddr);
907            } else {
908                mSupplicantStaIfaceHal.initiateTdlsTeardown(macAddr);
909            }
910        } else {
911            if (enable) {
912                synchronized (sLock) {
913                    doBooleanCommand("TDLS_DISCOVER " + macAddr);
914                    doBooleanCommand("TDLS_SETUP " + macAddr);
915                }
916            } else {
917                doBooleanCommand("TDLS_TEARDOWN " + macAddr);
918            }
919        }
920    }
921
922    public boolean startWpsPbc(String bssid) {
923        if (HIDL_SUP_ENABLE) {
924            return mSupplicantStaIfaceHal.startWpsPbc(bssid);
925        } else {
926            if (TextUtils.isEmpty(bssid)) {
927                return doBooleanCommand("WPS_PBC");
928            } else {
929                return doBooleanCommand("WPS_PBC " + bssid);
930            }
931        }
932    }
933
934    public boolean startWpsPbc(String iface, String bssid) {
935        synchronized (sLock) {
936            if (TextUtils.isEmpty(bssid)) {
937                return doBooleanCommandNative("IFNAME=" + iface + " WPS_PBC");
938            } else {
939                return doBooleanCommandNative("IFNAME=" + iface + " WPS_PBC " + bssid);
940            }
941        }
942    }
943
944    public boolean startWpsPinKeypad(String pin) {
945        if (TextUtils.isEmpty(pin)) return false;
946        if (HIDL_SUP_ENABLE) {
947            return mSupplicantStaIfaceHal.startWpsPinKeypad(pin);
948        } else {
949            return doBooleanCommand("WPS_PIN any " + pin);
950        }
951    }
952
953    public boolean startWpsPinKeypad(String iface, String pin) {
954        if (TextUtils.isEmpty(pin)) return false;
955        synchronized (sLock) {
956            return doBooleanCommandNative("IFNAME=" + iface + " WPS_PIN any " + pin);
957        }
958    }
959
960
961    public String startWpsPinDisplay(String bssid) {
962        if (HIDL_SUP_ENABLE) {
963            return mSupplicantStaIfaceHal.startWpsPinDisplay(bssid);
964        } else {
965            if (TextUtils.isEmpty(bssid)) {
966                return doStringCommand("WPS_PIN any");
967            } else {
968                return doStringCommand("WPS_PIN " + bssid);
969            }
970        }
971    }
972
973    public String startWpsPinDisplay(String iface, String bssid) {
974        synchronized (sLock) {
975            if (TextUtils.isEmpty(bssid)) {
976                return doStringCommandNative("IFNAME=" + iface + " WPS_PIN any");
977            } else {
978                return doStringCommandNative("IFNAME=" + iface + " WPS_PIN " + bssid);
979            }
980        }
981    }
982
983    public boolean setExternalSim(boolean external) {
984        if (HIDL_SUP_ENABLE) {
985            return mSupplicantStaIfaceHal.setExternalSim(external);
986        } else {
987            String value = external ? "1" : "0";
988            Log.d(TAG, "Setting external_sim to " + value);
989            return doBooleanCommand("SET external_sim " + value);
990        }
991    }
992
993    public boolean simAuthResponse(int id, String type, String response) {
994        if (HIDL_SUP_ENABLE) {
995            if ("GSM-AUTH".equals(type)) {
996                return mSupplicantStaIfaceHal.sendCurrentNetworkEapSimGsmAuthResponse(response);
997            } else if ("UMTS-AUTH".equals(type)) {
998                return mSupplicantStaIfaceHal.sendCurrentNetworkEapSimUmtsAuthResponse(response);
999            } else if ("UMTS-AUTS".equals(type)) {
1000                return mSupplicantStaIfaceHal.sendCurrentNetworkEapSimUmtsAuthResponse(response);
1001            } else {
1002                return false;
1003            }
1004        } else {
1005            // with type = GSM-AUTH, UMTS-AUTH or UMTS-AUTS
1006            return doBooleanCommand("CTRL-RSP-SIM-" + id + ":" + type + response);
1007        }
1008    }
1009
1010    public boolean simAuthFailedResponse(int id) {
1011        if (HIDL_SUP_ENABLE) {
1012            return mSupplicantStaIfaceHal.sendCurrentNetworkEapSimGsmAuthFailure();
1013        } else {
1014            // should be used with type GSM-AUTH
1015            return doBooleanCommand("CTRL-RSP-SIM-" + id + ":GSM-FAIL");
1016        }
1017    }
1018
1019    public boolean umtsAuthFailedResponse(int id) {
1020        if (HIDL_SUP_ENABLE) {
1021            return mSupplicantStaIfaceHal.sendCurrentNetworkEapSimUmtsAuthFailure();
1022        } else {
1023            // should be used with type UMTS-AUTH
1024            return doBooleanCommand("CTRL-RSP-SIM-" + id + ":UMTS-FAIL");
1025        }
1026    }
1027
1028    public boolean simIdentityResponse(int id, String response) {
1029        if (HIDL_SUP_ENABLE) {
1030            return mSupplicantStaIfaceHal.sendCurrentNetworkEapIdentityResponse(response);
1031        } else {
1032            return doBooleanCommand("CTRL-RSP-IDENTITY-" + id + ":" + response);
1033        }
1034    }
1035
1036    /* Configures an access point connection */
1037    public boolean startWpsRegistrar(String bssid, String pin) {
1038        if (TextUtils.isEmpty(bssid) || TextUtils.isEmpty(pin)) return false;
1039        if (HIDL_SUP_ENABLE) {
1040            return mSupplicantStaIfaceHal.startWpsRegistrar(bssid, pin);
1041        } else {
1042            return doBooleanCommand("WPS_REG " + bssid + " " + pin);
1043        }
1044    }
1045
1046    public boolean cancelWps() {
1047        if (HIDL_SUP_ENABLE) {
1048            return mSupplicantStaIfaceHal.cancelWps();
1049        } else {
1050            return doBooleanCommand("WPS_CANCEL");
1051        }
1052    }
1053
1054    public boolean setPersistentReconnect(boolean enabled) {
1055        int value = (enabled == true) ? 1 : 0;
1056        return doBooleanCommand("SET persistent_reconnect " + value);
1057    }
1058
1059    public boolean setDeviceName(String name) {
1060        if (HIDL_SUP_ENABLE) {
1061            return mSupplicantStaIfaceHal.setWpsDeviceName(name);
1062        } else {
1063            return doBooleanCommand("SET device_name " + name);
1064        }
1065    }
1066
1067    public boolean setDeviceType(String type) {
1068        if (HIDL_SUP_ENABLE) {
1069            return mSupplicantStaIfaceHal.setWpsDeviceType(type);
1070        } else {
1071            return doBooleanCommand("SET device_type " + type);
1072        }
1073    }
1074
1075    public boolean setConfigMethods(String cfg) {
1076        if (HIDL_SUP_ENABLE) {
1077            return mSupplicantStaIfaceHal.setWpsConfigMethods(cfg);
1078        } else {
1079            return doBooleanCommand("SET config_methods " + cfg);
1080        }
1081    }
1082
1083    public boolean setManufacturer(String value) {
1084        if (HIDL_SUP_ENABLE) {
1085            return mSupplicantStaIfaceHal.setWpsManufacturer(value);
1086        } else {
1087            return doBooleanCommand("SET manufacturer " + value);
1088        }
1089    }
1090
1091    public boolean setModelName(String value) {
1092        if (HIDL_SUP_ENABLE) {
1093            return mSupplicantStaIfaceHal.setWpsModelName(value);
1094        } else {
1095            return doBooleanCommand("SET model_name " + value);
1096        }
1097    }
1098
1099    public boolean setModelNumber(String value) {
1100        if (HIDL_SUP_ENABLE) {
1101            return mSupplicantStaIfaceHal.setWpsModelNumber(value);
1102        } else {
1103            return doBooleanCommand("SET model_number " + value);
1104        }
1105    }
1106
1107    public boolean setSerialNumber(String value) {
1108        if (HIDL_SUP_ENABLE) {
1109            return mSupplicantStaIfaceHal.setWpsSerialNumber(value);
1110        } else {
1111            return doBooleanCommand("SET serial_number " + value);
1112        }
1113    }
1114
1115    public boolean setP2pSsidPostfix(String postfix) {
1116        return doBooleanCommand("SET p2p_ssid_postfix " + postfix);
1117    }
1118
1119    public boolean setP2pGroupIdle(String iface, int time) {
1120        synchronized (sLock) {
1121            return doBooleanCommandNative("IFNAME=" + iface + " SET p2p_group_idle " + time);
1122        }
1123    }
1124
1125    public void setPowerSave(boolean enabled) {
1126        if (HIDL_SUP_ENABLE) {
1127            mSupplicantStaIfaceHal.setPowerSave(enabled);
1128        } else {
1129            if (enabled) {
1130                doBooleanCommand("SET ps 1");
1131            } else {
1132                doBooleanCommand("SET ps 0");
1133            }
1134        }
1135    }
1136
1137    public boolean setP2pPowerSave(String iface, boolean enabled) {
1138        synchronized (sLock) {
1139            if (enabled) {
1140                return doBooleanCommandNative("IFNAME=" + iface + " P2P_SET ps 1");
1141            } else {
1142                return doBooleanCommandNative("IFNAME=" + iface + " P2P_SET ps 0");
1143            }
1144        }
1145    }
1146
1147    public boolean setWfdEnable(boolean enable) {
1148        return doBooleanCommand("SET wifi_display " + (enable ? "1" : "0"));
1149    }
1150
1151    public boolean setWfdDeviceInfo(String hex) {
1152        return doBooleanCommand("WFD_SUBELEM_SET 0 " + hex);
1153    }
1154
1155    /**
1156     * "sta" prioritizes STA connection over P2P and "p2p" prioritizes
1157     * P2P connection over STA
1158     */
1159    public boolean setConcurrencyPriority(String s) {
1160        return doBooleanCommand("P2P_SET conc_pref " + s);
1161    }
1162
1163    public boolean p2pFind() {
1164        return doBooleanCommand("P2P_FIND");
1165    }
1166
1167    public boolean p2pFind(int timeout) {
1168        if (timeout <= 0) {
1169            return p2pFind();
1170        }
1171        return doBooleanCommand("P2P_FIND " + timeout);
1172    }
1173
1174    public boolean p2pStopFind() {
1175       return doBooleanCommand("P2P_STOP_FIND");
1176    }
1177
1178    public boolean p2pListen() {
1179        return doBooleanCommand("P2P_LISTEN");
1180    }
1181
1182    public boolean p2pListen(int timeout) {
1183        if (timeout <= 0) {
1184            return p2pListen();
1185        }
1186        return doBooleanCommand("P2P_LISTEN " + timeout);
1187    }
1188
1189    public boolean p2pExtListen(boolean enable, int period, int interval) {
1190        if (enable && interval < period) {
1191            return false;
1192        }
1193        return doBooleanCommand("P2P_EXT_LISTEN"
1194                    + (enable ? (" " + period + " " + interval) : ""));
1195    }
1196
1197    public boolean p2pSetChannel(int lc, int oc) {
1198        if (DBG) Log.d(mTAG, "p2pSetChannel: lc="+lc+", oc="+oc);
1199
1200        synchronized (sLock) {
1201            if (lc >=1 && lc <= 11) {
1202                if (!doBooleanCommand("P2P_SET listen_channel " + lc)) {
1203                    return false;
1204                }
1205            } else if (lc != 0) {
1206                return false;
1207            }
1208
1209            if (oc >= 1 && oc <= 165 ) {
1210                int freq = (oc <= 14 ? 2407 : 5000) + oc * 5;
1211                return doBooleanCommand("P2P_SET disallow_freq 1000-"
1212                        + (freq - 5) + "," + (freq + 5) + "-6000");
1213            } else if (oc == 0) {
1214                /* oc==0 disables "P2P_SET disallow_freq" (enables all freqs) */
1215                return doBooleanCommand("P2P_SET disallow_freq \"\"");
1216            }
1217        }
1218        return false;
1219    }
1220
1221    public boolean p2pFlush() {
1222        return doBooleanCommand("P2P_FLUSH");
1223    }
1224
1225    private static final int DEFAULT_GROUP_OWNER_INTENT     = 6;
1226    /* p2p_connect <peer device address> <pbc|pin|PIN#> [label|display|keypad]
1227        [persistent] [join|auth] [go_intent=<0..15>] [freq=<in MHz>] */
1228    public String p2pConnect(WifiP2pConfig config, boolean joinExistingGroup) {
1229        if (config == null) return null;
1230        List<String> args = new ArrayList<>();
1231        WpsInfo wps = config.wps;
1232        args.add(config.deviceAddress);
1233
1234        switch (wps.setup) {
1235            case WpsInfo.PBC:
1236                args.add("pbc");
1237                break;
1238            case WpsInfo.DISPLAY:
1239                if (TextUtils.isEmpty(wps.pin)) {
1240                    args.add("pin");
1241                } else {
1242                    args.add(wps.pin);
1243                }
1244                args.add("display");
1245                break;
1246            case WpsInfo.KEYPAD:
1247                args.add(wps.pin);
1248                args.add("keypad");
1249                break;
1250            case WpsInfo.LABEL:
1251                args.add(wps.pin);
1252                args.add("label");
1253            default:
1254                break;
1255        }
1256
1257        if (config.netId == WifiP2pGroup.PERSISTENT_NET_ID) {
1258            args.add("persistent");
1259        }
1260
1261        if (joinExistingGroup) {
1262            args.add("join");
1263        } else {
1264            //TODO: This can be adapted based on device plugged in state and
1265            //device battery state
1266            int groupOwnerIntent = config.groupOwnerIntent;
1267            if (groupOwnerIntent < 0 || groupOwnerIntent > 15) {
1268                groupOwnerIntent = DEFAULT_GROUP_OWNER_INTENT;
1269            }
1270            args.add("go_intent=" + groupOwnerIntent);
1271        }
1272
1273        String command = "P2P_CONNECT ";
1274        for (String s : args) command += s + " ";
1275
1276        return doStringCommand(command);
1277    }
1278
1279    public boolean p2pCancelConnect() {
1280        return doBooleanCommand("P2P_CANCEL");
1281    }
1282
1283    public boolean p2pProvisionDiscovery(WifiP2pConfig config) {
1284        if (config == null) return false;
1285
1286        switch (config.wps.setup) {
1287            case WpsInfo.PBC:
1288                return doBooleanCommand("P2P_PROV_DISC " + config.deviceAddress + " pbc");
1289            case WpsInfo.DISPLAY:
1290                //We are doing display, so provision discovery is keypad
1291                return doBooleanCommand("P2P_PROV_DISC " + config.deviceAddress + " keypad");
1292            case WpsInfo.KEYPAD:
1293                //We are doing keypad, so provision discovery is display
1294                return doBooleanCommand("P2P_PROV_DISC " + config.deviceAddress + " display");
1295            default:
1296                break;
1297        }
1298        return false;
1299    }
1300
1301    public boolean p2pGroupAdd(boolean persistent) {
1302        if (persistent) {
1303            return doBooleanCommand("P2P_GROUP_ADD persistent");
1304        }
1305        return doBooleanCommand("P2P_GROUP_ADD");
1306    }
1307
1308    public boolean p2pGroupAdd(int netId) {
1309        return doBooleanCommand("P2P_GROUP_ADD persistent=" + netId);
1310    }
1311
1312    public boolean p2pGroupRemove(String iface) {
1313        if (TextUtils.isEmpty(iface)) return false;
1314        synchronized (sLock) {
1315            return doBooleanCommandNative("IFNAME=" + iface + " P2P_GROUP_REMOVE " + iface);
1316        }
1317    }
1318
1319    public boolean p2pReject(String deviceAddress) {
1320        return doBooleanCommand("P2P_REJECT " + deviceAddress);
1321    }
1322
1323    /* Invite a peer to a group */
1324    public boolean p2pInvite(WifiP2pGroup group, String deviceAddress) {
1325        if (TextUtils.isEmpty(deviceAddress)) return false;
1326
1327        if (group == null) {
1328            return doBooleanCommand("P2P_INVITE peer=" + deviceAddress);
1329        } else {
1330            return doBooleanCommand("P2P_INVITE group=" + group.getInterface()
1331                    + " peer=" + deviceAddress + " go_dev_addr=" + group.getOwner().deviceAddress);
1332        }
1333    }
1334
1335    /* Reinvoke a persistent connection */
1336    public boolean p2pReinvoke(int netId, String deviceAddress) {
1337        if (TextUtils.isEmpty(deviceAddress) || netId < 0) return false;
1338
1339        return doBooleanCommand("P2P_INVITE persistent=" + netId + " peer=" + deviceAddress);
1340    }
1341
1342    public String p2pGetSsid(String deviceAddress) {
1343        return p2pGetParam(deviceAddress, "oper_ssid");
1344    }
1345
1346    public String p2pGetDeviceAddress() {
1347        Log.d(TAG, "p2pGetDeviceAddress");
1348
1349        String status = null;
1350
1351        /* Explicitly calling the API without IFNAME= prefix to take care of the devices that
1352        don't have p2p0 interface. Supplicant seems to be returning the correct address anyway. */
1353
1354        synchronized (sLock) {
1355            status = doStringCommandNative("STATUS");
1356        }
1357
1358        String result = "";
1359        if (status != null) {
1360            String[] tokens = status.split("\n");
1361            for (String token : tokens) {
1362                if (token.startsWith("p2p_device_address=")) {
1363                    String[] nameValue = token.split("=");
1364                    if (nameValue.length != 2)
1365                        break;
1366                    result = nameValue[1];
1367                }
1368            }
1369        }
1370
1371        Log.d(TAG, "p2pGetDeviceAddress returning " + result);
1372        return result;
1373    }
1374
1375    public int getGroupCapability(String deviceAddress) {
1376        int gc = 0;
1377        if (TextUtils.isEmpty(deviceAddress)) return gc;
1378        String peerInfo = p2pPeer(deviceAddress);
1379        if (TextUtils.isEmpty(peerInfo)) return gc;
1380
1381        String[] tokens = peerInfo.split("\n");
1382        for (String token : tokens) {
1383            if (token.startsWith("group_capab=")) {
1384                String[] nameValue = token.split("=");
1385                if (nameValue.length != 2) break;
1386                try {
1387                    return Integer.decode(nameValue[1]);
1388                } catch(NumberFormatException e) {
1389                    return gc;
1390                }
1391            }
1392        }
1393        return gc;
1394    }
1395
1396    public String p2pPeer(String deviceAddress) {
1397        return doStringCommand("P2P_PEER " + deviceAddress);
1398    }
1399
1400    private String p2pGetParam(String deviceAddress, String key) {
1401        if (deviceAddress == null) return null;
1402
1403        String peerInfo = p2pPeer(deviceAddress);
1404        if (peerInfo == null) return null;
1405        String[] tokens= peerInfo.split("\n");
1406
1407        key += "=";
1408        for (String token : tokens) {
1409            if (token.startsWith(key)) {
1410                String[] nameValue = token.split("=");
1411                if (nameValue.length != 2) break;
1412                return nameValue[1];
1413            }
1414        }
1415        return null;
1416    }
1417
1418    public boolean p2pServiceAdd(WifiP2pServiceInfo servInfo) {
1419        /*
1420         * P2P_SERVICE_ADD bonjour <query hexdump> <RDATA hexdump>
1421         * P2P_SERVICE_ADD upnp <version hex> <service>
1422         *
1423         * e.g)
1424         * [Bonjour]
1425         * # IP Printing over TCP (PTR) (RDATA=MyPrinter._ipp._tcp.local.)
1426         * P2P_SERVICE_ADD bonjour 045f697070c00c000c01 094d795072696e746572c027
1427         * # IP Printing over TCP (TXT) (RDATA=txtvers=1,pdl=application/postscript)
1428         * P2P_SERVICE_ADD bonjour 096d797072696e746572045f697070c00c001001
1429         *  09747874766572733d311a70646c3d6170706c69636174696f6e2f706f7374736372797074
1430         *
1431         * [UPnP]
1432         * P2P_SERVICE_ADD upnp 10 uuid:6859dede-8574-59ab-9332-123456789012
1433         * P2P_SERVICE_ADD upnp 10 uuid:6859dede-8574-59ab-9332-123456789012::upnp:rootdevice
1434         * P2P_SERVICE_ADD upnp 10 uuid:6859dede-8574-59ab-9332-123456789012::urn:schemas-upnp
1435         * -org:device:InternetGatewayDevice:1
1436         * P2P_SERVICE_ADD upnp 10 uuid:6859dede-8574-59ab-9322-123456789012::urn:schemas-upnp
1437         * -org:service:ContentDirectory:2
1438         */
1439        synchronized (sLock) {
1440            for (String s : servInfo.getSupplicantQueryList()) {
1441                String command = "P2P_SERVICE_ADD";
1442                command += (" " + s);
1443                if (!doBooleanCommand(command)) {
1444                    return false;
1445                }
1446            }
1447        }
1448        return true;
1449    }
1450
1451    public boolean p2pServiceDel(WifiP2pServiceInfo servInfo) {
1452        /*
1453         * P2P_SERVICE_DEL bonjour <query hexdump>
1454         * P2P_SERVICE_DEL upnp <version hex> <service>
1455         */
1456        synchronized (sLock) {
1457            for (String s : servInfo.getSupplicantQueryList()) {
1458                String command = "P2P_SERVICE_DEL ";
1459
1460                String[] data = s.split(" ");
1461                if (data.length < 2) {
1462                    return false;
1463                }
1464                if ("upnp".equals(data[0])) {
1465                    command += s;
1466                } else if ("bonjour".equals(data[0])) {
1467                    command += data[0];
1468                    command += (" " + data[1]);
1469                } else {
1470                    return false;
1471                }
1472                if (!doBooleanCommand(command)) {
1473                    return false;
1474                }
1475            }
1476        }
1477        return true;
1478    }
1479
1480    public boolean p2pServiceFlush() {
1481        return doBooleanCommand("P2P_SERVICE_FLUSH");
1482    }
1483
1484    public String p2pServDiscReq(String addr, String query) {
1485        String command = "P2P_SERV_DISC_REQ";
1486        command += (" " + addr);
1487        command += (" " + query);
1488
1489        return doStringCommand(command);
1490    }
1491
1492    public boolean p2pServDiscCancelReq(String id) {
1493        return doBooleanCommand("P2P_SERV_DISC_CANCEL_REQ " + id);
1494    }
1495
1496    /* Set the current mode of miracast operation.
1497     *  0 = disabled
1498     *  1 = operating as source
1499     *  2 = operating as sink
1500     */
1501    public void setMiracastMode(int mode) {
1502        // Note: optional feature on the driver. It is ok for this to fail.
1503        doBooleanCommand("DRIVER MIRACAST " + mode);
1504    }
1505
1506    /*
1507     * NFC-related calls
1508     */
1509    public String getNfcWpsConfigurationToken(int netId) {
1510        return doStringCommand("WPS_NFC_CONFIG_TOKEN WPS " + netId);
1511    }
1512
1513    public String getNfcHandoverRequest() {
1514        return doStringCommand("NFC_GET_HANDOVER_REQ NDEF P2P-CR");
1515    }
1516
1517    public String getNfcHandoverSelect() {
1518        return doStringCommand("NFC_GET_HANDOVER_SEL NDEF P2P-CR");
1519    }
1520
1521    public boolean initiatorReportNfcHandover(String selectMessage) {
1522        return doBooleanCommand("NFC_REPORT_HANDOVER INIT P2P 00 " + selectMessage);
1523    }
1524
1525    public boolean responderReportNfcHandover(String requestMessage) {
1526        return doBooleanCommand("NFC_REPORT_HANDOVER RESP P2P " + requestMessage + " 00");
1527    }
1528
1529    /** WifiSupplicantControl methods. TODO: These should use HIDL soon. */
1530    /**
1531     * Migrate all the configured networks from wpa_supplicant.
1532     *
1533     * @param configs       Map of configuration key to configuration objects corresponding to all
1534     *                      the networks.
1535     * @param networkExtras Map of extra configuration parameters stored in wpa_supplicant.conf
1536     * @return Max priority of all the configs.
1537     */
1538    public boolean migrateNetworksFromSupplicant(Map<String, WifiConfiguration> configs,
1539                                                 SparseArray<Map<String, String>> networkExtras) {
1540        if (HIDL_SUP_ENABLE) {
1541            return mSupplicantStaIfaceHal.loadNetworks(configs, networkExtras);
1542        } else {
1543            mWifiSupplicantControl.loadNetworks(configs, networkExtras);
1544            return true;
1545        }
1546    }
1547
1548    /**
1549     * Add the provided network configuration to wpa_supplicant and initiate connection to it.
1550     * This method does the following:
1551     * 1. Triggers disconnect command to wpa_supplicant (if |shouldDisconnect| is true).
1552     * 2. Remove any existing network in wpa_supplicant.
1553     * 3. Add a new network to wpa_supplicant.
1554     * 4. Save the provided configuration to wpa_supplicant.
1555     * 5. Select the new network in wpa_supplicant.
1556     * 6. Triggers reconnect command to wpa_supplicant.
1557     *
1558     * @param configuration WifiConfiguration parameters for the provided network.
1559     * @param shouldDisconnect whether to trigger a disconnection or not.
1560     * @return {@code true} if it succeeds, {@code false} otherwise
1561     */
1562    public boolean connectToNetwork(WifiConfiguration configuration, boolean shouldDisconnect) {
1563        if (HIDL_SUP_ENABLE) {
1564            return mSupplicantStaIfaceHal.connectToNetwork(configuration, shouldDisconnect);
1565        } else {
1566            return mWifiSupplicantControl.connectToNetwork(configuration, shouldDisconnect);
1567        }
1568    }
1569
1570    /**
1571     * Initiates roaming to the already configured network in wpa_supplicant. If the network
1572     * configuration provided does not match the already configured network, then this triggers
1573     * a new connection attempt (instead of roam).
1574     * 1. First check if we're attempting to connect to the same network as we currently have
1575     * configured.
1576     * 2. Set the new bssid for the network in wpa_supplicant.
1577     * 3. Triggers reassociate command to wpa_supplicant.
1578     *
1579     * @param configuration WifiConfiguration parameters for the provided network.
1580     * @return {@code true} if it succeeds, {@code false} otherwise
1581     */
1582    public boolean roamToNetwork(WifiConfiguration configuration) {
1583        if (HIDL_SUP_ENABLE) {
1584            return mSupplicantStaIfaceHal.roamToNetwork(configuration);
1585        } else {
1586            return mWifiSupplicantControl.roamToNetwork(configuration);
1587        }
1588    }
1589
1590    /**
1591     * Get the framework network ID corresponding to the provided supplicant network ID for the
1592     * network configured in wpa_supplicant.
1593     *
1594     * @param supplicantNetworkId network ID in wpa_supplicant for the network.
1595     * @return Corresponding framework network ID if found, -1 if network not found.
1596     */
1597    public int getFrameworkNetworkId(int supplicantNetworkId) {
1598        if (HIDL_SUP_ENABLE) {
1599            // In the HIDL world, wifi monitor events contain the framework network Id.
1600            return supplicantNetworkId;
1601        } else {
1602            return mWifiSupplicantControl.getFrameworkNetworkId(supplicantNetworkId);
1603        }
1604    }
1605
1606    /**
1607     * Remove all the networks.
1608     *
1609     * @return {@code true} if it succeeds, {@code false} otherwise
1610     */
1611    public boolean removeAllNetworks() {
1612        if (HIDL_SUP_ENABLE) {
1613            return mSupplicantStaIfaceHal.removeAllNetworks();
1614        } else {
1615            if (!doBooleanCommand("REMOVE_NETWORK all")) {
1616                Log.e(TAG, "Remove all networks in wpa_supplicant failed");
1617                return false;
1618            }
1619            return true;
1620        }
1621    }
1622
1623    /**
1624     * Set the BSSID for the currently configured network in wpa_supplicant.
1625     *
1626     * @return true if successful, false otherwise.
1627     */
1628    public boolean setConfiguredNetworkBSSID(String bssid) {
1629        if (HIDL_SUP_ENABLE) {
1630            return mSupplicantStaIfaceHal.setCurrentNetworkBssid(bssid);
1631        } else {
1632            return mWifiSupplicantControl.setConfiguredNetworkBSSID(bssid);
1633        }
1634    }
1635
1636    /**
1637     * Save the current configuration to wpa_supplicant.conf.
1638     */
1639    public boolean saveConfig() {
1640        return doBooleanCommand("SAVE_CONFIG");
1641    }
1642
1643    /**
1644     * Read network variables from wpa_supplicant.conf.
1645     *
1646     * @param key The parameter to be parsed.
1647     * @return Map of corresponding configKey to the value of the param requested.
1648     */
1649    public Map<String, String> readNetworkVariablesFromSupplicantFile(String key) {
1650        return mWifiSupplicantControl.readNetworkVariablesFromSupplicantFile(key);
1651    }
1652
1653    /**
1654     * Get Fast BSS Transition capability.
1655     */
1656    public boolean getSystemSupportsFastBssTransition() {
1657        return mWifiSupplicantControl.getSystemSupportsFastBssTransition();
1658    }
1659
1660    /**
1661     * Set Fast BSS Transition capability.
1662     */
1663    public void setSystemSupportsFastBssTransition(boolean supported) {
1664        mWifiSupplicantControl.setSystemSupportsFastBssTransition(supported);
1665    }
1666
1667    /**
1668     * Initiate ANQP query.
1669     *
1670     * @param bssid BSSID of the AP to be queried
1671     * @param anqpIds Set of anqp IDs.
1672     * @param hs20Subtypes Set of HS20 subtypes.
1673     * @return true on success, false otherwise.
1674     */
1675    public boolean requestAnqp(String bssid, Set<Integer> anqpIds, Set<Integer> hs20Subtypes) {
1676        if (bssid == null || ((anqpIds == null || anqpIds.isEmpty())
1677                && (hs20Subtypes == null || hs20Subtypes.isEmpty()))) {
1678            return false;
1679        }
1680        if (HIDL_SUP_ENABLE) {
1681            ArrayList<Short> anqpIdList = new ArrayList<>();
1682            for (Integer anqpId : anqpIds) {
1683                anqpIdList.add(anqpId.shortValue());
1684            }
1685            ArrayList<Integer> hs20SubtypeList = new ArrayList<>();
1686            hs20SubtypeList.addAll(hs20Subtypes);
1687            return mSupplicantStaIfaceHal.initiateAnqpQuery(bssid, anqpIdList, hs20SubtypeList);
1688        } else {
1689            String command = buildAnqpQueryCommand(bssid, anqpIds, hs20Subtypes);
1690            String result = doStringCommand(command);
1691            return result != null && result.startsWith("OK");
1692        }
1693    }
1694
1695    /**
1696     * Build a wpa_supplicant ANQP query command
1697     *
1698     * @param bssid BSSID of the AP to be queried
1699     * @param anqpIds Set of anqp IDs.
1700     * @param hs20Subtypes Set of HS20 subtypes.
1701     * @return A command string.
1702     */
1703    @VisibleForTesting
1704    public static String buildAnqpQueryCommand(String bssid, Set<Integer> anqpIds,
1705                                               Set<Integer> hs20Subtypes) {
1706
1707        boolean baseANQPElements = !anqpIds.isEmpty();
1708        StringBuilder sb = new StringBuilder();
1709        if (baseANQPElements) {
1710            sb.append("ANQP_GET ");
1711        } else {
1712            // ANQP_GET does not work for a sole hs20:8 (OSU) query
1713            sb.append("HS20_ANQP_GET ");
1714        }
1715        sb.append(bssid).append(' ');
1716        boolean first = true;
1717        for (Integer id : anqpIds) {
1718            if (first) {
1719                first = false;
1720            } else {
1721                sb.append(',');
1722            }
1723            sb.append(id);
1724        }
1725        for (Integer subType : hs20Subtypes) {
1726            if (first) {
1727                first = false;
1728            } else {
1729                sb.append(',');
1730            }
1731            if (baseANQPElements) {
1732                sb.append("hs20:");
1733            }
1734            sb.append(subType);
1735        }
1736        return sb.toString();
1737    }
1738
1739    /**
1740     * Request a passpoint icon file |filename| from the specified AP |bssid|.
1741     * @param bssid BSSID of the AP
1742     * @param fileName name of the icon file
1743     * @return true if request is sent successfully, false otherwise
1744     */
1745    public boolean requestIcon(String  bssid, String fileName) {
1746        if (HIDL_SUP_ENABLE) {
1747            return mSupplicantStaIfaceHal.initiateHs20IconQuery(bssid, fileName);
1748        } else {
1749            String result = doStringCommand("REQ_HS20_ICON " + bssid + " " + fileName);
1750            return result != null && result.startsWith("OK");
1751        }
1752    }
1753
1754    /* kernel logging support */
1755    private static native byte[] readKernelLogNative();
1756
1757    synchronized public String readKernelLog() {
1758        byte[] bytes = readKernelLogNative();
1759        if (bytes != null) {
1760            CharsetDecoder decoder = StandardCharsets.UTF_8.newDecoder();
1761            try {
1762                CharBuffer decoded = decoder.decode(ByteBuffer.wrap(bytes));
1763                return decoded.toString();
1764            } catch (CharacterCodingException cce) {
1765                return new String(bytes, StandardCharsets.ISO_8859_1);
1766            }
1767        } else {
1768            return "*** failed to read kernel log ***";
1769        }
1770    }
1771
1772    /* WIFI HAL support */
1773
1774    // HAL command ids
1775    private static int sCmdId = 1;
1776    private static int getNewCmdIdLocked() {
1777        return sCmdId++;
1778    }
1779
1780    private static final String TAG = "WifiNative-HAL";
1781    private static long sWifiHalHandle = 0;             /* used by JNI to save wifi_handle */
1782    private static long[] sWifiIfaceHandles = null;     /* used by JNI to save interface handles */
1783    public static int sWlan0Index = -1;
1784    private static MonitorThread sThread;
1785    private static final int STOP_HAL_TIMEOUT_MS = 1000;
1786
1787    private static native boolean startHalNative();
1788    private static native void stopHalNative();
1789    private static native void waitForHalEventNative();
1790
1791    private static class MonitorThread extends Thread {
1792        @Override
1793        public void run() {
1794            Log.i(TAG, "Waiting for HAL events mWifiHalHandle=" + Long.toString(sWifiHalHandle));
1795            waitForHalEventNative();
1796        }
1797    }
1798
1799    /**
1800     * Bring up the Vendor HAL and configure for STA mode or AP mode.
1801     *
1802     * @param isStaMode true to start HAL in STA mode, false to start in AP mode.
1803     */
1804    public boolean startHal(boolean isStaMode) {
1805        String debugLog = "startHal stack: ";
1806        java.lang.StackTraceElement[] elements = Thread.currentThread().getStackTrace();
1807        for (int i = 2; i < elements.length && i <= 7; i++ ) {
1808            debugLog = debugLog + " - " + elements[i].getMethodName();
1809        }
1810
1811        sLocalLog.log(debugLog);
1812
1813        synchronized (sLock) {
1814            if (startHalNative()) {
1815                int wlan0Index = queryInterfaceIndex(mInterfaceName);
1816                if (wlan0Index == -1) {
1817                    if (DBG) sLocalLog.log("Could not find interface with name: " + mInterfaceName);
1818                    return false;
1819                }
1820                sWlan0Index = wlan0Index;
1821                sThread = new MonitorThread();
1822                sThread.start();
1823                return true;
1824            } else {
1825                if (DBG) sLocalLog.log("Could not start hal");
1826                Log.e(TAG, "Could not start hal");
1827                return false;
1828            }
1829        }
1830    }
1831
1832    public void stopHal() {
1833        synchronized (sLock) {
1834            if (isHalStarted()) {
1835                stopHalNative();
1836                try {
1837                    sThread.join(STOP_HAL_TIMEOUT_MS);
1838                    Log.d(TAG, "HAL event thread stopped successfully");
1839                } catch (InterruptedException e) {
1840                    Log.e(TAG, "Could not stop HAL cleanly");
1841                }
1842                sThread = null;
1843                sWifiHalHandle = 0;
1844                sWifiIfaceHandles = null;
1845                sWlan0Index = -1;
1846            }
1847        }
1848    }
1849
1850    public boolean isHalStarted() {
1851        return (sWifiHalHandle != 0);
1852    }
1853    private static native int getInterfacesNative();
1854
1855    public int queryInterfaceIndex(String interfaceName) {
1856        synchronized (sLock) {
1857            if (isHalStarted()) {
1858                int num = getInterfacesNative();
1859                for (int i = 0; i < num; i++) {
1860                    String name = getInterfaceNameNative(i);
1861                    if (name.equals(interfaceName)) {
1862                        return i;
1863                    }
1864                }
1865            }
1866        }
1867        return -1;
1868    }
1869
1870    private static native String getInterfaceNameNative(int index);
1871    public String getInterfaceName(int index) {
1872        synchronized (sLock) {
1873            return getInterfaceNameNative(index);
1874        }
1875    }
1876
1877    // TODO: Change variable names to camel style.
1878    public static class ScanCapabilities {
1879        public int  max_scan_cache_size;
1880        public int  max_scan_buckets;
1881        public int  max_ap_cache_per_scan;
1882        public int  max_rssi_sample_size;
1883        public int  max_scan_reporting_threshold;
1884        public int  max_hotlist_bssids;
1885        public int  max_significant_wifi_change_aps;
1886        public int  max_bssid_history_entries;
1887        public int  max_number_epno_networks;
1888        public int  max_number_epno_networks_by_ssid;
1889        public int  max_number_of_white_listed_ssid;
1890    }
1891
1892    public boolean getScanCapabilities(ScanCapabilities capabilities) {
1893        synchronized (sLock) {
1894            return isHalStarted() && getScanCapabilitiesNative(sWlan0Index, capabilities);
1895        }
1896    }
1897
1898    private static native boolean getScanCapabilitiesNative(
1899            int iface, ScanCapabilities capabilities);
1900
1901    private static native boolean startScanNative(int iface, int id, ScanSettings settings);
1902    private static native boolean stopScanNative(int iface, int id);
1903    private static native WifiScanner.ScanData[] getScanResultsNative(int iface, boolean flush);
1904    private static native WifiLinkLayerStats getWifiLinkLayerStatsNative(int iface);
1905    private static native void setWifiLinkLayerStatsNative(int iface, int enable);
1906
1907    public static class ChannelSettings {
1908        public int frequency;
1909        public int dwell_time_ms;
1910        public boolean passive;
1911    }
1912
1913    public static class BucketSettings {
1914        public int bucket;
1915        public int band;
1916        public int period_ms;
1917        public int max_period_ms;
1918        public int step_count;
1919        public int report_events;
1920        public int num_channels;
1921        public ChannelSettings[] channels;
1922    }
1923
1924    /**
1925     * Network parameters for hidden networks to be scanned for.
1926     */
1927    public static class HiddenNetwork {
1928        public String ssid;
1929
1930        @Override
1931        public boolean equals(Object otherObj) {
1932            if (this == otherObj) {
1933                return true;
1934            } else if (otherObj == null || getClass() != otherObj.getClass()) {
1935                return false;
1936            }
1937            HiddenNetwork other = (HiddenNetwork) otherObj;
1938            return Objects.equals(ssid, other.ssid);
1939        }
1940    }
1941
1942    public static class ScanSettings {
1943        public int base_period_ms;
1944        public int max_ap_per_scan;
1945        public int report_threshold_percent;
1946        public int report_threshold_num_scans;
1947        public int num_buckets;
1948        /* Not used for bg scans. Only works for single scans. */
1949        public HiddenNetwork[] hiddenNetworks;
1950        public BucketSettings[] buckets;
1951    }
1952
1953    /**
1954     * Network parameters to start PNO scan.
1955     */
1956    public static class PnoNetwork {
1957        public String ssid;
1958        public byte flags;
1959        public byte auth_bit_field;
1960
1961        @Override
1962        public boolean equals(Object otherObj) {
1963            if (this == otherObj) {
1964                return true;
1965            } else if (otherObj == null || getClass() != otherObj.getClass()) {
1966                return false;
1967            }
1968            PnoNetwork other = (PnoNetwork) otherObj;
1969            return ((Objects.equals(ssid, other.ssid)) && (flags == other.flags)
1970                    && (auth_bit_field == other.auth_bit_field));
1971        }
1972    }
1973
1974    /**
1975     * Parameters to start PNO scan. This holds the list of networks which are going to used for
1976     * PNO scan.
1977     */
1978    public static class PnoSettings {
1979        public int min5GHzRssi;
1980        public int min24GHzRssi;
1981        public int initialScoreMax;
1982        public int currentConnectionBonus;
1983        public int sameNetworkBonus;
1984        public int secureBonus;
1985        public int band5GHzBonus;
1986        public boolean isConnected;
1987        public PnoNetwork[] networkList;
1988    }
1989
1990    /**
1991     * Wi-Fi channel information.
1992     */
1993    public static class WifiChannelInfo {
1994        int mPrimaryFrequency;
1995        int mCenterFrequency0;
1996        int mCenterFrequency1;
1997        int mChannelWidth;
1998        // TODO: add preamble once available in HAL.
1999    }
2000
2001    /**
2002     * Result of a signal poll.
2003     */
2004    public static class SignalPollResult {
2005        // RSSI value in dBM.
2006        public int currentRssi;
2007        //Transmission bit rate in Mbps.
2008        public int txBitrate;
2009        // Association frequency in MHz.
2010        public int associationFrequency;
2011    }
2012
2013    /**
2014     * WiFi interface transimission counters.
2015     */
2016    public static class TxPacketCounters {
2017        // Number of successfully transmitted packets.
2018        public int txSucceeded;
2019        // Number of tramsmission failures.
2020        public int txFailed;
2021    }
2022
2023    public static interface ScanEventHandler {
2024        /**
2025         * Called for each AP as it is found with the entire contents of the beacon/probe response.
2026         * Only called when WifiScanner.REPORT_EVENT_FULL_SCAN_RESULT is specified.
2027         */
2028        void onFullScanResult(ScanResult fullScanResult, int bucketsScanned);
2029        /**
2030         * Callback on an event during a gscan scan.
2031         * See WifiNative.WIFI_SCAN_* for possible values.
2032         */
2033        void onScanStatus(int event);
2034        /**
2035         * Called with the current cached scan results when gscan is paused.
2036         */
2037        void onScanPaused(WifiScanner.ScanData[] data);
2038        /**
2039         * Called with the current cached scan results when gscan is resumed.
2040         */
2041        void onScanRestarted();
2042    }
2043
2044    /**
2045     * Handler to notify the occurrence of various events during PNO scan.
2046     */
2047    public interface PnoEventHandler {
2048        /**
2049         * Callback to notify when one of the shortlisted networks is found during PNO scan.
2050         * @param results List of Scan results received.
2051         */
2052        void onPnoNetworkFound(ScanResult[] results);
2053
2054        /**
2055         * Callback to notify when the PNO scan schedule fails.
2056         */
2057        void onPnoScanFailed();
2058    }
2059
2060    /* scan status, keep these values in sync with gscan.h */
2061    public static final int WIFI_SCAN_RESULTS_AVAILABLE = 0;
2062    public static final int WIFI_SCAN_THRESHOLD_NUM_SCANS = 1;
2063    public static final int WIFI_SCAN_THRESHOLD_PERCENT = 2;
2064    public static final int WIFI_SCAN_FAILED = 3;
2065
2066    // Callback from native
2067    private static void onScanStatus(int id, int event) {
2068        ScanEventHandler handler = sScanEventHandler;
2069        if (handler != null) {
2070            handler.onScanStatus(event);
2071        }
2072    }
2073
2074    public static  WifiSsid createWifiSsid(byte[] rawSsid) {
2075        String ssidHexString = String.valueOf(HexEncoding.encode(rawSsid));
2076
2077        if (ssidHexString == null) {
2078            return null;
2079        }
2080
2081        WifiSsid wifiSsid = WifiSsid.createFromHex(ssidHexString);
2082
2083        return wifiSsid;
2084    }
2085
2086    public static String ssidConvert(byte[] rawSsid) {
2087        String ssid;
2088
2089        CharsetDecoder decoder = StandardCharsets.UTF_8.newDecoder();
2090        try {
2091            CharBuffer decoded = decoder.decode(ByteBuffer.wrap(rawSsid));
2092            ssid = decoded.toString();
2093        } catch (CharacterCodingException cce) {
2094            ssid = null;
2095        }
2096
2097        if (ssid == null) {
2098            ssid = new String(rawSsid, StandardCharsets.ISO_8859_1);
2099        }
2100
2101        return ssid;
2102    }
2103
2104    // Called from native
2105    public static boolean setSsid(byte[] rawSsid, ScanResult result) {
2106        if (rawSsid == null || rawSsid.length == 0 || result == null) {
2107            return false;
2108        }
2109
2110        result.SSID = ssidConvert(rawSsid);
2111        result.wifiSsid = createWifiSsid(rawSsid);
2112        return true;
2113    }
2114
2115    private static void populateScanResult(ScanResult result, int beaconCap, String dbg) {
2116        if (dbg == null) dbg = "";
2117
2118        InformationElementUtil.HtOperation htOperation = new InformationElementUtil.HtOperation();
2119        InformationElementUtil.VhtOperation vhtOperation =
2120                new InformationElementUtil.VhtOperation();
2121        InformationElementUtil.ExtendedCapabilities extendedCaps =
2122                new InformationElementUtil.ExtendedCapabilities();
2123
2124        ScanResult.InformationElement elements[] =
2125                InformationElementUtil.parseInformationElements(result.bytes);
2126        for (ScanResult.InformationElement ie : elements) {
2127            if(ie.id == ScanResult.InformationElement.EID_HT_OPERATION) {
2128                htOperation.from(ie);
2129            } else if(ie.id == ScanResult.InformationElement.EID_VHT_OPERATION) {
2130                vhtOperation.from(ie);
2131            } else if (ie.id == ScanResult.InformationElement.EID_EXTENDED_CAPS) {
2132                extendedCaps.from(ie);
2133            }
2134        }
2135
2136        if (extendedCaps.is80211McRTTResponder()) {
2137            result.setFlag(ScanResult.FLAG_80211mc_RESPONDER);
2138        } else {
2139            result.clearFlag(ScanResult.FLAG_80211mc_RESPONDER);
2140        }
2141
2142        //handle RTT related information
2143        if (vhtOperation.isValid()) {
2144            result.channelWidth = vhtOperation.getChannelWidth();
2145            result.centerFreq0 = vhtOperation.getCenterFreq0();
2146            result.centerFreq1 = vhtOperation.getCenterFreq1();
2147        } else {
2148            result.channelWidth = htOperation.getChannelWidth();
2149            result.centerFreq0 = htOperation.getCenterFreq0(result.frequency);
2150            result.centerFreq1  = 0;
2151        }
2152
2153        // build capabilities string
2154        BitSet beaconCapBits = new BitSet(16);
2155        for (int i = 0; i < 16; i++) {
2156            if ((beaconCap & (1 << i)) != 0) {
2157                beaconCapBits.set(i);
2158            }
2159        }
2160        InformationElementUtil.Capabilities capabilities =
2161                new InformationElementUtil.Capabilities();
2162        capabilities.from(elements, beaconCapBits);
2163        result.capabilities = capabilities.generateCapabilitiesString();
2164
2165        if(DBG) {
2166            Log.d(TAG, dbg + "SSID: " + result.SSID + " ChannelWidth is: " + result.channelWidth
2167                    + " PrimaryFreq: " + result.frequency + " mCenterfreq0: " + result.centerFreq0
2168                    + " mCenterfreq1: " + result.centerFreq1
2169                    + (extendedCaps.is80211McRTTResponder() ? "Support RTT reponder: "
2170                            : "Do not support RTT responder")
2171                    + " Capabilities: " + result.capabilities);
2172        }
2173
2174        result.informationElements = elements;
2175    }
2176
2177    // Callback from native
2178    private static void onFullScanResult(int id, ScanResult result,
2179            int bucketsScanned, int beaconCap) {
2180        if (DBG) Log.i(TAG, "Got a full scan results event, ssid = " + result.SSID);
2181
2182        ScanEventHandler handler = sScanEventHandler;
2183        if (handler != null) {
2184            populateScanResult(result, beaconCap, " onFullScanResult ");
2185            handler.onFullScanResult(result, bucketsScanned);
2186        }
2187    }
2188
2189    private static int sScanCmdId = 0;
2190    private static ScanEventHandler sScanEventHandler;
2191    private static ScanSettings sScanSettings;
2192
2193    public boolean startScan(ScanSettings settings, ScanEventHandler eventHandler) {
2194        synchronized (sLock) {
2195            if (isHalStarted()) {
2196                if (sScanCmdId != 0) {
2197                    stopScan();
2198                } else if (sScanSettings != null || sScanEventHandler != null) {
2199                /* current scan is paused; no need to stop it */
2200                }
2201
2202                sScanCmdId = getNewCmdIdLocked();
2203
2204                sScanSettings = settings;
2205                sScanEventHandler = eventHandler;
2206
2207                if (startScanNative(sWlan0Index, sScanCmdId, settings) == false) {
2208                    sScanEventHandler = null;
2209                    sScanSettings = null;
2210                    sScanCmdId = 0;
2211                    return false;
2212                }
2213
2214                return true;
2215            } else {
2216                return false;
2217            }
2218        }
2219    }
2220
2221    public void stopScan() {
2222        synchronized (sLock) {
2223            if (isHalStarted()) {
2224                if (sScanCmdId != 0) {
2225                    stopScanNative(sWlan0Index, sScanCmdId);
2226                }
2227                sScanSettings = null;
2228                sScanEventHandler = null;
2229                sScanCmdId = 0;
2230            }
2231        }
2232    }
2233
2234    public void pauseScan() {
2235        synchronized (sLock) {
2236            if (isHalStarted()) {
2237                if (sScanCmdId != 0 && sScanSettings != null && sScanEventHandler != null) {
2238                    Log.d(TAG, "Pausing scan");
2239                    WifiScanner.ScanData scanData[] = getScanResultsNative(sWlan0Index, true);
2240                    stopScanNative(sWlan0Index, sScanCmdId);
2241                    sScanCmdId = 0;
2242                    sScanEventHandler.onScanPaused(scanData);
2243                }
2244            }
2245        }
2246    }
2247
2248    public void restartScan() {
2249        synchronized (sLock) {
2250            if (isHalStarted()) {
2251                if (sScanCmdId == 0 && sScanSettings != null && sScanEventHandler != null) {
2252                    Log.d(TAG, "Restarting scan");
2253                    ScanEventHandler handler = sScanEventHandler;
2254                    ScanSettings settings = sScanSettings;
2255                    if (startScan(sScanSettings, sScanEventHandler)) {
2256                        sScanEventHandler.onScanRestarted();
2257                    } else {
2258                    /* we are still paused; don't change state */
2259                        sScanEventHandler = handler;
2260                        sScanSettings = settings;
2261                    }
2262                }
2263            }
2264        }
2265    }
2266
2267    public WifiScanner.ScanData[] getScanResults(boolean flush) {
2268        synchronized (sLock) {
2269            WifiScanner.ScanData[] sd = null;
2270            if (isHalStarted()) {
2271                sd = getScanResultsNative(sWlan0Index, flush);
2272            }
2273
2274            if (sd != null) {
2275                return sd;
2276            } else {
2277                return new WifiScanner.ScanData[0];
2278            }
2279        }
2280    }
2281
2282    public static interface HotlistEventHandler {
2283        void onHotlistApFound (ScanResult[] result);
2284        void onHotlistApLost  (ScanResult[] result);
2285    }
2286
2287    private static int sHotlistCmdId = 0;
2288    private static HotlistEventHandler sHotlistEventHandler;
2289
2290    private native static boolean setHotlistNative(int iface, int id,
2291            WifiScanner.HotlistSettings settings);
2292    private native static boolean resetHotlistNative(int iface, int id);
2293
2294    public boolean setHotlist(WifiScanner.HotlistSettings settings,
2295            HotlistEventHandler eventHandler) {
2296        synchronized (sLock) {
2297            if (isHalStarted()) {
2298                if (sHotlistCmdId != 0) {
2299                    return false;
2300                } else {
2301                    sHotlistCmdId = getNewCmdIdLocked();
2302                }
2303
2304                sHotlistEventHandler = eventHandler;
2305                if (setHotlistNative(sWlan0Index, sHotlistCmdId, settings) == false) {
2306                    sHotlistEventHandler = null;
2307                    return false;
2308                }
2309
2310                return true;
2311            } else {
2312                return false;
2313            }
2314        }
2315    }
2316
2317    public void resetHotlist() {
2318        synchronized (sLock) {
2319            if (isHalStarted()) {
2320                if (sHotlistCmdId != 0) {
2321                    resetHotlistNative(sWlan0Index, sHotlistCmdId);
2322                    sHotlistCmdId = 0;
2323                    sHotlistEventHandler = null;
2324                }
2325            }
2326        }
2327    }
2328
2329    // Callback from native
2330    private static void onHotlistApFound(int id, ScanResult[] results) {
2331        HotlistEventHandler handler = sHotlistEventHandler;
2332        if (handler != null) {
2333            handler.onHotlistApFound(results);
2334        } else {
2335            /* this can happen because of race conditions */
2336            Log.d(TAG, "Ignoring hotlist AP found event");
2337        }
2338    }
2339
2340    // Callback from native
2341    private static void onHotlistApLost(int id, ScanResult[] results) {
2342        HotlistEventHandler handler = sHotlistEventHandler;
2343        if (handler != null) {
2344            handler.onHotlistApLost(results);
2345        } else {
2346            /* this can happen because of race conditions */
2347            Log.d(TAG, "Ignoring hotlist AP lost event");
2348        }
2349    }
2350
2351    public static interface SignificantWifiChangeEventHandler {
2352        void onChangesFound(ScanResult[] result);
2353    }
2354
2355    private static SignificantWifiChangeEventHandler sSignificantWifiChangeHandler;
2356    private static int sSignificantWifiChangeCmdId;
2357
2358    private static native boolean trackSignificantWifiChangeNative(
2359            int iface, int id, WifiScanner.WifiChangeSettings settings);
2360    private static native boolean untrackSignificantWifiChangeNative(int iface, int id);
2361
2362    public boolean trackSignificantWifiChange(
2363            WifiScanner.WifiChangeSettings settings, SignificantWifiChangeEventHandler handler) {
2364        synchronized (sLock) {
2365            if (isHalStarted()) {
2366                if (sSignificantWifiChangeCmdId != 0) {
2367                    return false;
2368                } else {
2369                    sSignificantWifiChangeCmdId = getNewCmdIdLocked();
2370                }
2371
2372                sSignificantWifiChangeHandler = handler;
2373                if (trackSignificantWifiChangeNative(sWlan0Index, sSignificantWifiChangeCmdId,
2374                        settings) == false) {
2375                    sSignificantWifiChangeHandler = null;
2376                    return false;
2377                }
2378
2379                return true;
2380            } else {
2381                return false;
2382            }
2383
2384        }
2385    }
2386
2387    public void untrackSignificantWifiChange() {
2388        synchronized (sLock) {
2389            if (isHalStarted()) {
2390                if (sSignificantWifiChangeCmdId != 0) {
2391                    untrackSignificantWifiChangeNative(sWlan0Index, sSignificantWifiChangeCmdId);
2392                    sSignificantWifiChangeCmdId = 0;
2393                    sSignificantWifiChangeHandler = null;
2394                }
2395            }
2396        }
2397    }
2398
2399    // Callback from native
2400    private static void onSignificantWifiChange(int id, ScanResult[] results) {
2401        SignificantWifiChangeEventHandler handler = sSignificantWifiChangeHandler;
2402        if (handler != null) {
2403            handler.onChangesFound(results);
2404        } else {
2405            /* this can happen because of race conditions */
2406            Log.d(TAG, "Ignoring significant wifi change");
2407        }
2408    }
2409
2410    public WifiLinkLayerStats getWifiLinkLayerStats(String iface) {
2411        // TODO: use correct iface name to Index translation
2412        if (iface == null) return null;
2413        synchronized (sLock) {
2414            if (isHalStarted()) {
2415                return getWifiLinkLayerStatsNative(sWlan0Index);
2416            } else {
2417                return null;
2418            }
2419        }
2420    }
2421
2422    public void setWifiLinkLayerStats(String iface, int enable) {
2423        if (iface == null) return;
2424        synchronized (sLock) {
2425            if (isHalStarted()) {
2426                setWifiLinkLayerStatsNative(sWlan0Index, enable);
2427            }
2428        }
2429    }
2430
2431    public static native int getSupportedFeatureSetNative(int iface);
2432    public int getSupportedFeatureSet() {
2433        synchronized (sLock) {
2434            if (isHalStarted()) {
2435                return getSupportedFeatureSetNative(sWlan0Index);
2436            } else {
2437                Log.d(TAG, "Failing getSupportedFeatureset because HAL isn't started");
2438                return 0;
2439            }
2440        }
2441    }
2442
2443    /* Rtt related commands/events */
2444    public static interface RttEventHandler {
2445        void onRttResults(RttManager.RttResult[] result);
2446    }
2447
2448    private static RttEventHandler sRttEventHandler;
2449    private static int sRttCmdId;
2450
2451    // Callback from native
2452    private static void onRttResults(int id, RttManager.RttResult[] results) {
2453        RttEventHandler handler = sRttEventHandler;
2454        if (handler != null && id == sRttCmdId) {
2455            Log.d(TAG, "Received " + results.length + " rtt results");
2456            handler.onRttResults(results);
2457            sRttCmdId = 0;
2458        } else {
2459            Log.d(TAG, "RTT Received event for unknown cmd = " + id +
2460                    ", current id = " + sRttCmdId);
2461        }
2462    }
2463
2464    private static native boolean requestRangeNative(
2465            int iface, int id, RttManager.RttParams[] params);
2466    private static native boolean cancelRangeRequestNative(
2467            int iface, int id, RttManager.RttParams[] params);
2468
2469    public boolean requestRtt(
2470            RttManager.RttParams[] params, RttEventHandler handler) {
2471        synchronized (sLock) {
2472            if (isHalStarted()) {
2473                if (sRttCmdId != 0) {
2474                    Log.w(TAG, "Last one is still under measurement!");
2475                    return false;
2476                } else {
2477                    sRttCmdId = getNewCmdIdLocked();
2478                }
2479                sRttEventHandler = handler;
2480                return requestRangeNative(sWlan0Index, sRttCmdId, params);
2481            } else {
2482                return false;
2483            }
2484        }
2485    }
2486
2487    public boolean cancelRtt(RttManager.RttParams[] params) {
2488        synchronized (sLock) {
2489            if (isHalStarted()) {
2490                if (sRttCmdId == 0) {
2491                    return false;
2492                }
2493
2494                sRttCmdId = 0;
2495
2496                if (cancelRangeRequestNative(sWlan0Index, sRttCmdId, params)) {
2497                    sRttEventHandler = null;
2498                    return true;
2499                } else {
2500                    Log.e(TAG, "RTT cancel Request failed");
2501                    return false;
2502                }
2503            } else {
2504                return false;
2505            }
2506        }
2507    }
2508
2509    private static int sRttResponderCmdId = 0;
2510
2511    private static native ResponderConfig enableRttResponderNative(int iface, int commandId,
2512            int timeoutSeconds, WifiChannelInfo channelHint);
2513    /**
2514     * Enable RTT responder role on the device. Returns {@link ResponderConfig} if the responder
2515     * role is successfully enabled, {@code null} otherwise.
2516     */
2517    @Nullable
2518    public ResponderConfig enableRttResponder(int timeoutSeconds) {
2519        synchronized (sLock) {
2520            if (!isHalStarted()) return null;
2521            if (sRttResponderCmdId != 0) {
2522                if (DBG) Log.e(mTAG, "responder mode already enabled - this shouldn't happen");
2523                return null;
2524            }
2525            int id = getNewCmdIdLocked();
2526            ResponderConfig config = enableRttResponderNative(
2527                    sWlan0Index, id, timeoutSeconds, null);
2528            if (config != null) sRttResponderCmdId = id;
2529            if (DBG) Log.d(TAG, "enabling rtt " + (config != null));
2530            return config;
2531        }
2532    }
2533
2534    private static native boolean disableRttResponderNative(int iface, int commandId);
2535    /**
2536     * Disable RTT responder role. Returns {@code true} if responder role is successfully disabled,
2537     * {@code false} otherwise.
2538     */
2539    public boolean disableRttResponder() {
2540        synchronized (sLock) {
2541            if (!isHalStarted()) return false;
2542            if (sRttResponderCmdId == 0) {
2543                Log.e(mTAG, "responder role not enabled yet");
2544                return true;
2545            }
2546            sRttResponderCmdId = 0;
2547            return disableRttResponderNative(sWlan0Index, sRttResponderCmdId);
2548        }
2549    }
2550
2551    private static native boolean setScanningMacOuiNative(int iface, byte[] oui);
2552
2553    public boolean setScanningMacOui(byte[] oui) {
2554        synchronized (sLock) {
2555            if (isHalStarted()) {
2556                return setScanningMacOuiNative(sWlan0Index, oui);
2557            } else {
2558                return false;
2559            }
2560        }
2561    }
2562
2563    private static native int[] getChannelsForBandNative(
2564            int iface, int band);
2565
2566    public int [] getChannelsForBand(int band) {
2567        synchronized (sLock) {
2568            if (isHalStarted()) {
2569                return getChannelsForBandNative(sWlan0Index, band);
2570            } else {
2571                return null;
2572            }
2573        }
2574    }
2575
2576    private static native boolean isGetChannelsForBandSupportedNative();
2577    public boolean isGetChannelsForBandSupported(){
2578        synchronized (sLock) {
2579            if (isHalStarted()) {
2580                return isGetChannelsForBandSupportedNative();
2581            } else {
2582                return false;
2583            }
2584        }
2585    }
2586
2587    private static native boolean setDfsFlagNative(int iface, boolean dfsOn);
2588    public boolean setDfsFlag(boolean dfsOn) {
2589        synchronized (sLock) {
2590            if (isHalStarted()) {
2591                return setDfsFlagNative(sWlan0Index, dfsOn);
2592            } else {
2593                return false;
2594            }
2595        }
2596    }
2597
2598    private static native RttManager.RttCapabilities getRttCapabilitiesNative(int iface);
2599    public RttManager.RttCapabilities getRttCapabilities() {
2600        synchronized (sLock) {
2601            if (isHalStarted()) {
2602                return getRttCapabilitiesNative(sWlan0Index);
2603            } else {
2604                return null;
2605            }
2606        }
2607    }
2608
2609    private static native ApfCapabilities getApfCapabilitiesNative(int iface);
2610    public ApfCapabilities getApfCapabilities() {
2611        synchronized (sLock) {
2612            if (isHalStarted()) {
2613                return getApfCapabilitiesNative(sWlan0Index);
2614            } else {
2615                return null;
2616            }
2617        }
2618    }
2619
2620    private static native boolean installPacketFilterNative(int iface, byte[] filter);
2621    public boolean installPacketFilter(byte[] filter) {
2622        synchronized (sLock) {
2623            if (isHalStarted()) {
2624                return installPacketFilterNative(sWlan0Index, filter);
2625            } else {
2626                return false;
2627            }
2628        }
2629    }
2630
2631    private static native boolean setCountryCodeHalNative(int iface, String CountryCode);
2632    public boolean setCountryCodeHal(String CountryCode) {
2633        synchronized (sLock) {
2634            if (isHalStarted()) {
2635                return setCountryCodeHalNative(sWlan0Index, CountryCode);
2636            } else {
2637                return false;
2638            }
2639        }
2640    }
2641
2642    /* Rtt related commands/events */
2643    public abstract class TdlsEventHandler {
2644        abstract public void onTdlsStatus(String macAddr, int status, int reason);
2645    }
2646
2647    private static TdlsEventHandler sTdlsEventHandler;
2648
2649    private static native boolean enableDisableTdlsNative(int iface, boolean enable,
2650            String macAddr);
2651    public boolean enableDisableTdls(boolean enable, String macAdd, TdlsEventHandler tdlsCallBack) {
2652        synchronized (sLock) {
2653            sTdlsEventHandler = tdlsCallBack;
2654            return enableDisableTdlsNative(sWlan0Index, enable, macAdd);
2655        }
2656    }
2657
2658    // Once TDLS per mac and event feature is implemented, this class definition should be
2659    // moved to the right place, like WifiManager etc
2660    public static class TdlsStatus {
2661        int channel;
2662        int global_operating_class;
2663        int state;
2664        int reason;
2665    }
2666    private static native TdlsStatus getTdlsStatusNative(int iface, String macAddr);
2667    public TdlsStatus getTdlsStatus(String macAdd) {
2668        synchronized (sLock) {
2669            if (isHalStarted()) {
2670                return getTdlsStatusNative(sWlan0Index, macAdd);
2671            } else {
2672                return null;
2673            }
2674        }
2675    }
2676
2677    //ToFix: Once TDLS per mac and event feature is implemented, this class definition should be
2678    // moved to the right place, like WifiStateMachine etc
2679    public static class TdlsCapabilities {
2680        /* Maximum TDLS session number can be supported by the Firmware and hardware */
2681        int maxConcurrentTdlsSessionNumber;
2682        boolean isGlobalTdlsSupported;
2683        boolean isPerMacTdlsSupported;
2684        boolean isOffChannelTdlsSupported;
2685    }
2686
2687
2688
2689    private static native TdlsCapabilities getTdlsCapabilitiesNative(int iface);
2690    public TdlsCapabilities getTdlsCapabilities () {
2691        synchronized (sLock) {
2692            if (isHalStarted()) {
2693                return getTdlsCapabilitiesNative(sWlan0Index);
2694            } else {
2695                return null;
2696            }
2697        }
2698    }
2699
2700    private static boolean onTdlsStatus(String macAddr, int status, int reason) {
2701        TdlsEventHandler handler = sTdlsEventHandler;
2702        if (handler == null) {
2703            return false;
2704        } else {
2705            handler.onTdlsStatus(macAddr, status, reason);
2706            return true;
2707        }
2708    }
2709
2710    //---------------------------------------------------------------------------------
2711
2712    /* Wifi Logger commands/events */
2713
2714    public static interface WifiLoggerEventHandler {
2715        void onRingBufferData(RingBufferStatus status, byte[] buffer);
2716        void onWifiAlert(int errorCode, byte[] buffer);
2717    }
2718
2719    private static WifiLoggerEventHandler sWifiLoggerEventHandler = null;
2720
2721    // Callback from native
2722    private static void onRingBufferData(RingBufferStatus status, byte[] buffer) {
2723        WifiLoggerEventHandler handler = sWifiLoggerEventHandler;
2724        if (handler != null)
2725            handler.onRingBufferData(status, buffer);
2726    }
2727
2728    // Callback from native
2729    private static void onWifiAlert(byte[] buffer, int errorCode) {
2730        WifiLoggerEventHandler handler = sWifiLoggerEventHandler;
2731        if (handler != null)
2732            handler.onWifiAlert(errorCode, buffer);
2733    }
2734
2735    private static int sLogCmdId = -1;
2736    private static native boolean setLoggingEventHandlerNative(int iface, int id);
2737    public boolean setLoggingEventHandler(WifiLoggerEventHandler handler) {
2738        synchronized (sLock) {
2739            if (isHalStarted()) {
2740                int oldId =  sLogCmdId;
2741                sLogCmdId = getNewCmdIdLocked();
2742                if (!setLoggingEventHandlerNative(sWlan0Index, sLogCmdId)) {
2743                    sLogCmdId = oldId;
2744                    return false;
2745                }
2746                sWifiLoggerEventHandler = handler;
2747                return true;
2748            } else {
2749                return false;
2750            }
2751        }
2752    }
2753
2754    private static native boolean startLoggingRingBufferNative(int iface, int verboseLevel,
2755            int flags, int minIntervalSec ,int minDataSize, String ringName);
2756    public boolean startLoggingRingBuffer(int verboseLevel, int flags, int maxInterval,
2757            int minDataSize, String ringName){
2758        synchronized (sLock) {
2759            if (isHalStarted()) {
2760                return startLoggingRingBufferNative(sWlan0Index, verboseLevel, flags, maxInterval,
2761                        minDataSize, ringName);
2762            } else {
2763                return false;
2764            }
2765        }
2766    }
2767
2768    private static native int getSupportedLoggerFeatureSetNative(int iface);
2769    public int getSupportedLoggerFeatureSet() {
2770        synchronized (sLock) {
2771            if (isHalStarted()) {
2772                return getSupportedLoggerFeatureSetNative(sWlan0Index);
2773            } else {
2774                return 0;
2775            }
2776        }
2777    }
2778
2779    private static native boolean resetLogHandlerNative(int iface, int id);
2780    public boolean resetLogHandler() {
2781        synchronized (sLock) {
2782            if (isHalStarted()) {
2783                if (sLogCmdId == -1) {
2784                    Log.e(TAG,"Can not reset handler Before set any handler");
2785                    return false;
2786                }
2787                sWifiLoggerEventHandler = null;
2788                if (resetLogHandlerNative(sWlan0Index, sLogCmdId)) {
2789                    sLogCmdId = -1;
2790                    return true;
2791                } else {
2792                    return false;
2793                }
2794            } else {
2795                return false;
2796            }
2797        }
2798    }
2799
2800    private static native String getDriverVersionNative(int iface);
2801    public String getDriverVersion() {
2802        synchronized (sLock) {
2803            if (isHalStarted()) {
2804                return getDriverVersionNative(sWlan0Index);
2805            } else {
2806                return "";
2807            }
2808        }
2809    }
2810
2811
2812    private static native String getFirmwareVersionNative(int iface);
2813    public String getFirmwareVersion() {
2814        synchronized (sLock) {
2815            if (isHalStarted()) {
2816                return getFirmwareVersionNative(sWlan0Index);
2817            } else {
2818                return "";
2819            }
2820        }
2821    }
2822
2823    public static class RingBufferStatus{
2824        String name;
2825        int flag;
2826        int ringBufferId;
2827        int ringBufferByteSize;
2828        int verboseLevel;
2829        int writtenBytes;
2830        int readBytes;
2831        int writtenRecords;
2832
2833        // Bit masks for interpreting |flag|
2834        public static final int HAS_BINARY_ENTRIES = (1 << 0);
2835        public static final int HAS_ASCII_ENTRIES = (1 << 1);
2836        public static final int HAS_PER_PACKET_ENTRIES = (1 << 2);
2837
2838        @Override
2839        public String toString() {
2840            return "name: " + name + " flag: " + flag + " ringBufferId: " + ringBufferId +
2841                    " ringBufferByteSize: " +ringBufferByteSize + " verboseLevel: " +verboseLevel +
2842                    " writtenBytes: " + writtenBytes + " readBytes: " + readBytes +
2843                    " writtenRecords: " + writtenRecords;
2844        }
2845    }
2846
2847    private static native RingBufferStatus[] getRingBufferStatusNative(int iface);
2848    public RingBufferStatus[] getRingBufferStatus() {
2849        synchronized (sLock) {
2850            if (isHalStarted()) {
2851                return getRingBufferStatusNative(sWlan0Index);
2852            } else {
2853                return null;
2854            }
2855        }
2856    }
2857
2858    private static native boolean getRingBufferDataNative(int iface, String ringName);
2859    public boolean getRingBufferData(String ringName) {
2860        synchronized (sLock) {
2861            if (isHalStarted()) {
2862                return getRingBufferDataNative(sWlan0Index, ringName);
2863            } else {
2864                return false;
2865            }
2866        }
2867    }
2868
2869    private static byte[] mFwMemoryDump;
2870    // Callback from native
2871    private static void onWifiFwMemoryAvailable(byte[] buffer) {
2872        mFwMemoryDump = buffer;
2873        if (DBG) {
2874            Log.d(TAG, "onWifiFwMemoryAvailable is called and buffer length is: " +
2875                    (buffer == null ? 0 :  buffer.length));
2876        }
2877    }
2878
2879    private static native boolean getFwMemoryDumpNative(int iface);
2880    public byte[] getFwMemoryDump() {
2881        synchronized (sLock) {
2882            if (isHalStarted()) {
2883                if(getFwMemoryDumpNative(sWlan0Index)) {
2884                    byte[] fwMemoryDump = mFwMemoryDump;
2885                    mFwMemoryDump = null;
2886                    return fwMemoryDump;
2887                } else {
2888                    return null;
2889                }
2890            }
2891            return null;
2892        }
2893    }
2894
2895    private static native byte[] getDriverStateDumpNative(int iface);
2896    /** Fetch the driver state, for driver debugging. */
2897    public byte[] getDriverStateDump() {
2898        synchronized (sLock) {
2899            if (isHalStarted()) {
2900                return getDriverStateDumpNative(sWlan0Index);
2901            } else {
2902                return null;
2903            }
2904        }
2905    }
2906
2907    //---------------------------------------------------------------------------------
2908    /* Packet fate API */
2909
2910    @Immutable
2911    abstract static class FateReport {
2912        final static int USEC_PER_MSEC = 1000;
2913        // The driver timestamp is a 32-bit counter, in microseconds. This field holds the
2914        // maximal value of a driver timestamp in milliseconds.
2915        final static int MAX_DRIVER_TIMESTAMP_MSEC = (int) (0xffffffffL / 1000);
2916        final static SimpleDateFormat dateFormatter = new SimpleDateFormat("HH:mm:ss.SSS");
2917
2918        final byte mFate;
2919        final long mDriverTimestampUSec;
2920        final byte mFrameType;
2921        final byte[] mFrameBytes;
2922        final long mEstimatedWallclockMSec;
2923
2924        FateReport(byte fate, long driverTimestampUSec, byte frameType, byte[] frameBytes) {
2925            mFate = fate;
2926            mDriverTimestampUSec = driverTimestampUSec;
2927            mEstimatedWallclockMSec =
2928                    convertDriverTimestampUSecToWallclockMSec(mDriverTimestampUSec);
2929            mFrameType = frameType;
2930            mFrameBytes = frameBytes;
2931        }
2932
2933        public String toTableRowString() {
2934            StringWriter sw = new StringWriter();
2935            PrintWriter pw = new PrintWriter(sw);
2936            FrameParser parser = new FrameParser(mFrameType, mFrameBytes);
2937            dateFormatter.setTimeZone(TimeZone.getDefault());
2938            pw.format("%-15s  %12s  %-9s  %-32s  %-12s  %-23s  %s\n",
2939                    mDriverTimestampUSec,
2940                    dateFormatter.format(new Date(mEstimatedWallclockMSec)),
2941                    directionToString(), fateToString(), parser.mMostSpecificProtocolString,
2942                    parser.mTypeString, parser.mResultString);
2943            return sw.toString();
2944        }
2945
2946        public String toVerboseStringWithPiiAllowed() {
2947            StringWriter sw = new StringWriter();
2948            PrintWriter pw = new PrintWriter(sw);
2949            FrameParser parser = new FrameParser(mFrameType, mFrameBytes);
2950            pw.format("Frame direction: %s\n", directionToString());
2951            pw.format("Frame timestamp: %d\n", mDriverTimestampUSec);
2952            pw.format("Frame fate: %s\n", fateToString());
2953            pw.format("Frame type: %s\n", frameTypeToString(mFrameType));
2954            pw.format("Frame protocol: %s\n", parser.mMostSpecificProtocolString);
2955            pw.format("Frame protocol type: %s\n", parser.mTypeString);
2956            pw.format("Frame length: %d\n", mFrameBytes.length);
2957            pw.append("Frame bytes");
2958            pw.append(HexDump.dumpHexString(mFrameBytes));  // potentially contains PII
2959            pw.append("\n");
2960            return sw.toString();
2961        }
2962
2963        /* Returns a header to match the output of toTableRowString(). */
2964        public static String getTableHeader() {
2965            StringWriter sw = new StringWriter();
2966            PrintWriter pw = new PrintWriter(sw);
2967            pw.format("\n%-15s  %-12s  %-9s  %-32s  %-12s  %-23s  %s\n",
2968                    "Time usec", "Walltime", "Direction", "Fate", "Protocol", "Type", "Result");
2969            pw.format("%-15s  %-12s  %-9s  %-32s  %-12s  %-23s  %s\n",
2970                    "---------", "--------", "---------", "----", "--------", "----", "------");
2971            return sw.toString();
2972        }
2973
2974        protected abstract String directionToString();
2975
2976        protected abstract String fateToString();
2977
2978        private static String frameTypeToString(byte frameType) {
2979            switch (frameType) {
2980                case WifiLoggerHal.FRAME_TYPE_UNKNOWN:
2981                    return "unknown";
2982                case WifiLoggerHal.FRAME_TYPE_ETHERNET_II:
2983                    return "data";
2984                case WifiLoggerHal.FRAME_TYPE_80211_MGMT:
2985                    return "802.11 management";
2986                default:
2987                    return Byte.toString(frameType);
2988            }
2989        }
2990
2991        /**
2992         * Converts a driver timestamp to a wallclock time, based on the current
2993         * BOOTTIME to wallclock mapping. The driver timestamp is a 32-bit counter of
2994         * microseconds, with the same base as BOOTTIME.
2995         */
2996        private static long convertDriverTimestampUSecToWallclockMSec(long driverTimestampUSec) {
2997            final long wallclockMillisNow = System.currentTimeMillis();
2998            final long boottimeMillisNow = SystemClock.elapsedRealtime();
2999            final long driverTimestampMillis = driverTimestampUSec / USEC_PER_MSEC;
3000
3001            long boottimeTimestampMillis = boottimeMillisNow % MAX_DRIVER_TIMESTAMP_MSEC;
3002            if (boottimeTimestampMillis < driverTimestampMillis) {
3003                // The 32-bit microsecond count has wrapped between the time that the driver
3004                // recorded the packet, and the call to this function. Adjust the BOOTTIME
3005                // timestamp, to compensate.
3006                //
3007                // Note that overflow is not a concern here, since the result is less than
3008                // 2 * MAX_DRIVER_TIMESTAMP_MSEC. (Given the modulus operation above,
3009                // boottimeTimestampMillis must be less than MAX_DRIVER_TIMESTAMP_MSEC.) And, since
3010                // MAX_DRIVER_TIMESTAMP_MSEC is an int, 2 * MAX_DRIVER_TIMESTAMP_MSEC must fit
3011                // within a long.
3012                boottimeTimestampMillis += MAX_DRIVER_TIMESTAMP_MSEC;
3013            }
3014
3015            final long millisSincePacketTimestamp = boottimeTimestampMillis - driverTimestampMillis;
3016            return wallclockMillisNow - millisSincePacketTimestamp;
3017        }
3018    }
3019
3020    /**
3021     * Represents the fate information for one outbound packet.
3022     */
3023    @Immutable
3024    public static final class TxFateReport extends FateReport {
3025        TxFateReport(byte fate, long driverTimestampUSec, byte frameType, byte[] frameBytes) {
3026            super(fate, driverTimestampUSec, frameType, frameBytes);
3027        }
3028
3029        @Override
3030        protected String directionToString() {
3031            return "TX";
3032        }
3033
3034        @Override
3035        protected String fateToString() {
3036            switch (mFate) {
3037                case WifiLoggerHal.TX_PKT_FATE_ACKED:
3038                    return "acked";
3039                case WifiLoggerHal.TX_PKT_FATE_SENT:
3040                    return "sent";
3041                case WifiLoggerHal.TX_PKT_FATE_FW_QUEUED:
3042                    return "firmware queued";
3043                case WifiLoggerHal.TX_PKT_FATE_FW_DROP_INVALID:
3044                    return "firmware dropped (invalid frame)";
3045                case WifiLoggerHal.TX_PKT_FATE_FW_DROP_NOBUFS:
3046                    return "firmware dropped (no bufs)";
3047                case WifiLoggerHal.TX_PKT_FATE_FW_DROP_OTHER:
3048                    return "firmware dropped (other)";
3049                case WifiLoggerHal.TX_PKT_FATE_DRV_QUEUED:
3050                    return "driver queued";
3051                case WifiLoggerHal.TX_PKT_FATE_DRV_DROP_INVALID:
3052                    return "driver dropped (invalid frame)";
3053                case WifiLoggerHal.TX_PKT_FATE_DRV_DROP_NOBUFS:
3054                    return "driver dropped (no bufs)";
3055                case WifiLoggerHal.TX_PKT_FATE_DRV_DROP_OTHER:
3056                    return "driver dropped (other)";
3057                default:
3058                    return Byte.toString(mFate);
3059            }
3060        }
3061    }
3062
3063    /**
3064     * Represents the fate information for one inbound packet.
3065     */
3066    @Immutable
3067    public static final class RxFateReport extends FateReport {
3068        RxFateReport(byte fate, long driverTimestampUSec, byte frameType, byte[] frameBytes) {
3069            super(fate, driverTimestampUSec, frameType, frameBytes);
3070        }
3071
3072        @Override
3073        protected String directionToString() {
3074            return "RX";
3075        }
3076
3077        @Override
3078        protected String fateToString() {
3079            switch (mFate) {
3080                case WifiLoggerHal.RX_PKT_FATE_SUCCESS:
3081                    return "success";
3082                case WifiLoggerHal.RX_PKT_FATE_FW_QUEUED:
3083                    return "firmware queued";
3084                case WifiLoggerHal.RX_PKT_FATE_FW_DROP_FILTER:
3085                    return "firmware dropped (filter)";
3086                case WifiLoggerHal.RX_PKT_FATE_FW_DROP_INVALID:
3087                    return "firmware dropped (invalid frame)";
3088                case WifiLoggerHal.RX_PKT_FATE_FW_DROP_NOBUFS:
3089                    return "firmware dropped (no bufs)";
3090                case WifiLoggerHal.RX_PKT_FATE_FW_DROP_OTHER:
3091                    return "firmware dropped (other)";
3092                case WifiLoggerHal.RX_PKT_FATE_DRV_QUEUED:
3093                    return "driver queued";
3094                case WifiLoggerHal.RX_PKT_FATE_DRV_DROP_FILTER:
3095                    return "driver dropped (filter)";
3096                case WifiLoggerHal.RX_PKT_FATE_DRV_DROP_INVALID:
3097                    return "driver dropped (invalid frame)";
3098                case WifiLoggerHal.RX_PKT_FATE_DRV_DROP_NOBUFS:
3099                    return "driver dropped (no bufs)";
3100                case WifiLoggerHal.RX_PKT_FATE_DRV_DROP_OTHER:
3101                    return "driver dropped (other)";
3102                default:
3103                    return Byte.toString(mFate);
3104            }
3105        }
3106    }
3107
3108    private static native int startPktFateMonitoringNative(int iface);
3109    /**
3110     * Ask the HAL to enable packet fate monitoring. Fails unless HAL is started.
3111     */
3112    public boolean startPktFateMonitoring() {
3113        synchronized (sLock) {
3114            if (isHalStarted()) {
3115                return startPktFateMonitoringNative(sWlan0Index) == WIFI_SUCCESS;
3116            } else {
3117                return false;
3118            }
3119        }
3120    }
3121
3122    private static native int getTxPktFatesNative(int iface, TxFateReport[] reportBufs);
3123    /**
3124     * Fetch the most recent TX packet fates from the HAL. Fails unless HAL is started.
3125     */
3126    public boolean getTxPktFates(TxFateReport[] reportBufs) {
3127        synchronized (sLock) {
3128            if (isHalStarted()) {
3129                int res = getTxPktFatesNative(sWlan0Index, reportBufs);
3130                if (res != WIFI_SUCCESS) {
3131                    Log.e(TAG, "getTxPktFatesNative returned " + res);
3132                    return false;
3133                } else {
3134                    return true;
3135                }
3136            } else {
3137                return false;
3138            }
3139        }
3140    }
3141
3142    private static native int getRxPktFatesNative(int iface, RxFateReport[] reportBufs);
3143    /**
3144     * Fetch the most recent RX packet fates from the HAL. Fails unless HAL is started.
3145     */
3146    public boolean getRxPktFates(RxFateReport[] reportBufs) {
3147        synchronized (sLock) {
3148            if (isHalStarted()) {
3149                int res = getRxPktFatesNative(sWlan0Index, reportBufs);
3150                if (res != WIFI_SUCCESS) {
3151                    Log.e(TAG, "getRxPktFatesNative returned " + res);
3152                    return false;
3153                } else {
3154                    return true;
3155                }
3156            } else {
3157                return false;
3158            }
3159        }
3160    }
3161
3162    //---------------------------------------------------------------------------------
3163    /* Configure ePNO/PNO */
3164    private static PnoEventHandler sPnoEventHandler;
3165    private static int sPnoCmdId = 0;
3166
3167    private static native boolean setPnoListNative(int iface, int id, PnoSettings settings);
3168
3169    /**
3170     * Set the PNO settings & the network list in HAL to start PNO.
3171     * @param settings PNO settings and network list.
3172     * @param eventHandler Handler to receive notifications back during PNO scan.
3173     * @return true if success, false otherwise
3174     */
3175    public boolean setPnoList(PnoSettings settings, PnoEventHandler eventHandler) {
3176        Log.e(TAG, "setPnoList cmd " + sPnoCmdId);
3177
3178        synchronized (sLock) {
3179            if (isHalStarted()) {
3180                sPnoCmdId = getNewCmdIdLocked();
3181                sPnoEventHandler = eventHandler;
3182                if (setPnoListNative(sWlan0Index, sPnoCmdId, settings)) {
3183                    return true;
3184                }
3185            }
3186            sPnoEventHandler = null;
3187            return false;
3188        }
3189    }
3190
3191    /**
3192     * Set the PNO network list in HAL to start PNO.
3193     * @param list PNO network list.
3194     * @param eventHandler Handler to receive notifications back during PNO scan.
3195     * @return true if success, false otherwise
3196     */
3197    public boolean setPnoList(PnoNetwork[] list, PnoEventHandler eventHandler) {
3198        PnoSettings settings = new PnoSettings();
3199        settings.networkList = list;
3200        return setPnoList(settings, eventHandler);
3201    }
3202
3203    private static native boolean resetPnoListNative(int iface, int id);
3204
3205    /**
3206     * Reset the PNO settings in HAL to stop PNO.
3207     * @return true if success, false otherwise
3208     */
3209    public boolean resetPnoList() {
3210        Log.e(TAG, "resetPnoList cmd " + sPnoCmdId);
3211
3212        synchronized (sLock) {
3213            if (isHalStarted()) {
3214                sPnoCmdId = getNewCmdIdLocked();
3215                sPnoEventHandler = null;
3216                if (resetPnoListNative(sWlan0Index, sPnoCmdId)) {
3217                    return true;
3218                }
3219            }
3220            return false;
3221        }
3222    }
3223
3224    // Callback from native
3225    private static void onPnoNetworkFound(int id, ScanResult[] results, int[] beaconCaps) {
3226        if (results == null) {
3227            Log.e(TAG, "onPnoNetworkFound null results");
3228            return;
3229
3230        }
3231        Log.d(TAG, "WifiNative.onPnoNetworkFound result " + results.length);
3232
3233        PnoEventHandler handler = sPnoEventHandler;
3234        if (sPnoCmdId != 0 && handler != null) {
3235            for (int i=0; i<results.length; i++) {
3236                Log.e(TAG, "onPnoNetworkFound SSID " + results[i].SSID
3237                        + " " + results[i].level + " " + results[i].frequency);
3238
3239                populateScanResult(results[i], beaconCaps[i], "onPnoNetworkFound ");
3240                results[i].wifiSsid = WifiSsid.createFromAsciiEncoded(results[i].SSID);
3241            }
3242
3243            handler.onPnoNetworkFound(results);
3244        } else {
3245            /* this can happen because of race conditions */
3246            Log.d(TAG, "Ignoring Pno Network found event");
3247        }
3248    }
3249
3250    private native static int startSendingOffloadedPacketNative(int iface, int idx,
3251                                    byte[] srcMac, byte[] dstMac, byte[] pktData, int period);
3252
3253    public int
3254    startSendingOffloadedPacket(int slot, KeepalivePacketData keepAlivePacket, int period) {
3255        Log.d(TAG, "startSendingOffloadedPacket slot=" + slot + " period=" + period);
3256
3257        String[] macAddrStr = getMacAddress().split(":");
3258        byte[] srcMac = new byte[6];
3259        for(int i = 0; i < 6; i++) {
3260            Integer hexVal = Integer.parseInt(macAddrStr[i], 16);
3261            srcMac[i] = hexVal.byteValue();
3262        }
3263        synchronized (sLock) {
3264            if (isHalStarted()) {
3265                return startSendingOffloadedPacketNative(sWlan0Index, slot, srcMac,
3266                        keepAlivePacket.dstMac, keepAlivePacket.data, period);
3267            } else {
3268                return -1;
3269            }
3270        }
3271    }
3272
3273    private native static int stopSendingOffloadedPacketNative(int iface, int idx);
3274
3275    public int
3276    stopSendingOffloadedPacket(int slot) {
3277        Log.d(TAG, "stopSendingOffloadedPacket " + slot);
3278        synchronized (sLock) {
3279            if (isHalStarted()) {
3280                return stopSendingOffloadedPacketNative(sWlan0Index, slot);
3281            } else {
3282                return -1;
3283            }
3284        }
3285    }
3286
3287    public static interface WifiRssiEventHandler {
3288        void onRssiThresholdBreached(byte curRssi);
3289    }
3290
3291    private static WifiRssiEventHandler sWifiRssiEventHandler;
3292
3293    // Callback from native
3294    private static void onRssiThresholdBreached(int id, byte curRssi) {
3295        WifiRssiEventHandler handler = sWifiRssiEventHandler;
3296        if (handler != null) {
3297            handler.onRssiThresholdBreached(curRssi);
3298        }
3299    }
3300
3301    private native static int startRssiMonitoringNative(int iface, int id,
3302                                        byte maxRssi, byte minRssi);
3303
3304    private static int sRssiMonitorCmdId = 0;
3305
3306    public int startRssiMonitoring(byte maxRssi, byte minRssi,
3307                                                WifiRssiEventHandler rssiEventHandler) {
3308        Log.d(TAG, "startRssiMonitoring: maxRssi=" + maxRssi + " minRssi=" + minRssi);
3309        synchronized (sLock) {
3310            sWifiRssiEventHandler = rssiEventHandler;
3311            if (isHalStarted()) {
3312                if (sRssiMonitorCmdId != 0) {
3313                    stopRssiMonitoring();
3314                }
3315
3316                sRssiMonitorCmdId = getNewCmdIdLocked();
3317                Log.d(TAG, "sRssiMonitorCmdId = " + sRssiMonitorCmdId);
3318                int ret = startRssiMonitoringNative(sWlan0Index, sRssiMonitorCmdId,
3319                        maxRssi, minRssi);
3320                if (ret != 0) { // if not success
3321                    sRssiMonitorCmdId = 0;
3322                }
3323                return ret;
3324            } else {
3325                return -1;
3326            }
3327        }
3328    }
3329
3330    private native static int stopRssiMonitoringNative(int iface, int idx);
3331
3332    public int stopRssiMonitoring() {
3333        Log.d(TAG, "stopRssiMonitoring, cmdId " + sRssiMonitorCmdId);
3334        synchronized (sLock) {
3335            if (isHalStarted()) {
3336                int ret = 0;
3337                if (sRssiMonitorCmdId != 0) {
3338                    ret = stopRssiMonitoringNative(sWlan0Index, sRssiMonitorCmdId);
3339                }
3340                sRssiMonitorCmdId = 0;
3341                return ret;
3342            } else {
3343                return -1;
3344            }
3345        }
3346    }
3347
3348    private static native WifiWakeReasonAndCounts getWlanWakeReasonCountNative(int iface);
3349
3350    /**
3351     * Fetch the host wakeup reasons stats from wlan driver.
3352     * @return the |WifiWakeReasonAndCounts| object retrieved from the wlan driver.
3353     */
3354    public WifiWakeReasonAndCounts getWlanWakeReasonCount() {
3355        Log.d(TAG, "getWlanWakeReasonCount " + sWlan0Index);
3356        synchronized (sLock) {
3357            if (isHalStarted()) {
3358                return getWlanWakeReasonCountNative(sWlan0Index);
3359            } else {
3360                return null;
3361            }
3362        }
3363    }
3364
3365    private static native int configureNeighborDiscoveryOffload(int iface, boolean enabled);
3366
3367    public boolean configureNeighborDiscoveryOffload(boolean enabled) {
3368        final String logMsg =  "configureNeighborDiscoveryOffload(" + enabled + ")";
3369        Log.d(mTAG, logMsg);
3370        synchronized (sLock) {
3371            if (isHalStarted()) {
3372                final int ret = configureNeighborDiscoveryOffload(sWlan0Index, enabled);
3373                if (ret != 0) {
3374                    Log.d(mTAG, logMsg + " returned: " + ret);
3375                }
3376                return (ret == 0);
3377            }
3378        }
3379        return false;
3380    }
3381
3382    // Firmware roaming control.
3383
3384    /**
3385     * Class to retrieve firmware roaming capability parameters.
3386     */
3387    public static class RoamingCapabilities {
3388        public int  maxBlacklistSize;
3389        public int  maxWhitelistSize;
3390    }
3391
3392    /**
3393     * Query the firmware roaming capabilities.
3394     */
3395    public boolean getRoamingCapabilities(RoamingCapabilities capabilities) {
3396        Log.d(TAG, "getRoamingCapabilities ");
3397        try {
3398            if (mWifiVendorHal != null) {
3399                return mWifiVendorHal.getRoamingCapabilities(capabilities);
3400            }
3401        } catch (UnsupportedOperationException e) {
3402        }
3403
3404        return false;
3405    }
3406
3407    /**
3408     * Macros for controlling firmware roaming.
3409     */
3410    public static final int DISABLE_FIRMWARE_ROAMING = 0;
3411    public static final int ENABLE_FIRMWARE_ROAMING = 1;
3412
3413    /**
3414     * Enable/disable firmware roaming.
3415     */
3416    public int enableFirmwareRoaming(int state) {
3417        Log.d(TAG, "enableFirmwareRoaming: state =" + state);
3418        try {
3419            if (mWifiVendorHal != null) {
3420                return mWifiVendorHal.enableFirmwareRoaming(state);
3421            }
3422        } catch (UnsupportedOperationException e) {
3423        }
3424
3425        return -1;
3426    }
3427
3428    /**
3429     * Class for specifying the roaming configurations.
3430     */
3431    public static class RoamingConfig {
3432        public ArrayList<String> blacklistBssids;
3433        public ArrayList<String> whitelistSsids;
3434    }
3435
3436    /**
3437     * Set firmware roaming configurations.
3438     */
3439    public boolean configureRoaming(RoamingConfig config) {
3440        Log.d(TAG, "configureRoaming ");
3441        try {
3442            if (mWifiVendorHal != null) {
3443                return mWifiVendorHal.configureRoaming(config);
3444            }
3445        } catch (UnsupportedOperationException e) {
3446        }
3447
3448        return false;
3449    }
3450
3451    /**
3452     * Reset firmware roaming configuration.
3453     */
3454    public boolean resetRoamingConfiguration() {
3455        Log.d(TAG, "resetRoamingConfiguration ");
3456        try {
3457            if (mWifiVendorHal != null) {
3458                // Pass in an empty RoamingConfig object which translates to zero size
3459                // blacklist and whitelist to reset the firmware roaming configuration.
3460                RoamingConfig config = new RoamingConfig();
3461                return mWifiVendorHal.configureRoaming(config);
3462            }
3463        } catch (UnsupportedOperationException e) {
3464        }
3465
3466        return false;
3467    }
3468
3469
3470    StackTraceElement[] mTrace;
3471    void legacyHalWarning() {
3472        Thread cur = Thread.currentThread();
3473        mTrace = cur.getStackTrace();
3474        StackTraceElement s = mTrace[3];
3475        Log.e(TAG, "LEGACY HAL th " + cur.getId()
3476                + " line " + s.getLineNumber() + " " + s.getMethodName());
3477    }
3478}
3479