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