WifiNative.java revision dcd877d6c143db557884993ea437e2a432cb0ba3
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.Nullable;
20import android.app.AlarmManager;
21import android.app.PendingIntent;
22import android.content.BroadcastReceiver;
23import android.content.Context;
24import android.content.Intent;
25import android.content.IntentFilter;
26import android.net.wifi.RttManager;
27import android.net.wifi.RttManager.ResponderConfig;
28import android.net.wifi.ScanResult;
29import android.net.wifi.WifiConfiguration;
30import android.net.wifi.WifiEnterpriseConfig;
31import android.net.wifi.WifiLinkLayerStats;
32import android.net.wifi.WifiManager;
33import android.net.wifi.WifiScanner;
34import android.net.wifi.WifiSsid;
35import android.net.wifi.WifiWakeReasonAndCounts;
36import android.net.wifi.WpsInfo;
37import android.net.wifi.p2p.WifiP2pConfig;
38import android.net.wifi.p2p.WifiP2pGroup;
39import android.net.wifi.p2p.nsd.WifiP2pServiceInfo;
40import android.os.SystemClock;
41import android.os.SystemProperties;
42import android.text.TextUtils;
43import android.util.LocalLog;
44import android.util.Log;
45
46import com.android.server.connectivity.KeepalivePacketData;
47import com.android.server.wifi.hotspot2.NetworkDetail;
48import com.android.server.wifi.hotspot2.SupplicantBridge;
49import com.android.server.wifi.hotspot2.Utils;
50import com.android.server.wifi.util.InformationElementUtil;
51
52import libcore.util.HexEncoding;
53
54import org.json.JSONException;
55import org.json.JSONObject;
56
57import java.io.UnsupportedEncodingException;
58import java.net.URLDecoder;
59import java.net.URLEncoder;
60import java.nio.ByteBuffer;
61import java.nio.CharBuffer;
62import java.nio.charset.CharacterCodingException;
63import java.nio.charset.CharsetDecoder;
64import java.nio.charset.StandardCharsets;
65import java.util.ArrayList;
66import java.util.BitSet;
67import java.util.HashMap;
68import java.util.Iterator;
69import java.util.List;
70import java.util.Locale;
71import java.util.Map;
72import java.util.Set;
73
74
75/**
76 * Native calls for bring up/shut down of the supplicant daemon and for
77 * sending requests to the supplicant daemon
78 *
79 * waitForEvent() is called on the monitor thread for events. All other methods
80 * must be serialized from the framework.
81 *
82 * {@hide}
83 */
84public class WifiNative {
85    private static boolean DBG = false;
86
87    /**
88     * Hold this lock before calling supplicant or HAL methods
89     * it is required to mutually exclude access to the driver
90     */
91    public static final Object sLock = new Object();
92
93    private static final LocalLog sLocalLog = new LocalLog(16384);
94
95    public static LocalLog getLocalLog() {
96        return sLocalLog;
97    }
98
99    /* Register native functions */
100    static {
101        /* Native functions are defined in libwifi-service.so */
102        System.loadLibrary("wifi-service");
103        registerNatives();
104    }
105
106    private static native int registerNatives();
107
108    /*
109     * Singleton WifiNative instances
110     */
111    private static WifiNative wlanNativeInterface =
112            new WifiNative(SystemProperties.get("wifi.interface", "wlan0"));
113    public static WifiNative getWlanNativeInterface() {
114        return wlanNativeInterface;
115    }
116
117    //STOPSHIP: get interface name from native side
118    private static WifiNative p2pNativeInterface = new WifiNative("p2p0");
119    public static WifiNative getP2pNativeInterface() {
120        return p2pNativeInterface;
121    }
122
123
124    private final String mTAG;
125    private final String mInterfaceName;
126    private final String mInterfacePrefix;
127
128    private Context mContext = null;
129    private PnoMonitor mPnoMonitor = null;
130    public void initContext(Context context) {
131        if (mContext == null && context != null) {
132            mContext = context;
133            mPnoMonitor = new PnoMonitor();
134        }
135    }
136
137    private WifiNative(String interfaceName) {
138        mInterfaceName = interfaceName;
139        mTAG = "WifiNative-" + interfaceName;
140
141        if (!interfaceName.equals("p2p0")) {
142            mInterfacePrefix = "IFNAME=" + interfaceName + " ";
143        } else {
144            // commands for p2p0 interface don't need prefix
145            mInterfacePrefix = "";
146        }
147    }
148
149    public String getInterfaceName() {
150        return mInterfaceName;
151    }
152
153    // Note this affects logging on for all interfaces
154    void enableVerboseLogging(int verbose) {
155        if (verbose > 0) {
156            DBG = true;
157        } else {
158            DBG = false;
159        }
160    }
161
162    private void localLog(String s) {
163        if (sLocalLog != null) sLocalLog.log(mInterfaceName + ": " + s);
164    }
165
166
167
168    /*
169     * Driver and Supplicant management
170     */
171    private native static boolean loadDriverNative();
172    public boolean loadDriver() {
173        synchronized (sLock) {
174            return loadDriverNative();
175        }
176    }
177
178    private native static boolean isDriverLoadedNative();
179    public boolean isDriverLoaded() {
180        synchronized (sLock) {
181            return isDriverLoadedNative();
182        }
183    }
184
185    private native static boolean unloadDriverNative();
186    public boolean unloadDriver() {
187        synchronized (sLock) {
188            return unloadDriverNative();
189        }
190    }
191
192    private native static boolean startSupplicantNative(boolean p2pSupported);
193    public boolean startSupplicant(boolean p2pSupported) {
194        synchronized (sLock) {
195            return startSupplicantNative(p2pSupported);
196        }
197    }
198
199    /* Sends a kill signal to supplicant. To be used when we have lost connection
200       or when the supplicant is hung */
201    private native static boolean killSupplicantNative(boolean p2pSupported);
202    public boolean killSupplicant(boolean p2pSupported) {
203        synchronized (sLock) {
204            return killSupplicantNative(p2pSupported);
205        }
206    }
207
208    private native static boolean connectToSupplicantNative();
209    public boolean connectToSupplicant() {
210        synchronized (sLock) {
211            localLog(mInterfacePrefix + "connectToSupplicant");
212            return connectToSupplicantNative();
213        }
214    }
215
216    private native static void closeSupplicantConnectionNative();
217    public void closeSupplicantConnection() {
218        synchronized (sLock) {
219            localLog(mInterfacePrefix + "closeSupplicantConnection");
220            closeSupplicantConnectionNative();
221        }
222    }
223
224    /**
225     * Wait for the supplicant to send an event, returning the event string.
226     * @return the event string sent by the supplicant.
227     */
228    private native static String waitForEventNative();
229    public String waitForEvent() {
230        // No synchronization necessary .. it is implemented in WifiMonitor
231        return waitForEventNative();
232    }
233
234
235    /*
236     * Supplicant Command Primitives
237     */
238    private native boolean doBooleanCommandNative(String command);
239
240    private native int doIntCommandNative(String command);
241
242    private native String doStringCommandNative(String command);
243
244    private boolean doBooleanCommand(String command) {
245        if (DBG) Log.d(mTAG, "doBoolean: " + command);
246        synchronized (sLock) {
247            String toLog = mInterfacePrefix + command;
248            boolean result = doBooleanCommandNative(mInterfacePrefix + command);
249            localLog(toLog + " -> " + result);
250            if (DBG) Log.d(mTAG, command + ": returned " + result);
251            return result;
252        }
253    }
254
255    private boolean doBooleanCommandWithoutLogging(String command) {
256        if (DBG) Log.d(mTAG, "doBooleanCommandWithoutLogging: " + command);
257        synchronized (sLock) {
258            boolean result = doBooleanCommandNative(mInterfacePrefix + command);
259            if (DBG) Log.d(mTAG, command + ": returned " + result);
260            return result;
261        }
262    }
263
264    private int doIntCommand(String command) {
265        if (DBG) Log.d(mTAG, "doInt: " + command);
266        synchronized (sLock) {
267            String toLog = mInterfacePrefix + command;
268            int result = doIntCommandNative(mInterfacePrefix + command);
269            localLog(toLog + " -> " + result);
270            if (DBG) Log.d(mTAG, "   returned " + result);
271            return result;
272        }
273    }
274
275    private String doStringCommand(String command) {
276        if (DBG) {
277            //GET_NETWORK commands flood the logs
278            if (!command.startsWith("GET_NETWORK")) {
279                Log.d(mTAG, "doString: [" + command + "]");
280            }
281        }
282        synchronized (sLock) {
283            String toLog = mInterfacePrefix + command;
284            String result = doStringCommandNative(mInterfacePrefix + command);
285            if (result == null) {
286                if (DBG) Log.d(mTAG, "doStringCommandNative no result");
287            } else {
288                if (!command.startsWith("STATUS-")) {
289                    localLog(toLog + " -> " + result);
290                }
291                if (DBG) Log.d(mTAG, "   returned " + result.replace("\n", " "));
292            }
293            return result;
294        }
295    }
296
297    private String doStringCommandWithoutLogging(String command) {
298        if (DBG) {
299            //GET_NETWORK commands flood the logs
300            if (!command.startsWith("GET_NETWORK")) {
301                Log.d(mTAG, "doString: [" + command + "]");
302            }
303        }
304        synchronized (sLock) {
305            return doStringCommandNative(mInterfacePrefix + command);
306        }
307    }
308
309    public String doCustomSupplicantCommand(String command) {
310        return doStringCommand(command);
311    }
312
313    /*
314     * Wrappers for supplicant commands
315     */
316    public boolean ping() {
317        String pong = doStringCommand("PING");
318        return (pong != null && pong.equals("PONG"));
319    }
320
321    public void setSupplicantLogLevel(String level) {
322        doStringCommand("LOG_LEVEL " + level);
323    }
324
325    public String getFreqCapability() {
326        return doStringCommand("GET_CAPABILITY freq");
327    }
328
329    /**
330     * Create a comma separate string from integer set.
331     * @param values List of integers.
332     * @return comma separated string.
333     */
334    private static String createCSVStringFromIntegerSet(Set<Integer> values) {
335        StringBuilder list = new StringBuilder();
336        boolean first = true;
337        for (Integer value : values) {
338            if (!first) {
339                list.append(",");
340            }
341            list.append(value);
342            first = false;
343        }
344        return list.toString();
345    }
346
347    /**
348     * Start a scan using wpa_supplicant for the given frequencies.
349     * @param freqs list of frequencies to scan for, if null scan all supported channels.
350     * @param hiddenNetworkIds List of hidden networks to be scanned for.
351     */
352    public boolean scan(Set<Integer> freqs, Set<Integer> hiddenNetworkIds) {
353        String freqList = null;
354        String hiddenNetworkIdList = null;
355        if (freqs != null && freqs.size() != 0) {
356            freqList = createCSVStringFromIntegerSet(freqs);
357        }
358        if (hiddenNetworkIds != null && hiddenNetworkIds.size() != 0) {
359            hiddenNetworkIdList = createCSVStringFromIntegerSet(hiddenNetworkIds);
360        }
361        return scanWithParams(freqList, hiddenNetworkIdList);
362    }
363
364    private boolean scanWithParams(String freqList, String hiddenNetworkIdList) {
365        StringBuilder scanCommand = new StringBuilder();
366        scanCommand.append("SCAN TYPE=ONLY");
367        if (freqList != null) {
368            scanCommand.append(" freq=" + freqList);
369        }
370        if (hiddenNetworkIdList != null) {
371            scanCommand.append(" scan_id=" + hiddenNetworkIdList);
372        }
373        return doBooleanCommand(scanCommand.toString());
374    }
375
376    /* Does a graceful shutdown of supplicant. Is a common stop function for both p2p and sta.
377     *
378     * Note that underneath we use a harsh-sounding "terminate" supplicant command
379     * for a graceful stop and a mild-sounding "stop" interface
380     * to kill the process
381     */
382    public boolean stopSupplicant() {
383        return doBooleanCommand("TERMINATE");
384    }
385
386    public String listNetworks() {
387        return doStringCommand("LIST_NETWORKS");
388    }
389
390    public String listNetworks(int last_id) {
391        return doStringCommand("LIST_NETWORKS LAST_ID=" + last_id);
392    }
393
394    public int addNetwork() {
395        return doIntCommand("ADD_NETWORK");
396    }
397
398    public boolean setNetworkExtra(int netId, String name, Map<String, String> values) {
399        final String encoded;
400        try {
401            encoded = URLEncoder.encode(new JSONObject(values).toString(), "UTF-8");
402        } catch (NullPointerException e) {
403            Log.e(TAG, "Unable to serialize networkExtra: " + e.toString());
404            return false;
405        } catch (UnsupportedEncodingException e) {
406            Log.e(TAG, "Unable to serialize networkExtra: " + e.toString());
407            return false;
408        }
409        return setNetworkVariable(netId, name, "\"" + encoded + "\"");
410    }
411
412    public boolean setNetworkVariable(int netId, String name, String value) {
413        if (TextUtils.isEmpty(name) || TextUtils.isEmpty(value)) return false;
414        if (name.equals(WifiConfiguration.pskVarName)
415                || name.equals(WifiEnterpriseConfig.PASSWORD_KEY)) {
416            return doBooleanCommandWithoutLogging("SET_NETWORK " + netId + " " + name + " " + value);
417        } else {
418            return doBooleanCommand("SET_NETWORK " + netId + " " + name + " " + value);
419        }
420    }
421
422    public Map<String, String> getNetworkExtra(int netId, String name) {
423        final String wrapped = getNetworkVariable(netId, name);
424        if (wrapped == null || !wrapped.startsWith("\"") || !wrapped.endsWith("\"")) {
425            return null;
426        }
427        try {
428            final String encoded = wrapped.substring(1, wrapped.length() - 1);
429            // This method reads a JSON dictionary that was written by setNetworkExtra(). However,
430            // on devices that upgraded from Marshmallow, it may encounter a legacy value instead -
431            // an FQDN stored as a plain string. If such a value is encountered, the JSONObject
432            // constructor will thrown a JSONException and the method will return null.
433            final JSONObject json = new JSONObject(URLDecoder.decode(encoded, "UTF-8"));
434            final Map<String, String> values = new HashMap<String, String>();
435            final Iterator<?> it = json.keys();
436            while (it.hasNext()) {
437                final String key = (String) it.next();
438                final Object value = json.get(key);
439                if (value instanceof String) {
440                    values.put(key, (String) value);
441                }
442            }
443            return values;
444        } catch (UnsupportedEncodingException e) {
445            Log.e(TAG, "Unable to serialize networkExtra: " + e.toString());
446            return null;
447        } catch (JSONException e) {
448            // This is not necessarily an error. This exception will also occur if we encounter a
449            // legacy FQDN stored as a plain string. We want to return null in this case as no JSON
450            // dictionary of extras was found.
451            return null;
452        }
453    }
454
455    public String getNetworkVariable(int netId, String name) {
456        if (TextUtils.isEmpty(name)) return null;
457
458        // GET_NETWORK will likely flood the logs ...
459        return doStringCommandWithoutLogging("GET_NETWORK " + netId + " " + name);
460    }
461
462    public boolean removeNetwork(int netId) {
463        return doBooleanCommand("REMOVE_NETWORK " + netId);
464    }
465
466
467    private void logDbg(String debug) {
468        long now = SystemClock.elapsedRealtimeNanos();
469        String ts = String.format("[%,d us] ", now/1000);
470        Log.e("WifiNative: ", ts+debug+ " stack:"
471                + Thread.currentThread().getStackTrace()[2].getMethodName() +" - "
472                + Thread.currentThread().getStackTrace()[3].getMethodName() +" - "
473                + Thread.currentThread().getStackTrace()[4].getMethodName() +" - "
474                + Thread.currentThread().getStackTrace()[5].getMethodName()+" - "
475                + Thread.currentThread().getStackTrace()[6].getMethodName());
476
477    }
478
479    /**
480     * Enables a network in wpa_supplicant.
481     * @param netId - Network ID of the network to be enabled.
482     * @return true if command succeeded, false otherwise.
483     */
484    public boolean enableNetwork(int netId) {
485        if (DBG) logDbg("enableNetwork nid=" + Integer.toString(netId));
486        return doBooleanCommand("ENABLE_NETWORK " + netId);
487    }
488
489    /**
490     * Enable a network in wpa_supplicant, do not connect.
491     * @param netId - Network ID of the network to be enabled.
492     * @return true if command succeeded, false otherwise.
493     */
494    public boolean enableNetworkWithoutConnect(int netId) {
495        if (DBG) logDbg("enableNetworkWithoutConnect nid=" + Integer.toString(netId));
496        return doBooleanCommand("ENABLE_NETWORK " + netId + " " + "no-connect");
497    }
498
499    /**
500     * Disables a network in wpa_supplicant.
501     * @param netId - Network ID of the network to be disabled.
502     * @return true if command succeeded, false otherwise.
503     */
504    public boolean disableNetwork(int netId) {
505        if (DBG) logDbg("disableNetwork nid=" + Integer.toString(netId));
506        return doBooleanCommand("DISABLE_NETWORK " + netId);
507    }
508
509    /**
510     * Select a network in wpa_supplicant (Disables all others).
511     * @param netId - Network ID of the network to be selected.
512     * @return true if command succeeded, false otherwise.
513     */
514    public boolean selectNetwork(int netId) {
515        if (DBG) logDbg("selectNetwork nid=" + Integer.toString(netId));
516        return doBooleanCommand("SELECT_NETWORK " + netId);
517    }
518
519    public boolean reconnect() {
520        if (DBG) logDbg("RECONNECT ");
521        return doBooleanCommand("RECONNECT");
522    }
523
524    public boolean reassociate() {
525        if (DBG) logDbg("REASSOCIATE ");
526        return doBooleanCommand("REASSOCIATE");
527    }
528
529    public boolean disconnect() {
530        if (DBG) logDbg("DISCONNECT ");
531        return doBooleanCommand("DISCONNECT");
532    }
533
534    public String status() {
535        return status(false);
536    }
537
538    public String status(boolean noEvents) {
539        if (noEvents) {
540            return doStringCommand("STATUS-NO_EVENTS");
541        } else {
542            return doStringCommand("STATUS");
543        }
544    }
545
546    public String getMacAddress() {
547        //Macaddr = XX.XX.XX.XX.XX.XX
548        String ret = doStringCommand("DRIVER MACADDR");
549        if (!TextUtils.isEmpty(ret)) {
550            String[] tokens = ret.split(" = ");
551            if (tokens.length == 2) return tokens[1];
552        }
553        return null;
554    }
555
556
557
558    /**
559     * Format of results:
560     * =================
561     * id=1
562     * bssid=68:7f:76:d7:1a:6e
563     * freq=2412
564     * level=-44
565     * tsf=1344626243700342
566     * flags=[WPA2-PSK-CCMP][WPS][ESS]
567     * ssid=zfdy
568     * ====
569     * id=2
570     * bssid=68:5f:74:d7:1a:6f
571     * freq=5180
572     * level=-73
573     * tsf=1344626243700373
574     * flags=[WPA2-PSK-CCMP][WPS][ESS]
575     * ssid=zuby
576     * ====
577     *
578     * RANGE=ALL gets all scan results
579     * RANGE=ID- gets results from ID
580     * MASK=<N> see wpa_supplicant/src/common/wpa_ctrl.h for details
581     * 0                         0                        1                       0     2
582     *                           WPA_BSS_MASK_MESH_SCAN | WPA_BSS_MASK_DELIM    | WPA_BSS_MASK_WIFI_DISPLAY
583     * 0                         0                        0                       1     1   -> 9
584     * WPA_BSS_MASK_INTERNETW  | WPA_BSS_MASK_P2P_SCAN  | WPA_BSS_MASK_WPS_SCAN | WPA_BSS_MASK_SSID
585     * 1                         0                        0                       1     9   -> d
586     * WPA_BSS_MASK_FLAGS      | WPA_BSS_MASK_IE        | WPA_BSS_MASK_AGE      | WPA_BSS_MASK_TSF
587     * 1                         0                        0                       0     8
588     * WPA_BSS_MASK_LEVEL      | WPA_BSS_MASK_NOISE     | WPA_BSS_MASK_QUAL     | WPA_BSS_MASK_CAPABILITIES
589     * 0                         1                        1                       1     7
590     * WPA_BSS_MASK_BEACON_INT | WPA_BSS_MASK_FREQ      | WPA_BSS_MASK_BSSID    | WPA_BSS_MASK_ID
591     *
592     * WPA_BSS_MASK_INTERNETW adds ANQP info (ctrl_iface:4151-4176)
593     *
594     * ctrl_iface.c:wpa_supplicant_ctrl_iface_process:7884
595     *  wpa_supplicant_ctrl_iface_bss:4315
596     *  print_bss_info
597     */
598    private String getRawScanResults(String range) {
599        return doStringCommandWithoutLogging("BSS RANGE=" + range + " MASK=0x29d87");
600    }
601
602    private static final String BSS_IE_STR = "ie=";
603    private static final String BSS_ID_STR = "id=";
604    private static final String BSS_BSSID_STR = "bssid=";
605    private static final String BSS_FREQ_STR = "freq=";
606    private static final String BSS_LEVEL_STR = "level=";
607    private static final String BSS_TSF_STR = "tsf=";
608    private static final String BSS_FLAGS_STR = "flags=";
609    private static final String BSS_SSID_STR = "ssid=";
610    private static final String BSS_DELIMITER_STR = "====";
611    private static final String BSS_END_STR = "####";
612
613    public ArrayList<ScanDetail> getScanResults() {
614        int next_sid = 0;
615        ArrayList<ScanDetail> results = new ArrayList<>();
616        while(next_sid >= 0) {
617            String rawResult = getRawScanResults(next_sid+"-");
618            next_sid = -1;
619
620            if (TextUtils.isEmpty(rawResult))
621                break;
622
623            String[] lines = rawResult.split("\n");
624
625
626            // note that all these splits and substrings keep references to the original
627            // huge string buffer while the amount we really want is generally pretty small
628            // so make copies instead (one example b/11087956 wasted 400k of heap here).
629            final int bssidStrLen = BSS_BSSID_STR.length();
630            final int flagLen = BSS_FLAGS_STR.length();
631
632            String bssid = "";
633            int level = 0;
634            int freq = 0;
635            long tsf = 0;
636            String flags = "";
637            WifiSsid wifiSsid = null;
638            String infoElementsStr = null;
639            List<String> anqpLines = null;
640
641            for (String line : lines) {
642                if (line.startsWith(BSS_ID_STR)) { // Will find the last id line
643                    try {
644                        next_sid = Integer.parseInt(line.substring(BSS_ID_STR.length())) + 1;
645                    } catch (NumberFormatException e) {
646                        // Nothing to do
647                    }
648                } else if (line.startsWith(BSS_BSSID_STR)) {
649                    bssid = new String(line.getBytes(), bssidStrLen, line.length() - bssidStrLen);
650                } else if (line.startsWith(BSS_FREQ_STR)) {
651                    try {
652                        freq = Integer.parseInt(line.substring(BSS_FREQ_STR.length()));
653                    } catch (NumberFormatException e) {
654                        freq = 0;
655                    }
656                } else if (line.startsWith(BSS_LEVEL_STR)) {
657                    try {
658                        level = Integer.parseInt(line.substring(BSS_LEVEL_STR.length()));
659                        /* some implementations avoid negative values by adding 256
660                         * so we need to adjust for that here.
661                         */
662                        if (level > 0) level -= 256;
663                    } catch (NumberFormatException e) {
664                        level = 0;
665                    }
666                } else if (line.startsWith(BSS_TSF_STR)) {
667                    try {
668                        tsf = Long.parseLong(line.substring(BSS_TSF_STR.length()));
669                    } catch (NumberFormatException e) {
670                        tsf = 0;
671                    }
672                } else if (line.startsWith(BSS_FLAGS_STR)) {
673                    flags = new String(line.getBytes(), flagLen, line.length() - flagLen);
674                } else if (line.startsWith(BSS_SSID_STR)) {
675                    wifiSsid = WifiSsid.createFromAsciiEncoded(
676                            line.substring(BSS_SSID_STR.length()));
677                } else if (line.startsWith(BSS_IE_STR)) {
678                    infoElementsStr = line;
679                } else if (SupplicantBridge.isAnqpAttribute(line)) {
680                    if (anqpLines == null) {
681                        anqpLines = new ArrayList<>();
682                    }
683                    anqpLines.add(line);
684                } else if (line.startsWith(BSS_DELIMITER_STR) || line.startsWith(BSS_END_STR)) {
685                    if (bssid != null) {
686                        try {
687                            if (infoElementsStr == null) {
688                                throw new IllegalArgumentException("Null information element data");
689                            }
690                            int seperator = infoElementsStr.indexOf('=');
691                            if (seperator < 0) {
692                                throw new IllegalArgumentException("No element separator");
693                            }
694
695                            ScanResult.InformationElement[] infoElements =
696                                        InformationElementUtil.parseInformationElements(
697                                        Utils.hexToBytes(infoElementsStr.substring(seperator + 1)));
698
699                            NetworkDetail networkDetail = new NetworkDetail(bssid,
700                                    infoElements, anqpLines, freq);
701                            if (DBG) {
702                                Log.v(TAG + ":DTIM", "SSID" + networkDetail.getSSID()
703                                        + ", DTIM=" + networkDetail.getDtimInterval() + ", "
704                                        + " IEstr:" + infoElementsStr);
705                            }
706                            String xssid = (wifiSsid != null) ? wifiSsid.toString() : WifiSsid.NONE;
707                            if (!xssid.equals(networkDetail.getTrimmedSSID())) {
708                                Log.d(TAG, String.format(
709                                        "Inconsistent SSID on BSSID '%s': '%s' vs '%s': %s",
710                                        bssid, xssid, networkDetail.getSSID(), infoElementsStr));
711                            }
712
713                            if (networkDetail.hasInterworking()) {
714                                if (DBG) Log.d(TAG, "HSNwk: '" + networkDetail);
715                            }
716                            ScanDetail scan = new ScanDetail(networkDetail, wifiSsid, bssid, flags,
717                                    level, freq, tsf, infoElements, anqpLines);
718                            results.add(scan);
719                        } catch (IllegalArgumentException iae) {
720                            Log.d(TAG, "Failed to parse information elements: " + iae);
721                        }
722                    }
723                    bssid = null;
724                    level = 0;
725                    freq = 0;
726                    tsf = 0;
727                    flags = "";
728                    wifiSsid = null;
729                    infoElementsStr = null;
730                    anqpLines = null;
731                }
732            }
733        }
734        return results;
735    }
736
737    /**
738     * Format of result:
739     * id=1016
740     * bssid=00:03:7f:40:84:10
741     * freq=2462
742     * beacon_int=200
743     * capabilities=0x0431
744     * qual=0
745     * noise=0
746     * level=-46
747     * tsf=0000002669008476
748     * age=5
749     * ie=00105143412d485332302d52322d54455354010882848b960c12182403010b0706555...
750     * flags=[WPA2-EAP-CCMP][ESS][P2P][HS20]
751     * ssid=QCA-HS20-R2-TEST
752     * p2p_device_name=
753     * p2p_config_methods=0x0SET_NE
754     * anqp_venue_name=02083d656e6757692d466920416c6c69616e63650a3239383920436f...
755     * anqp_network_auth_type=010000
756     * anqp_roaming_consortium=03506f9a05001bc504bd
757     * anqp_ip_addr_type_availability=0c
758     * anqp_nai_realm=0200300000246d61696c2e6578616d706c652e636f6d3b636973636f2...
759     * anqp_3gpp=000600040132f465
760     * anqp_domain_name=0b65786d61706c652e636f6d
761     * hs20_operator_friendly_name=11656e6757692d466920416c6c69616e63650e636869...
762     * hs20_wan_metrics=01c40900008001000000000a00
763     * hs20_connection_capability=0100000006140001061600000650000106bb010106bb0...
764     * hs20_osu_providers_list=0b5143412d4f53552d425353010901310015656e6757692d...
765     */
766    public String scanResult(String bssid) {
767        return doStringCommand("BSS " + bssid);
768    }
769
770    public boolean startDriver() {
771        return doBooleanCommand("DRIVER START");
772    }
773
774    public boolean stopDriver() {
775        return doBooleanCommand("DRIVER STOP");
776    }
777
778
779    /**
780     * Start filtering out Multicast V4 packets
781     * @return {@code true} if the operation succeeded, {@code false} otherwise
782     *
783     * Multicast filtering rules work as follows:
784     *
785     * The driver can filter multicast (v4 and/or v6) and broadcast packets when in
786     * a power optimized mode (typically when screen goes off).
787     *
788     * In order to prevent the driver from filtering the multicast/broadcast packets, we have to
789     * add a DRIVER RXFILTER-ADD rule followed by DRIVER RXFILTER-START to make the rule effective
790     *
791     * DRIVER RXFILTER-ADD Num
792     *   where Num = 0 - Unicast, 1 - Broadcast, 2 - Mutil4 or 3 - Multi6
793     *
794     * and DRIVER RXFILTER-START
795     * In order to stop the usage of these rules, we do
796     *
797     * DRIVER RXFILTER-STOP
798     * DRIVER RXFILTER-REMOVE Num
799     *   where Num is as described for RXFILTER-ADD
800     *
801     * The  SETSUSPENDOPT driver command overrides the filtering rules
802     */
803    public boolean startFilteringMulticastV4Packets() {
804        return doBooleanCommand("DRIVER RXFILTER-STOP")
805            && doBooleanCommand("DRIVER RXFILTER-REMOVE 2")
806            && doBooleanCommand("DRIVER RXFILTER-START");
807    }
808
809    /**
810     * Stop filtering out Multicast V4 packets.
811     * @return {@code true} if the operation succeeded, {@code false} otherwise
812     */
813    public boolean stopFilteringMulticastV4Packets() {
814        return doBooleanCommand("DRIVER RXFILTER-STOP")
815            && doBooleanCommand("DRIVER RXFILTER-ADD 2")
816            && doBooleanCommand("DRIVER RXFILTER-START");
817    }
818
819    /**
820     * Start filtering out Multicast V6 packets
821     * @return {@code true} if the operation succeeded, {@code false} otherwise
822     */
823    public boolean startFilteringMulticastV6Packets() {
824        return doBooleanCommand("DRIVER RXFILTER-STOP")
825            && doBooleanCommand("DRIVER RXFILTER-REMOVE 3")
826            && doBooleanCommand("DRIVER RXFILTER-START");
827    }
828
829    /**
830     * Stop filtering out Multicast V6 packets.
831     * @return {@code true} if the operation succeeded, {@code false} otherwise
832     */
833    public boolean stopFilteringMulticastV6Packets() {
834        return doBooleanCommand("DRIVER RXFILTER-STOP")
835            && doBooleanCommand("DRIVER RXFILTER-ADD 3")
836            && doBooleanCommand("DRIVER RXFILTER-START");
837    }
838
839    /**
840     * Set the operational frequency band
841     * @param band One of
842     *     {@link WifiManager#WIFI_FREQUENCY_BAND_AUTO},
843     *     {@link WifiManager#WIFI_FREQUENCY_BAND_5GHZ},
844     *     {@link WifiManager#WIFI_FREQUENCY_BAND_2GHZ},
845     * @return {@code true} if the operation succeeded, {@code false} otherwise
846     */
847    public boolean setBand(int band) {
848        String bandstr;
849
850        if (band == WifiManager.WIFI_FREQUENCY_BAND_5GHZ)
851            bandstr = "5G";
852        else if (band == WifiManager.WIFI_FREQUENCY_BAND_2GHZ)
853            bandstr = "2G";
854        else
855            bandstr = "AUTO";
856        return doBooleanCommand("SET SETBAND " + bandstr);
857    }
858
859    public static final int BLUETOOTH_COEXISTENCE_MODE_ENABLED     = 0;
860    public static final int BLUETOOTH_COEXISTENCE_MODE_DISABLED    = 1;
861    public static final int BLUETOOTH_COEXISTENCE_MODE_SENSE       = 2;
862    /**
863      * Sets the bluetooth coexistence mode.
864      *
865      * @param mode One of {@link #BLUETOOTH_COEXISTENCE_MODE_DISABLED},
866      *            {@link #BLUETOOTH_COEXISTENCE_MODE_ENABLED}, or
867      *            {@link #BLUETOOTH_COEXISTENCE_MODE_SENSE}.
868      * @return Whether the mode was successfully set.
869      */
870    public boolean setBluetoothCoexistenceMode(int mode) {
871        return doBooleanCommand("DRIVER BTCOEXMODE " + mode);
872    }
873
874    /**
875     * Enable or disable Bluetooth coexistence scan mode. When this mode is on,
876     * some of the low-level scan parameters used by the driver are changed to
877     * reduce interference with A2DP streaming.
878     *
879     * @param isSet whether to enable or disable this mode
880     * @return {@code true} if the command succeeded, {@code false} otherwise.
881     */
882    public boolean setBluetoothCoexistenceScanMode(boolean setCoexScanMode) {
883        if (setCoexScanMode) {
884            return doBooleanCommand("DRIVER BTCOEXSCAN-START");
885        } else {
886            return doBooleanCommand("DRIVER BTCOEXSCAN-STOP");
887        }
888    }
889
890    public void enableSaveConfig() {
891        doBooleanCommand("SET update_config 1");
892    }
893
894    public boolean saveConfig() {
895        return doBooleanCommand("SAVE_CONFIG");
896    }
897
898    public boolean addToBlacklist(String bssid) {
899        if (TextUtils.isEmpty(bssid)) return false;
900        return doBooleanCommand("BLACKLIST " + bssid);
901    }
902
903    public boolean clearBlacklist() {
904        return doBooleanCommand("BLACKLIST clear");
905    }
906
907    public boolean setSuspendOptimizations(boolean enabled) {
908        if (enabled) {
909            return doBooleanCommand("DRIVER SETSUSPENDMODE 1");
910        } else {
911            return doBooleanCommand("DRIVER SETSUSPENDMODE 0");
912        }
913    }
914
915    public boolean setCountryCode(String countryCode) {
916        if (countryCode != null)
917            return doBooleanCommand("DRIVER COUNTRY " + countryCode.toUpperCase(Locale.ROOT));
918        else
919            return doBooleanCommand("DRIVER COUNTRY");
920    }
921
922    /**
923     * Start/Stop PNO scan.
924     * @param enable boolean indicating whether PNO is being enabled or disabled.
925     */
926    public boolean setPnoScan(boolean enable) {
927        String cmd = enable ? "SET pno 1" : "SET pno 0";
928        return doBooleanCommand(cmd);
929    }
930
931    // TODO(rpius): Move PnoMonitor to SupplicantWifiScannerImpl.
932    //PNO Monitor
933    private class PnoMonitor {
934        private static final int MINIMUM_PNO_GAP = 5 * 1000;
935        private static final String ACTION_TOGGLE_PNO =
936            "com.android.server.Wifi.action.TOGGLE_PNO";
937        long mLastPnoChangeTimeStamp = -1L;
938        boolean mExpectedPnoState = false;
939        List<PnoNetwork> mExpectedPnoNetworkList = null;
940        boolean mCurrentPnoState = false;;
941        boolean mWaitForTimer = false;
942        private boolean mIsPaused = false;
943        final Object mPnoLock = new Object();
944        private final AlarmManager mAlarmManager =
945                (AlarmManager) mContext.getSystemService(Context.ALARM_SERVICE);
946        private final PendingIntent mPnoIntent;
947
948        public PnoMonitor() {
949            Intent intent = new Intent(ACTION_TOGGLE_PNO, null);
950            intent.setPackage("android");
951            mPnoIntent = PendingIntent.getBroadcast(mContext, 0, intent, 0);
952
953            mContext.registerReceiver(
954                new BroadcastReceiver() {
955                    @Override
956                    public void onReceive(Context context, Intent intent) {
957                        synchronized(mPnoLock) {
958                            if (DBG) {
959                                Log.d(mTAG, "PNO timer expire, PNO should change to "
960                                        + mExpectedPnoState);
961                            }
962                            boolean ret = setPno(mExpectedPnoState, mExpectedPnoNetworkList);
963                            if (!ret) {
964                                Log.e(mTAG, "set PNO failure");
965                            }
966                            mWaitForTimer = false;
967                        }
968                    }
969                },
970                new IntentFilter(ACTION_TOGGLE_PNO));
971        }
972
973        /**
974         * Enable/Disable PNO with updated network priorities.
975         * @param enable boolean indicating whether PNO is being enabled or disabled.
976         * @param pnoNetworkList list of networks with priorities to be set before PNO setting.
977         */
978        private boolean setPno(boolean enable, List<PnoNetwork> pnoNetworkList) {
979            if (mIsPaused) {
980                if (DBG) Log.d(mTAG, "Do not change PNO state since it is paused");
981                return true;
982            }
983            if (mCurrentPnoState == enable) {
984                if (DBG) Log.d(mTAG, "Do not change PNO since current is expected");
985                return true;
986            }
987            if (DBG) {
988                Log.d(mTAG, "change PNO from " + mCurrentPnoState + " to " + enable);
989            }
990            // TODO: Couple of cases yet to be handled:
991            // 1. What if the network priority update fails, should we bail out of PNO setting?
992            // 2. If PNO setting fails below, should we go back and revert this priority change?
993            if (pnoNetworkList != null) {
994                if (DBG) Log.i(mTAG, "update priorities for PNO. Enable: " + enable);
995                for (PnoNetwork pnoNetwork : pnoNetworkList) {
996                    // What if this fails? Should we bail out?
997                    boolean isSuccess = setNetworkVariable(pnoNetwork.networkId,
998                            WifiConfiguration.priorityVarName,
999                            Integer.toString(pnoNetwork.priority));
1000                    if (!isSuccess) {
1001                        Log.e(mTAG, "update priority failed for :" + pnoNetwork.networkId);
1002                    }
1003                }
1004            }
1005            boolean ret = WifiNative.this.setPnoScan(enable);
1006            mLastPnoChangeTimeStamp = System.currentTimeMillis();
1007            if (ret) {
1008                mCurrentPnoState = enable;
1009            }
1010            return ret;
1011        }
1012
1013        public boolean enableBackgroundScan(
1014                boolean enable,
1015                List<PnoNetwork> pnoNetworkList) {
1016            synchronized(mPnoLock) {
1017                Log.i(mTAG, "set expected PNO to " +  enable);
1018                mExpectedPnoState = enable;
1019                mExpectedPnoNetworkList = pnoNetworkList;
1020                if (mWaitForTimer) {
1021                    //already has a timer
1022                    if (DBG) Log.d(mTAG, "update expected PNO to " +  mExpectedPnoState);
1023                } else {
1024                    long timeDifference = System.currentTimeMillis() - mLastPnoChangeTimeStamp;
1025                    if (timeDifference >= MINIMUM_PNO_GAP) {
1026                        return setPno(enable, pnoNetworkList);
1027                    } else {
1028                        mWaitForTimer = true;
1029                        if (DBG) Log.d(mTAG, "start PNO timer with delay:" + timeDifference);
1030                        mAlarmManager.set(AlarmManager.RTC_WAKEUP,
1031                                System.currentTimeMillis() + timeDifference, mPnoIntent);
1032                    }
1033                }
1034                return true;
1035            }
1036        }
1037
1038        /**
1039         * Pause PNO scanning.
1040         */
1041        public void pauseBackgroundScan() {
1042            synchronized (mPnoLock) {
1043                if (!mIsPaused) {
1044                    Log.i(mTAG, "Pausing Pno scan. Current state: " + mCurrentPnoState);
1045                    if (mCurrentPnoState) {
1046                        setPno(false, null);
1047                    }
1048                    mIsPaused = true;
1049                }
1050            }
1051        }
1052
1053        /**
1054         * Resume PNO scanning.
1055         */
1056        public void resumeBackgroundScan() {
1057            synchronized (mPnoLock) {
1058                if (mIsPaused) {
1059                    Log.i(mTAG, "Resuming Pno scan. Expected state: " + mExpectedPnoState);
1060                    mIsPaused = false;
1061                    if (mExpectedPnoState) {
1062                        setPno(true, null);
1063                    }
1064                }
1065            }
1066        }
1067    }
1068
1069    public boolean enableBackgroundScan(
1070            boolean enable,
1071            List<PnoNetwork> pnoNetworkList) {
1072        if (mPnoMonitor != null) {
1073            return mPnoMonitor.enableBackgroundScan(enable, pnoNetworkList);
1074        } else {
1075            return false;
1076        }
1077    }
1078
1079    /**
1080     * Pause PNO scanning.
1081     * Note: If Pno is currently running, the stop might take a little while to take effect!
1082     */
1083    public void pauseBackgroundScan() {
1084        if (mPnoMonitor != null) {
1085            mPnoMonitor.pauseBackgroundScan();
1086        }
1087    }
1088
1089    /**
1090     * Resume PNO scanning.
1091     */
1092    public void resumeBackgroundScan() {
1093        if (mPnoMonitor != null) {
1094            mPnoMonitor.resumeBackgroundScan();
1095        }
1096    }
1097
1098    public void enableAutoConnect(boolean enable) {
1099        if (enable) {
1100            doBooleanCommand("STA_AUTOCONNECT 1");
1101        } else {
1102            doBooleanCommand("STA_AUTOCONNECT 0");
1103        }
1104    }
1105
1106    public void setScanInterval(int scanInterval) {
1107        doBooleanCommand("SCAN_INTERVAL " + scanInterval);
1108    }
1109
1110    public void setHs20(boolean hs20) {
1111        if (hs20) {
1112            doBooleanCommand("SET HS20 1");
1113        } else {
1114            doBooleanCommand("SET HS20 0");
1115        }
1116    }
1117
1118    public void startTdls(String macAddr, boolean enable) {
1119        if (enable) {
1120            synchronized (sLock) {
1121                doBooleanCommand("TDLS_DISCOVER " + macAddr);
1122                doBooleanCommand("TDLS_SETUP " + macAddr);
1123            }
1124        } else {
1125            doBooleanCommand("TDLS_TEARDOWN " + macAddr);
1126        }
1127    }
1128
1129    /** Example output:
1130     * RSSI=-65
1131     * LINKSPEED=48
1132     * NOISE=9999
1133     * FREQUENCY=0
1134     */
1135    public String signalPoll() {
1136        return doStringCommandWithoutLogging("SIGNAL_POLL");
1137    }
1138
1139    /** Example outout:
1140     * TXGOOD=396
1141     * TXBAD=1
1142     */
1143    public String pktcntPoll() {
1144        return doStringCommand("PKTCNT_POLL");
1145    }
1146
1147    public void bssFlush() {
1148        doBooleanCommand("BSS_FLUSH 0");
1149    }
1150
1151    public boolean startWpsPbc(String bssid) {
1152        if (TextUtils.isEmpty(bssid)) {
1153            return doBooleanCommand("WPS_PBC");
1154        } else {
1155            return doBooleanCommand("WPS_PBC " + bssid);
1156        }
1157    }
1158
1159    public boolean startWpsPbc(String iface, String bssid) {
1160        synchronized (sLock) {
1161            if (TextUtils.isEmpty(bssid)) {
1162                return doBooleanCommandNative("IFNAME=" + iface + " WPS_PBC");
1163            } else {
1164                return doBooleanCommandNative("IFNAME=" + iface + " WPS_PBC " + bssid);
1165            }
1166        }
1167    }
1168
1169    public boolean startWpsPinKeypad(String pin) {
1170        if (TextUtils.isEmpty(pin)) return false;
1171        return doBooleanCommand("WPS_PIN any " + pin);
1172    }
1173
1174    public boolean startWpsPinKeypad(String iface, String pin) {
1175        if (TextUtils.isEmpty(pin)) return false;
1176        synchronized (sLock) {
1177            return doBooleanCommandNative("IFNAME=" + iface + " WPS_PIN any " + pin);
1178        }
1179    }
1180
1181
1182    public String startWpsPinDisplay(String bssid) {
1183        if (TextUtils.isEmpty(bssid)) {
1184            return doStringCommand("WPS_PIN any");
1185        } else {
1186            return doStringCommand("WPS_PIN " + bssid);
1187        }
1188    }
1189
1190    public String startWpsPinDisplay(String iface, String bssid) {
1191        synchronized (sLock) {
1192            if (TextUtils.isEmpty(bssid)) {
1193                return doStringCommandNative("IFNAME=" + iface + " WPS_PIN any");
1194            } else {
1195                return doStringCommandNative("IFNAME=" + iface + " WPS_PIN " + bssid);
1196            }
1197        }
1198    }
1199
1200    public boolean setExternalSim(boolean external) {
1201        String value = external ? "1" : "0";
1202        Log.d(TAG, "Setting external_sim to " + value);
1203        return doBooleanCommand("SET external_sim " + value);
1204    }
1205
1206    public boolean simAuthResponse(int id, String type, String response) {
1207        // with type = GSM-AUTH, UMTS-AUTH or UMTS-AUTS
1208        return doBooleanCommand("CTRL-RSP-SIM-" + id + ":" + type + response);
1209    }
1210
1211    public boolean simAuthFailedResponse(int id) {
1212        // should be used with type GSM-AUTH
1213        return doBooleanCommand("CTRL-RSP-SIM-" + id + ":GSM-FAIL");
1214    }
1215
1216    public boolean umtsAuthFailedResponse(int id) {
1217        // should be used with type UMTS-AUTH
1218        return doBooleanCommand("CTRL-RSP-SIM-" + id + ":UMTS-FAIL");
1219    }
1220
1221    public boolean simIdentityResponse(int id, String response) {
1222        return doBooleanCommand("CTRL-RSP-IDENTITY-" + id + ":" + response);
1223    }
1224
1225    /* Configures an access point connection */
1226    public boolean startWpsRegistrar(String bssid, String pin) {
1227        if (TextUtils.isEmpty(bssid) || TextUtils.isEmpty(pin)) return false;
1228        return doBooleanCommand("WPS_REG " + bssid + " " + pin);
1229    }
1230
1231    public boolean cancelWps() {
1232        return doBooleanCommand("WPS_CANCEL");
1233    }
1234
1235    public boolean setPersistentReconnect(boolean enabled) {
1236        int value = (enabled == true) ? 1 : 0;
1237        return doBooleanCommand("SET persistent_reconnect " + value);
1238    }
1239
1240    public boolean setDeviceName(String name) {
1241        return doBooleanCommand("SET device_name " + name);
1242    }
1243
1244    public boolean setDeviceType(String type) {
1245        return doBooleanCommand("SET device_type " + type);
1246    }
1247
1248    public boolean setConfigMethods(String cfg) {
1249        return doBooleanCommand("SET config_methods " + cfg);
1250    }
1251
1252    public boolean setManufacturer(String value) {
1253        return doBooleanCommand("SET manufacturer " + value);
1254    }
1255
1256    public boolean setModelName(String value) {
1257        return doBooleanCommand("SET model_name " + value);
1258    }
1259
1260    public boolean setModelNumber(String value) {
1261        return doBooleanCommand("SET model_number " + value);
1262    }
1263
1264    public boolean setSerialNumber(String value) {
1265        return doBooleanCommand("SET serial_number " + value);
1266    }
1267
1268    public boolean setP2pSsidPostfix(String postfix) {
1269        return doBooleanCommand("SET p2p_ssid_postfix " + postfix);
1270    }
1271
1272    public boolean setP2pGroupIdle(String iface, int time) {
1273        synchronized (sLock) {
1274            return doBooleanCommandNative("IFNAME=" + iface + " SET p2p_group_idle " + time);
1275        }
1276    }
1277
1278    public void setPowerSave(boolean enabled) {
1279        if (enabled) {
1280            doBooleanCommand("SET ps 1");
1281        } else {
1282            doBooleanCommand("SET ps 0");
1283        }
1284    }
1285
1286    public boolean setP2pPowerSave(String iface, boolean enabled) {
1287        synchronized (sLock) {
1288            if (enabled) {
1289                return doBooleanCommandNative("IFNAME=" + iface + " P2P_SET ps 1");
1290            } else {
1291                return doBooleanCommandNative("IFNAME=" + iface + " P2P_SET ps 0");
1292            }
1293        }
1294    }
1295
1296    public boolean setWfdEnable(boolean enable) {
1297        return doBooleanCommand("SET wifi_display " + (enable ? "1" : "0"));
1298    }
1299
1300    public boolean setWfdDeviceInfo(String hex) {
1301        return doBooleanCommand("WFD_SUBELEM_SET 0 " + hex);
1302    }
1303
1304    /**
1305     * "sta" prioritizes STA connection over P2P and "p2p" prioritizes
1306     * P2P connection over STA
1307     */
1308    public boolean setConcurrencyPriority(String s) {
1309        return doBooleanCommand("P2P_SET conc_pref " + s);
1310    }
1311
1312    public boolean p2pFind() {
1313        return doBooleanCommand("P2P_FIND");
1314    }
1315
1316    public boolean p2pFind(int timeout) {
1317        if (timeout <= 0) {
1318            return p2pFind();
1319        }
1320        return doBooleanCommand("P2P_FIND " + timeout);
1321    }
1322
1323    public boolean p2pStopFind() {
1324       return doBooleanCommand("P2P_STOP_FIND");
1325    }
1326
1327    public boolean p2pListen() {
1328        return doBooleanCommand("P2P_LISTEN");
1329    }
1330
1331    public boolean p2pListen(int timeout) {
1332        if (timeout <= 0) {
1333            return p2pListen();
1334        }
1335        return doBooleanCommand("P2P_LISTEN " + timeout);
1336    }
1337
1338    public boolean p2pExtListen(boolean enable, int period, int interval) {
1339        if (enable && interval < period) {
1340            return false;
1341        }
1342        return doBooleanCommand("P2P_EXT_LISTEN"
1343                    + (enable ? (" " + period + " " + interval) : ""));
1344    }
1345
1346    public boolean p2pSetChannel(int lc, int oc) {
1347        if (DBG) Log.d(mTAG, "p2pSetChannel: lc="+lc+", oc="+oc);
1348
1349        synchronized (sLock) {
1350            if (lc >=1 && lc <= 11) {
1351                if (!doBooleanCommand("P2P_SET listen_channel " + lc)) {
1352                    return false;
1353                }
1354            } else if (lc != 0) {
1355                return false;
1356            }
1357
1358            if (oc >= 1 && oc <= 165 ) {
1359                int freq = (oc <= 14 ? 2407 : 5000) + oc * 5;
1360                return doBooleanCommand("P2P_SET disallow_freq 1000-"
1361                        + (freq - 5) + "," + (freq + 5) + "-6000");
1362            } else if (oc == 0) {
1363                /* oc==0 disables "P2P_SET disallow_freq" (enables all freqs) */
1364                return doBooleanCommand("P2P_SET disallow_freq \"\"");
1365            }
1366        }
1367        return false;
1368    }
1369
1370    public boolean p2pFlush() {
1371        return doBooleanCommand("P2P_FLUSH");
1372    }
1373
1374    private static final int DEFAULT_GROUP_OWNER_INTENT     = 6;
1375    /* p2p_connect <peer device address> <pbc|pin|PIN#> [label|display|keypad]
1376        [persistent] [join|auth] [go_intent=<0..15>] [freq=<in MHz>] */
1377    public String p2pConnect(WifiP2pConfig config, boolean joinExistingGroup) {
1378        if (config == null) return null;
1379        List<String> args = new ArrayList<String>();
1380        WpsInfo wps = config.wps;
1381        args.add(config.deviceAddress);
1382
1383        switch (wps.setup) {
1384            case WpsInfo.PBC:
1385                args.add("pbc");
1386                break;
1387            case WpsInfo.DISPLAY:
1388                if (TextUtils.isEmpty(wps.pin)) {
1389                    args.add("pin");
1390                } else {
1391                    args.add(wps.pin);
1392                }
1393                args.add("display");
1394                break;
1395            case WpsInfo.KEYPAD:
1396                args.add(wps.pin);
1397                args.add("keypad");
1398                break;
1399            case WpsInfo.LABEL:
1400                args.add(wps.pin);
1401                args.add("label");
1402            default:
1403                break;
1404        }
1405
1406        if (config.netId == WifiP2pGroup.PERSISTENT_NET_ID) {
1407            args.add("persistent");
1408        }
1409
1410        if (joinExistingGroup) {
1411            args.add("join");
1412        } else {
1413            //TODO: This can be adapted based on device plugged in state and
1414            //device battery state
1415            int groupOwnerIntent = config.groupOwnerIntent;
1416            if (groupOwnerIntent < 0 || groupOwnerIntent > 15) {
1417                groupOwnerIntent = DEFAULT_GROUP_OWNER_INTENT;
1418            }
1419            args.add("go_intent=" + groupOwnerIntent);
1420        }
1421
1422        String command = "P2P_CONNECT ";
1423        for (String s : args) command += s + " ";
1424
1425        return doStringCommand(command);
1426    }
1427
1428    public boolean p2pCancelConnect() {
1429        return doBooleanCommand("P2P_CANCEL");
1430    }
1431
1432    public boolean p2pProvisionDiscovery(WifiP2pConfig config) {
1433        if (config == null) return false;
1434
1435        switch (config.wps.setup) {
1436            case WpsInfo.PBC:
1437                return doBooleanCommand("P2P_PROV_DISC " + config.deviceAddress + " pbc");
1438            case WpsInfo.DISPLAY:
1439                //We are doing display, so provision discovery is keypad
1440                return doBooleanCommand("P2P_PROV_DISC " + config.deviceAddress + " keypad");
1441            case WpsInfo.KEYPAD:
1442                //We are doing keypad, so provision discovery is display
1443                return doBooleanCommand("P2P_PROV_DISC " + config.deviceAddress + " display");
1444            default:
1445                break;
1446        }
1447        return false;
1448    }
1449
1450    public boolean p2pGroupAdd(boolean persistent) {
1451        if (persistent) {
1452            return doBooleanCommand("P2P_GROUP_ADD persistent");
1453        }
1454        return doBooleanCommand("P2P_GROUP_ADD");
1455    }
1456
1457    public boolean p2pGroupAdd(int netId) {
1458        return doBooleanCommand("P2P_GROUP_ADD persistent=" + netId);
1459    }
1460
1461    public boolean p2pGroupRemove(String iface) {
1462        if (TextUtils.isEmpty(iface)) return false;
1463        synchronized (sLock) {
1464            return doBooleanCommandNative("IFNAME=" + iface + " P2P_GROUP_REMOVE " + iface);
1465        }
1466    }
1467
1468    public boolean p2pReject(String deviceAddress) {
1469        return doBooleanCommand("P2P_REJECT " + deviceAddress);
1470    }
1471
1472    /* Invite a peer to a group */
1473    public boolean p2pInvite(WifiP2pGroup group, String deviceAddress) {
1474        if (TextUtils.isEmpty(deviceAddress)) return false;
1475
1476        if (group == null) {
1477            return doBooleanCommand("P2P_INVITE peer=" + deviceAddress);
1478        } else {
1479            return doBooleanCommand("P2P_INVITE group=" + group.getInterface()
1480                    + " peer=" + deviceAddress + " go_dev_addr=" + group.getOwner().deviceAddress);
1481        }
1482    }
1483
1484    /* Reinvoke a persistent connection */
1485    public boolean p2pReinvoke(int netId, String deviceAddress) {
1486        if (TextUtils.isEmpty(deviceAddress) || netId < 0) return false;
1487
1488        return doBooleanCommand("P2P_INVITE persistent=" + netId + " peer=" + deviceAddress);
1489    }
1490
1491    public String p2pGetSsid(String deviceAddress) {
1492        return p2pGetParam(deviceAddress, "oper_ssid");
1493    }
1494
1495    public String p2pGetDeviceAddress() {
1496        Log.d(TAG, "p2pGetDeviceAddress");
1497
1498        String status = null;
1499
1500        /* Explicitly calling the API without IFNAME= prefix to take care of the devices that
1501        don't have p2p0 interface. Supplicant seems to be returning the correct address anyway. */
1502
1503        synchronized (sLock) {
1504            status = doStringCommandNative("STATUS");
1505        }
1506
1507        String result = "";
1508        if (status != null) {
1509            String[] tokens = status.split("\n");
1510            for (String token : tokens) {
1511                if (token.startsWith("p2p_device_address=")) {
1512                    String[] nameValue = token.split("=");
1513                    if (nameValue.length != 2)
1514                        break;
1515                    result = nameValue[1];
1516                }
1517            }
1518        }
1519
1520        Log.d(TAG, "p2pGetDeviceAddress returning " + result);
1521        return result;
1522    }
1523
1524    public int getGroupCapability(String deviceAddress) {
1525        int gc = 0;
1526        if (TextUtils.isEmpty(deviceAddress)) return gc;
1527        String peerInfo = p2pPeer(deviceAddress);
1528        if (TextUtils.isEmpty(peerInfo)) return gc;
1529
1530        String[] tokens = peerInfo.split("\n");
1531        for (String token : tokens) {
1532            if (token.startsWith("group_capab=")) {
1533                String[] nameValue = token.split("=");
1534                if (nameValue.length != 2) break;
1535                try {
1536                    return Integer.decode(nameValue[1]);
1537                } catch(NumberFormatException e) {
1538                    return gc;
1539                }
1540            }
1541        }
1542        return gc;
1543    }
1544
1545    public String p2pPeer(String deviceAddress) {
1546        return doStringCommand("P2P_PEER " + deviceAddress);
1547    }
1548
1549    private String p2pGetParam(String deviceAddress, String key) {
1550        if (deviceAddress == null) return null;
1551
1552        String peerInfo = p2pPeer(deviceAddress);
1553        if (peerInfo == null) return null;
1554        String[] tokens= peerInfo.split("\n");
1555
1556        key += "=";
1557        for (String token : tokens) {
1558            if (token.startsWith(key)) {
1559                String[] nameValue = token.split("=");
1560                if (nameValue.length != 2) break;
1561                return nameValue[1];
1562            }
1563        }
1564        return null;
1565    }
1566
1567    public boolean p2pServiceAdd(WifiP2pServiceInfo servInfo) {
1568        /*
1569         * P2P_SERVICE_ADD bonjour <query hexdump> <RDATA hexdump>
1570         * P2P_SERVICE_ADD upnp <version hex> <service>
1571         *
1572         * e.g)
1573         * [Bonjour]
1574         * # IP Printing over TCP (PTR) (RDATA=MyPrinter._ipp._tcp.local.)
1575         * P2P_SERVICE_ADD bonjour 045f697070c00c000c01 094d795072696e746572c027
1576         * # IP Printing over TCP (TXT) (RDATA=txtvers=1,pdl=application/postscript)
1577         * P2P_SERVICE_ADD bonjour 096d797072696e746572045f697070c00c001001
1578         *  09747874766572733d311a70646c3d6170706c69636174696f6e2f706f7374736372797074
1579         *
1580         * [UPnP]
1581         * P2P_SERVICE_ADD upnp 10 uuid:6859dede-8574-59ab-9332-123456789012
1582         * P2P_SERVICE_ADD upnp 10 uuid:6859dede-8574-59ab-9332-123456789012::upnp:rootdevice
1583         * P2P_SERVICE_ADD upnp 10 uuid:6859dede-8574-59ab-9332-123456789012::urn:schemas-upnp
1584         * -org:device:InternetGatewayDevice:1
1585         * P2P_SERVICE_ADD upnp 10 uuid:6859dede-8574-59ab-9322-123456789012::urn:schemas-upnp
1586         * -org:service:ContentDirectory:2
1587         */
1588        synchronized (sLock) {
1589            for (String s : servInfo.getSupplicantQueryList()) {
1590                String command = "P2P_SERVICE_ADD";
1591                command += (" " + s);
1592                if (!doBooleanCommand(command)) {
1593                    return false;
1594                }
1595            }
1596        }
1597        return true;
1598    }
1599
1600    public boolean p2pServiceDel(WifiP2pServiceInfo servInfo) {
1601        /*
1602         * P2P_SERVICE_DEL bonjour <query hexdump>
1603         * P2P_SERVICE_DEL upnp <version hex> <service>
1604         */
1605        synchronized (sLock) {
1606            for (String s : servInfo.getSupplicantQueryList()) {
1607                String command = "P2P_SERVICE_DEL ";
1608
1609                String[] data = s.split(" ");
1610                if (data.length < 2) {
1611                    return false;
1612                }
1613                if ("upnp".equals(data[0])) {
1614                    command += s;
1615                } else if ("bonjour".equals(data[0])) {
1616                    command += data[0];
1617                    command += (" " + data[1]);
1618                } else {
1619                    return false;
1620                }
1621                if (!doBooleanCommand(command)) {
1622                    return false;
1623                }
1624            }
1625        }
1626        return true;
1627    }
1628
1629    public boolean p2pServiceFlush() {
1630        return doBooleanCommand("P2P_SERVICE_FLUSH");
1631    }
1632
1633    public String p2pServDiscReq(String addr, String query) {
1634        String command = "P2P_SERV_DISC_REQ";
1635        command += (" " + addr);
1636        command += (" " + query);
1637
1638        return doStringCommand(command);
1639    }
1640
1641    public boolean p2pServDiscCancelReq(String id) {
1642        return doBooleanCommand("P2P_SERV_DISC_CANCEL_REQ " + id);
1643    }
1644
1645    /* Set the current mode of miracast operation.
1646     *  0 = disabled
1647     *  1 = operating as source
1648     *  2 = operating as sink
1649     */
1650    public void setMiracastMode(int mode) {
1651        // Note: optional feature on the driver. It is ok for this to fail.
1652        doBooleanCommand("DRIVER MIRACAST " + mode);
1653    }
1654
1655    public boolean fetchAnqp(String bssid, String subtypes) {
1656        return doBooleanCommand("ANQP_GET " + bssid + " " + subtypes);
1657    }
1658
1659    /*
1660     * NFC-related calls
1661     */
1662    public String getNfcWpsConfigurationToken(int netId) {
1663        return doStringCommand("WPS_NFC_CONFIG_TOKEN WPS " + netId);
1664    }
1665
1666    public String getNfcHandoverRequest() {
1667        return doStringCommand("NFC_GET_HANDOVER_REQ NDEF P2P-CR");
1668    }
1669
1670    public String getNfcHandoverSelect() {
1671        return doStringCommand("NFC_GET_HANDOVER_SEL NDEF P2P-CR");
1672    }
1673
1674    public boolean initiatorReportNfcHandover(String selectMessage) {
1675        return doBooleanCommand("NFC_REPORT_HANDOVER INIT P2P 00 " + selectMessage);
1676    }
1677
1678    public boolean responderReportNfcHandover(String requestMessage) {
1679        return doBooleanCommand("NFC_REPORT_HANDOVER RESP P2P " + requestMessage + " 00");
1680    }
1681
1682
1683    /* kernel logging support */
1684    private static native byte[] readKernelLogNative();
1685
1686    synchronized public String readKernelLog() {
1687        byte[] bytes = readKernelLogNative();
1688        if (bytes != null) {
1689            CharsetDecoder decoder = StandardCharsets.UTF_8.newDecoder();
1690            try {
1691                CharBuffer decoded = decoder.decode(ByteBuffer.wrap(bytes));
1692                return decoded.toString();
1693            } catch (CharacterCodingException cce) {
1694                return new String(bytes, StandardCharsets.ISO_8859_1);
1695            }
1696        } else {
1697            return "*** failed to read kernel log ***";
1698        }
1699    }
1700
1701    /* WIFI HAL support */
1702
1703    // HAL command ids
1704    private static int sCmdId = 1;
1705    private static int getNewCmdIdLocked() {
1706        return sCmdId++;
1707    }
1708
1709    private static final String TAG = "WifiNative-HAL";
1710    private static long sWifiHalHandle = 0;             /* used by JNI to save wifi_handle */
1711    private static long[] sWifiIfaceHandles = null;     /* used by JNI to save interface handles */
1712    public static int sWlan0Index = -1;
1713    private static int sP2p0Index = -1;
1714    private static MonitorThread sThread;
1715    private static final int STOP_HAL_TIMEOUT_MS = 1000;
1716
1717    private static native boolean startHalNative();
1718    private static native void stopHalNative();
1719    private static native void waitForHalEventNative();
1720
1721    private static class MonitorThread extends Thread {
1722        public void run() {
1723            Log.i(TAG, "Waiting for HAL events mWifiHalHandle=" + Long.toString(sWifiHalHandle));
1724            waitForHalEventNative();
1725        }
1726    }
1727
1728    public boolean startHal() {
1729        String debugLog = "startHal stack: ";
1730        java.lang.StackTraceElement[] elements = Thread.currentThread().getStackTrace();
1731        for (int i = 2; i < elements.length && i <= 7; i++ ) {
1732            debugLog = debugLog + " - " + elements[i].getMethodName();
1733        }
1734
1735        sLocalLog.log(debugLog);
1736
1737        synchronized (sLock) {
1738            if (startHalNative() && (getInterfaces() != 0) && (sWlan0Index != -1)) {
1739                sThread = new MonitorThread();
1740                sThread.start();
1741                return true;
1742            } else {
1743                if (DBG) sLocalLog.log("Could not start hal");
1744                Log.e(TAG, "Could not start hal");
1745                return false;
1746            }
1747        }
1748    }
1749
1750    public void stopHal() {
1751        synchronized (sLock) {
1752            if (isHalStarted()) {
1753                stopHalNative();
1754                try {
1755                    sThread.join(STOP_HAL_TIMEOUT_MS);
1756                    Log.d(TAG, "HAL event thread stopped successfully");
1757                } catch (InterruptedException e) {
1758                    Log.e(TAG, "Could not stop HAL cleanly");
1759                }
1760                sThread = null;
1761                sWifiHalHandle = 0;
1762                sWifiIfaceHandles = null;
1763                sWlan0Index = -1;
1764                sP2p0Index = -1;
1765            }
1766        }
1767    }
1768
1769    public boolean isHalStarted() {
1770        return (sWifiHalHandle != 0);
1771    }
1772    private static native int getInterfacesNative();
1773
1774    public int getInterfaces() {
1775        synchronized (sLock) {
1776            if (isHalStarted()) {
1777                if (sWifiIfaceHandles == null) {
1778                    int num = getInterfacesNative();
1779                    int wifi_num = 0;
1780                    for (int i = 0; i < num; i++) {
1781                        String name = getInterfaceNameNative(i);
1782                        Log.i(TAG, "interface[" + i + "] = " + name);
1783                        if (name.equals("wlan0")) {
1784                            sWlan0Index = i;
1785                            wifi_num++;
1786                        } else if (name.equals("p2p0")) {
1787                            sP2p0Index = i;
1788                            wifi_num++;
1789                        }
1790                    }
1791                    return wifi_num;
1792                } else {
1793                    return sWifiIfaceHandles.length;
1794                }
1795            } else {
1796                return 0;
1797            }
1798        }
1799    }
1800
1801    private static native String getInterfaceNameNative(int index);
1802    public String getInterfaceName(int index) {
1803        synchronized (sLock) {
1804            return getInterfaceNameNative(index);
1805        }
1806    }
1807
1808    // TODO: Change variable names to camel style.
1809    public static class ScanCapabilities {
1810        public int  max_scan_cache_size;
1811        public int  max_scan_buckets;
1812        public int  max_ap_cache_per_scan;
1813        public int  max_rssi_sample_size;
1814        public int  max_scan_reporting_threshold;
1815        public int  max_hotlist_bssids;
1816        public int  max_significant_wifi_change_aps;
1817        public int  max_bssid_history_entries;
1818        public int  max_number_epno_networks;
1819        public int  max_number_epno_networks_by_ssid;
1820        public int  max_number_of_white_listed_ssid;
1821    }
1822
1823    public boolean getScanCapabilities(ScanCapabilities capabilities) {
1824        synchronized (sLock) {
1825            return isHalStarted() && getScanCapabilitiesNative(sWlan0Index, capabilities);
1826        }
1827    }
1828
1829    private static native boolean getScanCapabilitiesNative(
1830            int iface, ScanCapabilities capabilities);
1831
1832    private static native boolean startScanNative(int iface, int id, ScanSettings settings);
1833    private static native boolean stopScanNative(int iface, int id);
1834    private static native WifiScanner.ScanData[] getScanResultsNative(int iface, boolean flush);
1835    private static native WifiLinkLayerStats getWifiLinkLayerStatsNative(int iface);
1836    private static native void setWifiLinkLayerStatsNative(int iface, int enable);
1837
1838    public static class ChannelSettings {
1839        public int frequency;
1840        public int dwell_time_ms;
1841        public boolean passive;
1842    }
1843
1844    public static class BucketSettings {
1845        public int bucket;
1846        public int band;
1847        public int period_ms;
1848        public int max_period_ms;
1849        public int step_count;
1850        public int report_events;
1851        public int num_channels;
1852        public ChannelSettings[] channels;
1853    }
1854
1855    public static class ScanSettings {
1856        public int base_period_ms;
1857        public int max_ap_per_scan;
1858        public int report_threshold_percent;
1859        public int report_threshold_num_scans;
1860        public int num_buckets;
1861        /* Not part of gscan HAL API. Used only for wpa_supplicant scanning */
1862        public int[] hiddenNetworkIds;
1863        public BucketSettings[] buckets;
1864    }
1865
1866    /**
1867     * Network parameters to start PNO scan.
1868     */
1869    public static class PnoNetwork {
1870        public String ssid;
1871        public int networkId;
1872        public int priority;
1873        public byte flags;
1874        public byte auth_bit_field;
1875    }
1876
1877    /**
1878     * Parameters to start PNO scan. This holds the list of networks which are going to used for
1879     * PNO scan.
1880     */
1881    public static class PnoSettings {
1882        public int min5GHzRssi;
1883        public int min24GHzRssi;
1884        public int initialScoreMax;
1885        public int currentConnectionBonus;
1886        public int sameNetworkBonus;
1887        public int secureBonus;
1888        public int band5GHzBonus;
1889        public boolean isConnected;
1890        public PnoNetwork[] networkList;
1891    }
1892
1893    /**
1894     * Wi-Fi channel information.
1895     */
1896    public static class WifiChannelInfo {
1897        int mPrimaryFrequency;
1898        int mCenterFrequency0;
1899        int mCenterFrequency1;
1900        int mChannelWidth;
1901        // TODO: add preamble once available in HAL.
1902    }
1903
1904    public static interface ScanEventHandler {
1905        /**
1906         * Called for each AP as it is found with the entire contents of the beacon/probe response.
1907         * Only called when WifiScanner.REPORT_EVENT_FULL_SCAN_RESULT is specified.
1908         */
1909        void onFullScanResult(ScanResult fullScanResult);
1910        /**
1911         * Callback on an event during a gscan scan.
1912         * See WifiNative.WIFI_SCAN_* for possible values.
1913         */
1914        void onScanStatus(int event);
1915        /**
1916         * Called with the current cached scan results when gscan is paused.
1917         */
1918        void onScanPaused(WifiScanner.ScanData[] data);
1919        /**
1920         * Called with the current cached scan results when gscan is resumed.
1921         */
1922        void onScanRestarted();
1923    }
1924
1925    /**
1926     * Handler to notify the occurrence of various events during PNO scan.
1927     */
1928    public interface PnoEventHandler {
1929        /**
1930         * Callback to notify when one of the shortlisted networks is found during PNO scan.
1931         * @param results List of Scan results received.
1932         */
1933        void onPnoNetworkFound(ScanResult[] results);
1934
1935        /**
1936         * Callback to notify when the PNO scan schedule fails.
1937         */
1938        void onPnoScanFailed();
1939    }
1940
1941    /* scan status, keep these values in sync with gscan.h */
1942    public static final int WIFI_SCAN_RESULTS_AVAILABLE = 0;
1943    public static final int WIFI_SCAN_THRESHOLD_NUM_SCANS = 1;
1944    public static final int WIFI_SCAN_THRESHOLD_PERCENT = 2;
1945    public static final int WIFI_SCAN_FAILED = 3;
1946
1947    // Callback from native
1948    private static void onScanStatus(int id, int event) {
1949        ScanEventHandler handler = sScanEventHandler;
1950        if (handler != null) {
1951            handler.onScanStatus(event);
1952        }
1953    }
1954
1955    public static  WifiSsid createWifiSsid(byte[] rawSsid) {
1956        String ssidHexString = String.valueOf(HexEncoding.encode(rawSsid));
1957
1958        if (ssidHexString == null) {
1959            return null;
1960        }
1961
1962        WifiSsid wifiSsid = WifiSsid.createFromHex(ssidHexString);
1963
1964        return wifiSsid;
1965    }
1966
1967    public static String ssidConvert(byte[] rawSsid) {
1968        String ssid;
1969
1970        CharsetDecoder decoder = StandardCharsets.UTF_8.newDecoder();
1971        try {
1972            CharBuffer decoded = decoder.decode(ByteBuffer.wrap(rawSsid));
1973            ssid = decoded.toString();
1974        } catch (CharacterCodingException cce) {
1975            ssid = null;
1976        }
1977
1978        if (ssid == null) {
1979            ssid = new String(rawSsid, StandardCharsets.ISO_8859_1);
1980        }
1981
1982        return ssid;
1983    }
1984
1985    // Called from native
1986    public static boolean setSsid(byte[] rawSsid, ScanResult result) {
1987        if (rawSsid == null || rawSsid.length == 0 || result == null) {
1988            return false;
1989        }
1990
1991        result.SSID = ssidConvert(rawSsid);
1992        result.wifiSsid = createWifiSsid(rawSsid);
1993        return true;
1994    }
1995
1996    private static void populateScanResult(ScanResult result, int beaconCap, String dbg) {
1997        if (dbg == null) dbg = "";
1998
1999        InformationElementUtil.HtOperation htOperation = new InformationElementUtil.HtOperation();
2000        InformationElementUtil.VhtOperation vhtOperation =
2001                new InformationElementUtil.VhtOperation();
2002        InformationElementUtil.ExtendedCapabilities extendedCaps =
2003                new InformationElementUtil.ExtendedCapabilities();
2004
2005        ScanResult.InformationElement elements[] =
2006                InformationElementUtil.parseInformationElements(result.bytes);
2007        for (ScanResult.InformationElement ie : elements) {
2008            if(ie.id == ScanResult.InformationElement.EID_HT_OPERATION) {
2009                htOperation.from(ie);
2010            } else if(ie.id == ScanResult.InformationElement.EID_VHT_OPERATION) {
2011                vhtOperation.from(ie);
2012            } else if (ie.id == ScanResult.InformationElement.EID_EXTENDED_CAPS) {
2013                extendedCaps.from(ie);
2014            }
2015        }
2016
2017        if (extendedCaps.is80211McRTTResponder) {
2018            result.setFlag(ScanResult.FLAG_80211mc_RESPONDER);
2019        } else {
2020            result.clearFlag(ScanResult.FLAG_80211mc_RESPONDER);
2021        }
2022
2023        //handle RTT related information
2024        if (vhtOperation.isValid()) {
2025            result.channelWidth = vhtOperation.getChannelWidth();
2026            result.centerFreq0 = vhtOperation.getCenterFreq0();
2027            result.centerFreq1 = vhtOperation.getCenterFreq1();
2028        } else {
2029            result.channelWidth = htOperation.getChannelWidth();
2030            result.centerFreq0 = htOperation.getCenterFreq0(result.frequency);
2031            result.centerFreq1  = 0;
2032        }
2033
2034        // build capabilities string
2035        BitSet beaconCapBits = new BitSet(16);
2036        for (int i = 0; i < 16; i++) {
2037            if ((beaconCap & (1 << i)) != 0) {
2038                beaconCapBits.set(i);
2039            }
2040        }
2041        result.capabilities = InformationElementUtil.Capabilities.buildCapabilities(elements,
2042                                               beaconCapBits);
2043
2044        if(DBG) {
2045            Log.d(TAG, dbg + "SSID: " + result.SSID + " ChannelWidth is: " + result.channelWidth
2046                    + " PrimaryFreq: " + result.frequency + " mCenterfreq0: " + result.centerFreq0
2047                    + " mCenterfreq1: " + result.centerFreq1 + (extendedCaps.is80211McRTTResponder
2048                    ? "Support RTT reponder: " : "Do not support RTT responder")
2049                    + " Capabilities: " + result.capabilities);
2050        }
2051
2052        result.informationElements = elements;
2053    }
2054
2055    // Callback from native
2056    private static void onFullScanResult(int id, ScanResult result,
2057            int bucketsScanned, int beaconCap) {
2058        if (DBG) Log.i(TAG, "Got a full scan results event, ssid = " + result.SSID);
2059
2060        ScanEventHandler handler = sScanEventHandler;
2061        if (handler != null) {
2062            populateScanResult(result, beaconCap, " onFullScanResult ");
2063            handler.onFullScanResult(result);
2064        }
2065    }
2066
2067    private static int sScanCmdId = 0;
2068    private static ScanEventHandler sScanEventHandler;
2069    private static ScanSettings sScanSettings;
2070
2071    public boolean startScan(ScanSettings settings, ScanEventHandler eventHandler) {
2072        synchronized (sLock) {
2073            if (isHalStarted()) {
2074                if (sScanCmdId != 0) {
2075                    stopScan();
2076                } else if (sScanSettings != null || sScanEventHandler != null) {
2077                /* current scan is paused; no need to stop it */
2078                }
2079
2080                sScanCmdId = getNewCmdIdLocked();
2081
2082                sScanSettings = settings;
2083                sScanEventHandler = eventHandler;
2084
2085                if (startScanNative(sWlan0Index, sScanCmdId, settings) == false) {
2086                    sScanEventHandler = null;
2087                    sScanSettings = null;
2088                    sScanCmdId = 0;
2089                    return false;
2090                }
2091
2092                return true;
2093            } else {
2094                return false;
2095            }
2096        }
2097    }
2098
2099    public void stopScan() {
2100        synchronized (sLock) {
2101            if (isHalStarted()) {
2102                if (sScanCmdId != 0) {
2103                    stopScanNative(sWlan0Index, sScanCmdId);
2104                }
2105                sScanSettings = null;
2106                sScanEventHandler = null;
2107                sScanCmdId = 0;
2108            }
2109        }
2110    }
2111
2112    public void pauseScan() {
2113        synchronized (sLock) {
2114            if (isHalStarted()) {
2115                if (sScanCmdId != 0 && sScanSettings != null && sScanEventHandler != null) {
2116                    Log.d(TAG, "Pausing scan");
2117                    WifiScanner.ScanData scanData[] = getScanResultsNative(sWlan0Index, true);
2118                    stopScanNative(sWlan0Index, sScanCmdId);
2119                    sScanCmdId = 0;
2120                    sScanEventHandler.onScanPaused(scanData);
2121                }
2122            }
2123        }
2124    }
2125
2126    public void restartScan() {
2127        synchronized (sLock) {
2128            if (isHalStarted()) {
2129                if (sScanCmdId == 0 && sScanSettings != null && sScanEventHandler != null) {
2130                    Log.d(TAG, "Restarting scan");
2131                    ScanEventHandler handler = sScanEventHandler;
2132                    ScanSettings settings = sScanSettings;
2133                    if (startScan(sScanSettings, sScanEventHandler)) {
2134                        sScanEventHandler.onScanRestarted();
2135                    } else {
2136                    /* we are still paused; don't change state */
2137                        sScanEventHandler = handler;
2138                        sScanSettings = settings;
2139                    }
2140                }
2141            }
2142        }
2143    }
2144
2145    public WifiScanner.ScanData[] getScanResults(boolean flush) {
2146        synchronized (sLock) {
2147            WifiScanner.ScanData[] sd = null;
2148            if (isHalStarted()) {
2149                sd = getScanResultsNative(sWlan0Index, flush);
2150            }
2151
2152            if (sd != null) {
2153                return sd;
2154            } else {
2155                return new WifiScanner.ScanData[0];
2156            }
2157        }
2158    }
2159
2160    public static interface HotlistEventHandler {
2161        void onHotlistApFound (ScanResult[] result);
2162        void onHotlistApLost  (ScanResult[] result);
2163    }
2164
2165    private static int sHotlistCmdId = 0;
2166    private static HotlistEventHandler sHotlistEventHandler;
2167
2168    private native static boolean setHotlistNative(int iface, int id,
2169            WifiScanner.HotlistSettings settings);
2170    private native static boolean resetHotlistNative(int iface, int id);
2171
2172    public boolean setHotlist(WifiScanner.HotlistSettings settings,
2173            HotlistEventHandler eventHandler) {
2174        synchronized (sLock) {
2175            if (isHalStarted()) {
2176                if (sHotlistCmdId != 0) {
2177                    return false;
2178                } else {
2179                    sHotlistCmdId = getNewCmdIdLocked();
2180                }
2181
2182                sHotlistEventHandler = eventHandler;
2183                if (setHotlistNative(sWlan0Index, sHotlistCmdId, settings) == false) {
2184                    sHotlistEventHandler = null;
2185                    return false;
2186                }
2187
2188                return true;
2189            } else {
2190                return false;
2191            }
2192        }
2193    }
2194
2195    public void resetHotlist() {
2196        synchronized (sLock) {
2197            if (isHalStarted()) {
2198                if (sHotlistCmdId != 0) {
2199                    resetHotlistNative(sWlan0Index, sHotlistCmdId);
2200                    sHotlistCmdId = 0;
2201                    sHotlistEventHandler = null;
2202                }
2203            }
2204        }
2205    }
2206
2207    // Callback from native
2208    private static void onHotlistApFound(int id, ScanResult[] results) {
2209        HotlistEventHandler handler = sHotlistEventHandler;
2210        if (handler != null) {
2211            handler.onHotlistApFound(results);
2212        } else {
2213            /* this can happen because of race conditions */
2214            Log.d(TAG, "Ignoring hotlist AP found event");
2215        }
2216    }
2217
2218    // Callback from native
2219    private static void onHotlistApLost(int id, ScanResult[] results) {
2220        HotlistEventHandler handler = sHotlistEventHandler;
2221        if (handler != null) {
2222            handler.onHotlistApLost(results);
2223        } else {
2224            /* this can happen because of race conditions */
2225            Log.d(TAG, "Ignoring hotlist AP lost event");
2226        }
2227    }
2228
2229    public static interface SignificantWifiChangeEventHandler {
2230        void onChangesFound(ScanResult[] result);
2231    }
2232
2233    private static SignificantWifiChangeEventHandler sSignificantWifiChangeHandler;
2234    private static int sSignificantWifiChangeCmdId;
2235
2236    private static native boolean trackSignificantWifiChangeNative(
2237            int iface, int id, WifiScanner.WifiChangeSettings settings);
2238    private static native boolean untrackSignificantWifiChangeNative(int iface, int id);
2239
2240    public boolean trackSignificantWifiChange(
2241            WifiScanner.WifiChangeSettings settings, SignificantWifiChangeEventHandler handler) {
2242        synchronized (sLock) {
2243            if (isHalStarted()) {
2244                if (sSignificantWifiChangeCmdId != 0) {
2245                    return false;
2246                } else {
2247                    sSignificantWifiChangeCmdId = getNewCmdIdLocked();
2248                }
2249
2250                sSignificantWifiChangeHandler = handler;
2251                if (trackSignificantWifiChangeNative(sWlan0Index, sSignificantWifiChangeCmdId,
2252                        settings) == false) {
2253                    sSignificantWifiChangeHandler = null;
2254                    return false;
2255                }
2256
2257                return true;
2258            } else {
2259                return false;
2260            }
2261
2262        }
2263    }
2264
2265    public void untrackSignificantWifiChange() {
2266        synchronized (sLock) {
2267            if (isHalStarted()) {
2268                if (sSignificantWifiChangeCmdId != 0) {
2269                    untrackSignificantWifiChangeNative(sWlan0Index, sSignificantWifiChangeCmdId);
2270                    sSignificantWifiChangeCmdId = 0;
2271                    sSignificantWifiChangeHandler = null;
2272                }
2273            }
2274        }
2275    }
2276
2277    // Callback from native
2278    private static void onSignificantWifiChange(int id, ScanResult[] results) {
2279        SignificantWifiChangeEventHandler handler = sSignificantWifiChangeHandler;
2280        if (handler != null) {
2281            handler.onChangesFound(results);
2282        } else {
2283            /* this can happen because of race conditions */
2284            Log.d(TAG, "Ignoring significant wifi change");
2285        }
2286    }
2287
2288    public WifiLinkLayerStats getWifiLinkLayerStats(String iface) {
2289        // TODO: use correct iface name to Index translation
2290        if (iface == null) return null;
2291        synchronized (sLock) {
2292            if (isHalStarted()) {
2293                return getWifiLinkLayerStatsNative(sWlan0Index);
2294            } else {
2295                return null;
2296            }
2297        }
2298    }
2299
2300    public void setWifiLinkLayerStats(String iface, int enable) {
2301        if (iface == null) return;
2302        synchronized (sLock) {
2303            if (isHalStarted()) {
2304                setWifiLinkLayerStatsNative(sWlan0Index, enable);
2305            }
2306        }
2307    }
2308
2309    public static native int getSupportedFeatureSetNative(int iface);
2310    public int getSupportedFeatureSet() {
2311        synchronized (sLock) {
2312            if (isHalStarted()) {
2313                return getSupportedFeatureSetNative(sWlan0Index);
2314            } else {
2315                Log.d(TAG, "Failing getSupportedFeatureset because HAL isn't started");
2316                return 0;
2317            }
2318        }
2319    }
2320
2321    /* Rtt related commands/events */
2322    public static interface RttEventHandler {
2323        void onRttResults(RttManager.RttResult[] result);
2324    }
2325
2326    private static RttEventHandler sRttEventHandler;
2327    private static int sRttCmdId;
2328
2329    // Callback from native
2330    private static void onRttResults(int id, RttManager.RttResult[] results) {
2331        RttEventHandler handler = sRttEventHandler;
2332        if (handler != null && id == sRttCmdId) {
2333            Log.d(TAG, "Received " + results.length + " rtt results");
2334            handler.onRttResults(results);
2335            sRttCmdId = 0;
2336        } else {
2337            Log.d(TAG, "RTT Received event for unknown cmd = " + id +
2338                    ", current id = " + sRttCmdId);
2339        }
2340    }
2341
2342    private static native boolean requestRangeNative(
2343            int iface, int id, RttManager.RttParams[] params);
2344    private static native boolean cancelRangeRequestNative(
2345            int iface, int id, RttManager.RttParams[] params);
2346
2347    public boolean requestRtt(
2348            RttManager.RttParams[] params, RttEventHandler handler) {
2349        synchronized (sLock) {
2350            if (isHalStarted()) {
2351                if (sRttCmdId != 0) {
2352                    Log.v("TAG", "Last one is still under measurement!");
2353                    return false;
2354                } else {
2355                    sRttCmdId = getNewCmdIdLocked();
2356                }
2357                sRttEventHandler = handler;
2358                Log.v(TAG, "native issue RTT request");
2359                return requestRangeNative(sWlan0Index, sRttCmdId, params);
2360            } else {
2361                return false;
2362            }
2363        }
2364    }
2365
2366    public boolean cancelRtt(RttManager.RttParams[] params) {
2367        synchronized (sLock) {
2368            if (isHalStarted()) {
2369                if (sRttCmdId == 0) {
2370                    return false;
2371                }
2372
2373                sRttCmdId = 0;
2374
2375                if (cancelRangeRequestNative(sWlan0Index, sRttCmdId, params)) {
2376                    sRttEventHandler = null;
2377                    Log.v(TAG, "RTT cancel Request Successfully");
2378                    return true;
2379                } else {
2380                    Log.e(TAG, "RTT cancel Request failed");
2381                    return false;
2382                }
2383            } else {
2384                return false;
2385            }
2386        }
2387    }
2388
2389    private static int sRttResponderCmdId = 0;
2390
2391    private static native ResponderConfig enableRttResponderNative(int iface, int commandId,
2392            int timeoutSeconds, WifiChannelInfo channelHint);
2393    /**
2394     * Enable RTT responder role on the device. Returns {@link ResponderConfig} if the responder
2395     * role is successfully enabled, {@code null} otherwise.
2396     */
2397    @Nullable
2398    public ResponderConfig enableRttResponder(int timeoutSeconds) {
2399        synchronized (sLock) {
2400            if (!isHalStarted()) return null;
2401            if (sRttResponderCmdId != 0) {
2402                if (DBG) Log.e(mTAG, "responder mode already enabled - this shouldn't happen");
2403                return null;
2404            }
2405            int id = getNewCmdIdLocked();
2406            ResponderConfig config = enableRttResponderNative(
2407                    sWlan0Index, id, timeoutSeconds, null);
2408            if (config != null) sRttResponderCmdId = id;
2409            if (DBG) Log.d(TAG, "enabling rtt " + (config != null));
2410            return config;
2411        }
2412    }
2413
2414    private static native boolean disableRttResponderNative(int iface, int commandId);
2415    /**
2416     * Disable RTT responder role. Returns {@code true} if responder role is successfully disabled,
2417     * {@code false} otherwise.
2418     */
2419    public boolean disableRttResponder() {
2420        synchronized (sLock) {
2421            if (!isHalStarted()) return false;
2422            if (sRttResponderCmdId == 0) {
2423                Log.e(mTAG, "responder role not enabled yet");
2424                return true;
2425            }
2426            sRttResponderCmdId = 0;
2427            return disableRttResponderNative(sWlan0Index, sRttResponderCmdId);
2428        }
2429    }
2430
2431    private static native boolean setScanningMacOuiNative(int iface, byte[] oui);
2432
2433    public boolean setScanningMacOui(byte[] oui) {
2434        synchronized (sLock) {
2435            if (isHalStarted()) {
2436                return setScanningMacOuiNative(sWlan0Index, oui);
2437            } else {
2438                return false;
2439            }
2440        }
2441    }
2442
2443    private static native int[] getChannelsForBandNative(
2444            int iface, int band);
2445
2446    public int [] getChannelsForBand(int band) {
2447        synchronized (sLock) {
2448            if (isHalStarted()) {
2449                return getChannelsForBandNative(sWlan0Index, band);
2450            } else {
2451                return null;
2452            }
2453        }
2454    }
2455
2456    private static native boolean isGetChannelsForBandSupportedNative();
2457    public boolean isGetChannelsForBandSupported(){
2458        synchronized (sLock) {
2459            if (isHalStarted()) {
2460                return isGetChannelsForBandSupportedNative();
2461            } else {
2462                return false;
2463            }
2464        }
2465    }
2466
2467    private static native boolean setDfsFlagNative(int iface, boolean dfsOn);
2468    public boolean setDfsFlag(boolean dfsOn) {
2469        synchronized (sLock) {
2470            if (isHalStarted()) {
2471                return setDfsFlagNative(sWlan0Index, dfsOn);
2472            } else {
2473                return false;
2474            }
2475        }
2476    }
2477
2478    private static native boolean toggleInterfaceNative(int on);
2479    public boolean toggleInterface(int on) {
2480        synchronized (sLock) {
2481            if (isHalStarted()) {
2482                return toggleInterfaceNative(on);
2483            } else {
2484                return false;
2485            }
2486        }
2487    }
2488
2489    private static native RttManager.RttCapabilities getRttCapabilitiesNative(int iface);
2490    public RttManager.RttCapabilities getRttCapabilities() {
2491        synchronized (sLock) {
2492            if (isHalStarted()) {
2493                return getRttCapabilitiesNative(sWlan0Index);
2494            } else {
2495                return null;
2496            }
2497        }
2498    }
2499
2500    public static final class PacketFilterCapabilities {
2501        /**
2502         * Version of APF instruction set supported for packet filtering.  0 indicates no support for
2503         * packet filtering using APF programs.
2504         */
2505        public int apfVersionSupported;
2506
2507        /**
2508         * Maximum size of APF program allowed.
2509         */
2510        public int maximumApfProgramSize;
2511    }
2512    private static native PacketFilterCapabilities getPacketFilterCapabilitiesNative(int iface);
2513    public PacketFilterCapabilities getPacketFilterCapabilities() {
2514        synchronized (sLock) {
2515            if (isHalStarted()) {
2516                return getPacketFilterCapabilitiesNative(sWlan0Index);
2517            } else {
2518                return null;
2519            }
2520        }
2521    }
2522
2523    private static native boolean installPacketFilterNative(int iface, byte[] filter);
2524    public boolean installPacketFilter(byte[] filter) {
2525        synchronized (sLock) {
2526            if (isHalStarted()) {
2527                return installPacketFilterNative(sWlan0Index, filter);
2528            } else {
2529                return false;
2530            }
2531        }
2532    }
2533
2534    private static native boolean setCountryCodeHalNative(int iface, String CountryCode);
2535    public boolean setCountryCodeHal(String CountryCode) {
2536        synchronized (sLock) {
2537            if (isHalStarted()) {
2538                return setCountryCodeHalNative(sWlan0Index, CountryCode);
2539            } else {
2540                return false;
2541            }
2542        }
2543    }
2544
2545    /* Rtt related commands/events */
2546    public abstract class TdlsEventHandler {
2547        abstract public void onTdlsStatus(String macAddr, int status, int reason);
2548    }
2549
2550    private static TdlsEventHandler sTdlsEventHandler;
2551
2552    private static native boolean enableDisableTdlsNative(int iface, boolean enable,
2553            String macAddr);
2554    public boolean enableDisableTdls(boolean enable, String macAdd, TdlsEventHandler tdlsCallBack) {
2555        synchronized (sLock) {
2556            sTdlsEventHandler = tdlsCallBack;
2557            return enableDisableTdlsNative(sWlan0Index, enable, macAdd);
2558        }
2559    }
2560
2561    // Once TDLS per mac and event feature is implemented, this class definition should be
2562    // moved to the right place, like WifiManager etc
2563    public static class TdlsStatus {
2564        int channel;
2565        int global_operating_class;
2566        int state;
2567        int reason;
2568    }
2569    private static native TdlsStatus getTdlsStatusNative(int iface, String macAddr);
2570    public TdlsStatus getTdlsStatus(String macAdd) {
2571        synchronized (sLock) {
2572            if (isHalStarted()) {
2573                return getTdlsStatusNative(sWlan0Index, macAdd);
2574            } else {
2575                return null;
2576            }
2577        }
2578    }
2579
2580    //ToFix: Once TDLS per mac and event feature is implemented, this class definition should be
2581    // moved to the right place, like WifiStateMachine etc
2582    public static class TdlsCapabilities {
2583        /* Maximum TDLS session number can be supported by the Firmware and hardware */
2584        int maxConcurrentTdlsSessionNumber;
2585        boolean isGlobalTdlsSupported;
2586        boolean isPerMacTdlsSupported;
2587        boolean isOffChannelTdlsSupported;
2588    }
2589
2590
2591
2592    private static native TdlsCapabilities getTdlsCapabilitiesNative(int iface);
2593    public TdlsCapabilities getTdlsCapabilities () {
2594        synchronized (sLock) {
2595            if (isHalStarted()) {
2596                return getTdlsCapabilitiesNative(sWlan0Index);
2597            } else {
2598                return null;
2599            }
2600        }
2601    }
2602
2603    private static boolean onTdlsStatus(String macAddr, int status, int reason) {
2604        TdlsEventHandler handler = sTdlsEventHandler;
2605        if (handler == null) {
2606            return false;
2607        } else {
2608            handler.onTdlsStatus(macAddr, status, reason);
2609            return true;
2610        }
2611    }
2612
2613    //---------------------------------------------------------------------------------
2614
2615    /* Wifi Logger commands/events */
2616
2617    public static interface WifiLoggerEventHandler {
2618        void onRingBufferData(RingBufferStatus status, byte[] buffer);
2619        void onWifiAlert(int errorCode, byte[] buffer);
2620    }
2621
2622    private static WifiLoggerEventHandler sWifiLoggerEventHandler = null;
2623
2624    // Callback from native
2625    private static void onRingBufferData(RingBufferStatus status, byte[] buffer) {
2626        WifiLoggerEventHandler handler = sWifiLoggerEventHandler;
2627        if (handler != null)
2628            handler.onRingBufferData(status, buffer);
2629    }
2630
2631    // Callback from native
2632    private static void onWifiAlert(byte[] buffer, int errorCode) {
2633        WifiLoggerEventHandler handler = sWifiLoggerEventHandler;
2634        if (handler != null)
2635            handler.onWifiAlert(errorCode, buffer);
2636    }
2637
2638    private static int sLogCmdId = -1;
2639    private static native boolean setLoggingEventHandlerNative(int iface, int id);
2640    public boolean setLoggingEventHandler(WifiLoggerEventHandler handler) {
2641        synchronized (sLock) {
2642            if (isHalStarted()) {
2643                int oldId =  sLogCmdId;
2644                sLogCmdId = getNewCmdIdLocked();
2645                if (!setLoggingEventHandlerNative(sWlan0Index, sLogCmdId)) {
2646                    sLogCmdId = oldId;
2647                    return false;
2648                }
2649                sWifiLoggerEventHandler = handler;
2650                return true;
2651            } else {
2652                return false;
2653            }
2654        }
2655    }
2656
2657    private static native boolean startLoggingRingBufferNative(int iface, int verboseLevel,
2658            int flags, int minIntervalSec ,int minDataSize, String ringName);
2659    public boolean startLoggingRingBuffer(int verboseLevel, int flags, int maxInterval,
2660            int minDataSize, String ringName){
2661        synchronized (sLock) {
2662            if (isHalStarted()) {
2663                return startLoggingRingBufferNative(sWlan0Index, verboseLevel, flags, maxInterval,
2664                        minDataSize, ringName);
2665            } else {
2666                return false;
2667            }
2668        }
2669    }
2670
2671    private static native int getSupportedLoggerFeatureSetNative(int iface);
2672    public int getSupportedLoggerFeatureSet() {
2673        synchronized (sLock) {
2674            if (isHalStarted()) {
2675                return getSupportedLoggerFeatureSetNative(sWlan0Index);
2676            } else {
2677                return 0;
2678            }
2679        }
2680    }
2681
2682    private static native boolean resetLogHandlerNative(int iface, int id);
2683    public boolean resetLogHandler() {
2684        synchronized (sLock) {
2685            if (isHalStarted()) {
2686                if (sLogCmdId == -1) {
2687                    Log.e(TAG,"Can not reset handler Before set any handler");
2688                    return false;
2689                }
2690                sWifiLoggerEventHandler = null;
2691                if (resetLogHandlerNative(sWlan0Index, sLogCmdId)) {
2692                    sLogCmdId = -1;
2693                    return true;
2694                } else {
2695                    return false;
2696                }
2697            } else {
2698                return false;
2699            }
2700        }
2701    }
2702
2703    private static native String getDriverVersionNative(int iface);
2704    public String getDriverVersion() {
2705        synchronized (sLock) {
2706            if (isHalStarted()) {
2707                return getDriverVersionNative(sWlan0Index);
2708            } else {
2709                return "";
2710            }
2711        }
2712    }
2713
2714
2715    private static native String getFirmwareVersionNative(int iface);
2716    public String getFirmwareVersion() {
2717        synchronized (sLock) {
2718            if (isHalStarted()) {
2719                return getFirmwareVersionNative(sWlan0Index);
2720            } else {
2721                return "";
2722            }
2723        }
2724    }
2725
2726    public static class RingBufferStatus{
2727        String name;
2728        int flag;
2729        int ringBufferId;
2730        int ringBufferByteSize;
2731        int verboseLevel;
2732        int writtenBytes;
2733        int readBytes;
2734        int writtenRecords;
2735
2736        @Override
2737        public String toString() {
2738            return "name: " + name + " flag: " + flag + " ringBufferId: " + ringBufferId +
2739                    " ringBufferByteSize: " +ringBufferByteSize + " verboseLevel: " +verboseLevel +
2740                    " writtenBytes: " + writtenBytes + " readBytes: " + readBytes +
2741                    " writtenRecords: " + writtenRecords;
2742        }
2743    }
2744
2745    private static native RingBufferStatus[] getRingBufferStatusNative(int iface);
2746    public RingBufferStatus[] getRingBufferStatus() {
2747        synchronized (sLock) {
2748            if (isHalStarted()) {
2749                return getRingBufferStatusNative(sWlan0Index);
2750            } else {
2751                return null;
2752            }
2753        }
2754    }
2755
2756    private static native boolean getRingBufferDataNative(int iface, String ringName);
2757    public boolean getRingBufferData(String ringName) {
2758        synchronized (sLock) {
2759            if (isHalStarted()) {
2760                return getRingBufferDataNative(sWlan0Index, ringName);
2761            } else {
2762                return false;
2763            }
2764        }
2765    }
2766
2767    private static byte[] mFwMemoryDump;
2768    // Callback from native
2769    private static void onWifiFwMemoryAvailable(byte[] buffer) {
2770        mFwMemoryDump = buffer;
2771        if (DBG) {
2772            Log.d(TAG, "onWifiFwMemoryAvailable is called and buffer length is: " +
2773                    (buffer == null ? 0 :  buffer.length));
2774        }
2775    }
2776
2777    private static native boolean getFwMemoryDumpNative(int iface);
2778    public byte[] getFwMemoryDump() {
2779        synchronized (sLock) {
2780            if (isHalStarted()) {
2781                if(getFwMemoryDumpNative(sWlan0Index)) {
2782                    byte[] fwMemoryDump = mFwMemoryDump;
2783                    mFwMemoryDump = null;
2784                    return fwMemoryDump;
2785                } else {
2786                    return null;
2787                }
2788            }
2789            return null;
2790        }
2791    }
2792
2793    //---------------------------------------------------------------------------------
2794    /* Configure ePNO/PNO */
2795    private static PnoEventHandler sPnoEventHandler;
2796    private static int sPnoCmdId = 0;
2797
2798    private static native boolean setPnoListNative(int iface, int id, PnoSettings settings);
2799
2800    /**
2801     * Set the PNO settings & the network list in HAL to start PNO.
2802     * @param settings PNO settings and network list.
2803     * @param eventHandler Handler to receive notifications back during PNO scan.
2804     * @return true if success, false otherwise
2805     */
2806    public boolean setPnoList(PnoSettings settings, PnoEventHandler eventHandler) {
2807        Log.e(TAG, "setPnoList cmd " + sPnoCmdId);
2808
2809        synchronized (sLock) {
2810            if (isHalStarted()) {
2811                sPnoCmdId = getNewCmdIdLocked();
2812                sPnoEventHandler = eventHandler;
2813                if (setPnoListNative(sWlan0Index, sPnoCmdId, settings)) {
2814                    return true;
2815                }
2816            }
2817            sPnoEventHandler = null;
2818            return false;
2819        }
2820    }
2821
2822    /**
2823     * Set the PNO network list in HAL to start PNO.
2824     * @param list PNO network list.
2825     * @param eventHandler Handler to receive notifications back during PNO scan.
2826     * @return true if success, false otherwise
2827     */
2828    public boolean setPnoList(PnoNetwork[] list, PnoEventHandler eventHandler) {
2829        PnoSettings settings = new PnoSettings();
2830        settings.networkList = list;
2831        return setPnoList(settings, eventHandler);
2832    }
2833
2834    private static native boolean resetPnoListNative(int iface, int id);
2835
2836    /**
2837     * Reset the PNO settings in HAL to stop PNO.
2838     * @return true if success, false otherwise
2839     */
2840    public boolean resetPnoList() {
2841        Log.e(TAG, "resetPnoList cmd " + sPnoCmdId);
2842
2843        synchronized (sLock) {
2844            if (isHalStarted()) {
2845                sPnoCmdId = getNewCmdIdLocked();
2846                sPnoEventHandler = null;
2847                if (resetPnoListNative(sWlan0Index, sPnoCmdId)) {
2848                    return true;
2849                }
2850            }
2851            return false;
2852        }
2853    }
2854
2855    // Callback from native
2856    private static void onPnoNetworkFound(int id, ScanResult[] results, int[] beaconCaps) {
2857        if (results == null) {
2858            Log.e(TAG, "onPnoNetworkFound null results");
2859            return;
2860
2861        }
2862        Log.d(TAG, "WifiNative.onPnoNetworkFound result " + results.length);
2863
2864        PnoEventHandler handler = sPnoEventHandler;
2865        if (sPnoCmdId != 0 && handler != null) {
2866            for (int i=0; i<results.length; i++) {
2867                Log.e(TAG, "onPnoNetworkFound SSID " + results[i].SSID
2868                        + " " + results[i].level + " " + results[i].frequency);
2869
2870                populateScanResult(results[i], beaconCaps[i], "onPnoNetworkFound ");
2871                results[i].wifiSsid = WifiSsid.createFromAsciiEncoded(results[i].SSID);
2872            }
2873
2874            handler.onPnoNetworkFound(results);
2875        } else {
2876            /* this can happen because of race conditions */
2877            Log.d(TAG, "Ignoring Pno Network found event");
2878        }
2879    }
2880
2881    private native static boolean setBssidBlacklistNative(int iface, int id,
2882                                              String list[]);
2883
2884    public boolean setBssidBlacklist(String list[]) {
2885        int size = 0;
2886        if (list != null) {
2887            size = list.length;
2888        }
2889        Log.e(TAG, "setBssidBlacklist cmd " + sPnoCmdId + " size " + size);
2890
2891        synchronized (sLock) {
2892            if (isHalStarted()) {
2893                sPnoCmdId = getNewCmdIdLocked();
2894                return setBssidBlacklistNative(sWlan0Index, sPnoCmdId, list);
2895            } else {
2896                return false;
2897            }
2898        }
2899    }
2900
2901    private native static int startSendingOffloadedPacketNative(int iface, int idx,
2902                                    byte[] srcMac, byte[] dstMac, byte[] pktData, int period);
2903
2904    public int
2905    startSendingOffloadedPacket(int slot, KeepalivePacketData keepAlivePacket, int period) {
2906        Log.d(TAG, "startSendingOffloadedPacket slot=" + slot + " period=" + period);
2907
2908        String[] macAddrStr = getMacAddress().split(":");
2909        byte[] srcMac = new byte[6];
2910        for(int i = 0; i < 6; i++) {
2911            Integer hexVal = Integer.parseInt(macAddrStr[i], 16);
2912            srcMac[i] = hexVal.byteValue();
2913        }
2914        synchronized (sLock) {
2915            if (isHalStarted()) {
2916                return startSendingOffloadedPacketNative(sWlan0Index, slot, srcMac,
2917                        keepAlivePacket.dstMac, keepAlivePacket.data, period);
2918            } else {
2919                return -1;
2920            }
2921        }
2922    }
2923
2924    private native static int stopSendingOffloadedPacketNative(int iface, int idx);
2925
2926    public int
2927    stopSendingOffloadedPacket(int slot) {
2928        Log.d(TAG, "stopSendingOffloadedPacket " + slot);
2929        synchronized (sLock) {
2930            if (isHalStarted()) {
2931                return stopSendingOffloadedPacketNative(sWlan0Index, slot);
2932            } else {
2933                return -1;
2934            }
2935        }
2936    }
2937
2938    public static interface WifiRssiEventHandler {
2939        void onRssiThresholdBreached(byte curRssi);
2940    }
2941
2942    private static WifiRssiEventHandler sWifiRssiEventHandler;
2943
2944    // Callback from native
2945    private static void onRssiThresholdBreached(int id, byte curRssi) {
2946        WifiRssiEventHandler handler = sWifiRssiEventHandler;
2947        if (handler != null) {
2948            handler.onRssiThresholdBreached(curRssi);
2949        }
2950    }
2951
2952    private native static int startRssiMonitoringNative(int iface, int id,
2953                                        byte maxRssi, byte minRssi);
2954
2955    private static int sRssiMonitorCmdId = 0;
2956
2957    public int startRssiMonitoring(byte maxRssi, byte minRssi,
2958                                                WifiRssiEventHandler rssiEventHandler) {
2959        Log.d(TAG, "startRssiMonitoring: maxRssi=" + maxRssi + " minRssi=" + minRssi);
2960        synchronized (sLock) {
2961            sWifiRssiEventHandler = rssiEventHandler;
2962            if (isHalStarted()) {
2963                if (sRssiMonitorCmdId != 0) {
2964                    stopRssiMonitoring();
2965                }
2966
2967                sRssiMonitorCmdId = getNewCmdIdLocked();
2968                Log.d(TAG, "sRssiMonitorCmdId = " + sRssiMonitorCmdId);
2969                int ret = startRssiMonitoringNative(sWlan0Index, sRssiMonitorCmdId,
2970                        maxRssi, minRssi);
2971                if (ret != 0) { // if not success
2972                    sRssiMonitorCmdId = 0;
2973                }
2974                return ret;
2975            } else {
2976                return -1;
2977            }
2978        }
2979    }
2980
2981    private native static int stopRssiMonitoringNative(int iface, int idx);
2982
2983    public int stopRssiMonitoring() {
2984        Log.d(TAG, "stopRssiMonitoring, cmdId " + sRssiMonitorCmdId);
2985        synchronized (sLock) {
2986            if (isHalStarted()) {
2987                int ret = 0;
2988                if (sRssiMonitorCmdId != 0) {
2989                    ret = stopRssiMonitoringNative(sWlan0Index, sRssiMonitorCmdId);
2990                }
2991                sRssiMonitorCmdId = 0;
2992                return ret;
2993            } else {
2994                return -1;
2995            }
2996        }
2997    }
2998
2999    private static native WifiWakeReasonAndCounts getWlanWakeReasonCountNative(int iface);
3000
3001    /**
3002     * Fetch the host wakeup reasons stats from wlan driver.
3003     * @return the |WifiWakeReasonAndCounts| object retrieved from the wlan driver.
3004     */
3005    public WifiWakeReasonAndCounts getWlanWakeReasonCount() {
3006        Log.d(TAG, "getWlanWakeReasonCount " + sWlan0Index);
3007        synchronized (sLock) {
3008            if (isHalStarted()) {
3009                return getWlanWakeReasonCountNative(sWlan0Index);
3010            } else {
3011                return null;
3012            }
3013        }
3014    }
3015
3016    private static native int configureNeighborDiscoveryOffload(int iface, boolean enabled);
3017
3018    public boolean configureNeighborDiscoveryOffload(boolean enabled) {
3019        final String logMsg =  "configureNeighborDiscoveryOffload(" + enabled + ")";
3020        Log.d(mTAG, logMsg);
3021        synchronized (sLock) {
3022            if (isHalStarted()) {
3023                final int ret = configureNeighborDiscoveryOffload(sWlan0Index, enabled);
3024                if (ret != 0) {
3025                    Log.d(mTAG, logMsg + " returned: " + ret);
3026                }
3027                return (ret == 0);
3028            }
3029        }
3030        return false;
3031    }
3032}
3033