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