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