android_net_wifi_Wifi.cpp revision f013e1afd1e68af5e3b868c26a653bbfb39538f8
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 <utils/misc.h>
21#include <android_runtime/AndroidRuntime.h>
22#include <utils/Log.h>
23
24#include "wifi.h"
25
26#define WIFI_PKG_NAME "android/net/wifi/WifiNative"
27
28namespace android {
29
30/*
31 * The following remembers the jfieldID's of the fields
32 * of the DhcpInfo Java object, so that we don't have
33 * to look them up every time.
34 */
35static struct fieldIds {
36    jclass dhcpInfoClass;
37    jmethodID constructorId;
38    jfieldID ipaddress;
39    jfieldID gateway;
40    jfieldID netmask;
41    jfieldID dns1;
42    jfieldID dns2;
43    jfieldID serverAddress;
44    jfieldID leaseDuration;
45} dhcpInfoFieldIds;
46
47static int doCommand(const char *cmd, char *replybuf, int replybuflen)
48{
49    size_t reply_len = replybuflen - 1;
50
51    if (::wifi_command(cmd, replybuf, &reply_len) != 0)
52        return -1;
53    else {
54        // Strip off trailing newline
55        if (reply_len > 0 && replybuf[reply_len-1] == '\n')
56            replybuf[reply_len-1] = '\0';
57        else
58            replybuf[reply_len] = '\0';
59        return 0;
60    }
61}
62
63static jint doIntCommand(const char *cmd)
64{
65    char reply[256];
66
67    if (doCommand(cmd, reply, sizeof(reply)) != 0) {
68        return (jint)-1;
69    } else {
70        return (jint)atoi(reply);
71    }
72}
73
74static jboolean doBooleanCommand(const char *cmd, const char *expect)
75{
76    char reply[256];
77
78    if (doCommand(cmd, reply, sizeof(reply)) != 0) {
79        return (jboolean)JNI_FALSE;
80    } else {
81        return (jboolean)(strcmp(reply, expect) == 0);
82    }
83}
84
85// Send a command to the supplicant, and return the reply as a String
86static jstring doStringCommand(JNIEnv *env, const char *cmd)
87{
88    char reply[4096];
89
90    if (doCommand(cmd, reply, sizeof(reply)) != 0) {
91        return env->NewStringUTF(NULL);
92    } else {
93        return env->NewStringUTF(reply);
94    }
95}
96
97static jboolean android_net_wifi_loadDriver(JNIEnv* env, jobject clazz)
98{
99    return (jboolean)(::wifi_load_driver() == 0);
100}
101
102static jboolean android_net_wifi_unloadDriver(JNIEnv* env, jobject clazz)
103{
104    return (jboolean)(::wifi_unload_driver() == 0);
105}
106
107static jboolean android_net_wifi_startSupplicant(JNIEnv* env, jobject clazz)
108{
109    return (jboolean)(::wifi_start_supplicant() == 0);
110}
111
112static jboolean android_net_wifi_stopSupplicant(JNIEnv* env, jobject clazz)
113{
114    return (jboolean)(::wifi_stop_supplicant() == 0);
115}
116
117static jboolean android_net_wifi_connectToSupplicant(JNIEnv* env, jobject clazz)
118{
119    return (jboolean)(::wifi_connect_to_supplicant() == 0);
120}
121
122static void android_net_wifi_closeSupplicantConnection(JNIEnv* env, jobject clazz)
123{
124    ::wifi_close_supplicant_connection();
125}
126
127static jstring android_net_wifi_waitForEvent(JNIEnv* env, jobject clazz)
128{
129    char buf[256];
130
131    int nread = ::wifi_wait_for_event(buf, sizeof buf);
132    if (nread > 0) {
133        return env->NewStringUTF(buf);
134    } else {
135        return  env->NewStringUTF(NULL);
136    }
137}
138
139static jstring android_net_wifi_listNetworksCommand(JNIEnv* env, jobject clazz)
140{
141    return doStringCommand(env, "LIST_NETWORKS");
142}
143
144static jint android_net_wifi_addNetworkCommand(JNIEnv* env, jobject clazz)
145{
146    return doIntCommand("ADD_NETWORK");
147}
148
149static jboolean android_net_wifi_setNetworkVariableCommand(JNIEnv* env,
150                                                           jobject clazz,
151                                                           jint netId,
152                                                           jstring name,
153                                                           jstring value)
154{
155    char cmdstr[256];
156    jboolean isCopy;
157
158    const char *nameStr = env->GetStringUTFChars(name, &isCopy);
159    const char *valueStr = env->GetStringUTFChars(value, &isCopy);
160
161    if (nameStr == NULL || valueStr == NULL)
162        return JNI_FALSE;
163
164    int cmdTooLong = snprintf(cmdstr, sizeof(cmdstr), "SET_NETWORK %d %s %s",
165                 netId, nameStr, valueStr) >= (int)sizeof(cmdstr);
166
167    env->ReleaseStringUTFChars(name, nameStr);
168    env->ReleaseStringUTFChars(value, valueStr);
169
170    return (jboolean)!cmdTooLong && doBooleanCommand(cmdstr, "OK");
171}
172
173static jstring android_net_wifi_getNetworkVariableCommand(JNIEnv* env,
174                                                          jobject clazz,
175                                                          jint netId,
176                                                          jstring name)
177{
178    char cmdstr[256];
179    jboolean isCopy;
180
181    const char *nameStr = env->GetStringUTFChars(name, &isCopy);
182
183    if (nameStr == NULL)
184        return env->NewStringUTF(NULL);
185
186    int cmdTooLong = snprintf(cmdstr, sizeof(cmdstr), "GET_NETWORK %d %s",
187                             netId, nameStr) >= (int)sizeof(cmdstr);
188
189    env->ReleaseStringUTFChars(name, nameStr);
190
191    return cmdTooLong ? env->NewStringUTF(NULL) : doStringCommand(env, cmdstr);
192}
193
194static jboolean android_net_wifi_removeNetworkCommand(JNIEnv* env, jobject clazz, jint netId)
195{
196    char cmdstr[256];
197
198    int numWritten = snprintf(cmdstr, sizeof(cmdstr), "REMOVE_NETWORK %d", netId);
199    int cmdTooLong = numWritten >= (int)sizeof(cmdstr);
200
201    return (jboolean)!cmdTooLong && doBooleanCommand(cmdstr, "OK");
202}
203
204static jboolean android_net_wifi_enableNetworkCommand(JNIEnv* env,
205                                                  jobject clazz,
206                                                  jint netId,
207                                                  jboolean disableOthers)
208{
209    char cmdstr[256];
210    const char *cmd = disableOthers ? "SELECT_NETWORK" : "ENABLE_NETWORK";
211
212    int numWritten = snprintf(cmdstr, sizeof(cmdstr), "%s %d", cmd, netId);
213    int cmdTooLong = numWritten >= (int)sizeof(cmdstr);
214
215    return (jboolean)!cmdTooLong && doBooleanCommand(cmdstr, "OK");
216}
217
218static jboolean android_net_wifi_disableNetworkCommand(JNIEnv* env, jobject clazz, jint netId)
219{
220    char cmdstr[256];
221
222    int numWritten = snprintf(cmdstr, sizeof(cmdstr), "DISABLE_NETWORK %d", netId);
223    int cmdTooLong = numWritten >= (int)sizeof(cmdstr);
224
225    return (jboolean)!cmdTooLong && doBooleanCommand(cmdstr, "OK");
226}
227
228static jstring android_net_wifi_statusCommand(JNIEnv* env, jobject clazz)
229{
230    return doStringCommand(env, "STATUS");
231}
232
233static jboolean android_net_wifi_pingCommand(JNIEnv* env, jobject clazz)
234{
235    return doBooleanCommand("PING", "PONG");
236}
237
238static jstring android_net_wifi_scanResultsCommand(JNIEnv* env, jobject clazz)
239{
240    return doStringCommand(env, "SCAN_RESULTS");
241}
242
243static jboolean android_net_wifi_disconnectCommand(JNIEnv* env, jobject clazz)
244{
245    return doBooleanCommand("DISCONNECT", "OK");
246}
247
248static jboolean android_net_wifi_reconnectCommand(JNIEnv* env, jobject clazz)
249{
250    return doBooleanCommand("RECONNECT", "OK");
251}
252static jboolean android_net_wifi_reassociateCommand(JNIEnv* env, jobject clazz)
253{
254    return doBooleanCommand("REASSOCIATE", "OK");
255}
256
257static jboolean android_net_wifi_scanCommand(JNIEnv* env, jobject clazz)
258{
259    jboolean result;
260    // Ignore any error from setting the scan mode.
261    // The scan will still work.
262    (void)doBooleanCommand("DRIVER SCAN-ACTIVE", "OK");
263    result = doBooleanCommand("SCAN", "OK");
264    (void)doBooleanCommand("DRIVER SCAN-PASSIVE", "OK");
265    return result;
266}
267
268static jboolean android_net_wifi_setScanModeCommand(JNIEnv* env, jobject clazz, jboolean setActive)
269{
270    jboolean result;
271    // Ignore any error from setting the scan mode.
272    // The scan will still work.
273    if (setActive) {
274        return doBooleanCommand("DRIVER SCAN-ACTIVE", "OK");
275    } else {
276        return doBooleanCommand("DRIVER SCAN-PASSIVE", "OK");
277    }
278}
279
280static jboolean android_net_wifi_startDriverCommand(JNIEnv* env, jobject clazz)
281{
282    return doBooleanCommand("DRIVER START", "OK");
283}
284
285static jboolean android_net_wifi_stopDriverCommand(JNIEnv* env, jobject clazz)
286{
287    return doBooleanCommand("DRIVER STOP", "OK");
288}
289
290static jint android_net_wifi_getRssiCommand(JNIEnv* env, jobject clazz)
291{
292    char reply[256];
293    int rssi = -200;
294
295    if (doCommand("DRIVER RSSI", reply, sizeof(reply)) != 0) {
296        return (jint)-1;
297    }
298    // reply comes back in the form "<SSID> rssi XX" where XX is the
299    // number we're interested in.  if we're associating, it returns "OK".
300    if (strcmp(reply, "OK") != 0) {
301    	sscanf(reply, "%*s %*s %d", &rssi);
302    }
303    return (jint)rssi;
304}
305
306static jint android_net_wifi_getLinkSpeedCommand(JNIEnv* env, jobject clazz)
307{
308    char reply[256];
309    int linkspeed;
310
311    if (doCommand("DRIVER LINKSPEED", reply, sizeof(reply)) != 0) {
312        return (jint)-1;
313    }
314    // reply comes back in the form "LinkSpeed XX" where XX is the
315    // number we're interested in.
316    sscanf(reply, "%*s %u", &linkspeed);
317    return (jint)linkspeed;
318}
319
320static jstring android_net_wifi_getMacAddressCommand(JNIEnv* env, jobject clazz)
321{
322    char reply[256];
323    char buf[256];
324
325    if (doCommand("DRIVER MACADDR", reply, sizeof(reply)) != 0) {
326        return env->NewStringUTF(NULL);
327    }
328    // reply comes back in the form "Macaddr = XX.XX.XX.XX.XX.XX" where XX
329    // is the part of the string we're interested in.
330    if (sscanf(reply, "%*s = %255s", buf) == 1)
331        return env->NewStringUTF(buf);
332    else
333        return env->NewStringUTF(NULL);
334}
335
336static jboolean android_net_wifi_setPowerModeCommand(JNIEnv* env, jobject clazz, jint mode)
337{
338    char cmdstr[256];
339
340    int numWritten = snprintf(cmdstr, sizeof(cmdstr), "DRIVER POWERMODE %d", mode);
341    int cmdTooLong = numWritten >= (int)sizeof(cmdstr);
342
343    return (jboolean)!cmdTooLong && doBooleanCommand(cmdstr, "OK");
344}
345
346static jboolean android_net_wifi_setNumAllowedChannelsCommand(JNIEnv* env, jobject clazz, jint numChannels)
347{
348    char cmdstr[256];
349
350    int numWritten = snprintf(cmdstr, sizeof(cmdstr), "DRIVER SCAN-CHANNELS %u", numChannels);
351    int cmdTooLong = numWritten >= (int)sizeof(cmdstr);
352
353    return (jboolean)!cmdTooLong && doBooleanCommand(cmdstr, "OK");
354}
355
356static jint android_net_wifi_getNumAllowedChannelsCommand(JNIEnv* env, jobject clazz)
357{
358    char reply[256];
359    int numChannels;
360
361    if (doCommand("DRIVER SCAN-CHANNELS", reply, sizeof(reply)) != 0) {
362        return -1;
363    }
364    // reply comes back in the form "Scan-Channels = X" where X is the
365    // number of channels
366    if (sscanf(reply, "%*s = %u", &numChannels) == 1)
367        return numChannels;
368    else
369        return -1;
370}
371
372static jboolean android_net_wifi_setBluetoothCoexistenceModeCommand(JNIEnv* env, jobject clazz, jint mode)
373{
374    char cmdstr[256];
375
376    int numWritten = snprintf(cmdstr, sizeof(cmdstr), "DRIVER BTCOEXMODE %d", mode);
377    int cmdTooLong = numWritten >= (int)sizeof(cmdstr);
378
379    return (jboolean)!cmdTooLong && doBooleanCommand(cmdstr, "OK");
380}
381
382static jboolean android_net_wifi_saveConfigCommand(JNIEnv* env, jobject clazz)
383{
384    // Make sure we never write out a value for AP_SCAN other than 1
385    (void)doBooleanCommand("AP_SCAN 1", "OK");
386    return doBooleanCommand("SAVE_CONFIG", "OK");
387}
388
389static jboolean android_net_wifi_reloadConfigCommand(JNIEnv* env, jobject clazz)
390{
391    return doBooleanCommand("RECONFIGURE", "OK");
392}
393
394static jboolean android_net_wifi_setScanResultHandlingCommand(JNIEnv* env, jobject clazz, jint mode)
395{
396    char cmdstr[256];
397
398    int numWritten = snprintf(cmdstr, sizeof(cmdstr), "AP_SCAN %d", mode);
399    int cmdTooLong = numWritten >= (int)sizeof(cmdstr);
400
401    return (jboolean)!cmdTooLong && doBooleanCommand(cmdstr, "OK");
402}
403
404static jboolean android_net_wifi_addToBlacklistCommand(JNIEnv* env, jobject clazz, jstring bssid)
405{
406    char cmdstr[256];
407    jboolean isCopy;
408
409    const char *bssidStr = env->GetStringUTFChars(bssid, &isCopy);
410
411    int cmdTooLong = snprintf(cmdstr, sizeof(cmdstr), "BLACKLIST %s", bssidStr) >= (int)sizeof(cmdstr);
412
413    env->ReleaseStringUTFChars(bssid, bssidStr);
414
415    return (jboolean)!cmdTooLong && doBooleanCommand(cmdstr, "OK");
416}
417
418static jboolean android_net_wifi_clearBlacklistCommand(JNIEnv* env, jobject clazz)
419{
420    return doBooleanCommand("BLACKLIST clear", "OK");
421}
422
423static jboolean android_net_wifi_doDhcpRequest(JNIEnv* env, jobject clazz, jobject info)
424{
425    jint ipaddr, gateway, mask, dns1, dns2, server, lease;
426    jboolean succeeded = ((jboolean)::do_dhcp_request(&ipaddr, &gateway, &mask,
427                                        &dns1, &dns2, &server, &lease) == 0);
428    if (succeeded && dhcpInfoFieldIds.dhcpInfoClass != NULL) {
429        env->SetIntField(info, dhcpInfoFieldIds.ipaddress, ipaddr);
430        env->SetIntField(info, dhcpInfoFieldIds.gateway, gateway);
431        env->SetIntField(info, dhcpInfoFieldIds.netmask, mask);
432        env->SetIntField(info, dhcpInfoFieldIds.dns1, dns1);
433        env->SetIntField(info, dhcpInfoFieldIds.dns2, dns2);
434        env->SetIntField(info, dhcpInfoFieldIds.serverAddress, server);
435        env->SetIntField(info, dhcpInfoFieldIds.leaseDuration, lease);
436    }
437    return succeeded;
438}
439
440static jstring android_net_wifi_getDhcpError(JNIEnv* env, jobject clazz)
441{
442    return env->NewStringUTF(::get_dhcp_error_string());
443}
444
445// ----------------------------------------------------------------------------
446
447/*
448 * JNI registration.
449 */
450static JNINativeMethod gWifiMethods[] = {
451    /* name, signature, funcPtr */
452
453    { "loadDriver", "()Z",  (void *)android_net_wifi_loadDriver },
454    { "unloadDriver", "()Z",  (void *)android_net_wifi_unloadDriver },
455    { "startSupplicant", "()Z",  (void *)android_net_wifi_startSupplicant },
456    { "stopSupplicant", "()Z",  (void *)android_net_wifi_stopSupplicant },
457    { "connectToSupplicant", "()Z",  (void *)android_net_wifi_connectToSupplicant },
458    { "closeSupplicantConnection", "()V",  (void *)android_net_wifi_closeSupplicantConnection },
459
460    { "listNetworksCommand", "()Ljava/lang/String;",
461        (void*) android_net_wifi_listNetworksCommand },
462    { "addNetworkCommand", "()I", (void*) android_net_wifi_addNetworkCommand },
463    { "setNetworkVariableCommand", "(ILjava/lang/String;Ljava/lang/String;)Z",
464        (void*) android_net_wifi_setNetworkVariableCommand },
465    { "getNetworkVariableCommand", "(ILjava/lang/String;)Ljava/lang/String;",
466        (void*) android_net_wifi_getNetworkVariableCommand },
467    { "removeNetworkCommand", "(I)Z", (void*) android_net_wifi_removeNetworkCommand },
468    { "enableNetworkCommand", "(IZ)Z", (void*) android_net_wifi_enableNetworkCommand },
469    { "disableNetworkCommand", "(I)Z", (void*) android_net_wifi_disableNetworkCommand },
470    { "waitForEvent", "()Ljava/lang/String;", (void*) android_net_wifi_waitForEvent },
471    { "statusCommand", "()Ljava/lang/String;", (void*) android_net_wifi_statusCommand },
472    { "scanResultsCommand", "()Ljava/lang/String;", (void*) android_net_wifi_scanResultsCommand },
473    { "pingCommand", "()Z",  (void *)android_net_wifi_pingCommand },
474    { "disconnectCommand", "()Z",  (void *)android_net_wifi_disconnectCommand },
475    { "reconnectCommand", "()Z",  (void *)android_net_wifi_reconnectCommand },
476    { "reassociateCommand", "()Z",  (void *)android_net_wifi_reassociateCommand },
477    { "scanCommand", "()Z", (void*) android_net_wifi_scanCommand },
478    { "setScanModeCommand", "(Z)Z", (void*) android_net_wifi_setScanModeCommand },
479    { "startDriverCommand", "()Z", (void*) android_net_wifi_startDriverCommand },
480    { "stopDriverCommand", "()Z", (void*) android_net_wifi_stopDriverCommand },
481    { "setPowerModeCommand", "(I)Z", (void*) android_net_wifi_setPowerModeCommand },
482    { "setNumAllowedChannelsCommand", "(I)Z", (void*) android_net_wifi_setNumAllowedChannelsCommand },
483    { "getNumAllowedChannelsCommand", "()I", (void*) android_net_wifi_getNumAllowedChannelsCommand },
484    { "setBluetoothCoexistenceModeCommand", "(I)Z",
485    		(void*) android_net_wifi_setBluetoothCoexistenceModeCommand },
486    { "getRssiCommand", "()I", (void*) android_net_wifi_getRssiCommand },
487    { "getLinkSpeedCommand", "()I", (void*) android_net_wifi_getLinkSpeedCommand },
488    { "getMacAddressCommand", "()Ljava/lang/String;", (void*) android_net_wifi_getMacAddressCommand },
489    { "saveConfigCommand", "()Z", (void*) android_net_wifi_saveConfigCommand },
490    { "reloadConfigCommand", "()Z", (void*) android_net_wifi_reloadConfigCommand },
491    { "setScanResultHandlingCommand", "(I)Z", (void*) android_net_wifi_setScanResultHandlingCommand },
492    { "addToBlacklistCommand", "(Ljava/lang/String;)Z", (void*) android_net_wifi_addToBlacklistCommand },
493    { "clearBlacklistCommand", "()Z", (void*) android_net_wifi_clearBlacklistCommand },
494
495    { "doDhcpRequest", "(Landroid/net/DhcpInfo;)Z", (void*) android_net_wifi_doDhcpRequest },
496    { "getDhcpError", "()Ljava/lang/String;", (void*) android_net_wifi_getDhcpError },
497};
498
499int register_android_net_wifi_WifiManager(JNIEnv* env)
500{
501    jclass wifi = env->FindClass(WIFI_PKG_NAME);
502    LOG_FATAL_IF(wifi == NULL, "Unable to find class " WIFI_PKG_NAME);
503
504    dhcpInfoFieldIds.dhcpInfoClass = env->FindClass("android/net/DhcpInfo");
505    if (dhcpInfoFieldIds.dhcpInfoClass != NULL) {
506        dhcpInfoFieldIds.constructorId = env->GetMethodID(dhcpInfoFieldIds.dhcpInfoClass, "<init>", "()V");
507        dhcpInfoFieldIds.ipaddress = env->GetFieldID(dhcpInfoFieldIds.dhcpInfoClass, "ipAddress", "I");
508        dhcpInfoFieldIds.gateway = env->GetFieldID(dhcpInfoFieldIds.dhcpInfoClass, "gateway", "I");
509        dhcpInfoFieldIds.netmask = env->GetFieldID(dhcpInfoFieldIds.dhcpInfoClass, "netmask", "I");
510        dhcpInfoFieldIds.dns1 = env->GetFieldID(dhcpInfoFieldIds.dhcpInfoClass, "dns1", "I");
511        dhcpInfoFieldIds.dns2 = env->GetFieldID(dhcpInfoFieldIds.dhcpInfoClass, "dns2", "I");
512        dhcpInfoFieldIds.serverAddress = env->GetFieldID(dhcpInfoFieldIds.dhcpInfoClass, "serverAddress", "I");
513        dhcpInfoFieldIds.leaseDuration = env->GetFieldID(dhcpInfoFieldIds.dhcpInfoClass, "leaseDuration", "I");
514    }
515
516    return AndroidRuntime::registerNativeMethods(env,
517            WIFI_PKG_NAME, gWifiMethods, NELEM(gWifiMethods));
518}
519
520}; // namespace android
521