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