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