WifiNative.java revision 18786eca942042388748b0d98979f21c9dff4a89
14f00eda9fa05364edd719b05b88e4445682eeee5SzuWei Lin/*
24f00eda9fa05364edd719b05b88e4445682eeee5SzuWei Lin * Copyright (C) 2008 The Android Open Source Project
34f00eda9fa05364edd719b05b88e4445682eeee5SzuWei Lin *
44f00eda9fa05364edd719b05b88e4445682eeee5SzuWei Lin * Licensed under the Apache License, Version 2.0 (the "License");
54f00eda9fa05364edd719b05b88e4445682eeee5SzuWei Lin * you may not use this file except in compliance with the License.
64f00eda9fa05364edd719b05b88e4445682eeee5SzuWei Lin * You may obtain a copy of the License at
74f00eda9fa05364edd719b05b88e4445682eeee5SzuWei Lin *
84f00eda9fa05364edd719b05b88e4445682eeee5SzuWei Lin *      http://www.apache.org/licenses/LICENSE-2.0
94f00eda9fa05364edd719b05b88e4445682eeee5SzuWei Lin *
104f00eda9fa05364edd719b05b88e4445682eeee5SzuWei Lin * Unless required by applicable law or agreed to in writing, software
114f00eda9fa05364edd719b05b88e4445682eeee5SzuWei Lin * distributed under the License is distributed on an "AS IS" BASIS,
124f00eda9fa05364edd719b05b88e4445682eeee5SzuWei Lin * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
134f00eda9fa05364edd719b05b88e4445682eeee5SzuWei Lin * See the License for the specific language governing permissions and
144f00eda9fa05364edd719b05b88e4445682eeee5SzuWei Lin * limitations under the License.
154f00eda9fa05364edd719b05b88e4445682eeee5SzuWei Lin */
164f00eda9fa05364edd719b05b88e4445682eeee5SzuWei Lin
17f7403df4976562ecf2c1266e5f05f800037c8772SzuWei Linpackage com.android.server.wifi;
18f7403df4976562ecf2c1266e5f05f800037c8772SzuWei Lin
19f7403df4976562ecf2c1266e5f05f800037c8772SzuWei Linimport android.net.wifi.RttManager;
20169cf6923d84170c383b0e8e720690bd75908a9fSzuWei Linimport android.net.wifi.ScanResult;
21f7403df4976562ecf2c1266e5f05f800037c8772SzuWei Linimport android.net.wifi.WifiConfiguration;
22f7403df4976562ecf2c1266e5f05f800037c8772SzuWei Linimport android.net.wifi.WifiEnterpriseConfig;
23f7403df4976562ecf2c1266e5f05f800037c8772SzuWei Linimport android.net.wifi.WifiLinkLayerStats;
24f7403df4976562ecf2c1266e5f05f800037c8772SzuWei Linimport android.net.wifi.WifiManager;
25dd7337e39b8d2b9123acff329bd9da22efec66d0SzuWei Linimport android.net.wifi.WifiScanner;
26f7403df4976562ecf2c1266e5f05f800037c8772SzuWei Linimport android.net.wifi.WifiSsid;
27f7403df4976562ecf2c1266e5f05f800037c8772SzuWei Linimport android.net.wifi.WpsInfo;
28f7403df4976562ecf2c1266e5f05f800037c8772SzuWei Linimport android.net.wifi.p2p.WifiP2pConfig;
29f7403df4976562ecf2c1266e5f05f800037c8772SzuWei Linimport android.net.wifi.p2p.WifiP2pGroup;
30f7403df4976562ecf2c1266e5f05f800037c8772SzuWei Linimport android.net.wifi.p2p.nsd.WifiP2pServiceInfo;
31dd7337e39b8d2b9123acff329bd9da22efec66d0SzuWei Linimport android.os.SystemClock;
32dd7337e39b8d2b9123acff329bd9da22efec66d0SzuWei Linimport android.os.SystemProperties;
33dd7337e39b8d2b9123acff329bd9da22efec66d0SzuWei Linimport android.text.TextUtils;
34dd7337e39b8d2b9123acff329bd9da22efec66d0SzuWei Linimport android.util.LocalLog;
35dd7337e39b8d2b9123acff329bd9da22efec66d0SzuWei Linimport android.util.Log;
36f7403df4976562ecf2c1266e5f05f800037c8772SzuWei Linimport android.content.Context;
37dd7337e39b8d2b9123acff329bd9da22efec66d0SzuWei Linimport android.content.Intent;
38f7403df4976562ecf2c1266e5f05f800037c8772SzuWei Linimport android.app.AlarmManager;
39f7403df4976562ecf2c1266e5f05f800037c8772SzuWei Linimport android.app.PendingIntent;
40f7403df4976562ecf2c1266e5f05f800037c8772SzuWei Linimport android.content.IntentFilter;
41dd7337e39b8d2b9123acff329bd9da22efec66d0SzuWei Linimport android.content.BroadcastReceiver;
42dd7337e39b8d2b9123acff329bd9da22efec66d0SzuWei Linimport com.android.server.connectivity.KeepalivePacketData;
43dd7337e39b8d2b9123acff329bd9da22efec66d0SzuWei Linimport com.android.server.wifi.hotspot2.NetworkDetail;
44dd7337e39b8d2b9123acff329bd9da22efec66d0SzuWei Linimport com.android.server.wifi.hotspot2.SupplicantBridge;
45dd7337e39b8d2b9123acff329bd9da22efec66d0SzuWei Linimport com.android.server.wifi.hotspot2.Utils;
46dd7337e39b8d2b9123acff329bd9da22efec66d0SzuWei Linimport com.android.server.wifi.util.InformationElementUtil;
47dd7337e39b8d2b9123acff329bd9da22efec66d0SzuWei Lin
48dd7337e39b8d2b9123acff329bd9da22efec66d0SzuWei Linimport libcore.util.HexEncoding;
49dd7337e39b8d2b9123acff329bd9da22efec66d0SzuWei Lin
50dd7337e39b8d2b9123acff329bd9da22efec66d0SzuWei Linimport java.nio.ByteBuffer;
51dd7337e39b8d2b9123acff329bd9da22efec66d0SzuWei Linimport java.nio.CharBuffer;
52f7403df4976562ecf2c1266e5f05f800037c8772SzuWei Linimport java.nio.charset.CharacterCodingException;
53dd7337e39b8d2b9123acff329bd9da22efec66d0SzuWei Linimport java.nio.charset.CharsetDecoder;
54dd7337e39b8d2b9123acff329bd9da22efec66d0SzuWei Linimport java.nio.charset.StandardCharsets;
55dd7337e39b8d2b9123acff329bd9da22efec66d0SzuWei Linimport java.util.ArrayList;
56dd7337e39b8d2b9123acff329bd9da22efec66d0SzuWei Linimport java.util.List;
57dd7337e39b8d2b9123acff329bd9da22efec66d0SzuWei Linimport java.util.Locale;
58f7403df4976562ecf2c1266e5f05f800037c8772SzuWei Lin
59f7403df4976562ecf2c1266e5f05f800037c8772SzuWei Lin/**
60f7403df4976562ecf2c1266e5f05f800037c8772SzuWei Lin * Native calls for bring up/shut down of the supplicant daemon and for
61169cf6923d84170c383b0e8e720690bd75908a9fSzuWei Lin * sending requests to the supplicant daemon
62169cf6923d84170c383b0e8e720690bd75908a9fSzuWei Lin *
63f7403df4976562ecf2c1266e5f05f800037c8772SzuWei Lin * waitForEvent() is called on the monitor thread for events. All other methods
64f7403df4976562ecf2c1266e5f05f800037c8772SzuWei Lin * must be serialized from the framework.
65f7403df4976562ecf2c1266e5f05f800037c8772SzuWei Lin *
66f7403df4976562ecf2c1266e5f05f800037c8772SzuWei Lin * {@hide}
67f7403df4976562ecf2c1266e5f05f800037c8772SzuWei Lin */
68f7403df4976562ecf2c1266e5f05f800037c8772SzuWei Linpublic class WifiNative {
69f7403df4976562ecf2c1266e5f05f800037c8772SzuWei Lin    private static boolean DBG = false;
70f7403df4976562ecf2c1266e5f05f800037c8772SzuWei Lin
71169cf6923d84170c383b0e8e720690bd75908a9fSzuWei Lin    /**
72f7403df4976562ecf2c1266e5f05f800037c8772SzuWei Lin     * Hold this lock before calling supplicant or HAL methods
73f7403df4976562ecf2c1266e5f05f800037c8772SzuWei Lin     * it is required to mutually exclude access to the driver
74f7403df4976562ecf2c1266e5f05f800037c8772SzuWei Lin     */
75f7403df4976562ecf2c1266e5f05f800037c8772SzuWei Lin    private static final Object mLock = new Object();
76f7403df4976562ecf2c1266e5f05f800037c8772SzuWei Lin
77f7403df4976562ecf2c1266e5f05f800037c8772SzuWei Lin    private static final LocalLog mLocalLog = new LocalLog(16384);
78f7403df4976562ecf2c1266e5f05f800037c8772SzuWei Lin
79f7403df4976562ecf2c1266e5f05f800037c8772SzuWei Lin    public static LocalLog getLocalLog() {
80f7403df4976562ecf2c1266e5f05f800037c8772SzuWei Lin        return mLocalLog;
81169cf6923d84170c383b0e8e720690bd75908a9fSzuWei Lin    }
82169cf6923d84170c383b0e8e720690bd75908a9fSzuWei Lin
83169cf6923d84170c383b0e8e720690bd75908a9fSzuWei Lin    /* Register native functions */
84169cf6923d84170c383b0e8e720690bd75908a9fSzuWei Lin    static {
85        /* Native functions are defined in libwifi-service.so */
86        System.loadLibrary("wifi-service");
87        registerNatives();
88    }
89
90    private static native int registerNatives();
91
92    /*
93     * Singleton WifiNative instances
94     */
95    private static WifiNative wlanNativeInterface =
96            new WifiNative(SystemProperties.get("wifi.interface", "wlan0"));
97    public static WifiNative getWlanNativeInterface() {
98        return wlanNativeInterface;
99    }
100
101    //STOPSHIP: get interface name from native side
102    private static WifiNative p2pNativeInterface = new WifiNative("p2p0");
103    public static WifiNative getP2pNativeInterface() {
104        return p2pNativeInterface;
105    }
106
107
108    private final String mTAG;
109    private final String mInterfaceName;
110    private final String mInterfacePrefix;
111
112    private Context mContext = null;
113    private PnoMonitor mPnoMonitor = null;
114    public void initContext(Context context) {
115        if (mContext == null && context != null) {
116            mContext = context;
117            mPnoMonitor = new PnoMonitor();
118        }
119    }
120
121    private WifiNative(String interfaceName) {
122        mInterfaceName = interfaceName;
123        mTAG = "WifiNative-" + interfaceName;
124
125        if (!interfaceName.equals("p2p0")) {
126            mInterfacePrefix = "IFNAME=" + interfaceName + " ";
127        } else {
128            // commands for p2p0 interface don't need prefix
129            mInterfacePrefix = "";
130        }
131    }
132
133    public String getInterfaceName() {
134        return mInterfaceName;
135    }
136
137    // Note this affects logging on for all interfaces
138    void enableVerboseLogging(int verbose) {
139        if (verbose > 0) {
140            DBG = true;
141        } else {
142            DBG = false;
143        }
144    }
145
146    private void localLog(String s) {
147        if (mLocalLog != null)
148            mLocalLog.log(mInterfaceName + ": " + s);
149    }
150
151
152
153    /*
154     * Driver and Supplicant management
155     */
156    private native static boolean loadDriverNative();
157    public boolean loadDriver() {
158        synchronized (mLock) {
159            return loadDriverNative();
160        }
161    }
162
163    private native static boolean isDriverLoadedNative();
164    public boolean isDriverLoaded() {
165        synchronized (mLock) {
166            return isDriverLoadedNative();
167        }
168    }
169
170    private native static boolean unloadDriverNative();
171    public boolean unloadDriver() {
172        synchronized (mLock) {
173            return unloadDriverNative();
174        }
175    }
176
177    private native static boolean startSupplicantNative(boolean p2pSupported);
178    public boolean startSupplicant(boolean p2pSupported) {
179        synchronized (mLock) {
180            return startSupplicantNative(p2pSupported);
181        }
182    }
183
184    /* Sends a kill signal to supplicant. To be used when we have lost connection
185       or when the supplicant is hung */
186    private native static boolean killSupplicantNative(boolean p2pSupported);
187    public boolean killSupplicant(boolean p2pSupported) {
188        synchronized (mLock) {
189            return killSupplicantNative(p2pSupported);
190        }
191    }
192
193    private native static boolean connectToSupplicantNative();
194    public boolean connectToSupplicant() {
195        synchronized (mLock) {
196            localLog(mInterfacePrefix + "connectToSupplicant");
197            return connectToSupplicantNative();
198        }
199    }
200
201    private native static void closeSupplicantConnectionNative();
202    public void closeSupplicantConnection() {
203        synchronized (mLock) {
204            localLog(mInterfacePrefix + "closeSupplicantConnection");
205            closeSupplicantConnectionNative();
206        }
207    }
208
209    /**
210     * Wait for the supplicant to send an event, returning the event string.
211     * @return the event string sent by the supplicant.
212     */
213    private native static String waitForEventNative();
214    public String waitForEvent() {
215        // No synchronization necessary .. it is implemented in WifiMonitor
216        return waitForEventNative();
217    }
218
219
220    /*
221     * Supplicant Command Primitives
222     */
223    private native boolean doBooleanCommandNative(String command);
224
225    private native int doIntCommandNative(String command);
226
227    private native String doStringCommandNative(String command);
228
229    private boolean doBooleanCommand(String command) {
230        if (DBG) Log.d(mTAG, "doBoolean: " + command);
231        synchronized (mLock) {
232            String toLog = mInterfacePrefix + command;
233            boolean result = doBooleanCommandNative(mInterfacePrefix + command);
234            localLog(toLog + " -> " + result);
235            if (DBG) Log.d(mTAG, command + ": returned " + result);
236            return result;
237        }
238    }
239
240    private boolean doBooleanCommandWithoutLogging(String command) {
241        if (DBG) Log.d(mTAG, "doBooleanCommandWithoutLogging: " + command);
242        synchronized (mLock) {
243            boolean result = doBooleanCommandNative(mInterfacePrefix + command);
244            if (DBG) Log.d(mTAG, command + ": returned " + result);
245            return result;
246        }
247    }
248
249    private int doIntCommand(String command) {
250        if (DBG) Log.d(mTAG, "doInt: " + command);
251        synchronized (mLock) {
252            String toLog = mInterfacePrefix + command;
253            int result = doIntCommandNative(mInterfacePrefix + command);
254            localLog(toLog + " -> " + result);
255            if (DBG) Log.d(mTAG, "   returned " + result);
256            return result;
257        }
258    }
259
260    private String doStringCommand(String command) {
261        if (DBG) {
262            //GET_NETWORK commands flood the logs
263            if (!command.startsWith("GET_NETWORK")) {
264                Log.d(mTAG, "doString: [" + command + "]");
265            }
266        }
267        synchronized (mLock) {
268            String toLog = mInterfacePrefix + command;
269            String result = doStringCommandNative(mInterfacePrefix + command);
270            if (result == null) {
271                if (DBG) Log.d(mTAG, "doStringCommandNative no result");
272            } else {
273                if (!command.startsWith("STATUS-")) {
274                    localLog(toLog + " -> " + result);
275                }
276                if (DBG) Log.d(mTAG, "   returned " + result.replace("\n", " "));
277            }
278            return result;
279        }
280    }
281
282    private String doStringCommandWithoutLogging(String command) {
283        if (DBG) {
284            //GET_NETWORK commands flood the logs
285            if (!command.startsWith("GET_NETWORK")) {
286                Log.d(mTAG, "doString: [" + command + "]");
287            }
288        }
289        synchronized (mLock) {
290            return doStringCommandNative(mInterfacePrefix + command);
291        }
292    }
293
294    public String doCustomSupplicantCommand(String command) {
295        return doStringCommand(command);
296    }
297
298    /*
299     * Wrappers for supplicant commands
300     */
301    public boolean ping() {
302        String pong = doStringCommand("PING");
303        return (pong != null && pong.equals("PONG"));
304    }
305
306    public void setSupplicantLogLevel(String level) {
307        doStringCommand("LOG_LEVEL " + level);
308    }
309
310    public String getFreqCapability() {
311        return doStringCommand("GET_CAPABILITY freq");
312    }
313
314
315    public static final int SCAN_WITHOUT_CONNECTION_SETUP          = 1;
316    public static final int SCAN_WITH_CONNECTION_SETUP             = 2;
317
318    public boolean scan(int type, String freqList) {
319        if (type == SCAN_WITHOUT_CONNECTION_SETUP) {
320            if (freqList == null) return doBooleanCommand("SCAN TYPE=ONLY");
321            else return doBooleanCommand("SCAN TYPE=ONLY freq=" + freqList);
322        } else if (type == SCAN_WITH_CONNECTION_SETUP) {
323            if (freqList == null) return doBooleanCommand("SCAN");
324            else return doBooleanCommand("SCAN freq=" + freqList);
325        } else {
326            throw new IllegalArgumentException("Invalid scan type");
327        }
328    }
329
330    /* Does a graceful shutdown of supplicant. Is a common stop function for both p2p and sta.
331     *
332     * Note that underneath we use a harsh-sounding "terminate" supplicant command
333     * for a graceful stop and a mild-sounding "stop" interface
334     * to kill the process
335     */
336    public boolean stopSupplicant() {
337        return doBooleanCommand("TERMINATE");
338    }
339
340    public String listNetworks() {
341        return doStringCommand("LIST_NETWORKS");
342    }
343
344    public String listNetworks(int last_id) {
345        return doStringCommand("LIST_NETWORKS LAST_ID=" + last_id);
346    }
347
348    public int addNetwork() {
349        return doIntCommand("ADD_NETWORK");
350    }
351
352    public boolean setNetworkVariable(int netId, String name, String value) {
353        if (TextUtils.isEmpty(name) || TextUtils.isEmpty(value)) return false;
354        if (name.equals(WifiConfiguration.pskVarName)
355                || name.equals(WifiEnterpriseConfig.PASSWORD_KEY)) {
356            return doBooleanCommandWithoutLogging("SET_NETWORK " + netId + " " + name + " " + value);
357        } else {
358            return doBooleanCommand("SET_NETWORK " + netId + " " + name + " " + value);
359        }
360    }
361
362    public String getNetworkVariable(int netId, String name) {
363        if (TextUtils.isEmpty(name)) return null;
364
365        // GET_NETWORK will likely flood the logs ...
366        return doStringCommandWithoutLogging("GET_NETWORK " + netId + " " + name);
367    }
368
369    public boolean removeNetwork(int netId) {
370        return doBooleanCommand("REMOVE_NETWORK " + netId);
371    }
372
373
374    private void logDbg(String debug) {
375        long now = SystemClock.elapsedRealtimeNanos();
376        String ts = String.format("[%,d us] ", now/1000);
377        Log.e("WifiNative: ", ts+debug+ " stack:"
378                + Thread.currentThread().getStackTrace()[2].getMethodName() +" - "
379                + Thread.currentThread().getStackTrace()[3].getMethodName() +" - "
380                + Thread.currentThread().getStackTrace()[4].getMethodName() +" - "
381                + Thread.currentThread().getStackTrace()[5].getMethodName()+" - "
382                + Thread.currentThread().getStackTrace()[6].getMethodName());
383
384    }
385    public boolean enableNetwork(int netId, boolean disableOthers) {
386        if (DBG) logDbg("enableNetwork nid=" + Integer.toString(netId)
387                + " disableOthers=" + disableOthers);
388        if (disableOthers) {
389            return doBooleanCommand("SELECT_NETWORK " + netId);
390        } else {
391            return doBooleanCommand("ENABLE_NETWORK " + netId);
392        }
393    }
394
395    public boolean disableNetwork(int netId) {
396        if (DBG) logDbg("disableNetwork nid=" + Integer.toString(netId));
397        return doBooleanCommand("DISABLE_NETWORK " + netId);
398    }
399
400    public boolean selectNetwork(int netId) {
401        if (DBG) logDbg("selectNetwork nid=" + Integer.toString(netId));
402        return doBooleanCommand("SELECT_NETWORK " + netId);
403    }
404
405    public boolean reconnect() {
406        if (DBG) logDbg("RECONNECT ");
407        return doBooleanCommand("RECONNECT");
408    }
409
410    public boolean reassociate() {
411        if (DBG) logDbg("REASSOCIATE ");
412        return doBooleanCommand("REASSOCIATE");
413    }
414
415    public boolean disconnect() {
416        if (DBG) logDbg("DISCONNECT ");
417        return doBooleanCommand("DISCONNECT");
418    }
419
420    public String status() {
421        return status(false);
422    }
423
424    public String status(boolean noEvents) {
425        if (noEvents) {
426            return doStringCommand("STATUS-NO_EVENTS");
427        } else {
428            return doStringCommand("STATUS");
429        }
430    }
431
432    public String getMacAddress() {
433        //Macaddr = XX.XX.XX.XX.XX.XX
434        String ret = doStringCommand("DRIVER MACADDR");
435        if (!TextUtils.isEmpty(ret)) {
436            String[] tokens = ret.split(" = ");
437            if (tokens.length == 2) return tokens[1];
438        }
439        return null;
440    }
441
442
443
444    /**
445     * Format of results:
446     * =================
447     * id=1
448     * bssid=68:7f:76:d7:1a:6e
449     * freq=2412
450     * level=-44
451     * tsf=1344626243700342
452     * flags=[WPA2-PSK-CCMP][WPS][ESS]
453     * ssid=zfdy
454     * ====
455     * id=2
456     * bssid=68:5f:74:d7:1a:6f
457     * freq=5180
458     * level=-73
459     * tsf=1344626243700373
460     * flags=[WPA2-PSK-CCMP][WPS][ESS]
461     * ssid=zuby
462     * ====
463     *
464     * RANGE=ALL gets all scan results
465     * RANGE=ID- gets results from ID
466     * MASK=<N> see wpa_supplicant/src/common/wpa_ctrl.h for details
467     * 0                         0                        1                       0     2
468     *                           WPA_BSS_MASK_MESH_SCAN | WPA_BSS_MASK_DELIM    | WPA_BSS_MASK_WIFI_DISPLAY
469     * 0                         0                        0                       1     1   -> 9
470     * WPA_BSS_MASK_INTERNETW  | WPA_BSS_MASK_P2P_SCAN  | WPA_BSS_MASK_WPS_SCAN | WPA_BSS_MASK_SSID
471     * 1                         0                        0                       1     9   -> d
472     * WPA_BSS_MASK_FLAGS      | WPA_BSS_MASK_IE        | WPA_BSS_MASK_AGE      | WPA_BSS_MASK_TSF
473     * 1                         0                        0                       0     8
474     * WPA_BSS_MASK_LEVEL      | WPA_BSS_MASK_NOISE     | WPA_BSS_MASK_QUAL     | WPA_BSS_MASK_CAPABILITIES
475     * 0                         1                        1                       1     7
476     * WPA_BSS_MASK_BEACON_INT | WPA_BSS_MASK_FREQ      | WPA_BSS_MASK_BSSID    | WPA_BSS_MASK_ID
477     *
478     * WPA_BSS_MASK_INTERNETW adds ANQP info (ctrl_iface:4151-4176)
479     *
480     * ctrl_iface.c:wpa_supplicant_ctrl_iface_process:7884
481     *  wpa_supplicant_ctrl_iface_bss:4315
482     *  print_bss_info
483     */
484    private String getRawScanResults(String range) {
485        return doStringCommandWithoutLogging("BSS RANGE=" + range + " MASK=0x29d87");
486    }
487
488    private static final String BSS_IE_STR = "ie=";
489    private static final String BSS_ID_STR = "id=";
490    private static final String BSS_BSSID_STR = "bssid=";
491    private static final String BSS_FREQ_STR = "freq=";
492    private static final String BSS_LEVEL_STR = "level=";
493    private static final String BSS_TSF_STR = "tsf=";
494    private static final String BSS_FLAGS_STR = "flags=";
495    private static final String BSS_SSID_STR = "ssid=";
496    private static final String BSS_DELIMITER_STR = "====";
497    private static final String BSS_END_STR = "####";
498
499    public ArrayList<ScanDetail> getScanResults() {
500        int next_sid = 0;
501        ArrayList<ScanDetail> results = new ArrayList<>();
502        while(next_sid >= 0) {
503            String rawResult = getRawScanResults(next_sid+"-");
504            next_sid = -1;
505
506            if (TextUtils.isEmpty(rawResult))
507                break;
508
509            String[] lines = rawResult.split("\n");
510
511
512            // note that all these splits and substrings keep references to the original
513            // huge string buffer while the amount we really want is generally pretty small
514            // so make copies instead (one example b/11087956 wasted 400k of heap here).
515            final int bssidStrLen = BSS_BSSID_STR.length();
516            final int flagLen = BSS_FLAGS_STR.length();
517
518            String bssid = "";
519            int level = 0;
520            int freq = 0;
521            long tsf = 0;
522            String flags = "";
523            WifiSsid wifiSsid = null;
524            String infoElementsStr = null;
525            List<String> anqpLines = null;
526
527            for (String line : lines) {
528                if (line.startsWith(BSS_ID_STR)) { // Will find the last id line
529                    try {
530                        next_sid = Integer.parseInt(line.substring(BSS_ID_STR.length())) + 1;
531                    } catch (NumberFormatException e) {
532                        // Nothing to do
533                    }
534                } else if (line.startsWith(BSS_BSSID_STR)) {
535                    bssid = new String(line.getBytes(), bssidStrLen, line.length() - bssidStrLen);
536                } else if (line.startsWith(BSS_FREQ_STR)) {
537                    try {
538                        freq = Integer.parseInt(line.substring(BSS_FREQ_STR.length()));
539                    } catch (NumberFormatException e) {
540                        freq = 0;
541                    }
542                } else if (line.startsWith(BSS_LEVEL_STR)) {
543                    try {
544                        level = Integer.parseInt(line.substring(BSS_LEVEL_STR.length()));
545                        /* some implementations avoid negative values by adding 256
546                         * so we need to adjust for that here.
547                         */
548                        if (level > 0) level -= 256;
549                    } catch (NumberFormatException e) {
550                        level = 0;
551                    }
552                } else if (line.startsWith(BSS_TSF_STR)) {
553                    try {
554                        tsf = Long.parseLong(line.substring(BSS_TSF_STR.length()));
555                    } catch (NumberFormatException e) {
556                        tsf = 0;
557                    }
558                } else if (line.startsWith(BSS_FLAGS_STR)) {
559                    flags = new String(line.getBytes(), flagLen, line.length() - flagLen);
560                } else if (line.startsWith(BSS_SSID_STR)) {
561                    wifiSsid = WifiSsid.createFromAsciiEncoded(
562                            line.substring(BSS_SSID_STR.length()));
563                } else if (line.startsWith(BSS_IE_STR)) {
564                    infoElementsStr = line;
565                } else if (SupplicantBridge.isAnqpAttribute(line)) {
566                    if (anqpLines == null) {
567                        anqpLines = new ArrayList<>();
568                    }
569                    anqpLines.add(line);
570                } else if (line.startsWith(BSS_DELIMITER_STR) || line.startsWith(BSS_END_STR)) {
571                    if (bssid != null) {
572                        try {
573                            if (infoElementsStr == null) {
574                                throw new IllegalArgumentException("Null information element data");
575                            }
576                            int seperator = infoElementsStr.indexOf('=');
577                            if (seperator < 0) {
578                                throw new IllegalArgumentException("No element separator");
579                            }
580
581                            ScanResult.InformationElement[] infoElements =
582                                        InformationElementUtil.parseInformationElements(
583                                        Utils.hexToBytes(infoElementsStr.substring(seperator + 1)));
584
585                            NetworkDetail networkDetail = new NetworkDetail(bssid,
586                                    infoElements, anqpLines, freq);
587
588                            String xssid = (wifiSsid != null) ? wifiSsid.toString() : WifiSsid.NONE;
589                            if (!xssid.equals(networkDetail.getTrimmedSSID())) {
590                                Log.d(TAG, String.format(
591                                        "Inconsistent SSID on BSSID '%s': '%s' vs '%s': %s",
592                                        bssid, xssid, networkDetail.getSSID(), infoElementsStr));
593                            }
594
595                            if (networkDetail.hasInterworking()) {
596                                Log.d(TAG, "HSNwk: '" + networkDetail);
597                            }
598
599                            results.add(new ScanDetail(networkDetail, wifiSsid, bssid,
600                                     flags, level, freq, tsf));
601                        } catch (IllegalArgumentException iae) {
602                            Log.d(TAG, "Failed to parse information elements: " + iae);
603                        }
604                    }
605                    bssid = null;
606                    level = 0;
607                    freq = 0;
608                    tsf = 0;
609                    flags = "";
610                    wifiSsid = null;
611                    infoElementsStr = null;
612                    anqpLines = null;
613                }
614            }
615        }
616        return results;
617    }
618
619    /**
620     * Format of result:
621     * id=1016
622     * bssid=00:03:7f:40:84:10
623     * freq=2462
624     * beacon_int=200
625     * capabilities=0x0431
626     * qual=0
627     * noise=0
628     * level=-46
629     * tsf=0000002669008476
630     * age=5
631     * ie=00105143412d485332302d52322d54455354010882848b960c12182403010b0706555...
632     * flags=[WPA2-EAP-CCMP][ESS][P2P][HS20]
633     * ssid=QCA-HS20-R2-TEST
634     * p2p_device_name=
635     * p2p_config_methods=0x0SET_NE
636     * anqp_venue_name=02083d656e6757692d466920416c6c69616e63650a3239383920436f...
637     * anqp_network_auth_type=010000
638     * anqp_roaming_consortium=03506f9a05001bc504bd
639     * anqp_ip_addr_type_availability=0c
640     * anqp_nai_realm=0200300000246d61696c2e6578616d706c652e636f6d3b636973636f2...
641     * anqp_3gpp=000600040132f465
642     * anqp_domain_name=0b65786d61706c652e636f6d
643     * hs20_operator_friendly_name=11656e6757692d466920416c6c69616e63650e636869...
644     * hs20_wan_metrics=01c40900008001000000000a00
645     * hs20_connection_capability=0100000006140001061600000650000106bb010106bb0...
646     * hs20_osu_providers_list=0b5143412d4f53552d425353010901310015656e6757692d...
647     */
648    public String scanResult(String bssid) {
649        return doStringCommand("BSS " + bssid);
650    }
651
652    public boolean startDriver() {
653        return doBooleanCommand("DRIVER START");
654    }
655
656    public boolean stopDriver() {
657        return doBooleanCommand("DRIVER STOP");
658    }
659
660
661    /**
662     * Start filtering out Multicast V4 packets
663     * @return {@code true} if the operation succeeded, {@code false} otherwise
664     *
665     * Multicast filtering rules work as follows:
666     *
667     * The driver can filter multicast (v4 and/or v6) and broadcast packets when in
668     * a power optimized mode (typically when screen goes off).
669     *
670     * In order to prevent the driver from filtering the multicast/broadcast packets, we have to
671     * add a DRIVER RXFILTER-ADD rule followed by DRIVER RXFILTER-START to make the rule effective
672     *
673     * DRIVER RXFILTER-ADD Num
674     *   where Num = 0 - Unicast, 1 - Broadcast, 2 - Mutil4 or 3 - Multi6
675     *
676     * and DRIVER RXFILTER-START
677     * In order to stop the usage of these rules, we do
678     *
679     * DRIVER RXFILTER-STOP
680     * DRIVER RXFILTER-REMOVE Num
681     *   where Num is as described for RXFILTER-ADD
682     *
683     * The  SETSUSPENDOPT driver command overrides the filtering rules
684     */
685    public boolean startFilteringMulticastV4Packets() {
686        return doBooleanCommand("DRIVER RXFILTER-STOP")
687            && doBooleanCommand("DRIVER RXFILTER-REMOVE 2")
688            && doBooleanCommand("DRIVER RXFILTER-START");
689    }
690
691    /**
692     * Stop filtering out Multicast V4 packets.
693     * @return {@code true} if the operation succeeded, {@code false} otherwise
694     */
695    public boolean stopFilteringMulticastV4Packets() {
696        return doBooleanCommand("DRIVER RXFILTER-STOP")
697            && doBooleanCommand("DRIVER RXFILTER-ADD 2")
698            && doBooleanCommand("DRIVER RXFILTER-START");
699    }
700
701    /**
702     * Start filtering out Multicast V6 packets
703     * @return {@code true} if the operation succeeded, {@code false} otherwise
704     */
705    public boolean startFilteringMulticastV6Packets() {
706        return doBooleanCommand("DRIVER RXFILTER-STOP")
707            && doBooleanCommand("DRIVER RXFILTER-REMOVE 3")
708            && doBooleanCommand("DRIVER RXFILTER-START");
709    }
710
711    /**
712     * Stop filtering out Multicast V6 packets.
713     * @return {@code true} if the operation succeeded, {@code false} otherwise
714     */
715    public boolean stopFilteringMulticastV6Packets() {
716        return doBooleanCommand("DRIVER RXFILTER-STOP")
717            && doBooleanCommand("DRIVER RXFILTER-ADD 3")
718            && doBooleanCommand("DRIVER RXFILTER-START");
719    }
720
721    /**
722     * Set the operational frequency band
723     * @param band One of
724     *     {@link WifiManager#WIFI_FREQUENCY_BAND_AUTO},
725     *     {@link WifiManager#WIFI_FREQUENCY_BAND_5GHZ},
726     *     {@link WifiManager#WIFI_FREQUENCY_BAND_2GHZ},
727     * @return {@code true} if the operation succeeded, {@code false} otherwise
728     */
729    public boolean setBand(int band) {
730        String bandstr;
731
732        if (band == WifiManager.WIFI_FREQUENCY_BAND_5GHZ)
733            bandstr = "5G";
734        else if (band == WifiManager.WIFI_FREQUENCY_BAND_2GHZ)
735            bandstr = "2G";
736        else
737            bandstr = "AUTO";
738        return doBooleanCommand("SET SETBAND " + bandstr);
739    }
740
741    public static final int BLUETOOTH_COEXISTENCE_MODE_ENABLED     = 0;
742    public static final int BLUETOOTH_COEXISTENCE_MODE_DISABLED    = 1;
743    public static final int BLUETOOTH_COEXISTENCE_MODE_SENSE       = 2;
744    /**
745      * Sets the bluetooth coexistence mode.
746      *
747      * @param mode One of {@link #BLUETOOTH_COEXISTENCE_MODE_DISABLED},
748      *            {@link #BLUETOOTH_COEXISTENCE_MODE_ENABLED}, or
749      *            {@link #BLUETOOTH_COEXISTENCE_MODE_SENSE}.
750      * @return Whether the mode was successfully set.
751      */
752    public boolean setBluetoothCoexistenceMode(int mode) {
753        return doBooleanCommand("DRIVER BTCOEXMODE " + mode);
754    }
755
756    /**
757     * Enable or disable Bluetooth coexistence scan mode. When this mode is on,
758     * some of the low-level scan parameters used by the driver are changed to
759     * reduce interference with A2DP streaming.
760     *
761     * @param isSet whether to enable or disable this mode
762     * @return {@code true} if the command succeeded, {@code false} otherwise.
763     */
764    public boolean setBluetoothCoexistenceScanMode(boolean setCoexScanMode) {
765        if (setCoexScanMode) {
766            return doBooleanCommand("DRIVER BTCOEXSCAN-START");
767        } else {
768            return doBooleanCommand("DRIVER BTCOEXSCAN-STOP");
769        }
770    }
771
772    public void enableSaveConfig() {
773        doBooleanCommand("SET update_config 1");
774    }
775
776    public boolean saveConfig() {
777        return doBooleanCommand("SAVE_CONFIG");
778    }
779
780    public boolean addToBlacklist(String bssid) {
781        if (TextUtils.isEmpty(bssid)) return false;
782        return doBooleanCommand("BLACKLIST " + bssid);
783    }
784
785    public boolean clearBlacklist() {
786        return doBooleanCommand("BLACKLIST clear");
787    }
788
789    public boolean setSuspendOptimizations(boolean enabled) {
790        if (enabled) {
791            return doBooleanCommand("DRIVER SETSUSPENDMODE 1");
792        } else {
793            return doBooleanCommand("DRIVER SETSUSPENDMODE 0");
794        }
795    }
796
797    public boolean setCountryCode(String countryCode) {
798        if (countryCode != null)
799            return doBooleanCommand("DRIVER COUNTRY " + countryCode.toUpperCase(Locale.ROOT));
800        else
801            return doBooleanCommand("DRIVER COUNTRY");
802    }
803
804    //PNO Monitor
805    private class PnoMonitor {
806        private static final int MINIMUM_PNO_GAP = 5 * 1000;
807        private static final String ACTION_TOGGLE_PNO =
808            "com.android.server.Wifi.action.TOGGLE_PNO";
809        long mLastPnoChangeTimeStamp = -1L;
810        boolean mExpectedPnoState = false;
811        boolean mCurrentPnoState = false;;
812        boolean mWaitForTimer = false;
813        final Object mPnoLock = new Object();
814        private final AlarmManager mAlarmManager =
815                (AlarmManager) mContext.getSystemService(Context.ALARM_SERVICE);
816        private final PendingIntent mPnoIntent;
817
818        public PnoMonitor() {
819            Intent intent = new Intent(ACTION_TOGGLE_PNO, null);
820            intent.setPackage("android");
821            mPnoIntent = PendingIntent.getBroadcast(mContext, 0, intent, 0);
822
823            mContext.registerReceiver(
824                new BroadcastReceiver() {
825                    @Override
826                    public void onReceive(Context context, Intent intent) {
827                        synchronized(mPnoLock) {
828                            if (DBG) Log.d(mTAG, "PNO timer expire, PNO should change to " +
829                                    mExpectedPnoState);
830                            if (mCurrentPnoState != mExpectedPnoState) {
831                                if (DBG) Log.d(mTAG, "change PNO from " + mCurrentPnoState + " to "
832                                        + mExpectedPnoState);
833                                boolean ret = setPno(mExpectedPnoState);
834                                if (!ret) {
835                                    Log.e(mTAG, "set PNO failure");
836                                }
837                            } else {
838                                if (DBG) Log.d(mTAG, "Do not change PNO since current is expected");
839                            }
840                            mWaitForTimer = false;
841                        }
842                    }
843                },
844                new IntentFilter(ACTION_TOGGLE_PNO));
845        }
846
847        private boolean setPno(boolean enable) {
848            String cmd = enable ? "SET pno 1" : "SET pno 0";
849            boolean ret = doBooleanCommand(cmd);
850            mLastPnoChangeTimeStamp = System.currentTimeMillis();
851            if (ret) {
852                mCurrentPnoState = enable;
853            }
854            return ret;
855        }
856
857        public boolean enableBackgroundScan(boolean enable) {
858            synchronized(mPnoLock) {
859                if (mWaitForTimer) {
860                    //already has a timer
861                    mExpectedPnoState = enable;
862                    if (DBG) Log.d(mTAG, "update expected PNO to " +  mExpectedPnoState);
863                } else {
864                    if (mCurrentPnoState == enable) {
865                        return true;
866                    }
867                    long timeDifference = System.currentTimeMillis() - mLastPnoChangeTimeStamp;
868                    if (timeDifference >= MINIMUM_PNO_GAP) {
869                        return setPno(enable);
870                    } else {
871                        mExpectedPnoState = enable;
872                        mWaitForTimer = true;
873                        if (DBG) Log.d(mTAG, "start PNO timer with delay:" + timeDifference);
874                        mAlarmManager.set(AlarmManager.RTC_WAKEUP,
875                                System.currentTimeMillis() + timeDifference, mPnoIntent);
876                    }
877                }
878                return true;
879            }
880        }
881    }
882
883    public boolean enableBackgroundScan(boolean enable) {
884        if (mPnoMonitor != null) {
885            return mPnoMonitor.enableBackgroundScan(enable);
886        } else {
887            return false;
888        }
889    }
890
891    public void enableAutoConnect(boolean enable) {
892        if (enable) {
893            doBooleanCommand("STA_AUTOCONNECT 1");
894        } else {
895            doBooleanCommand("STA_AUTOCONNECT 0");
896        }
897    }
898
899    public void setScanInterval(int scanInterval) {
900        doBooleanCommand("SCAN_INTERVAL " + scanInterval);
901    }
902
903    public void setHs20(boolean hs20) {
904        if (hs20) {
905            doBooleanCommand("SET HS20 1");
906        } else {
907            doBooleanCommand("SET HS20 0");
908        }
909    }
910
911    public void startTdls(String macAddr, boolean enable) {
912        if (enable) {
913            synchronized (mLock) {
914                doBooleanCommand("TDLS_DISCOVER " + macAddr);
915                doBooleanCommand("TDLS_SETUP " + macAddr);
916            }
917        } else {
918            doBooleanCommand("TDLS_TEARDOWN " + macAddr);
919        }
920    }
921
922    /** Example output:
923     * RSSI=-65
924     * LINKSPEED=48
925     * NOISE=9999
926     * FREQUENCY=0
927     */
928    public String signalPoll() {
929        return doStringCommandWithoutLogging("SIGNAL_POLL");
930    }
931
932    /** Example outout:
933     * TXGOOD=396
934     * TXBAD=1
935     */
936    public String pktcntPoll() {
937        return doStringCommand("PKTCNT_POLL");
938    }
939
940    public void bssFlush() {
941        doBooleanCommand("BSS_FLUSH 0");
942    }
943
944    public boolean startWpsPbc(String bssid) {
945        if (TextUtils.isEmpty(bssid)) {
946            return doBooleanCommand("WPS_PBC");
947        } else {
948            return doBooleanCommand("WPS_PBC " + bssid);
949        }
950    }
951
952    public boolean startWpsPbc(String iface, String bssid) {
953        synchronized (mLock) {
954            if (TextUtils.isEmpty(bssid)) {
955                return doBooleanCommandNative("IFNAME=" + iface + " WPS_PBC");
956            } else {
957                return doBooleanCommandNative("IFNAME=" + iface + " WPS_PBC " + bssid);
958            }
959        }
960    }
961
962    public boolean startWpsPinKeypad(String pin) {
963        if (TextUtils.isEmpty(pin)) return false;
964        return doBooleanCommand("WPS_PIN any " + pin);
965    }
966
967    public boolean startWpsPinKeypad(String iface, String pin) {
968        if (TextUtils.isEmpty(pin)) return false;
969        synchronized (mLock) {
970            return doBooleanCommandNative("IFNAME=" + iface + " WPS_PIN any " + pin);
971        }
972    }
973
974
975    public String startWpsPinDisplay(String bssid) {
976        if (TextUtils.isEmpty(bssid)) {
977            return doStringCommand("WPS_PIN any");
978        } else {
979            return doStringCommand("WPS_PIN " + bssid);
980        }
981    }
982
983    public String startWpsPinDisplay(String iface, String bssid) {
984        synchronized (mLock) {
985            if (TextUtils.isEmpty(bssid)) {
986                return doStringCommandNative("IFNAME=" + iface + " WPS_PIN any");
987            } else {
988                return doStringCommandNative("IFNAME=" + iface + " WPS_PIN " + bssid);
989            }
990        }
991    }
992
993    public boolean setExternalSim(boolean external) {
994        String value = external ? "1" : "0";
995        Log.d(TAG, "Setting external_sim to " + value);
996        return doBooleanCommand("SET external_sim " + value);
997    }
998
999    public boolean simAuthResponse(int id, String type, String response) {
1000        // with type = GSM-AUTH, UMTS-AUTH or UMTS-AUTS
1001        return doBooleanCommand("CTRL-RSP-SIM-" + id + ":" + type + response);
1002    }
1003
1004    public boolean simIdentityResponse(int id, String response) {
1005        return doBooleanCommand("CTRL-RSP-IDENTITY-" + id + ":" + response);
1006    }
1007
1008    /* Configures an access point connection */
1009    public boolean startWpsRegistrar(String bssid, String pin) {
1010        if (TextUtils.isEmpty(bssid) || TextUtils.isEmpty(pin)) return false;
1011        return doBooleanCommand("WPS_REG " + bssid + " " + pin);
1012    }
1013
1014    public boolean cancelWps() {
1015        return doBooleanCommand("WPS_CANCEL");
1016    }
1017
1018    public boolean setPersistentReconnect(boolean enabled) {
1019        int value = (enabled == true) ? 1 : 0;
1020        return doBooleanCommand("SET persistent_reconnect " + value);
1021    }
1022
1023    public boolean setDeviceName(String name) {
1024        return doBooleanCommand("SET device_name " + name);
1025    }
1026
1027    public boolean setDeviceType(String type) {
1028        return doBooleanCommand("SET device_type " + type);
1029    }
1030
1031    public boolean setConfigMethods(String cfg) {
1032        return doBooleanCommand("SET config_methods " + cfg);
1033    }
1034
1035    public boolean setManufacturer(String value) {
1036        return doBooleanCommand("SET manufacturer " + value);
1037    }
1038
1039    public boolean setModelName(String value) {
1040        return doBooleanCommand("SET model_name " + value);
1041    }
1042
1043    public boolean setModelNumber(String value) {
1044        return doBooleanCommand("SET model_number " + value);
1045    }
1046
1047    public boolean setSerialNumber(String value) {
1048        return doBooleanCommand("SET serial_number " + value);
1049    }
1050
1051    public boolean setP2pSsidPostfix(String postfix) {
1052        return doBooleanCommand("SET p2p_ssid_postfix " + postfix);
1053    }
1054
1055    public boolean setP2pGroupIdle(String iface, int time) {
1056        synchronized (mLock) {
1057            return doBooleanCommandNative("IFNAME=" + iface + " SET p2p_group_idle " + time);
1058        }
1059    }
1060
1061    public void setPowerSave(boolean enabled) {
1062        if (enabled) {
1063            doBooleanCommand("SET ps 1");
1064        } else {
1065            doBooleanCommand("SET ps 0");
1066        }
1067    }
1068
1069    public boolean setP2pPowerSave(String iface, boolean enabled) {
1070        synchronized (mLock) {
1071            if (enabled) {
1072                return doBooleanCommandNative("IFNAME=" + iface + " P2P_SET ps 1");
1073            } else {
1074                return doBooleanCommandNative("IFNAME=" + iface + " P2P_SET ps 0");
1075            }
1076        }
1077    }
1078
1079    public boolean setWfdEnable(boolean enable) {
1080        return doBooleanCommand("SET wifi_display " + (enable ? "1" : "0"));
1081    }
1082
1083    public boolean setWfdDeviceInfo(String hex) {
1084        return doBooleanCommand("WFD_SUBELEM_SET 0 " + hex);
1085    }
1086
1087    /**
1088     * "sta" prioritizes STA connection over P2P and "p2p" prioritizes
1089     * P2P connection over STA
1090     */
1091    public boolean setConcurrencyPriority(String s) {
1092        return doBooleanCommand("P2P_SET conc_pref " + s);
1093    }
1094
1095    public boolean p2pFind() {
1096        return doBooleanCommand("P2P_FIND");
1097    }
1098
1099    public boolean p2pFind(int timeout) {
1100        if (timeout <= 0) {
1101            return p2pFind();
1102        }
1103        return doBooleanCommand("P2P_FIND " + timeout);
1104    }
1105
1106    public boolean p2pStopFind() {
1107       return doBooleanCommand("P2P_STOP_FIND");
1108    }
1109
1110    public boolean p2pListen() {
1111        return doBooleanCommand("P2P_LISTEN");
1112    }
1113
1114    public boolean p2pListen(int timeout) {
1115        if (timeout <= 0) {
1116            return p2pListen();
1117        }
1118        return doBooleanCommand("P2P_LISTEN " + timeout);
1119    }
1120
1121    public boolean p2pExtListen(boolean enable, int period, int interval) {
1122        if (enable && interval < period) {
1123            return false;
1124        }
1125        return doBooleanCommand("P2P_EXT_LISTEN"
1126                    + (enable ? (" " + period + " " + interval) : ""));
1127    }
1128
1129    public boolean p2pSetChannel(int lc, int oc) {
1130        if (DBG) Log.d(mTAG, "p2pSetChannel: lc="+lc+", oc="+oc);
1131
1132        synchronized (mLock) {
1133            if (lc >=1 && lc <= 11) {
1134                if (!doBooleanCommand("P2P_SET listen_channel " + lc)) {
1135                    return false;
1136                }
1137            } else if (lc != 0) {
1138                return false;
1139            }
1140
1141            if (oc >= 1 && oc <= 165 ) {
1142                int freq = (oc <= 14 ? 2407 : 5000) + oc * 5;
1143                return doBooleanCommand("P2P_SET disallow_freq 1000-"
1144                        + (freq - 5) + "," + (freq + 5) + "-6000");
1145            } else if (oc == 0) {
1146                /* oc==0 disables "P2P_SET disallow_freq" (enables all freqs) */
1147                return doBooleanCommand("P2P_SET disallow_freq \"\"");
1148            }
1149        }
1150        return false;
1151    }
1152
1153    public boolean p2pFlush() {
1154        return doBooleanCommand("P2P_FLUSH");
1155    }
1156
1157    private static final int DEFAULT_GROUP_OWNER_INTENT     = 6;
1158    /* p2p_connect <peer device address> <pbc|pin|PIN#> [label|display|keypad]
1159        [persistent] [join|auth] [go_intent=<0..15>] [freq=<in MHz>] */
1160    public String p2pConnect(WifiP2pConfig config, boolean joinExistingGroup) {
1161        if (config == null) return null;
1162        List<String> args = new ArrayList<String>();
1163        WpsInfo wps = config.wps;
1164        args.add(config.deviceAddress);
1165
1166        switch (wps.setup) {
1167            case WpsInfo.PBC:
1168                args.add("pbc");
1169                break;
1170            case WpsInfo.DISPLAY:
1171                if (TextUtils.isEmpty(wps.pin)) {
1172                    args.add("pin");
1173                } else {
1174                    args.add(wps.pin);
1175                }
1176                args.add("display");
1177                break;
1178            case WpsInfo.KEYPAD:
1179                args.add(wps.pin);
1180                args.add("keypad");
1181                break;
1182            case WpsInfo.LABEL:
1183                args.add(wps.pin);
1184                args.add("label");
1185            default:
1186                break;
1187        }
1188
1189        if (config.netId == WifiP2pGroup.PERSISTENT_NET_ID) {
1190            args.add("persistent");
1191        }
1192
1193        if (joinExistingGroup) {
1194            args.add("join");
1195        } else {
1196            //TODO: This can be adapted based on device plugged in state and
1197            //device battery state
1198            int groupOwnerIntent = config.groupOwnerIntent;
1199            if (groupOwnerIntent < 0 || groupOwnerIntent > 15) {
1200                groupOwnerIntent = DEFAULT_GROUP_OWNER_INTENT;
1201            }
1202            args.add("go_intent=" + groupOwnerIntent);
1203        }
1204
1205        String command = "P2P_CONNECT ";
1206        for (String s : args) command += s + " ";
1207
1208        return doStringCommand(command);
1209    }
1210
1211    public boolean p2pCancelConnect() {
1212        return doBooleanCommand("P2P_CANCEL");
1213    }
1214
1215    public boolean p2pProvisionDiscovery(WifiP2pConfig config) {
1216        if (config == null) return false;
1217
1218        switch (config.wps.setup) {
1219            case WpsInfo.PBC:
1220                return doBooleanCommand("P2P_PROV_DISC " + config.deviceAddress + " pbc");
1221            case WpsInfo.DISPLAY:
1222                //We are doing display, so provision discovery is keypad
1223                return doBooleanCommand("P2P_PROV_DISC " + config.deviceAddress + " keypad");
1224            case WpsInfo.KEYPAD:
1225                //We are doing keypad, so provision discovery is display
1226                return doBooleanCommand("P2P_PROV_DISC " + config.deviceAddress + " display");
1227            default:
1228                break;
1229        }
1230        return false;
1231    }
1232
1233    public boolean p2pGroupAdd(boolean persistent) {
1234        if (persistent) {
1235            return doBooleanCommand("P2P_GROUP_ADD persistent");
1236        }
1237        return doBooleanCommand("P2P_GROUP_ADD");
1238    }
1239
1240    public boolean p2pGroupAdd(int netId) {
1241        return doBooleanCommand("P2P_GROUP_ADD persistent=" + netId);
1242    }
1243
1244    public boolean p2pGroupRemove(String iface) {
1245        if (TextUtils.isEmpty(iface)) return false;
1246        synchronized (mLock) {
1247            return doBooleanCommandNative("IFNAME=" + iface + " P2P_GROUP_REMOVE " + iface);
1248        }
1249    }
1250
1251    public boolean p2pReject(String deviceAddress) {
1252        return doBooleanCommand("P2P_REJECT " + deviceAddress);
1253    }
1254
1255    /* Invite a peer to a group */
1256    public boolean p2pInvite(WifiP2pGroup group, String deviceAddress) {
1257        if (TextUtils.isEmpty(deviceAddress)) return false;
1258
1259        if (group == null) {
1260            return doBooleanCommand("P2P_INVITE peer=" + deviceAddress);
1261        } else {
1262            return doBooleanCommand("P2P_INVITE group=" + group.getInterface()
1263                    + " peer=" + deviceAddress + " go_dev_addr=" + group.getOwner().deviceAddress);
1264        }
1265    }
1266
1267    /* Reinvoke a persistent connection */
1268    public boolean p2pReinvoke(int netId, String deviceAddress) {
1269        if (TextUtils.isEmpty(deviceAddress) || netId < 0) return false;
1270
1271        return doBooleanCommand("P2P_INVITE persistent=" + netId + " peer=" + deviceAddress);
1272    }
1273
1274    public String p2pGetSsid(String deviceAddress) {
1275        return p2pGetParam(deviceAddress, "oper_ssid");
1276    }
1277
1278    public String p2pGetDeviceAddress() {
1279        Log.d(TAG, "p2pGetDeviceAddress");
1280
1281        String status = null;
1282
1283        /* Explicitly calling the API without IFNAME= prefix to take care of the devices that
1284        don't have p2p0 interface. Supplicant seems to be returning the correct address anyway. */
1285
1286        synchronized (mLock) {
1287            status = doStringCommandNative("STATUS");
1288        }
1289
1290        String result = "";
1291        if (status != null) {
1292            String[] tokens = status.split("\n");
1293            for (String token : tokens) {
1294                if (token.startsWith("p2p_device_address=")) {
1295                    String[] nameValue = token.split("=");
1296                    if (nameValue.length != 2)
1297                        break;
1298                    result = nameValue[1];
1299                }
1300            }
1301        }
1302
1303        Log.d(TAG, "p2pGetDeviceAddress returning " + result);
1304        return result;
1305    }
1306
1307    public int getGroupCapability(String deviceAddress) {
1308        int gc = 0;
1309        if (TextUtils.isEmpty(deviceAddress)) return gc;
1310        String peerInfo = p2pPeer(deviceAddress);
1311        if (TextUtils.isEmpty(peerInfo)) return gc;
1312
1313        String[] tokens = peerInfo.split("\n");
1314        for (String token : tokens) {
1315            if (token.startsWith("group_capab=")) {
1316                String[] nameValue = token.split("=");
1317                if (nameValue.length != 2) break;
1318                try {
1319                    return Integer.decode(nameValue[1]);
1320                } catch(NumberFormatException e) {
1321                    return gc;
1322                }
1323            }
1324        }
1325        return gc;
1326    }
1327
1328    public String p2pPeer(String deviceAddress) {
1329        return doStringCommand("P2P_PEER " + deviceAddress);
1330    }
1331
1332    private String p2pGetParam(String deviceAddress, String key) {
1333        if (deviceAddress == null) return null;
1334
1335        String peerInfo = p2pPeer(deviceAddress);
1336        if (peerInfo == null) return null;
1337        String[] tokens= peerInfo.split("\n");
1338
1339        key += "=";
1340        for (String token : tokens) {
1341            if (token.startsWith(key)) {
1342                String[] nameValue = token.split("=");
1343                if (nameValue.length != 2) break;
1344                return nameValue[1];
1345            }
1346        }
1347        return null;
1348    }
1349
1350    public boolean p2pServiceAdd(WifiP2pServiceInfo servInfo) {
1351        /*
1352         * P2P_SERVICE_ADD bonjour <query hexdump> <RDATA hexdump>
1353         * P2P_SERVICE_ADD upnp <version hex> <service>
1354         *
1355         * e.g)
1356         * [Bonjour]
1357         * # IP Printing over TCP (PTR) (RDATA=MyPrinter._ipp._tcp.local.)
1358         * P2P_SERVICE_ADD bonjour 045f697070c00c000c01 094d795072696e746572c027
1359         * # IP Printing over TCP (TXT) (RDATA=txtvers=1,pdl=application/postscript)
1360         * P2P_SERVICE_ADD bonjour 096d797072696e746572045f697070c00c001001
1361         *  09747874766572733d311a70646c3d6170706c69636174696f6e2f706f7374736372797074
1362         *
1363         * [UPnP]
1364         * P2P_SERVICE_ADD upnp 10 uuid:6859dede-8574-59ab-9332-123456789012
1365         * P2P_SERVICE_ADD upnp 10 uuid:6859dede-8574-59ab-9332-123456789012::upnp:rootdevice
1366         * P2P_SERVICE_ADD upnp 10 uuid:6859dede-8574-59ab-9332-123456789012::urn:schemas-upnp
1367         * -org:device:InternetGatewayDevice:1
1368         * P2P_SERVICE_ADD upnp 10 uuid:6859dede-8574-59ab-9322-123456789012::urn:schemas-upnp
1369         * -org:service:ContentDirectory:2
1370         */
1371        synchronized (mLock) {
1372            for (String s : servInfo.getSupplicantQueryList()) {
1373                String command = "P2P_SERVICE_ADD";
1374                command += (" " + s);
1375                if (!doBooleanCommand(command)) {
1376                    return false;
1377                }
1378            }
1379        }
1380        return true;
1381    }
1382
1383    public boolean p2pServiceDel(WifiP2pServiceInfo servInfo) {
1384        /*
1385         * P2P_SERVICE_DEL bonjour <query hexdump>
1386         * P2P_SERVICE_DEL upnp <version hex> <service>
1387         */
1388        synchronized (mLock) {
1389            for (String s : servInfo.getSupplicantQueryList()) {
1390                String command = "P2P_SERVICE_DEL ";
1391
1392                String[] data = s.split(" ");
1393                if (data.length < 2) {
1394                    return false;
1395                }
1396                if ("upnp".equals(data[0])) {
1397                    command += s;
1398                } else if ("bonjour".equals(data[0])) {
1399                    command += data[0];
1400                    command += (" " + data[1]);
1401                } else {
1402                    return false;
1403                }
1404                if (!doBooleanCommand(command)) {
1405                    return false;
1406                }
1407            }
1408        }
1409        return true;
1410    }
1411
1412    public boolean p2pServiceFlush() {
1413        return doBooleanCommand("P2P_SERVICE_FLUSH");
1414    }
1415
1416    public String p2pServDiscReq(String addr, String query) {
1417        String command = "P2P_SERV_DISC_REQ";
1418        command += (" " + addr);
1419        command += (" " + query);
1420
1421        return doStringCommand(command);
1422    }
1423
1424    public boolean p2pServDiscCancelReq(String id) {
1425        return doBooleanCommand("P2P_SERV_DISC_CANCEL_REQ " + id);
1426    }
1427
1428    /* Set the current mode of miracast operation.
1429     *  0 = disabled
1430     *  1 = operating as source
1431     *  2 = operating as sink
1432     */
1433    public void setMiracastMode(int mode) {
1434        // Note: optional feature on the driver. It is ok for this to fail.
1435        doBooleanCommand("DRIVER MIRACAST " + mode);
1436    }
1437
1438    public boolean fetchAnqp(String bssid, String subtypes) {
1439        return doBooleanCommand("ANQP_GET " + bssid + " " + subtypes);
1440    }
1441
1442    /*
1443     * NFC-related calls
1444     */
1445    public String getNfcWpsConfigurationToken(int netId) {
1446        return doStringCommand("WPS_NFC_CONFIG_TOKEN WPS " + netId);
1447    }
1448
1449    public String getNfcHandoverRequest() {
1450        return doStringCommand("NFC_GET_HANDOVER_REQ NDEF P2P-CR");
1451    }
1452
1453    public String getNfcHandoverSelect() {
1454        return doStringCommand("NFC_GET_HANDOVER_SEL NDEF P2P-CR");
1455    }
1456
1457    public boolean initiatorReportNfcHandover(String selectMessage) {
1458        return doBooleanCommand("NFC_REPORT_HANDOVER INIT P2P 00 " + selectMessage);
1459    }
1460
1461    public boolean responderReportNfcHandover(String requestMessage) {
1462        return doBooleanCommand("NFC_REPORT_HANDOVER RESP P2P " + requestMessage + " 00");
1463    }
1464
1465    /* WIFI HAL support */
1466
1467    // HAL command ids
1468    private static int sCmdId = 1;
1469    private static int getNewCmdIdLocked() {
1470        return sCmdId++;
1471    }
1472
1473    private static final String TAG = "WifiNative-HAL";
1474    private static long sWifiHalHandle = 0;             /* used by JNI to save wifi_handle */
1475    private static long[] sWifiIfaceHandles = null;     /* used by JNI to save interface handles */
1476    private static int sWlan0Index = -1;
1477    private static int sP2p0Index = -1;
1478    private static MonitorThread sThread;
1479    private static final int STOP_HAL_TIMEOUT_MS = 1000;
1480
1481    private static native boolean startHalNative();
1482    private static native void stopHalNative();
1483    private static native void waitForHalEventNative();
1484
1485    private static class MonitorThread extends Thread {
1486        public void run() {
1487            Log.i(TAG, "Waiting for HAL events mWifiHalHandle=" + Long.toString(sWifiHalHandle));
1488            waitForHalEventNative();
1489        }
1490    }
1491
1492    public boolean startHal() {
1493        String debugLog = "startHal stack: ";
1494        java.lang.StackTraceElement[] elements = Thread.currentThread().getStackTrace();
1495        for (int i = 2; i < elements.length && i <= 7; i++ ) {
1496            debugLog = debugLog + " - " + elements[i].getMethodName();
1497        }
1498
1499        mLocalLog.log(debugLog);
1500
1501        synchronized (mLock) {
1502            if (startHalNative() && (getInterfaces() != 0) && (sWlan0Index != -1)) {
1503                sThread = new MonitorThread();
1504                sThread.start();
1505                return true;
1506            } else {
1507                if (DBG) mLocalLog.log("Could not start hal");
1508                Log.e(TAG, "Could not start hal");
1509                return false;
1510            }
1511        }
1512    }
1513
1514    public void stopHal() {
1515        synchronized (mLock) {
1516            if (isHalStarted()) {
1517                stopHalNative();
1518                try {
1519                    sThread.join(STOP_HAL_TIMEOUT_MS);
1520                    Log.d(TAG, "HAL event thread stopped successfully");
1521                } catch (InterruptedException e) {
1522                    Log.e(TAG, "Could not stop HAL cleanly");
1523                }
1524                sThread = null;
1525                sWifiHalHandle = 0;
1526                sWifiIfaceHandles = null;
1527                sWlan0Index = -1;
1528                sP2p0Index = -1;
1529            }
1530        }
1531    }
1532
1533    public boolean isHalStarted() {
1534        return (sWifiHalHandle != 0);
1535    }
1536    private static native int getInterfacesNative();
1537
1538    public int getInterfaces() {
1539        synchronized (mLock) {
1540            if (isHalStarted()) {
1541                if (sWifiIfaceHandles == null) {
1542                    int num = getInterfacesNative();
1543                    int wifi_num = 0;
1544                    for (int i = 0; i < num; i++) {
1545                        String name = getInterfaceNameNative(i);
1546                        Log.i(TAG, "interface[" + i + "] = " + name);
1547                        if (name.equals("wlan0")) {
1548                            sWlan0Index = i;
1549                            wifi_num++;
1550                        } else if (name.equals("p2p0")) {
1551                            sP2p0Index = i;
1552                            wifi_num++;
1553                        }
1554                    }
1555                    return wifi_num;
1556                } else {
1557                    return sWifiIfaceHandles.length;
1558                }
1559            } else {
1560                return 0;
1561            }
1562        }
1563    }
1564
1565    private static native String getInterfaceNameNative(int index);
1566    public String getInterfaceName(int index) {
1567        synchronized (mLock) {
1568            return getInterfaceNameNative(index);
1569        }
1570    }
1571
1572    public static class ScanCapabilities {
1573        public int  max_scan_cache_size;                 // in number of scan results??
1574        public int  max_scan_buckets;
1575        public int  max_ap_cache_per_scan;
1576        public int  max_rssi_sample_size;
1577        public int  max_scan_reporting_threshold;        // in number of scan results??
1578        public int  max_hotlist_bssids;
1579        public int  max_significant_wifi_change_aps;
1580    }
1581
1582    public boolean getScanCapabilities(ScanCapabilities capabilities) {
1583        synchronized (mLock) {
1584            return isHalStarted() && getScanCapabilitiesNative(sWlan0Index, capabilities);
1585        }
1586    }
1587
1588    private static native boolean getScanCapabilitiesNative(
1589            int iface, ScanCapabilities capabilities);
1590
1591    private static native boolean startScanNative(int iface, int id, ScanSettings settings);
1592    private static native boolean stopScanNative(int iface, int id);
1593    private static native WifiScanner.ScanData[] getScanResultsNative(int iface, boolean flush);
1594    private static native WifiLinkLayerStats getWifiLinkLayerStatsNative(int iface);
1595    private static native void setWifiLinkLayerStatsNative(int iface, int enable);
1596
1597    public static class ChannelSettings {
1598        int frequency;
1599        int dwell_time_ms;
1600        boolean passive;
1601    }
1602
1603    public static class BucketSettings {
1604        int bucket;
1605        int band;
1606        int period_ms;
1607        int report_events;
1608        int num_channels;
1609        ChannelSettings channels[];
1610    }
1611
1612    public static class ScanSettings {
1613        int base_period_ms;
1614        int max_ap_per_scan;
1615        int report_threshold_percent;
1616        int report_threshold_num_scans;
1617        int num_buckets;
1618        BucketSettings buckets[];
1619    }
1620
1621    public static interface ScanEventHandler {
1622        void onScanResultsAvailable();
1623        void onFullScanResult(ScanResult fullScanResult);
1624        void onScanStatus();
1625        void onScanPaused(WifiScanner.ScanData[] data);
1626        void onScanRestarted();
1627    }
1628
1629    // Callback from native
1630    private static void onScanResultsAvailable(int id) {
1631        ScanEventHandler handler = sScanEventHandler;
1632        if (handler != null) {
1633            handler.onScanResultsAvailable();
1634        }
1635    }
1636
1637    /* scan status, keep these values in sync with gscan.h */
1638    private static int WIFI_SCAN_BUFFER_FULL = 0;
1639    private static int WIFI_SCAN_COMPLETE = 1;
1640
1641    // Callback from native
1642    private static void onScanStatus(int status) {
1643        ScanEventHandler handler = sScanEventHandler;
1644        if (status == WIFI_SCAN_BUFFER_FULL) {
1645            /* we have a separate event to take care of this */
1646        } else if (status == WIFI_SCAN_COMPLETE) {
1647            if (handler != null) {
1648                handler.onScanStatus();
1649            }
1650        }
1651    }
1652
1653    public static  WifiSsid createWifiSsid(byte[] rawSsid) {
1654        String ssidHexString = String.valueOf(HexEncoding.encode(rawSsid));
1655
1656        if (ssidHexString == null) {
1657            return null;
1658        }
1659
1660        WifiSsid wifiSsid = WifiSsid.createFromHex(ssidHexString);
1661
1662        return wifiSsid;
1663    }
1664
1665    public static String ssidConvert(byte[] rawSsid) {
1666        String ssid;
1667
1668        CharsetDecoder decoder = StandardCharsets.UTF_8.newDecoder();
1669        try {
1670            CharBuffer decoded = decoder.decode(ByteBuffer.wrap(rawSsid));
1671            ssid = decoded.toString();
1672        } catch (CharacterCodingException cce) {
1673            ssid = null;
1674        }
1675
1676        if (ssid == null) {
1677            ssid = new String(rawSsid, StandardCharsets.ISO_8859_1);
1678        }
1679
1680        return ssid;
1681    }
1682
1683    // Called from native
1684    public static boolean setSsid(byte[] rawSsid, ScanResult result) {
1685        if (rawSsid == null || rawSsid.length == 0 || result == null) {
1686            return false;
1687        }
1688
1689        result.SSID = ssidConvert(rawSsid);
1690        result.wifiSsid = createWifiSsid(rawSsid);
1691        return true;
1692    }
1693
1694    private static void populateScanResult(ScanResult result, byte bytes[], String dbg) {
1695        if (bytes == null) return;
1696        if (dbg == null) dbg = "";
1697
1698        InformationElementUtil.HtOperation htOperation = new InformationElementUtil.HtOperation();
1699        InformationElementUtil.VhtOperation vhtOperation =
1700                new InformationElementUtil.VhtOperation();
1701        InformationElementUtil.ExtendedCapabilities extendedCaps =
1702                new InformationElementUtil.ExtendedCapabilities();
1703
1704        ScanResult.InformationElement elements[] =
1705                InformationElementUtil.parseInformationElements(bytes);
1706        for (ScanResult.InformationElement ie : elements) {
1707            if(ie.id == ScanResult.InformationElement.EID_HT_OPERATION) {
1708                htOperation.from(ie);
1709            } else if(ie.id == ScanResult.InformationElement.EID_VHT_OPERATION) {
1710                vhtOperation.from(ie);
1711            } else if (ie.id == ScanResult.InformationElement.EID_EXTENDED_CAPS) {
1712                extendedCaps.from(ie);
1713            }
1714        }
1715
1716        if (extendedCaps.is80211McRTTResponder) {
1717            result.setFlag(ScanResult.FLAG_80211mc_RESPONDER);
1718        } else {
1719            result.clearFlag(ScanResult.FLAG_80211mc_RESPONDER);
1720        }
1721
1722        //handle RTT related information
1723        if (vhtOperation.isValid()) {
1724            result.channelWidth = vhtOperation.getChannelWidth();
1725            result.centerFreq0 = vhtOperation.getCenterFreq0();
1726            result.centerFreq1 = vhtOperation.getCenterFreq1();
1727        } else {
1728            result.channelWidth = htOperation.getChannelWidth();
1729            result.centerFreq0 = htOperation.getCenterFreq0(result.frequency);
1730            result.centerFreq1  = 0;
1731        }
1732        if(DBG) {
1733            Log.d(TAG, dbg + "SSID: " + result.SSID + " ChannelWidth is: " + result.channelWidth +
1734                    " PrimaryFreq: " + result.frequency +" mCenterfreq0: " + result.centerFreq0 +
1735                    " mCenterfreq1: " + result.centerFreq1 + (extendedCaps.is80211McRTTResponder ?
1736                    "Support RTT reponder: " : "Do not support RTT responder"));
1737        }
1738
1739        result.informationElements = elements;
1740    }
1741
1742    // Callback from native
1743    private static void onFullScanResult(int id, ScanResult result, byte bytes[]) {
1744        if (DBG) Log.i(TAG, "Got a full scan results event, ssid = " + result.SSID + ", " +
1745                "num = " + bytes.length);
1746
1747        ScanEventHandler handler = sScanEventHandler;
1748        if (handler != null) {
1749            populateScanResult(result, bytes, " onFullScanResult ");
1750            handler.onFullScanResult(result);
1751        }
1752    }
1753
1754    private static int sScanCmdId = 0;
1755    private static ScanEventHandler sScanEventHandler;
1756    private static ScanSettings sScanSettings;
1757
1758    public boolean startScan(ScanSettings settings, ScanEventHandler eventHandler) {
1759        synchronized (mLock) {
1760            if (isHalStarted()) {
1761                if (sScanCmdId != 0) {
1762                    stopScan();
1763                } else if (sScanSettings != null || sScanEventHandler != null) {
1764                /* current scan is paused; no need to stop it */
1765                }
1766
1767                sScanCmdId = getNewCmdIdLocked();
1768
1769                sScanSettings = settings;
1770                sScanEventHandler = eventHandler;
1771
1772                if (startScanNative(sWlan0Index, sScanCmdId, settings) == false) {
1773                    sScanEventHandler = null;
1774                    sScanSettings = null;
1775                    sScanCmdId = 0;
1776                    return false;
1777                }
1778
1779                return true;
1780            } else {
1781                return false;
1782            }
1783        }
1784    }
1785
1786    public void stopScan() {
1787        synchronized (mLock) {
1788            if (isHalStarted()) {
1789                if (sScanCmdId != 0) {
1790                    stopScanNative(sWlan0Index, sScanCmdId);
1791                }
1792                sScanSettings = null;
1793                sScanEventHandler = null;
1794                sScanCmdId = 0;
1795            }
1796        }
1797    }
1798
1799    public void pauseScan() {
1800        synchronized (mLock) {
1801            if (isHalStarted()) {
1802                if (sScanCmdId != 0 && sScanSettings != null && sScanEventHandler != null) {
1803                    Log.d(TAG, "Pausing scan");
1804                    WifiScanner.ScanData scanData[] = getScanResultsNative(sWlan0Index, true);
1805                    stopScanNative(sWlan0Index, sScanCmdId);
1806                    sScanCmdId = 0;
1807                    sScanEventHandler.onScanPaused(scanData);
1808                }
1809            }
1810        }
1811    }
1812
1813    public void restartScan() {
1814        synchronized (mLock) {
1815            if (isHalStarted()) {
1816                if (sScanCmdId == 0 && sScanSettings != null && sScanEventHandler != null) {
1817                    Log.d(TAG, "Restarting scan");
1818                    ScanEventHandler handler = sScanEventHandler;
1819                    ScanSettings settings = sScanSettings;
1820                    if (startScan(sScanSettings, sScanEventHandler)) {
1821                        sScanEventHandler.onScanRestarted();
1822                    } else {
1823                    /* we are still paused; don't change state */
1824                        sScanEventHandler = handler;
1825                        sScanSettings = settings;
1826                    }
1827                }
1828            }
1829        }
1830    }
1831
1832    public WifiScanner.ScanData[] getScanResults(boolean flush) {
1833        synchronized (mLock) {
1834            if (isHalStarted()) {
1835                return getScanResultsNative(sWlan0Index, flush);
1836            } else {
1837                return null;
1838            }
1839        }
1840    }
1841
1842    public static interface HotlistEventHandler {
1843        void onHotlistApFound (ScanResult[] result);
1844        void onHotlistApLost  (ScanResult[] result);
1845    }
1846
1847    private static int sHotlistCmdId = 0;
1848    private static HotlistEventHandler sHotlistEventHandler;
1849
1850    private native static boolean setHotlistNative(int iface, int id,
1851            WifiScanner.HotlistSettings settings);
1852    private native static boolean resetHotlistNative(int iface, int id);
1853
1854    public boolean setHotlist(WifiScanner.HotlistSettings settings,
1855            HotlistEventHandler eventHandler) {
1856        synchronized (mLock) {
1857            if (isHalStarted()) {
1858                if (sHotlistCmdId != 0) {
1859                    return false;
1860                } else {
1861                    sHotlistCmdId = getNewCmdIdLocked();
1862                }
1863
1864                sHotlistEventHandler = eventHandler;
1865                if (setHotlistNative(sWlan0Index, sHotlistCmdId, settings) == false) {
1866                    sHotlistEventHandler = null;
1867                    return false;
1868                }
1869
1870                return true;
1871            } else {
1872                return false;
1873            }
1874        }
1875    }
1876
1877    public void resetHotlist() {
1878        synchronized (mLock) {
1879            if (isHalStarted()) {
1880                if (sHotlistCmdId != 0) {
1881                    resetHotlistNative(sWlan0Index, sHotlistCmdId);
1882                    sHotlistCmdId = 0;
1883                    sHotlistEventHandler = null;
1884                }
1885            }
1886        }
1887    }
1888
1889    // Callback from native
1890    private static void onHotlistApFound(int id, ScanResult[] results) {
1891        HotlistEventHandler handler = sHotlistEventHandler;
1892        if (handler != null) {
1893            handler.onHotlistApFound(results);
1894        } else {
1895            /* this can happen because of race conditions */
1896            Log.d(TAG, "Ignoring hotlist AP found event");
1897        }
1898    }
1899
1900    // Callback from native
1901    private static void onHotlistApLost(int id, ScanResult[] results) {
1902        HotlistEventHandler handler = sHotlistEventHandler;
1903        if (handler != null) {
1904            handler.onHotlistApLost(results);
1905        } else {
1906            /* this can happen because of race conditions */
1907            Log.d(TAG, "Ignoring hotlist AP lost event");
1908        }
1909    }
1910
1911    public static interface SignificantWifiChangeEventHandler {
1912        void onChangesFound(ScanResult[] result);
1913    }
1914
1915    private static SignificantWifiChangeEventHandler sSignificantWifiChangeHandler;
1916    private static int sSignificantWifiChangeCmdId;
1917
1918    private static native boolean trackSignificantWifiChangeNative(
1919            int iface, int id, WifiScanner.WifiChangeSettings settings);
1920    private static native boolean untrackSignificantWifiChangeNative(int iface, int id);
1921
1922    public boolean trackSignificantWifiChange(
1923            WifiScanner.WifiChangeSettings settings, SignificantWifiChangeEventHandler handler) {
1924        synchronized (mLock) {
1925            if (isHalStarted()) {
1926                if (sSignificantWifiChangeCmdId != 0) {
1927                    return false;
1928                } else {
1929                    sSignificantWifiChangeCmdId = getNewCmdIdLocked();
1930                }
1931
1932                sSignificantWifiChangeHandler = handler;
1933                if (trackSignificantWifiChangeNative(sWlan0Index, sSignificantWifiChangeCmdId,
1934                        settings) == false) {
1935                    sSignificantWifiChangeHandler = null;
1936                    return false;
1937                }
1938
1939                return true;
1940            } else {
1941                return false;
1942            }
1943
1944        }
1945    }
1946
1947    public void untrackSignificantWifiChange() {
1948        synchronized (mLock) {
1949            if (isHalStarted()) {
1950                if (sSignificantWifiChangeCmdId != 0) {
1951                    untrackSignificantWifiChangeNative(sWlan0Index, sSignificantWifiChangeCmdId);
1952                    sSignificantWifiChangeCmdId = 0;
1953                    sSignificantWifiChangeHandler = null;
1954                }
1955            }
1956        }
1957    }
1958
1959    // Callback from native
1960    private static void onSignificantWifiChange(int id, ScanResult[] results) {
1961        SignificantWifiChangeEventHandler handler = sSignificantWifiChangeHandler;
1962        if (handler != null) {
1963            handler.onChangesFound(results);
1964        } else {
1965            /* this can happen because of race conditions */
1966            Log.d(TAG, "Ignoring significant wifi change");
1967        }
1968    }
1969
1970    public WifiLinkLayerStats getWifiLinkLayerStats(String iface) {
1971        // TODO: use correct iface name to Index translation
1972        if (iface == null) return null;
1973        synchronized (mLock) {
1974            if (isHalStarted()) {
1975                return getWifiLinkLayerStatsNative(sWlan0Index);
1976            } else {
1977                return null;
1978            }
1979        }
1980    }
1981
1982    public void setWifiLinkLayerStats(String iface, int enable) {
1983        if (iface == null) return;
1984        synchronized (mLock) {
1985            if (isHalStarted()) {
1986                setWifiLinkLayerStatsNative(sWlan0Index, enable);
1987            }
1988        }
1989    }
1990
1991    public static native int getSupportedFeatureSetNative(int iface);
1992    public int getSupportedFeatureSet() {
1993        synchronized (mLock) {
1994            if (isHalStarted()) {
1995                return getSupportedFeatureSetNative(sWlan0Index);
1996            } else {
1997                Log.d(TAG, "Failing getSupportedFeatureset because HAL isn't started");
1998                return 0;
1999            }
2000        }
2001    }
2002
2003    /* Rtt related commands/events */
2004    public static interface RttEventHandler {
2005        void onRttResults(RttManager.RttResult[] result);
2006    }
2007
2008    private static RttEventHandler sRttEventHandler;
2009    private static int sRttCmdId;
2010
2011    // Callback from native
2012    private static void onRttResults(int id, RttManager.RttResult[] results) {
2013        RttEventHandler handler = sRttEventHandler;
2014        if (handler != null && id == sRttCmdId) {
2015            Log.d(TAG, "Received " + results.length + " rtt results");
2016            handler.onRttResults(results);
2017            sRttCmdId = 0;
2018        } else {
2019            Log.d(TAG, "RTT Received event for unknown cmd = " + id +
2020                    ", current id = " + sRttCmdId);
2021        }
2022    }
2023
2024    private static native boolean requestRangeNative(
2025            int iface, int id, RttManager.RttParams[] params);
2026    private static native boolean cancelRangeRequestNative(
2027            int iface, int id, RttManager.RttParams[] params);
2028
2029    public boolean requestRtt(
2030            RttManager.RttParams[] params, RttEventHandler handler) {
2031        synchronized (mLock) {
2032            if (isHalStarted()) {
2033                if (sRttCmdId != 0) {
2034                    Log.v("TAG", "Last one is still under measurement!");
2035                    return false;
2036                } else {
2037                    sRttCmdId = getNewCmdIdLocked();
2038                }
2039                sRttEventHandler = handler;
2040                Log.v(TAG, "native issue RTT request");
2041                return requestRangeNative(sWlan0Index, sRttCmdId, params);
2042            } else {
2043                return false;
2044            }
2045        }
2046    }
2047
2048    public boolean cancelRtt(RttManager.RttParams[] params) {
2049        synchronized (mLock) {
2050            if (isHalStarted()) {
2051                if (sRttCmdId == 0) {
2052                    return false;
2053                }
2054
2055                sRttCmdId = 0;
2056
2057                if (cancelRangeRequestNative(sWlan0Index, sRttCmdId, params)) {
2058                    sRttEventHandler = null;
2059                    Log.v(TAG, "RTT cancel Request Successfully");
2060                    return true;
2061                } else {
2062                    Log.e(TAG, "RTT cancel Request failed");
2063                    return false;
2064                }
2065            } else {
2066                return false;
2067            }
2068        }
2069    }
2070
2071    private static native boolean setScanningMacOuiNative(int iface, byte[] oui);
2072
2073    public boolean setScanningMacOui(byte[] oui) {
2074        synchronized (mLock) {
2075            if (isHalStarted()) {
2076                return setScanningMacOuiNative(sWlan0Index, oui);
2077            } else {
2078                return false;
2079            }
2080        }
2081    }
2082
2083    private static native int[] getChannelsForBandNative(
2084            int iface, int band);
2085
2086    public int [] getChannelsForBand(int band) {
2087        synchronized (mLock) {
2088            if (isHalStarted()) {
2089                return getChannelsForBandNative(sWlan0Index, band);
2090            } else {
2091                return null;
2092            }
2093        }
2094    }
2095
2096    private static native boolean isGetChannelsForBandSupportedNative();
2097    public boolean isGetChannelsForBandSupported(){
2098        synchronized (mLock) {
2099            if (isHalStarted()) {
2100                return isGetChannelsForBandSupportedNative();
2101            } else {
2102                return false;
2103            }
2104        }
2105    }
2106
2107    private static native boolean setDfsFlagNative(int iface, boolean dfsOn);
2108    public boolean setDfsFlag(boolean dfsOn) {
2109        synchronized (mLock) {
2110            if (isHalStarted()) {
2111                return setDfsFlagNative(sWlan0Index, dfsOn);
2112            } else {
2113                return false;
2114            }
2115        }
2116    }
2117
2118    private static native boolean toggleInterfaceNative(int on);
2119    public boolean toggleInterface(int on) {
2120        synchronized (mLock) {
2121            if (isHalStarted()) {
2122                return toggleInterfaceNative(on);
2123            } else {
2124                return false;
2125            }
2126        }
2127    }
2128
2129    private static native RttManager.RttCapabilities getRttCapabilitiesNative(int iface);
2130    public RttManager.RttCapabilities getRttCapabilities() {
2131        synchronized (mLock) {
2132            if (isHalStarted()) {
2133                return getRttCapabilitiesNative(sWlan0Index);
2134            } else {
2135                return null;
2136            }
2137        }
2138    }
2139
2140    private static native boolean setCountryCodeHalNative(int iface, String CountryCode);
2141    public boolean setCountryCodeHal(String CountryCode) {
2142        synchronized (mLock) {
2143            if (isHalStarted()) {
2144                return setCountryCodeHalNative(sWlan0Index, CountryCode);
2145            } else {
2146                return false;
2147            }
2148        }
2149    }
2150
2151    /* Rtt related commands/events */
2152    public abstract class TdlsEventHandler {
2153        abstract public void onTdlsStatus(String macAddr, int status, int reason);
2154    }
2155
2156    private static TdlsEventHandler sTdlsEventHandler;
2157
2158    private static native boolean enableDisableTdlsNative(int iface, boolean enable,
2159            String macAddr);
2160    public boolean enableDisableTdls(boolean enable, String macAdd, TdlsEventHandler tdlsCallBack) {
2161        synchronized (mLock) {
2162            sTdlsEventHandler = tdlsCallBack;
2163            return enableDisableTdlsNative(sWlan0Index, enable, macAdd);
2164        }
2165    }
2166
2167    // Once TDLS per mac and event feature is implemented, this class definition should be
2168    // moved to the right place, like WifiManager etc
2169    public static class TdlsStatus {
2170        int channel;
2171        int global_operating_class;
2172        int state;
2173        int reason;
2174    }
2175    private static native TdlsStatus getTdlsStatusNative(int iface, String macAddr);
2176    public TdlsStatus getTdlsStatus(String macAdd) {
2177        synchronized (mLock) {
2178            if (isHalStarted()) {
2179                return getTdlsStatusNative(sWlan0Index, macAdd);
2180            } else {
2181                return null;
2182            }
2183        }
2184    }
2185
2186    //ToFix: Once TDLS per mac and event feature is implemented, this class definition should be
2187    // moved to the right place, like WifiStateMachine etc
2188    public static class TdlsCapabilities {
2189        /* Maximum TDLS session number can be supported by the Firmware and hardware */
2190        int maxConcurrentTdlsSessionNumber;
2191        boolean isGlobalTdlsSupported;
2192        boolean isPerMacTdlsSupported;
2193        boolean isOffChannelTdlsSupported;
2194    }
2195
2196
2197
2198    private static native TdlsCapabilities getTdlsCapabilitiesNative(int iface);
2199    public TdlsCapabilities getTdlsCapabilities () {
2200        synchronized (mLock) {
2201            if (isHalStarted()) {
2202                return getTdlsCapabilitiesNative(sWlan0Index);
2203            } else {
2204                return null;
2205            }
2206        }
2207    }
2208
2209    private static boolean onTdlsStatus(String macAddr, int status, int reason) {
2210        TdlsEventHandler handler = sTdlsEventHandler;
2211        if (handler == null) {
2212            return false;
2213        } else {
2214            handler.onTdlsStatus(macAddr, status, reason);
2215            return true;
2216        }
2217    }
2218
2219    //---------------------------------------------------------------------------------
2220
2221    /* Wifi Logger commands/events */
2222
2223    public static interface WifiLoggerEventHandler {
2224        void onRingBufferData(RingBufferStatus status, byte[] buffer);
2225        void onWifiAlert(int errorCode, byte[] buffer);
2226    }
2227
2228    private static WifiLoggerEventHandler sWifiLoggerEventHandler = null;
2229
2230    // Callback from native
2231    private static void onRingBufferData(RingBufferStatus status, byte[] buffer) {
2232        WifiLoggerEventHandler handler = sWifiLoggerEventHandler;
2233        if (handler != null)
2234            handler.onRingBufferData(status, buffer);
2235    }
2236
2237    // Callback from native
2238    private static void onWifiAlert(byte[] buffer, int errorCode) {
2239        WifiLoggerEventHandler handler = sWifiLoggerEventHandler;
2240        if (handler != null)
2241            handler.onWifiAlert(errorCode, buffer);
2242    }
2243
2244    private static int sLogCmdId = -1;
2245    private static native boolean setLoggingEventHandlerNative(int iface, int id);
2246    public boolean setLoggingEventHandler(WifiLoggerEventHandler handler) {
2247        synchronized (mLock) {
2248            if (isHalStarted()) {
2249                int oldId =  sLogCmdId;
2250                sLogCmdId = getNewCmdIdLocked();
2251                if (!setLoggingEventHandlerNative(sWlan0Index, sLogCmdId)) {
2252                    sLogCmdId = oldId;
2253                    return false;
2254                }
2255                sWifiLoggerEventHandler = handler;
2256                return true;
2257            } else {
2258                return false;
2259            }
2260        }
2261    }
2262
2263    private static native boolean startLoggingRingBufferNative(int iface, int verboseLevel,
2264            int flags, int minIntervalSec ,int minDataSize, String ringName);
2265    public boolean startLoggingRingBuffer(int verboseLevel, int flags, int maxInterval,
2266            int minDataSize, String ringName){
2267        synchronized (mLock) {
2268            if (isHalStarted()) {
2269                return startLoggingRingBufferNative(sWlan0Index, verboseLevel, flags, maxInterval,
2270                        minDataSize, ringName);
2271            } else {
2272                return false;
2273            }
2274        }
2275    }
2276
2277    private static native int getSupportedLoggerFeatureSetNative(int iface);
2278    public int getSupportedLoggerFeatureSet() {
2279        synchronized (mLock) {
2280            if (isHalStarted()) {
2281                return getSupportedLoggerFeatureSetNative(sWlan0Index);
2282            } else {
2283                return 0;
2284            }
2285        }
2286    }
2287
2288    private static native boolean resetLogHandlerNative(int iface, int id);
2289    public boolean resetLogHandler() {
2290        synchronized (mLock) {
2291            if (isHalStarted()) {
2292                if (sLogCmdId == -1) {
2293                    Log.e(TAG,"Can not reset handler Before set any handler");
2294                    return false;
2295                }
2296                sWifiLoggerEventHandler = null;
2297                if (resetLogHandlerNative(sWlan0Index, sLogCmdId)) {
2298                    sLogCmdId = -1;
2299                    return true;
2300                } else {
2301                    return false;
2302                }
2303            } else {
2304                return false;
2305            }
2306        }
2307    }
2308
2309    private static native String getDriverVersionNative(int iface);
2310    public String getDriverVersion() {
2311        synchronized (mLock) {
2312            if (isHalStarted()) {
2313                return getDriverVersionNative(sWlan0Index);
2314            } else {
2315                return "";
2316            }
2317        }
2318    }
2319
2320
2321    private static native String getFirmwareVersionNative(int iface);
2322    public String getFirmwareVersion() {
2323        synchronized (mLock) {
2324            if (isHalStarted()) {
2325                return getFirmwareVersionNative(sWlan0Index);
2326            } else {
2327                return "";
2328            }
2329        }
2330    }
2331
2332    public static class RingBufferStatus{
2333        String name;
2334        int flag;
2335        int ringBufferId;
2336        int ringBufferByteSize;
2337        int verboseLevel;
2338        int writtenBytes;
2339        int readBytes;
2340        int writtenRecords;
2341
2342        @Override
2343        public String toString() {
2344            return "name: " + name + " flag: " + flag + " ringBufferId: " + ringBufferId +
2345                    " ringBufferByteSize: " +ringBufferByteSize + " verboseLevel: " +verboseLevel +
2346                    " writtenBytes: " + writtenBytes + " readBytes: " + readBytes +
2347                    " writtenRecords: " + writtenRecords;
2348        }
2349    }
2350
2351    private static native RingBufferStatus[] getRingBufferStatusNative(int iface);
2352    public RingBufferStatus[] getRingBufferStatus() {
2353        synchronized (mLock) {
2354            if (isHalStarted()) {
2355                return getRingBufferStatusNative(sWlan0Index);
2356            } else {
2357                return null;
2358            }
2359        }
2360    }
2361
2362    private static native boolean getRingBufferDataNative(int iface, String ringName);
2363    public boolean getRingBufferData(String ringName) {
2364        synchronized (mLock) {
2365            if (isHalStarted()) {
2366                return getRingBufferDataNative(sWlan0Index, ringName);
2367            } else {
2368                return false;
2369            }
2370        }
2371    }
2372
2373    private static byte[] mFwMemoryDump;
2374    // Callback from native
2375    private static void onWifiFwMemoryAvailable(byte[] buffer) {
2376        mFwMemoryDump = buffer;
2377        if (DBG) {
2378            Log.d(TAG, "onWifiFwMemoryAvailable is called and buffer length is: " +
2379                    (buffer == null ? 0 :  buffer.length));
2380        }
2381    }
2382
2383    private static native boolean getFwMemoryDumpNative(int iface);
2384    public byte[] getFwMemoryDump() {
2385        synchronized (mLock) {
2386            if (isHalStarted()) {
2387                if(getFwMemoryDumpNative(sWlan0Index)) {
2388                    byte[] fwMemoryDump = mFwMemoryDump;
2389                    mFwMemoryDump = null;
2390                    return fwMemoryDump;
2391                } else {
2392                    return null;
2393                }
2394            }
2395            return null;
2396        }
2397    }
2398
2399    //---------------------------------------------------------------------------------
2400    /* Configure ePNO */
2401
2402    /* pno flags, keep these values in sync with gscan.h */
2403    private static int WIFI_PNO_AUTH_CODE_OPEN  = 1; // open
2404    private static int WIFI_PNO_AUTH_CODE_PSK   = 2; // WPA_PSK or WPA2PSK
2405    private static int WIFI_PNO_AUTH_CODE_EAPOL = 4; // any EAPOL
2406
2407    // Whether directed scan needs to be performed (for hidden SSIDs)
2408    private static int WIFI_PNO_FLAG_DIRECTED_SCAN = 1;
2409    // Whether PNO event shall be triggered if the network is found on A band
2410    private static int WIFI_PNO_FLAG_A_BAND = 2;
2411    // Whether PNO event shall be triggered if the network is found on G band
2412    private static int WIFI_PNO_FLAG_G_BAND = 4;
2413    // Whether strict matching is required (i.e. firmware shall not match on the entire SSID)
2414    private static int WIFI_PNO_FLAG_STRICT_MATCH = 8;
2415
2416    public static class WifiPnoNetwork {
2417        String SSID;
2418        int rssi_threshold;
2419        int flags;
2420        int auth;
2421        String configKey; // kept for reference
2422
2423        WifiPnoNetwork(WifiConfiguration config, int threshold) {
2424            if (config.SSID == null) {
2425                this.SSID = "";
2426                this.flags = WIFI_PNO_FLAG_DIRECTED_SCAN;
2427            } else {
2428                this.SSID = config.SSID;
2429            }
2430            this.rssi_threshold = threshold;
2431            if (config.allowedKeyManagement.get(WifiConfiguration.KeyMgmt.WPA_PSK)) {
2432                auth |= WIFI_PNO_AUTH_CODE_PSK;
2433            } else if (config.allowedKeyManagement.get(WifiConfiguration.KeyMgmt.WPA_EAP) ||
2434                    config.allowedKeyManagement.get(WifiConfiguration.KeyMgmt.IEEE8021X)) {
2435                auth |= WIFI_PNO_AUTH_CODE_EAPOL;
2436            } else if (config.wepKeys[0] != null) {
2437                auth |= WIFI_PNO_AUTH_CODE_OPEN;
2438            } else {
2439                auth |= WIFI_PNO_AUTH_CODE_OPEN;
2440            }
2441
2442            flags |= WIFI_PNO_FLAG_A_BAND | WIFI_PNO_FLAG_G_BAND;
2443            configKey = config.configKey();
2444        }
2445
2446        @Override
2447        public String toString() {
2448            StringBuilder sbuf = new StringBuilder();
2449            sbuf.append(this.SSID);
2450            sbuf.append(" flags=").append(this.flags);
2451            sbuf.append(" rssi=").append(this.rssi_threshold);
2452            sbuf.append(" auth=").append(this.auth);
2453            return sbuf.toString();
2454        }
2455    }
2456
2457    public static interface WifiPnoEventHandler {
2458        void onPnoNetworkFound(ScanResult results[]);
2459    }
2460
2461    private static WifiPnoEventHandler sWifiPnoEventHandler;
2462
2463    private static int sPnoCmdId = 0;
2464
2465    private native static boolean setPnoListNative(int iface, int id, WifiPnoNetwork list[]);
2466
2467    public boolean setPnoList(WifiPnoNetwork list[],
2468                                                  WifiPnoEventHandler eventHandler) {
2469        Log.e(TAG, "setPnoList cmd " + sPnoCmdId);
2470
2471        synchronized (mLock) {
2472            if (isHalStarted()) {
2473
2474                sPnoCmdId = getNewCmdIdLocked();
2475
2476                sWifiPnoEventHandler = eventHandler;
2477                if (setPnoListNative(sWlan0Index, sPnoCmdId, list)) {
2478                    return true;
2479                }
2480            }
2481
2482            sWifiPnoEventHandler = null;
2483            return false;
2484        }
2485    }
2486
2487    // Callback from native
2488    private static void onPnoNetworkFound(int id, ScanResult[] results) {
2489        if (results == null) {
2490            Log.e(TAG, "onPnoNetworkFound null results");
2491            return;
2492
2493        }
2494        Log.d(TAG, "WifiNative.onPnoNetworkFound result " + results.length);
2495
2496        WifiPnoEventHandler handler = sWifiPnoEventHandler;
2497        if (sPnoCmdId != 0 && handler != null) {
2498            for (int i=0; i<results.length; i++) {
2499                Log.e(TAG, "onPnoNetworkFound SSID " + results[i].SSID
2500                        + " " + results[i].level + " " + results[i].frequency);
2501
2502                populateScanResult(results[i], results[i].bytes, "onPnoNetworkFound ");
2503                results[i].wifiSsid = WifiSsid.createFromAsciiEncoded(results[i].SSID);
2504            }
2505
2506            handler.onPnoNetworkFound(results);
2507        } else {
2508            /* this can happen because of race conditions */
2509            Log.d(TAG, "Ignoring Pno Network found event");
2510        }
2511    }
2512
2513    public static class WifiLazyRoamParams {
2514        int A_band_boost_threshold;
2515        int A_band_penalty_threshold;
2516        int A_band_boost_factor;
2517        int A_band_penalty_factor;
2518        int A_band_max_boost;
2519        int lazy_roam_hysteresis;
2520        int alert_roam_rssi_trigger;
2521
2522        WifiLazyRoamParams() {
2523        }
2524
2525        @Override
2526        public String toString() {
2527            StringBuilder sbuf = new StringBuilder();
2528            sbuf.append(" A_band_boost_threshold=").append(this.A_band_boost_threshold);
2529            sbuf.append(" A_band_penalty_threshold=").append(this.A_band_penalty_threshold);
2530            sbuf.append(" A_band_boost_factor=").append(this.A_band_boost_factor);
2531            sbuf.append(" A_band_penalty_factor=").append(this.A_band_penalty_factor);
2532            sbuf.append(" A_band_max_boost=").append(this.A_band_max_boost);
2533            sbuf.append(" lazy_roam_hysteresis=").append(this.lazy_roam_hysteresis);
2534            sbuf.append(" alert_roam_rssi_trigger=").append(this.alert_roam_rssi_trigger);
2535            return sbuf.toString();
2536        }
2537    }
2538
2539    private native static boolean setLazyRoamNative(int iface, int id,
2540                                              boolean enabled, WifiLazyRoamParams param);
2541
2542    public boolean setLazyRoam(boolean enabled, WifiLazyRoamParams params) {
2543        synchronized (mLock) {
2544            if (isHalStarted()) {
2545                sPnoCmdId = getNewCmdIdLocked();
2546                return setLazyRoamNative(sWlan0Index, sPnoCmdId, enabled, params);
2547            } else {
2548                return false;
2549            }
2550        }
2551    }
2552
2553    private native static boolean setBssidBlacklistNative(int iface, int id,
2554                                              String list[]);
2555
2556    public boolean setBssidBlacklist(String list[]) {
2557        int size = 0;
2558        if (list != null) {
2559            size = list.length;
2560        }
2561        Log.e(TAG, "setBssidBlacklist cmd " + sPnoCmdId + " size " + size);
2562
2563        synchronized (mLock) {
2564            if (isHalStarted()) {
2565                sPnoCmdId = getNewCmdIdLocked();
2566                return setBssidBlacklistNative(sWlan0Index, sPnoCmdId, list);
2567            } else {
2568                return false;
2569            }
2570        }
2571    }
2572
2573    private native static boolean setSsidWhitelistNative(int iface, int id, String list[]);
2574
2575    public boolean setSsidWhitelist(String list[]) {
2576        int size = 0;
2577        if (list != null) {
2578            size = list.length;
2579        }
2580        Log.e(TAG, "setSsidWhitelist cmd " + sPnoCmdId + " size " + size);
2581
2582        synchronized (mLock) {
2583            if (isHalStarted()) {
2584                sPnoCmdId = getNewCmdIdLocked();
2585
2586                return setSsidWhitelistNative(sWlan0Index, sPnoCmdId, list);
2587            } else {
2588                return false;
2589            }
2590        }
2591    }
2592
2593    private native static int startSendingOffloadedPacketNative(int iface, int idx,
2594                                    byte[] srcMac, byte[] dstMac, byte[] pktData, int period);
2595
2596    public int
2597    startSendingOffloadedPacket(int slot, KeepalivePacketData keepAlivePacket, int period) {
2598        Log.d(TAG, "startSendingOffloadedPacket slot=" + slot + " period=" + period);
2599
2600        String[] macAddrStr = getMacAddress().split(":");
2601        byte[] srcMac = new byte[6];
2602        for(int i = 0; i < 6; i++) {
2603            Integer hexVal = Integer.parseInt(macAddrStr[i], 16);
2604            srcMac[i] = hexVal.byteValue();
2605        }
2606        synchronized (mLock) {
2607            if (isHalStarted()) {
2608                return startSendingOffloadedPacketNative(sWlan0Index, slot, srcMac,
2609                        keepAlivePacket.dstMac, keepAlivePacket.data, period);
2610            } else {
2611                return -1;
2612            }
2613        }
2614    }
2615
2616    private native static int stopSendingOffloadedPacketNative(int iface, int idx);
2617
2618    public int
2619    stopSendingOffloadedPacket(int slot) {
2620        Log.d(TAG, "stopSendingOffloadedPacket " + slot);
2621        synchronized (mLock) {
2622            if (isHalStarted()) {
2623                return stopSendingOffloadedPacketNative(sWlan0Index, slot);
2624            } else {
2625                return -1;
2626            }
2627        }
2628    }
2629
2630    public static interface WifiRssiEventHandler {
2631        void onRssiThresholdBreached(byte curRssi);
2632    }
2633
2634    private static WifiRssiEventHandler sWifiRssiEventHandler;
2635
2636    // Callback from native
2637    private static void onRssiThresholdBreached(int id, byte curRssi) {
2638        WifiRssiEventHandler handler = sWifiRssiEventHandler;
2639        if (handler != null) {
2640            handler.onRssiThresholdBreached(curRssi);
2641        }
2642    }
2643
2644    private native static int startRssiMonitoringNative(int iface, int id,
2645                                        byte maxRssi, byte minRssi);
2646
2647    private static int sRssiMonitorCmdId = 0;
2648
2649    public int startRssiMonitoring(byte maxRssi, byte minRssi,
2650                                                WifiRssiEventHandler rssiEventHandler) {
2651        Log.d(TAG, "startRssiMonitoring: maxRssi=" + maxRssi + " minRssi=" + minRssi);
2652        synchronized (mLock) {
2653            sWifiRssiEventHandler = rssiEventHandler;
2654            if (isHalStarted()) {
2655                if (sRssiMonitorCmdId != 0) {
2656                    stopRssiMonitoring();
2657                }
2658
2659                sRssiMonitorCmdId = getNewCmdIdLocked();
2660                Log.d(TAG, "sRssiMonitorCmdId = " + sRssiMonitorCmdId);
2661                int ret = startRssiMonitoringNative(sWlan0Index, sRssiMonitorCmdId,
2662                        maxRssi, minRssi);
2663                if (ret != 0) { // if not success
2664                    sRssiMonitorCmdId = 0;
2665                }
2666                return ret;
2667            } else {
2668                return -1;
2669            }
2670        }
2671    }
2672
2673    private native static int stopRssiMonitoringNative(int iface, int idx);
2674
2675    public int stopRssiMonitoring() {
2676        Log.d(TAG, "stopRssiMonitoring, cmdId " + sRssiMonitorCmdId);
2677        synchronized (mLock) {
2678            if (isHalStarted()) {
2679                int ret = 0;
2680                if (sRssiMonitorCmdId != 0) {
2681                    ret = stopRssiMonitoringNative(sWlan0Index, sRssiMonitorCmdId);
2682                }
2683                sRssiMonitorCmdId = 0;
2684                return ret;
2685            } else {
2686                return -1;
2687            }
2688        }
2689    }
2690}
2691