WifiNative.java revision 1f095869536472c178046bb63c59947508eac4a6
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 android.net.wifi;
18
19import android.net.wifi.p2p.WifiP2pConfig;
20import android.net.wifi.p2p.WifiP2pGroup;
21import android.net.wifi.p2p.WifiP2pDevice;
22import android.text.TextUtils;
23import android.util.Log;
24
25import java.io.InputStream;
26import java.lang.Process;
27import java.util.ArrayList;
28import java.util.List;
29
30/**
31 * Native calls for bring up/shut down of the supplicant daemon and for
32 * sending requests to the supplicant daemon
33 *
34 * waitForEvent() is called on the monitor thread for events. All other methods
35 * must be serialized from the framework.
36 *
37 * {@hide}
38 */
39public class WifiNative {
40
41    static final int BLUETOOTH_COEXISTENCE_MODE_ENABLED = 0;
42    static final int BLUETOOTH_COEXISTENCE_MODE_DISABLED = 1;
43    static final int BLUETOOTH_COEXISTENCE_MODE_SENSE = 2;
44
45    public native static boolean loadDriver();
46
47    public native static boolean isDriverLoaded();
48
49    public native static boolean unloadDriver();
50
51    public native static boolean startSupplicant();
52
53    public native static boolean startP2pSupplicant();
54
55    /* Sends a kill signal to supplicant. To be used when we have lost connection
56       or when the supplicant is hung */
57    public native static boolean killSupplicant();
58
59    public native static boolean connectToSupplicant();
60
61    public native static void closeSupplicantConnection();
62
63    /**
64     * Wait for the supplicant to send an event, returning the event string.
65     * @return the event string sent by the supplicant.
66     */
67    public native static String waitForEvent();
68
69    private native static boolean doBooleanCommand(String command);
70
71    private native static int doIntCommand(String command);
72
73    private native static String doStringCommand(String command);
74
75    public static boolean ping() {
76        String pong = doStringCommand("PING");
77        return (pong != null && pong.equals("PONG"));
78    }
79
80    public static boolean scan() {
81       return doBooleanCommand("SCAN");
82    }
83
84    public static boolean setScanMode(boolean setActive) {
85        if (setActive) {
86            return doBooleanCommand("DRIVER SCAN-ACTIVE");
87        } else {
88            return doBooleanCommand("DRIVER SCAN-PASSIVE");
89        }
90    }
91
92    /* Does a graceful shutdown of supplicant. Is a common stop function for both p2p and sta.
93     *
94     * Note that underneath we use a harsh-sounding "terminate" supplicant command
95     * for a graceful stop and a mild-sounding "stop" interface
96     * to kill the process
97     */
98    public static boolean stopSupplicant() {
99        return doBooleanCommand("TERMINATE");
100    }
101
102    public static String listNetworks() {
103        return doStringCommand("LIST_NETWORKS");
104    }
105
106    public static int addNetwork() {
107        return doIntCommand("ADD_NETWORK");
108    }
109
110    public static boolean setNetworkVariable(int netId, String name, String value) {
111        if (TextUtils.isEmpty(name) || TextUtils.isEmpty(value)) return false;
112        return doBooleanCommand("SET_NETWORK " + netId + " " + name + " " + value);
113    }
114
115    public static String getNetworkVariable(int netId, String name) {
116        if (TextUtils.isEmpty(name)) return null;
117        return doStringCommand("GET_NETWORK " + netId + " " + name);
118    }
119
120    public static boolean removeNetwork(int netId) {
121        return doBooleanCommand("REMOVE_NETWORK " + netId);
122    }
123
124    public static boolean enableNetwork(int netId, boolean disableOthers) {
125        if (disableOthers) {
126            return doBooleanCommand("SELECT_NETWORK " + netId);
127        } else {
128            return doBooleanCommand("ENABLE_NETWORK " + netId);
129        }
130    }
131
132    public static boolean disableNetwork(int netId) {
133        return doBooleanCommand("DISABLE_NETWORK " + netId);
134    }
135
136    public static boolean reconnect() {
137        return doBooleanCommand("RECONNECT");
138    }
139
140    public static boolean reassociate() {
141        return doBooleanCommand("REASSOCIATE");
142    }
143
144    public static boolean disconnect() {
145        return doBooleanCommand("DISCONNECT");
146    }
147
148    public static String status() {
149        return doStringCommand("STATUS");
150    }
151
152    public static String getMacAddress() {
153        //Macaddr = XX.XX.XX.XX.XX.XX
154        String ret = doStringCommand("DRIVER MACADDR");
155        if (!TextUtils.isEmpty(ret)) {
156            String[] tokens = ret.split(" = ");
157            if (tokens.length == 2) return tokens[1];
158        }
159        return null;
160    }
161
162    public static String scanResults() {
163        return doStringCommand("SCAN_RESULTS");
164    }
165
166    public static boolean startDriver() {
167        return doBooleanCommand("DRIVER START");
168    }
169
170    public static boolean stopDriver() {
171        return doBooleanCommand("DRIVER STOP");
172    }
173
174
175    /**
176     * Start filtering out Multicast V4 packets
177     * @return {@code true} if the operation succeeded, {@code false} otherwise
178     *
179     * Multicast filtering rules work as follows:
180     *
181     * The driver can filter multicast (v4 and/or v6) and broadcast packets when in
182     * a power optimized mode (typically when screen goes off).
183     *
184     * In order to prevent the driver from filtering the multicast/broadcast packets, we have to
185     * add a DRIVER RXFILTER-ADD rule followed by DRIVER RXFILTER-START to make the rule effective
186     *
187     * DRIVER RXFILTER-ADD Num
188     *   where Num = 0 - Unicast, 1 - Broadcast, 2 - Mutil4 or 3 - Multi6
189     *
190     * and DRIVER RXFILTER-START
191     * In order to stop the usage of these rules, we do
192     *
193     * DRIVER RXFILTER-STOP
194     * DRIVER RXFILTER-REMOVE Num
195     *   where Num is as described for RXFILTER-ADD
196     *
197     * The  SETSUSPENDOPT driver command overrides the filtering rules
198     */
199    public static boolean startFilteringMulticastV4Packets() {
200        return doBooleanCommand("DRIVER RXFILTER-STOP")
201            && doBooleanCommand("DRIVER RXFILTER-REMOVE 2")
202            && doBooleanCommand("DRIVER RXFILTER-START");
203    }
204
205    /**
206     * Stop filtering out Multicast V4 packets.
207     * @return {@code true} if the operation succeeded, {@code false} otherwise
208     */
209    public static boolean stopFilteringMulticastV4Packets() {
210        return doBooleanCommand("DRIVER RXFILTER-STOP")
211            && doBooleanCommand("DRIVER RXFILTER-ADD 2")
212            && doBooleanCommand("DRIVER RXFILTER-START");
213    }
214
215    /**
216     * Start filtering out Multicast V6 packets
217     * @return {@code true} if the operation succeeded, {@code false} otherwise
218     */
219    public static boolean startFilteringMulticastV6Packets() {
220        return doBooleanCommand("DRIVER RXFILTER-STOP")
221            && doBooleanCommand("DRIVER RXFILTER-REMOVE 3")
222            && doBooleanCommand("DRIVER RXFILTER-START");
223    }
224
225    /**
226     * Stop filtering out Multicast V6 packets.
227     * @return {@code true} if the operation succeeded, {@code false} otherwise
228     */
229    public static boolean stopFilteringMulticastV6Packets() {
230        return doBooleanCommand("DRIVER RXFILTER-STOP")
231            && doBooleanCommand("DRIVER RXFILTER-ADD 3")
232            && doBooleanCommand("DRIVER RXFILTER-START");
233    }
234
235    public static int getPowerMode() {
236        String ret = doStringCommand("DRIVER GETPOWER");
237        if (!TextUtils.isEmpty(ret)) {
238            // reply comes back in the form "powermode = XX" where XX is the
239            // number we're interested in.
240            String[] tokens = ret.split(" = ");
241            try {
242                if (tokens.length == 2) return Integer.parseInt(tokens[1]);
243            } catch (NumberFormatException e) {
244                return -1;
245            }
246        }
247        return -1;
248    }
249
250    public static boolean setPowerMode(int mode) {
251        return doBooleanCommand("DRIVER POWERMODE " + mode);
252    }
253
254    public static int getBand() {
255       String ret = doStringCommand("DRIVER GETBAND");
256        if (!TextUtils.isEmpty(ret)) {
257            //reply is "BAND X" where X is the band
258            String[] tokens = ret.split(" ");
259            try {
260                if (tokens.length == 2) return Integer.parseInt(tokens[1]);
261            } catch (NumberFormatException e) {
262                return -1;
263            }
264        }
265        return -1;
266    }
267
268    public static boolean setBand(int band) {
269        return doBooleanCommand("DRIVER SETBAND " + band);
270    }
271
272   /**
273     * Sets the bluetooth coexistence mode.
274     *
275     * @param mode One of {@link #BLUETOOTH_COEXISTENCE_MODE_DISABLED},
276     *            {@link #BLUETOOTH_COEXISTENCE_MODE_ENABLED}, or
277     *            {@link #BLUETOOTH_COEXISTENCE_MODE_SENSE}.
278     * @return Whether the mode was successfully set.
279     */
280    public static boolean setBluetoothCoexistenceMode(int mode) {
281        return doBooleanCommand("DRIVER BTCOEXMODE " + mode);
282    }
283
284    /**
285     * Enable or disable Bluetooth coexistence scan mode. When this mode is on,
286     * some of the low-level scan parameters used by the driver are changed to
287     * reduce interference with A2DP streaming.
288     *
289     * @param isSet whether to enable or disable this mode
290     * @return {@code true} if the command succeeded, {@code false} otherwise.
291     */
292    public static boolean setBluetoothCoexistenceScanMode(boolean setCoexScanMode) {
293        if (setCoexScanMode) {
294            return doBooleanCommand("DRIVER BTCOEXSCAN-START");
295        } else {
296            return doBooleanCommand("DRIVER BTCOEXSCAN-STOP");
297        }
298    }
299
300    public static boolean saveConfig() {
301        // Make sure we never write out a value for AP_SCAN other than 1
302        return doBooleanCommand("AP_SCAN 1") && doBooleanCommand("SAVE_CONFIG");
303    }
304
305    public static boolean setScanResultHandling(int mode) {
306        return doBooleanCommand("AP_SCAN " + mode);
307    }
308
309    public static boolean addToBlacklist(String bssid) {
310        if (TextUtils.isEmpty(bssid)) return false;
311        return doBooleanCommand("BLACKLIST " + bssid);
312    }
313
314    public static boolean clearBlacklist() {
315        return doBooleanCommand("BLACKLIST clear");
316    }
317
318    public static boolean setSuspendOptimizations(boolean enabled) {
319        if (enabled) {
320            return doBooleanCommand("DRIVER SETSUSPENDOPT 0");
321        } else {
322            return doBooleanCommand("DRIVER SETSUSPENDOPT 1");
323        }
324    }
325
326    public static boolean setCountryCode(String countryCode) {
327        return doBooleanCommand("DRIVER COUNTRY " + countryCode);
328    }
329
330    public static void enableBackgroundScan(boolean enable) {
331        //Note: BGSCAN-START and BGSCAN-STOP are documented in core/res/res/values/config.xml
332        //and will need an update if the names are changed
333        if (enable) {
334            doBooleanCommand("DRIVER BGSCAN-START");
335        } else {
336            doBooleanCommand("DRIVER BGSCAN-STOP");
337        }
338    }
339
340    public static void setScanInterval(int scanInterval) {
341        doBooleanCommand("SCAN_INTERVAL " + scanInterval);
342    }
343
344    /** Example output:
345     * RSSI=-65
346     * LINKSPEED=48
347     * NOISE=9999
348     * FREQUENCY=0
349     */
350    public static String signalPoll() {
351        return doStringCommand("SIGNAL_POLL");
352    }
353
354    public static boolean startWpsPbc() {
355        return doBooleanCommand("WPS_PBC");
356    }
357
358    public static boolean startWpsPbc(String bssid) {
359        return doBooleanCommand("WPS_PBC " + bssid);
360    }
361
362    public static boolean startWpsPinKeypad(String pin) {
363        return doBooleanCommand("WPS_PIN any " + pin);
364    }
365
366    public static String startWpsPinDisplay(String bssid) {
367        return doStringCommand("WPS_PIN " + bssid);
368    }
369
370    /* Configures an access point connection */
371    public static boolean startWpsRegistrar(String bssid, String pin) {
372        return doBooleanCommand("WPS_REG " + bssid + " " + pin);
373    }
374
375    public static boolean setPersistentReconnect(boolean enabled) {
376        int value = (enabled == true) ? 1 : 0;
377        return doBooleanCommand("SET persistent_reconnect " + value);
378    }
379
380    public static boolean setDeviceName(String name) {
381        return doBooleanCommand("SET device_name " + name);
382    }
383
384    public static boolean setDeviceType(String type) {
385        return doBooleanCommand("SET device_type " + type);
386    }
387
388    public static boolean setConfigMethods(String cfg) {
389        return doBooleanCommand("SET config_methods " + cfg);
390    }
391
392    public static boolean setP2pSsidPostfix(String postfix) {
393        return doBooleanCommand("SET p2p_ssid_postfix " + postfix);
394    }
395
396    public static boolean p2pFind() {
397        return doBooleanCommand("P2P_FIND");
398    }
399
400    public static boolean p2pFind(int timeout) {
401        if (timeout <= 0) {
402            return p2pFind();
403        }
404        return doBooleanCommand("P2P_FIND " + timeout);
405    }
406
407    public static boolean p2pListen() {
408        return doBooleanCommand("P2P_LISTEN");
409    }
410
411    public static boolean p2pListen(int timeout) {
412        if (timeout <= 0) {
413            return p2pListen();
414        }
415        return doBooleanCommand("P2P_LISTEN " + timeout);
416    }
417
418    public static boolean p2pFlush() {
419        return doBooleanCommand("P2P_FLUSH");
420    }
421
422    /* p2p_connect <peer device address> <pbc|pin|PIN#> [label|display|keypad]
423        [persistent] [join|auth] [go_intent=<0..15>] [freq=<in MHz>] */
424    public static String p2pConnect(WifiP2pConfig config, boolean joinExistingGroup) {
425        if (config == null) return null;
426        List<String> args = new ArrayList<String>();
427        WpsInfo wps = config.wps;
428        args.add(config.deviceAddress);
429
430        switch (wps.setup) {
431            case WpsInfo.PBC:
432                args.add("pbc");
433                break;
434            case WpsInfo.DISPLAY:
435                if (TextUtils.isEmpty(wps.pin)) {
436                    args.add("pin");
437                } else {
438                    args.add(wps.pin);
439                }
440                args.add("display");
441                break;
442            case WpsInfo.KEYPAD:
443                args.add(wps.pin);
444                args.add("keypad");
445                break;
446            case WpsInfo.LABEL:
447                args.add(wps.pin);
448                args.add("label");
449            default:
450                break;
451        }
452
453        //TODO: Add persist behavior once the supplicant interaction is fixed for both
454        // group and client scenarios
455        /* Persist unless there is an explicit request to not do so*/
456        //if (config.persist != WifiP2pConfig.Persist.NO) args.add("persistent");
457
458        if (joinExistingGroup) args.add("join");
459
460        //TODO: This can be adapted based on device plugged in state and
461        //device battery state
462        int groupOwnerIntent = config.groupOwnerIntent;
463        if (groupOwnerIntent < 0 || groupOwnerIntent > 15) {
464            groupOwnerIntent = 7; //default value
465        }
466        args.add("go_intent=" + groupOwnerIntent);
467
468        String command = "P2P_CONNECT ";
469        for (String s : args) command += s + " ";
470
471        return doStringCommand(command);
472    }
473
474    public static boolean p2pCancelConnect() {
475        return doBooleanCommand("P2P_CANCEL");
476    }
477
478    public static boolean p2pProvisionDiscovery(WifiP2pConfig config) {
479        if (config == null) return false;
480
481        switch (config.wps.setup) {
482            case WpsInfo.PBC:
483                return doBooleanCommand("P2P_PROV_DISC " + config.deviceAddress + " pbc");
484            case WpsInfo.DISPLAY:
485                //We are doing display, so provision discovery is keypad
486                return doBooleanCommand("P2P_PROV_DISC " + config.deviceAddress + " keypad");
487            case WpsInfo.KEYPAD:
488                //We are doing keypad, so provision discovery is display
489                return doBooleanCommand("P2P_PROV_DISC " + config.deviceAddress + " display");
490            default:
491                break;
492        }
493        return false;
494    }
495
496    public static boolean p2pGroupAdd() {
497        return doBooleanCommand("P2P_GROUP_ADD");
498    }
499
500    public static boolean p2pGroupRemove(String iface) {
501        if (iface == null) return false;
502        return doBooleanCommand("P2P_GROUP_REMOVE " + iface);
503    }
504
505    public static boolean p2pReject(String deviceAddress) {
506        return doBooleanCommand("P2P_REJECT " + deviceAddress);
507    }
508
509    /* Invite a peer to a group */
510    public static boolean p2pInvite(WifiP2pGroup group, String deviceAddress) {
511        if (deviceAddress == null) return false;
512
513        if (group == null) {
514            return doBooleanCommand("P2P_INVITE peer=" + deviceAddress);
515        } else {
516            return doBooleanCommand("P2P_INVITE group=" + group.getInterface()
517                    + " peer=" + deviceAddress + " go_dev_addr=" + group.getOwner().deviceAddress);
518        }
519    }
520
521    /* Reinvoke a persistent connection */
522    public static boolean p2pReinvoke(int netId, String deviceAddress) {
523        if (deviceAddress == null || netId < 0) return false;
524
525        return doBooleanCommand("P2P_INVITE persistent=" + netId + " peer=" + deviceAddress);
526    }
527
528
529    public static String p2pGetInterfaceAddress(String deviceAddress) {
530        if (deviceAddress == null) return null;
531
532        //  "p2p_peer deviceAddress" returns a multi-line result containing
533        //      intended_addr=fa:7b:7a:42:82:13
534        String peerInfo = p2pPeer(deviceAddress);
535        if (peerInfo == null) return null;
536        String[] tokens= peerInfo.split("\n");
537
538        for (String token : tokens) {
539            //TODO: update from interface_addr when wpa_supplicant implementation is fixed
540            if (token.startsWith("intended_addr=")) {
541                String[] nameValue = token.split("=");
542                if (nameValue.length != 2) break;
543                return nameValue[1];
544            }
545        }
546        return null;
547    }
548
549    public static String p2pGetDeviceAddress() {
550        String status = status();
551        if (status == null) return "";
552
553        String[] tokens = status.split("\n");
554        for (String token : tokens) {
555            if (token.startsWith("p2p_device_address=")) {
556                String[] nameValue = token.split("=");
557                if (nameValue.length != 2) break;
558                return nameValue[1];
559            }
560        }
561        return "";
562    }
563
564    public static String p2pPeer(String deviceAddress) {
565        return doStringCommand("P2P_PEER " + deviceAddress);
566    }
567}
568