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