WifiNative.java revision e2639d782eef1365a98dbd2639be23a6dd06e691
1842b3d411592847be496590608d298bc17cf70b2Tadashi G. Takaoka/*
2842b3d411592847be496590608d298bc17cf70b2Tadashi G. Takaoka * Copyright (C) 2008 The Android Open Source Project
3842b3d411592847be496590608d298bc17cf70b2Tadashi G. Takaoka *
4842b3d411592847be496590608d298bc17cf70b2Tadashi G. Takaoka * Licensed under the Apache License, Version 2.0 (the "License");
5842b3d411592847be496590608d298bc17cf70b2Tadashi G. Takaoka * you may not use this file except in compliance with the License.
6842b3d411592847be496590608d298bc17cf70b2Tadashi G. Takaoka * You may obtain a copy of the License at
7842b3d411592847be496590608d298bc17cf70b2Tadashi G. Takaoka *
8842b3d411592847be496590608d298bc17cf70b2Tadashi G. Takaoka *      http://www.apache.org/licenses/LICENSE-2.0
9842b3d411592847be496590608d298bc17cf70b2Tadashi G. Takaoka *
10842b3d411592847be496590608d298bc17cf70b2Tadashi G. Takaoka * Unless required by applicable law or agreed to in writing, software
11842b3d411592847be496590608d298bc17cf70b2Tadashi G. Takaoka * distributed under the License is distributed on an "AS IS" BASIS,
12842b3d411592847be496590608d298bc17cf70b2Tadashi G. Takaoka * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13842b3d411592847be496590608d298bc17cf70b2Tadashi G. Takaoka * See the License for the specific language governing permissions and
14842b3d411592847be496590608d298bc17cf70b2Tadashi G. Takaoka * limitations under the License.
15842b3d411592847be496590608d298bc17cf70b2Tadashi G. Takaoka */
16842b3d411592847be496590608d298bc17cf70b2Tadashi G. Takaoka
17842b3d411592847be496590608d298bc17cf70b2Tadashi G. Takaokapackage android.net.wifi;
18842b3d411592847be496590608d298bc17cf70b2Tadashi G. Takaoka
19842b3d411592847be496590608d298bc17cf70b2Tadashi G. Takaokaimport android.net.wifi.p2p.WifiP2pConfig;
20842b3d411592847be496590608d298bc17cf70b2Tadashi G. Takaokaimport android.net.wifi.p2p.WifiP2pGroup;
21842b3d411592847be496590608d298bc17cf70b2Tadashi G. Takaokaimport android.net.wifi.p2p.WifiP2pDevice;
22842b3d411592847be496590608d298bc17cf70b2Tadashi G. Takaokaimport android.text.TextUtils;
23842b3d411592847be496590608d298bc17cf70b2Tadashi G. Takaokaimport android.net.wifi.p2p.nsd.WifiP2pServiceInfo;
24842b3d411592847be496590608d298bc17cf70b2Tadashi G. Takaokaimport android.net.wifi.p2p.nsd.WifiP2pServiceRequest;
25842b3d411592847be496590608d298bc17cf70b2Tadashi G. Takaokaimport android.util.Log;
26842b3d411592847be496590608d298bc17cf70b2Tadashi G. Takaoka
27842b3d411592847be496590608d298bc17cf70b2Tadashi G. Takaokaimport java.io.InputStream;
28842b3d411592847be496590608d298bc17cf70b2Tadashi G. Takaokaimport java.lang.Process;
29842b3d411592847be496590608d298bc17cf70b2Tadashi G. Takaokaimport java.util.ArrayList;
30842b3d411592847be496590608d298bc17cf70b2Tadashi G. Takaokaimport java.util.List;
31842b3d411592847be496590608d298bc17cf70b2Tadashi G. Takaoka
32842b3d411592847be496590608d298bc17cf70b2Tadashi G. Takaoka/**
33842b3d411592847be496590608d298bc17cf70b2Tadashi G. Takaoka * Native calls for bring up/shut down of the supplicant daemon and for
34842b3d411592847be496590608d298bc17cf70b2Tadashi G. Takaoka * sending requests to the supplicant daemon
35842b3d411592847be496590608d298bc17cf70b2Tadashi G. Takaoka *
36842b3d411592847be496590608d298bc17cf70b2Tadashi G. Takaoka * waitForEvent() is called on the monitor thread for events. All other methods
37842b3d411592847be496590608d298bc17cf70b2Tadashi G. Takaoka * must be serialized from the framework.
38842b3d411592847be496590608d298bc17cf70b2Tadashi G. Takaoka *
39842b3d411592847be496590608d298bc17cf70b2Tadashi G. Takaoka * {@hide}
40842b3d411592847be496590608d298bc17cf70b2Tadashi G. Takaoka */
41842b3d411592847be496590608d298bc17cf70b2Tadashi G. Takaokapublic class WifiNative {
42842b3d411592847be496590608d298bc17cf70b2Tadashi G. Takaoka
43842b3d411592847be496590608d298bc17cf70b2Tadashi G. Takaoka    private static final boolean DBG = false;
44842b3d411592847be496590608d298bc17cf70b2Tadashi G. Takaoka    private final String mTAG;
45842b3d411592847be496590608d298bc17cf70b2Tadashi G. Takaoka    private static final int DEFAULT_GROUP_OWNER_INTENT = 7;
46842b3d411592847be496590608d298bc17cf70b2Tadashi G. Takaoka
47842b3d411592847be496590608d298bc17cf70b2Tadashi G. Takaoka    static final int BLUETOOTH_COEXISTENCE_MODE_ENABLED = 0;
48842b3d411592847be496590608d298bc17cf70b2Tadashi G. Takaoka    static final int BLUETOOTH_COEXISTENCE_MODE_DISABLED = 1;
49842b3d411592847be496590608d298bc17cf70b2Tadashi G. Takaoka    static final int BLUETOOTH_COEXISTENCE_MODE_SENSE = 2;
50842b3d411592847be496590608d298bc17cf70b2Tadashi G. Takaoka
51842b3d411592847be496590608d298bc17cf70b2Tadashi G. Takaoka    String mInterface = "";
52842b3d411592847be496590608d298bc17cf70b2Tadashi G. Takaoka
53842b3d411592847be496590608d298bc17cf70b2Tadashi G. Takaoka    public native static boolean loadDriver();
54842b3d411592847be496590608d298bc17cf70b2Tadashi G. Takaoka
55842b3d411592847be496590608d298bc17cf70b2Tadashi G. Takaoka    public native static boolean isDriverLoaded();
56842b3d411592847be496590608d298bc17cf70b2Tadashi G. Takaoka
57842b3d411592847be496590608d298bc17cf70b2Tadashi G. Takaoka    public native static boolean unloadDriver();
58842b3d411592847be496590608d298bc17cf70b2Tadashi G. Takaoka
59842b3d411592847be496590608d298bc17cf70b2Tadashi G. Takaoka    public native static boolean startSupplicant(boolean p2pSupported);
60842b3d411592847be496590608d298bc17cf70b2Tadashi G. Takaoka
61842b3d411592847be496590608d298bc17cf70b2Tadashi G. Takaoka    /* Sends a kill signal to supplicant. To be used when we have lost connection
62842b3d411592847be496590608d298bc17cf70b2Tadashi G. Takaoka       or when the supplicant is hung */
63842b3d411592847be496590608d298bc17cf70b2Tadashi G. Takaoka    public native static boolean killSupplicant();
64842b3d411592847be496590608d298bc17cf70b2Tadashi G. Takaoka
65842b3d411592847be496590608d298bc17cf70b2Tadashi G. Takaoka    private native boolean connectToSupplicant(String iface);
66842b3d411592847be496590608d298bc17cf70b2Tadashi G. Takaoka
67842b3d411592847be496590608d298bc17cf70b2Tadashi G. Takaoka    private native void closeSupplicantConnection(String iface);
68842b3d411592847be496590608d298bc17cf70b2Tadashi G. Takaoka
69842b3d411592847be496590608d298bc17cf70b2Tadashi G. Takaoka    /**
70842b3d411592847be496590608d298bc17cf70b2Tadashi G. Takaoka     * Wait for the supplicant to send an event, returning the event string.
71842b3d411592847be496590608d298bc17cf70b2Tadashi G. Takaoka     * @return the event string sent by the supplicant.
72842b3d411592847be496590608d298bc17cf70b2Tadashi G. Takaoka     */
73842b3d411592847be496590608d298bc17cf70b2Tadashi G. Takaoka    private native String waitForEvent(String iface);
74842b3d411592847be496590608d298bc17cf70b2Tadashi G. Takaoka
75842b3d411592847be496590608d298bc17cf70b2Tadashi G. Takaoka    private native boolean doBooleanCommand(String iface, String command);
76842b3d411592847be496590608d298bc17cf70b2Tadashi G. Takaoka
77842b3d411592847be496590608d298bc17cf70b2Tadashi G. Takaoka    private native int doIntCommand(String iface, String command);
78842b3d411592847be496590608d298bc17cf70b2Tadashi G. Takaoka
79842b3d411592847be496590608d298bc17cf70b2Tadashi G. Takaoka    private native String doStringCommand(String iface, String command);
80842b3d411592847be496590608d298bc17cf70b2Tadashi G. Takaoka
81842b3d411592847be496590608d298bc17cf70b2Tadashi G. Takaoka    public WifiNative(String iface) {
82842b3d411592847be496590608d298bc17cf70b2Tadashi G. Takaoka        mInterface = iface;
83842b3d411592847be496590608d298bc17cf70b2Tadashi G. Takaoka        mTAG = "WifiNative-" + iface;
84842b3d411592847be496590608d298bc17cf70b2Tadashi G. Takaoka    }
85842b3d411592847be496590608d298bc17cf70b2Tadashi G. Takaoka
86842b3d411592847be496590608d298bc17cf70b2Tadashi G. Takaoka    public boolean connectToSupplicant() {
87842b3d411592847be496590608d298bc17cf70b2Tadashi G. Takaoka        return connectToSupplicant(mInterface);
88842b3d411592847be496590608d298bc17cf70b2Tadashi G. Takaoka    }
89842b3d411592847be496590608d298bc17cf70b2Tadashi G. Takaoka
90842b3d411592847be496590608d298bc17cf70b2Tadashi G. Takaoka    public void closeSupplicantConnection() {
91842b3d411592847be496590608d298bc17cf70b2Tadashi G. Takaoka        closeSupplicantConnection(mInterface);
92842b3d411592847be496590608d298bc17cf70b2Tadashi G. Takaoka    }
93842b3d411592847be496590608d298bc17cf70b2Tadashi G. Takaoka
94842b3d411592847be496590608d298bc17cf70b2Tadashi G. Takaoka    public String waitForEvent() {
95842b3d411592847be496590608d298bc17cf70b2Tadashi G. Takaoka        return waitForEvent(mInterface);
96842b3d411592847be496590608d298bc17cf70b2Tadashi G. Takaoka    }
97842b3d411592847be496590608d298bc17cf70b2Tadashi G. Takaoka
98842b3d411592847be496590608d298bc17cf70b2Tadashi G. Takaoka    private boolean doBooleanCommand(String command) {
99842b3d411592847be496590608d298bc17cf70b2Tadashi G. Takaoka        if (DBG) Log.d(mTAG, "doBoolean: " + command);
100842b3d411592847be496590608d298bc17cf70b2Tadashi G. Takaoka        return doBooleanCommand(mInterface, command);
101842b3d411592847be496590608d298bc17cf70b2Tadashi G. Takaoka    }
102842b3d411592847be496590608d298bc17cf70b2Tadashi G. Takaoka
103842b3d411592847be496590608d298bc17cf70b2Tadashi G. Takaoka    private int doIntCommand(String command) {
104842b3d411592847be496590608d298bc17cf70b2Tadashi G. Takaoka        if (DBG) Log.d(mTAG, "doInt: " + command);
105842b3d411592847be496590608d298bc17cf70b2Tadashi G. Takaoka        return doIntCommand(mInterface, command);
106842b3d411592847be496590608d298bc17cf70b2Tadashi G. Takaoka    }
107842b3d411592847be496590608d298bc17cf70b2Tadashi G. Takaoka
108842b3d411592847be496590608d298bc17cf70b2Tadashi G. Takaoka    private String doStringCommand(String command) {
109842b3d411592847be496590608d298bc17cf70b2Tadashi G. Takaoka        if (DBG) Log.d(mTAG, "doString: " + command);
110842b3d411592847be496590608d298bc17cf70b2Tadashi G. Takaoka        return doStringCommand(mInterface, command);
111842b3d411592847be496590608d298bc17cf70b2Tadashi G. Takaoka    }
112842b3d411592847be496590608d298bc17cf70b2Tadashi G. Takaoka
113842b3d411592847be496590608d298bc17cf70b2Tadashi G. Takaoka    public boolean ping() {
114842b3d411592847be496590608d298bc17cf70b2Tadashi G. Takaoka        String pong = doStringCommand("PING");
115842b3d411592847be496590608d298bc17cf70b2Tadashi G. Takaoka        return (pong != null && pong.equals("PONG"));
116842b3d411592847be496590608d298bc17cf70b2Tadashi G. Takaoka    }
117842b3d411592847be496590608d298bc17cf70b2Tadashi G. Takaoka
118842b3d411592847be496590608d298bc17cf70b2Tadashi G. Takaoka    public boolean scan() {
119842b3d411592847be496590608d298bc17cf70b2Tadashi G. Takaoka       return doBooleanCommand("SCAN");
120842b3d411592847be496590608d298bc17cf70b2Tadashi G. Takaoka    }
121842b3d411592847be496590608d298bc17cf70b2Tadashi G. Takaoka
122    public boolean setScanMode(boolean setActive) {
123        if (setActive) {
124            return doBooleanCommand("DRIVER SCAN-ACTIVE");
125        } else {
126            return doBooleanCommand("DRIVER SCAN-PASSIVE");
127        }
128    }
129
130    /* Does a graceful shutdown of supplicant. Is a common stop function for both p2p and sta.
131     *
132     * Note that underneath we use a harsh-sounding "terminate" supplicant command
133     * for a graceful stop and a mild-sounding "stop" interface
134     * to kill the process
135     */
136    public boolean stopSupplicant() {
137        return doBooleanCommand("TERMINATE");
138    }
139
140    public String listNetworks() {
141        return doStringCommand("LIST_NETWORKS");
142    }
143
144    public int addNetwork() {
145        return doIntCommand("ADD_NETWORK");
146    }
147
148    public boolean setNetworkVariable(int netId, String name, String value) {
149        if (TextUtils.isEmpty(name) || TextUtils.isEmpty(value)) return false;
150        return doBooleanCommand("SET_NETWORK " + netId + " " + name + " " + value);
151    }
152
153    public String getNetworkVariable(int netId, String name) {
154        if (TextUtils.isEmpty(name)) return null;
155        return doStringCommand("GET_NETWORK " + netId + " " + name);
156    }
157
158    public boolean removeNetwork(int netId) {
159        return doBooleanCommand("REMOVE_NETWORK " + netId);
160    }
161
162    public boolean enableNetwork(int netId, boolean disableOthers) {
163        if (disableOthers) {
164            return doBooleanCommand("SELECT_NETWORK " + netId);
165        } else {
166            return doBooleanCommand("ENABLE_NETWORK " + netId);
167        }
168    }
169
170    public boolean disableNetwork(int netId) {
171        return doBooleanCommand("DISABLE_NETWORK " + netId);
172    }
173
174    public boolean reconnect() {
175        return doBooleanCommand("RECONNECT");
176    }
177
178    public boolean reassociate() {
179        return doBooleanCommand("REASSOCIATE");
180    }
181
182    public boolean disconnect() {
183        return doBooleanCommand("DISCONNECT");
184    }
185
186    public String status() {
187        return doStringCommand("STATUS");
188    }
189
190    public String getMacAddress() {
191        //Macaddr = XX.XX.XX.XX.XX.XX
192        String ret = doStringCommand("DRIVER MACADDR");
193        if (!TextUtils.isEmpty(ret)) {
194            String[] tokens = ret.split(" = ");
195            if (tokens.length == 2) return tokens[1];
196        }
197        return null;
198    }
199
200    public String scanResults() {
201        return doStringCommand("SCAN_RESULTS");
202    }
203
204    public boolean startDriver() {
205        return doBooleanCommand("DRIVER START");
206    }
207
208    public boolean stopDriver() {
209        return doBooleanCommand("DRIVER STOP");
210    }
211
212
213    /**
214     * Start filtering out Multicast V4 packets
215     * @return {@code true} if the operation succeeded, {@code false} otherwise
216     *
217     * Multicast filtering rules work as follows:
218     *
219     * The driver can filter multicast (v4 and/or v6) and broadcast packets when in
220     * a power optimized mode (typically when screen goes off).
221     *
222     * In order to prevent the driver from filtering the multicast/broadcast packets, we have to
223     * add a DRIVER RXFILTER-ADD rule followed by DRIVER RXFILTER-START to make the rule effective
224     *
225     * DRIVER RXFILTER-ADD Num
226     *   where Num = 0 - Unicast, 1 - Broadcast, 2 - Mutil4 or 3 - Multi6
227     *
228     * and DRIVER RXFILTER-START
229     * In order to stop the usage of these rules, we do
230     *
231     * DRIVER RXFILTER-STOP
232     * DRIVER RXFILTER-REMOVE Num
233     *   where Num is as described for RXFILTER-ADD
234     *
235     * The  SETSUSPENDOPT driver command overrides the filtering rules
236     */
237    public boolean startFilteringMulticastV4Packets() {
238        return doBooleanCommand("DRIVER RXFILTER-STOP")
239            && doBooleanCommand("DRIVER RXFILTER-REMOVE 2")
240            && doBooleanCommand("DRIVER RXFILTER-START");
241    }
242
243    /**
244     * Stop filtering out Multicast V4 packets.
245     * @return {@code true} if the operation succeeded, {@code false} otherwise
246     */
247    public boolean stopFilteringMulticastV4Packets() {
248        return doBooleanCommand("DRIVER RXFILTER-STOP")
249            && doBooleanCommand("DRIVER RXFILTER-ADD 2")
250            && doBooleanCommand("DRIVER RXFILTER-START");
251    }
252
253    /**
254     * Start filtering out Multicast V6 packets
255     * @return {@code true} if the operation succeeded, {@code false} otherwise
256     */
257    public boolean startFilteringMulticastV6Packets() {
258        return doBooleanCommand("DRIVER RXFILTER-STOP")
259            && doBooleanCommand("DRIVER RXFILTER-REMOVE 3")
260            && doBooleanCommand("DRIVER RXFILTER-START");
261    }
262
263    /**
264     * Stop filtering out Multicast V6 packets.
265     * @return {@code true} if the operation succeeded, {@code false} otherwise
266     */
267    public boolean stopFilteringMulticastV6Packets() {
268        return doBooleanCommand("DRIVER RXFILTER-STOP")
269            && doBooleanCommand("DRIVER RXFILTER-ADD 3")
270            && doBooleanCommand("DRIVER RXFILTER-START");
271    }
272
273    public int getBand() {
274       String ret = doStringCommand("DRIVER GETBAND");
275        if (!TextUtils.isEmpty(ret)) {
276            //reply is "BAND X" where X is the band
277            String[] tokens = ret.split(" ");
278            try {
279                if (tokens.length == 2) return Integer.parseInt(tokens[1]);
280            } catch (NumberFormatException e) {
281                return -1;
282            }
283        }
284        return -1;
285    }
286
287    public boolean setBand(int band) {
288        return doBooleanCommand("DRIVER SETBAND " + band);
289    }
290
291   /**
292     * Sets the bluetooth coexistence mode.
293     *
294     * @param mode One of {@link #BLUETOOTH_COEXISTENCE_MODE_DISABLED},
295     *            {@link #BLUETOOTH_COEXISTENCE_MODE_ENABLED}, or
296     *            {@link #BLUETOOTH_COEXISTENCE_MODE_SENSE}.
297     * @return Whether the mode was successfully set.
298     */
299    public boolean setBluetoothCoexistenceMode(int mode) {
300        return doBooleanCommand("DRIVER BTCOEXMODE " + mode);
301    }
302
303    /**
304     * Enable or disable Bluetooth coexistence scan mode. When this mode is on,
305     * some of the low-level scan parameters used by the driver are changed to
306     * reduce interference with A2DP streaming.
307     *
308     * @param isSet whether to enable or disable this mode
309     * @return {@code true} if the command succeeded, {@code false} otherwise.
310     */
311    public boolean setBluetoothCoexistenceScanMode(boolean setCoexScanMode) {
312        if (setCoexScanMode) {
313            return doBooleanCommand("DRIVER BTCOEXSCAN-START");
314        } else {
315            return doBooleanCommand("DRIVER BTCOEXSCAN-STOP");
316        }
317    }
318
319    public boolean saveConfig() {
320        // Make sure we never write out a value for AP_SCAN other than 1
321        return doBooleanCommand("AP_SCAN 1") && doBooleanCommand("SAVE_CONFIG");
322    }
323
324    public boolean setScanResultHandling(int mode) {
325        return doBooleanCommand("AP_SCAN " + mode);
326    }
327
328    public boolean addToBlacklist(String bssid) {
329        if (TextUtils.isEmpty(bssid)) return false;
330        return doBooleanCommand("BLACKLIST " + bssid);
331    }
332
333    public boolean clearBlacklist() {
334        return doBooleanCommand("BLACKLIST clear");
335    }
336
337    public boolean setSuspendOptimizations(boolean enabled) {
338        if (enabled) {
339            return doBooleanCommand("DRIVER SETSUSPENDMODE 1");
340        } else {
341            return doBooleanCommand("DRIVER SETSUSPENDMODE 0");
342        }
343    }
344
345    public boolean setCountryCode(String countryCode) {
346        return doBooleanCommand("DRIVER COUNTRY " + countryCode);
347    }
348
349    public void enableBackgroundScan(boolean enable) {
350        if (enable) {
351            doBooleanCommand("SET pno 1");
352        } else {
353            doBooleanCommand("SET pno 0");
354        }
355    }
356
357    public void setScanInterval(int scanInterval) {
358        doBooleanCommand("SCAN_INTERVAL " + scanInterval);
359    }
360
361    /** Example output:
362     * RSSI=-65
363     * LINKSPEED=48
364     * NOISE=9999
365     * FREQUENCY=0
366     */
367    public String signalPoll() {
368        return doStringCommand("SIGNAL_POLL");
369    }
370
371    public boolean startWpsPbc(String bssid) {
372        if (TextUtils.isEmpty(bssid)) {
373            return doBooleanCommand("WPS_PBC");
374        } else {
375            return doBooleanCommand("WPS_PBC " + bssid);
376        }
377    }
378
379    public boolean startWpsPbc(String iface, String bssid) {
380        if (TextUtils.isEmpty(bssid)) {
381            return doBooleanCommand("WPS_PBC interface=" + iface);
382        } else {
383            return doBooleanCommand("WPS_PBC interface=" + iface + " " + bssid);
384        }
385    }
386
387    public boolean startWpsPinKeypad(String pin) {
388        if (TextUtils.isEmpty(pin)) return false;
389        return doBooleanCommand("WPS_PIN any " + pin);
390    }
391
392    public boolean startWpsPinKeypad(String iface, String pin) {
393        if (TextUtils.isEmpty(pin)) return false;
394        return doBooleanCommand("WPS_PIN interface=" + iface + " any " + pin);
395    }
396
397
398    public String startWpsPinDisplay(String bssid) {
399        if (TextUtils.isEmpty(bssid)) {
400            return doStringCommand("WPS_PIN any");
401        } else {
402            return doStringCommand("WPS_PIN " + bssid);
403        }
404    }
405
406    public String startWpsPinDisplay(String iface, String bssid) {
407        if (TextUtils.isEmpty(bssid)) {
408            return doStringCommand("WPS_PIN interface=" + iface + " any");
409        } else {
410            return doStringCommand("WPS_PIN interface=" + iface + " " + bssid);
411        }
412    }
413
414    /* Configures an access point connection */
415    public boolean startWpsRegistrar(String bssid, String pin) {
416        if (TextUtils.isEmpty(bssid) || TextUtils.isEmpty(pin)) return false;
417        return doBooleanCommand("WPS_REG " + bssid + " " + pin);
418    }
419
420    public boolean cancelWps() {
421        return doBooleanCommand("WPS_CANCEL");
422    }
423
424    public boolean setPersistentReconnect(boolean enabled) {
425        int value = (enabled == true) ? 1 : 0;
426        return doBooleanCommand("SET persistent_reconnect " + value);
427    }
428
429    public boolean setDeviceName(String name) {
430        return doBooleanCommand("SET device_name " + name);
431    }
432
433    public boolean setDeviceType(String type) {
434        return doBooleanCommand("SET device_type " + type);
435    }
436
437    public boolean setConfigMethods(String cfg) {
438        return doBooleanCommand("SET config_methods " + cfg);
439    }
440
441    public boolean setP2pSsidPostfix(String postfix) {
442        return doBooleanCommand("SET p2p_ssid_postfix " + postfix);
443    }
444
445    public boolean setP2pGroupIdle(String iface, int time) {
446        return doBooleanCommand("SET interface=" + iface + " p2p_group_idle " + time);
447    }
448
449    public void setPowerSave(boolean enabled) {
450        if (enabled) {
451            doBooleanCommand("SET ps 1");
452        } else {
453            doBooleanCommand("SET ps 0");
454        }
455    }
456
457    public boolean setP2pPowerSave(String iface, boolean enabled) {
458        if (enabled) {
459            return doBooleanCommand("P2P_SET interface=" + iface + " ps 1");
460        } else {
461            return doBooleanCommand("P2P_SET interface=" + iface + " ps 0");
462        }
463    }
464
465    /**
466     * "sta" prioritizes STA connection over P2P and "p2p" prioritizes
467     * P2P connection over STA
468     */
469    public boolean setConcurrencyPriority(String s) {
470        return doBooleanCommand("P2P_SET conc_priority " + s);
471    }
472
473    public boolean p2pFind() {
474        return doBooleanCommand("P2P_FIND");
475    }
476
477    public boolean p2pFind(int timeout) {
478        if (timeout <= 0) {
479            return p2pFind();
480        }
481        return doBooleanCommand("P2P_FIND " + timeout);
482    }
483
484    public boolean p2pStopFind() {
485       return doBooleanCommand("P2P_STOP_FIND");
486    }
487
488    public boolean p2pListen() {
489        return doBooleanCommand("P2P_LISTEN");
490    }
491
492    public boolean p2pListen(int timeout) {
493        if (timeout <= 0) {
494            return p2pListen();
495        }
496        return doBooleanCommand("P2P_LISTEN " + timeout);
497    }
498
499    public boolean p2pFlush() {
500        return doBooleanCommand("P2P_FLUSH");
501    }
502
503    /* p2p_connect <peer device address> <pbc|pin|PIN#> [label|display|keypad]
504        [persistent] [join|auth] [go_intent=<0..15>] [freq=<in MHz>] */
505    public String p2pConnect(WifiP2pConfig config, boolean joinExistingGroup) {
506        if (config == null) return null;
507        List<String> args = new ArrayList<String>();
508        WpsInfo wps = config.wps;
509        args.add(config.deviceAddress);
510
511        switch (wps.setup) {
512            case WpsInfo.PBC:
513                args.add("pbc");
514                break;
515            case WpsInfo.DISPLAY:
516                if (TextUtils.isEmpty(wps.pin)) {
517                    args.add("pin");
518                } else {
519                    args.add(wps.pin);
520                }
521                args.add("display");
522                break;
523            case WpsInfo.KEYPAD:
524                args.add(wps.pin);
525                args.add("keypad");
526                break;
527            case WpsInfo.LABEL:
528                args.add(wps.pin);
529                args.add("label");
530            default:
531                break;
532        }
533
534        //TODO: Add persist behavior once the supplicant interaction is fixed for both
535        // group and client scenarios
536        /* Persist unless there is an explicit request to not do so*/
537        //if (config.persist != WifiP2pConfig.Persist.NO) args.add("persistent");
538
539        if (joinExistingGroup) args.add("join");
540
541        //TODO: This can be adapted based on device plugged in state and
542        //device battery state
543        int groupOwnerIntent = config.groupOwnerIntent;
544        if (groupOwnerIntent < 0 || groupOwnerIntent > 15) {
545            groupOwnerIntent = DEFAULT_GROUP_OWNER_INTENT;
546        }
547        args.add("go_intent=" + groupOwnerIntent);
548
549        String command = "P2P_CONNECT ";
550        for (String s : args) command += s + " ";
551
552        return doStringCommand(command);
553    }
554
555    public boolean p2pCancelConnect() {
556        return doBooleanCommand("P2P_CANCEL");
557    }
558
559    public boolean p2pProvisionDiscovery(WifiP2pConfig config) {
560        if (config == null) return false;
561
562        switch (config.wps.setup) {
563            case WpsInfo.PBC:
564                return doBooleanCommand("P2P_PROV_DISC " + config.deviceAddress + " pbc");
565            case WpsInfo.DISPLAY:
566                //We are doing display, so provision discovery is keypad
567                return doBooleanCommand("P2P_PROV_DISC " + config.deviceAddress + " keypad");
568            case WpsInfo.KEYPAD:
569                //We are doing keypad, so provision discovery is display
570                return doBooleanCommand("P2P_PROV_DISC " + config.deviceAddress + " display");
571            default:
572                break;
573        }
574        return false;
575    }
576
577    public boolean p2pGroupAdd() {
578        return doBooleanCommand("P2P_GROUP_ADD");
579    }
580
581    public boolean p2pGroupRemove(String iface) {
582        if (TextUtils.isEmpty(iface)) return false;
583        return doBooleanCommand("P2P_GROUP_REMOVE " + iface);
584    }
585
586    public boolean p2pReject(String deviceAddress) {
587        return doBooleanCommand("P2P_REJECT " + deviceAddress);
588    }
589
590    /* Invite a peer to a group */
591    public boolean p2pInvite(WifiP2pGroup group, String deviceAddress) {
592        if (TextUtils.isEmpty(deviceAddress)) return false;
593
594        if (group == null) {
595            return doBooleanCommand("P2P_INVITE peer=" + deviceAddress);
596        } else {
597            return doBooleanCommand("P2P_INVITE group=" + group.getInterface()
598                    + " peer=" + deviceAddress + " go_dev_addr=" + group.getOwner().deviceAddress);
599        }
600    }
601
602    /* Reinvoke a persistent connection */
603    public boolean p2pReinvoke(int netId, String deviceAddress) {
604        if (TextUtils.isEmpty(deviceAddress) || netId < 0) return false;
605
606        return doBooleanCommand("P2P_INVITE persistent=" + netId + " peer=" + deviceAddress);
607    }
608
609
610    public String p2pGetDeviceAddress() {
611        String status = status();
612        if (status == null) return "";
613
614        String[] tokens = status.split("\n");
615        for (String token : tokens) {
616            if (token.startsWith("p2p_device_address=")) {
617                String[] nameValue = token.split("=");
618                if (nameValue.length != 2) break;
619                return nameValue[1];
620            }
621        }
622        return "";
623    }
624
625    public boolean isGroupOwner(String deviceAddress) {
626        /* BSS returns details only for a GO */
627        String bssInfo = doStringCommand("BSS p2p_dev_addr=" + deviceAddress);
628        if (TextUtils.isEmpty(bssInfo)) return false;
629        return true;
630    }
631
632    public String p2pPeer(String deviceAddress) {
633        return doStringCommand("P2P_PEER " + deviceAddress);
634    }
635
636    public boolean p2pServiceAdd(WifiP2pServiceInfo servInfo) {
637        /*
638         * P2P_SERVICE_ADD bonjour <query hexdump> <RDATA hexdump>
639         * P2P_SERVICE_ADD upnp <version hex> <service>
640         *
641         * e.g)
642         * [Bonjour]
643         * # IP Printing over TCP (PTR) (RDATA=MyPrinter._ipp._tcp.local.)
644         * P2P_SERVICE_ADD bonjour 045f697070c00c000c01 094d795072696e746572c027
645         * # IP Printing over TCP (TXT) (RDATA=txtvers=1,pdl=application/postscript)
646         * P2P_SERVICE_ADD bonjour 096d797072696e746572045f697070c00c001001
647         *  09747874766572733d311a70646c3d6170706c69636174696f6e2f706f7374736372797074
648         *
649         * [UPnP]
650         * P2P_SERVICE_ADD upnp 10 uuid:6859dede-8574-59ab-9332-123456789012
651         * P2P_SERVICE_ADD upnp 10 uuid:6859dede-8574-59ab-9332-123456789012::upnp:rootdevice
652         * P2P_SERVICE_ADD upnp 10 uuid:6859dede-8574-59ab-9332-123456789012::urn:schemas-upnp
653         * -org:device:InternetGatewayDevice:1
654         * P2P_SERVICE_ADD upnp 10 uuid:6859dede-8574-59ab-9322-123456789012::urn:schemas-upnp
655         * -org:service:ContentDirectory:2
656         */
657        for (String s : servInfo.getSupplicantQueryList()) {
658            String command = "P2P_SERVICE_ADD";
659            command += (" " + s);
660            if (!doBooleanCommand(command)) {
661                return false;
662            }
663        }
664        return true;
665    }
666
667    public boolean p2pServiceDel(WifiP2pServiceInfo servInfo) {
668        /*
669         * P2P_SERVICE_DEL bonjour <query hexdump>
670         * P2P_SERVICE_DEL upnp <version hex> <service>
671         */
672        for (String s : servInfo.getSupplicantQueryList()) {
673            String command = "P2P_SERVICE_DEL ";
674
675            String[] data = s.split(" ");
676            if (data.length < 2) {
677                return false;
678            }
679            if ("upnp".equals(data[0])) {
680                command += s;
681            } else if ("bonjour".equals(data[0])) {
682                command += data[0];
683                command += (" " + data[1]);
684            } else {
685                return false;
686            }
687            if (!doBooleanCommand(command)) {
688                return false;
689            }
690        }
691        return true;
692    }
693
694    public boolean p2pServiceFlush() {
695        return doBooleanCommand("P2P_SERVICE_FLUSH");
696    }
697
698    public String p2pServDiscReq(String addr, String query) {
699        String command = "P2P_SERV_DISC_REQ";
700        command += (" " + addr);
701        command += (" " + query);
702
703        return doStringCommand(command);
704    }
705
706    public boolean p2pServDiscCancelReq(String id) {
707        return doBooleanCommand("P2P_SERV_DISC_CANCEL_REQ " + id);
708    }
709}
710