WifiNative.java revision 7b581f46f6c9bc6edf0edd287d47106712fb2144
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.net.wifi.BatchedScanSettings;
20import android.net.wifi.RttManager;
21import android.net.wifi.ScanResult;
22import android.net.wifi.WifiLinkLayerStats;
23import android.net.wifi.WifiScanner;
24import android.net.wifi.WpsInfo;
25import android.net.wifi.p2p.WifiP2pConfig;
26import android.net.wifi.p2p.WifiP2pGroup;
27import android.os.SystemClock;
28import android.text.TextUtils;
29import android.net.wifi.p2p.nsd.WifiP2pServiceInfo;
30import android.util.LocalLog;
31import android.util.Log;
32
33import java.util.ArrayList;
34import java.util.List;
35import java.util.Locale;
36
37/**
38 * Native calls for bring up/shut down of the supplicant daemon and for
39 * sending requests to the supplicant daemon
40 *
41 * waitForEvent() is called on the monitor thread for events. All other methods
42 * must be serialized from the framework.
43 *
44 * {@hide}
45 */
46public class WifiNative {
47
48    private static boolean DBG = false;
49    private final String mTAG;
50    private static final int DEFAULT_GROUP_OWNER_INTENT     = 6;
51
52    static final int BLUETOOTH_COEXISTENCE_MODE_ENABLED     = 0;
53    static final int BLUETOOTH_COEXISTENCE_MODE_DISABLED    = 1;
54    static final int BLUETOOTH_COEXISTENCE_MODE_SENSE       = 2;
55
56    static final int SCAN_WITHOUT_CONNECTION_SETUP          = 1;
57    static final int SCAN_WITH_CONNECTION_SETUP             = 2;
58
59    // Hold this lock before calling supplicant - it is required to
60    // mutually exclude access from Wifi and P2p state machines
61    static final Object mLock = new Object();
62
63    public final String mInterfaceName;
64    public final String mInterfacePrefix;
65
66    private boolean mSuspendOptEnabled = false;
67
68    /* Register native functions */
69
70    static {
71        /* Native functions are defined in libwifi-service.so */
72        System.loadLibrary("wifi-service");
73        registerNatives();
74    }
75
76    private static native int registerNatives();
77
78    public native static boolean loadDriver();
79
80    public native static boolean isDriverLoaded();
81
82    public native static boolean unloadDriver();
83
84    public native static boolean startSupplicant(boolean p2pSupported);
85
86    /* Sends a kill signal to supplicant. To be used when we have lost connection
87       or when the supplicant is hung */
88    public native static boolean killSupplicant(boolean p2pSupported);
89
90    private native boolean connectToSupplicantNative();
91
92    private native void closeSupplicantConnectionNative();
93
94    /**
95     * Wait for the supplicant to send an event, returning the event string.
96     * @return the event string sent by the supplicant.
97     */
98    private native String waitForEventNative();
99
100    private native boolean doBooleanCommandNative(String command);
101
102    private native int doIntCommandNative(String command);
103
104    private native String doStringCommandNative(String command);
105
106    public WifiNative(String interfaceName) {
107        mInterfaceName = interfaceName;
108        mTAG = "WifiNative-" + interfaceName;
109        if (!interfaceName.equals("p2p0")) {
110            mInterfacePrefix = "IFNAME=" + interfaceName + " ";
111        } else {
112            // commands for p2p0 interface don't need prefix
113            mInterfacePrefix = "";
114        }
115    }
116
117    void enableVerboseLogging(int verbose) {
118        if (verbose > 0) {
119            DBG = true;
120        } else {
121            DBG = false;
122        }
123    }
124
125    private static final LocalLog mLocalLog = new LocalLog(1024);
126
127    // hold mLock before accessing mCmdIdLock
128    private static int sCmdId;
129
130    public LocalLog getLocalLog() {
131        return mLocalLog;
132    }
133
134    private static int getNewCmdIdLocked() {
135        return sCmdId++;
136    }
137
138    private void localLog(String s) {
139        if (mLocalLog != null)
140            mLocalLog.log(mInterfaceName + ": " + s);
141    }
142
143    public boolean connectToSupplicant() {
144        // No synchronization necessary .. it is implemented in WifiMonitor
145        localLog(mInterfacePrefix + "connectToSupplicant");
146        return connectToSupplicantNative();
147    }
148
149    public void closeSupplicantConnection() {
150        localLog(mInterfacePrefix + "closeSupplicantConnection");
151        closeSupplicantConnectionNative();
152    }
153
154    public String waitForEvent() {
155        // No synchronization necessary .. it is implemented in WifiMonitor
156        return waitForEventNative();
157    }
158
159    private boolean doBooleanCommand(String command) {
160        if (DBG) Log.d(mTAG, "doBoolean: " + command);
161        synchronized (mLock) {
162            int cmdId = getNewCmdIdLocked();
163            String toLog = Integer.toString(cmdId) + ":" + mInterfacePrefix + command;
164            boolean result = doBooleanCommandNative(mInterfacePrefix + command);
165            localLog(toLog + " -> " + result);
166            if (DBG) Log.d(mTAG, command + ": returned " + result);
167            return result;
168        }
169    }
170
171    private int doIntCommand(String command) {
172        if (DBG) Log.d(mTAG, "doInt: " + command);
173        synchronized (mLock) {
174            int cmdId = getNewCmdIdLocked();
175            String toLog = Integer.toString(cmdId) + ":" + mInterfacePrefix + command;
176            int result = doIntCommandNative(mInterfacePrefix + command);
177            localLog(toLog + " -> " + result);
178            if (DBG) Log.d(mTAG, "   returned " + result);
179            return result;
180        }
181    }
182
183    private String doStringCommand(String command) {
184        if (DBG) {
185            //GET_NETWORK commands flood the logs
186            if (!command.startsWith("GET_NETWORK")) {
187                Log.d(mTAG, "doString: [" + command + "]");
188            }
189        }
190        synchronized (mLock) {
191            int cmdId = getNewCmdIdLocked();
192            String toLog = Integer.toString(cmdId) + ":" + mInterfacePrefix + command;
193            String result = doStringCommandNative(mInterfacePrefix + command);
194            if (result == null) {
195                if (DBG) Log.d(mTAG, "doStringCommandNative no result");
196            } else {
197                if (!command.startsWith("STATUS-")) {
198                    localLog(toLog + " -> " + result);
199                }
200                if (DBG) Log.d(mTAG, "   returned " + result.replace("\n", " "));
201            }
202            return result;
203        }
204    }
205
206    private String doStringCommandWithoutLogging(String command) {
207        if (DBG) {
208            //GET_NETWORK commands flood the logs
209            if (!command.startsWith("GET_NETWORK")) {
210                Log.d(mTAG, "doString: [" + command + "]");
211            }
212        }
213        synchronized (mLock) {
214            return doStringCommandNative(mInterfacePrefix + command);
215        }
216    }
217
218    public boolean ping() {
219        String pong = doStringCommand("PING");
220        return (pong != null && pong.equals("PONG"));
221    }
222
223    public void setSupplicantLogLevel(String level) {
224        doStringCommand("LOG_LEVEL " + level);
225    }
226
227    public String getFreqCapability() {
228        return doStringCommand("GET_CAPABILITY freq");
229    }
230
231    public boolean scan(int type, String freqList) {
232        if (type == SCAN_WITHOUT_CONNECTION_SETUP) {
233            if (freqList == null) return doBooleanCommand("SCAN TYPE=ONLY");
234            else return doBooleanCommand("SCAN TYPE=ONLY freq=" + freqList);
235        } else if (type == SCAN_WITH_CONNECTION_SETUP) {
236            if (freqList == null) return doBooleanCommand("SCAN");
237            else return doBooleanCommand("SCAN freq=" + freqList);
238        } else {
239            throw new IllegalArgumentException("Invalid scan type");
240        }
241    }
242
243    /* Does a graceful shutdown of supplicant. Is a common stop function for both p2p and sta.
244     *
245     * Note that underneath we use a harsh-sounding "terminate" supplicant command
246     * for a graceful stop and a mild-sounding "stop" interface
247     * to kill the process
248     */
249    public boolean stopSupplicant() {
250        return doBooleanCommand("TERMINATE");
251    }
252
253    public String listNetworks() {
254        return doStringCommand("LIST_NETWORKS");
255    }
256
257    public int addNetwork() {
258        return doIntCommand("ADD_NETWORK");
259    }
260
261    public boolean setNetworkVariable(int netId, String name, String value) {
262        if (TextUtils.isEmpty(name) || TextUtils.isEmpty(value)) return false;
263        return doBooleanCommand("SET_NETWORK " + netId + " " + name + " " + value);
264    }
265
266    public String getNetworkVariable(int netId, String name) {
267        if (TextUtils.isEmpty(name)) return null;
268
269        // GET_NETWORK will likely flood the logs ...
270        return doStringCommandWithoutLogging("GET_NETWORK " + netId + " " + name);
271    }
272
273    public boolean removeNetwork(int netId) {
274        return doBooleanCommand("REMOVE_NETWORK " + netId);
275    }
276
277
278    private void logDbg(String debug) {
279        long now = SystemClock.elapsedRealtimeNanos();
280        String ts = String.format("[%,d us] ", now/1000);
281        Log.e("WifiNative: ", ts+debug+ " stack:"
282                + Thread.currentThread().getStackTrace()[2].getMethodName() +" - "
283                + Thread.currentThread().getStackTrace()[3].getMethodName() +" - "
284                + Thread.currentThread().getStackTrace()[4].getMethodName() +" - "
285                + Thread.currentThread().getStackTrace()[5].getMethodName()+" - "
286                + Thread.currentThread().getStackTrace()[6].getMethodName());
287
288    }
289    public boolean enableNetwork(int netId, boolean disableOthers) {
290        if (DBG) logDbg("enableNetwork nid=" + Integer.toString(netId)
291                + " disableOthers=" + disableOthers);
292        if (disableOthers) {
293            return doBooleanCommand("SELECT_NETWORK " + netId);
294        } else {
295            return doBooleanCommand("ENABLE_NETWORK " + netId);
296        }
297    }
298
299    public boolean disableNetwork(int netId) {
300        if (DBG) logDbg("disableNetwork nid=" + Integer.toString(netId));
301        return doBooleanCommand("DISABLE_NETWORK " + netId);
302    }
303
304    public boolean reconnect() {
305        if (DBG) logDbg("RECONNECT ");
306        return doBooleanCommand("RECONNECT");
307    }
308
309    public boolean reassociate() {
310        if (DBG) logDbg("REASSOCIATE ");
311        return doBooleanCommand("REASSOCIATE");
312    }
313
314    public boolean disconnect() {
315        if (DBG) logDbg("DISCONNECT ");
316        return doBooleanCommand("DISCONNECT");
317    }
318
319    public String status() {
320        return status(false);
321    }
322
323    public String status(boolean noEvents) {
324        if (noEvents) {
325            return doStringCommand("STATUS-NO_EVENTS");
326        } else {
327            return doStringCommand("STATUS");
328        }
329    }
330
331
332    public String getMacAddress() {
333        //Macaddr = XX.XX.XX.XX.XX.XX
334        String ret = doStringCommand("DRIVER MACADDR");
335        if (!TextUtils.isEmpty(ret)) {
336            String[] tokens = ret.split(" = ");
337            if (tokens.length == 2) return tokens[1];
338        }
339        return null;
340    }
341
342    /**
343     * Format of results:
344     * =================
345     * id=1
346     * bssid=68:7f:74:d7:1b:6e
347     * freq=2412
348     * level=-43
349     * tsf=1344621975160944
350     * age=2623
351     * flags=[WPA2-PSK-CCMP][WPS][ESS]
352     * ssid=zubyb
353     * ====
354     *
355     * RANGE=ALL gets all scan results
356     * RANGE=ID- gets results from ID
357     * MASK=<N> see wpa_supplicant/src/common/wpa_ctrl.h for details
358     */
359    public String scanResults(int sid) {
360        return doStringCommandWithoutLogging("BSS RANGE=" + sid + "- MASK=0x21987");
361    }
362
363    /**
364     * Format of result:
365     * id=1016
366     * bssid=00:03:7f:40:84:10
367     * freq=2462
368     * beacon_int=200
369     * capabilities=0x0431
370     * qual=0
371     * noise=0
372     * level=-46
373     * tsf=0000002669008476
374     * age=5
375     * ie=00105143412d485332302d52322d54455354010882848b960c12182403010b0706555...
376     * flags=[WPA2-EAP-CCMP][ESS][P2P][HS20]
377     * ssid=QCA-HS20-R2-TEST
378     * p2p_device_name=
379     * p2p_config_methods=0x0SET_NE
380     * anqp_venue_name=02083d656e6757692d466920416c6c69616e63650a3239383920436f...
381     * anqp_network_auth_type=010000
382     * anqp_roaming_consortium=03506f9a05001bc504bd
383     * anqp_ip_addr_type_availability=0c
384     * anqp_nai_realm=0200300000246d61696c2e6578616d706c652e636f6d3b636973636f2...
385     * anqp_3gpp=000600040132f465
386     * anqp_domain_name=0b65786d61706c652e636f6d
387     * hs20_operator_friendly_name=11656e6757692d466920416c6c69616e63650e636869...
388     * hs20_wan_metrics=01c40900008001000000000a00
389     * hs20_connection_capability=0100000006140001061600000650000106bb010106bb0...
390     * hs20_osu_providers_list=0b5143412d4f53552d425353010901310015656e6757692d...
391     */
392    public String scanResult(String bssid) {
393        return doStringCommand("BSS " + bssid);
394    }
395
396    /**
397     * Format of command
398     * DRIVER WLS_BATCHING SET SCANFREQ=x MSCAN=r BESTN=y CHANNEL=<z, w, t> RTT=s
399     * where x is an ascii representation of an integer number of seconds between scans
400     *       r is an ascii representation of an integer number of scans per batch
401     *       y is an ascii representation of an integer number of the max AP to remember per scan
402     *       z, w, t represent a 1..n size list of channel numbers and/or 'A', 'B' values
403     *           indicating entire ranges of channels
404     *       s is an ascii representation of an integer number of highest-strength AP
405     *           for which we'd like approximate distance reported
406     *
407     * The return value is an ascii integer representing a guess of the number of scans
408     * the firmware can remember before it runs out of buffer space or -1 on error
409     */
410    public String setBatchedScanSettings(BatchedScanSettings settings) {
411        if (settings == null) {
412            return doStringCommand("DRIVER WLS_BATCHING STOP");
413        }
414        String cmd = "DRIVER WLS_BATCHING SET SCANFREQ=" + settings.scanIntervalSec;
415        cmd += " MSCAN=" + settings.maxScansPerBatch;
416        if (settings.maxApPerScan != BatchedScanSettings.UNSPECIFIED) {
417            cmd += " BESTN=" + settings.maxApPerScan;
418        }
419        if (settings.channelSet != null && !settings.channelSet.isEmpty()) {
420            cmd += " CHANNEL=<";
421            int i = 0;
422            for (String channel : settings.channelSet) {
423                cmd += (i > 0 ? "," : "") + channel;
424                ++i;
425            }
426            cmd += ">";
427        }
428        if (settings.maxApForDistance != BatchedScanSettings.UNSPECIFIED) {
429            cmd += " RTT=" + settings.maxApForDistance;
430        }
431        return doStringCommand(cmd);
432    }
433
434    public String getBatchedScanResults() {
435        return doStringCommand("DRIVER WLS_BATCHING GET");
436    }
437
438    public boolean startDriver() {
439        return doBooleanCommand("DRIVER START");
440    }
441
442    public boolean stopDriver() {
443        return doBooleanCommand("DRIVER STOP");
444    }
445
446
447    /**
448     * Start filtering out Multicast V4 packets
449     * @return {@code true} if the operation succeeded, {@code false} otherwise
450     *
451     * Multicast filtering rules work as follows:
452     *
453     * The driver can filter multicast (v4 and/or v6) and broadcast packets when in
454     * a power optimized mode (typically when screen goes off).
455     *
456     * In order to prevent the driver from filtering the multicast/broadcast packets, we have to
457     * add a DRIVER RXFILTER-ADD rule followed by DRIVER RXFILTER-START to make the rule effective
458     *
459     * DRIVER RXFILTER-ADD Num
460     *   where Num = 0 - Unicast, 1 - Broadcast, 2 - Mutil4 or 3 - Multi6
461     *
462     * and DRIVER RXFILTER-START
463     * In order to stop the usage of these rules, we do
464     *
465     * DRIVER RXFILTER-STOP
466     * DRIVER RXFILTER-REMOVE Num
467     *   where Num is as described for RXFILTER-ADD
468     *
469     * The  SETSUSPENDOPT driver command overrides the filtering rules
470     */
471    public boolean startFilteringMulticastV4Packets() {
472        return doBooleanCommand("DRIVER RXFILTER-STOP")
473            && doBooleanCommand("DRIVER RXFILTER-REMOVE 2")
474            && doBooleanCommand("DRIVER RXFILTER-START");
475    }
476
477    /**
478     * Stop filtering out Multicast V4 packets.
479     * @return {@code true} if the operation succeeded, {@code false} otherwise
480     */
481    public boolean stopFilteringMulticastV4Packets() {
482        return doBooleanCommand("DRIVER RXFILTER-STOP")
483            && doBooleanCommand("DRIVER RXFILTER-ADD 2")
484            && doBooleanCommand("DRIVER RXFILTER-START");
485    }
486
487    /**
488     * Start filtering out Multicast V6 packets
489     * @return {@code true} if the operation succeeded, {@code false} otherwise
490     */
491    public boolean startFilteringMulticastV6Packets() {
492        return doBooleanCommand("DRIVER RXFILTER-STOP")
493            && doBooleanCommand("DRIVER RXFILTER-REMOVE 3")
494            && doBooleanCommand("DRIVER RXFILTER-START");
495    }
496
497    /**
498     * Stop filtering out Multicast V6 packets.
499     * @return {@code true} if the operation succeeded, {@code false} otherwise
500     */
501    public boolean stopFilteringMulticastV6Packets() {
502        return doBooleanCommand("DRIVER RXFILTER-STOP")
503            && doBooleanCommand("DRIVER RXFILTER-ADD 3")
504            && doBooleanCommand("DRIVER RXFILTER-START");
505    }
506
507    public int getBand() {
508       String ret = doStringCommand("DRIVER GETBAND");
509        if (!TextUtils.isEmpty(ret)) {
510            //reply is "BAND X" where X is the band
511            String[] tokens = ret.split(" ");
512            try {
513                if (tokens.length == 2) return Integer.parseInt(tokens[1]);
514            } catch (NumberFormatException e) {
515                return -1;
516            }
517        }
518        return -1;
519    }
520
521    public boolean setBand(int band) {
522        return doBooleanCommand("DRIVER SETBAND " + band);
523    }
524
525    /**
526      * Sets the bluetooth coexistence mode.
527      *
528      * @param mode One of {@link #BLUETOOTH_COEXISTENCE_MODE_DISABLED},
529      *            {@link #BLUETOOTH_COEXISTENCE_MODE_ENABLED}, or
530      *            {@link #BLUETOOTH_COEXISTENCE_MODE_SENSE}.
531      * @return Whether the mode was successfully set.
532      */
533    public boolean setBluetoothCoexistenceMode(int mode) {
534        return doBooleanCommand("DRIVER BTCOEXMODE " + mode);
535    }
536
537    /**
538     * Enable or disable Bluetooth coexistence scan mode. When this mode is on,
539     * some of the low-level scan parameters used by the driver are changed to
540     * reduce interference with A2DP streaming.
541     *
542     * @param isSet whether to enable or disable this mode
543     * @return {@code true} if the command succeeded, {@code false} otherwise.
544     */
545    public boolean setBluetoothCoexistenceScanMode(boolean setCoexScanMode) {
546        if (setCoexScanMode) {
547            return doBooleanCommand("DRIVER BTCOEXSCAN-START");
548        } else {
549            return doBooleanCommand("DRIVER BTCOEXSCAN-STOP");
550        }
551    }
552
553    public void enableSaveConfig() {
554        doBooleanCommand("SET update_config 1");
555    }
556
557    public boolean saveConfig() {
558        return doBooleanCommand("SAVE_CONFIG");
559    }
560
561    public boolean addToBlacklist(String bssid) {
562        if (TextUtils.isEmpty(bssid)) return false;
563        return doBooleanCommand("BLACKLIST " + bssid);
564    }
565
566    public boolean clearBlacklist() {
567        return doBooleanCommand("BLACKLIST clear");
568    }
569
570    public boolean setSuspendOptimizations(boolean enabled) {
571       // if (mSuspendOptEnabled == enabled) return true;
572        mSuspendOptEnabled = enabled;
573
574        Log.e("native", "do suspend " + enabled);
575        if (enabled) {
576            return doBooleanCommand("DRIVER SETSUSPENDMODE 1");
577        } else {
578            return doBooleanCommand("DRIVER SETSUSPENDMODE 0");
579        }
580    }
581
582    public boolean setCountryCode(String countryCode) {
583        return doBooleanCommand("DRIVER COUNTRY " + countryCode.toUpperCase(Locale.ROOT));
584    }
585
586    public void enableBackgroundScan(boolean enable) {
587        if (enable) {
588            doBooleanCommand("SET pno 1");
589        } else {
590            doBooleanCommand("SET pno 0");
591        }
592    }
593
594    public void enableAutoConnect(boolean enable) {
595        if (enable) {
596            doBooleanCommand("STA_AUTOCONNECT 1");
597        } else {
598            doBooleanCommand("STA_AUTOCONNECT 0");
599        }
600    }
601
602    public void setScanInterval(int scanInterval) {
603        doBooleanCommand("SCAN_INTERVAL " + scanInterval);
604    }
605
606    public void startTdls(String macAddr, boolean enable) {
607        if (enable) {
608            doBooleanCommand("TDLS_DISCOVER " + macAddr);
609            doBooleanCommand("TDLS_SETUP " + macAddr);
610        } else {
611            doBooleanCommand("TDLS_TEARDOWN " + macAddr);
612        }
613    }
614
615    /** Example output:
616     * RSSI=-65
617     * LINKSPEED=48
618     * NOISE=9999
619     * FREQUENCY=0
620     */
621    public String signalPoll() {
622        return doStringCommandWithoutLogging("SIGNAL_POLL");
623    }
624
625    /** Example outout:
626     * TXGOOD=396
627     * TXBAD=1
628     */
629    public String pktcntPoll() {
630        return doStringCommand("PKTCNT_POLL");
631    }
632
633    public void bssFlush() {
634        doBooleanCommand("BSS_FLUSH 0");
635    }
636
637    public boolean startWpsPbc(String bssid) {
638        if (TextUtils.isEmpty(bssid)) {
639            return doBooleanCommand("WPS_PBC");
640        } else {
641            return doBooleanCommand("WPS_PBC " + bssid);
642        }
643    }
644
645    public boolean startWpsPbc(String iface, String bssid) {
646        synchronized (mLock) {
647            if (TextUtils.isEmpty(bssid)) {
648                return doBooleanCommandNative("IFNAME=" + iface + " WPS_PBC");
649            } else {
650                return doBooleanCommandNative("IFNAME=" + iface + " WPS_PBC " + bssid);
651            }
652        }
653    }
654
655    public boolean startWpsPinKeypad(String pin) {
656        if (TextUtils.isEmpty(pin)) return false;
657        return doBooleanCommand("WPS_PIN any " + pin);
658    }
659
660    public boolean startWpsPinKeypad(String iface, String pin) {
661        if (TextUtils.isEmpty(pin)) return false;
662        synchronized (mLock) {
663            return doBooleanCommandNative("IFNAME=" + iface + " WPS_PIN any " + pin);
664        }
665    }
666
667
668    public String startWpsPinDisplay(String bssid) {
669        if (TextUtils.isEmpty(bssid)) {
670            return doStringCommand("WPS_PIN any");
671        } else {
672            return doStringCommand("WPS_PIN " + bssid);
673        }
674    }
675
676    public String startWpsPinDisplay(String iface, String bssid) {
677        synchronized (mLock) {
678            if (TextUtils.isEmpty(bssid)) {
679                return doStringCommandNative("IFNAME=" + iface + " WPS_PIN any");
680            } else {
681                return doStringCommandNative("IFNAME=" + iface + " WPS_PIN " + bssid);
682            }
683        }
684    }
685
686    public boolean setExternalSim(boolean external) {
687        synchronized (mLock) {
688            String value = external ? "1" : "0";
689            Log.d(TAG, "Setting external_sim to " + value);
690            return doBooleanCommand("SET external_sim " + value);
691        }
692    }
693
694    public boolean simAuthResponse(int id, String response) {
695        synchronized (mLock) {
696            return doBooleanCommand("CTRL-RSP-SIM-" + id + ":GSM-AUTH" + response);
697        }
698    }
699
700    /* Configures an access point connection */
701    public boolean startWpsRegistrar(String bssid, String pin) {
702        if (TextUtils.isEmpty(bssid) || TextUtils.isEmpty(pin)) return false;
703        return doBooleanCommand("WPS_REG " + bssid + " " + pin);
704    }
705
706    public boolean cancelWps() {
707        return doBooleanCommand("WPS_CANCEL");
708    }
709
710    public boolean setPersistentReconnect(boolean enabled) {
711        int value = (enabled == true) ? 1 : 0;
712        return doBooleanCommand("SET persistent_reconnect " + value);
713    }
714
715    public boolean setDeviceName(String name) {
716        return doBooleanCommand("SET device_name " + name);
717    }
718
719    public boolean setDeviceType(String type) {
720        return doBooleanCommand("SET device_type " + type);
721    }
722
723    public boolean setConfigMethods(String cfg) {
724        return doBooleanCommand("SET config_methods " + cfg);
725    }
726
727    public boolean setManufacturer(String value) {
728        return doBooleanCommand("SET manufacturer " + value);
729    }
730
731    public boolean setModelName(String value) {
732        return doBooleanCommand("SET model_name " + value);
733    }
734
735    public boolean setModelNumber(String value) {
736        return doBooleanCommand("SET model_number " + value);
737    }
738
739    public boolean setSerialNumber(String value) {
740        return doBooleanCommand("SET serial_number " + value);
741    }
742
743    public boolean setP2pSsidPostfix(String postfix) {
744        return doBooleanCommand("SET p2p_ssid_postfix " + postfix);
745    }
746
747    public boolean setP2pGroupIdle(String iface, int time) {
748        synchronized (mLock) {
749            return doBooleanCommandNative("IFNAME=" + iface + " SET p2p_group_idle " + time);
750        }
751    }
752
753    public void setPowerSave(boolean enabled) {
754        if (enabled) {
755            doBooleanCommand("SET ps 1");
756        } else {
757            doBooleanCommand("SET ps 0");
758        }
759    }
760
761    public boolean setP2pPowerSave(String iface, boolean enabled) {
762        synchronized (mLock) {
763            if (enabled) {
764                return doBooleanCommandNative("IFNAME=" + iface + " P2P_SET ps 1");
765            } else {
766                return doBooleanCommandNative("IFNAME=" + iface + " P2P_SET ps 0");
767            }
768        }
769    }
770
771    public boolean setWfdEnable(boolean enable) {
772        return doBooleanCommand("SET wifi_display " + (enable ? "1" : "0"));
773    }
774
775    public boolean setWfdDeviceInfo(String hex) {
776        return doBooleanCommand("WFD_SUBELEM_SET 0 " + hex);
777    }
778
779    /**
780     * "sta" prioritizes STA connection over P2P and "p2p" prioritizes
781     * P2P connection over STA
782     */
783    public boolean setConcurrencyPriority(String s) {
784        return doBooleanCommand("P2P_SET conc_pref " + s);
785    }
786
787    public boolean p2pFind() {
788        return doBooleanCommand("P2P_FIND");
789    }
790
791    public boolean p2pFind(int timeout) {
792        if (timeout <= 0) {
793            return p2pFind();
794        }
795        return doBooleanCommand("P2P_FIND " + timeout);
796    }
797
798    public boolean p2pStopFind() {
799       return doBooleanCommand("P2P_STOP_FIND");
800    }
801
802    public boolean p2pListen() {
803        return doBooleanCommand("P2P_LISTEN");
804    }
805
806    public boolean p2pListen(int timeout) {
807        if (timeout <= 0) {
808            return p2pListen();
809        }
810        return doBooleanCommand("P2P_LISTEN " + timeout);
811    }
812
813    public boolean p2pExtListen(boolean enable, int period, int interval) {
814        if (enable && interval < period) {
815            return false;
816        }
817        return doBooleanCommand("P2P_EXT_LISTEN"
818                    + (enable ? (" " + period + " " + interval) : ""));
819    }
820
821    public boolean p2pSetChannel(int lc, int oc) {
822        if (DBG) Log.d(mTAG, "p2pSetChannel: lc="+lc+", oc="+oc);
823
824        if (lc >=1 && lc <= 11) {
825            if (!doBooleanCommand("P2P_SET listen_channel " + lc)) {
826                return false;
827            }
828        } else if (lc != 0) {
829            return false;
830        }
831
832        if (oc >= 1 && oc <= 165 ) {
833            int freq = (oc <= 14 ? 2407 : 5000) + oc * 5;
834            return doBooleanCommand("P2P_SET disallow_freq 1000-"
835                    + (freq - 5) + "," + (freq + 5) + "-6000");
836        } else if (oc == 0) {
837            /* oc==0 disables "P2P_SET disallow_freq" (enables all freqs) */
838            return doBooleanCommand("P2P_SET disallow_freq \"\"");
839        }
840
841        return false;
842    }
843
844    public boolean p2pFlush() {
845        return doBooleanCommand("P2P_FLUSH");
846    }
847
848    /* p2p_connect <peer device address> <pbc|pin|PIN#> [label|display|keypad]
849        [persistent] [join|auth] [go_intent=<0..15>] [freq=<in MHz>] */
850    public String p2pConnect(WifiP2pConfig config, boolean joinExistingGroup) {
851        if (config == null) return null;
852        List<String> args = new ArrayList<String>();
853        WpsInfo wps = config.wps;
854        args.add(config.deviceAddress);
855
856        switch (wps.setup) {
857            case WpsInfo.PBC:
858                args.add("pbc");
859                break;
860            case WpsInfo.DISPLAY:
861                if (TextUtils.isEmpty(wps.pin)) {
862                    args.add("pin");
863                } else {
864                    args.add(wps.pin);
865                }
866                args.add("display");
867                break;
868            case WpsInfo.KEYPAD:
869                args.add(wps.pin);
870                args.add("keypad");
871                break;
872            case WpsInfo.LABEL:
873                args.add(wps.pin);
874                args.add("label");
875            default:
876                break;
877        }
878
879        if (config.netId == WifiP2pGroup.PERSISTENT_NET_ID) {
880            args.add("persistent");
881        }
882
883        if (joinExistingGroup) {
884            args.add("join");
885        } else {
886            //TODO: This can be adapted based on device plugged in state and
887            //device battery state
888            int groupOwnerIntent = config.groupOwnerIntent;
889            if (groupOwnerIntent < 0 || groupOwnerIntent > 15) {
890                groupOwnerIntent = DEFAULT_GROUP_OWNER_INTENT;
891            }
892            args.add("go_intent=" + groupOwnerIntent);
893        }
894
895        String command = "P2P_CONNECT ";
896        for (String s : args) command += s + " ";
897
898        return doStringCommand(command);
899    }
900
901    public boolean p2pCancelConnect() {
902        return doBooleanCommand("P2P_CANCEL");
903    }
904
905    public boolean p2pProvisionDiscovery(WifiP2pConfig config) {
906        if (config == null) return false;
907
908        switch (config.wps.setup) {
909            case WpsInfo.PBC:
910                return doBooleanCommand("P2P_PROV_DISC " + config.deviceAddress + " pbc");
911            case WpsInfo.DISPLAY:
912                //We are doing display, so provision discovery is keypad
913                return doBooleanCommand("P2P_PROV_DISC " + config.deviceAddress + " keypad");
914            case WpsInfo.KEYPAD:
915                //We are doing keypad, so provision discovery is display
916                return doBooleanCommand("P2P_PROV_DISC " + config.deviceAddress + " display");
917            default:
918                break;
919        }
920        return false;
921    }
922
923    public boolean p2pGroupAdd(boolean persistent) {
924        if (persistent) {
925            return doBooleanCommand("P2P_GROUP_ADD persistent");
926        }
927        return doBooleanCommand("P2P_GROUP_ADD");
928    }
929
930    public boolean p2pGroupAdd(int netId) {
931        return doBooleanCommand("P2P_GROUP_ADD persistent=" + netId);
932    }
933
934    public boolean p2pGroupRemove(String iface) {
935        if (TextUtils.isEmpty(iface)) return false;
936        synchronized (mLock) {
937            return doBooleanCommandNative("IFNAME=" + iface + " P2P_GROUP_REMOVE " + iface);
938        }
939    }
940
941    public boolean p2pReject(String deviceAddress) {
942        return doBooleanCommand("P2P_REJECT " + deviceAddress);
943    }
944
945    /* Invite a peer to a group */
946    public boolean p2pInvite(WifiP2pGroup group, String deviceAddress) {
947        if (TextUtils.isEmpty(deviceAddress)) return false;
948
949        if (group == null) {
950            return doBooleanCommand("P2P_INVITE peer=" + deviceAddress);
951        } else {
952            return doBooleanCommand("P2P_INVITE group=" + group.getInterface()
953                    + " peer=" + deviceAddress + " go_dev_addr=" + group.getOwner().deviceAddress);
954        }
955    }
956
957    /* Reinvoke a persistent connection */
958    public boolean p2pReinvoke(int netId, String deviceAddress) {
959        if (TextUtils.isEmpty(deviceAddress) || netId < 0) return false;
960
961        return doBooleanCommand("P2P_INVITE persistent=" + netId + " peer=" + deviceAddress);
962    }
963
964    public String p2pGetSsid(String deviceAddress) {
965        return p2pGetParam(deviceAddress, "oper_ssid");
966    }
967
968    public String p2pGetDeviceAddress() {
969        Log.d(TAG, "p2pGetDeviceAddress");
970
971        /* Explicitly calling the API without IFNAME= prefix to take care of the devices that
972        don't have p2p0 interface. Supplicant seems to be returning the correct address anyway. */
973        String status = doStringCommandNative("STATUS");
974        String result = "";
975
976        if (status != null) {
977            String[] tokens = status.split("\n");
978            for (String token : tokens) {
979                if (token.startsWith("p2p_device_address=")) {
980                    String[] nameValue = token.split("=");
981                    if (nameValue.length != 2)
982                        break;
983                    result = nameValue[1];
984                }
985            }
986        }
987
988        Log.d(TAG, "p2pGetDeviceAddress returning " + result);
989        return result;
990    }
991
992    public int getGroupCapability(String deviceAddress) {
993        int gc = 0;
994        if (TextUtils.isEmpty(deviceAddress)) return gc;
995        String peerInfo = p2pPeer(deviceAddress);
996        if (TextUtils.isEmpty(peerInfo)) return gc;
997
998        String[] tokens = peerInfo.split("\n");
999        for (String token : tokens) {
1000            if (token.startsWith("group_capab=")) {
1001                String[] nameValue = token.split("=");
1002                if (nameValue.length != 2) break;
1003                try {
1004                    return Integer.decode(nameValue[1]);
1005                } catch(NumberFormatException e) {
1006                    return gc;
1007                }
1008            }
1009        }
1010        return gc;
1011    }
1012
1013    public String p2pPeer(String deviceAddress) {
1014        return doStringCommand("P2P_PEER " + deviceAddress);
1015    }
1016
1017    private String p2pGetParam(String deviceAddress, String key) {
1018        if (deviceAddress == null) return null;
1019
1020        String peerInfo = p2pPeer(deviceAddress);
1021        if (peerInfo == null) return null;
1022        String[] tokens= peerInfo.split("\n");
1023
1024        key += "=";
1025        for (String token : tokens) {
1026            if (token.startsWith(key)) {
1027                String[] nameValue = token.split("=");
1028                if (nameValue.length != 2) break;
1029                return nameValue[1];
1030            }
1031        }
1032        return null;
1033    }
1034
1035    public boolean p2pServiceAdd(WifiP2pServiceInfo servInfo) {
1036        /*
1037         * P2P_SERVICE_ADD bonjour <query hexdump> <RDATA hexdump>
1038         * P2P_SERVICE_ADD upnp <version hex> <service>
1039         *
1040         * e.g)
1041         * [Bonjour]
1042         * # IP Printing over TCP (PTR) (RDATA=MyPrinter._ipp._tcp.local.)
1043         * P2P_SERVICE_ADD bonjour 045f697070c00c000c01 094d795072696e746572c027
1044         * # IP Printing over TCP (TXT) (RDATA=txtvers=1,pdl=application/postscript)
1045         * P2P_SERVICE_ADD bonjour 096d797072696e746572045f697070c00c001001
1046         *  09747874766572733d311a70646c3d6170706c69636174696f6e2f706f7374736372797074
1047         *
1048         * [UPnP]
1049         * P2P_SERVICE_ADD upnp 10 uuid:6859dede-8574-59ab-9332-123456789012
1050         * P2P_SERVICE_ADD upnp 10 uuid:6859dede-8574-59ab-9332-123456789012::upnp:rootdevice
1051         * P2P_SERVICE_ADD upnp 10 uuid:6859dede-8574-59ab-9332-123456789012::urn:schemas-upnp
1052         * -org:device:InternetGatewayDevice:1
1053         * P2P_SERVICE_ADD upnp 10 uuid:6859dede-8574-59ab-9322-123456789012::urn:schemas-upnp
1054         * -org:service:ContentDirectory:2
1055         */
1056        for (String s : servInfo.getSupplicantQueryList()) {
1057            String command = "P2P_SERVICE_ADD";
1058            command += (" " + s);
1059            if (!doBooleanCommand(command)) {
1060                return false;
1061            }
1062        }
1063        return true;
1064    }
1065
1066    public boolean p2pServiceDel(WifiP2pServiceInfo servInfo) {
1067        /*
1068         * P2P_SERVICE_DEL bonjour <query hexdump>
1069         * P2P_SERVICE_DEL upnp <version hex> <service>
1070         */
1071        for (String s : servInfo.getSupplicantQueryList()) {
1072            String command = "P2P_SERVICE_DEL ";
1073
1074            String[] data = s.split(" ");
1075            if (data.length < 2) {
1076                return false;
1077            }
1078            if ("upnp".equals(data[0])) {
1079                command += s;
1080            } else if ("bonjour".equals(data[0])) {
1081                command += data[0];
1082                command += (" " + data[1]);
1083            } else {
1084                return false;
1085            }
1086            if (!doBooleanCommand(command)) {
1087                return false;
1088            }
1089        }
1090        return true;
1091    }
1092
1093    public boolean p2pServiceFlush() {
1094        return doBooleanCommand("P2P_SERVICE_FLUSH");
1095    }
1096
1097    public String p2pServDiscReq(String addr, String query) {
1098        String command = "P2P_SERV_DISC_REQ";
1099        command += (" " + addr);
1100        command += (" " + query);
1101
1102        return doStringCommand(command);
1103    }
1104
1105    public boolean p2pServDiscCancelReq(String id) {
1106        return doBooleanCommand("P2P_SERV_DISC_CANCEL_REQ " + id);
1107    }
1108
1109    /* Set the current mode of miracast operation.
1110     *  0 = disabled
1111     *  1 = operating as source
1112     *  2 = operating as sink
1113     */
1114    public void setMiracastMode(int mode) {
1115        // Note: optional feature on the driver. It is ok for this to fail.
1116        doBooleanCommand("DRIVER MIRACAST " + mode);
1117    }
1118
1119    public boolean fetchAnqp(String bssid, String subtypes) {
1120        return doBooleanCommand("ANQP_GET " + bssid + " " + subtypes);
1121    }
1122
1123    /* WIFI HAL support */
1124
1125    private static final String TAG = "WifiNative-HAL";
1126    private static long sWifiHalHandle = 0;  /* used by JNI to save wifi_handle */
1127    private static long[] sWifiIfaceHandles = null;  /* used by JNI to save interface handles */
1128    private static int sWlan0Index = -1;
1129    private static int sP2p0Index = -1;
1130
1131    private static boolean sHalIsStarted = false;
1132
1133    private static native boolean startHalNative();
1134    private static native void stopHalNative();
1135    private static native void waitForHalEventNative();
1136
1137    private static class MonitorThread extends Thread {
1138        public void run() {
1139            Log.i(TAG, "Waiting for HAL events mWifiHalHandle=" + Long.toString(sWifiHalHandle));
1140            waitForHalEventNative();
1141        }
1142    }
1143
1144    synchronized public static boolean startHal() {
1145        Log.i(TAG, "startHal");
1146        synchronized (mLock) {
1147            if (sHalIsStarted)
1148                return true;
1149            if (startHalNative()) {
1150                getInterfaces();
1151                new MonitorThread().start();
1152                sHalIsStarted = true;
1153                return true;
1154            } else {
1155                Log.i(TAG, "Could not start hal");
1156                sHalIsStarted = false;
1157                return false;
1158            }
1159        }
1160    }
1161
1162    synchronized public static void stopHal() {
1163        stopHalNative();
1164    }
1165
1166    private static native int getInterfacesNative();
1167
1168    synchronized public static int getInterfaces() {
1169        synchronized (mLock) {
1170            if (sWifiIfaceHandles == null) {
1171                int num = getInterfacesNative();
1172                int wifi_num = 0;
1173                for (int i = 0; i < num; i++) {
1174                    String name = getInterfaceNameNative(i);
1175                    Log.i(TAG, "interface[" + i + "] = " + name);
1176                    if (name.equals("wlan0")) {
1177                        sWlan0Index = i;
1178                        wifi_num++;
1179                    } else if (name.equals("p2p0")) {
1180                        sP2p0Index = i;
1181                        wifi_num++;
1182                    }
1183                }
1184                return wifi_num;
1185            } else {
1186                return sWifiIfaceHandles.length;
1187            }
1188        }
1189    }
1190
1191    private static native String getInterfaceNameNative(int index);
1192    synchronized public static String getInterfaceName(int index) {
1193        return getInterfaceNameNative(index);
1194    }
1195
1196    public static class ScanCapabilities {
1197        public int  max_scan_cache_size;                 // in number of scan results??
1198        public int  max_scan_buckets;
1199        public int  max_ap_cache_per_scan;
1200        public int  max_rssi_sample_size;
1201        public int  max_scan_reporting_threshold;        // in number of scan results??
1202        public int  max_hotlist_aps;
1203        public int  max_significant_wifi_change_aps;
1204    }
1205
1206    public static boolean getScanCapabilities(ScanCapabilities capabilities) {
1207        return getScanCapabilitiesNative(sWlan0Index, capabilities);
1208    }
1209
1210    private static native boolean getScanCapabilitiesNative(
1211            int iface, ScanCapabilities capabilities);
1212
1213    private static native boolean startScanNative(int iface, int id, ScanSettings settings);
1214    private static native boolean stopScanNative(int iface, int id);
1215    private static native ScanResult[] getScanResultsNative(int iface, boolean flush);
1216    private static native WifiLinkLayerStats getWifiLinkLayerStatsNative(int iface);
1217
1218    public static class ChannelSettings {
1219        int frequency;
1220        int dwell_time_ms;
1221        boolean passive;
1222    }
1223
1224    public static class BucketSettings {
1225        int bucket;
1226        int band;
1227        int period_ms;
1228        int report_events;
1229        int num_channels;
1230        ChannelSettings channels[];
1231    }
1232
1233    public static class ScanSettings {
1234        int base_period_ms;
1235        int max_ap_per_scan;
1236        int report_threshold;
1237        int num_buckets;
1238        BucketSettings buckets[];
1239    }
1240
1241    public static interface ScanEventHandler {
1242        void onScanResultsAvailable();
1243        void onFullScanResult(ScanResult fullScanResult);
1244        void onSingleScanComplete();
1245        void onScanPaused();
1246        void onScanRestarted();
1247    }
1248
1249    synchronized static void onScanResultsAvailable(int id) {
1250        if (sScanEventHandler  != null) {
1251            sScanEventHandler.onScanResultsAvailable();
1252        }
1253    }
1254
1255    /* scan status, keep these values in sync with gscan.h */
1256    private static int WIFI_SCAN_BUFFER_FULL = 0;
1257    private static int WIFI_SCAN_COMPLETE = 1;
1258
1259    synchronized static void onScanStatus(int status) {
1260        Log.i(TAG, "Got a scan status changed event, status = " + status);
1261
1262        if (status == WIFI_SCAN_BUFFER_FULL) {
1263            /* we have a separate event to take care of this */
1264        } else if (status == WIFI_SCAN_COMPLETE) {
1265            if (sScanEventHandler  != null) {
1266                sScanEventHandler.onSingleScanComplete();
1267            }
1268        }
1269    }
1270
1271    synchronized static void onFullScanResult(int id, ScanResult result, byte bytes[]) {
1272        if (DBG) Log.i(TAG, "Got a full scan results event, ssid = " + result.SSID + ", " +
1273                "num = " + bytes.length);
1274
1275        int num = 0;
1276        for (int i = 0; i < bytes.length; ) {
1277            num++;
1278            int type  = (int) bytes[i] & 0xFF;
1279            int len = (int) bytes[i + 1] & 0xFF;
1280            if (len < 0) {
1281                Log.e(TAG, "bad length; returning");
1282                return;
1283            }
1284            i += len + 2;
1285            if (DBG) Log.i(TAG, "bytes[" + i + "] = [" + type + ", " + len + "]" + ", " +
1286                    "next = " + i);
1287        }
1288
1289        ScanResult.InformationElement elements[] = new ScanResult.InformationElement[num];
1290        for (int i = 0, index = 0; i < num; i++) {
1291            int type  = (int) bytes[index] & 0xFF;
1292            int len = (int) bytes[index + 1] & 0xFF;
1293            if (DBG) Log.i(TAG, "index = " + index + ", type = " + type + ", len = " + len);
1294            ScanResult.InformationElement elem = new ScanResult.InformationElement();
1295            elem.id = type;
1296            elem.bytes = new byte[len];
1297            for (int j = 0; j < len; j++) {
1298                elem.bytes[j] = bytes[index + j + 2];
1299            }
1300            elements[i] = elem;
1301            index += (len + 2);
1302        }
1303
1304        result.informationElements = elements;
1305        if (sScanEventHandler  != null) {
1306            sScanEventHandler.onFullScanResult(result);
1307        }
1308    }
1309
1310    private static int sScanCmdId = 0;
1311    private static ScanEventHandler sScanEventHandler;
1312    private static ScanSettings sScanSettings;
1313
1314    synchronized public static boolean startScan(
1315            ScanSettings settings, ScanEventHandler eventHandler) {
1316        synchronized (mLock) {
1317
1318            if (sScanCmdId != 0) {
1319                stopScan();
1320            } else if (sScanSettings != null || sScanEventHandler != null) {
1321                /* current scan is paused; no need to stop it */
1322            }
1323
1324            sScanCmdId = getNewCmdIdLocked();
1325
1326            sScanSettings = settings;
1327            sScanEventHandler = eventHandler;
1328
1329            if (startScanNative(sWlan0Index, sScanCmdId, settings) == false) {
1330                sScanEventHandler = null;
1331                sScanSettings = null;
1332                return false;
1333            }
1334
1335            return true;
1336        }
1337    }
1338
1339    synchronized public static void stopScan() {
1340        synchronized (mLock) {
1341            stopScanNative(sWlan0Index, sScanCmdId);
1342            sScanSettings = null;
1343            sScanEventHandler = null;
1344            sScanCmdId = 0;
1345        }
1346    }
1347
1348    synchronized public static void pauseScan() {
1349        synchronized (mLock) {
1350            if (sScanCmdId != 0 && sScanSettings != null && sScanEventHandler != null) {
1351                Log.d(TAG, "Pausing scan");
1352                stopScanNative(sWlan0Index, sScanCmdId);
1353                sScanCmdId = 0;
1354                sScanEventHandler.onScanPaused();
1355            }
1356        }
1357    }
1358
1359    synchronized public static void restartScan() {
1360        synchronized (mLock) {
1361            if (sScanCmdId == 0 && sScanSettings != null && sScanEventHandler != null) {
1362                Log.d(TAG, "Restarting scan");
1363                startScan(sScanSettings, sScanEventHandler);
1364                sScanEventHandler.onScanRestarted();
1365            }
1366        }
1367    }
1368
1369    synchronized public static ScanResult[] getScanResults() {
1370        synchronized (mLock) {
1371            return getScanResultsNative(sWlan0Index, /* flush = */ false);
1372        }
1373    }
1374
1375    public static interface HotlistEventHandler {
1376        void onHotlistApFound (ScanResult[]result);
1377    }
1378
1379    private static int sHotlistCmdId = 0;
1380    private static HotlistEventHandler sHotlistEventHandler;
1381
1382    private native static boolean setHotlistNative(int iface, int id,
1383            WifiScanner.HotlistSettings settings);
1384    private native static boolean resetHotlistNative(int iface, int id);
1385
1386    synchronized public static boolean setHotlist(WifiScanner.HotlistSettings settings,
1387                                    HotlistEventHandler eventHandler) {
1388        synchronized (mLock) {
1389            if (sHotlistCmdId != 0) {
1390                return false;
1391            } else {
1392                sHotlistCmdId = getNewCmdIdLocked();
1393            }
1394
1395            sHotlistEventHandler = eventHandler;
1396            if (setHotlistNative(sWlan0Index, sScanCmdId, settings) == false) {
1397                sHotlistEventHandler = null;
1398                return false;
1399            }
1400
1401            return true;
1402        }
1403    }
1404
1405    synchronized public static void resetHotlist() {
1406        synchronized (mLock) {
1407            if (sHotlistCmdId != 0) {
1408                resetHotlistNative(sWlan0Index, sHotlistCmdId);
1409                sHotlistCmdId = 0;
1410                sHotlistEventHandler = null;
1411            }
1412        }
1413    }
1414
1415    synchronized public static void onHotlistApFound(int id, ScanResult[] results) {
1416        synchronized (mLock) {
1417            if (sHotlistCmdId != 0) {
1418                sHotlistEventHandler.onHotlistApFound(results);
1419            } else {
1420                /* this can happen because of race conditions */
1421                Log.d(TAG, "Ignoring hotlist AP found change");
1422            }
1423        }
1424    }
1425
1426    public static interface SignificantWifiChangeEventHandler {
1427        void onChangesFound(ScanResult[] result);
1428    }
1429
1430    private static SignificantWifiChangeEventHandler sSignificantWifiChangeHandler;
1431    private static int sSignificantWifiChangeCmdId;
1432
1433    private static native boolean trackSignificantWifiChangeNative(
1434            int iface, int id, WifiScanner.WifiChangeSettings settings);
1435    private static native boolean untrackSignificantWifiChangeNative(int iface, int id);
1436
1437    synchronized public static boolean trackSignificantWifiChange(
1438            WifiScanner.WifiChangeSettings settings, SignificantWifiChangeEventHandler handler) {
1439        synchronized (mLock) {
1440            if (sSignificantWifiChangeCmdId != 0) {
1441                return false;
1442            } else {
1443                sSignificantWifiChangeCmdId = getNewCmdIdLocked();
1444            }
1445
1446            sSignificantWifiChangeHandler = handler;
1447            if (trackSignificantWifiChangeNative(sWlan0Index, sScanCmdId, settings) == false) {
1448                sSignificantWifiChangeHandler = null;
1449                return false;
1450            }
1451
1452            return true;
1453        }
1454    }
1455
1456    synchronized static void untrackSignificantWifiChange() {
1457        synchronized (mLock) {
1458            if (sSignificantWifiChangeCmdId != 0) {
1459                untrackSignificantWifiChangeNative(sWlan0Index, sSignificantWifiChangeCmdId);
1460                sSignificantWifiChangeCmdId = 0;
1461                sSignificantWifiChangeHandler = null;
1462            }
1463        }
1464    }
1465
1466    synchronized static void onSignificantWifiChange(int id, ScanResult[] results) {
1467        synchronized (mLock) {
1468            if (sSignificantWifiChangeCmdId != 0) {
1469                sSignificantWifiChangeHandler.onChangesFound(results);
1470            } else {
1471                /* this can happen because of race conditions */
1472                Log.d(TAG, "Ignoring significant wifi change");
1473            }
1474        }
1475    }
1476
1477    synchronized public static WifiLinkLayerStats getWifiLinkLayerStats(String iface) {
1478        // TODO: use correct iface name to Index translation
1479        if (iface == null) return null;
1480        synchronized (mLock) {
1481            if (!sHalIsStarted)
1482                startHal();
1483            if (sHalIsStarted)
1484                return getWifiLinkLayerStatsNative(sWlan0Index);
1485        }
1486        return null;
1487    }
1488
1489    /*
1490     * NFC-related calls
1491     */
1492    public String getNfcWpsConfigurationToken(int netId) {
1493        return doStringCommand("WPS_NFC_CONFIG_TOKEN WPS " + netId);
1494    }
1495
1496    public String getNfcHandoverRequest() {
1497        return doStringCommand("NFC_GET_HANDOVER_REQ NDEF P2P-CR");
1498    }
1499
1500    public String getNfcHandoverSelect() {
1501        return doStringCommand("NFC_GET_HANDOVER_SEL NDEF P2P-CR");
1502    }
1503
1504    public boolean initiatorReportNfcHandover(String selectMessage) {
1505        return doBooleanCommand("NFC_REPORT_HANDOVER INIT P2P 00 " + selectMessage);
1506    }
1507
1508    public boolean responderReportNfcHandover(String requestMessage) {
1509        return doBooleanCommand("NFC_REPORT_HANDOVER RESP P2P " + requestMessage + " 00");
1510    }
1511
1512    public static native int getSupportedFeatureSetNative(int iface);
1513    synchronized public static int getSupportedFeatureSet() {
1514        return getSupportedFeatureSetNative(sWlan0Index);
1515    }
1516
1517    /* Rtt related commands/events */
1518    public static interface RttEventHandler {
1519        void onRttResults(RttManager.RttResult[] result);
1520    }
1521
1522    private static RttEventHandler sRttEventHandler;
1523    private static int sRttCmdId;
1524
1525    synchronized private static void onRttResults(int id, RttManager.RttResult[] results) {
1526        if (id == sRttCmdId) {
1527            Log.d(TAG, "Received " + results.length + " rtt results");
1528            sRttEventHandler.onRttResults(results);
1529            sRttCmdId = 0;
1530        } else {
1531            Log.d(TAG, "Received event for unknown cmd = " + id + ", current id = " + sRttCmdId);
1532        }
1533    }
1534
1535    private static native boolean requestRangeNative(
1536            int iface, int id, RttManager.RttParams[] params);
1537    private static native boolean cancelRangeRequestNative(
1538            int iface, int id, RttManager.RttParams[] params);
1539
1540    synchronized public static boolean requestRtt(
1541            RttManager.RttParams[] params, RttEventHandler handler) {
1542        synchronized (mLock) {
1543            if (sRttCmdId != 0) {
1544                return false;
1545            } else {
1546                sRttCmdId = getNewCmdIdLocked();
1547            }
1548            sRttEventHandler = handler;
1549            return requestRangeNative(sWlan0Index, sRttCmdId, params);
1550        }
1551    }
1552
1553    synchronized public static boolean cancelRtt(RttManager.RttParams[] params) {
1554        synchronized(mLock) {
1555            if (sRttCmdId == 0) {
1556                return false;
1557            }
1558
1559            if (cancelRangeRequestNative(sWlan0Index, sRttCmdId, params)) {
1560                sRttEventHandler = null;
1561                return true;
1562            } else {
1563                return false;
1564            }
1565        }
1566    }
1567
1568    private static native boolean setScanningMacOuiNative(int iface, byte[] oui);
1569
1570    synchronized public static boolean setScanningMacOui(byte[] oui) {
1571        synchronized (mLock) {
1572            if (startHal()) {
1573                return setScanningMacOuiNative(sWlan0Index, oui);
1574            } else {
1575                return false;
1576            }
1577        }
1578    }
1579
1580    private static native int[] getChannelsForBandNative(
1581            int iface, int band);
1582
1583    synchronized public static int [] getChannelsForBand(int band) {
1584        synchronized (mLock) {
1585            if (startHal()) {
1586                return getChannelsForBandNative(sWlan0Index, band);
1587            } else {
1588                return null;
1589            }
1590        }
1591    }
1592}
1593