WifiNative.java revision e4e3750390bec0a849a9153348b7c21b2cc8b843
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:" + Thread.currentThread().getStackTrace()[2].getMethodName() +" - "+ Thread.currentThread().getStackTrace()[3].getMethodName() +" - "+ Thread.currentThread().getStackTrace()[4].getMethodName() +" - "+ Thread.currentThread().getStackTrace()[5].getMethodName()+" - "+ Thread.currentThread().getStackTrace()[6].getMethodName());
252
253    }
254    public boolean enableNetwork(int netId, boolean disableOthers) {
255        if (DBG) logDbg("enableNetwork nid=" + Integer.toString(netId) + " disableOthers=" + disableOthers);
256        if (disableOthers) {
257            return doBooleanCommand("SELECT_NETWORK " + netId);
258        } else {
259            return doBooleanCommand("ENABLE_NETWORK " + netId);
260        }
261    }
262
263    public boolean disableNetwork(int netId) {
264        if (DBG) logDbg("disableNetwork nid=" + Integer.toString(netId));
265        return doBooleanCommand("DISABLE_NETWORK " + netId);
266    }
267
268    public boolean reconnect() {
269        if (DBG) logDbg("RECONNECT ");
270        return doBooleanCommand("RECONNECT");
271    }
272
273    public boolean reassociate() {
274        if (DBG) logDbg("REASSOCIATE ");
275        return doBooleanCommand("REASSOCIATE");
276    }
277
278    public boolean disconnect() {
279        if (DBG) logDbg("RECONNECT ");
280        return doBooleanCommand("DISCONNECT");
281    }
282
283    public String status() {
284        return doStringCommand("STATUS");
285    }
286
287    public String getMacAddress() {
288        //Macaddr = XX.XX.XX.XX.XX.XX
289        String ret = doStringCommand("DRIVER MACADDR");
290        if (!TextUtils.isEmpty(ret)) {
291            String[] tokens = ret.split(" = ");
292            if (tokens.length == 2) return tokens[1];
293        }
294        return null;
295    }
296
297    /**
298     * Format of results:
299     * =================
300     * id=1
301     * bssid=68:7f:74:d7:1b:6e
302     * freq=2412
303     * level=-43
304     * tsf=1344621975160944
305     * age=2623
306     * flags=[WPA2-PSK-CCMP][WPS][ESS]
307     * ssid=zubyb
308     * ====
309     *
310     * RANGE=ALL gets all scan results
311     * RANGE=ID- gets results from ID
312     * MASK=<N> see wpa_supplicant/src/common/wpa_ctrl.h for details
313     */
314    public String scanResults(int sid) {
315        return doStringCommandWithoutLogging("BSS RANGE=" + sid + "- MASK=0x21987");
316    }
317
318    /**
319     * Format of command
320     * DRIVER WLS_BATCHING SET SCANFREQ=x MSCAN=r BESTN=y CHANNEL=<z, w, t> RTT=s
321     * where x is an ascii representation of an integer number of seconds between scans
322     *       r is an ascii representation of an integer number of scans per batch
323     *       y is an ascii representation of an integer number of the max AP to remember per scan
324     *       z, w, t represent a 1..n size list of channel numbers and/or 'A', 'B' values
325     *           indicating entire ranges of channels
326     *       s is an ascii representation of an integer number of highest-strength AP
327     *           for which we'd like approximate distance reported
328     *
329     * The return value is an ascii integer representing a guess of the number of scans
330     * the firmware can remember before it runs out of buffer space or -1 on error
331     */
332    public String setBatchedScanSettings(BatchedScanSettings settings) {
333        if (settings == null) {
334            return doStringCommand("DRIVER WLS_BATCHING STOP");
335        }
336        String cmd = "DRIVER WLS_BATCHING SET SCANFREQ=" + settings.scanIntervalSec;
337        cmd += " MSCAN=" + settings.maxScansPerBatch;
338        if (settings.maxApPerScan != BatchedScanSettings.UNSPECIFIED) {
339            cmd += " BESTN=" + settings.maxApPerScan;
340        }
341        if (settings.channelSet != null && !settings.channelSet.isEmpty()) {
342            cmd += " CHANNEL=<";
343            int i = 0;
344            for (String channel : settings.channelSet) {
345                cmd += (i > 0 ? "," : "") + channel;
346                ++i;
347            }
348            cmd += ">";
349        }
350        if (settings.maxApForDistance != BatchedScanSettings.UNSPECIFIED) {
351            cmd += " RTT=" + settings.maxApForDistance;
352        }
353        return doStringCommand(cmd);
354    }
355
356    public String getBatchedScanResults() {
357        return doStringCommand("DRIVER WLS_BATCHING GET");
358    }
359
360    public boolean startDriver() {
361        return doBooleanCommand("DRIVER START");
362    }
363
364    public boolean stopDriver() {
365        return doBooleanCommand("DRIVER STOP");
366    }
367
368
369    /**
370     * Start filtering out Multicast V4 packets
371     * @return {@code true} if the operation succeeded, {@code false} otherwise
372     *
373     * Multicast filtering rules work as follows:
374     *
375     * The driver can filter multicast (v4 and/or v6) and broadcast packets when in
376     * a power optimized mode (typically when screen goes off).
377     *
378     * In order to prevent the driver from filtering the multicast/broadcast packets, we have to
379     * add a DRIVER RXFILTER-ADD rule followed by DRIVER RXFILTER-START to make the rule effective
380     *
381     * DRIVER RXFILTER-ADD Num
382     *   where Num = 0 - Unicast, 1 - Broadcast, 2 - Mutil4 or 3 - Multi6
383     *
384     * and DRIVER RXFILTER-START
385     * In order to stop the usage of these rules, we do
386     *
387     * DRIVER RXFILTER-STOP
388     * DRIVER RXFILTER-REMOVE Num
389     *   where Num is as described for RXFILTER-ADD
390     *
391     * The  SETSUSPENDOPT driver command overrides the filtering rules
392     */
393    public boolean startFilteringMulticastV4Packets() {
394        return doBooleanCommand("DRIVER RXFILTER-STOP")
395            && doBooleanCommand("DRIVER RXFILTER-REMOVE 2")
396            && doBooleanCommand("DRIVER RXFILTER-START");
397    }
398
399    /**
400     * Stop filtering out Multicast V4 packets.
401     * @return {@code true} if the operation succeeded, {@code false} otherwise
402     */
403    public boolean stopFilteringMulticastV4Packets() {
404        return doBooleanCommand("DRIVER RXFILTER-STOP")
405            && doBooleanCommand("DRIVER RXFILTER-ADD 2")
406            && doBooleanCommand("DRIVER RXFILTER-START");
407    }
408
409    /**
410     * Start filtering out Multicast V6 packets
411     * @return {@code true} if the operation succeeded, {@code false} otherwise
412     */
413    public boolean startFilteringMulticastV6Packets() {
414        return doBooleanCommand("DRIVER RXFILTER-STOP")
415            && doBooleanCommand("DRIVER RXFILTER-REMOVE 3")
416            && doBooleanCommand("DRIVER RXFILTER-START");
417    }
418
419    /**
420     * Stop filtering out Multicast V6 packets.
421     * @return {@code true} if the operation succeeded, {@code false} otherwise
422     */
423    public boolean stopFilteringMulticastV6Packets() {
424        return doBooleanCommand("DRIVER RXFILTER-STOP")
425            && doBooleanCommand("DRIVER RXFILTER-ADD 3")
426            && doBooleanCommand("DRIVER RXFILTER-START");
427    }
428
429    public int getBand() {
430       String ret = doStringCommand("DRIVER GETBAND");
431        if (!TextUtils.isEmpty(ret)) {
432            //reply is "BAND X" where X is the band
433            String[] tokens = ret.split(" ");
434            try {
435                if (tokens.length == 2) return Integer.parseInt(tokens[1]);
436            } catch (NumberFormatException e) {
437                return -1;
438            }
439        }
440        return -1;
441    }
442
443    public boolean setBand(int band) {
444        return doBooleanCommand("DRIVER SETBAND " + band);
445    }
446
447    /**
448      * Sets the bluetooth coexistence mode.
449      *
450      * @param mode One of {@link #BLUETOOTH_COEXISTENCE_MODE_DISABLED},
451      *            {@link #BLUETOOTH_COEXISTENCE_MODE_ENABLED}, or
452      *            {@link #BLUETOOTH_COEXISTENCE_MODE_SENSE}.
453      * @return Whether the mode was successfully set.
454      */
455    public boolean setBluetoothCoexistenceMode(int mode) {
456        return doBooleanCommand("DRIVER BTCOEXMODE " + mode);
457    }
458
459    /**
460     * Enable or disable Bluetooth coexistence scan mode. When this mode is on,
461     * some of the low-level scan parameters used by the driver are changed to
462     * reduce interference with A2DP streaming.
463     *
464     * @param isSet whether to enable or disable this mode
465     * @return {@code true} if the command succeeded, {@code false} otherwise.
466     */
467    public boolean setBluetoothCoexistenceScanMode(boolean setCoexScanMode) {
468        if (setCoexScanMode) {
469            return doBooleanCommand("DRIVER BTCOEXSCAN-START");
470        } else {
471            return doBooleanCommand("DRIVER BTCOEXSCAN-STOP");
472        }
473    }
474
475    public void enableSaveConfig() {
476        doBooleanCommand("SET update_config 1");
477    }
478
479    public boolean saveConfig() {
480        return doBooleanCommand("SAVE_CONFIG");
481    }
482
483    public boolean addToBlacklist(String bssid) {
484        if (TextUtils.isEmpty(bssid)) return false;
485        return doBooleanCommand("BLACKLIST " + bssid);
486    }
487
488    public boolean clearBlacklist() {
489        return doBooleanCommand("BLACKLIST clear");
490    }
491
492    public boolean setSuspendOptimizations(boolean enabled) {
493       // if (mSuspendOptEnabled == enabled) return true;
494        mSuspendOptEnabled = enabled;
495
496        Log.e("native", "do suspend " + enabled);
497        if (enabled) {
498            return doBooleanCommand("DRIVER SETSUSPENDMODE 1");
499        } else {
500            return doBooleanCommand("DRIVER SETSUSPENDMODE 0");
501        }
502    }
503
504    public boolean setCountryCode(String countryCode) {
505        return doBooleanCommand("DRIVER COUNTRY " + countryCode.toUpperCase(Locale.ROOT));
506    }
507
508    public void enableBackgroundScan(boolean enable) {
509        if (enable) {
510            doBooleanCommand("SET pno 1");
511        } else {
512            doBooleanCommand("SET pno 0");
513        }
514    }
515
516    public void enableAutoConnect(boolean enable) {
517        if (enable) {
518            doBooleanCommand("STA_AUTOCONNECT 1");
519        } else {
520            doBooleanCommand("STA_AUTOCONNECT 0");
521        }
522    }
523
524    public void setScanInterval(int scanInterval) {
525        doBooleanCommand("SCAN_INTERVAL " + scanInterval);
526    }
527
528    public void startTdls(String macAddr, boolean enable) {
529        if (enable) {
530            doBooleanCommand("TDLS_DISCOVER " + macAddr);
531            doBooleanCommand("TDLS_SETUP " + macAddr);
532        } else {
533            doBooleanCommand("TDLS_TEARDOWN " + macAddr);
534        }
535    }
536
537    /** Example output:
538     * RSSI=-65
539     * LINKSPEED=48
540     * NOISE=9999
541     * FREQUENCY=0
542     */
543    public String signalPoll() {
544        return doStringCommandWithoutLogging("SIGNAL_POLL");
545    }
546
547    /** Example outout:
548     * TXGOOD=396
549     * TXBAD=1
550     */
551    public String pktcntPoll() {
552        return doStringCommand("PKTCNT_POLL");
553    }
554
555    public void bssFlush() {
556        doBooleanCommand("BSS_FLUSH 0");
557    }
558
559    public boolean startWpsPbc(String bssid) {
560        if (TextUtils.isEmpty(bssid)) {
561            return doBooleanCommand("WPS_PBC");
562        } else {
563            return doBooleanCommand("WPS_PBC " + bssid);
564        }
565    }
566
567    public boolean startWpsPbc(String iface, String bssid) {
568        synchronized (mLock) {
569            if (TextUtils.isEmpty(bssid)) {
570                return doBooleanCommandNative("IFNAME=" + iface + " WPS_PBC");
571            } else {
572                return doBooleanCommandNative("IFNAME=" + iface + " WPS_PBC " + bssid);
573            }
574        }
575    }
576
577    public boolean startWpsPinKeypad(String pin) {
578        if (TextUtils.isEmpty(pin)) return false;
579        return doBooleanCommand("WPS_PIN any " + pin);
580    }
581
582    public boolean startWpsPinKeypad(String iface, String pin) {
583        if (TextUtils.isEmpty(pin)) return false;
584        synchronized (mLock) {
585            return doBooleanCommandNative("IFNAME=" + iface + " WPS_PIN any " + pin);
586        }
587    }
588
589
590    public String startWpsPinDisplay(String bssid) {
591        if (TextUtils.isEmpty(bssid)) {
592            return doStringCommand("WPS_PIN any");
593        } else {
594            return doStringCommand("WPS_PIN " + bssid);
595        }
596    }
597
598    public String startWpsPinDisplay(String iface, String bssid) {
599        synchronized (mLock) {
600            if (TextUtils.isEmpty(bssid)) {
601                return doStringCommandNative("IFNAME=" + iface + " WPS_PIN any");
602            } else {
603                return doStringCommandNative("IFNAME=" + iface + " WPS_PIN " + bssid);
604            }
605        }
606    }
607
608    /* Configures an access point connection */
609    public boolean startWpsRegistrar(String bssid, String pin) {
610        if (TextUtils.isEmpty(bssid) || TextUtils.isEmpty(pin)) return false;
611        return doBooleanCommand("WPS_REG " + bssid + " " + pin);
612    }
613
614    public boolean cancelWps() {
615        return doBooleanCommand("WPS_CANCEL");
616    }
617
618    public boolean setPersistentReconnect(boolean enabled) {
619        int value = (enabled == true) ? 1 : 0;
620        return doBooleanCommand("SET persistent_reconnect " + value);
621    }
622
623    public boolean setDeviceName(String name) {
624        return doBooleanCommand("SET device_name " + name);
625    }
626
627    public boolean setDeviceType(String type) {
628        return doBooleanCommand("SET device_type " + type);
629    }
630
631    public boolean setConfigMethods(String cfg) {
632        return doBooleanCommand("SET config_methods " + cfg);
633    }
634
635    public boolean setManufacturer(String value) {
636        return doBooleanCommand("SET manufacturer " + value);
637    }
638
639    public boolean setModelName(String value) {
640        return doBooleanCommand("SET model_name " + value);
641    }
642
643    public boolean setModelNumber(String value) {
644        return doBooleanCommand("SET model_number " + value);
645    }
646
647    public boolean setSerialNumber(String value) {
648        return doBooleanCommand("SET serial_number " + value);
649    }
650
651    public boolean setP2pSsidPostfix(String postfix) {
652        return doBooleanCommand("SET p2p_ssid_postfix " + postfix);
653    }
654
655    public boolean setP2pGroupIdle(String iface, int time) {
656        synchronized (mLock) {
657            return doBooleanCommandNative("IFNAME=" + iface + " SET p2p_group_idle " + time);
658        }
659    }
660
661    public void setPowerSave(boolean enabled) {
662        if (enabled) {
663            doBooleanCommand("SET ps 1");
664        } else {
665            doBooleanCommand("SET ps 0");
666        }
667    }
668
669    public boolean setP2pPowerSave(String iface, boolean enabled) {
670        synchronized (mLock) {
671            if (enabled) {
672                return doBooleanCommandNative("IFNAME=" + iface + " P2P_SET ps 1");
673            } else {
674                return doBooleanCommandNative("IFNAME=" + iface + " P2P_SET ps 0");
675            }
676        }
677    }
678
679    public boolean setWfdEnable(boolean enable) {
680        return doBooleanCommand("SET wifi_display " + (enable ? "1" : "0"));
681    }
682
683    public boolean setWfdDeviceInfo(String hex) {
684        return doBooleanCommand("WFD_SUBELEM_SET 0 " + hex);
685    }
686
687    /**
688     * "sta" prioritizes STA connection over P2P and "p2p" prioritizes
689     * P2P connection over STA
690     */
691    public boolean setConcurrencyPriority(String s) {
692        return doBooleanCommand("P2P_SET conc_pref " + s);
693    }
694
695    public boolean p2pFind() {
696        return doBooleanCommand("P2P_FIND");
697    }
698
699    public boolean p2pFind(int timeout) {
700        if (timeout <= 0) {
701            return p2pFind();
702        }
703        return doBooleanCommand("P2P_FIND " + timeout);
704    }
705
706    public boolean p2pStopFind() {
707       return doBooleanCommand("P2P_STOP_FIND");
708    }
709
710    public boolean p2pListen() {
711        return doBooleanCommand("P2P_LISTEN");
712    }
713
714    public boolean p2pListen(int timeout) {
715        if (timeout <= 0) {
716            return p2pListen();
717        }
718        return doBooleanCommand("P2P_LISTEN " + timeout);
719    }
720
721    public boolean p2pExtListen(boolean enable, int period, int interval) {
722        if (enable && interval < period) {
723            return false;
724        }
725        return doBooleanCommand("P2P_EXT_LISTEN"
726                    + (enable ? (" " + period + " " + interval) : ""));
727    }
728
729    public boolean p2pSetChannel(int lc, int oc) {
730        if (DBG) Log.d(mTAG, "p2pSetChannel: lc="+lc+", oc="+oc);
731
732        if (lc >=1 && lc <= 11) {
733            if (!doBooleanCommand("P2P_SET listen_channel " + lc)) {
734                return false;
735            }
736        } else if (lc != 0) {
737            return false;
738        }
739
740        if (oc >= 1 && oc <= 165 ) {
741            int freq = (oc <= 14 ? 2407 : 5000) + oc * 5;
742            return doBooleanCommand("P2P_SET disallow_freq 1000-"
743                    + (freq - 5) + "," + (freq + 5) + "-6000");
744        } else if (oc == 0) {
745            /* oc==0 disables "P2P_SET disallow_freq" (enables all freqs) */
746            return doBooleanCommand("P2P_SET disallow_freq \"\"");
747        }
748
749        return false;
750    }
751
752    public boolean p2pFlush() {
753        return doBooleanCommand("P2P_FLUSH");
754    }
755
756    /* p2p_connect <peer device address> <pbc|pin|PIN#> [label|display|keypad]
757        [persistent] [join|auth] [go_intent=<0..15>] [freq=<in MHz>] */
758    public String p2pConnect(WifiP2pConfig config, boolean joinExistingGroup) {
759        if (config == null) return null;
760        List<String> args = new ArrayList<String>();
761        WpsInfo wps = config.wps;
762        args.add(config.deviceAddress);
763
764        switch (wps.setup) {
765            case WpsInfo.PBC:
766                args.add("pbc");
767                break;
768            case WpsInfo.DISPLAY:
769                if (TextUtils.isEmpty(wps.pin)) {
770                    args.add("pin");
771                } else {
772                    args.add(wps.pin);
773                }
774                args.add("display");
775                break;
776            case WpsInfo.KEYPAD:
777                args.add(wps.pin);
778                args.add("keypad");
779                break;
780            case WpsInfo.LABEL:
781                args.add(wps.pin);
782                args.add("label");
783            default:
784                break;
785        }
786
787        if (config.netId == WifiP2pGroup.PERSISTENT_NET_ID) {
788            args.add("persistent");
789        }
790
791        if (joinExistingGroup) {
792            args.add("join");
793        } else {
794            //TODO: This can be adapted based on device plugged in state and
795            //device battery state
796            int groupOwnerIntent = config.groupOwnerIntent;
797            if (groupOwnerIntent < 0 || groupOwnerIntent > 15) {
798                groupOwnerIntent = DEFAULT_GROUP_OWNER_INTENT;
799            }
800            args.add("go_intent=" + groupOwnerIntent);
801        }
802
803        String command = "P2P_CONNECT ";
804        for (String s : args) command += s + " ";
805
806        return doStringCommand(command);
807    }
808
809    public boolean p2pCancelConnect() {
810        return doBooleanCommand("P2P_CANCEL");
811    }
812
813    public boolean p2pProvisionDiscovery(WifiP2pConfig config) {
814        if (config == null) return false;
815
816        switch (config.wps.setup) {
817            case WpsInfo.PBC:
818                return doBooleanCommand("P2P_PROV_DISC " + config.deviceAddress + " pbc");
819            case WpsInfo.DISPLAY:
820                //We are doing display, so provision discovery is keypad
821                return doBooleanCommand("P2P_PROV_DISC " + config.deviceAddress + " keypad");
822            case WpsInfo.KEYPAD:
823                //We are doing keypad, so provision discovery is display
824                return doBooleanCommand("P2P_PROV_DISC " + config.deviceAddress + " display");
825            default:
826                break;
827        }
828        return false;
829    }
830
831    public boolean p2pGroupAdd(boolean persistent) {
832        if (persistent) {
833            return doBooleanCommand("P2P_GROUP_ADD persistent");
834        }
835        return doBooleanCommand("P2P_GROUP_ADD");
836    }
837
838    public boolean p2pGroupAdd(int netId) {
839        return doBooleanCommand("P2P_GROUP_ADD persistent=" + netId);
840    }
841
842    public boolean p2pGroupRemove(String iface) {
843        if (TextUtils.isEmpty(iface)) return false;
844        synchronized (mLock) {
845            return doBooleanCommandNative("IFNAME=" + iface + " P2P_GROUP_REMOVE " + iface);
846        }
847    }
848
849    public boolean p2pReject(String deviceAddress) {
850        return doBooleanCommand("P2P_REJECT " + deviceAddress);
851    }
852
853    /* Invite a peer to a group */
854    public boolean p2pInvite(WifiP2pGroup group, String deviceAddress) {
855        if (TextUtils.isEmpty(deviceAddress)) return false;
856
857        if (group == null) {
858            return doBooleanCommand("P2P_INVITE peer=" + deviceAddress);
859        } else {
860            return doBooleanCommand("P2P_INVITE group=" + group.getInterface()
861                    + " peer=" + deviceAddress + " go_dev_addr=" + group.getOwner().deviceAddress);
862        }
863    }
864
865    /* Reinvoke a persistent connection */
866    public boolean p2pReinvoke(int netId, String deviceAddress) {
867        if (TextUtils.isEmpty(deviceAddress) || netId < 0) return false;
868
869        return doBooleanCommand("P2P_INVITE persistent=" + netId + " peer=" + deviceAddress);
870    }
871
872    public String p2pGetSsid(String deviceAddress) {
873        return p2pGetParam(deviceAddress, "oper_ssid");
874    }
875
876    public String p2pGetDeviceAddress() {
877        String status = status();
878        if (status == null) return "";
879
880        String[] tokens = status.split("\n");
881        for (String token : tokens) {
882            if (token.startsWith("p2p_device_address=")) {
883                String[] nameValue = token.split("=");
884                if (nameValue.length != 2) break;
885                return nameValue[1];
886            }
887        }
888        return "";
889    }
890
891    public int getGroupCapability(String deviceAddress) {
892        int gc = 0;
893        if (TextUtils.isEmpty(deviceAddress)) return gc;
894        String peerInfo = p2pPeer(deviceAddress);
895        if (TextUtils.isEmpty(peerInfo)) return gc;
896
897        String[] tokens = peerInfo.split("\n");
898        for (String token : tokens) {
899            if (token.startsWith("group_capab=")) {
900                String[] nameValue = token.split("=");
901                if (nameValue.length != 2) break;
902                try {
903                    return Integer.decode(nameValue[1]);
904                } catch(NumberFormatException e) {
905                    return gc;
906                }
907            }
908        }
909        return gc;
910    }
911
912    public String p2pPeer(String deviceAddress) {
913        return doStringCommand("P2P_PEER " + deviceAddress);
914    }
915
916    private String p2pGetParam(String deviceAddress, String key) {
917        if (deviceAddress == null) return null;
918
919        String peerInfo = p2pPeer(deviceAddress);
920        if (peerInfo == null) return null;
921        String[] tokens= peerInfo.split("\n");
922
923        key += "=";
924        for (String token : tokens) {
925            if (token.startsWith(key)) {
926                String[] nameValue = token.split("=");
927                if (nameValue.length != 2) break;
928                return nameValue[1];
929            }
930        }
931        return null;
932    }
933
934    public boolean p2pServiceAdd(WifiP2pServiceInfo servInfo) {
935        /*
936         * P2P_SERVICE_ADD bonjour <query hexdump> <RDATA hexdump>
937         * P2P_SERVICE_ADD upnp <version hex> <service>
938         *
939         * e.g)
940         * [Bonjour]
941         * # IP Printing over TCP (PTR) (RDATA=MyPrinter._ipp._tcp.local.)
942         * P2P_SERVICE_ADD bonjour 045f697070c00c000c01 094d795072696e746572c027
943         * # IP Printing over TCP (TXT) (RDATA=txtvers=1,pdl=application/postscript)
944         * P2P_SERVICE_ADD bonjour 096d797072696e746572045f697070c00c001001
945         *  09747874766572733d311a70646c3d6170706c69636174696f6e2f706f7374736372797074
946         *
947         * [UPnP]
948         * P2P_SERVICE_ADD upnp 10 uuid:6859dede-8574-59ab-9332-123456789012
949         * P2P_SERVICE_ADD upnp 10 uuid:6859dede-8574-59ab-9332-123456789012::upnp:rootdevice
950         * P2P_SERVICE_ADD upnp 10 uuid:6859dede-8574-59ab-9332-123456789012::urn:schemas-upnp
951         * -org:device:InternetGatewayDevice:1
952         * P2P_SERVICE_ADD upnp 10 uuid:6859dede-8574-59ab-9322-123456789012::urn:schemas-upnp
953         * -org:service:ContentDirectory:2
954         */
955        for (String s : servInfo.getSupplicantQueryList()) {
956            String command = "P2P_SERVICE_ADD";
957            command += (" " + s);
958            if (!doBooleanCommand(command)) {
959                return false;
960            }
961        }
962        return true;
963    }
964
965    public boolean p2pServiceDel(WifiP2pServiceInfo servInfo) {
966        /*
967         * P2P_SERVICE_DEL bonjour <query hexdump>
968         * P2P_SERVICE_DEL upnp <version hex> <service>
969         */
970        for (String s : servInfo.getSupplicantQueryList()) {
971            String command = "P2P_SERVICE_DEL ";
972
973            String[] data = s.split(" ");
974            if (data.length < 2) {
975                return false;
976            }
977            if ("upnp".equals(data[0])) {
978                command += s;
979            } else if ("bonjour".equals(data[0])) {
980                command += data[0];
981                command += (" " + data[1]);
982            } else {
983                return false;
984            }
985            if (!doBooleanCommand(command)) {
986                return false;
987            }
988        }
989        return true;
990    }
991
992    public boolean p2pServiceFlush() {
993        return doBooleanCommand("P2P_SERVICE_FLUSH");
994    }
995
996    public String p2pServDiscReq(String addr, String query) {
997        String command = "P2P_SERV_DISC_REQ";
998        command += (" " + addr);
999        command += (" " + query);
1000
1001        return doStringCommand(command);
1002    }
1003
1004    public boolean p2pServDiscCancelReq(String id) {
1005        return doBooleanCommand("P2P_SERV_DISC_CANCEL_REQ " + id);
1006    }
1007
1008    /* Set the current mode of miracast operation.
1009     *  0 = disabled
1010     *  1 = operating as source
1011     *  2 = operating as sink
1012     */
1013    public void setMiracastMode(int mode) {
1014        // Note: optional feature on the driver. It is ok for this to fail.
1015        doBooleanCommand("DRIVER MIRACAST " + mode);
1016    }
1017
1018    public String getNfcWpsConfigurationToken(int netId) {
1019        return doStringCommand("WPS_NFC_CONFIG_TOKEN WPS " + netId);
1020    }
1021
1022
1023    /* WIFI HAL support */
1024
1025    private long mWifiHalHandle;                        /* used by JNI to save wifi_handle */
1026    private long[] mWifiIfaceHandles;                   /* used by JNI to save interface handles */
1027    private int mWlan0Index;
1028    private int mP2p0Index;
1029
1030    private native boolean startHalNative();
1031    private native void stopHalNative();
1032    private native void waitForHalEventNative();
1033
1034    private class MonitorThread extends Thread {
1035        public void run() {
1036            Log.i(mTAG, "Waiting for HAL events");
1037            waitForHalEventNative();
1038        }
1039    }
1040
1041    public boolean startHal() {
1042        if (startHalNative()) {
1043            new MonitorThread().start();
1044            return true;
1045        } else {
1046            Log.i(mTAG, "Could not start hal");
1047            return false;
1048        }
1049    }
1050
1051    public void stopHal() {
1052        stopHalNative();
1053    }
1054
1055    private native int getInterfacesNative();
1056
1057    public int getInterfaces() {
1058        int num = getInterfacesNative();
1059        for (int i = 0; i < num; i++) {
1060            String name = getInterfaceNameNative(i);
1061            Log.i(mTAG, "interface[" + i + "] = " + name);
1062            if (name.equals("wlan0")) {
1063                mWlan0Index = i;
1064            } else if (name.equals("p2p0")) {
1065                mP2p0Index = i;
1066            }
1067        }
1068        return num;
1069    }
1070
1071    private native String getInterfaceNameNative(int index);
1072
1073    public void printInterfaceNames() {
1074        for (int i = 0; i < mWifiIfaceHandles.length; i++) {
1075            String name = getInterfaceNameNative(i);
1076            Log.i(mTAG, "interface[" + i + "] = " + name);
1077        }
1078    }
1079
1080    public static class ScanCapabilities {
1081        public int  max_scan_cache_size;                 // in number of scan results??
1082        public int  max_scan_buckets;
1083        public int  max_ap_cache_per_scan;
1084        public int  max_rssi_sample_size;
1085        public int  max_scan_reporting_threshold;        // in number of scan results??
1086        public int  max_hotlist_aps;
1087        public int  max_significant_wifi_change_aps;
1088    }
1089
1090    public boolean getScanCapabilities(ScanCapabilities capabilities) {
1091        return getScanCapabilitiesNative(mWlan0Index, capabilities);
1092    }
1093
1094    private native boolean getScanCapabilitiesNative(int iface, ScanCapabilities capabilities);
1095
1096    private native boolean startScanNative(int iface, int id, ScanSettings settings);
1097    private native boolean stopScanNative(int iface, int id);
1098    private native ScanResult[] getScanResultsNative(int iface, boolean flush);
1099
1100    public static class ChannelSettings {
1101        int frequency;
1102        int dwell_time_ms;
1103        boolean passive;
1104    }
1105
1106    public static class BucketSettings {
1107        int bucket;
1108        int band;
1109        int period_ms;
1110        int report_events;
1111        int num_channels;
1112        ChannelSettings channels[] = new ChannelSettings[8];
1113    }
1114
1115    public static class ScanSettings {
1116        int base_period_ms;
1117        int max_ap_per_scan;
1118        int report_threshold;
1119        int num_buckets;
1120        BucketSettings buckets[] = new BucketSettings[8];
1121    }
1122
1123    public interface ScanEventHandler {
1124        void onScanResultsAvailable();
1125        void onFullScanResult(ScanResult result, WifiScanner.InformationElement elems[]);
1126    }
1127
1128    void onScanResultsAvailable(int id) {
1129        mScanEventHandler.onScanResultsAvailable();
1130    }
1131
1132    void onFullScanResult(int id, ScanResult result, WifiScanner.InformationElement elems[]) {
1133        mScanEventHandler.onFullScanResult(result, elems);
1134    }
1135
1136    private int mScanCmdId = 0;
1137    private ScanEventHandler mScanEventHandler;
1138
1139    public boolean startScan(ScanSettings settings, ScanEventHandler eventHandler) {
1140        synchronized (mLock) {
1141            if (mScanCmdId != 0) {
1142                return false;
1143            } else {
1144                mScanCmdId = getNewCmdIdLocked();
1145            }
1146
1147            mScanEventHandler = eventHandler;
1148
1149            if (startScanNative(mWlan0Index, mScanCmdId, settings) == false) {
1150                mScanEventHandler = null;
1151                return false;
1152            }
1153
1154            return true;
1155        }
1156    }
1157
1158    public void stopScan() {
1159        synchronized (mLock) {
1160            stopScanNative(mWlan0Index, mScanCmdId);
1161            mScanEventHandler = null;
1162            mScanCmdId = 0;
1163        }
1164    }
1165
1166    public ScanResult[] getScanResults() {
1167        return getScanResultsNative(mWlan0Index, /* flush = */ false);
1168    }
1169
1170    public interface HotlistEventHandler {
1171        void onHotlistApFound(ScanResult[] result);
1172    }
1173
1174    private int mHotlistCmdId = 0;
1175    private HotlistEventHandler mHotlistEventHandler;
1176
1177    private native boolean setHotlistNative(int iface, int id,
1178            WifiScanner.HotlistSettings settings);
1179    private native boolean resetHotlistNative(int iface, int id);
1180
1181    boolean setHotlist(WifiScanner.HotlistSettings settings, HotlistEventHandler eventHandler) {
1182        synchronized (mLock) {
1183            if (mHotlistCmdId != 0) {
1184                return false;
1185            } else {
1186                mHotlistCmdId = getNewCmdIdLocked();
1187            }
1188
1189            mHotlistEventHandler = eventHandler;
1190            if (setHotlistNative(mWlan0Index, mScanCmdId, settings) == false) {
1191                mHotlistEventHandler = null;
1192                return false;
1193            }
1194
1195            return true;
1196        }
1197    }
1198
1199    void resetHotlist() {
1200        synchronized (mLock) {
1201            if (mHotlistCmdId != 0) {
1202                resetHotlistNative(mWlan0Index, mHotlistCmdId);
1203                mHotlistCmdId = 0;
1204                mHotlistEventHandler = null;
1205            }
1206        }
1207    }
1208
1209    void onHotlistApFound(int id, ScanResult[] results) {
1210        mHotlistEventHandler.onHotlistApFound(results);
1211    }
1212
1213    public interface SignificantWifiChangeEventHandler {
1214        void onChangesFound(ScanResult[] result);
1215    }
1216
1217    SignificantWifiChangeEventHandler mSignificantWifiChangeHandler;
1218    int mSignificantWifiChangeCmdId;
1219
1220    private native boolean trackSignificantWifiChangeNative(
1221            int iface, int id, WifiScanner.WifiChangeSettings settings);
1222    private native boolean untrackSignificantWifiChangeNative(int iface, int id);
1223
1224    boolean trackSignificantWifiChange(WifiScanner.WifiChangeSettings settings,
1225                                       SignificantWifiChangeEventHandler handler) {
1226        synchronized (mLock) {
1227            if (mSignificantWifiChangeCmdId != 0) {
1228                return false;
1229            } else {
1230                mSignificantWifiChangeCmdId = getNewCmdIdLocked();
1231            }
1232
1233            mSignificantWifiChangeHandler = handler;
1234            if (trackSignificantWifiChangeNative(mWlan0Index, mScanCmdId, settings) == false) {
1235                mHotlistEventHandler = null;
1236                return false;
1237            }
1238
1239            return true;
1240        }
1241    }
1242
1243    void untrackSignificantWifiChange() {
1244        synchronized (mLock) {
1245            if (mSignificantWifiChangeCmdId != 0) {
1246                untrackSignificantWifiChangeNative(mWlan0Index, mSignificantWifiChangeCmdId);
1247                mSignificantWifiChangeCmdId = 0;
1248                mSignificantWifiChangeHandler = null;
1249            }
1250        }
1251    }
1252
1253    void onSignificantWifiChange(int id, ScanResult[] results) {
1254        mSignificantWifiChangeHandler.onChangesFound(results);
1255    }
1256
1257
1258
1259}
1260