WifiNative.java revision 3b51fd1bb8356b284822f4f677ad941524e616eb
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 MonitorThread sThread;
1714    private static final int STOP_HAL_TIMEOUT_MS = 1000;
1715
1716    private static native boolean startHalNative();
1717    private static native void stopHalNative();
1718    private static native void waitForHalEventNative();
1719
1720    private static class MonitorThread extends Thread {
1721        public void run() {
1722            Log.i(TAG, "Waiting for HAL events mWifiHalHandle=" + Long.toString(sWifiHalHandle));
1723            waitForHalEventNative();
1724        }
1725    }
1726
1727    public boolean startHal() {
1728        String debugLog = "startHal stack: ";
1729        java.lang.StackTraceElement[] elements = Thread.currentThread().getStackTrace();
1730        for (int i = 2; i < elements.length && i <= 7; i++ ) {
1731            debugLog = debugLog + " - " + elements[i].getMethodName();
1732        }
1733
1734        sLocalLog.log(debugLog);
1735
1736        synchronized (sLock) {
1737            if (startHalNative()) {
1738                int wlan0Index = queryInterfaceIndex(mInterfaceName);
1739                if (wlan0Index == -1) {
1740                    if (DBG) sLocalLog.log("Could not find interface with name: " + mInterfaceName);
1741                    return false;
1742                }
1743                sWlan0Index = wlan0Index;
1744                sThread = new MonitorThread();
1745                sThread.start();
1746                return true;
1747            } else {
1748                if (DBG) sLocalLog.log("Could not start hal");
1749                Log.e(TAG, "Could not start hal");
1750                return false;
1751            }
1752        }
1753    }
1754
1755    public void stopHal() {
1756        synchronized (sLock) {
1757            if (isHalStarted()) {
1758                stopHalNative();
1759                try {
1760                    sThread.join(STOP_HAL_TIMEOUT_MS);
1761                    Log.d(TAG, "HAL event thread stopped successfully");
1762                } catch (InterruptedException e) {
1763                    Log.e(TAG, "Could not stop HAL cleanly");
1764                }
1765                sThread = null;
1766                sWifiHalHandle = 0;
1767                sWifiIfaceHandles = null;
1768                sWlan0Index = -1;
1769            }
1770        }
1771    }
1772
1773    public boolean isHalStarted() {
1774        return (sWifiHalHandle != 0);
1775    }
1776    private static native int getInterfacesNative();
1777
1778    public int queryInterfaceIndex(String interfaceName) {
1779        synchronized (sLock) {
1780            if (isHalStarted()) {
1781                int num = getInterfacesNative();
1782                for (int i = 0; i < num; i++) {
1783                    String name = getInterfaceNameNative(i);
1784                    if (name.equals(interfaceName)) {
1785                        return i;
1786                    }
1787                }
1788            }
1789        }
1790        return -1;
1791    }
1792
1793    private static native String getInterfaceNameNative(int index);
1794    public String getInterfaceName(int index) {
1795        synchronized (sLock) {
1796            return getInterfaceNameNative(index);
1797        }
1798    }
1799
1800    // TODO: Change variable names to camel style.
1801    public static class ScanCapabilities {
1802        public int  max_scan_cache_size;
1803        public int  max_scan_buckets;
1804        public int  max_ap_cache_per_scan;
1805        public int  max_rssi_sample_size;
1806        public int  max_scan_reporting_threshold;
1807        public int  max_hotlist_bssids;
1808        public int  max_significant_wifi_change_aps;
1809        public int  max_bssid_history_entries;
1810        public int  max_number_epno_networks;
1811        public int  max_number_epno_networks_by_ssid;
1812        public int  max_number_of_white_listed_ssid;
1813    }
1814
1815    public boolean getScanCapabilities(ScanCapabilities capabilities) {
1816        synchronized (sLock) {
1817            return isHalStarted() && getScanCapabilitiesNative(sWlan0Index, capabilities);
1818        }
1819    }
1820
1821    private static native boolean getScanCapabilitiesNative(
1822            int iface, ScanCapabilities capabilities);
1823
1824    private static native boolean startScanNative(int iface, int id, ScanSettings settings);
1825    private static native boolean stopScanNative(int iface, int id);
1826    private static native WifiScanner.ScanData[] getScanResultsNative(int iface, boolean flush);
1827    private static native WifiLinkLayerStats getWifiLinkLayerStatsNative(int iface);
1828    private static native void setWifiLinkLayerStatsNative(int iface, int enable);
1829
1830    public static class ChannelSettings {
1831        public int frequency;
1832        public int dwell_time_ms;
1833        public boolean passive;
1834    }
1835
1836    public static class BucketSettings {
1837        public int bucket;
1838        public int band;
1839        public int period_ms;
1840        public int max_period_ms;
1841        public int step_count;
1842        public int report_events;
1843        public int num_channels;
1844        public ChannelSettings[] channels;
1845    }
1846
1847    public static class ScanSettings {
1848        public int base_period_ms;
1849        public int max_ap_per_scan;
1850        public int report_threshold_percent;
1851        public int report_threshold_num_scans;
1852        public int num_buckets;
1853        /* Not part of gscan HAL API. Used only for wpa_supplicant scanning */
1854        public int[] hiddenNetworkIds;
1855        public BucketSettings[] buckets;
1856    }
1857
1858    /**
1859     * Network parameters to start PNO scan.
1860     */
1861    public static class PnoNetwork {
1862        public String ssid;
1863        public int networkId;
1864        public int priority;
1865        public byte flags;
1866        public byte auth_bit_field;
1867    }
1868
1869    /**
1870     * Parameters to start PNO scan. This holds the list of networks which are going to used for
1871     * PNO scan.
1872     */
1873    public static class PnoSettings {
1874        public int min5GHzRssi;
1875        public int min24GHzRssi;
1876        public int initialScoreMax;
1877        public int currentConnectionBonus;
1878        public int sameNetworkBonus;
1879        public int secureBonus;
1880        public int band5GHzBonus;
1881        public boolean isConnected;
1882        public PnoNetwork[] networkList;
1883    }
1884
1885    /**
1886     * Wi-Fi channel information.
1887     */
1888    public static class WifiChannelInfo {
1889        int mPrimaryFrequency;
1890        int mCenterFrequency0;
1891        int mCenterFrequency1;
1892        int mChannelWidth;
1893        // TODO: add preamble once available in HAL.
1894    }
1895
1896    public static interface ScanEventHandler {
1897        /**
1898         * Called for each AP as it is found with the entire contents of the beacon/probe response.
1899         * Only called when WifiScanner.REPORT_EVENT_FULL_SCAN_RESULT is specified.
1900         */
1901        void onFullScanResult(ScanResult fullScanResult);
1902        /**
1903         * Callback on an event during a gscan scan.
1904         * See WifiNative.WIFI_SCAN_* for possible values.
1905         */
1906        void onScanStatus(int event);
1907        /**
1908         * Called with the current cached scan results when gscan is paused.
1909         */
1910        void onScanPaused(WifiScanner.ScanData[] data);
1911        /**
1912         * Called with the current cached scan results when gscan is resumed.
1913         */
1914        void onScanRestarted();
1915    }
1916
1917    /**
1918     * Handler to notify the occurrence of various events during PNO scan.
1919     */
1920    public interface PnoEventHandler {
1921        /**
1922         * Callback to notify when one of the shortlisted networks is found during PNO scan.
1923         * @param results List of Scan results received.
1924         */
1925        void onPnoNetworkFound(ScanResult[] results);
1926
1927        /**
1928         * Callback to notify when the PNO scan schedule fails.
1929         */
1930        void onPnoScanFailed();
1931    }
1932
1933    /* scan status, keep these values in sync with gscan.h */
1934    public static final int WIFI_SCAN_RESULTS_AVAILABLE = 0;
1935    public static final int WIFI_SCAN_THRESHOLD_NUM_SCANS = 1;
1936    public static final int WIFI_SCAN_THRESHOLD_PERCENT = 2;
1937    public static final int WIFI_SCAN_FAILED = 3;
1938
1939    // Callback from native
1940    private static void onScanStatus(int id, int event) {
1941        ScanEventHandler handler = sScanEventHandler;
1942        if (handler != null) {
1943            handler.onScanStatus(event);
1944        }
1945    }
1946
1947    public static  WifiSsid createWifiSsid(byte[] rawSsid) {
1948        String ssidHexString = String.valueOf(HexEncoding.encode(rawSsid));
1949
1950        if (ssidHexString == null) {
1951            return null;
1952        }
1953
1954        WifiSsid wifiSsid = WifiSsid.createFromHex(ssidHexString);
1955
1956        return wifiSsid;
1957    }
1958
1959    public static String ssidConvert(byte[] rawSsid) {
1960        String ssid;
1961
1962        CharsetDecoder decoder = StandardCharsets.UTF_8.newDecoder();
1963        try {
1964            CharBuffer decoded = decoder.decode(ByteBuffer.wrap(rawSsid));
1965            ssid = decoded.toString();
1966        } catch (CharacterCodingException cce) {
1967            ssid = null;
1968        }
1969
1970        if (ssid == null) {
1971            ssid = new String(rawSsid, StandardCharsets.ISO_8859_1);
1972        }
1973
1974        return ssid;
1975    }
1976
1977    // Called from native
1978    public static boolean setSsid(byte[] rawSsid, ScanResult result) {
1979        if (rawSsid == null || rawSsid.length == 0 || result == null) {
1980            return false;
1981        }
1982
1983        result.SSID = ssidConvert(rawSsid);
1984        result.wifiSsid = createWifiSsid(rawSsid);
1985        return true;
1986    }
1987
1988    private static void populateScanResult(ScanResult result, int beaconCap, String dbg) {
1989        if (dbg == null) dbg = "";
1990
1991        InformationElementUtil.HtOperation htOperation = new InformationElementUtil.HtOperation();
1992        InformationElementUtil.VhtOperation vhtOperation =
1993                new InformationElementUtil.VhtOperation();
1994        InformationElementUtil.ExtendedCapabilities extendedCaps =
1995                new InformationElementUtil.ExtendedCapabilities();
1996
1997        ScanResult.InformationElement elements[] =
1998                InformationElementUtil.parseInformationElements(result.bytes);
1999        for (ScanResult.InformationElement ie : elements) {
2000            if(ie.id == ScanResult.InformationElement.EID_HT_OPERATION) {
2001                htOperation.from(ie);
2002            } else if(ie.id == ScanResult.InformationElement.EID_VHT_OPERATION) {
2003                vhtOperation.from(ie);
2004            } else if (ie.id == ScanResult.InformationElement.EID_EXTENDED_CAPS) {
2005                extendedCaps.from(ie);
2006            }
2007        }
2008
2009        if (extendedCaps.is80211McRTTResponder) {
2010            result.setFlag(ScanResult.FLAG_80211mc_RESPONDER);
2011        } else {
2012            result.clearFlag(ScanResult.FLAG_80211mc_RESPONDER);
2013        }
2014
2015        //handle RTT related information
2016        if (vhtOperation.isValid()) {
2017            result.channelWidth = vhtOperation.getChannelWidth();
2018            result.centerFreq0 = vhtOperation.getCenterFreq0();
2019            result.centerFreq1 = vhtOperation.getCenterFreq1();
2020        } else {
2021            result.channelWidth = htOperation.getChannelWidth();
2022            result.centerFreq0 = htOperation.getCenterFreq0(result.frequency);
2023            result.centerFreq1  = 0;
2024        }
2025
2026        // build capabilities string
2027        BitSet beaconCapBits = new BitSet(16);
2028        for (int i = 0; i < 16; i++) {
2029            if ((beaconCap & (1 << i)) != 0) {
2030                beaconCapBits.set(i);
2031            }
2032        }
2033        result.capabilities = InformationElementUtil.Capabilities.buildCapabilities(elements,
2034                                               beaconCapBits);
2035
2036        if(DBG) {
2037            Log.d(TAG, dbg + "SSID: " + result.SSID + " ChannelWidth is: " + result.channelWidth
2038                    + " PrimaryFreq: " + result.frequency + " mCenterfreq0: " + result.centerFreq0
2039                    + " mCenterfreq1: " + result.centerFreq1 + (extendedCaps.is80211McRTTResponder
2040                    ? "Support RTT reponder: " : "Do not support RTT responder")
2041                    + " Capabilities: " + result.capabilities);
2042        }
2043
2044        result.informationElements = elements;
2045    }
2046
2047    // Callback from native
2048    private static void onFullScanResult(int id, ScanResult result,
2049            int bucketsScanned, int beaconCap) {
2050        if (DBG) Log.i(TAG, "Got a full scan results event, ssid = " + result.SSID);
2051
2052        ScanEventHandler handler = sScanEventHandler;
2053        if (handler != null) {
2054            populateScanResult(result, beaconCap, " onFullScanResult ");
2055            handler.onFullScanResult(result);
2056        }
2057    }
2058
2059    private static int sScanCmdId = 0;
2060    private static ScanEventHandler sScanEventHandler;
2061    private static ScanSettings sScanSettings;
2062
2063    public boolean startScan(ScanSettings settings, ScanEventHandler eventHandler) {
2064        synchronized (sLock) {
2065            if (isHalStarted()) {
2066                if (sScanCmdId != 0) {
2067                    stopScan();
2068                } else if (sScanSettings != null || sScanEventHandler != null) {
2069                /* current scan is paused; no need to stop it */
2070                }
2071
2072                sScanCmdId = getNewCmdIdLocked();
2073
2074                sScanSettings = settings;
2075                sScanEventHandler = eventHandler;
2076
2077                if (startScanNative(sWlan0Index, sScanCmdId, settings) == false) {
2078                    sScanEventHandler = null;
2079                    sScanSettings = null;
2080                    sScanCmdId = 0;
2081                    return false;
2082                }
2083
2084                return true;
2085            } else {
2086                return false;
2087            }
2088        }
2089    }
2090
2091    public void stopScan() {
2092        synchronized (sLock) {
2093            if (isHalStarted()) {
2094                if (sScanCmdId != 0) {
2095                    stopScanNative(sWlan0Index, sScanCmdId);
2096                }
2097                sScanSettings = null;
2098                sScanEventHandler = null;
2099                sScanCmdId = 0;
2100            }
2101        }
2102    }
2103
2104    public void pauseScan() {
2105        synchronized (sLock) {
2106            if (isHalStarted()) {
2107                if (sScanCmdId != 0 && sScanSettings != null && sScanEventHandler != null) {
2108                    Log.d(TAG, "Pausing scan");
2109                    WifiScanner.ScanData scanData[] = getScanResultsNative(sWlan0Index, true);
2110                    stopScanNative(sWlan0Index, sScanCmdId);
2111                    sScanCmdId = 0;
2112                    sScanEventHandler.onScanPaused(scanData);
2113                }
2114            }
2115        }
2116    }
2117
2118    public void restartScan() {
2119        synchronized (sLock) {
2120            if (isHalStarted()) {
2121                if (sScanCmdId == 0 && sScanSettings != null && sScanEventHandler != null) {
2122                    Log.d(TAG, "Restarting scan");
2123                    ScanEventHandler handler = sScanEventHandler;
2124                    ScanSettings settings = sScanSettings;
2125                    if (startScan(sScanSettings, sScanEventHandler)) {
2126                        sScanEventHandler.onScanRestarted();
2127                    } else {
2128                    /* we are still paused; don't change state */
2129                        sScanEventHandler = handler;
2130                        sScanSettings = settings;
2131                    }
2132                }
2133            }
2134        }
2135    }
2136
2137    public WifiScanner.ScanData[] getScanResults(boolean flush) {
2138        synchronized (sLock) {
2139            WifiScanner.ScanData[] sd = null;
2140            if (isHalStarted()) {
2141                sd = getScanResultsNative(sWlan0Index, flush);
2142            }
2143
2144            if (sd != null) {
2145                return sd;
2146            } else {
2147                return new WifiScanner.ScanData[0];
2148            }
2149        }
2150    }
2151
2152    public static interface HotlistEventHandler {
2153        void onHotlistApFound (ScanResult[] result);
2154        void onHotlistApLost  (ScanResult[] result);
2155    }
2156
2157    private static int sHotlistCmdId = 0;
2158    private static HotlistEventHandler sHotlistEventHandler;
2159
2160    private native static boolean setHotlistNative(int iface, int id,
2161            WifiScanner.HotlistSettings settings);
2162    private native static boolean resetHotlistNative(int iface, int id);
2163
2164    public boolean setHotlist(WifiScanner.HotlistSettings settings,
2165            HotlistEventHandler eventHandler) {
2166        synchronized (sLock) {
2167            if (isHalStarted()) {
2168                if (sHotlistCmdId != 0) {
2169                    return false;
2170                } else {
2171                    sHotlistCmdId = getNewCmdIdLocked();
2172                }
2173
2174                sHotlistEventHandler = eventHandler;
2175                if (setHotlistNative(sWlan0Index, sHotlistCmdId, settings) == false) {
2176                    sHotlistEventHandler = null;
2177                    return false;
2178                }
2179
2180                return true;
2181            } else {
2182                return false;
2183            }
2184        }
2185    }
2186
2187    public void resetHotlist() {
2188        synchronized (sLock) {
2189            if (isHalStarted()) {
2190                if (sHotlistCmdId != 0) {
2191                    resetHotlistNative(sWlan0Index, sHotlistCmdId);
2192                    sHotlistCmdId = 0;
2193                    sHotlistEventHandler = null;
2194                }
2195            }
2196        }
2197    }
2198
2199    // Callback from native
2200    private static void onHotlistApFound(int id, ScanResult[] results) {
2201        HotlistEventHandler handler = sHotlistEventHandler;
2202        if (handler != null) {
2203            handler.onHotlistApFound(results);
2204        } else {
2205            /* this can happen because of race conditions */
2206            Log.d(TAG, "Ignoring hotlist AP found event");
2207        }
2208    }
2209
2210    // Callback from native
2211    private static void onHotlistApLost(int id, ScanResult[] results) {
2212        HotlistEventHandler handler = sHotlistEventHandler;
2213        if (handler != null) {
2214            handler.onHotlistApLost(results);
2215        } else {
2216            /* this can happen because of race conditions */
2217            Log.d(TAG, "Ignoring hotlist AP lost event");
2218        }
2219    }
2220
2221    public static interface SignificantWifiChangeEventHandler {
2222        void onChangesFound(ScanResult[] result);
2223    }
2224
2225    private static SignificantWifiChangeEventHandler sSignificantWifiChangeHandler;
2226    private static int sSignificantWifiChangeCmdId;
2227
2228    private static native boolean trackSignificantWifiChangeNative(
2229            int iface, int id, WifiScanner.WifiChangeSettings settings);
2230    private static native boolean untrackSignificantWifiChangeNative(int iface, int id);
2231
2232    public boolean trackSignificantWifiChange(
2233            WifiScanner.WifiChangeSettings settings, SignificantWifiChangeEventHandler handler) {
2234        synchronized (sLock) {
2235            if (isHalStarted()) {
2236                if (sSignificantWifiChangeCmdId != 0) {
2237                    return false;
2238                } else {
2239                    sSignificantWifiChangeCmdId = getNewCmdIdLocked();
2240                }
2241
2242                sSignificantWifiChangeHandler = handler;
2243                if (trackSignificantWifiChangeNative(sWlan0Index, sSignificantWifiChangeCmdId,
2244                        settings) == false) {
2245                    sSignificantWifiChangeHandler = null;
2246                    return false;
2247                }
2248
2249                return true;
2250            } else {
2251                return false;
2252            }
2253
2254        }
2255    }
2256
2257    public void untrackSignificantWifiChange() {
2258        synchronized (sLock) {
2259            if (isHalStarted()) {
2260                if (sSignificantWifiChangeCmdId != 0) {
2261                    untrackSignificantWifiChangeNative(sWlan0Index, sSignificantWifiChangeCmdId);
2262                    sSignificantWifiChangeCmdId = 0;
2263                    sSignificantWifiChangeHandler = null;
2264                }
2265            }
2266        }
2267    }
2268
2269    // Callback from native
2270    private static void onSignificantWifiChange(int id, ScanResult[] results) {
2271        SignificantWifiChangeEventHandler handler = sSignificantWifiChangeHandler;
2272        if (handler != null) {
2273            handler.onChangesFound(results);
2274        } else {
2275            /* this can happen because of race conditions */
2276            Log.d(TAG, "Ignoring significant wifi change");
2277        }
2278    }
2279
2280    public WifiLinkLayerStats getWifiLinkLayerStats(String iface) {
2281        // TODO: use correct iface name to Index translation
2282        if (iface == null) return null;
2283        synchronized (sLock) {
2284            if (isHalStarted()) {
2285                return getWifiLinkLayerStatsNative(sWlan0Index);
2286            } else {
2287                return null;
2288            }
2289        }
2290    }
2291
2292    public void setWifiLinkLayerStats(String iface, int enable) {
2293        if (iface == null) return;
2294        synchronized (sLock) {
2295            if (isHalStarted()) {
2296                setWifiLinkLayerStatsNative(sWlan0Index, enable);
2297            }
2298        }
2299    }
2300
2301    public static native int getSupportedFeatureSetNative(int iface);
2302    public int getSupportedFeatureSet() {
2303        synchronized (sLock) {
2304            if (isHalStarted()) {
2305                return getSupportedFeatureSetNative(sWlan0Index);
2306            } else {
2307                Log.d(TAG, "Failing getSupportedFeatureset because HAL isn't started");
2308                return 0;
2309            }
2310        }
2311    }
2312
2313    /* Rtt related commands/events */
2314    public static interface RttEventHandler {
2315        void onRttResults(RttManager.RttResult[] result);
2316    }
2317
2318    private static RttEventHandler sRttEventHandler;
2319    private static int sRttCmdId;
2320
2321    // Callback from native
2322    private static void onRttResults(int id, RttManager.RttResult[] results) {
2323        RttEventHandler handler = sRttEventHandler;
2324        if (handler != null && id == sRttCmdId) {
2325            Log.d(TAG, "Received " + results.length + " rtt results");
2326            handler.onRttResults(results);
2327            sRttCmdId = 0;
2328        } else {
2329            Log.d(TAG, "RTT Received event for unknown cmd = " + id +
2330                    ", current id = " + sRttCmdId);
2331        }
2332    }
2333
2334    private static native boolean requestRangeNative(
2335            int iface, int id, RttManager.RttParams[] params);
2336    private static native boolean cancelRangeRequestNative(
2337            int iface, int id, RttManager.RttParams[] params);
2338
2339    public boolean requestRtt(
2340            RttManager.RttParams[] params, RttEventHandler handler) {
2341        synchronized (sLock) {
2342            if (isHalStarted()) {
2343                if (sRttCmdId != 0) {
2344                    Log.v("TAG", "Last one is still under measurement!");
2345                    return false;
2346                } else {
2347                    sRttCmdId = getNewCmdIdLocked();
2348                }
2349                sRttEventHandler = handler;
2350                Log.v(TAG, "native issue RTT request");
2351                return requestRangeNative(sWlan0Index, sRttCmdId, params);
2352            } else {
2353                return false;
2354            }
2355        }
2356    }
2357
2358    public boolean cancelRtt(RttManager.RttParams[] params) {
2359        synchronized (sLock) {
2360            if (isHalStarted()) {
2361                if (sRttCmdId == 0) {
2362                    return false;
2363                }
2364
2365                sRttCmdId = 0;
2366
2367                if (cancelRangeRequestNative(sWlan0Index, sRttCmdId, params)) {
2368                    sRttEventHandler = null;
2369                    Log.v(TAG, "RTT cancel Request Successfully");
2370                    return true;
2371                } else {
2372                    Log.e(TAG, "RTT cancel Request failed");
2373                    return false;
2374                }
2375            } else {
2376                return false;
2377            }
2378        }
2379    }
2380
2381    private static int sRttResponderCmdId = 0;
2382
2383    private static native ResponderConfig enableRttResponderNative(int iface, int commandId,
2384            int timeoutSeconds, WifiChannelInfo channelHint);
2385    /**
2386     * Enable RTT responder role on the device. Returns {@link ResponderConfig} if the responder
2387     * role is successfully enabled, {@code null} otherwise.
2388     */
2389    @Nullable
2390    public ResponderConfig enableRttResponder(int timeoutSeconds) {
2391        synchronized (sLock) {
2392            if (!isHalStarted()) return null;
2393            if (sRttResponderCmdId != 0) {
2394                if (DBG) Log.e(mTAG, "responder mode already enabled - this shouldn't happen");
2395                return null;
2396            }
2397            int id = getNewCmdIdLocked();
2398            ResponderConfig config = enableRttResponderNative(
2399                    sWlan0Index, id, timeoutSeconds, null);
2400            if (config != null) sRttResponderCmdId = id;
2401            if (DBG) Log.d(TAG, "enabling rtt " + (config != null));
2402            return config;
2403        }
2404    }
2405
2406    private static native boolean disableRttResponderNative(int iface, int commandId);
2407    /**
2408     * Disable RTT responder role. Returns {@code true} if responder role is successfully disabled,
2409     * {@code false} otherwise.
2410     */
2411    public boolean disableRttResponder() {
2412        synchronized (sLock) {
2413            if (!isHalStarted()) return false;
2414            if (sRttResponderCmdId == 0) {
2415                Log.e(mTAG, "responder role not enabled yet");
2416                return true;
2417            }
2418            sRttResponderCmdId = 0;
2419            return disableRttResponderNative(sWlan0Index, sRttResponderCmdId);
2420        }
2421    }
2422
2423    private static native boolean setScanningMacOuiNative(int iface, byte[] oui);
2424
2425    public boolean setScanningMacOui(byte[] oui) {
2426        synchronized (sLock) {
2427            if (isHalStarted()) {
2428                return setScanningMacOuiNative(sWlan0Index, oui);
2429            } else {
2430                return false;
2431            }
2432        }
2433    }
2434
2435    private static native int[] getChannelsForBandNative(
2436            int iface, int band);
2437
2438    public int [] getChannelsForBand(int band) {
2439        synchronized (sLock) {
2440            if (isHalStarted()) {
2441                return getChannelsForBandNative(sWlan0Index, band);
2442            } else {
2443                return null;
2444            }
2445        }
2446    }
2447
2448    private static native boolean isGetChannelsForBandSupportedNative();
2449    public boolean isGetChannelsForBandSupported(){
2450        synchronized (sLock) {
2451            if (isHalStarted()) {
2452                return isGetChannelsForBandSupportedNative();
2453            } else {
2454                return false;
2455            }
2456        }
2457    }
2458
2459    private static native boolean setDfsFlagNative(int iface, boolean dfsOn);
2460    public boolean setDfsFlag(boolean dfsOn) {
2461        synchronized (sLock) {
2462            if (isHalStarted()) {
2463                return setDfsFlagNative(sWlan0Index, dfsOn);
2464            } else {
2465                return false;
2466            }
2467        }
2468    }
2469
2470    private static native boolean setInterfaceUpNative(boolean up);
2471    public boolean setInterfaceUp(boolean up) {
2472        synchronized (sLock) {
2473            if (isHalStarted()) {
2474                return setInterfaceUpNative(up);
2475            } else {
2476                return false;
2477            }
2478        }
2479    }
2480
2481    private static native RttManager.RttCapabilities getRttCapabilitiesNative(int iface);
2482    public RttManager.RttCapabilities getRttCapabilities() {
2483        synchronized (sLock) {
2484            if (isHalStarted()) {
2485                return getRttCapabilitiesNative(sWlan0Index);
2486            } else {
2487                return null;
2488            }
2489        }
2490    }
2491
2492    public static final class PacketFilterCapabilities {
2493        /**
2494         * Version of APF instruction set supported for packet filtering.  0 indicates no support for
2495         * packet filtering using APF programs.
2496         */
2497        public int apfVersionSupported;
2498
2499        /**
2500         * Maximum size of APF program allowed.
2501         */
2502        public int maximumApfProgramSize;
2503    }
2504    private static native PacketFilterCapabilities getPacketFilterCapabilitiesNative(int iface);
2505    public PacketFilterCapabilities getPacketFilterCapabilities() {
2506        synchronized (sLock) {
2507            if (isHalStarted()) {
2508                return getPacketFilterCapabilitiesNative(sWlan0Index);
2509            } else {
2510                return null;
2511            }
2512        }
2513    }
2514
2515    private static native boolean installPacketFilterNative(int iface, byte[] filter);
2516    public boolean installPacketFilter(byte[] filter) {
2517        synchronized (sLock) {
2518            if (isHalStarted()) {
2519                return installPacketFilterNative(sWlan0Index, filter);
2520            } else {
2521                return false;
2522            }
2523        }
2524    }
2525
2526    private static native boolean setCountryCodeHalNative(int iface, String CountryCode);
2527    public boolean setCountryCodeHal(String CountryCode) {
2528        synchronized (sLock) {
2529            if (isHalStarted()) {
2530                return setCountryCodeHalNative(sWlan0Index, CountryCode);
2531            } else {
2532                return false;
2533            }
2534        }
2535    }
2536
2537    /* Rtt related commands/events */
2538    public abstract class TdlsEventHandler {
2539        abstract public void onTdlsStatus(String macAddr, int status, int reason);
2540    }
2541
2542    private static TdlsEventHandler sTdlsEventHandler;
2543
2544    private static native boolean enableDisableTdlsNative(int iface, boolean enable,
2545            String macAddr);
2546    public boolean enableDisableTdls(boolean enable, String macAdd, TdlsEventHandler tdlsCallBack) {
2547        synchronized (sLock) {
2548            sTdlsEventHandler = tdlsCallBack;
2549            return enableDisableTdlsNative(sWlan0Index, enable, macAdd);
2550        }
2551    }
2552
2553    // Once TDLS per mac and event feature is implemented, this class definition should be
2554    // moved to the right place, like WifiManager etc
2555    public static class TdlsStatus {
2556        int channel;
2557        int global_operating_class;
2558        int state;
2559        int reason;
2560    }
2561    private static native TdlsStatus getTdlsStatusNative(int iface, String macAddr);
2562    public TdlsStatus getTdlsStatus(String macAdd) {
2563        synchronized (sLock) {
2564            if (isHalStarted()) {
2565                return getTdlsStatusNative(sWlan0Index, macAdd);
2566            } else {
2567                return null;
2568            }
2569        }
2570    }
2571
2572    //ToFix: Once TDLS per mac and event feature is implemented, this class definition should be
2573    // moved to the right place, like WifiStateMachine etc
2574    public static class TdlsCapabilities {
2575        /* Maximum TDLS session number can be supported by the Firmware and hardware */
2576        int maxConcurrentTdlsSessionNumber;
2577        boolean isGlobalTdlsSupported;
2578        boolean isPerMacTdlsSupported;
2579        boolean isOffChannelTdlsSupported;
2580    }
2581
2582
2583
2584    private static native TdlsCapabilities getTdlsCapabilitiesNative(int iface);
2585    public TdlsCapabilities getTdlsCapabilities () {
2586        synchronized (sLock) {
2587            if (isHalStarted()) {
2588                return getTdlsCapabilitiesNative(sWlan0Index);
2589            } else {
2590                return null;
2591            }
2592        }
2593    }
2594
2595    private static boolean onTdlsStatus(String macAddr, int status, int reason) {
2596        TdlsEventHandler handler = sTdlsEventHandler;
2597        if (handler == null) {
2598            return false;
2599        } else {
2600            handler.onTdlsStatus(macAddr, status, reason);
2601            return true;
2602        }
2603    }
2604
2605    //---------------------------------------------------------------------------------
2606
2607    /* Wifi Logger commands/events */
2608
2609    public static interface WifiLoggerEventHandler {
2610        void onRingBufferData(RingBufferStatus status, byte[] buffer);
2611        void onWifiAlert(int errorCode, byte[] buffer);
2612    }
2613
2614    private static WifiLoggerEventHandler sWifiLoggerEventHandler = null;
2615
2616    // Callback from native
2617    private static void onRingBufferData(RingBufferStatus status, byte[] buffer) {
2618        WifiLoggerEventHandler handler = sWifiLoggerEventHandler;
2619        if (handler != null)
2620            handler.onRingBufferData(status, buffer);
2621    }
2622
2623    // Callback from native
2624    private static void onWifiAlert(byte[] buffer, int errorCode) {
2625        WifiLoggerEventHandler handler = sWifiLoggerEventHandler;
2626        if (handler != null)
2627            handler.onWifiAlert(errorCode, buffer);
2628    }
2629
2630    private static int sLogCmdId = -1;
2631    private static native boolean setLoggingEventHandlerNative(int iface, int id);
2632    public boolean setLoggingEventHandler(WifiLoggerEventHandler handler) {
2633        synchronized (sLock) {
2634            if (isHalStarted()) {
2635                int oldId =  sLogCmdId;
2636                sLogCmdId = getNewCmdIdLocked();
2637                if (!setLoggingEventHandlerNative(sWlan0Index, sLogCmdId)) {
2638                    sLogCmdId = oldId;
2639                    return false;
2640                }
2641                sWifiLoggerEventHandler = handler;
2642                return true;
2643            } else {
2644                return false;
2645            }
2646        }
2647    }
2648
2649    private static native boolean startLoggingRingBufferNative(int iface, int verboseLevel,
2650            int flags, int minIntervalSec ,int minDataSize, String ringName);
2651    public boolean startLoggingRingBuffer(int verboseLevel, int flags, int maxInterval,
2652            int minDataSize, String ringName){
2653        synchronized (sLock) {
2654            if (isHalStarted()) {
2655                return startLoggingRingBufferNative(sWlan0Index, verboseLevel, flags, maxInterval,
2656                        minDataSize, ringName);
2657            } else {
2658                return false;
2659            }
2660        }
2661    }
2662
2663    private static native int getSupportedLoggerFeatureSetNative(int iface);
2664    public int getSupportedLoggerFeatureSet() {
2665        synchronized (sLock) {
2666            if (isHalStarted()) {
2667                return getSupportedLoggerFeatureSetNative(sWlan0Index);
2668            } else {
2669                return 0;
2670            }
2671        }
2672    }
2673
2674    private static native boolean resetLogHandlerNative(int iface, int id);
2675    public boolean resetLogHandler() {
2676        synchronized (sLock) {
2677            if (isHalStarted()) {
2678                if (sLogCmdId == -1) {
2679                    Log.e(TAG,"Can not reset handler Before set any handler");
2680                    return false;
2681                }
2682                sWifiLoggerEventHandler = null;
2683                if (resetLogHandlerNative(sWlan0Index, sLogCmdId)) {
2684                    sLogCmdId = -1;
2685                    return true;
2686                } else {
2687                    return false;
2688                }
2689            } else {
2690                return false;
2691            }
2692        }
2693    }
2694
2695    private static native String getDriverVersionNative(int iface);
2696    public String getDriverVersion() {
2697        synchronized (sLock) {
2698            if (isHalStarted()) {
2699                return getDriverVersionNative(sWlan0Index);
2700            } else {
2701                return "";
2702            }
2703        }
2704    }
2705
2706
2707    private static native String getFirmwareVersionNative(int iface);
2708    public String getFirmwareVersion() {
2709        synchronized (sLock) {
2710            if (isHalStarted()) {
2711                return getFirmwareVersionNative(sWlan0Index);
2712            } else {
2713                return "";
2714            }
2715        }
2716    }
2717
2718    public static class RingBufferStatus{
2719        String name;
2720        int flag;
2721        int ringBufferId;
2722        int ringBufferByteSize;
2723        int verboseLevel;
2724        int writtenBytes;
2725        int readBytes;
2726        int writtenRecords;
2727
2728        @Override
2729        public String toString() {
2730            return "name: " + name + " flag: " + flag + " ringBufferId: " + ringBufferId +
2731                    " ringBufferByteSize: " +ringBufferByteSize + " verboseLevel: " +verboseLevel +
2732                    " writtenBytes: " + writtenBytes + " readBytes: " + readBytes +
2733                    " writtenRecords: " + writtenRecords;
2734        }
2735    }
2736
2737    private static native RingBufferStatus[] getRingBufferStatusNative(int iface);
2738    public RingBufferStatus[] getRingBufferStatus() {
2739        synchronized (sLock) {
2740            if (isHalStarted()) {
2741                return getRingBufferStatusNative(sWlan0Index);
2742            } else {
2743                return null;
2744            }
2745        }
2746    }
2747
2748    private static native boolean getRingBufferDataNative(int iface, String ringName);
2749    public boolean getRingBufferData(String ringName) {
2750        synchronized (sLock) {
2751            if (isHalStarted()) {
2752                return getRingBufferDataNative(sWlan0Index, ringName);
2753            } else {
2754                return false;
2755            }
2756        }
2757    }
2758
2759    private static byte[] mFwMemoryDump;
2760    // Callback from native
2761    private static void onWifiFwMemoryAvailable(byte[] buffer) {
2762        mFwMemoryDump = buffer;
2763        if (DBG) {
2764            Log.d(TAG, "onWifiFwMemoryAvailable is called and buffer length is: " +
2765                    (buffer == null ? 0 :  buffer.length));
2766        }
2767    }
2768
2769    private static native boolean getFwMemoryDumpNative(int iface);
2770    public byte[] getFwMemoryDump() {
2771        synchronized (sLock) {
2772            if (isHalStarted()) {
2773                if(getFwMemoryDumpNative(sWlan0Index)) {
2774                    byte[] fwMemoryDump = mFwMemoryDump;
2775                    mFwMemoryDump = null;
2776                    return fwMemoryDump;
2777                } else {
2778                    return null;
2779                }
2780            }
2781            return null;
2782        }
2783    }
2784
2785    //---------------------------------------------------------------------------------
2786    /* Configure ePNO/PNO */
2787    private static PnoEventHandler sPnoEventHandler;
2788    private static int sPnoCmdId = 0;
2789
2790    private static native boolean setPnoListNative(int iface, int id, PnoSettings settings);
2791
2792    /**
2793     * Set the PNO settings & the network list in HAL to start PNO.
2794     * @param settings PNO settings and network list.
2795     * @param eventHandler Handler to receive notifications back during PNO scan.
2796     * @return true if success, false otherwise
2797     */
2798    public boolean setPnoList(PnoSettings settings, PnoEventHandler eventHandler) {
2799        Log.e(TAG, "setPnoList cmd " + sPnoCmdId);
2800
2801        synchronized (sLock) {
2802            if (isHalStarted()) {
2803                sPnoCmdId = getNewCmdIdLocked();
2804                sPnoEventHandler = eventHandler;
2805                if (setPnoListNative(sWlan0Index, sPnoCmdId, settings)) {
2806                    return true;
2807                }
2808            }
2809            sPnoEventHandler = null;
2810            return false;
2811        }
2812    }
2813
2814    /**
2815     * Set the PNO network list in HAL to start PNO.
2816     * @param list PNO network list.
2817     * @param eventHandler Handler to receive notifications back during PNO scan.
2818     * @return true if success, false otherwise
2819     */
2820    public boolean setPnoList(PnoNetwork[] list, PnoEventHandler eventHandler) {
2821        PnoSettings settings = new PnoSettings();
2822        settings.networkList = list;
2823        return setPnoList(settings, eventHandler);
2824    }
2825
2826    private static native boolean resetPnoListNative(int iface, int id);
2827
2828    /**
2829     * Reset the PNO settings in HAL to stop PNO.
2830     * @return true if success, false otherwise
2831     */
2832    public boolean resetPnoList() {
2833        Log.e(TAG, "resetPnoList cmd " + sPnoCmdId);
2834
2835        synchronized (sLock) {
2836            if (isHalStarted()) {
2837                sPnoCmdId = getNewCmdIdLocked();
2838                sPnoEventHandler = null;
2839                if (resetPnoListNative(sWlan0Index, sPnoCmdId)) {
2840                    return true;
2841                }
2842            }
2843            return false;
2844        }
2845    }
2846
2847    // Callback from native
2848    private static void onPnoNetworkFound(int id, ScanResult[] results, int[] beaconCaps) {
2849        if (results == null) {
2850            Log.e(TAG, "onPnoNetworkFound null results");
2851            return;
2852
2853        }
2854        Log.d(TAG, "WifiNative.onPnoNetworkFound result " + results.length);
2855
2856        PnoEventHandler handler = sPnoEventHandler;
2857        if (sPnoCmdId != 0 && handler != null) {
2858            for (int i=0; i<results.length; i++) {
2859                Log.e(TAG, "onPnoNetworkFound SSID " + results[i].SSID
2860                        + " " + results[i].level + " " + results[i].frequency);
2861
2862                populateScanResult(results[i], beaconCaps[i], "onPnoNetworkFound ");
2863                results[i].wifiSsid = WifiSsid.createFromAsciiEncoded(results[i].SSID);
2864            }
2865
2866            handler.onPnoNetworkFound(results);
2867        } else {
2868            /* this can happen because of race conditions */
2869            Log.d(TAG, "Ignoring Pno Network found event");
2870        }
2871    }
2872
2873    private native static boolean setBssidBlacklistNative(int iface, int id,
2874                                              String list[]);
2875
2876    public boolean setBssidBlacklist(String list[]) {
2877        int size = 0;
2878        if (list != null) {
2879            size = list.length;
2880        }
2881        Log.e(TAG, "setBssidBlacklist cmd " + sPnoCmdId + " size " + size);
2882
2883        synchronized (sLock) {
2884            if (isHalStarted()) {
2885                sPnoCmdId = getNewCmdIdLocked();
2886                return setBssidBlacklistNative(sWlan0Index, sPnoCmdId, list);
2887            } else {
2888                return false;
2889            }
2890        }
2891    }
2892
2893    private native static int startSendingOffloadedPacketNative(int iface, int idx,
2894                                    byte[] srcMac, byte[] dstMac, byte[] pktData, int period);
2895
2896    public int
2897    startSendingOffloadedPacket(int slot, KeepalivePacketData keepAlivePacket, int period) {
2898        Log.d(TAG, "startSendingOffloadedPacket slot=" + slot + " period=" + period);
2899
2900        String[] macAddrStr = getMacAddress().split(":");
2901        byte[] srcMac = new byte[6];
2902        for(int i = 0; i < 6; i++) {
2903            Integer hexVal = Integer.parseInt(macAddrStr[i], 16);
2904            srcMac[i] = hexVal.byteValue();
2905        }
2906        synchronized (sLock) {
2907            if (isHalStarted()) {
2908                return startSendingOffloadedPacketNative(sWlan0Index, slot, srcMac,
2909                        keepAlivePacket.dstMac, keepAlivePacket.data, period);
2910            } else {
2911                return -1;
2912            }
2913        }
2914    }
2915
2916    private native static int stopSendingOffloadedPacketNative(int iface, int idx);
2917
2918    public int
2919    stopSendingOffloadedPacket(int slot) {
2920        Log.d(TAG, "stopSendingOffloadedPacket " + slot);
2921        synchronized (sLock) {
2922            if (isHalStarted()) {
2923                return stopSendingOffloadedPacketNative(sWlan0Index, slot);
2924            } else {
2925                return -1;
2926            }
2927        }
2928    }
2929
2930    public static interface WifiRssiEventHandler {
2931        void onRssiThresholdBreached(byte curRssi);
2932    }
2933
2934    private static WifiRssiEventHandler sWifiRssiEventHandler;
2935
2936    // Callback from native
2937    private static void onRssiThresholdBreached(int id, byte curRssi) {
2938        WifiRssiEventHandler handler = sWifiRssiEventHandler;
2939        if (handler != null) {
2940            handler.onRssiThresholdBreached(curRssi);
2941        }
2942    }
2943
2944    private native static int startRssiMonitoringNative(int iface, int id,
2945                                        byte maxRssi, byte minRssi);
2946
2947    private static int sRssiMonitorCmdId = 0;
2948
2949    public int startRssiMonitoring(byte maxRssi, byte minRssi,
2950                                                WifiRssiEventHandler rssiEventHandler) {
2951        Log.d(TAG, "startRssiMonitoring: maxRssi=" + maxRssi + " minRssi=" + minRssi);
2952        synchronized (sLock) {
2953            sWifiRssiEventHandler = rssiEventHandler;
2954            if (isHalStarted()) {
2955                if (sRssiMonitorCmdId != 0) {
2956                    stopRssiMonitoring();
2957                }
2958
2959                sRssiMonitorCmdId = getNewCmdIdLocked();
2960                Log.d(TAG, "sRssiMonitorCmdId = " + sRssiMonitorCmdId);
2961                int ret = startRssiMonitoringNative(sWlan0Index, sRssiMonitorCmdId,
2962                        maxRssi, minRssi);
2963                if (ret != 0) { // if not success
2964                    sRssiMonitorCmdId = 0;
2965                }
2966                return ret;
2967            } else {
2968                return -1;
2969            }
2970        }
2971    }
2972
2973    private native static int stopRssiMonitoringNative(int iface, int idx);
2974
2975    public int stopRssiMonitoring() {
2976        Log.d(TAG, "stopRssiMonitoring, cmdId " + sRssiMonitorCmdId);
2977        synchronized (sLock) {
2978            if (isHalStarted()) {
2979                int ret = 0;
2980                if (sRssiMonitorCmdId != 0) {
2981                    ret = stopRssiMonitoringNative(sWlan0Index, sRssiMonitorCmdId);
2982                }
2983                sRssiMonitorCmdId = 0;
2984                return ret;
2985            } else {
2986                return -1;
2987            }
2988        }
2989    }
2990
2991    private static native WifiWakeReasonAndCounts getWlanWakeReasonCountNative(int iface);
2992
2993    /**
2994     * Fetch the host wakeup reasons stats from wlan driver.
2995     * @return the |WifiWakeReasonAndCounts| object retrieved from the wlan driver.
2996     */
2997    public WifiWakeReasonAndCounts getWlanWakeReasonCount() {
2998        Log.d(TAG, "getWlanWakeReasonCount " + sWlan0Index);
2999        synchronized (sLock) {
3000            if (isHalStarted()) {
3001                return getWlanWakeReasonCountNative(sWlan0Index);
3002            } else {
3003                return null;
3004            }
3005        }
3006    }
3007
3008    private static native int configureNeighborDiscoveryOffload(int iface, boolean enabled);
3009
3010    public boolean configureNeighborDiscoveryOffload(boolean enabled) {
3011        final String logMsg =  "configureNeighborDiscoveryOffload(" + enabled + ")";
3012        Log.d(mTAG, logMsg);
3013        synchronized (sLock) {
3014            if (isHalStarted()) {
3015                final int ret = configureNeighborDiscoveryOffload(sWlan0Index, enabled);
3016                if (ret != 0) {
3017                    Log.d(mTAG, logMsg + " returned: " + ret);
3018                }
3019                return (ret == 0);
3020            }
3021        }
3022        return false;
3023    }
3024}
3025