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