Bumping manifests a=b2g-bump
[gecko.git] / dom / wifi / WifiUtils.cpp
blob4cfbdf48481b522a7754dd4bd2357e1b4733d119
1 /* This Source Code Form is subject to the terms of the Mozilla Public
2 * License, v. 2.0. If a copy of the MPL was not distributed with this
3 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
5 #include "WifiUtils.h"
6 #include <dlfcn.h>
7 #include <errno.h>
8 #include <cutils/properties.h>
9 #include "prinit.h"
10 #include "js/CharacterEncoding.h"
12 using namespace mozilla::dom;
14 #define BUFFER_SIZE 4096
15 #define COMMAND_SIZE 256
17 // Intentionally not trying to dlclose() this handle. That's playing
18 // Russian roulette with security bugs.
19 static void* sWifiLib;
20 static PRCallOnceType sInitWifiLib;
22 static PRStatus
23 InitWifiLib()
25 sWifiLib = dlopen("/system/lib/libhardware_legacy.so", RTLD_LAZY);
26 // We might fail to open the hardware lib. That's OK.
27 return PR_SUCCESS;
30 static void*
31 GetSharedLibrary()
33 PR_CallOnce(&sInitWifiLib, InitWifiLib);
34 return sWifiLib;
37 static bool
38 GetWifiP2pSupported()
40 char propP2pSupported[PROPERTY_VALUE_MAX];
41 property_get("ro.moz.wifi.p2p_supported", propP2pSupported, "0");
42 return (0 == strcmp(propP2pSupported, "1"));
45 static int
46 hex2num(char c)
48 if (c >= '0' && c <= '9')
49 return c - '0';
50 if (c >= 'a' && c <= 'f')
51 return c - 'a' + 10;
52 if (c >= 'A' && c <= 'F')
53 return c - 'A' + 10;
54 return -1;
57 static int
58 hex2byte(const char* hex)
60 int a, b;
61 a = hex2num(*hex++);
62 if (a < 0)
63 return -1;
64 b = hex2num(*hex++);
65 if (b < 0)
66 return -1;
67 return (a << 4) | b;
70 // This function is equivalent to printf_decode() at src/utils/common.c in
71 // the supplicant.
73 static uint32_t
74 convertToBytes(char* buf, uint32_t maxlen, const char* str)
76 const char *pos = str;
77 uint32_t len = 0;
78 int val;
80 while (*pos) {
81 if (len == maxlen)
82 break;
83 switch (*pos) {
84 case '\\':
85 pos++;
86 switch (*pos) {
87 case '\\':
88 buf[len++] = '\\';
89 pos++;
90 break;
91 case '"':
92 buf[len++] = '"';
93 pos++;
94 break;
95 case 'n':
96 buf[len++] = '\n';
97 pos++;
98 break;
99 case 'r':
100 buf[len++] = '\r';
101 pos++;
102 break;
103 case 't':
104 buf[len++] = '\t';
105 pos++;
106 break;
107 case 'e':
108 buf[len++] = '\e';
109 pos++;
110 break;
111 case 'x':
112 pos++;
113 val = hex2byte(pos);
114 if (val < 0) {
115 val = hex2num(*pos);
116 if (val < 0)
117 break;
118 buf[len++] = val;
119 pos++;
120 } else {
121 buf[len++] = val;
122 pos += 2;
124 break;
125 case '0':
126 case '1':
127 case '2':
128 case '3':
129 case '4':
130 case '5':
131 case '6':
132 case '7':
133 val = *pos++ - '0';
134 if (*pos >= '0' && *pos <= '7')
135 val = val * 8 + (*pos++ - '0');
136 if (*pos >= '0' && *pos <= '7')
137 val = val * 8 + (*pos++ - '0');
138 buf[len++] = val;
139 break;
140 default:
141 break;
143 break;
144 default:
145 buf[len++] = *pos++;
146 break;
149 return len;
152 // This is the same algorithm as in InflateUTF8StringToBuffer with Copy and
153 // while ignoring invalids.
154 // https://mxr.mozilla.org/mozilla-central/source/js/src/vm/CharacterEncoding.cpp#231
156 static const uint32_t REPLACE_UTF8 = 0xFFFD;
158 static void
159 LossyConvertUTF8toUTF16(const char* aInput, uint32_t aLength, nsAString& aOut)
161 JS::UTF8Chars src(aInput, aLength);
163 char16_t dst[aLength]; // Allocating for worst case.
165 // Count how many char16_t characters are needed in the inflated string.
166 // |i| is the index into |src|, and |j| is the the index into |dst|.
167 size_t srclen = src.length();
168 uint32_t j = 0;
169 for (uint32_t i = 0; i < srclen; i++, j++) {
170 uint32_t v = uint32_t(src[i]);
171 if (v == uint32_t('\0') && i < srclen - 1) {
172 // If the leading byte is '\0' and it's not the last byte,
173 // just ignore it to prevent from being truncated. This could
174 // be caused by |convertToBytes| (e.g. \x00 would be converted to '\0')
175 j--;
176 continue;
178 if (!(v & 0x80)) {
179 // ASCII code unit. Simple copy.
180 dst[j] = char16_t(v);
181 } else {
182 // Non-ASCII code unit. Determine its length in bytes (n).
183 uint32_t n = 1;
184 while (v & (0x80 >> n))
185 n++;
187 #define INVALID(report, arg, n2) \
188 do { \
189 n = n2; \
190 goto invalidMultiByteCodeUnit; \
191 } while (0)
193 // Check the leading byte.
194 if (n < 2 || n > 4)
195 INVALID(ReportInvalidCharacter, i, 1);
197 // Check that |src| is large enough to hold an n-byte code unit.
198 if (i + n > srclen)
199 INVALID(ReportBufferTooSmall, /* dummy = */ 0, 1);
201 // Check the second byte. From Unicode Standard v6.2, Table 3-7
202 // Well-Formed UTF-8 Byte Sequences.
203 if ((v == 0xE0 && ((uint8_t)src[i + 1] & 0xE0) != 0xA0) || // E0 A0~BF
204 (v == 0xED && ((uint8_t)src[i + 1] & 0xE0) != 0x80) || // ED 80~9F
205 (v == 0xF0 && ((uint8_t)src[i + 1] & 0xF0) == 0x80) || // F0 90~BF
206 (v == 0xF4 && ((uint8_t)src[i + 1] & 0xF0) != 0x80)) // F4 80~8F
208 INVALID(ReportInvalidCharacter, i, 1);
211 // Check the continuation bytes.
212 for (uint32_t m = 1; m < n; m++)
213 if ((src[i + m] & 0xC0) != 0x80)
214 INVALID(ReportInvalidCharacter, i, m);
216 // Determine the code unit's length in char16_t units and act accordingly.
217 v = JS::Utf8ToOneUcs4Char((uint8_t *)&src[i], n);
218 if (v < 0x10000) {
219 // The n-byte UTF8 code unit will fit in a single char16_t.
220 dst[j] = char16_t(v);
221 } else {
222 v -= 0x10000;
223 if (v <= 0xFFFFF) {
224 // The n-byte UTF8 code unit will fit in two char16_t units.
225 dst[j] = char16_t((v >> 10) + 0xD800);
226 j++;
227 dst[j] = char16_t((v & 0x3FF) + 0xDC00);
228 } else {
229 // The n-byte UTF8 code unit won't fit in two char16_t units.
230 INVALID(ReportTooBigCharacter, v, 1);
234 invalidMultiByteCodeUnit:
235 // Move i to the last byte of the multi-byte code unit; the loop
236 // header will do the final i++ to move to the start of the next
237 // code unit.
238 i += n - 1;
242 dst[j] = 0;
243 aOut = dst;
246 // Helper to check we have loaded the hardware shared library.
247 #define CHECK_HWLIB(ret) \
248 void* hwLib = GetSharedLibrary(); \
249 if (!hwLib) { \
250 NS_WARNING("No /system/lib/libhardware_legacy.so"); \
251 return ret; \
254 #define DEFAULT_IMPL(name, ret, args...) \
255 DEFINE_DLFUNC(name, ret, args...) \
256 ret do_##name(args) { \
257 USE_DLFUNC(name) \
258 return name(args); \
261 // ICS implementation.
262 class ICSWpaSupplicantImpl : public WpaSupplicantImpl
264 public:
265 DEFAULT_IMPL(wifi_load_driver, int32_t, )
266 DEFAULT_IMPL(wifi_unload_driver, int32_t, )
268 DEFINE_DLFUNC(wifi_wait_for_event, int32_t, char*, size_t)
269 int32_t do_wifi_wait_for_event(const char *iface, char *buf, size_t len) {
270 USE_DLFUNC(wifi_wait_for_event)
271 return wifi_wait_for_event(buf, len);
274 DEFINE_DLFUNC(wifi_command, int32_t, const char*, char*, size_t*)
275 int32_t do_wifi_command(const char* iface, const char* cmd, char* buf, size_t* len) {
276 USE_DLFUNC(wifi_command)
277 return wifi_command(cmd, buf, len);
280 DEFINE_DLFUNC(wifi_start_supplicant, int32_t, )
281 int32_t do_wifi_start_supplicant(int32_t) {
282 USE_DLFUNC(wifi_start_supplicant)
283 return wifi_start_supplicant();
286 DEFINE_DLFUNC(wifi_stop_supplicant, int32_t)
287 int32_t do_wifi_stop_supplicant(int32_t) {
288 USE_DLFUNC(wifi_stop_supplicant)
289 return wifi_stop_supplicant();
292 DEFINE_DLFUNC(wifi_connect_to_supplicant, int32_t, )
293 int32_t do_wifi_connect_to_supplicant(const char* iface) {
294 USE_DLFUNC(wifi_connect_to_supplicant)
295 return wifi_connect_to_supplicant();
298 DEFINE_DLFUNC(wifi_close_supplicant_connection, void, )
299 void do_wifi_close_supplicant_connection(const char* iface) {
300 USE_DLFUNC(wifi_close_supplicant_connection)
301 return wifi_close_supplicant_connection();
305 // JB implementation.
306 // We only redefine the methods that have a different signature than on ICS.
307 class JBWpaSupplicantImpl : public ICSWpaSupplicantImpl
309 public:
310 DEFINE_DLFUNC(wifi_wait_for_event, int32_t, const char*, char*, size_t)
311 int32_t do_wifi_wait_for_event(const char* iface, char* buf, size_t len) {
312 USE_DLFUNC(wifi_wait_for_event)
313 return wifi_wait_for_event(iface, buf, len);
316 DEFINE_DLFUNC(wifi_command, int32_t, const char*, const char*, char*, size_t*)
317 int32_t do_wifi_command(const char* iface, const char* cmd, char* buf, size_t* len) {
318 USE_DLFUNC(wifi_command)
319 return wifi_command(iface, cmd, buf, len);
322 DEFINE_DLFUNC(wifi_start_supplicant, int32_t, int32_t)
323 int32_t do_wifi_start_supplicant(int32_t arg) {
324 USE_DLFUNC(wifi_start_supplicant)
325 return wifi_start_supplicant(arg);
328 DEFINE_DLFUNC(wifi_stop_supplicant, int32_t, int32_t)
329 int32_t do_wifi_stop_supplicant(int32_t arg) {
330 USE_DLFUNC(wifi_stop_supplicant)
331 return wifi_stop_supplicant(arg);
334 DEFINE_DLFUNC(wifi_connect_to_supplicant, int32_t, const char*)
335 int32_t do_wifi_connect_to_supplicant(const char* iface) {
336 USE_DLFUNC(wifi_connect_to_supplicant)
337 return wifi_connect_to_supplicant(iface);
340 DEFINE_DLFUNC(wifi_close_supplicant_connection, void, const char*)
341 void do_wifi_close_supplicant_connection(const char* iface) {
342 USE_DLFUNC(wifi_close_supplicant_connection)
343 wifi_close_supplicant_connection(iface);
347 // KK implementation.
348 // We only redefine the methods that have a different signature than on ICS.
349 class KKWpaSupplicantImpl : public ICSWpaSupplicantImpl
351 public:
352 DEFINE_DLFUNC(wifi_start_supplicant, int32_t, int32_t)
353 int32_t do_wifi_start_supplicant(int32_t arg) {
354 USE_DLFUNC(wifi_start_supplicant)
355 return wifi_start_supplicant(arg);
358 DEFINE_DLFUNC(wifi_stop_supplicant, int32_t, int32_t)
359 int32_t do_wifi_stop_supplicant(int32_t arg) {
360 USE_DLFUNC(wifi_stop_supplicant)
361 return wifi_stop_supplicant(arg);
364 DEFINE_DLFUNC(wifi_command, int32_t, const char*, char*, size_t*)
365 int32_t do_wifi_command(const char* iface, const char* cmd, char* buf, size_t* len) {
366 char command[COMMAND_SIZE];
367 if (!strcmp(iface, "p2p0")) {
368 // Commands for p2p0 interface don't need prefix
369 snprintf(command, COMMAND_SIZE, "%s", cmd);
371 else {
372 snprintf(command, COMMAND_SIZE, "IFNAME=%s %s", iface, cmd);
374 USE_DLFUNC(wifi_command)
375 return wifi_command(command, buf, len);
379 // Concrete class to use to access the wpa supplicant.
380 WpaSupplicant::WpaSupplicant()
382 char propVersion[PROPERTY_VALUE_MAX];
383 property_get("ro.build.version.sdk", propVersion, "0");
384 mSdkVersion = strtol(propVersion, nullptr, 10);
386 if (mSdkVersion < 16) {
387 mImpl = new ICSWpaSupplicantImpl();
388 } else if (mSdkVersion < 19) {
389 mImpl = new JBWpaSupplicantImpl();
390 } else {
391 mImpl = new KKWpaSupplicantImpl();
393 mWifiHotspotUtils = new WifiHotspotUtils();
396 void WpaSupplicant::WaitForEvent(nsAString& aEvent, const nsCString& aInterface)
398 CHECK_HWLIB()
400 char buffer[BUFFER_SIZE];
401 int32_t ret = mImpl->do_wifi_wait_for_event(aInterface.get(), buffer, BUFFER_SIZE);
402 CheckBuffer(buffer, ret, aEvent);
405 #define GET_CHAR(prop) NS_ConvertUTF16toUTF8(aOptions.prop).get()
408 * Make a subnet mask.
410 uint32_t WpaSupplicant::MakeMask(uint32_t len) {
411 uint32_t mask = 0;
412 for (uint32_t i = 0; i < len; ++i) {
413 mask |= (0x80000000 >> i);
415 return ntohl(mask);
418 bool WpaSupplicant::ExecuteCommand(CommandOptions aOptions,
419 WifiResultOptions& aResult,
420 const nsCString& aInterface)
422 CHECK_HWLIB(false)
424 if (!mWifiHotspotUtils->GetSharedLibrary()) {
425 return false;
428 // Always correlate the opaque ids.
429 aResult.mId = aOptions.mId;
431 if (aOptions.mCmd.EqualsLiteral("command")) {
432 size_t len = BUFFER_SIZE - 1;
433 char buffer[BUFFER_SIZE];
434 NS_ConvertUTF16toUTF8 request(aOptions.mRequest);
435 aResult.mStatus = mImpl->do_wifi_command(aInterface.get(), request.get(), buffer, &len);
436 nsString value;
437 if (aResult.mStatus == 0) {
438 if (buffer[len - 1] == '\n') { // remove trailing new lines.
439 len--;
441 buffer[len] = '\0';
442 CheckBuffer(buffer, len, value);
444 aResult.mReply = value;
445 } else if (aOptions.mCmd.EqualsLiteral("close_supplicant_connection")) {
446 mImpl->do_wifi_close_supplicant_connection(aInterface.get());
447 } else if (aOptions.mCmd.EqualsLiteral("load_driver")) {
448 aResult.mStatus = mImpl->do_wifi_load_driver();
449 } else if (aOptions.mCmd.EqualsLiteral("unload_driver")) {
450 aResult.mStatus = mImpl->do_wifi_unload_driver();
451 } else if (aOptions.mCmd.EqualsLiteral("start_supplicant")) {
452 aResult.mStatus = mImpl->do_wifi_start_supplicant(GetWifiP2pSupported() ? 1 : 0);
453 } else if (aOptions.mCmd.EqualsLiteral("stop_supplicant")) {
454 aResult.mStatus = mImpl->do_wifi_stop_supplicant(0);
455 } else if (aOptions.mCmd.EqualsLiteral("connect_to_supplicant")) {
456 aResult.mStatus = mImpl->do_wifi_connect_to_supplicant(aInterface.get());
457 } else if (aOptions.mCmd.EqualsLiteral("hostapd_command")) {
458 size_t len = BUFFER_SIZE - 1;
459 char buffer[BUFFER_SIZE];
460 NS_ConvertUTF16toUTF8 request(aOptions.mRequest);
461 aResult.mStatus = mWifiHotspotUtils->do_wifi_hostapd_command(request.get(),
462 buffer,
463 &len);
464 nsString value;
465 if (aResult.mStatus == 0) {
466 if (buffer[len - 1] == '\n') { // remove trailing new lines.
467 len--;
469 buffer[len] = '\0';
470 CheckBuffer(buffer, len, value);
472 aResult.mReply = value;
473 } else if (aOptions.mCmd.EqualsLiteral("hostapd_get_stations")) {
474 aResult.mStatus = mWifiHotspotUtils->do_wifi_hostapd_get_stations();
475 } else if (aOptions.mCmd.EqualsLiteral("connect_to_hostapd")) {
476 aResult.mStatus = mWifiHotspotUtils->do_wifi_connect_to_hostapd();
477 } else if (aOptions.mCmd.EqualsLiteral("close_hostapd_connection")) {
478 aResult.mStatus = mWifiHotspotUtils->do_wifi_close_hostapd_connection();
479 } else if (aOptions.mCmd.EqualsLiteral("hostapd_command")) {
480 size_t len = BUFFER_SIZE - 1;
481 char buffer[BUFFER_SIZE];
482 NS_ConvertUTF16toUTF8 request(aOptions.mRequest);
483 aResult.mStatus = mWifiHotspotUtils->do_wifi_hostapd_command(request.get(),
484 buffer,
485 &len);
486 nsString value;
487 if (aResult.mStatus == 0) {
488 if (buffer[len - 1] == '\n') { // remove trailing new lines.
489 len--;
491 buffer[len] = '\0';
492 CheckBuffer(buffer, len, value);
494 aResult.mReply = value;
495 } else if (aOptions.mCmd.EqualsLiteral("hostapd_get_stations")) {
496 aResult.mStatus = mWifiHotspotUtils->do_wifi_hostapd_get_stations();
497 } else if (aOptions.mCmd.EqualsLiteral("connect_to_hostapd")) {
498 aResult.mStatus = mWifiHotspotUtils->do_wifi_connect_to_hostapd();
499 } else if (aOptions.mCmd.EqualsLiteral("close_hostapd_connection")) {
500 aResult.mStatus = mWifiHotspotUtils->do_wifi_close_hostapd_connection();
502 } else {
503 NS_WARNING("WpaSupplicant::ExecuteCommand : Unknown command");
504 printf_stderr("WpaSupplicant::ExecuteCommand : Unknown command: %s",
505 NS_ConvertUTF16toUTF8(aOptions.mCmd).get());
506 return false;
509 return true;
512 // Checks the buffer and do the utf processing.
513 void
514 WpaSupplicant::CheckBuffer(char* buffer, int32_t length,
515 nsAString& aEvent)
517 if (length <= 0 || length >= (BUFFER_SIZE - 1)) {
518 NS_WARNING("WpaSupplicant::CheckBuffer: Invalid buffer length");
519 return;
522 if (mSdkVersion < 18) {
523 buffer[length] = 0;
524 LossyConvertUTF8toUTF16(buffer, length, aEvent);
525 return;
528 // After Android JB4.3, the SSIDs have been converted into printable form.
529 // In most of cases, SSIDs do not use unprintable characters, but IEEE 802.11
530 // standard does not limit the used character set, so anything could be used
531 // in an SSID. Convert it to raw data form here.
532 char bytesBuffer[BUFFER_SIZE];
533 uint32_t bytes = convertToBytes(bytesBuffer, length, buffer);
534 if (bytes <= 0 || bytes >= BUFFER_SIZE) {
535 NS_WARNING("WpaSupplicant::CheckBuffer: Invalid bytesbuffer length");
536 return;
538 bytesBuffer[bytes] = 0;
539 LossyConvertUTF8toUTF16(bytesBuffer, bytes, aEvent);