WifiNative.java revision 1ab129e587d334a144a0bca5323c27985397a403
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, infoElements, anqpLines);
678                            results.add(scan);
679                        } catch (IllegalArgumentException iae) {
680                            Log.d(TAG, "Failed to parse information elements: " + iae);
681                        }
682                    }
683                    bssid = null;
684                    level = 0;
685                    freq = 0;
686                    tsf = 0;
687                    flags = "";
688                    wifiSsid = null;
689                    infoElementsStr = null;
690                    anqpLines = null;
691                }
692            }
693        }
694        return results;
695    }
696
697    /**
698     * Format of result:
699     * id=1016
700     * bssid=00:03:7f:40:84:10
701     * freq=2462
702     * beacon_int=200
703     * capabilities=0x0431
704     * qual=0
705     * noise=0
706     * level=-46
707     * tsf=0000002669008476
708     * age=5
709     * ie=00105143412d485332302d52322d54455354010882848b960c12182403010b0706555...
710     * flags=[WPA2-EAP-CCMP][ESS][P2P][HS20]
711     * ssid=QCA-HS20-R2-TEST
712     * p2p_device_name=
713     * p2p_config_methods=0x0SET_NE
714     * anqp_venue_name=02083d656e6757692d466920416c6c69616e63650a3239383920436f...
715     * anqp_network_auth_type=010000
716     * anqp_roaming_consortium=03506f9a05001bc504bd
717     * anqp_ip_addr_type_availability=0c
718     * anqp_nai_realm=0200300000246d61696c2e6578616d706c652e636f6d3b636973636f2...
719     * anqp_3gpp=000600040132f465
720     * anqp_domain_name=0b65786d61706c652e636f6d
721     * hs20_operator_friendly_name=11656e6757692d466920416c6c69616e63650e636869...
722     * hs20_wan_metrics=01c40900008001000000000a00
723     * hs20_connection_capability=0100000006140001061600000650000106bb010106bb0...
724     * hs20_osu_providers_list=0b5143412d4f53552d425353010901310015656e6757692d...
725     */
726    public String scanResult(String bssid) {
727        return doStringCommand("BSS " + bssid);
728    }
729
730    public boolean startDriver() {
731        return doBooleanCommand("DRIVER START");
732    }
733
734    public boolean stopDriver() {
735        return doBooleanCommand("DRIVER STOP");
736    }
737
738
739    /**
740     * Start filtering out Multicast V4 packets
741     * @return {@code true} if the operation succeeded, {@code false} otherwise
742     *
743     * Multicast filtering rules work as follows:
744     *
745     * The driver can filter multicast (v4 and/or v6) and broadcast packets when in
746     * a power optimized mode (typically when screen goes off).
747     *
748     * In order to prevent the driver from filtering the multicast/broadcast packets, we have to
749     * add a DRIVER RXFILTER-ADD rule followed by DRIVER RXFILTER-START to make the rule effective
750     *
751     * DRIVER RXFILTER-ADD Num
752     *   where Num = 0 - Unicast, 1 - Broadcast, 2 - Mutil4 or 3 - Multi6
753     *
754     * and DRIVER RXFILTER-START
755     * In order to stop the usage of these rules, we do
756     *
757     * DRIVER RXFILTER-STOP
758     * DRIVER RXFILTER-REMOVE Num
759     *   where Num is as described for RXFILTER-ADD
760     *
761     * The  SETSUSPENDOPT driver command overrides the filtering rules
762     */
763    public boolean startFilteringMulticastV4Packets() {
764        return doBooleanCommand("DRIVER RXFILTER-STOP")
765            && doBooleanCommand("DRIVER RXFILTER-REMOVE 2")
766            && doBooleanCommand("DRIVER RXFILTER-START");
767    }
768
769    /**
770     * Stop filtering out Multicast V4 packets.
771     * @return {@code true} if the operation succeeded, {@code false} otherwise
772     */
773    public boolean stopFilteringMulticastV4Packets() {
774        return doBooleanCommand("DRIVER RXFILTER-STOP")
775            && doBooleanCommand("DRIVER RXFILTER-ADD 2")
776            && doBooleanCommand("DRIVER RXFILTER-START");
777    }
778
779    /**
780     * Start filtering out Multicast V6 packets
781     * @return {@code true} if the operation succeeded, {@code false} otherwise
782     */
783    public boolean startFilteringMulticastV6Packets() {
784        return doBooleanCommand("DRIVER RXFILTER-STOP")
785            && doBooleanCommand("DRIVER RXFILTER-REMOVE 3")
786            && doBooleanCommand("DRIVER RXFILTER-START");
787    }
788
789    /**
790     * Stop filtering out Multicast V6 packets.
791     * @return {@code true} if the operation succeeded, {@code false} otherwise
792     */
793    public boolean stopFilteringMulticastV6Packets() {
794        return doBooleanCommand("DRIVER RXFILTER-STOP")
795            && doBooleanCommand("DRIVER RXFILTER-ADD 3")
796            && doBooleanCommand("DRIVER RXFILTER-START");
797    }
798
799    /**
800     * Set the operational frequency band
801     * @param band One of
802     *     {@link WifiManager#WIFI_FREQUENCY_BAND_AUTO},
803     *     {@link WifiManager#WIFI_FREQUENCY_BAND_5GHZ},
804     *     {@link WifiManager#WIFI_FREQUENCY_BAND_2GHZ},
805     * @return {@code true} if the operation succeeded, {@code false} otherwise
806     */
807    public boolean setBand(int band) {
808        String bandstr;
809
810        if (band == WifiManager.WIFI_FREQUENCY_BAND_5GHZ)
811            bandstr = "5G";
812        else if (band == WifiManager.WIFI_FREQUENCY_BAND_2GHZ)
813            bandstr = "2G";
814        else
815            bandstr = "AUTO";
816        return doBooleanCommand("SET SETBAND " + bandstr);
817    }
818
819    public static final int BLUETOOTH_COEXISTENCE_MODE_ENABLED     = 0;
820    public static final int BLUETOOTH_COEXISTENCE_MODE_DISABLED    = 1;
821    public static final int BLUETOOTH_COEXISTENCE_MODE_SENSE       = 2;
822    /**
823      * Sets the bluetooth coexistence mode.
824      *
825      * @param mode One of {@link #BLUETOOTH_COEXISTENCE_MODE_DISABLED},
826      *            {@link #BLUETOOTH_COEXISTENCE_MODE_ENABLED}, or
827      *            {@link #BLUETOOTH_COEXISTENCE_MODE_SENSE}.
828      * @return Whether the mode was successfully set.
829      */
830    public boolean setBluetoothCoexistenceMode(int mode) {
831        return doBooleanCommand("DRIVER BTCOEXMODE " + mode);
832    }
833
834    /**
835     * Enable or disable Bluetooth coexistence scan mode. When this mode is on,
836     * some of the low-level scan parameters used by the driver are changed to
837     * reduce interference with A2DP streaming.
838     *
839     * @param isSet whether to enable or disable this mode
840     * @return {@code true} if the command succeeded, {@code false} otherwise.
841     */
842    public boolean setBluetoothCoexistenceScanMode(boolean setCoexScanMode) {
843        if (setCoexScanMode) {
844            return doBooleanCommand("DRIVER BTCOEXSCAN-START");
845        } else {
846            return doBooleanCommand("DRIVER BTCOEXSCAN-STOP");
847        }
848    }
849
850    public void enableSaveConfig() {
851        doBooleanCommand("SET update_config 1");
852    }
853
854    public boolean saveConfig() {
855        return doBooleanCommand("SAVE_CONFIG");
856    }
857
858    public boolean addToBlacklist(String bssid) {
859        if (TextUtils.isEmpty(bssid)) return false;
860        return doBooleanCommand("BLACKLIST " + bssid);
861    }
862
863    public boolean clearBlacklist() {
864        return doBooleanCommand("BLACKLIST clear");
865    }
866
867    public boolean setSuspendOptimizations(boolean enabled) {
868        if (enabled) {
869            return doBooleanCommand("DRIVER SETSUSPENDMODE 1");
870        } else {
871            return doBooleanCommand("DRIVER SETSUSPENDMODE 0");
872        }
873    }
874
875    public boolean setCountryCode(String countryCode) {
876        if (countryCode != null)
877            return doBooleanCommand("DRIVER COUNTRY " + countryCode.toUpperCase(Locale.ROOT));
878        else
879            return doBooleanCommand("DRIVER COUNTRY");
880    }
881
882
883    /**
884     * Object holding the network ID and the corresponding priority to be set before enabling/
885     * disabling PNO.
886     */
887    public static class PnoNetworkPriority {
888        public int networkId;
889        public int priority;
890
891        PnoNetworkPriority(int networkId, int priority) {
892            this.networkId = networkId;
893            this.priority = priority;
894        }
895
896        @Override
897        public String toString() {
898            StringBuilder sbuf = new StringBuilder();
899            sbuf.append(" Network ID=").append(this.networkId);
900            sbuf.append(" Priority=").append(this.priority);
901            return sbuf.toString();
902        }
903    }
904
905    //PNO Monitor
906    private class PnoMonitor {
907        private static final int MINIMUM_PNO_GAP = 5 * 1000;
908        private static final String ACTION_TOGGLE_PNO =
909            "com.android.server.Wifi.action.TOGGLE_PNO";
910        long mLastPnoChangeTimeStamp = -1L;
911        boolean mExpectedPnoState = false;
912        List<PnoNetworkPriority> mExpectedPnoNetworkPriorityList = null;
913        boolean mCurrentPnoState = false;;
914        boolean mWaitForTimer = false;
915        final Object mPnoLock = new Object();
916        private final AlarmManager mAlarmManager =
917                (AlarmManager) mContext.getSystemService(Context.ALARM_SERVICE);
918        private final PendingIntent mPnoIntent;
919
920        public PnoMonitor() {
921            Intent intent = new Intent(ACTION_TOGGLE_PNO, null);
922            intent.setPackage("android");
923            mPnoIntent = PendingIntent.getBroadcast(mContext, 0, intent, 0);
924
925            mContext.registerReceiver(
926                new BroadcastReceiver() {
927                    @Override
928                    public void onReceive(Context context, Intent intent) {
929                        synchronized(mPnoLock) {
930                            if (DBG) Log.d(mTAG, "PNO timer expire, PNO should change to " +
931                                    mExpectedPnoState);
932                            if (mCurrentPnoState != mExpectedPnoState) {
933                                if (DBG) Log.d(mTAG, "change PNO from " + mCurrentPnoState + " to "
934                                        + mExpectedPnoState);
935                                boolean ret = setPno(
936                                        mExpectedPnoState, mExpectedPnoNetworkPriorityList);
937                                if (!ret) {
938                                    Log.e(mTAG, "set PNO failure");
939                                }
940                            } else {
941                                if (DBG) Log.d(mTAG, "Do not change PNO since current is expected");
942                            }
943                            mWaitForTimer = false;
944                        }
945                    }
946                },
947                new IntentFilter(ACTION_TOGGLE_PNO));
948        }
949
950        /*  Enable/Disable PNO with updated network priorities.
951         *
952         * @param enable boolean indicating whether PNO is being enabled or disabled.
953         * @param pnoNetworkList list of networks with priorities to be set before PNO setting.
954         */
955        private boolean setPno(boolean enable, List<PnoNetworkPriority> pnoNetworkList) {
956            // TODO: Couple of cases yet to be handled:
957            // 1. What if the network priority update fails, should we bail out of PNO setting?
958            // 2. If PNO setting fails below, should we go back and revert this priority change?
959            if (pnoNetworkList != null) {
960                if (DBG) Log.i(mTAG, "Update priorities for PNO. Enable: " + enable);
961                for (PnoNetworkPriority pnoNetwork : pnoNetworkList) {
962                    // What if this fails? Should we bail out?
963                    boolean isSuccess = setNetworkVariable(pnoNetwork.networkId,
964                            WifiConfiguration.priorityVarName,
965                            Integer.toString(pnoNetwork.priority));
966                    if (DBG && !isSuccess) {
967                        Log.e(mTAG, "Update priority failed for :" + pnoNetwork.networkId);
968                    }
969                }
970            }
971            String cmd = enable ? "SET pno 1" : "SET pno 0";
972            boolean ret = doBooleanCommand(cmd);
973            mLastPnoChangeTimeStamp = System.currentTimeMillis();
974            if (ret) {
975                mCurrentPnoState = enable;
976            }
977            return ret;
978        }
979
980        public boolean enableBackgroundScan(
981                boolean enable,
982                List<PnoNetworkPriority> pnoNetworkList) {
983            synchronized(mPnoLock) {
984                if (mWaitForTimer) {
985                    //already has a timer
986                    mExpectedPnoState = enable;
987                    mExpectedPnoNetworkPriorityList = pnoNetworkList;
988                    if (DBG) Log.d(mTAG, "update expected PNO to " +  mExpectedPnoState);
989                } else {
990                    if (mCurrentPnoState == enable) {
991                        return true;
992                    }
993                    long timeDifference = System.currentTimeMillis() - mLastPnoChangeTimeStamp;
994                    if (timeDifference >= MINIMUM_PNO_GAP) {
995                        return setPno(enable, pnoNetworkList);
996                    } else {
997                        mExpectedPnoState = enable;
998                        mExpectedPnoNetworkPriorityList = pnoNetworkList;
999                        mWaitForTimer = true;
1000                        if (DBG) Log.d(mTAG, "start PNO timer with delay:" + timeDifference);
1001                        mAlarmManager.set(AlarmManager.RTC_WAKEUP,
1002                                System.currentTimeMillis() + timeDifference, mPnoIntent);
1003                    }
1004                }
1005                return true;
1006            }
1007        }
1008    }
1009
1010    public boolean enableBackgroundScan(
1011            boolean enable,
1012            List<PnoNetworkPriority> pnoNetworkList) {
1013        if (mPnoMonitor != null) {
1014            return mPnoMonitor.enableBackgroundScan(enable, pnoNetworkList);
1015        } else {
1016            return false;
1017        }
1018    }
1019
1020    public void enableAutoConnect(boolean enable) {
1021        if (enable) {
1022            doBooleanCommand("STA_AUTOCONNECT 1");
1023        } else {
1024            doBooleanCommand("STA_AUTOCONNECT 0");
1025        }
1026    }
1027
1028    public void setScanInterval(int scanInterval) {
1029        doBooleanCommand("SCAN_INTERVAL " + scanInterval);
1030    }
1031
1032    public void setHs20(boolean hs20) {
1033        if (hs20) {
1034            doBooleanCommand("SET HS20 1");
1035        } else {
1036            doBooleanCommand("SET HS20 0");
1037        }
1038    }
1039
1040    public void startTdls(String macAddr, boolean enable) {
1041        if (enable) {
1042            synchronized (sLock) {
1043                doBooleanCommand("TDLS_DISCOVER " + macAddr);
1044                doBooleanCommand("TDLS_SETUP " + macAddr);
1045            }
1046        } else {
1047            doBooleanCommand("TDLS_TEARDOWN " + macAddr);
1048        }
1049    }
1050
1051    /** Example output:
1052     * RSSI=-65
1053     * LINKSPEED=48
1054     * NOISE=9999
1055     * FREQUENCY=0
1056     */
1057    public String signalPoll() {
1058        return doStringCommandWithoutLogging("SIGNAL_POLL");
1059    }
1060
1061    /** Example outout:
1062     * TXGOOD=396
1063     * TXBAD=1
1064     */
1065    public String pktcntPoll() {
1066        return doStringCommand("PKTCNT_POLL");
1067    }
1068
1069    public void bssFlush() {
1070        doBooleanCommand("BSS_FLUSH 0");
1071    }
1072
1073    public boolean startWpsPbc(String bssid) {
1074        if (TextUtils.isEmpty(bssid)) {
1075            return doBooleanCommand("WPS_PBC");
1076        } else {
1077            return doBooleanCommand("WPS_PBC " + bssid);
1078        }
1079    }
1080
1081    public boolean startWpsPbc(String iface, String bssid) {
1082        synchronized (sLock) {
1083            if (TextUtils.isEmpty(bssid)) {
1084                return doBooleanCommandNative("IFNAME=" + iface + " WPS_PBC");
1085            } else {
1086                return doBooleanCommandNative("IFNAME=" + iface + " WPS_PBC " + bssid);
1087            }
1088        }
1089    }
1090
1091    public boolean startWpsPinKeypad(String pin) {
1092        if (TextUtils.isEmpty(pin)) return false;
1093        return doBooleanCommand("WPS_PIN any " + pin);
1094    }
1095
1096    public boolean startWpsPinKeypad(String iface, String pin) {
1097        if (TextUtils.isEmpty(pin)) return false;
1098        synchronized (sLock) {
1099            return doBooleanCommandNative("IFNAME=" + iface + " WPS_PIN any " + pin);
1100        }
1101    }
1102
1103
1104    public String startWpsPinDisplay(String bssid) {
1105        if (TextUtils.isEmpty(bssid)) {
1106            return doStringCommand("WPS_PIN any");
1107        } else {
1108            return doStringCommand("WPS_PIN " + bssid);
1109        }
1110    }
1111
1112    public String startWpsPinDisplay(String iface, String bssid) {
1113        synchronized (sLock) {
1114            if (TextUtils.isEmpty(bssid)) {
1115                return doStringCommandNative("IFNAME=" + iface + " WPS_PIN any");
1116            } else {
1117                return doStringCommandNative("IFNAME=" + iface + " WPS_PIN " + bssid);
1118            }
1119        }
1120    }
1121
1122    public boolean setExternalSim(boolean external) {
1123        String value = external ? "1" : "0";
1124        Log.d(TAG, "Setting external_sim to " + value);
1125        return doBooleanCommand("SET external_sim " + value);
1126    }
1127
1128    public boolean simAuthResponse(int id, String type, String response) {
1129        // with type = GSM-AUTH, UMTS-AUTH or UMTS-AUTS
1130        return doBooleanCommand("CTRL-RSP-SIM-" + id + ":" + type + response);
1131    }
1132
1133    public boolean simAuthFailedResponse(int id) {
1134        // should be used with type GSM-AUTH
1135        return doBooleanCommand("CTRL-RSP-SIM-" + id + ":GSM-FAIL");
1136    }
1137
1138    public boolean umtsAuthFailedResponse(int id) {
1139        // should be used with type UMTS-AUTH
1140        return doBooleanCommand("CTRL-RSP-SIM-" + id + ":UMTS-FAIL");
1141    }
1142
1143    public boolean simIdentityResponse(int id, String response) {
1144        return doBooleanCommand("CTRL-RSP-IDENTITY-" + id + ":" + response);
1145    }
1146
1147    /* Configures an access point connection */
1148    public boolean startWpsRegistrar(String bssid, String pin) {
1149        if (TextUtils.isEmpty(bssid) || TextUtils.isEmpty(pin)) return false;
1150        return doBooleanCommand("WPS_REG " + bssid + " " + pin);
1151    }
1152
1153    public boolean cancelWps() {
1154        return doBooleanCommand("WPS_CANCEL");
1155    }
1156
1157    public boolean setPersistentReconnect(boolean enabled) {
1158        int value = (enabled == true) ? 1 : 0;
1159        return doBooleanCommand("SET persistent_reconnect " + value);
1160    }
1161
1162    public boolean setDeviceName(String name) {
1163        return doBooleanCommand("SET device_name " + name);
1164    }
1165
1166    public boolean setDeviceType(String type) {
1167        return doBooleanCommand("SET device_type " + type);
1168    }
1169
1170    public boolean setConfigMethods(String cfg) {
1171        return doBooleanCommand("SET config_methods " + cfg);
1172    }
1173
1174    public boolean setManufacturer(String value) {
1175        return doBooleanCommand("SET manufacturer " + value);
1176    }
1177
1178    public boolean setModelName(String value) {
1179        return doBooleanCommand("SET model_name " + value);
1180    }
1181
1182    public boolean setModelNumber(String value) {
1183        return doBooleanCommand("SET model_number " + value);
1184    }
1185
1186    public boolean setSerialNumber(String value) {
1187        return doBooleanCommand("SET serial_number " + value);
1188    }
1189
1190    public boolean setP2pSsidPostfix(String postfix) {
1191        return doBooleanCommand("SET p2p_ssid_postfix " + postfix);
1192    }
1193
1194    public boolean setP2pGroupIdle(String iface, int time) {
1195        synchronized (sLock) {
1196            return doBooleanCommandNative("IFNAME=" + iface + " SET p2p_group_idle " + time);
1197        }
1198    }
1199
1200    public void setPowerSave(boolean enabled) {
1201        if (enabled) {
1202            doBooleanCommand("SET ps 1");
1203        } else {
1204            doBooleanCommand("SET ps 0");
1205        }
1206    }
1207
1208    public boolean setP2pPowerSave(String iface, boolean enabled) {
1209        synchronized (sLock) {
1210            if (enabled) {
1211                return doBooleanCommandNative("IFNAME=" + iface + " P2P_SET ps 1");
1212            } else {
1213                return doBooleanCommandNative("IFNAME=" + iface + " P2P_SET ps 0");
1214            }
1215        }
1216    }
1217
1218    public boolean setWfdEnable(boolean enable) {
1219        return doBooleanCommand("SET wifi_display " + (enable ? "1" : "0"));
1220    }
1221
1222    public boolean setWfdDeviceInfo(String hex) {
1223        return doBooleanCommand("WFD_SUBELEM_SET 0 " + hex);
1224    }
1225
1226    /**
1227     * "sta" prioritizes STA connection over P2P and "p2p" prioritizes
1228     * P2P connection over STA
1229     */
1230    public boolean setConcurrencyPriority(String s) {
1231        return doBooleanCommand("P2P_SET conc_pref " + s);
1232    }
1233
1234    public boolean p2pFind() {
1235        return doBooleanCommand("P2P_FIND");
1236    }
1237
1238    public boolean p2pFind(int timeout) {
1239        if (timeout <= 0) {
1240            return p2pFind();
1241        }
1242        return doBooleanCommand("P2P_FIND " + timeout);
1243    }
1244
1245    public boolean p2pStopFind() {
1246       return doBooleanCommand("P2P_STOP_FIND");
1247    }
1248
1249    public boolean p2pListen() {
1250        return doBooleanCommand("P2P_LISTEN");
1251    }
1252
1253    public boolean p2pListen(int timeout) {
1254        if (timeout <= 0) {
1255            return p2pListen();
1256        }
1257        return doBooleanCommand("P2P_LISTEN " + timeout);
1258    }
1259
1260    public boolean p2pExtListen(boolean enable, int period, int interval) {
1261        if (enable && interval < period) {
1262            return false;
1263        }
1264        return doBooleanCommand("P2P_EXT_LISTEN"
1265                    + (enable ? (" " + period + " " + interval) : ""));
1266    }
1267
1268    public boolean p2pSetChannel(int lc, int oc) {
1269        if (DBG) Log.d(mTAG, "p2pSetChannel: lc="+lc+", oc="+oc);
1270
1271        synchronized (sLock) {
1272            if (lc >=1 && lc <= 11) {
1273                if (!doBooleanCommand("P2P_SET listen_channel " + lc)) {
1274                    return false;
1275                }
1276            } else if (lc != 0) {
1277                return false;
1278            }
1279
1280            if (oc >= 1 && oc <= 165 ) {
1281                int freq = (oc <= 14 ? 2407 : 5000) + oc * 5;
1282                return doBooleanCommand("P2P_SET disallow_freq 1000-"
1283                        + (freq - 5) + "," + (freq + 5) + "-6000");
1284            } else if (oc == 0) {
1285                /* oc==0 disables "P2P_SET disallow_freq" (enables all freqs) */
1286                return doBooleanCommand("P2P_SET disallow_freq \"\"");
1287            }
1288        }
1289        return false;
1290    }
1291
1292    public boolean p2pFlush() {
1293        return doBooleanCommand("P2P_FLUSH");
1294    }
1295
1296    private static final int DEFAULT_GROUP_OWNER_INTENT     = 6;
1297    /* p2p_connect <peer device address> <pbc|pin|PIN#> [label|display|keypad]
1298        [persistent] [join|auth] [go_intent=<0..15>] [freq=<in MHz>] */
1299    public String p2pConnect(WifiP2pConfig config, boolean joinExistingGroup) {
1300        if (config == null) return null;
1301        List<String> args = new ArrayList<String>();
1302        WpsInfo wps = config.wps;
1303        args.add(config.deviceAddress);
1304
1305        switch (wps.setup) {
1306            case WpsInfo.PBC:
1307                args.add("pbc");
1308                break;
1309            case WpsInfo.DISPLAY:
1310                if (TextUtils.isEmpty(wps.pin)) {
1311                    args.add("pin");
1312                } else {
1313                    args.add(wps.pin);
1314                }
1315                args.add("display");
1316                break;
1317            case WpsInfo.KEYPAD:
1318                args.add(wps.pin);
1319                args.add("keypad");
1320                break;
1321            case WpsInfo.LABEL:
1322                args.add(wps.pin);
1323                args.add("label");
1324            default:
1325                break;
1326        }
1327
1328        if (config.netId == WifiP2pGroup.PERSISTENT_NET_ID) {
1329            args.add("persistent");
1330        }
1331
1332        if (joinExistingGroup) {
1333            args.add("join");
1334        } else {
1335            //TODO: This can be adapted based on device plugged in state and
1336            //device battery state
1337            int groupOwnerIntent = config.groupOwnerIntent;
1338            if (groupOwnerIntent < 0 || groupOwnerIntent > 15) {
1339                groupOwnerIntent = DEFAULT_GROUP_OWNER_INTENT;
1340            }
1341            args.add("go_intent=" + groupOwnerIntent);
1342        }
1343
1344        String command = "P2P_CONNECT ";
1345        for (String s : args) command += s + " ";
1346
1347        return doStringCommand(command);
1348    }
1349
1350    public boolean p2pCancelConnect() {
1351        return doBooleanCommand("P2P_CANCEL");
1352    }
1353
1354    public boolean p2pProvisionDiscovery(WifiP2pConfig config) {
1355        if (config == null) return false;
1356
1357        switch (config.wps.setup) {
1358            case WpsInfo.PBC:
1359                return doBooleanCommand("P2P_PROV_DISC " + config.deviceAddress + " pbc");
1360            case WpsInfo.DISPLAY:
1361                //We are doing display, so provision discovery is keypad
1362                return doBooleanCommand("P2P_PROV_DISC " + config.deviceAddress + " keypad");
1363            case WpsInfo.KEYPAD:
1364                //We are doing keypad, so provision discovery is display
1365                return doBooleanCommand("P2P_PROV_DISC " + config.deviceAddress + " display");
1366            default:
1367                break;
1368        }
1369        return false;
1370    }
1371
1372    public boolean p2pGroupAdd(boolean persistent) {
1373        if (persistent) {
1374            return doBooleanCommand("P2P_GROUP_ADD persistent");
1375        }
1376        return doBooleanCommand("P2P_GROUP_ADD");
1377    }
1378
1379    public boolean p2pGroupAdd(int netId) {
1380        return doBooleanCommand("P2P_GROUP_ADD persistent=" + netId);
1381    }
1382
1383    public boolean p2pGroupRemove(String iface) {
1384        if (TextUtils.isEmpty(iface)) return false;
1385        synchronized (sLock) {
1386            return doBooleanCommandNative("IFNAME=" + iface + " P2P_GROUP_REMOVE " + iface);
1387        }
1388    }
1389
1390    public boolean p2pReject(String deviceAddress) {
1391        return doBooleanCommand("P2P_REJECT " + deviceAddress);
1392    }
1393
1394    /* Invite a peer to a group */
1395    public boolean p2pInvite(WifiP2pGroup group, String deviceAddress) {
1396        if (TextUtils.isEmpty(deviceAddress)) return false;
1397
1398        if (group == null) {
1399            return doBooleanCommand("P2P_INVITE peer=" + deviceAddress);
1400        } else {
1401            return doBooleanCommand("P2P_INVITE group=" + group.getInterface()
1402                    + " peer=" + deviceAddress + " go_dev_addr=" + group.getOwner().deviceAddress);
1403        }
1404    }
1405
1406    /* Reinvoke a persistent connection */
1407    public boolean p2pReinvoke(int netId, String deviceAddress) {
1408        if (TextUtils.isEmpty(deviceAddress) || netId < 0) return false;
1409
1410        return doBooleanCommand("P2P_INVITE persistent=" + netId + " peer=" + deviceAddress);
1411    }
1412
1413    public String p2pGetSsid(String deviceAddress) {
1414        return p2pGetParam(deviceAddress, "oper_ssid");
1415    }
1416
1417    public String p2pGetDeviceAddress() {
1418        Log.d(TAG, "p2pGetDeviceAddress");
1419
1420        String status = null;
1421
1422        /* Explicitly calling the API without IFNAME= prefix to take care of the devices that
1423        don't have p2p0 interface. Supplicant seems to be returning the correct address anyway. */
1424
1425        synchronized (sLock) {
1426            status = doStringCommandNative("STATUS");
1427        }
1428
1429        String result = "";
1430        if (status != null) {
1431            String[] tokens = status.split("\n");
1432            for (String token : tokens) {
1433                if (token.startsWith("p2p_device_address=")) {
1434                    String[] nameValue = token.split("=");
1435                    if (nameValue.length != 2)
1436                        break;
1437                    result = nameValue[1];
1438                }
1439            }
1440        }
1441
1442        Log.d(TAG, "p2pGetDeviceAddress returning " + result);
1443        return result;
1444    }
1445
1446    public int getGroupCapability(String deviceAddress) {
1447        int gc = 0;
1448        if (TextUtils.isEmpty(deviceAddress)) return gc;
1449        String peerInfo = p2pPeer(deviceAddress);
1450        if (TextUtils.isEmpty(peerInfo)) return gc;
1451
1452        String[] tokens = peerInfo.split("\n");
1453        for (String token : tokens) {
1454            if (token.startsWith("group_capab=")) {
1455                String[] nameValue = token.split("=");
1456                if (nameValue.length != 2) break;
1457                try {
1458                    return Integer.decode(nameValue[1]);
1459                } catch(NumberFormatException e) {
1460                    return gc;
1461                }
1462            }
1463        }
1464        return gc;
1465    }
1466
1467    public String p2pPeer(String deviceAddress) {
1468        return doStringCommand("P2P_PEER " + deviceAddress);
1469    }
1470
1471    private String p2pGetParam(String deviceAddress, String key) {
1472        if (deviceAddress == null) return null;
1473
1474        String peerInfo = p2pPeer(deviceAddress);
1475        if (peerInfo == null) return null;
1476        String[] tokens= peerInfo.split("\n");
1477
1478        key += "=";
1479        for (String token : tokens) {
1480            if (token.startsWith(key)) {
1481                String[] nameValue = token.split("=");
1482                if (nameValue.length != 2) break;
1483                return nameValue[1];
1484            }
1485        }
1486        return null;
1487    }
1488
1489    public boolean p2pServiceAdd(WifiP2pServiceInfo servInfo) {
1490        /*
1491         * P2P_SERVICE_ADD bonjour <query hexdump> <RDATA hexdump>
1492         * P2P_SERVICE_ADD upnp <version hex> <service>
1493         *
1494         * e.g)
1495         * [Bonjour]
1496         * # IP Printing over TCP (PTR) (RDATA=MyPrinter._ipp._tcp.local.)
1497         * P2P_SERVICE_ADD bonjour 045f697070c00c000c01 094d795072696e746572c027
1498         * # IP Printing over TCP (TXT) (RDATA=txtvers=1,pdl=application/postscript)
1499         * P2P_SERVICE_ADD bonjour 096d797072696e746572045f697070c00c001001
1500         *  09747874766572733d311a70646c3d6170706c69636174696f6e2f706f7374736372797074
1501         *
1502         * [UPnP]
1503         * P2P_SERVICE_ADD upnp 10 uuid:6859dede-8574-59ab-9332-123456789012
1504         * P2P_SERVICE_ADD upnp 10 uuid:6859dede-8574-59ab-9332-123456789012::upnp:rootdevice
1505         * P2P_SERVICE_ADD upnp 10 uuid:6859dede-8574-59ab-9332-123456789012::urn:schemas-upnp
1506         * -org:device:InternetGatewayDevice:1
1507         * P2P_SERVICE_ADD upnp 10 uuid:6859dede-8574-59ab-9322-123456789012::urn:schemas-upnp
1508         * -org:service:ContentDirectory:2
1509         */
1510        synchronized (sLock) {
1511            for (String s : servInfo.getSupplicantQueryList()) {
1512                String command = "P2P_SERVICE_ADD";
1513                command += (" " + s);
1514                if (!doBooleanCommand(command)) {
1515                    return false;
1516                }
1517            }
1518        }
1519        return true;
1520    }
1521
1522    public boolean p2pServiceDel(WifiP2pServiceInfo servInfo) {
1523        /*
1524         * P2P_SERVICE_DEL bonjour <query hexdump>
1525         * P2P_SERVICE_DEL upnp <version hex> <service>
1526         */
1527        synchronized (sLock) {
1528            for (String s : servInfo.getSupplicantQueryList()) {
1529                String command = "P2P_SERVICE_DEL ";
1530
1531                String[] data = s.split(" ");
1532                if (data.length < 2) {
1533                    return false;
1534                }
1535                if ("upnp".equals(data[0])) {
1536                    command += s;
1537                } else if ("bonjour".equals(data[0])) {
1538                    command += data[0];
1539                    command += (" " + data[1]);
1540                } else {
1541                    return false;
1542                }
1543                if (!doBooleanCommand(command)) {
1544                    return false;
1545                }
1546            }
1547        }
1548        return true;
1549    }
1550
1551    public boolean p2pServiceFlush() {
1552        return doBooleanCommand("P2P_SERVICE_FLUSH");
1553    }
1554
1555    public String p2pServDiscReq(String addr, String query) {
1556        String command = "P2P_SERV_DISC_REQ";
1557        command += (" " + addr);
1558        command += (" " + query);
1559
1560        return doStringCommand(command);
1561    }
1562
1563    public boolean p2pServDiscCancelReq(String id) {
1564        return doBooleanCommand("P2P_SERV_DISC_CANCEL_REQ " + id);
1565    }
1566
1567    /* Set the current mode of miracast operation.
1568     *  0 = disabled
1569     *  1 = operating as source
1570     *  2 = operating as sink
1571     */
1572    public void setMiracastMode(int mode) {
1573        // Note: optional feature on the driver. It is ok for this to fail.
1574        doBooleanCommand("DRIVER MIRACAST " + mode);
1575    }
1576
1577    public boolean fetchAnqp(String bssid, String subtypes) {
1578        return doBooleanCommand("ANQP_GET " + bssid + " " + subtypes);
1579    }
1580
1581    /*
1582     * NFC-related calls
1583     */
1584    public String getNfcWpsConfigurationToken(int netId) {
1585        return doStringCommand("WPS_NFC_CONFIG_TOKEN WPS " + netId);
1586    }
1587
1588    public String getNfcHandoverRequest() {
1589        return doStringCommand("NFC_GET_HANDOVER_REQ NDEF P2P-CR");
1590    }
1591
1592    public String getNfcHandoverSelect() {
1593        return doStringCommand("NFC_GET_HANDOVER_SEL NDEF P2P-CR");
1594    }
1595
1596    public boolean initiatorReportNfcHandover(String selectMessage) {
1597        return doBooleanCommand("NFC_REPORT_HANDOVER INIT P2P 00 " + selectMessage);
1598    }
1599
1600    public boolean responderReportNfcHandover(String requestMessage) {
1601        return doBooleanCommand("NFC_REPORT_HANDOVER RESP P2P " + requestMessage + " 00");
1602    }
1603
1604
1605    /* kernel logging support */
1606    private static native byte[] readKernelLogNative();
1607
1608    synchronized public String readKernelLog() {
1609        byte[] bytes = readKernelLogNative();
1610        if (bytes != null) {
1611            CharsetDecoder decoder = StandardCharsets.UTF_8.newDecoder();
1612            try {
1613                CharBuffer decoded = decoder.decode(ByteBuffer.wrap(bytes));
1614                return decoded.toString();
1615            } catch (CharacterCodingException cce) {
1616                return new String(bytes, StandardCharsets.ISO_8859_1);
1617            }
1618        } else {
1619            return "*** failed to read kernel log ***";
1620        }
1621    }
1622
1623    /* WIFI HAL support */
1624
1625    // HAL command ids
1626    private static int sCmdId = 1;
1627    private static int getNewCmdIdLocked() {
1628        return sCmdId++;
1629    }
1630
1631    private static final String TAG = "WifiNative-HAL";
1632    private static long sWifiHalHandle = 0;             /* used by JNI to save wifi_handle */
1633    private static long[] sWifiIfaceHandles = null;     /* used by JNI to save interface handles */
1634    public static int sWlan0Index = -1;
1635    private static int sP2p0Index = -1;
1636    private static MonitorThread sThread;
1637    private static final int STOP_HAL_TIMEOUT_MS = 1000;
1638
1639    private static native boolean startHalNative();
1640    private static native void stopHalNative();
1641    private static native void waitForHalEventNative();
1642
1643    private static class MonitorThread extends Thread {
1644        public void run() {
1645            Log.i(TAG, "Waiting for HAL events mWifiHalHandle=" + Long.toString(sWifiHalHandle));
1646            waitForHalEventNative();
1647        }
1648    }
1649
1650    public boolean startHal() {
1651        String debugLog = "startHal stack: ";
1652        java.lang.StackTraceElement[] elements = Thread.currentThread().getStackTrace();
1653        for (int i = 2; i < elements.length && i <= 7; i++ ) {
1654            debugLog = debugLog + " - " + elements[i].getMethodName();
1655        }
1656
1657        sLocalLog.log(debugLog);
1658
1659        synchronized (sLock) {
1660            if (startHalNative() && (getInterfaces() != 0) && (sWlan0Index != -1)) {
1661                sThread = new MonitorThread();
1662                sThread.start();
1663                return true;
1664            } else {
1665                if (DBG) sLocalLog.log("Could not start hal");
1666                Log.e(TAG, "Could not start hal");
1667                return false;
1668            }
1669        }
1670    }
1671
1672    public void stopHal() {
1673        synchronized (sLock) {
1674            if (isHalStarted()) {
1675                stopHalNative();
1676                try {
1677                    sThread.join(STOP_HAL_TIMEOUT_MS);
1678                    Log.d(TAG, "HAL event thread stopped successfully");
1679                } catch (InterruptedException e) {
1680                    Log.e(TAG, "Could not stop HAL cleanly");
1681                }
1682                sThread = null;
1683                sWifiHalHandle = 0;
1684                sWifiIfaceHandles = null;
1685                sWlan0Index = -1;
1686                sP2p0Index = -1;
1687            }
1688        }
1689    }
1690
1691    public boolean isHalStarted() {
1692        return (sWifiHalHandle != 0);
1693    }
1694    private static native int getInterfacesNative();
1695
1696    public int getInterfaces() {
1697        synchronized (sLock) {
1698            if (isHalStarted()) {
1699                if (sWifiIfaceHandles == null) {
1700                    int num = getInterfacesNative();
1701                    int wifi_num = 0;
1702                    for (int i = 0; i < num; i++) {
1703                        String name = getInterfaceNameNative(i);
1704                        Log.i(TAG, "interface[" + i + "] = " + name);
1705                        if (name.equals("wlan0")) {
1706                            sWlan0Index = i;
1707                            wifi_num++;
1708                        } else if (name.equals("p2p0")) {
1709                            sP2p0Index = i;
1710                            wifi_num++;
1711                        }
1712                    }
1713                    return wifi_num;
1714                } else {
1715                    return sWifiIfaceHandles.length;
1716                }
1717            } else {
1718                return 0;
1719            }
1720        }
1721    }
1722
1723    private static native String getInterfaceNameNative(int index);
1724    public String getInterfaceName(int index) {
1725        synchronized (sLock) {
1726            return getInterfaceNameNative(index);
1727        }
1728    }
1729
1730    // TODO: Change variable names to camel style.
1731    public static class ScanCapabilities {
1732        public int  max_scan_cache_size;
1733        public int  max_scan_buckets;
1734        public int  max_ap_cache_per_scan;
1735        public int  max_rssi_sample_size;
1736        public int  max_scan_reporting_threshold;
1737        public int  max_hotlist_bssids;
1738        public int  max_significant_wifi_change_aps;
1739    }
1740
1741    public boolean getScanCapabilities(ScanCapabilities capabilities) {
1742        synchronized (sLock) {
1743            return isHalStarted() && getScanCapabilitiesNative(sWlan0Index, capabilities);
1744        }
1745    }
1746
1747    private static native boolean getScanCapabilitiesNative(
1748            int iface, ScanCapabilities capabilities);
1749
1750    private static native boolean startScanNative(int iface, int id, ScanSettings settings);
1751    private static native boolean stopScanNative(int iface, int id);
1752    private static native WifiScanner.ScanData[] getScanResultsNative(int iface, boolean flush);
1753    private static native WifiLinkLayerStats getWifiLinkLayerStatsNative(int iface);
1754    private static native void setWifiLinkLayerStatsNative(int iface, int enable);
1755
1756    public static class ChannelSettings {
1757        public int frequency;
1758        public int dwell_time_ms;
1759        public boolean passive;
1760    }
1761
1762    public static class BucketSettings {
1763        public int bucket;
1764        public int band;
1765        public int period_ms;
1766        public int max_period_ms;
1767        public int step_count;
1768        public int report_events;
1769        public int num_channels;
1770        public ChannelSettings[] channels;
1771    }
1772
1773    public static class ScanSettings {
1774        public int base_period_ms;
1775        public int max_ap_per_scan;
1776        public int report_threshold_percent;
1777        public int report_threshold_num_scans;
1778        public int num_buckets;
1779        public BucketSettings[] buckets;
1780    }
1781
1782    /**
1783     * Wi-Fi channel information.
1784     */
1785    public static class WifiChannelInfo {
1786        int mPrimaryFrequency;
1787        int mCenterFrequency0;
1788        int mCenterFrequency1;
1789        int mChannelWidth;
1790        // TODO: add preamble once available in HAL.
1791    }
1792
1793    public static interface ScanEventHandler {
1794        /**
1795         * Called for each AP as it is found with the entire contents of the beacon/probe response.
1796         * Only called when WifiScanner.REPORT_EVENT_FULL_SCAN_RESULT is specified.
1797         */
1798        void onFullScanResult(ScanResult fullScanResult);
1799        /**
1800         * Callback on an event during a gscan scan.
1801         * See WifiNative.WIFI_SCAN_* for possible values.
1802         */
1803        void onScanStatus(int event);
1804        /**
1805         * Called with the current cached scan results when gscan is paused.
1806         */
1807        void onScanPaused(WifiScanner.ScanData[] data);
1808        /**
1809         * Called with the current cached scan results when gscan is resumed.
1810         */
1811        void onScanRestarted();
1812    }
1813
1814    /* scan status, keep these values in sync with gscan.h */
1815    public static final int WIFI_SCAN_RESULTS_AVAILABLE = 0;
1816    public static final int WIFI_SCAN_THRESHOLD_NUM_SCANS = 1;
1817    public static final int WIFI_SCAN_THRESHOLD_PERCENT = 2;
1818    public static final int WIFI_SCAN_FAILED = 3;
1819
1820    // Callback from native
1821    private static void onScanStatus(int id, int event) {
1822        ScanEventHandler handler = sScanEventHandler;
1823        if (handler != null) {
1824            handler.onScanStatus(event);
1825        }
1826    }
1827
1828    public static  WifiSsid createWifiSsid(byte[] rawSsid) {
1829        String ssidHexString = String.valueOf(HexEncoding.encode(rawSsid));
1830
1831        if (ssidHexString == null) {
1832            return null;
1833        }
1834
1835        WifiSsid wifiSsid = WifiSsid.createFromHex(ssidHexString);
1836
1837        return wifiSsid;
1838    }
1839
1840    public static String ssidConvert(byte[] rawSsid) {
1841        String ssid;
1842
1843        CharsetDecoder decoder = StandardCharsets.UTF_8.newDecoder();
1844        try {
1845            CharBuffer decoded = decoder.decode(ByteBuffer.wrap(rawSsid));
1846            ssid = decoded.toString();
1847        } catch (CharacterCodingException cce) {
1848            ssid = null;
1849        }
1850
1851        if (ssid == null) {
1852            ssid = new String(rawSsid, StandardCharsets.ISO_8859_1);
1853        }
1854
1855        return ssid;
1856    }
1857
1858    // Called from native
1859    public static boolean setSsid(byte[] rawSsid, ScanResult result) {
1860        if (rawSsid == null || rawSsid.length == 0 || result == null) {
1861            return false;
1862        }
1863
1864        result.SSID = ssidConvert(rawSsid);
1865        result.wifiSsid = createWifiSsid(rawSsid);
1866        return true;
1867    }
1868
1869    private static void populateScanResult(ScanResult result, int beaconCap, String dbg) {
1870        if (dbg == null) dbg = "";
1871
1872        InformationElementUtil.HtOperation htOperation = new InformationElementUtil.HtOperation();
1873        InformationElementUtil.VhtOperation vhtOperation =
1874                new InformationElementUtil.VhtOperation();
1875        InformationElementUtil.ExtendedCapabilities extendedCaps =
1876                new InformationElementUtil.ExtendedCapabilities();
1877
1878        ScanResult.InformationElement elements[] =
1879                InformationElementUtil.parseInformationElements(result.bytes);
1880        for (ScanResult.InformationElement ie : elements) {
1881            if(ie.id == ScanResult.InformationElement.EID_HT_OPERATION) {
1882                htOperation.from(ie);
1883            } else if(ie.id == ScanResult.InformationElement.EID_VHT_OPERATION) {
1884                vhtOperation.from(ie);
1885            } else if (ie.id == ScanResult.InformationElement.EID_EXTENDED_CAPS) {
1886                extendedCaps.from(ie);
1887            }
1888        }
1889
1890        if (extendedCaps.is80211McRTTResponder) {
1891            result.setFlag(ScanResult.FLAG_80211mc_RESPONDER);
1892        } else {
1893            result.clearFlag(ScanResult.FLAG_80211mc_RESPONDER);
1894        }
1895
1896        //handle RTT related information
1897        if (vhtOperation.isValid()) {
1898            result.channelWidth = vhtOperation.getChannelWidth();
1899            result.centerFreq0 = vhtOperation.getCenterFreq0();
1900            result.centerFreq1 = vhtOperation.getCenterFreq1();
1901        } else {
1902            result.channelWidth = htOperation.getChannelWidth();
1903            result.centerFreq0 = htOperation.getCenterFreq0(result.frequency);
1904            result.centerFreq1  = 0;
1905        }
1906
1907        // build capabilities string
1908        BitSet beaconCapBits = new BitSet(16);
1909        for (int i = 0; i < 16; i++) {
1910            if ((beaconCap & (1 << i)) != 0) {
1911                beaconCapBits.set(i);
1912            }
1913        }
1914        result.capabilities = InformationElementUtil.Capabilities.buildCapabilities(elements,
1915                                               beaconCapBits);
1916
1917        if(DBG) {
1918            Log.d(TAG, dbg + "SSID: " + result.SSID + " ChannelWidth is: " + result.channelWidth
1919                    + " PrimaryFreq: " + result.frequency + " mCenterfreq0: " + result.centerFreq0
1920                    + " mCenterfreq1: " + result.centerFreq1 + (extendedCaps.is80211McRTTResponder
1921                    ? "Support RTT reponder: " : "Do not support RTT responder")
1922                    + " Capabilities: " + result.capabilities);
1923        }
1924
1925        result.informationElements = elements;
1926    }
1927
1928    // Callback from native
1929    private static void onFullScanResult(int id, ScanResult result,
1930            int bucketsScanned, int beaconCap) {
1931        if (DBG) Log.i(TAG, "Got a full scan results event, ssid = " + result.SSID);
1932
1933        ScanEventHandler handler = sScanEventHandler;
1934        if (handler != null) {
1935            populateScanResult(result, beaconCap, " onFullScanResult ");
1936            handler.onFullScanResult(result);
1937        }
1938    }
1939
1940    private static int sScanCmdId = 0;
1941    private static ScanEventHandler sScanEventHandler;
1942    private static ScanSettings sScanSettings;
1943
1944    public boolean startScan(ScanSettings settings, ScanEventHandler eventHandler) {
1945        synchronized (sLock) {
1946            if (isHalStarted()) {
1947                if (sScanCmdId != 0) {
1948                    stopScan();
1949                } else if (sScanSettings != null || sScanEventHandler != null) {
1950                /* current scan is paused; no need to stop it */
1951                }
1952
1953                sScanCmdId = getNewCmdIdLocked();
1954
1955                sScanSettings = settings;
1956                sScanEventHandler = eventHandler;
1957
1958                if (startScanNative(sWlan0Index, sScanCmdId, settings) == false) {
1959                    sScanEventHandler = null;
1960                    sScanSettings = null;
1961                    sScanCmdId = 0;
1962                    return false;
1963                }
1964
1965                return true;
1966            } else {
1967                return false;
1968            }
1969        }
1970    }
1971
1972    public void stopScan() {
1973        synchronized (sLock) {
1974            if (isHalStarted()) {
1975                if (sScanCmdId != 0) {
1976                    stopScanNative(sWlan0Index, sScanCmdId);
1977                }
1978                sScanSettings = null;
1979                sScanEventHandler = null;
1980                sScanCmdId = 0;
1981            }
1982        }
1983    }
1984
1985    public void pauseScan() {
1986        synchronized (sLock) {
1987            if (isHalStarted()) {
1988                if (sScanCmdId != 0 && sScanSettings != null && sScanEventHandler != null) {
1989                    Log.d(TAG, "Pausing scan");
1990                    WifiScanner.ScanData scanData[] = getScanResultsNative(sWlan0Index, true);
1991                    stopScanNative(sWlan0Index, sScanCmdId);
1992                    sScanCmdId = 0;
1993                    sScanEventHandler.onScanPaused(scanData);
1994                }
1995            }
1996        }
1997    }
1998
1999    public void restartScan() {
2000        synchronized (sLock) {
2001            if (isHalStarted()) {
2002                if (sScanCmdId == 0 && sScanSettings != null && sScanEventHandler != null) {
2003                    Log.d(TAG, "Restarting scan");
2004                    ScanEventHandler handler = sScanEventHandler;
2005                    ScanSettings settings = sScanSettings;
2006                    if (startScan(sScanSettings, sScanEventHandler)) {
2007                        sScanEventHandler.onScanRestarted();
2008                    } else {
2009                    /* we are still paused; don't change state */
2010                        sScanEventHandler = handler;
2011                        sScanSettings = settings;
2012                    }
2013                }
2014            }
2015        }
2016    }
2017
2018    public WifiScanner.ScanData[] getScanResults(boolean flush) {
2019        synchronized (sLock) {
2020            WifiScanner.ScanData[] sd = null;
2021            if (isHalStarted()) {
2022                sd = getScanResultsNative(sWlan0Index, flush);
2023            }
2024
2025            if (sd != null) {
2026                return sd;
2027            } else {
2028                return new WifiScanner.ScanData[0];
2029            }
2030        }
2031    }
2032
2033    public static interface HotlistEventHandler {
2034        void onHotlistApFound (ScanResult[] result);
2035        void onHotlistApLost  (ScanResult[] result);
2036    }
2037
2038    private static int sHotlistCmdId = 0;
2039    private static HotlistEventHandler sHotlistEventHandler;
2040
2041    private native static boolean setHotlistNative(int iface, int id,
2042            WifiScanner.HotlistSettings settings);
2043    private native static boolean resetHotlistNative(int iface, int id);
2044
2045    public boolean setHotlist(WifiScanner.HotlistSettings settings,
2046            HotlistEventHandler eventHandler) {
2047        synchronized (sLock) {
2048            if (isHalStarted()) {
2049                if (sHotlistCmdId != 0) {
2050                    return false;
2051                } else {
2052                    sHotlistCmdId = getNewCmdIdLocked();
2053                }
2054
2055                sHotlistEventHandler = eventHandler;
2056                if (setHotlistNative(sWlan0Index, sHotlistCmdId, settings) == false) {
2057                    sHotlistEventHandler = null;
2058                    return false;
2059                }
2060
2061                return true;
2062            } else {
2063                return false;
2064            }
2065        }
2066    }
2067
2068    public void resetHotlist() {
2069        synchronized (sLock) {
2070            if (isHalStarted()) {
2071                if (sHotlistCmdId != 0) {
2072                    resetHotlistNative(sWlan0Index, sHotlistCmdId);
2073                    sHotlistCmdId = 0;
2074                    sHotlistEventHandler = null;
2075                }
2076            }
2077        }
2078    }
2079
2080    // Callback from native
2081    private static void onHotlistApFound(int id, ScanResult[] results) {
2082        HotlistEventHandler handler = sHotlistEventHandler;
2083        if (handler != null) {
2084            handler.onHotlistApFound(results);
2085        } else {
2086            /* this can happen because of race conditions */
2087            Log.d(TAG, "Ignoring hotlist AP found event");
2088        }
2089    }
2090
2091    // Callback from native
2092    private static void onHotlistApLost(int id, ScanResult[] results) {
2093        HotlistEventHandler handler = sHotlistEventHandler;
2094        if (handler != null) {
2095            handler.onHotlistApLost(results);
2096        } else {
2097            /* this can happen because of race conditions */
2098            Log.d(TAG, "Ignoring hotlist AP lost event");
2099        }
2100    }
2101
2102    public static interface SignificantWifiChangeEventHandler {
2103        void onChangesFound(ScanResult[] result);
2104    }
2105
2106    private static SignificantWifiChangeEventHandler sSignificantWifiChangeHandler;
2107    private static int sSignificantWifiChangeCmdId;
2108
2109    private static native boolean trackSignificantWifiChangeNative(
2110            int iface, int id, WifiScanner.WifiChangeSettings settings);
2111    private static native boolean untrackSignificantWifiChangeNative(int iface, int id);
2112
2113    public boolean trackSignificantWifiChange(
2114            WifiScanner.WifiChangeSettings settings, SignificantWifiChangeEventHandler handler) {
2115        synchronized (sLock) {
2116            if (isHalStarted()) {
2117                if (sSignificantWifiChangeCmdId != 0) {
2118                    return false;
2119                } else {
2120                    sSignificantWifiChangeCmdId = getNewCmdIdLocked();
2121                }
2122
2123                sSignificantWifiChangeHandler = handler;
2124                if (trackSignificantWifiChangeNative(sWlan0Index, sSignificantWifiChangeCmdId,
2125                        settings) == false) {
2126                    sSignificantWifiChangeHandler = null;
2127                    return false;
2128                }
2129
2130                return true;
2131            } else {
2132                return false;
2133            }
2134
2135        }
2136    }
2137
2138    public void untrackSignificantWifiChange() {
2139        synchronized (sLock) {
2140            if (isHalStarted()) {
2141                if (sSignificantWifiChangeCmdId != 0) {
2142                    untrackSignificantWifiChangeNative(sWlan0Index, sSignificantWifiChangeCmdId);
2143                    sSignificantWifiChangeCmdId = 0;
2144                    sSignificantWifiChangeHandler = null;
2145                }
2146            }
2147        }
2148    }
2149
2150    // Callback from native
2151    private static void onSignificantWifiChange(int id, ScanResult[] results) {
2152        SignificantWifiChangeEventHandler handler = sSignificantWifiChangeHandler;
2153        if (handler != null) {
2154            handler.onChangesFound(results);
2155        } else {
2156            /* this can happen because of race conditions */
2157            Log.d(TAG, "Ignoring significant wifi change");
2158        }
2159    }
2160
2161    public WifiLinkLayerStats getWifiLinkLayerStats(String iface) {
2162        // TODO: use correct iface name to Index translation
2163        if (iface == null) return null;
2164        synchronized (sLock) {
2165            if (isHalStarted()) {
2166                return getWifiLinkLayerStatsNative(sWlan0Index);
2167            } else {
2168                return null;
2169            }
2170        }
2171    }
2172
2173    public void setWifiLinkLayerStats(String iface, int enable) {
2174        if (iface == null) return;
2175        synchronized (sLock) {
2176            if (isHalStarted()) {
2177                setWifiLinkLayerStatsNative(sWlan0Index, enable);
2178            }
2179        }
2180    }
2181
2182    public static native int getSupportedFeatureSetNative(int iface);
2183    public int getSupportedFeatureSet() {
2184        synchronized (sLock) {
2185            if (isHalStarted()) {
2186                return getSupportedFeatureSetNative(sWlan0Index);
2187            } else {
2188                Log.d(TAG, "Failing getSupportedFeatureset because HAL isn't started");
2189                return 0;
2190            }
2191        }
2192    }
2193
2194    /* Rtt related commands/events */
2195    public static interface RttEventHandler {
2196        void onRttResults(RttManager.RttResult[] result);
2197    }
2198
2199    private static RttEventHandler sRttEventHandler;
2200    private static int sRttCmdId;
2201
2202    // Callback from native
2203    private static void onRttResults(int id, RttManager.RttResult[] results) {
2204        RttEventHandler handler = sRttEventHandler;
2205        if (handler != null && id == sRttCmdId) {
2206            Log.d(TAG, "Received " + results.length + " rtt results");
2207            handler.onRttResults(results);
2208            sRttCmdId = 0;
2209        } else {
2210            Log.d(TAG, "RTT Received event for unknown cmd = " + id +
2211                    ", current id = " + sRttCmdId);
2212        }
2213    }
2214
2215    private static native boolean requestRangeNative(
2216            int iface, int id, RttManager.RttParams[] params);
2217    private static native boolean cancelRangeRequestNative(
2218            int iface, int id, RttManager.RttParams[] params);
2219
2220    public boolean requestRtt(
2221            RttManager.RttParams[] params, RttEventHandler handler) {
2222        synchronized (sLock) {
2223            if (isHalStarted()) {
2224                if (sRttCmdId != 0) {
2225                    Log.v("TAG", "Last one is still under measurement!");
2226                    return false;
2227                } else {
2228                    sRttCmdId = getNewCmdIdLocked();
2229                }
2230                sRttEventHandler = handler;
2231                Log.v(TAG, "native issue RTT request");
2232                return requestRangeNative(sWlan0Index, sRttCmdId, params);
2233            } else {
2234                return false;
2235            }
2236        }
2237    }
2238
2239    public boolean cancelRtt(RttManager.RttParams[] params) {
2240        synchronized (sLock) {
2241            if (isHalStarted()) {
2242                if (sRttCmdId == 0) {
2243                    return false;
2244                }
2245
2246                sRttCmdId = 0;
2247
2248                if (cancelRangeRequestNative(sWlan0Index, sRttCmdId, params)) {
2249                    sRttEventHandler = null;
2250                    Log.v(TAG, "RTT cancel Request Successfully");
2251                    return true;
2252                } else {
2253                    Log.e(TAG, "RTT cancel Request failed");
2254                    return false;
2255                }
2256            } else {
2257                return false;
2258            }
2259        }
2260    }
2261
2262    private static int sRttResponderCmdId = 0;
2263
2264    private static native ResponderConfig enableRttResponderNative(int iface, int commandId,
2265            int timeoutSeconds, WifiChannelInfo channelHint);
2266    /**
2267     * Enable RTT responder role on the device. Returns {@link ResponderConfig} if the responder
2268     * role is successfully enabled, {@code null} otherwise.
2269     */
2270    @Nullable
2271    public ResponderConfig enableRttResponder(int timeoutSeconds) {
2272        synchronized (sLock) {
2273            if (!isHalStarted()) return null;
2274            if (sRttResponderCmdId != 0) {
2275                if (DBG) Log.e(mTAG, "responder mode already enabled - this shouldn't happen");
2276                return null;
2277            }
2278            int id = getNewCmdIdLocked();
2279            ResponderConfig config = enableRttResponderNative(
2280                    sWlan0Index, id, timeoutSeconds, null);
2281            if (config != null) sRttResponderCmdId = id;
2282            if (DBG) Log.d(TAG, "enabling rtt " + (config != null));
2283            return config;
2284        }
2285    }
2286
2287    private static native boolean disableRttResponderNative(int iface, int commandId);
2288    /**
2289     * Disable RTT responder role. Returns {@code true} if responder role is successfully disabled,
2290     * {@code false} otherwise.
2291     */
2292    public boolean disableRttResponder() {
2293        synchronized (sLock) {
2294            if (!isHalStarted()) return false;
2295            if (sRttResponderCmdId == 0) {
2296                Log.e(mTAG, "responder role not enabled yet");
2297                return true;
2298            }
2299            sRttResponderCmdId = 0;
2300            return disableRttResponderNative(sWlan0Index, sRttResponderCmdId);
2301        }
2302    }
2303
2304    private static native boolean setScanningMacOuiNative(int iface, byte[] oui);
2305
2306    public boolean setScanningMacOui(byte[] oui) {
2307        synchronized (sLock) {
2308            if (isHalStarted()) {
2309                return setScanningMacOuiNative(sWlan0Index, oui);
2310            } else {
2311                return false;
2312            }
2313        }
2314    }
2315
2316    private static native int[] getChannelsForBandNative(
2317            int iface, int band);
2318
2319    public int [] getChannelsForBand(int band) {
2320        synchronized (sLock) {
2321            if (isHalStarted()) {
2322                return getChannelsForBandNative(sWlan0Index, band);
2323            } else {
2324                return null;
2325            }
2326        }
2327    }
2328
2329    private static native boolean isGetChannelsForBandSupportedNative();
2330    public boolean isGetChannelsForBandSupported(){
2331        synchronized (sLock) {
2332            if (isHalStarted()) {
2333                return isGetChannelsForBandSupportedNative();
2334            } else {
2335                return false;
2336            }
2337        }
2338    }
2339
2340    private static native boolean setDfsFlagNative(int iface, boolean dfsOn);
2341    public boolean setDfsFlag(boolean dfsOn) {
2342        synchronized (sLock) {
2343            if (isHalStarted()) {
2344                return setDfsFlagNative(sWlan0Index, dfsOn);
2345            } else {
2346                return false;
2347            }
2348        }
2349    }
2350
2351    private static native boolean toggleInterfaceNative(int on);
2352    public boolean toggleInterface(int on) {
2353        synchronized (sLock) {
2354            if (isHalStarted()) {
2355                return toggleInterfaceNative(on);
2356            } else {
2357                return false;
2358            }
2359        }
2360    }
2361
2362    private static native RttManager.RttCapabilities getRttCapabilitiesNative(int iface);
2363    public RttManager.RttCapabilities getRttCapabilities() {
2364        synchronized (sLock) {
2365            if (isHalStarted()) {
2366                return getRttCapabilitiesNative(sWlan0Index);
2367            } else {
2368                return null;
2369            }
2370        }
2371    }
2372
2373    private static native boolean setCountryCodeHalNative(int iface, String CountryCode);
2374    public boolean setCountryCodeHal(String CountryCode) {
2375        synchronized (sLock) {
2376            if (isHalStarted()) {
2377                return setCountryCodeHalNative(sWlan0Index, CountryCode);
2378            } else {
2379                return false;
2380            }
2381        }
2382    }
2383
2384    /* Rtt related commands/events */
2385    public abstract class TdlsEventHandler {
2386        abstract public void onTdlsStatus(String macAddr, int status, int reason);
2387    }
2388
2389    private static TdlsEventHandler sTdlsEventHandler;
2390
2391    private static native boolean enableDisableTdlsNative(int iface, boolean enable,
2392            String macAddr);
2393    public boolean enableDisableTdls(boolean enable, String macAdd, TdlsEventHandler tdlsCallBack) {
2394        synchronized (sLock) {
2395            sTdlsEventHandler = tdlsCallBack;
2396            return enableDisableTdlsNative(sWlan0Index, enable, macAdd);
2397        }
2398    }
2399
2400    // Once TDLS per mac and event feature is implemented, this class definition should be
2401    // moved to the right place, like WifiManager etc
2402    public static class TdlsStatus {
2403        int channel;
2404        int global_operating_class;
2405        int state;
2406        int reason;
2407    }
2408    private static native TdlsStatus getTdlsStatusNative(int iface, String macAddr);
2409    public TdlsStatus getTdlsStatus(String macAdd) {
2410        synchronized (sLock) {
2411            if (isHalStarted()) {
2412                return getTdlsStatusNative(sWlan0Index, macAdd);
2413            } else {
2414                return null;
2415            }
2416        }
2417    }
2418
2419    //ToFix: Once TDLS per mac and event feature is implemented, this class definition should be
2420    // moved to the right place, like WifiStateMachine etc
2421    public static class TdlsCapabilities {
2422        /* Maximum TDLS session number can be supported by the Firmware and hardware */
2423        int maxConcurrentTdlsSessionNumber;
2424        boolean isGlobalTdlsSupported;
2425        boolean isPerMacTdlsSupported;
2426        boolean isOffChannelTdlsSupported;
2427    }
2428
2429
2430
2431    private static native TdlsCapabilities getTdlsCapabilitiesNative(int iface);
2432    public TdlsCapabilities getTdlsCapabilities () {
2433        synchronized (sLock) {
2434            if (isHalStarted()) {
2435                return getTdlsCapabilitiesNative(sWlan0Index);
2436            } else {
2437                return null;
2438            }
2439        }
2440    }
2441
2442    private static boolean onTdlsStatus(String macAddr, int status, int reason) {
2443        TdlsEventHandler handler = sTdlsEventHandler;
2444        if (handler == null) {
2445            return false;
2446        } else {
2447            handler.onTdlsStatus(macAddr, status, reason);
2448            return true;
2449        }
2450    }
2451
2452    //---------------------------------------------------------------------------------
2453
2454    /* Wifi Logger commands/events */
2455
2456    public static interface WifiLoggerEventHandler {
2457        void onRingBufferData(RingBufferStatus status, byte[] buffer);
2458        void onWifiAlert(int errorCode, byte[] buffer);
2459    }
2460
2461    private static WifiLoggerEventHandler sWifiLoggerEventHandler = null;
2462
2463    // Callback from native
2464    private static void onRingBufferData(RingBufferStatus status, byte[] buffer) {
2465        WifiLoggerEventHandler handler = sWifiLoggerEventHandler;
2466        if (handler != null)
2467            handler.onRingBufferData(status, buffer);
2468    }
2469
2470    // Callback from native
2471    private static void onWifiAlert(byte[] buffer, int errorCode) {
2472        WifiLoggerEventHandler handler = sWifiLoggerEventHandler;
2473        if (handler != null)
2474            handler.onWifiAlert(errorCode, buffer);
2475    }
2476
2477    private static int sLogCmdId = -1;
2478    private static native boolean setLoggingEventHandlerNative(int iface, int id);
2479    public boolean setLoggingEventHandler(WifiLoggerEventHandler handler) {
2480        synchronized (sLock) {
2481            if (isHalStarted()) {
2482                int oldId =  sLogCmdId;
2483                sLogCmdId = getNewCmdIdLocked();
2484                if (!setLoggingEventHandlerNative(sWlan0Index, sLogCmdId)) {
2485                    sLogCmdId = oldId;
2486                    return false;
2487                }
2488                sWifiLoggerEventHandler = handler;
2489                return true;
2490            } else {
2491                return false;
2492            }
2493        }
2494    }
2495
2496    private static native boolean startLoggingRingBufferNative(int iface, int verboseLevel,
2497            int flags, int minIntervalSec ,int minDataSize, String ringName);
2498    public boolean startLoggingRingBuffer(int verboseLevel, int flags, int maxInterval,
2499            int minDataSize, String ringName){
2500        synchronized (sLock) {
2501            if (isHalStarted()) {
2502                return startLoggingRingBufferNative(sWlan0Index, verboseLevel, flags, maxInterval,
2503                        minDataSize, ringName);
2504            } else {
2505                return false;
2506            }
2507        }
2508    }
2509
2510    private static native int getSupportedLoggerFeatureSetNative(int iface);
2511    public int getSupportedLoggerFeatureSet() {
2512        synchronized (sLock) {
2513            if (isHalStarted()) {
2514                return getSupportedLoggerFeatureSetNative(sWlan0Index);
2515            } else {
2516                return 0;
2517            }
2518        }
2519    }
2520
2521    private static native boolean resetLogHandlerNative(int iface, int id);
2522    public boolean resetLogHandler() {
2523        synchronized (sLock) {
2524            if (isHalStarted()) {
2525                if (sLogCmdId == -1) {
2526                    Log.e(TAG,"Can not reset handler Before set any handler");
2527                    return false;
2528                }
2529                sWifiLoggerEventHandler = null;
2530                if (resetLogHandlerNative(sWlan0Index, sLogCmdId)) {
2531                    sLogCmdId = -1;
2532                    return true;
2533                } else {
2534                    return false;
2535                }
2536            } else {
2537                return false;
2538            }
2539        }
2540    }
2541
2542    private static native String getDriverVersionNative(int iface);
2543    public String getDriverVersion() {
2544        synchronized (sLock) {
2545            if (isHalStarted()) {
2546                return getDriverVersionNative(sWlan0Index);
2547            } else {
2548                return "";
2549            }
2550        }
2551    }
2552
2553
2554    private static native String getFirmwareVersionNative(int iface);
2555    public String getFirmwareVersion() {
2556        synchronized (sLock) {
2557            if (isHalStarted()) {
2558                return getFirmwareVersionNative(sWlan0Index);
2559            } else {
2560                return "";
2561            }
2562        }
2563    }
2564
2565    public static class RingBufferStatus{
2566        String name;
2567        int flag;
2568        int ringBufferId;
2569        int ringBufferByteSize;
2570        int verboseLevel;
2571        int writtenBytes;
2572        int readBytes;
2573        int writtenRecords;
2574
2575        @Override
2576        public String toString() {
2577            return "name: " + name + " flag: " + flag + " ringBufferId: " + ringBufferId +
2578                    " ringBufferByteSize: " +ringBufferByteSize + " verboseLevel: " +verboseLevel +
2579                    " writtenBytes: " + writtenBytes + " readBytes: " + readBytes +
2580                    " writtenRecords: " + writtenRecords;
2581        }
2582    }
2583
2584    private static native RingBufferStatus[] getRingBufferStatusNative(int iface);
2585    public RingBufferStatus[] getRingBufferStatus() {
2586        synchronized (sLock) {
2587            if (isHalStarted()) {
2588                return getRingBufferStatusNative(sWlan0Index);
2589            } else {
2590                return null;
2591            }
2592        }
2593    }
2594
2595    private static native boolean getRingBufferDataNative(int iface, String ringName);
2596    public boolean getRingBufferData(String ringName) {
2597        synchronized (sLock) {
2598            if (isHalStarted()) {
2599                return getRingBufferDataNative(sWlan0Index, ringName);
2600            } else {
2601                return false;
2602            }
2603        }
2604    }
2605
2606    private static byte[] mFwMemoryDump;
2607    // Callback from native
2608    private static void onWifiFwMemoryAvailable(byte[] buffer) {
2609        mFwMemoryDump = buffer;
2610        if (DBG) {
2611            Log.d(TAG, "onWifiFwMemoryAvailable is called and buffer length is: " +
2612                    (buffer == null ? 0 :  buffer.length));
2613        }
2614    }
2615
2616    private static native boolean getFwMemoryDumpNative(int iface);
2617    public byte[] getFwMemoryDump() {
2618        synchronized (sLock) {
2619            if (isHalStarted()) {
2620                if(getFwMemoryDumpNative(sWlan0Index)) {
2621                    byte[] fwMemoryDump = mFwMemoryDump;
2622                    mFwMemoryDump = null;
2623                    return fwMemoryDump;
2624                } else {
2625                    return null;
2626                }
2627            }
2628            return null;
2629        }
2630    }
2631
2632    //---------------------------------------------------------------------------------
2633    /* Configure ePNO */
2634
2635    /* pno flags, keep these values in sync with gscan.h */
2636    private static int WIFI_PNO_AUTH_CODE_OPEN  = 1; // open
2637    private static int WIFI_PNO_AUTH_CODE_PSK   = 2; // WPA_PSK or WPA2PSK
2638    private static int WIFI_PNO_AUTH_CODE_EAPOL = 4; // any EAPOL
2639
2640    // Whether directed scan needs to be performed (for hidden SSIDs)
2641    private static int WIFI_PNO_FLAG_DIRECTED_SCAN = 1;
2642    // Whether PNO event shall be triggered if the network is found on A band
2643    private static int WIFI_PNO_FLAG_A_BAND = 2;
2644    // Whether PNO event shall be triggered if the network is found on G band
2645    private static int WIFI_PNO_FLAG_G_BAND = 4;
2646    // Whether strict matching is required (i.e. firmware shall not match on the entire SSID)
2647    private static int WIFI_PNO_FLAG_STRICT_MATCH = 8;
2648
2649    public static class WifiPnoNetwork {
2650        String SSID;
2651        int rssi_threshold; // TODO remove
2652        int flags;
2653        int auth;
2654        String configKey; // kept for reference
2655
2656        WifiPnoNetwork(WifiConfiguration config, int threshold) {
2657            if (config.SSID == null) {
2658                this.SSID = "";
2659                this.flags = WIFI_PNO_FLAG_DIRECTED_SCAN;
2660            } else {
2661                this.SSID = config.SSID;
2662            }
2663            this.rssi_threshold = threshold;
2664            if (config.allowedKeyManagement.get(WifiConfiguration.KeyMgmt.WPA_PSK)) {
2665                auth |= WIFI_PNO_AUTH_CODE_PSK;
2666            } else if (config.allowedKeyManagement.get(WifiConfiguration.KeyMgmt.WPA_EAP) ||
2667                    config.allowedKeyManagement.get(WifiConfiguration.KeyMgmt.IEEE8021X)) {
2668                auth |= WIFI_PNO_AUTH_CODE_EAPOL;
2669            } else if (config.wepKeys[0] != null) {
2670                auth |= WIFI_PNO_AUTH_CODE_OPEN;
2671            } else {
2672                auth |= WIFI_PNO_AUTH_CODE_OPEN;
2673            }
2674
2675            flags |= WIFI_PNO_FLAG_A_BAND | WIFI_PNO_FLAG_G_BAND;
2676            configKey = config.configKey();
2677        }
2678
2679        @Override
2680        public String toString() {
2681            StringBuilder sbuf = new StringBuilder();
2682            sbuf.append(this.SSID);
2683            sbuf.append(" flags=").append(this.flags);
2684            sbuf.append(" rssi=").append(this.rssi_threshold);
2685            sbuf.append(" auth=").append(this.auth);
2686            return sbuf.toString();
2687        }
2688    }
2689
2690    public static interface WifiPnoEventHandler {
2691        void onPnoNetworkFound(ScanResult results[]);
2692    }
2693
2694    private static WifiPnoEventHandler sWifiPnoEventHandler;
2695
2696    private static int sPnoCmdId = 0;
2697
2698    private native static boolean setPnoListNative(int iface, int id, WifiPnoNetwork list[]);
2699
2700    public boolean setPnoList(WifiPnoNetwork list[],
2701                                                  WifiPnoEventHandler eventHandler) {
2702        Log.e(TAG, "setPnoList cmd " + sPnoCmdId);
2703
2704        synchronized (sLock) {
2705            if (isHalStarted()) {
2706
2707                sPnoCmdId = getNewCmdIdLocked();
2708
2709                sWifiPnoEventHandler = eventHandler;
2710                if (setPnoListNative(sWlan0Index, sPnoCmdId, list)) {
2711                    return true;
2712                }
2713            }
2714
2715            sWifiPnoEventHandler = null;
2716            return false;
2717        }
2718    }
2719
2720    // Callback from native
2721    private static void onPnoNetworkFound(int id, ScanResult[] results, int[] beaconCaps) {
2722        if (results == null) {
2723            Log.e(TAG, "onPnoNetworkFound null results");
2724            return;
2725
2726        }
2727        Log.d(TAG, "WifiNative.onPnoNetworkFound result " + results.length);
2728
2729        WifiPnoEventHandler handler = sWifiPnoEventHandler;
2730        if (sPnoCmdId != 0 && handler != null) {
2731            for (int i=0; i<results.length; i++) {
2732                Log.e(TAG, "onPnoNetworkFound SSID " + results[i].SSID
2733                        + " " + results[i].level + " " + results[i].frequency);
2734
2735                populateScanResult(results[i], beaconCaps[i], "onPnoNetworkFound ");
2736                results[i].wifiSsid = WifiSsid.createFromAsciiEncoded(results[i].SSID);
2737            }
2738
2739            handler.onPnoNetworkFound(results);
2740        } else {
2741            /* this can happen because of race conditions */
2742            Log.d(TAG, "Ignoring Pno Network found event");
2743        }
2744    }
2745
2746    // TODO remove
2747    public static class WifiLazyRoamParams {
2748        int A_band_boost_threshold;
2749        int A_band_penalty_threshold;
2750        int A_band_boost_factor;
2751        int A_band_penalty_factor;
2752        int A_band_max_boost;
2753        int lazy_roam_hysteresis;
2754        int alert_roam_rssi_trigger;
2755
2756        WifiLazyRoamParams() {
2757        }
2758
2759        @Override
2760        public String toString() {
2761            StringBuilder sbuf = new StringBuilder();
2762            sbuf.append(" A_band_boost_threshold=").append(this.A_band_boost_threshold);
2763            sbuf.append(" A_band_penalty_threshold=").append(this.A_band_penalty_threshold);
2764            sbuf.append(" A_band_boost_factor=").append(this.A_band_boost_factor);
2765            sbuf.append(" A_band_penalty_factor=").append(this.A_band_penalty_factor);
2766            sbuf.append(" A_band_max_boost=").append(this.A_band_max_boost);
2767            sbuf.append(" lazy_roam_hysteresis=").append(this.lazy_roam_hysteresis);
2768            sbuf.append(" alert_roam_rssi_trigger=").append(this.alert_roam_rssi_trigger);
2769            return sbuf.toString();
2770        }
2771    }
2772
2773    // TODO remove
2774    public boolean setLazyRoam(boolean enabled, WifiLazyRoamParams params) {
2775        return false;
2776    }
2777
2778    private native static boolean setBssidBlacklistNative(int iface, int id,
2779                                              String list[]);
2780
2781    public boolean setBssidBlacklist(String list[]) {
2782        int size = 0;
2783        if (list != null) {
2784            size = list.length;
2785        }
2786        Log.e(TAG, "setBssidBlacklist cmd " + sPnoCmdId + " size " + size);
2787
2788        synchronized (sLock) {
2789            if (isHalStarted()) {
2790                sPnoCmdId = getNewCmdIdLocked();
2791                return setBssidBlacklistNative(sWlan0Index, sPnoCmdId, list);
2792            } else {
2793                return false;
2794            }
2795        }
2796    }
2797
2798    // TODO remove
2799    public boolean setSsidWhitelist(String list[]) {
2800        return false;
2801    }
2802
2803    private native static int startSendingOffloadedPacketNative(int iface, int idx,
2804                                    byte[] srcMac, byte[] dstMac, byte[] pktData, int period);
2805
2806    public int
2807    startSendingOffloadedPacket(int slot, KeepalivePacketData keepAlivePacket, int period) {
2808        Log.d(TAG, "startSendingOffloadedPacket slot=" + slot + " period=" + period);
2809
2810        String[] macAddrStr = getMacAddress().split(":");
2811        byte[] srcMac = new byte[6];
2812        for(int i = 0; i < 6; i++) {
2813            Integer hexVal = Integer.parseInt(macAddrStr[i], 16);
2814            srcMac[i] = hexVal.byteValue();
2815        }
2816        synchronized (sLock) {
2817            if (isHalStarted()) {
2818                return startSendingOffloadedPacketNative(sWlan0Index, slot, srcMac,
2819                        keepAlivePacket.dstMac, keepAlivePacket.data, period);
2820            } else {
2821                return -1;
2822            }
2823        }
2824    }
2825
2826    private native static int stopSendingOffloadedPacketNative(int iface, int idx);
2827
2828    public int
2829    stopSendingOffloadedPacket(int slot) {
2830        Log.d(TAG, "stopSendingOffloadedPacket " + slot);
2831        synchronized (sLock) {
2832            if (isHalStarted()) {
2833                return stopSendingOffloadedPacketNative(sWlan0Index, slot);
2834            } else {
2835                return -1;
2836            }
2837        }
2838    }
2839
2840    public static interface WifiRssiEventHandler {
2841        void onRssiThresholdBreached(byte curRssi);
2842    }
2843
2844    private static WifiRssiEventHandler sWifiRssiEventHandler;
2845
2846    // Callback from native
2847    private static void onRssiThresholdBreached(int id, byte curRssi) {
2848        WifiRssiEventHandler handler = sWifiRssiEventHandler;
2849        if (handler != null) {
2850            handler.onRssiThresholdBreached(curRssi);
2851        }
2852    }
2853
2854    private native static int startRssiMonitoringNative(int iface, int id,
2855                                        byte maxRssi, byte minRssi);
2856
2857    private static int sRssiMonitorCmdId = 0;
2858
2859    public int startRssiMonitoring(byte maxRssi, byte minRssi,
2860                                                WifiRssiEventHandler rssiEventHandler) {
2861        Log.d(TAG, "startRssiMonitoring: maxRssi=" + maxRssi + " minRssi=" + minRssi);
2862        synchronized (sLock) {
2863            sWifiRssiEventHandler = rssiEventHandler;
2864            if (isHalStarted()) {
2865                if (sRssiMonitorCmdId != 0) {
2866                    stopRssiMonitoring();
2867                }
2868
2869                sRssiMonitorCmdId = getNewCmdIdLocked();
2870                Log.d(TAG, "sRssiMonitorCmdId = " + sRssiMonitorCmdId);
2871                int ret = startRssiMonitoringNative(sWlan0Index, sRssiMonitorCmdId,
2872                        maxRssi, minRssi);
2873                if (ret != 0) { // if not success
2874                    sRssiMonitorCmdId = 0;
2875                }
2876                return ret;
2877            } else {
2878                return -1;
2879            }
2880        }
2881    }
2882
2883    private native static int stopRssiMonitoringNative(int iface, int idx);
2884
2885    public int stopRssiMonitoring() {
2886        Log.d(TAG, "stopRssiMonitoring, cmdId " + sRssiMonitorCmdId);
2887        synchronized (sLock) {
2888            if (isHalStarted()) {
2889                int ret = 0;
2890                if (sRssiMonitorCmdId != 0) {
2891                    ret = stopRssiMonitoringNative(sWlan0Index, sRssiMonitorCmdId);
2892                }
2893                sRssiMonitorCmdId = 0;
2894                return ret;
2895            } else {
2896                return -1;
2897            }
2898        }
2899    }
2900
2901    private static native WifiWakeReasonAndCounts getWlanWakeReasonCountNative(int iface);
2902}
2903