blob: a52a74c3b1eca3b02956a5da0be0bff5c70bbf1f [file] [log] [blame]
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001/*
2** Copyright 2006, 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
Jaikumar Ganeshd5ac1ae2009-05-05 22:26:12 -070017#define DBUS_ADAPTER_IFACE BLUEZ_DBUS_BASE_IFC ".Adapter"
18#define DBUS_DEVICE_IFACE BLUEZ_DBUS_BASE_IFC ".Device"
Jaikumar Ganesh545e6702010-06-04 10:23:03 -070019#define DBUS_INPUT_IFACE BLUEZ_DBUS_BASE_IFC ".Input"
20
Nick Pellybd022f42009-08-14 18:33:38 -070021#define LOG_TAG "BluetoothService.cpp"
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080022
23#include "android_bluetooth_common.h"
24#include "android_runtime/AndroidRuntime.h"
25#include "JNIHelp.h"
26#include "jni.h"
27#include "utils/Log.h"
28#include "utils/misc.h"
29
30#include <ctype.h>
31#include <stdio.h>
32#include <string.h>
33#include <stdlib.h>
34#include <errno.h>
35#include <unistd.h>
36
37#include <sys/socket.h>
38#include <sys/ioctl.h>
39#include <fcntl.h>
40
41#ifdef HAVE_BLUETOOTH
42#include <dbus/dbus.h>
43#include <bluedroid/bluetooth.h>
44#endif
45
46#include <cutils/properties.h>
47
48namespace android {
49
Nick Pelly2b5be072009-03-31 14:42:33 -070050#define BLUETOOTH_CLASS_ERROR 0xFF000000
Jaikumar Ganesh6d56b532009-08-24 17:11:11 -070051#define PROPERTIES_NREFS 10
Nick Pelly2b5be072009-03-31 14:42:33 -070052
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080053#ifdef HAVE_BLUETOOTH
54// We initialize these variables when we load class
Nick Pellybd022f42009-08-14 18:33:38 -070055// android.server.BluetoothService
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080056static jfieldID field_mNativeData;
Robert Greenwalt28d139f2009-04-02 22:41:08 -070057static jfieldID field_mEventLoop;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080058
59typedef struct {
60 JNIEnv *env;
61 DBusConnection *conn;
62 const char *adapter; // dbus object name of the local adapter
63} native_data_t;
64
Robert Greenwalt28d139f2009-04-02 22:41:08 -070065extern event_loop_native_data_t *get_EventLoop_native_data(JNIEnv *,
66 jobject);
Jaikumar Ganeshd5ac1ae2009-05-05 22:26:12 -070067extern DBusHandlerResult agent_event_filter(DBusConnection *conn,
68 DBusMessage *msg,
69 void *data);
70void onCreatePairedDeviceResult(DBusMessage *msg, void *user, void *nat);
Jaikumar Ganesh1caa6d12009-09-18 11:32:54 -070071void onDiscoverServicesResult(DBusMessage *msg, void *user, void *nat);
72void onCreateDeviceResult(DBusMessage *msg, void *user, void *nat);
Jaikumar Ganeshd5ac1ae2009-05-05 22:26:12 -070073
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080074
75/** Get native data stored in the opaque (Java code maintained) pointer mNativeData
76 * Perform quick sanity check, if there are any problems return NULL
77 */
78static inline native_data_t * get_native_data(JNIEnv *env, jobject object) {
79 native_data_t *nat =
80 (native_data_t *)(env->GetIntField(object, field_mNativeData));
81 if (nat == NULL || nat->conn == NULL) {
82 LOGE("Uninitialized native data\n");
83 return NULL;
84 }
85 return nat;
86}
87#endif
88
89static void classInitNative(JNIEnv* env, jclass clazz) {
90 LOGV(__FUNCTION__);
91#ifdef HAVE_BLUETOOTH
92 field_mNativeData = get_field(env, clazz, "mNativeData", "I");
Robert Greenwalt28d139f2009-04-02 22:41:08 -070093 field_mEventLoop = get_field(env, clazz, "mEventLoop",
94 "Landroid/server/BluetoothEventLoop;");
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080095#endif
96}
97
98/* Returns true on success (even if adapter is present but disabled).
99 * Return false if dbus is down, or another serious error (out of memory)
100*/
101static bool initializeNativeDataNative(JNIEnv* env, jobject object) {
102 LOGV(__FUNCTION__);
103#ifdef HAVE_BLUETOOTH
104 native_data_t *nat = (native_data_t *)calloc(1, sizeof(native_data_t));
105 if (NULL == nat) {
106 LOGE("%s: out of memory!", __FUNCTION__);
107 return false;
108 }
109 nat->env = env;
110
111 env->SetIntField(object, field_mNativeData, (jint)nat);
112 DBusError err;
113 dbus_error_init(&err);
114 dbus_threads_init_default();
115 nat->conn = dbus_bus_get(DBUS_BUS_SYSTEM, &err);
116 if (dbus_error_is_set(&err)) {
117 LOGE("Could not get onto the system bus: %s", err.message);
118 dbus_error_free(&err);
119 return false;
120 }
Nick Pelly9e0a1952009-06-17 15:27:59 -0700121 dbus_connection_set_exit_on_disconnect(nat->conn, FALSE);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800122#endif /*HAVE_BLUETOOTH*/
123 return true;
124}
125
Jaikumar Ganeshd5ac1ae2009-05-05 22:26:12 -0700126static const char *get_adapter_path(JNIEnv* env, jobject object) {
Jaikumar Ganesh82aea4a2009-06-09 23:23:20 -0700127#ifdef HAVE_BLUETOOTH
Jaikumar Ganeshd5ac1ae2009-05-05 22:26:12 -0700128 event_loop_native_data_t *event_nat =
129 get_EventLoop_native_data(env, env->GetObjectField(object,
130 field_mEventLoop));
131 if (event_nat == NULL)
132 return NULL;
133 return event_nat->adapter;
Jaikumar Ganesh82aea4a2009-06-09 23:23:20 -0700134#else
135 return NULL;
136#endif
Jaikumar Ganeshd5ac1ae2009-05-05 22:26:12 -0700137}
138
139// This function is called when the adapter is enabled.
140static jboolean setupNativeDataNative(JNIEnv* env, jobject object) {
141 LOGV(__FUNCTION__);
142#ifdef HAVE_BLUETOOTH
143 native_data_t *nat =
144 (native_data_t *)env->GetIntField(object, field_mNativeData);
145 event_loop_native_data_t *event_nat =
146 get_EventLoop_native_data(env, env->GetObjectField(object,
147 field_mEventLoop));
148 // Register agent for remote devices.
149 const char *device_agent_path = "/android/bluetooth/remote_device_agent";
150 static const DBusObjectPathVTable agent_vtable = {
151 NULL, agent_event_filter, NULL, NULL, NULL, NULL };
152
153 if (!dbus_connection_register_object_path(nat->conn, device_agent_path,
154 &agent_vtable, event_nat)) {
155 LOGE("%s: Can't register object path %s for remote device agent!",
156 __FUNCTION__, device_agent_path);
157 return JNI_FALSE;
158 }
159#endif /*HAVE_BLUETOOTH*/
160 return JNI_TRUE;
161}
162
163static jboolean tearDownNativeDataNative(JNIEnv *env, jobject object) {
164 LOGV(__FUNCTION__);
165#ifdef HAVE_BLUETOOTH
166 native_data_t *nat =
167 (native_data_t *)env->GetIntField(object, field_mNativeData);
168 if (nat != NULL) {
169 const char *device_agent_path =
170 "/android/bluetooth/remote_device_agent";
171 dbus_connection_unregister_object_path (nat->conn, device_agent_path);
172 }
173#endif /*HAVE_BLUETOOTH*/
174 return JNI_TRUE;
175}
176
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800177static void cleanupNativeDataNative(JNIEnv* env, jobject object) {
178 LOGV(__FUNCTION__);
179#ifdef HAVE_BLUETOOTH
180 native_data_t *nat =
181 (native_data_t *)env->GetIntField(object, field_mNativeData);
182 if (nat) {
183 free(nat);
184 nat = NULL;
185 }
186#endif
187}
188
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800189static jstring getAdapterPathNative(JNIEnv *env, jobject object) {
190 LOGV(__FUNCTION__);
191#ifdef HAVE_BLUETOOTH
192 native_data_t *nat = get_native_data(env, object);
193 if (nat) {
Jaikumar Ganeshd5ac1ae2009-05-05 22:26:12 -0700194 return (env->NewStringUTF(get_adapter_path(env, object)));
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800195 }
196#endif
197 return NULL;
198}
199
200
201static jboolean startDiscoveryNative(JNIEnv *env, jobject object) {
202 LOGV(__FUNCTION__);
203#ifdef HAVE_BLUETOOTH
204 DBusMessage *msg = NULL;
205 DBusMessage *reply = NULL;
206 DBusError err;
207 const char *name;
208 jboolean ret = JNI_FALSE;
209
210 native_data_t *nat = get_native_data(env, object);
211 if (nat == NULL) {
212 goto done;
213 }
214
215 dbus_error_init(&err);
216
217 /* Compose the command */
Jaikumar Ganeshd5ac1ae2009-05-05 22:26:12 -0700218 msg = dbus_message_new_method_call(BLUEZ_DBUS_BASE_IFC,
219 get_adapter_path(env, object),
220 DBUS_ADAPTER_IFACE, "StartDiscovery");
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800221
222 if (msg == NULL) {
Jaikumar Ganeshd5ac1ae2009-05-05 22:26:12 -0700223 if (dbus_error_is_set(&err)) {
224 LOG_AND_FREE_DBUS_ERROR_WITH_MSG(&err, msg);
225 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800226 goto done;
227 }
228
229 /* Send the command. */
230 reply = dbus_connection_send_with_reply_and_block(nat->conn, msg, -1, &err);
231 if (dbus_error_is_set(&err)) {
Jaikumar Ganeshd5ac1ae2009-05-05 22:26:12 -0700232 LOG_AND_FREE_DBUS_ERROR_WITH_MSG(&err, msg);
233 ret = JNI_FALSE;
234 goto done;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800235 }
236
237 ret = JNI_TRUE;
238done:
239 if (reply) dbus_message_unref(reply);
240 if (msg) dbus_message_unref(msg);
241 return ret;
242#else
243 return JNI_FALSE;
244#endif
245}
246
Jaikumar Ganeshd5ac1ae2009-05-05 22:26:12 -0700247static void stopDiscoveryNative(JNIEnv *env, jobject object) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800248 LOGV(__FUNCTION__);
249#ifdef HAVE_BLUETOOTH
250 DBusMessage *msg = NULL;
251 DBusMessage *reply = NULL;
252 DBusError err;
253 const char *name;
254 jstring ret;
255 native_data_t *nat;
256
257 dbus_error_init(&err);
258
259 nat = get_native_data(env, object);
260 if (nat == NULL) {
261 goto done;
262 }
263
264 /* Compose the command */
Jaikumar Ganeshd5ac1ae2009-05-05 22:26:12 -0700265 msg = dbus_message_new_method_call(BLUEZ_DBUS_BASE_IFC,
266 get_adapter_path(env, object),
267 DBUS_ADAPTER_IFACE, "StopDiscovery");
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800268 if (msg == NULL) {
Jaikumar Ganeshd5ac1ae2009-05-05 22:26:12 -0700269 if (dbus_error_is_set(&err))
270 LOG_AND_FREE_DBUS_ERROR_WITH_MSG(&err, msg);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800271 goto done;
272 }
273
274 /* Send the command. */
275 reply = dbus_connection_send_with_reply_and_block(nat->conn, msg, -1, &err);
276 if (dbus_error_is_set(&err)) {
Jaikumar Ganeshd5ac1ae2009-05-05 22:26:12 -0700277 if(strncmp(err.name, BLUEZ_DBUS_BASE_IFC ".Error.NotAuthorized",
278 strlen(BLUEZ_DBUS_BASE_IFC ".Error.NotAuthorized")) == 0) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800279 // hcid sends this if there is no active discovery to cancel
280 LOGV("%s: There was no active discovery to cancel", __FUNCTION__);
281 dbus_error_free(&err);
282 } else {
283 LOG_AND_FREE_DBUS_ERROR_WITH_MSG(&err, msg);
284 }
285 }
286
287done:
288 if (msg) dbus_message_unref(msg);
289 if (reply) dbus_message_unref(reply);
290#endif
291}
292
Jaikumar Ganeshd5ac1ae2009-05-05 22:26:12 -0700293static jboolean createPairedDeviceNative(JNIEnv *env, jobject object,
294 jstring address, jint timeout_ms) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800295 LOGV(__FUNCTION__);
296#ifdef HAVE_BLUETOOTH
297 native_data_t *nat = get_native_data(env, object);
Robert Greenwalt28d139f2009-04-02 22:41:08 -0700298 jobject eventLoop = env->GetObjectField(object, field_mEventLoop);
299 struct event_loop_native_data_t *eventLoopNat =
300 get_EventLoop_native_data(env, eventLoop);
301
302 if (nat && eventLoopNat) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800303 const char *c_address = env->GetStringUTFChars(address, NULL);
304 LOGV("... address = %s", c_address);
305 char *context_address = (char *)calloc(BTADDR_SIZE, sizeof(char));
Jaikumar Ganeshd5ac1ae2009-05-05 22:26:12 -0700306 const char *capabilities = "DisplayYesNo";
307 const char *agent_path = "/android/bluetooth/remote_device_agent";
308
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800309 strlcpy(context_address, c_address, BTADDR_SIZE); // for callback
310 bool ret = dbus_func_args_async(env, nat->conn, (int)timeout_ms,
Jaikumar Ganeshd5ac1ae2009-05-05 22:26:12 -0700311 onCreatePairedDeviceResult, // callback
Robert Greenwalt28d139f2009-04-02 22:41:08 -0700312 context_address,
313 eventLoopNat,
Jaikumar Ganeshd5ac1ae2009-05-05 22:26:12 -0700314 get_adapter_path(env, object),
315 DBUS_ADAPTER_IFACE,
316 "CreatePairedDevice",
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800317 DBUS_TYPE_STRING, &c_address,
Jaikumar Ganeshd5ac1ae2009-05-05 22:26:12 -0700318 DBUS_TYPE_OBJECT_PATH, &agent_path,
319 DBUS_TYPE_STRING, &capabilities,
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800320 DBUS_TYPE_INVALID);
321 env->ReleaseStringUTFChars(address, c_address);
322 return ret ? JNI_TRUE : JNI_FALSE;
323
324 }
325#endif
326 return JNI_FALSE;
327}
328
Jaikumar Ganeshd5ac1ae2009-05-05 22:26:12 -0700329static jint getDeviceServiceChannelNative(JNIEnv *env, jobject object,
330 jstring path,
331 jstring pattern, jint attr_id) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800332#ifdef HAVE_BLUETOOTH
333 LOGV(__FUNCTION__);
334 native_data_t *nat = get_native_data(env, object);
Robert Greenwalt28d139f2009-04-02 22:41:08 -0700335 jobject eventLoop = env->GetObjectField(object, field_mEventLoop);
336 struct event_loop_native_data_t *eventLoopNat =
337 get_EventLoop_native_data(env, eventLoop);
338 if (nat && eventLoopNat) {
Jaikumar Ganeshd5ac1ae2009-05-05 22:26:12 -0700339 const char *c_pattern = env->GetStringUTFChars(pattern, NULL);
340 const char *c_path = env->GetStringUTFChars(path, NULL);
341 LOGV("... pattern = %s", c_pattern);
342 LOGV("... attr_id = %#X", attr_id);
343 DBusMessage *reply =
344 dbus_func_args(env, nat->conn, c_path,
345 DBUS_DEVICE_IFACE, "GetServiceAttributeValue",
346 DBUS_TYPE_STRING, &c_pattern,
347 DBUS_TYPE_UINT16, &attr_id,
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800348 DBUS_TYPE_INVALID);
Jaikumar Ganeshd5ac1ae2009-05-05 22:26:12 -0700349 env->ReleaseStringUTFChars(pattern, c_pattern);
350 env->ReleaseStringUTFChars(path, c_path);
351 return reply ? dbus_returns_int32(env, reply) : -1;
352 }
353#endif
354 return -1;
355}
356
357static jboolean cancelDeviceCreationNative(JNIEnv *env, jobject object,
358 jstring address) {
359 LOGV(__FUNCTION__);
360 jboolean result = JNI_FALSE;
361#ifdef HAVE_BLUETOOTH
362 native_data_t *nat = get_native_data(env, object);
363 if (nat) {
364 const char *c_address = env->GetStringUTFChars(address, NULL);
365 DBusError err;
366 dbus_error_init(&err);
367 LOGV("... address = %s", c_address);
368 DBusMessage *reply =
369 dbus_func_args_timeout(env, nat->conn, -1,
370 get_adapter_path(env, object),
371 DBUS_ADAPTER_IFACE, "CancelDeviceCreation",
372 DBUS_TYPE_STRING, &c_address,
373 DBUS_TYPE_INVALID);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800374 env->ReleaseStringUTFChars(address, c_address);
Jaikumar Ganeshd5ac1ae2009-05-05 22:26:12 -0700375 if (!reply) {
376 if (dbus_error_is_set(&err)) {
377 LOG_AND_FREE_DBUS_ERROR(&err);
378 } else
379 LOGE("DBus reply is NULL in function %s", __FUNCTION__);
380 return JNI_FALSE;
381 } else {
382 result = JNI_TRUE;
383 }
384 dbus_message_unref(reply);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800385 }
386#endif
387 return JNI_FALSE;
388}
389
Jaikumar Ganeshd5ac1ae2009-05-05 22:26:12 -0700390static jboolean removeDeviceNative(JNIEnv *env, jobject object, jstring object_path) {
391 LOGV(__FUNCTION__);
Jaikumar Ganeshd5ac1ae2009-05-05 22:26:12 -0700392#ifdef HAVE_BLUETOOTH
393 native_data_t *nat = get_native_data(env, object);
394 if (nat) {
395 const char *c_object_path = env->GetStringUTFChars(object_path, NULL);
Jaikumar Ganesh995ae822009-09-20 11:25:09 -0700396 bool ret = dbus_func_args_async(env, nat->conn, -1,
397 NULL,
398 NULL,
399 NULL,
400 get_adapter_path(env, object),
401 DBUS_ADAPTER_IFACE,
402 "RemoveDevice",
403 DBUS_TYPE_OBJECT_PATH, &c_object_path,
404 DBUS_TYPE_INVALID);
Jaikumar Ganeshd5ac1ae2009-05-05 22:26:12 -0700405 env->ReleaseStringUTFChars(object_path, c_object_path);
Jaikumar Ganesh995ae822009-09-20 11:25:09 -0700406 return ret ? JNI_TRUE : JNI_FALSE;
Jaikumar Ganeshd5ac1ae2009-05-05 22:26:12 -0700407 }
408#endif
Jaikumar Ganesh995ae822009-09-20 11:25:09 -0700409 return JNI_FALSE;
Jaikumar Ganeshd5ac1ae2009-05-05 22:26:12 -0700410}
411
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800412static jint enableNative(JNIEnv *env, jobject object) {
413#ifdef HAVE_BLUETOOTH
414 LOGV(__FUNCTION__);
415 return bt_enable();
416#endif
417 return -1;
418}
419
420static jint disableNative(JNIEnv *env, jobject object) {
421#ifdef HAVE_BLUETOOTH
422 LOGV(__FUNCTION__);
423 return bt_disable();
424#endif
425 return -1;
426}
427
428static jint isEnabledNative(JNIEnv *env, jobject object) {
429#ifdef HAVE_BLUETOOTH
430 LOGV(__FUNCTION__);
431 return bt_is_enabled();
432#endif
433 return -1;
434}
435
Jaikumar Ganeshb0eca412009-07-16 18:26:28 -0700436static jboolean setPairingConfirmationNative(JNIEnv *env, jobject object,
437 jstring address, bool confirm,
438 int nativeData) {
439#ifdef HAVE_BLUETOOTH
440 LOGV(__FUNCTION__);
441 native_data_t *nat = get_native_data(env, object);
442 if (nat) {
443 DBusMessage *msg = (DBusMessage *)nativeData;
444 DBusMessage *reply;
445 if (confirm) {
446 reply = dbus_message_new_method_return(msg);
447 } else {
448 reply = dbus_message_new_error(msg,
449 "org.bluez.Error.Rejected", "User rejected confirmation");
450 }
451
452 if (!reply) {
Jaikumar Ganesh32d85712009-09-10 22:00:05 -0700453 LOGE("%s: Cannot create message reply to RequestPasskeyConfirmation or"
454 "RequestPairingConsent to D-Bus\n", __FUNCTION__);
Jaikumar Ganeshb0eca412009-07-16 18:26:28 -0700455 dbus_message_unref(msg);
456 return JNI_FALSE;
457 }
458
459 dbus_connection_send(nat->conn, reply, NULL);
460 dbus_message_unref(msg);
461 dbus_message_unref(reply);
462 return JNI_TRUE;
463 }
464#endif
465 return JNI_FALSE;
466}
467
468static jboolean setPasskeyNative(JNIEnv *env, jobject object, jstring address,
469 int passkey, int nativeData) {
470#ifdef HAVE_BLUETOOTH
471 LOGV(__FUNCTION__);
472 native_data_t *nat = get_native_data(env, object);
473 if (nat) {
474 DBusMessage *msg = (DBusMessage *)nativeData;
475 DBusMessage *reply = dbus_message_new_method_return(msg);
476 if (!reply) {
477 LOGE("%s: Cannot create message reply to return Passkey code to "
478 "D-Bus\n", __FUNCTION__);
479 dbus_message_unref(msg);
480 return JNI_FALSE;
481 }
482
483 dbus_message_append_args(reply, DBUS_TYPE_UINT32, (uint32_t *)&passkey,
484 DBUS_TYPE_INVALID);
485
486 dbus_connection_send(nat->conn, reply, NULL);
487 dbus_message_unref(msg);
488 dbus_message_unref(reply);
489 return JNI_TRUE;
490 }
491#endif
492 return JNI_FALSE;
493}
494
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800495static jboolean setPinNative(JNIEnv *env, jobject object, jstring address,
496 jstring pin, int nativeData) {
497#ifdef HAVE_BLUETOOTH
498 LOGV(__FUNCTION__);
499 native_data_t *nat = get_native_data(env, object);
500 if (nat) {
501 DBusMessage *msg = (DBusMessage *)nativeData;
502 DBusMessage *reply = dbus_message_new_method_return(msg);
503 if (!reply) {
504 LOGE("%s: Cannot create message reply to return PIN code to "
505 "D-Bus\n", __FUNCTION__);
506 dbus_message_unref(msg);
507 return JNI_FALSE;
508 }
509
510 const char *c_pin = env->GetStringUTFChars(pin, NULL);
511
512 dbus_message_append_args(reply, DBUS_TYPE_STRING, &c_pin,
513 DBUS_TYPE_INVALID);
514
515 dbus_connection_send(nat->conn, reply, NULL);
516 dbus_message_unref(msg);
517 dbus_message_unref(reply);
518 env->ReleaseStringUTFChars(pin, c_pin);
519 return JNI_TRUE;
520 }
521#endif
522 return JNI_FALSE;
523}
524
Jaikumar Ganeshb0eca412009-07-16 18:26:28 -0700525static jboolean cancelPairingUserInputNative(JNIEnv *env, jobject object,
526 jstring address, int nativeData) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800527#ifdef HAVE_BLUETOOTH
528 LOGV(__FUNCTION__);
529 native_data_t *nat = get_native_data(env, object);
530 if (nat) {
531 DBusMessage *msg = (DBusMessage *)nativeData;
532 DBusMessage *reply = dbus_message_new_error(msg,
Jaikumar Ganeshb0eca412009-07-16 18:26:28 -0700533 "org.bluez.Error.Canceled", "Pairing User Input was canceled");
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800534 if (!reply) {
Jaikumar Ganeshb0eca412009-07-16 18:26:28 -0700535 LOGE("%s: Cannot create message reply to return cancelUserInput to"
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800536 "D-BUS\n", __FUNCTION__);
537 dbus_message_unref(msg);
538 return JNI_FALSE;
539 }
540
541 dbus_connection_send(nat->conn, reply, NULL);
542 dbus_message_unref(msg);
543 dbus_message_unref(reply);
544 return JNI_TRUE;
545 }
546#endif
547 return JNI_FALSE;
548}
549
Jaikumar Ganeshd5ac1ae2009-05-05 22:26:12 -0700550static jobjectArray getDevicePropertiesNative(JNIEnv *env, jobject object,
551 jstring path)
552{
553#ifdef HAVE_BLUETOOTH
554 LOGV(__FUNCTION__);
555 native_data_t *nat = get_native_data(env, object);
556 if (nat) {
557 DBusMessage *msg, *reply;
558 DBusError err;
559 dbus_error_init(&err);
560
561 const char *c_path = env->GetStringUTFChars(path, NULL);
562 reply = dbus_func_args_timeout(env,
563 nat->conn, -1, c_path,
564 DBUS_DEVICE_IFACE, "GetProperties",
565 DBUS_TYPE_INVALID);
566 env->ReleaseStringUTFChars(path, c_path);
567
568 if (!reply) {
569 if (dbus_error_is_set(&err)) {
570 LOG_AND_FREE_DBUS_ERROR(&err);
571 } else
572 LOGE("DBus reply is NULL in function %s", __FUNCTION__);
573 return NULL;
574 }
Jaikumar Ganesh6d56b532009-08-24 17:11:11 -0700575 env->PushLocalFrame(PROPERTIES_NREFS);
576
Jaikumar Ganeshd5ac1ae2009-05-05 22:26:12 -0700577 DBusMessageIter iter;
578 jobjectArray str_array = NULL;
579 if (dbus_message_iter_init(reply, &iter))
580 str_array = parse_remote_device_properties(env, &iter);
581 dbus_message_unref(reply);
Jaikumar Ganesh6d56b532009-08-24 17:11:11 -0700582
583 env->PopLocalFrame(NULL);
584
Jaikumar Ganeshd5ac1ae2009-05-05 22:26:12 -0700585 return str_array;
586 }
587#endif
588 return NULL;
589}
590
591static jobjectArray getAdapterPropertiesNative(JNIEnv *env, jobject object) {
592#ifdef HAVE_BLUETOOTH
593 LOGV(__FUNCTION__);
594 native_data_t *nat = get_native_data(env, object);
595 if (nat) {
596 DBusMessage *msg, *reply;
597 DBusError err;
598 dbus_error_init(&err);
599
600 reply = dbus_func_args_timeout(env,
601 nat->conn, -1, get_adapter_path(env, object),
602 DBUS_ADAPTER_IFACE, "GetProperties",
603 DBUS_TYPE_INVALID);
604 if (!reply) {
605 if (dbus_error_is_set(&err)) {
606 LOG_AND_FREE_DBUS_ERROR(&err);
607 } else
608 LOGE("DBus reply is NULL in function %s", __FUNCTION__);
609 return NULL;
610 }
Jaikumar Ganesh6d56b532009-08-24 17:11:11 -0700611 env->PushLocalFrame(PROPERTIES_NREFS);
612
Jaikumar Ganeshd5ac1ae2009-05-05 22:26:12 -0700613 DBusMessageIter iter;
614 jobjectArray str_array = NULL;
615 if (dbus_message_iter_init(reply, &iter))
616 str_array = parse_adapter_properties(env, &iter);
617 dbus_message_unref(reply);
Jaikumar Ganesh6d56b532009-08-24 17:11:11 -0700618
619 env->PopLocalFrame(NULL);
Jaikumar Ganeshd5ac1ae2009-05-05 22:26:12 -0700620 return str_array;
621 }
622#endif
623 return NULL;
624}
625
626static jboolean setAdapterPropertyNative(JNIEnv *env, jobject object, jstring key,
627 void *value, jint type) {
628#ifdef HAVE_BLUETOOTH
629 LOGV(__FUNCTION__);
630 native_data_t *nat = get_native_data(env, object);
631 if (nat) {
632 DBusMessage *reply, *msg;
633 DBusMessageIter iter;
634 DBusError err;
635 const char *c_key = env->GetStringUTFChars(key, NULL);
636 dbus_error_init(&err);
637
638 msg = dbus_message_new_method_call(BLUEZ_DBUS_BASE_IFC,
639 get_adapter_path(env, object),
640 DBUS_ADAPTER_IFACE, "SetProperty");
641 if (!msg) {
642 LOGE("%s: Can't allocate new method call for GetProperties!",
643 __FUNCTION__);
Lixin Yueefa1dd72009-08-31 15:55:13 +0800644 env->ReleaseStringUTFChars(key, c_key);
Jaikumar Ganeshd5ac1ae2009-05-05 22:26:12 -0700645 return JNI_FALSE;
646 }
647
648 dbus_message_append_args(msg, DBUS_TYPE_STRING, &c_key, DBUS_TYPE_INVALID);
649 dbus_message_iter_init_append(msg, &iter);
650 append_variant(&iter, type, value);
651
652 reply = dbus_connection_send_with_reply_and_block(nat->conn, msg, -1, &err);
653 dbus_message_unref(msg);
654
655 env->ReleaseStringUTFChars(key, c_key);
656
657 if (!reply) {
658 if (dbus_error_is_set(&err)) {
659 LOG_AND_FREE_DBUS_ERROR(&err);
660 } else
661 LOGE("DBus reply is NULL in function %s", __FUNCTION__);
662 return JNI_FALSE;
663 }
664 return JNI_TRUE;
665 }
666#endif
667 return JNI_FALSE;
668}
669
670static jboolean setAdapterPropertyStringNative(JNIEnv *env, jobject object, jstring key,
671 jstring value) {
Jaikumar Ganeshd4613492009-06-09 23:32:42 -0700672#ifdef HAVE_BLUETOOTH
Jaikumar Ganeshd5ac1ae2009-05-05 22:26:12 -0700673 const char *c_value = env->GetStringUTFChars(value, NULL);
674 jboolean ret = setAdapterPropertyNative(env, object, key, (void *)&c_value, DBUS_TYPE_STRING);
675 env->ReleaseStringUTFChars(value, (char *)c_value);
676 return ret;
Jaikumar Ganeshd4613492009-06-09 23:32:42 -0700677#else
678 return JNI_FALSE;
679#endif
Jaikumar Ganeshd5ac1ae2009-05-05 22:26:12 -0700680}
681
682static jboolean setAdapterPropertyIntegerNative(JNIEnv *env, jobject object, jstring key,
683 jint value) {
Jaikumar Ganeshd4613492009-06-09 23:32:42 -0700684#ifdef HAVE_BLUETOOTH
Jaikumar Ganeshd5ac1ae2009-05-05 22:26:12 -0700685 return setAdapterPropertyNative(env, object, key, (void *)&value, DBUS_TYPE_UINT32);
Jaikumar Ganeshd4613492009-06-09 23:32:42 -0700686#else
687 return JNI_FALSE;
688#endif
Jaikumar Ganeshd5ac1ae2009-05-05 22:26:12 -0700689}
690
691static jboolean setAdapterPropertyBooleanNative(JNIEnv *env, jobject object, jstring key,
692 jint value) {
Jaikumar Ganeshd4613492009-06-09 23:32:42 -0700693#ifdef HAVE_BLUETOOTH
Jaikumar Ganeshd5ac1ae2009-05-05 22:26:12 -0700694 return setAdapterPropertyNative(env, object, key, (void *)&value, DBUS_TYPE_BOOLEAN);
Jaikumar Ganeshd4613492009-06-09 23:32:42 -0700695#else
696 return JNI_FALSE;
697#endif
Jaikumar Ganeshd5ac1ae2009-05-05 22:26:12 -0700698}
699
Lixin Yueefa1dd72009-08-31 15:55:13 +0800700static jboolean setDevicePropertyNative(JNIEnv *env, jobject object, jstring path,
701 jstring key, void *value, jint type) {
702#ifdef HAVE_BLUETOOTH
703 LOGV(__FUNCTION__);
704 native_data_t *nat = get_native_data(env, object);
705 if (nat) {
706 DBusMessage *reply, *msg;
707 DBusMessageIter iter;
708 DBusError err;
709
710 const char *c_key = env->GetStringUTFChars(key, NULL);
711 const char *c_path = env->GetStringUTFChars(path, NULL);
712
713 dbus_error_init(&err);
714 msg = dbus_message_new_method_call(BLUEZ_DBUS_BASE_IFC,
715 c_path, DBUS_DEVICE_IFACE, "SetProperty");
716 if (!msg) {
717 LOGE("%s: Can't allocate new method call for device SetProperty!", __FUNCTION__);
718 env->ReleaseStringUTFChars(key, c_key);
719 env->ReleaseStringUTFChars(path, c_path);
720 return JNI_FALSE;
721 }
722
723 dbus_message_append_args(msg, DBUS_TYPE_STRING, &c_key, DBUS_TYPE_INVALID);
724 dbus_message_iter_init_append(msg, &iter);
725 append_variant(&iter, type, value);
726
727 reply = dbus_connection_send_with_reply_and_block(nat->conn, msg, -1, &err);
728 dbus_message_unref(msg);
729
730 env->ReleaseStringUTFChars(key, c_key);
731 env->ReleaseStringUTFChars(path, c_path);
732 if (!reply) {
733 if (dbus_error_is_set(&err)) {
734 LOG_AND_FREE_DBUS_ERROR(&err);
735 } else
736 LOGE("DBus reply is NULL in function %s", __FUNCTION__);
737 return JNI_FALSE;
738 }
739 return JNI_TRUE;
740 }
741#endif
742 return JNI_FALSE;
743}
744
745static jboolean setDevicePropertyBooleanNative(JNIEnv *env, jobject object,
746 jstring path, jstring key, jint value) {
747#ifdef HAVE_BLUETOOTH
748 return setDevicePropertyNative(env, object, path, key,
749 (void *)&value, DBUS_TYPE_BOOLEAN);
750#else
751 return JNI_FALSE;
752#endif
753}
Jaikumar Ganeshd5ac1ae2009-05-05 22:26:12 -0700754
Jaikumar Ganesh1caa6d12009-09-18 11:32:54 -0700755
756static jboolean createDeviceNative(JNIEnv *env, jobject object,
757 jstring address) {
758 LOGV(__FUNCTION__);
759#ifdef HAVE_BLUETOOTH
760 native_data_t *nat = get_native_data(env, object);
761 jobject eventLoop = env->GetObjectField(object, field_mEventLoop);
762 struct event_loop_native_data_t *eventLoopNat =
763 get_EventLoop_native_data(env, eventLoop);
764
765 if (nat && eventLoopNat) {
766 const char *c_address = env->GetStringUTFChars(address, NULL);
767 LOGV("... address = %s", c_address);
768 char *context_address = (char *)calloc(BTADDR_SIZE, sizeof(char));
769 strlcpy(context_address, c_address, BTADDR_SIZE); // for callback
770
771 bool ret = dbus_func_args_async(env, nat->conn, -1,
772 onCreateDeviceResult,
773 context_address,
774 eventLoopNat,
775 get_adapter_path(env, object),
776 DBUS_ADAPTER_IFACE,
777 "CreateDevice",
778 DBUS_TYPE_STRING, &c_address,
779 DBUS_TYPE_INVALID);
780 env->ReleaseStringUTFChars(address, c_address);
781 return ret ? JNI_TRUE : JNI_FALSE;
782 }
783#endif
784 return JNI_FALSE;
785}
786
787static jboolean discoverServicesNative(JNIEnv *env, jobject object,
788 jstring path, jstring pattern) {
789 LOGV(__FUNCTION__);
790#ifdef HAVE_BLUETOOTH
791 native_data_t *nat = get_native_data(env, object);
792 jobject eventLoop = env->GetObjectField(object, field_mEventLoop);
793 struct event_loop_native_data_t *eventLoopNat =
794 get_EventLoop_native_data(env, eventLoop);
795
796 if (nat && eventLoopNat) {
797 const char *c_path = env->GetStringUTFChars(path, NULL);
798 const char *c_pattern = env->GetStringUTFChars(pattern, NULL);
799 int len = env->GetStringLength(path) + 1;
800 char *context_path = (char *)calloc(len, sizeof(char));
801 strlcpy(context_path, c_path, len); // for callback
802
803 LOGV("... Object Path = %s", c_path);
804 LOGV("... Pattern = %s, strlen = %d", c_pattern, strlen(c_pattern));
805
806 bool ret = dbus_func_args_async(env, nat->conn, -1,
807 onDiscoverServicesResult,
808 context_path,
809 eventLoopNat,
810 c_path,
811 DBUS_DEVICE_IFACE,
812 "DiscoverServices",
813 DBUS_TYPE_STRING, &c_pattern,
814 DBUS_TYPE_INVALID);
815 env->ReleaseStringUTFChars(path, c_path);
816 env->ReleaseStringUTFChars(pattern, c_pattern);
817 return ret ? JNI_TRUE : JNI_FALSE;
818 }
819#endif
820 return JNI_FALSE;
821}
822
Nick Pelly24bb9b82009-10-02 20:34:18 -0700823static jint addRfcommServiceRecordNative(JNIEnv *env, jobject object,
824 jstring name, jlong uuidMsb, jlong uuidLsb, jshort channel) {
825 LOGV(__FUNCTION__);
826#ifdef HAVE_BLUETOOTH
827 native_data_t *nat = get_native_data(env, object);
828 if (nat) {
829 const char *c_name = env->GetStringUTFChars(name, NULL);
830 LOGV("... name = %s", c_name);
831 LOGV("... uuid1 = %llX", uuidMsb);
832 LOGV("... uuid2 = %llX", uuidLsb);
833 LOGV("... channel = %d", channel);
834 DBusMessage *reply = dbus_func_args(env, nat->conn,
835 get_adapter_path(env, object),
836 DBUS_ADAPTER_IFACE, "AddRfcommServiceRecord",
837 DBUS_TYPE_STRING, &c_name,
838 DBUS_TYPE_UINT64, &uuidMsb,
839 DBUS_TYPE_UINT64, &uuidLsb,
840 DBUS_TYPE_UINT16, &channel,
841 DBUS_TYPE_INVALID);
842 env->ReleaseStringUTFChars(name, c_name);
843 return reply ? dbus_returns_uint32(env, reply) : -1;
844 }
845#endif
846 return -1;
847}
848
849static jboolean removeServiceRecordNative(JNIEnv *env, jobject object, jint handle) {
850 LOGV(__FUNCTION__);
851#ifdef HAVE_BLUETOOTH
852 native_data_t *nat = get_native_data(env, object);
853 if (nat) {
854 LOGV("... handle = %X", handle);
855 DBusMessage *reply = dbus_func_args(env, nat->conn,
856 get_adapter_path(env, object),
857 DBUS_ADAPTER_IFACE, "RemoveServiceRecord",
858 DBUS_TYPE_UINT32, &handle,
859 DBUS_TYPE_INVALID);
860 return reply ? JNI_TRUE : JNI_FALSE;
861 }
862#endif
863 return JNI_FALSE;
864}
Jaikumar Ganesh1caa6d12009-09-18 11:32:54 -0700865
Jaikumar Ganeshb7e029d2010-03-09 15:31:24 -0800866static jboolean setLinkTimeoutNative(JNIEnv *env, jobject object, jstring object_path,
867 jint num_slots) {
868 LOGV(__FUNCTION__);
869#ifdef HAVE_BLUETOOTH
870 native_data_t *nat = get_native_data(env, object);
871 if (nat) {
872 const char *c_object_path = env->GetStringUTFChars(object_path, NULL);
873 DBusMessage *reply = dbus_func_args(env, nat->conn,
874 get_adapter_path(env, object),
875 DBUS_ADAPTER_IFACE, "SetLinkTimeout",
876 DBUS_TYPE_OBJECT_PATH, &c_object_path,
877 DBUS_TYPE_UINT32, &num_slots,
878 DBUS_TYPE_INVALID);
879 env->ReleaseStringUTFChars(object_path, c_object_path);
880 return reply ? JNI_TRUE : JNI_FALSE;
881 }
882#endif
883 return JNI_FALSE;
884}
885
Jaikumar Ganesh545e6702010-06-04 10:23:03 -0700886static jboolean connectInputDeviceNative(JNIEnv *env, jobject object, jstring path) {
887 LOGV(__FUNCTION__);
888#ifdef HAVE_BLUETOOTH
889 native_data_t *nat = get_native_data(env, object);
890 if (nat) {
891 const char *c_path = env->GetStringUTFChars(path, NULL);
892
893 bool ret = dbus_func_args_async(env, nat->conn, -1, NULL, NULL, nat,
894 c_path, DBUS_INPUT_IFACE, "Connect",
895 DBUS_TYPE_INVALID);
896
897 env->ReleaseStringUTFChars(path, c_path);
898 return ret ? JNI_TRUE : JNI_FALSE;
899 }
900#endif
901 return JNI_FALSE;
902}
903
904static jboolean disconnectInputDeviceNative(JNIEnv *env, jobject object,
905 jstring path) {
906 LOGV(__FUNCTION__);
907#ifdef HAVE_BLUETOOTH
908 native_data_t *nat = get_native_data(env, object);
909 if (nat) {
910 const char *c_path = env->GetStringUTFChars(path, NULL);
911
912 bool ret = dbus_func_args_async(env, nat->conn, -1, NULL, NULL, nat,
913 c_path, DBUS_INPUT_IFACE, "Disconnect",
914 DBUS_TYPE_INVALID);
915
916 env->ReleaseStringUTFChars(path, c_path);
917 return ret ? JNI_TRUE : JNI_FALSE;
918 }
919#endif
920 return JNI_FALSE;
921}
922
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800923static JNINativeMethod sMethods[] = {
924 /* name, signature, funcPtr */
925 {"classInitNative", "()V", (void*)classInitNative},
926 {"initializeNativeDataNative", "()V", (void *)initializeNativeDataNative},
Jaikumar Ganeshd5ac1ae2009-05-05 22:26:12 -0700927 {"setupNativeDataNative", "()Z", (void *)setupNativeDataNative},
928 {"tearDownNativeDataNative", "()Z", (void *)tearDownNativeDataNative},
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800929 {"cleanupNativeDataNative", "()V", (void *)cleanupNativeDataNative},
930 {"getAdapterPathNative", "()Ljava/lang/String;", (void*)getAdapterPathNative},
931
932 {"isEnabledNative", "()I", (void *)isEnabledNative},
933 {"enableNative", "()I", (void *)enableNative},
934 {"disableNative", "()I", (void *)disableNative},
935
Jaikumar Ganeshd5ac1ae2009-05-05 22:26:12 -0700936 {"getAdapterPropertiesNative", "()[Ljava/lang/Object;", (void *)getAdapterPropertiesNative},
937 {"getDevicePropertiesNative", "(Ljava/lang/String;)[Ljava/lang/Object;",
938 (void *)getDevicePropertiesNative},
939 {"setAdapterPropertyStringNative", "(Ljava/lang/String;Ljava/lang/String;)Z",
940 (void *)setAdapterPropertyStringNative},
941 {"setAdapterPropertyBooleanNative", "(Ljava/lang/String;I)Z",
942 (void *)setAdapterPropertyBooleanNative},
943 {"setAdapterPropertyIntegerNative", "(Ljava/lang/String;I)Z",
944 (void *)setAdapterPropertyIntegerNative},
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800945
Jaikumar Ganeshd5ac1ae2009-05-05 22:26:12 -0700946 {"startDiscoveryNative", "()Z", (void*)startDiscoveryNative},
947 {"stopDiscoveryNative", "()Z", (void *)stopDiscoveryNative},
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800948
Jaikumar Ganeshd5ac1ae2009-05-05 22:26:12 -0700949 {"createPairedDeviceNative", "(Ljava/lang/String;I)Z", (void *)createPairedDeviceNative},
950 {"cancelDeviceCreationNative", "(Ljava/lang/String;)Z", (void *)cancelDeviceCreationNative},
951 {"removeDeviceNative", "(Ljava/lang/String;)Z", (void *)removeDeviceNative},
952 {"getDeviceServiceChannelNative", "(Ljava/lang/String;Ljava/lang/String;I)I",
953 (void *)getDeviceServiceChannelNative},
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800954
Jaikumar Ganeshb0eca412009-07-16 18:26:28 -0700955 {"setPairingConfirmationNative", "(Ljava/lang/String;ZI)Z",
956 (void *)setPairingConfirmationNative},
957 {"setPasskeyNative", "(Ljava/lang/String;II)Z", (void *)setPasskeyNative},
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800958 {"setPinNative", "(Ljava/lang/String;Ljava/lang/String;I)Z", (void *)setPinNative},
Jaikumar Ganeshb0eca412009-07-16 18:26:28 -0700959 {"cancelPairingUserInputNative", "(Ljava/lang/String;I)Z",
960 (void *)cancelPairingUserInputNative},
Lixin Yueefa1dd72009-08-31 15:55:13 +0800961 {"setDevicePropertyBooleanNative", "(Ljava/lang/String;Ljava/lang/String;I)Z",
962 (void *)setDevicePropertyBooleanNative},
Jaikumar Ganesh1caa6d12009-09-18 11:32:54 -0700963 {"createDeviceNative", "(Ljava/lang/String;)Z", (void *)createDeviceNative},
964 {"discoverServicesNative", "(Ljava/lang/String;Ljava/lang/String;)Z", (void *)discoverServicesNative},
Nick Pelly24bb9b82009-10-02 20:34:18 -0700965 {"addRfcommServiceRecordNative", "(Ljava/lang/String;JJS)I", (void *)addRfcommServiceRecordNative},
966 {"removeServiceRecordNative", "(I)Z", (void *)removeServiceRecordNative},
Jaikumar Ganeshb7e029d2010-03-09 15:31:24 -0800967 {"setLinkTimeoutNative", "(Ljava/lang/String;I)Z", (void *)setLinkTimeoutNative},
Jaikumar Ganesh545e6702010-06-04 10:23:03 -0700968 // HID functions
969 {"connectInputDeviceNative", "(Ljava/lang/String;)Z", (void *)connectInputDeviceNative},
970 {"disconnectInputDeviceNative", "(Ljava/lang/String;)Z", (void *)disconnectInputDeviceNative},
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800971};
972
Nick Pellybd022f42009-08-14 18:33:38 -0700973int register_android_server_BluetoothService(JNIEnv *env) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800974 return AndroidRuntime::registerNativeMethods(env,
Nick Pellybd022f42009-08-14 18:33:38 -0700975 "android/server/BluetoothService", sMethods, NELEM(sMethods));
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800976}
977
978} /* namespace android */