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