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