android_net_wifi_Wifi.cpp revision 33bf60a3fcab0489077314e845323de2d856af43
1/*
2 * Copyright 2008, The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 *     http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17#define LOG_TAG "wifi"
18
19#include "jni.h"
20#include <ScopedUtfChars.h>
21#include <utils/misc.h>
22#include <android_runtime/AndroidRuntime.h>
23#include <utils/Log.h>
24#include <utils/String16.h>
25
26#include "wifi.h"
27
28#define WIFI_PKG_NAME "android/net/wifi/WifiNative"
29#define BUF_SIZE 256
30
31namespace android {
32
33static jboolean sScanModeActive = false;
34
35static int doCommand(const char *cmd, char *replybuf, int replybuflen)
36{
37    size_t reply_len = replybuflen - 1;
38
39    if (::wifi_command(cmd, replybuf, &reply_len) != 0)
40        return -1;
41    else {
42        // Strip off trailing newline
43        if (reply_len > 0 && replybuf[reply_len-1] == '\n')
44            replybuf[reply_len-1] = '\0';
45        else
46            replybuf[reply_len] = '\0';
47        return 0;
48    }
49}
50
51static jint doIntCommand(const char* fmt, ...)
52{
53    char buf[BUF_SIZE];
54    va_list args;
55    va_start(args, fmt);
56    int byteCount = vsnprintf(buf, sizeof(buf), fmt, args);
57    va_end(args);
58    if (byteCount < 0 || byteCount >= BUF_SIZE) {
59        return -1;
60    }
61    char reply[BUF_SIZE];
62    if (doCommand(buf, reply, sizeof(reply)) != 0) {
63        return -1;
64    }
65    return static_cast<jint>(atoi(reply));
66}
67
68static jboolean doBooleanCommand(const char* expect, const char* fmt, ...)
69{
70    char buf[BUF_SIZE];
71    va_list args;
72    va_start(args, fmt);
73    int byteCount = vsnprintf(buf, sizeof(buf), fmt, args);
74    va_end(args);
75    if (byteCount < 0 || byteCount >= BUF_SIZE) {
76        return JNI_FALSE;
77    }
78    char reply[BUF_SIZE];
79    if (doCommand(buf, reply, sizeof(reply)) != 0) {
80        return JNI_FALSE;
81    }
82    return (strcmp(reply, expect) == 0);
83}
84
85// Send a command to the supplicant, and return the reply as a String
86static jstring doStringCommand(JNIEnv* env, const char* fmt, ...) {
87    char buf[BUF_SIZE];
88    va_list args;
89    va_start(args, fmt);
90    int byteCount = vsnprintf(buf, sizeof(buf), fmt, args);
91    va_end(args);
92    if (byteCount < 0 || byteCount >= BUF_SIZE) {
93        return NULL;
94    }
95    char reply[4096];
96    if (doCommand(buf, reply, sizeof(reply)) != 0) {
97        return NULL;
98    }
99    // TODO: why not just NewStringUTF?
100    String16 str((char *)reply);
101    return env->NewString((const jchar *)str.string(), str.size());
102}
103
104static jboolean android_net_wifi_isDriverLoaded(JNIEnv* env, jobject)
105{
106    return (jboolean)(::is_wifi_driver_loaded() == 1);
107}
108
109static jboolean android_net_wifi_loadDriver(JNIEnv* env, jobject)
110{
111    return (jboolean)(::wifi_load_driver() == 0);
112}
113
114static jboolean android_net_wifi_unloadDriver(JNIEnv* env, jobject)
115{
116    return (jboolean)(::wifi_unload_driver() == 0);
117}
118
119static jboolean android_net_wifi_startSupplicant(JNIEnv* env, jobject)
120{
121    return (jboolean)(::wifi_start_supplicant() == 0);
122}
123
124static jboolean android_net_wifi_stopSupplicant(JNIEnv* env, jobject)
125{
126    return doBooleanCommand("OK", "TERMINATE");
127}
128
129static jboolean android_net_wifi_killSupplicant(JNIEnv* env, jobject)
130{
131    return (jboolean)(::wifi_stop_supplicant() == 0);
132}
133
134static jboolean android_net_wifi_connectToSupplicant(JNIEnv* env, jobject)
135{
136    return (jboolean)(::wifi_connect_to_supplicant() == 0);
137}
138
139static void android_net_wifi_closeSupplicantConnection(JNIEnv* env, jobject)
140{
141    ::wifi_close_supplicant_connection();
142}
143
144static jstring android_net_wifi_waitForEvent(JNIEnv* env, jobject)
145{
146    char buf[BUF_SIZE];
147
148    int nread = ::wifi_wait_for_event(buf, sizeof buf);
149    if (nread > 0) {
150        return env->NewStringUTF(buf);
151    } else {
152        return NULL;
153    }
154}
155
156static jstring android_net_wifi_listNetworksCommand(JNIEnv* env, jobject)
157{
158    return doStringCommand(env, "LIST_NETWORKS");
159}
160
161static jint android_net_wifi_addNetworkCommand(JNIEnv* env, jobject)
162{
163    return doIntCommand("ADD_NETWORK");
164}
165
166static jboolean android_net_wifi_wpsPbcCommand(JNIEnv* env, jobject, jstring javaBssid)
167{
168    ScopedUtfChars bssid(env, javaBssid);
169    if (bssid.c_str() == NULL) {
170        return JNI_FALSE;
171    }
172    return doBooleanCommand("OK", "WPS_PBC %s", bssid.c_str());
173}
174
175static jboolean android_net_wifi_wpsPinFromAccessPointCommand(JNIEnv* env, jobject,
176        jstring javaBssid, jstring javaApPin)
177{
178    ScopedUtfChars bssid(env, javaBssid);
179    if (bssid.c_str() == NULL) {
180        return JNI_FALSE;
181    }
182    ScopedUtfChars apPin(env, javaApPin);
183    if (apPin.c_str() == NULL) {
184        return JNI_FALSE;
185    }
186    return doBooleanCommand("OK", "WPS_REG %s %s", bssid.c_str(), apPin.c_str());
187}
188
189static jstring android_net_wifi_wpsPinFromDeviceCommand(JNIEnv* env, jobject, jstring javaBssid)
190{
191    ScopedUtfChars bssid(env, javaBssid);
192    if (bssid.c_str() == NULL) {
193        return NULL;
194    }
195    return doStringCommand(env, "WPS_PIN %s", bssid.c_str());
196}
197
198static jboolean android_net_wifi_setCountryCodeCommand(JNIEnv* env, jobject, jstring javaCountry)
199{
200    ScopedUtfChars country(env, javaCountry);
201    if (country.c_str() == NULL) {
202        return JNI_FALSE;
203    }
204    return doBooleanCommand("OK", "DRIVER COUNTRY %s", country.c_str());
205}
206
207static jboolean android_net_wifi_setNetworkVariableCommand(JNIEnv* env,
208                                                           jobject,
209                                                           jint netId,
210                                                           jstring javaName,
211                                                           jstring javaValue)
212{
213    ScopedUtfChars name(env, javaName);
214    if (name.c_str() == NULL) {
215        return JNI_FALSE;
216    }
217    ScopedUtfChars value(env, javaValue);
218    if (value.c_str() == NULL) {
219        return JNI_FALSE;
220    }
221    return doBooleanCommand("OK", "SET_NETWORK %d %s %s", netId, name.c_str(), value.c_str());
222}
223
224static jstring android_net_wifi_getNetworkVariableCommand(JNIEnv* env,
225                                                          jobject,
226                                                          jint netId,
227                                                          jstring javaName)
228{
229    ScopedUtfChars name(env, javaName);
230    if (name.c_str() == NULL) {
231        return NULL;
232    }
233    return doStringCommand(env, "GET_NETWORK %d %s", netId, name.c_str());
234}
235
236static jboolean android_net_wifi_removeNetworkCommand(JNIEnv* env, jobject, jint netId)
237{
238    return doBooleanCommand("OK", "REMOVE_NETWORK %d", netId);
239}
240
241static jboolean android_net_wifi_enableNetworkCommand(JNIEnv* env,
242                                                  jobject,
243                                                  jint netId,
244                                                  jboolean disableOthers)
245{
246    return doBooleanCommand("OK", "%s_NETWORK %d", disableOthers ? "SELECT" : "ENABLE", netId);
247}
248
249static jboolean android_net_wifi_disableNetworkCommand(JNIEnv* env, jobject, jint netId)
250{
251    return doBooleanCommand("OK", "DISABLE_NETWORK %d", netId);
252}
253
254static jstring android_net_wifi_statusCommand(JNIEnv* env, jobject)
255{
256    return doStringCommand(env, "STATUS");
257}
258
259static jboolean android_net_wifi_pingCommand(JNIEnv* env, jobject)
260{
261    return doBooleanCommand("PONG", "PING");
262}
263
264static jstring android_net_wifi_scanResultsCommand(JNIEnv* env, jobject)
265{
266    return doStringCommand(env, "SCAN_RESULTS");
267}
268
269static jboolean android_net_wifi_disconnectCommand(JNIEnv* env, jobject)
270{
271    return doBooleanCommand("OK", "DISCONNECT");
272}
273
274static jboolean android_net_wifi_reconnectCommand(JNIEnv* env, jobject)
275{
276    return doBooleanCommand("OK", "RECONNECT");
277}
278static jboolean android_net_wifi_reassociateCommand(JNIEnv* env, jobject)
279{
280    return doBooleanCommand("OK", "REASSOCIATE");
281}
282
283static jboolean doSetScanMode(jboolean setActive)
284{
285    return doBooleanCommand("OK", (setActive ? "DRIVER SCAN-ACTIVE" : "DRIVER SCAN-PASSIVE"));
286}
287
288static jboolean android_net_wifi_scanCommand(JNIEnv* env, jobject, jboolean forceActive)
289{
290    jboolean result;
291
292    // Ignore any error from setting the scan mode.
293    // The scan will still work.
294    if (forceActive && !sScanModeActive)
295        doSetScanMode(true);
296    result = doBooleanCommand("OK", "SCAN");
297    if (forceActive && !sScanModeActive)
298        doSetScanMode(sScanModeActive);
299    return result;
300}
301
302static jboolean android_net_wifi_setScanModeCommand(JNIEnv* env, jobject, jboolean setActive)
303{
304    sScanModeActive = setActive;
305    return doSetScanMode(setActive);
306}
307
308static jboolean android_net_wifi_startDriverCommand(JNIEnv* env, jobject)
309{
310    return doBooleanCommand("OK", "DRIVER START");
311}
312
313static jboolean android_net_wifi_stopDriverCommand(JNIEnv* env, jobject)
314{
315    return doBooleanCommand("OK", "DRIVER STOP");
316}
317
318static jboolean android_net_wifi_startPacketFiltering(JNIEnv* env, jobject)
319{
320    return doBooleanCommand("OK", "DRIVER RXFILTER-ADD 0")
321            && doBooleanCommand("OK", "DRIVER RXFILTER-ADD 1")
322            && doBooleanCommand("OK", "DRIVER RXFILTER-ADD 3")
323            && doBooleanCommand("OK", "DRIVER RXFILTER-START");
324}
325
326static jboolean android_net_wifi_stopPacketFiltering(JNIEnv* env, jobject)
327{
328    jboolean result = doBooleanCommand("OK", "DRIVER RXFILTER-STOP");
329    if (result) {
330        (void)doBooleanCommand("OK", "DRIVER RXFILTER-REMOVE 3");
331        (void)doBooleanCommand("OK", "DRIVER RXFILTER-REMOVE 1");
332        (void)doBooleanCommand("OK", "DRIVER RXFILTER-REMOVE 0");
333    }
334
335    return result;
336}
337
338static jint android_net_wifi_getRssiHelper(const char *cmd)
339{
340    char reply[BUF_SIZE];
341    int rssi = -200;
342
343    if (doCommand(cmd, reply, sizeof(reply)) != 0) {
344        return (jint)-1;
345    }
346
347    // reply comes back in the form "<SSID> rssi XX" where XX is the
348    // number we're interested in.  if we're associating, it returns "OK".
349    // beware - <SSID> can contain spaces.
350    if (strcmp(reply, "OK") != 0) {
351        // beware of trailing spaces
352        char* end = reply + strlen(reply);
353        while (end > reply && end[-1] == ' ') {
354            end--;
355        }
356        *end = 0;
357
358        char* lastSpace = strrchr(reply, ' ');
359        // lastSpace should be preceded by "rssi" and followed by the value
360        if (lastSpace && !strncasecmp(lastSpace - 4, "rssi", 4)) {
361            sscanf(lastSpace + 1, "%d", &rssi);
362        }
363    }
364    return (jint)rssi;
365}
366
367static jint android_net_wifi_getRssiCommand(JNIEnv* env, jobject)
368{
369    return android_net_wifi_getRssiHelper("DRIVER RSSI");
370}
371
372static jint android_net_wifi_getRssiApproxCommand(JNIEnv* env, jobject)
373{
374    return android_net_wifi_getRssiHelper("DRIVER RSSI-APPROX");
375}
376
377static jint android_net_wifi_getLinkSpeedCommand(JNIEnv* env, jobject)
378{
379    char reply[BUF_SIZE];
380    int linkspeed;
381
382    if (doCommand("DRIVER LINKSPEED", reply, sizeof(reply)) != 0) {
383        return (jint)-1;
384    }
385    // reply comes back in the form "LinkSpeed XX" where XX is the
386    // number we're interested in.
387    sscanf(reply, "%*s %u", &linkspeed);
388    return (jint)linkspeed;
389}
390
391static jstring android_net_wifi_getMacAddressCommand(JNIEnv* env, jobject)
392{
393    char reply[BUF_SIZE];
394    char buf[BUF_SIZE];
395
396    if (doCommand("DRIVER MACADDR", reply, sizeof(reply)) != 0) {
397        return NULL;
398    }
399    // reply comes back in the form "Macaddr = XX.XX.XX.XX.XX.XX" where XX
400    // is the part of the string we're interested in.
401    if (sscanf(reply, "%*s = %255s", buf) == 1) {
402        return env->NewStringUTF(buf);
403    }
404    return NULL;
405}
406
407static jboolean android_net_wifi_setPowerModeCommand(JNIEnv* env, jobject, jint mode)
408{
409    return doBooleanCommand("OK", "DRIVER POWERMODE %d", mode);
410}
411
412static jint android_net_wifi_getPowerModeCommand(JNIEnv* env, jobject)
413{
414    char reply[BUF_SIZE];
415    int power;
416
417    if (doCommand("DRIVER GETPOWER", reply, sizeof(reply)) != 0) {
418        return (jint)-1;
419    }
420    // reply comes back in the form "powermode = XX" where XX is the
421    // number we're interested in.
422    if (sscanf(reply, "%*s = %u", &power) != 1) {
423        return (jint)-1;
424    }
425    return (jint)power;
426}
427
428static jboolean android_net_wifi_setBandCommand(JNIEnv* env, jobject, jint band)
429{
430    return doBooleanCommand("OK", "DRIVER SETBAND %d", band);
431}
432
433static jint android_net_wifi_getBandCommand(JNIEnv* env, jobject)
434{
435    char reply[25];
436    int band;
437
438    if (doCommand("DRIVER GETBAND", reply, sizeof(reply)) != 0) {
439        return (jint)-1;
440    }
441    // reply comes back in the form "Band X" where X is the
442    // number we're interested in.
443    sscanf(reply, "%*s %u", &band);
444    return (jint)band;
445}
446
447static jboolean android_net_wifi_setBluetoothCoexistenceModeCommand(JNIEnv* env, jobject, jint mode)
448{
449    return doBooleanCommand("OK", "DRIVER BTCOEXMODE %d", mode);
450}
451
452static jboolean android_net_wifi_setBluetoothCoexistenceScanModeCommand(JNIEnv* env, jobject, jboolean setCoexScanMode)
453{
454    return doBooleanCommand("OK", "DRIVER BTCOEXSCAN-%s", setCoexScanMode ? "START" : "STOP");
455}
456
457static jboolean android_net_wifi_saveConfigCommand(JNIEnv* env, jobject)
458{
459    // Make sure we never write out a value for AP_SCAN other than 1
460    (void)doBooleanCommand("OK", "AP_SCAN 1");
461    return doBooleanCommand("OK", "SAVE_CONFIG");
462}
463
464static jboolean android_net_wifi_reloadConfigCommand(JNIEnv* env, jobject)
465{
466    return doBooleanCommand("OK", "RECONFIGURE");
467}
468
469static jboolean android_net_wifi_setScanResultHandlingCommand(JNIEnv* env, jobject, jint mode)
470{
471    return doBooleanCommand("OK", "AP_SCAN %d", mode);
472}
473
474static jboolean android_net_wifi_addToBlacklistCommand(JNIEnv* env, jobject, jstring javaBssid)
475{
476    ScopedUtfChars bssid(env, javaBssid);
477    if (bssid.c_str() == NULL) {
478        return JNI_FALSE;
479    }
480    return doBooleanCommand("OK", "BLACKLIST %s", bssid.c_str());
481}
482
483static jboolean android_net_wifi_clearBlacklistCommand(JNIEnv* env, jobject)
484{
485    return doBooleanCommand("OK", "BLACKLIST clear");
486}
487
488static jboolean android_net_wifi_setSuspendOptimizationsCommand(JNIEnv* env, jobject, jboolean enabled)
489{
490    return doBooleanCommand("OK", "DRIVER SETSUSPENDOPT %d", enabled ? 0 : 1);
491}
492
493static void android_net_wifi_enableBackgroundScanCommand(JNIEnv* env, jobject, jboolean enable)
494{
495    //Note: BGSCAN-START and BGSCAN-STOP are documented in core/res/res/values/config.xml
496    //and will need an update if the names are changed
497    if (enable) {
498        doBooleanCommand("OK", "DRIVER BGSCAN-START");
499    } else {
500        doBooleanCommand("OK", "DRIVER BGSCAN-STOP");
501    }
502}
503
504static void android_net_wifi_setScanIntervalCommand(JNIEnv* env, jobject, jint scanInterval)
505{
506    doBooleanCommand("OK", "SCAN_INTERVAL %d", scanInterval);
507}
508
509
510// ----------------------------------------------------------------------------
511
512/*
513 * JNI registration.
514 */
515static JNINativeMethod gWifiMethods[] = {
516    /* name, signature, funcPtr */
517
518    { "loadDriver", "()Z",  (void *)android_net_wifi_loadDriver },
519    { "isDriverLoaded", "()Z",  (void *)android_net_wifi_isDriverLoaded},
520    { "unloadDriver", "()Z",  (void *)android_net_wifi_unloadDriver },
521    { "startSupplicant", "()Z",  (void *)android_net_wifi_startSupplicant },
522    { "stopSupplicant", "()Z", (void*) android_net_wifi_stopSupplicant },
523    { "killSupplicant", "()Z",  (void *)android_net_wifi_killSupplicant },
524    { "connectToSupplicant", "()Z",  (void *)android_net_wifi_connectToSupplicant },
525    { "closeSupplicantConnection", "()V",  (void *)android_net_wifi_closeSupplicantConnection },
526
527    { "listNetworksCommand", "()Ljava/lang/String;",
528        (void*) android_net_wifi_listNetworksCommand },
529    { "addNetworkCommand", "()I", (void*) android_net_wifi_addNetworkCommand },
530    { "setNetworkVariableCommand", "(ILjava/lang/String;Ljava/lang/String;)Z",
531        (void*) android_net_wifi_setNetworkVariableCommand },
532    { "getNetworkVariableCommand", "(ILjava/lang/String;)Ljava/lang/String;",
533        (void*) android_net_wifi_getNetworkVariableCommand },
534    { "removeNetworkCommand", "(I)Z", (void*) android_net_wifi_removeNetworkCommand },
535    { "enableNetworkCommand", "(IZ)Z", (void*) android_net_wifi_enableNetworkCommand },
536    { "disableNetworkCommand", "(I)Z", (void*) android_net_wifi_disableNetworkCommand },
537    { "waitForEvent", "()Ljava/lang/String;", (void*) android_net_wifi_waitForEvent },
538    { "statusCommand", "()Ljava/lang/String;", (void*) android_net_wifi_statusCommand },
539    { "scanResultsCommand", "()Ljava/lang/String;", (void*) android_net_wifi_scanResultsCommand },
540    { "pingCommand", "()Z",  (void *)android_net_wifi_pingCommand },
541    { "disconnectCommand", "()Z",  (void *)android_net_wifi_disconnectCommand },
542    { "reconnectCommand", "()Z",  (void *)android_net_wifi_reconnectCommand },
543    { "reassociateCommand", "()Z",  (void *)android_net_wifi_reassociateCommand },
544    { "scanCommand", "(Z)Z", (void*) android_net_wifi_scanCommand },
545    { "setScanModeCommand", "(Z)Z", (void*) android_net_wifi_setScanModeCommand },
546    { "startDriverCommand", "()Z", (void*) android_net_wifi_startDriverCommand },
547    { "stopDriverCommand", "()Z", (void*) android_net_wifi_stopDriverCommand },
548    { "startPacketFiltering", "()Z", (void*) android_net_wifi_startPacketFiltering },
549    { "stopPacketFiltering", "()Z", (void*) android_net_wifi_stopPacketFiltering },
550    { "setPowerModeCommand", "(I)Z", (void*) android_net_wifi_setPowerModeCommand },
551    { "getPowerModeCommand", "()I", (void*) android_net_wifi_getPowerModeCommand },
552    { "setBandCommand", "(I)Z", (void*) android_net_wifi_setBandCommand},
553    { "getBandCommand", "()I", (void*) android_net_wifi_getBandCommand},
554    { "setBluetoothCoexistenceModeCommand", "(I)Z",
555    		(void*) android_net_wifi_setBluetoothCoexistenceModeCommand },
556    { "setBluetoothCoexistenceScanModeCommand", "(Z)Z",
557    		(void*) android_net_wifi_setBluetoothCoexistenceScanModeCommand },
558    { "getRssiCommand", "()I", (void*) android_net_wifi_getRssiCommand },
559    { "getRssiApproxCommand", "()I",
560            (void*) android_net_wifi_getRssiApproxCommand},
561    { "getLinkSpeedCommand", "()I", (void*) android_net_wifi_getLinkSpeedCommand },
562    { "getMacAddressCommand", "()Ljava/lang/String;", (void*) android_net_wifi_getMacAddressCommand },
563    { "saveConfigCommand", "()Z", (void*) android_net_wifi_saveConfigCommand },
564    { "reloadConfigCommand", "()Z", (void*) android_net_wifi_reloadConfigCommand },
565    { "setScanResultHandlingCommand", "(I)Z", (void*) android_net_wifi_setScanResultHandlingCommand },
566    { "addToBlacklistCommand", "(Ljava/lang/String;)Z", (void*) android_net_wifi_addToBlacklistCommand },
567    { "clearBlacklistCommand", "()Z", (void*) android_net_wifi_clearBlacklistCommand },
568    { "startWpsPbcCommand", "(Ljava/lang/String;)Z", (void*) android_net_wifi_wpsPbcCommand },
569    { "startWpsWithPinFromAccessPointCommand", "(Ljava/lang/String;Ljava/lang/String;)Z",
570        (void*) android_net_wifi_wpsPinFromAccessPointCommand },
571    { "startWpsWithPinFromDeviceCommand", "(Ljava/lang/String;)Ljava/lang/String;",
572        (void*) android_net_wifi_wpsPinFromDeviceCommand },
573    { "setSuspendOptimizationsCommand", "(Z)Z",
574        (void*) android_net_wifi_setSuspendOptimizationsCommand},
575    { "setCountryCodeCommand", "(Ljava/lang/String;)Z",
576        (void*) android_net_wifi_setCountryCodeCommand},
577    { "enableBackgroundScanCommand", "(Z)V", (void*) android_net_wifi_enableBackgroundScanCommand},
578    { "setScanIntervalCommand", "(I)V", (void*) android_net_wifi_setScanIntervalCommand},
579};
580
581int register_android_net_wifi_WifiManager(JNIEnv* env)
582{
583    return AndroidRuntime::registerNativeMethods(env,
584            WIFI_PKG_NAME, gWifiMethods, NELEM(gWifiMethods));
585}
586
587}; // namespace android
588