| /* |
| ** Copyright 2006, The Android Open Source Project |
| ** |
| ** Licensed under the Apache License, Version 2.0 (the "License"); |
| ** you may not use this file except in compliance with the License. |
| ** You may obtain a copy of the License at |
| ** |
| ** http://www.apache.org/licenses/LICENSE-2.0 |
| ** |
| ** Unless required by applicable law or agreed to in writing, software |
| ** distributed under the License is distributed on an "AS IS" BASIS, |
| ** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| ** See the License for the specific language governing permissions and |
| ** limitations under the License. |
| */ |
| |
| #define DBUS_CLASS_NAME BLUEZ_DBUS_BASE_IFC ".Adapter" |
| #define LOG_TAG "BluetoothDeviceService.cpp" |
| |
| #include "android_bluetooth_common.h" |
| #include "android_runtime/AndroidRuntime.h" |
| #include "JNIHelp.h" |
| #include "jni.h" |
| #include "utils/Log.h" |
| #include "utils/misc.h" |
| |
| #include <ctype.h> |
| #include <stdio.h> |
| #include <string.h> |
| #include <stdlib.h> |
| #include <errno.h> |
| #include <unistd.h> |
| |
| #include <sys/socket.h> |
| #include <sys/ioctl.h> |
| #include <fcntl.h> |
| |
| #ifdef HAVE_BLUETOOTH |
| #include <dbus/dbus.h> |
| #include <bluedroid/bluetooth.h> |
| #endif |
| |
| #include <cutils/properties.h> |
| |
| namespace android { |
| |
| #define BLUETOOTH_CLASS_ERROR 0xFF000000 |
| |
| #ifdef HAVE_BLUETOOTH |
| // We initialize these variables when we load class |
| // android.server.BluetoothDeviceService |
| static jfieldID field_mNativeData; |
| static jfieldID field_mEventLoop; |
| |
| typedef struct { |
| JNIEnv *env; |
| DBusConnection *conn; |
| const char *adapter; // dbus object name of the local adapter |
| } native_data_t; |
| |
| extern event_loop_native_data_t *get_EventLoop_native_data(JNIEnv *, |
| jobject); |
| void onCreateBondingResult(DBusMessage *msg, void *user, void *nat); |
| void onGetRemoteServiceChannelResult(DBusMessage *msg, void *user, void *nat); |
| |
| /** Get native data stored in the opaque (Java code maintained) pointer mNativeData |
| * Perform quick sanity check, if there are any problems return NULL |
| */ |
| static inline native_data_t * get_native_data(JNIEnv *env, jobject object) { |
| native_data_t *nat = |
| (native_data_t *)(env->GetIntField(object, field_mNativeData)); |
| if (nat == NULL || nat->conn == NULL) { |
| LOGE("Uninitialized native data\n"); |
| return NULL; |
| } |
| return nat; |
| } |
| #endif |
| |
| static void classInitNative(JNIEnv* env, jclass clazz) { |
| LOGV(__FUNCTION__); |
| #ifdef HAVE_BLUETOOTH |
| field_mNativeData = get_field(env, clazz, "mNativeData", "I"); |
| field_mEventLoop = get_field(env, clazz, "mEventLoop", |
| "Landroid/server/BluetoothEventLoop;"); |
| #endif |
| } |
| |
| /* Returns true on success (even if adapter is present but disabled). |
| * Return false if dbus is down, or another serious error (out of memory) |
| */ |
| static bool initializeNativeDataNative(JNIEnv* env, jobject object) { |
| LOGV(__FUNCTION__); |
| #ifdef HAVE_BLUETOOTH |
| native_data_t *nat = (native_data_t *)calloc(1, sizeof(native_data_t)); |
| if (NULL == nat) { |
| LOGE("%s: out of memory!", __FUNCTION__); |
| return false; |
| } |
| nat->env = env; |
| |
| env->SetIntField(object, field_mNativeData, (jint)nat); |
| DBusError err; |
| dbus_error_init(&err); |
| dbus_threads_init_default(); |
| nat->conn = dbus_bus_get(DBUS_BUS_SYSTEM, &err); |
| if (dbus_error_is_set(&err)) { |
| LOGE("Could not get onto the system bus: %s", err.message); |
| dbus_error_free(&err); |
| return false; |
| } |
| dbus_connection_set_exit_on_disconnect(nat->conn, FALSE); |
| |
| nat->adapter = BLUEZ_ADAPTER_OBJECT_NAME; |
| #endif /*HAVE_BLUETOOTH*/ |
| return true; |
| } |
| |
| static void cleanupNativeDataNative(JNIEnv* env, jobject object) { |
| LOGV(__FUNCTION__); |
| #ifdef HAVE_BLUETOOTH |
| native_data_t *nat = |
| (native_data_t *)env->GetIntField(object, field_mNativeData); |
| if (nat) { |
| free(nat); |
| nat = NULL; |
| } |
| #endif |
| } |
| |
| static jstring getNameNative(JNIEnv *env, jobject object){ |
| LOGV(__FUNCTION__); |
| #ifdef HAVE_BLUETOOTH |
| native_data_t *nat = get_native_data(env, object); |
| if (nat) { |
| DBusMessage *reply = dbus_func_args(env, nat->conn, nat->adapter, |
| DBUS_CLASS_NAME, "GetName", |
| DBUS_TYPE_INVALID); |
| return reply ? dbus_returns_string(env, reply) : NULL; |
| } |
| #endif |
| return NULL; |
| } |
| |
| static jstring getAdapterPathNative(JNIEnv *env, jobject object) { |
| LOGV(__FUNCTION__); |
| #ifdef HAVE_BLUETOOTH |
| native_data_t *nat = get_native_data(env, object); |
| if (nat) { |
| return (env->NewStringUTF(nat->adapter)); |
| } |
| #endif |
| return NULL; |
| } |
| |
| |
| static jboolean startDiscoveryNative(JNIEnv *env, jobject object) { |
| LOGV(__FUNCTION__); |
| #ifdef HAVE_BLUETOOTH |
| DBusMessage *msg = NULL; |
| DBusMessage *reply = NULL; |
| DBusError err; |
| const char *name; |
| jboolean ret = JNI_FALSE; |
| |
| native_data_t *nat = get_native_data(env, object); |
| if (nat == NULL) { |
| goto done; |
| } |
| |
| dbus_error_init(&err); |
| |
| /* Compose the command */ |
| msg = dbus_message_new_method_call(BLUEZ_DBUS_BASE_IFC, nat->adapter, |
| DBUS_CLASS_NAME, "DiscoverDevices"); |
| |
| if (msg == NULL) { |
| LOGE("%s: Could not allocate D-Bus message object!", __FUNCTION__); |
| goto done; |
| } |
| |
| /* Send the command. */ |
| reply = dbus_connection_send_with_reply_and_block(nat->conn, msg, -1, &err); |
| if (dbus_error_is_set(&err)) { |
| /* We treat the in-progress error code as success. */ |
| if(strcmp(err.message, BLUEZ_DBUS_BASE_IFC ".Error.InProgress") == 0) { |
| LOGW("%s: D-Bus error: %s, treating as startDiscoveryNative success\n", |
| __FUNCTION__, err.message); |
| ret = JNI_TRUE; |
| goto done; |
| } else { |
| LOGE("%s: D-Bus error: %s (%s)\n", __FUNCTION__, err.name, err.message); |
| ret = JNI_FALSE; |
| goto done; |
| } |
| } |
| |
| ret = JNI_TRUE; |
| done: |
| if (reply) dbus_message_unref(reply); |
| if (msg) dbus_message_unref(msg); |
| return ret; |
| #else |
| return JNI_FALSE; |
| #endif |
| } |
| |
| static void cancelDiscoveryNative(JNIEnv *env, jobject object) { |
| LOGV(__FUNCTION__); |
| #ifdef HAVE_BLUETOOTH |
| DBusMessage *msg = NULL; |
| DBusMessage *reply = NULL; |
| DBusError err; |
| const char *name; |
| jstring ret; |
| native_data_t *nat; |
| |
| dbus_error_init(&err); |
| |
| nat = get_native_data(env, object); |
| if (nat == NULL) { |
| goto done; |
| } |
| |
| /* Compose the command */ |
| msg = dbus_message_new_method_call(BLUEZ_DBUS_BASE_IFC, nat->adapter, |
| DBUS_CLASS_NAME, "CancelDiscovery"); |
| |
| if (msg == NULL) { |
| LOGE("%s: Could not allocate D-Bus message object!", __FUNCTION__); |
| goto done; |
| } |
| |
| /* Send the command. */ |
| reply = dbus_connection_send_with_reply_and_block(nat->conn, msg, -1, &err); |
| if (dbus_error_is_set(&err)) { |
| if(strcmp(err.name, BLUEZ_DBUS_BASE_IFC ".Error.NotAuthorized") == 0) { |
| // hcid sends this if there is no active discovery to cancel |
| LOGV("%s: There was no active discovery to cancel", __FUNCTION__); |
| dbus_error_free(&err); |
| } else { |
| LOG_AND_FREE_DBUS_ERROR_WITH_MSG(&err, msg); |
| } |
| } |
| |
| done: |
| if (msg) dbus_message_unref(msg); |
| if (reply) dbus_message_unref(reply); |
| #endif |
| } |
| |
| static jboolean startPeriodicDiscoveryNative(JNIEnv *env, jobject object) { |
| LOGV(__FUNCTION__); |
| #ifdef HAVE_BLUETOOTH |
| DBusMessage *msg = NULL; |
| DBusMessage *reply = NULL; |
| DBusError err; |
| jboolean ret = JNI_FALSE; |
| |
| native_data_t *nat = get_native_data(env, object); |
| if (nat == NULL) { |
| goto done; |
| } |
| |
| dbus_error_init(&err); |
| msg = dbus_message_new_method_call(BLUEZ_DBUS_BASE_IFC, nat->adapter, |
| DBUS_CLASS_NAME, "StartPeriodicDiscovery"); |
| if (msg == NULL) { |
| LOGE("%s: Could not allocate DBUS message object\n", __FUNCTION__); |
| goto done; |
| } |
| |
| /* Send the command. */ |
| reply = dbus_connection_send_with_reply_and_block(nat->conn, msg, -1, &err); |
| if (dbus_error_is_set(&err)) { |
| /* We treat the in-progress error code as success. */ |
| if(strcmp(err.name, BLUEZ_DBUS_BASE_IFC ".Error.InProgress") == 0) { |
| LOGW("%s: D-Bus error: %s (%s), treating as " |
| "startPeriodicDiscoveryNative success\n", |
| __FUNCTION__, err.name, err.message); |
| } else { |
| LOGE("%s: D-Bus error: %s (%s)\n", __FUNCTION__, err.name, err.message); |
| ret = JNI_FALSE; |
| goto done; |
| } |
| } |
| |
| ret = JNI_TRUE; |
| done: |
| if (reply) dbus_message_unref(reply); |
| if (msg) dbus_message_unref(msg); |
| return ret; |
| #else |
| return JNI_FALSE; |
| #endif |
| } |
| |
| static jboolean stopPeriodicDiscoveryNative(JNIEnv *env, jobject object) { |
| LOGV(__FUNCTION__); |
| #ifdef HAVE_BLUETOOTH |
| DBusMessage *msg = NULL; |
| DBusMessage *reply = NULL; |
| DBusError err; |
| const char *name; |
| jboolean ret = JNI_FALSE; |
| |
| native_data_t *nat = get_native_data(env, object); |
| if (nat == NULL) { |
| goto done; |
| } |
| |
| dbus_error_init(&err); |
| msg = dbus_message_new_method_call(BLUEZ_DBUS_BASE_IFC, nat->adapter, |
| DBUS_CLASS_NAME, "StopPeriodicDiscovery"); |
| if (msg == NULL) { |
| LOGE("%s: Could not allocate DBUS message object\n", __FUNCTION__); |
| goto done; |
| } |
| |
| /* Send the command. */ |
| reply = dbus_connection_send_with_reply_and_block(nat->conn, msg, -1, &err); |
| if (dbus_error_is_set(&err)) { |
| /* We treat the in-progress error code as success. */ |
| if(strcmp(err.name, BLUEZ_DBUS_BASE_IFC ".Error.InProgress") == 0) { |
| LOGW("%s: D-Bus error: %s (%s), treating as " |
| "stopPeriodicDiscoveryNative success\n", |
| __FUNCTION__, err.name, err.message); |
| } else { |
| LOGE("%s: D-Bus error: %s (%s)\n", __FUNCTION__, err.name, err.message); |
| ret = JNI_FALSE; |
| goto done; |
| } |
| } |
| |
| ret = JNI_TRUE; |
| done: |
| if (reply) dbus_message_unref(reply); |
| if (msg) dbus_message_unref(msg); |
| return ret; |
| #else |
| return JNI_FALSE; |
| #endif |
| } |
| |
| static jboolean isPeriodicDiscoveryNative(JNIEnv *env, jobject object) { |
| #ifdef HAVE_BLUETOOTH |
| LOGV(__FUNCTION__); |
| native_data_t *nat = get_native_data(env, object); |
| if (nat) { |
| DBusMessage *reply = |
| dbus_func_args(env, nat->conn, nat->adapter, |
| DBUS_CLASS_NAME, "IsPeriodicDiscovery", |
| DBUS_TYPE_INVALID); |
| return reply ? dbus_returns_boolean(env, reply) : JNI_FALSE; |
| } |
| #endif |
| return JNI_FALSE; |
| } |
| |
| static jboolean setDiscoverableTimeoutNative(JNIEnv *env, jobject object, jint timeout_s) { |
| #ifdef HAVE_BLUETOOTH |
| LOGV(__FUNCTION__); |
| |
| if (timeout_s < 0) { |
| return JNI_FALSE; |
| } |
| |
| native_data_t *nat = get_native_data(env, object); |
| if (nat) { |
| DBusMessage *reply = |
| dbus_func_args(env, nat->conn, nat->adapter, |
| DBUS_CLASS_NAME, "SetDiscoverableTimeout", |
| DBUS_TYPE_UINT32, &timeout_s, |
| DBUS_TYPE_INVALID); |
| if (reply != NULL) { |
| dbus_message_unref(reply); |
| return JNI_TRUE; |
| } |
| } |
| #endif |
| return JNI_FALSE; |
| } |
| |
| static jint getDiscoverableTimeoutNative(JNIEnv *env, jobject object) { |
| #ifdef HAVE_BLUETOOTH |
| LOGV(__FUNCTION__); |
| native_data_t *nat = get_native_data(env, object); |
| if (nat) { |
| DBusMessage *reply = |
| dbus_func_args(env, nat->conn, nat->adapter, |
| DBUS_CLASS_NAME, "GetDiscoverableTimeout", |
| DBUS_TYPE_INVALID); |
| return reply ? dbus_returns_uint32(env, reply) : -1; |
| } |
| #endif |
| return -1; |
| } |
| |
| static jboolean isConnectedNative(JNIEnv *env, jobject object, jstring address) { |
| #ifdef HAVE_BLUETOOTH |
| LOGV(__FUNCTION__); |
| native_data_t *nat = get_native_data(env, object); |
| if (nat) { |
| const char *c_address = env->GetStringUTFChars(address, NULL); |
| DBusMessage *reply = |
| dbus_func_args(env, nat->conn, nat->adapter, |
| DBUS_CLASS_NAME, "IsConnected", |
| DBUS_TYPE_STRING, &c_address, |
| DBUS_TYPE_INVALID); |
| env->ReleaseStringUTFChars(address, c_address); |
| return reply ? dbus_returns_boolean(env, reply) : JNI_FALSE; |
| } |
| #endif |
| return JNI_FALSE; |
| } |
| |
| static void disconnectRemoteDeviceNative(JNIEnv *env, jobject object, jstring address) { |
| #ifdef HAVE_BLUETOOTH |
| LOGV(__FUNCTION__); |
| native_data_t *nat = get_native_data(env, object); |
| if (nat) { |
| const char *c_address = env->GetStringUTFChars(address, NULL); |
| // Set a timeout of 5 seconds. Specifying the default timeout is |
| // not long enough, as a remote-device disconnect results in |
| // signal RemoteDisconnectRequested being sent, followed by a |
| // delay of 2 seconds, after which the actual disconnect takes |
| // place. |
| DBusMessage *reply = |
| dbus_func_args_timeout(env, nat->conn, 60000, nat->adapter, |
| DBUS_CLASS_NAME, "DisconnectRemoteDevice", |
| DBUS_TYPE_STRING, &c_address, |
| DBUS_TYPE_INVALID); |
| env->ReleaseStringUTFChars(address, c_address); |
| if (reply) dbus_message_unref(reply); |
| } |
| #endif |
| } |
| |
| static jstring getModeNative(JNIEnv *env, jobject object) { |
| #ifdef HAVE_BLUETOOTH |
| LOGV(__FUNCTION__); |
| native_data_t *nat = get_native_data(env, object); |
| if (nat) { |
| DBusMessage *reply = |
| dbus_func_args(env, nat->conn, nat->adapter, |
| DBUS_CLASS_NAME, "GetMode", |
| DBUS_TYPE_INVALID); |
| return reply ? dbus_returns_string(env, reply) : NULL; |
| } |
| #endif |
| return NULL; |
| } |
| |
| static jboolean setModeNative(JNIEnv *env, jobject object, jstring mode) { |
| #ifdef HAVE_BLUETOOTH |
| LOGV(__FUNCTION__); |
| native_data_t *nat = get_native_data(env, object); |
| if (nat) { |
| const char *c_mode = env->GetStringUTFChars(mode, NULL); |
| DBusMessage *reply = |
| dbus_func_args(env, nat->conn, nat->adapter, |
| DBUS_CLASS_NAME, "SetMode", |
| DBUS_TYPE_STRING, &c_mode, |
| DBUS_TYPE_INVALID); |
| env->ReleaseStringUTFChars(mode, c_mode); |
| if (reply) { |
| dbus_message_unref(reply); |
| return JNI_TRUE; |
| } |
| return JNI_FALSE; |
| } |
| #endif |
| return JNI_FALSE; |
| } |
| |
| static jboolean createBondingNative(JNIEnv *env, jobject object, |
| jstring address, jint timeout_ms) { |
| LOGV(__FUNCTION__); |
| #ifdef HAVE_BLUETOOTH |
| native_data_t *nat = get_native_data(env, object); |
| jobject eventLoop = env->GetObjectField(object, field_mEventLoop); |
| struct event_loop_native_data_t *eventLoopNat = |
| get_EventLoop_native_data(env, eventLoop); |
| |
| if (nat && eventLoopNat) { |
| const char *c_address = env->GetStringUTFChars(address, NULL); |
| LOGV("... address = %s", c_address); |
| char *context_address = (char *)calloc(BTADDR_SIZE, sizeof(char)); |
| strlcpy(context_address, c_address, BTADDR_SIZE); // for callback |
| bool ret = dbus_func_args_async(env, nat->conn, (int)timeout_ms, |
| onCreateBondingResult, // callback |
| context_address, |
| eventLoopNat, |
| nat->adapter, |
| DBUS_CLASS_NAME, "CreateBonding", |
| DBUS_TYPE_STRING, &c_address, |
| DBUS_TYPE_INVALID); |
| env->ReleaseStringUTFChars(address, c_address); |
| return ret ? JNI_TRUE : JNI_FALSE; |
| |
| } |
| #endif |
| return JNI_FALSE; |
| } |
| |
| static jboolean cancelBondingProcessNative(JNIEnv *env, jobject object, |
| jstring address) { |
| LOGV(__FUNCTION__); |
| #ifdef HAVE_BLUETOOTH |
| native_data_t *nat = get_native_data(env, object); |
| if (nat) { |
| const char *c_address = env->GetStringUTFChars(address, NULL); |
| LOGV("... address = %s", c_address); |
| DBusMessage *reply = |
| dbus_func_args_timeout(env, nat->conn, -1, nat->adapter, |
| DBUS_CLASS_NAME, "CancelBondingProcess", |
| DBUS_TYPE_STRING, &c_address, |
| DBUS_TYPE_INVALID); |
| env->ReleaseStringUTFChars(address, c_address); |
| if (reply) { |
| dbus_message_unref(reply); |
| } |
| return JNI_TRUE; |
| } |
| #endif |
| return JNI_FALSE; |
| } |
| |
| static jboolean removeBondingNative(JNIEnv *env, jobject object, jstring address) { |
| LOGV(__FUNCTION__); |
| jboolean result = JNI_FALSE; |
| #ifdef HAVE_BLUETOOTH |
| native_data_t *nat = get_native_data(env, object); |
| if (nat) { |
| const char *c_address = env->GetStringUTFChars(address, NULL); |
| LOGV("... address = %s", c_address); |
| DBusError err; |
| dbus_error_init(&err); |
| DBusMessage *reply = |
| dbus_func_args_error(env, nat->conn, &err, nat->adapter, |
| DBUS_CLASS_NAME, "RemoveBonding", |
| DBUS_TYPE_STRING, &c_address, |
| DBUS_TYPE_INVALID); |
| if (dbus_error_is_set(&err)) { |
| if (dbus_error_has_name(&err, |
| BLUEZ_DBUS_BASE_IFC ".Error.DoesNotExist")) { |
| LOGW("%s: Warning: %s (%s)", __FUNCTION__, err.message, |
| c_address); |
| result = JNI_TRUE; |
| } else { |
| LOGE("%s: D-Bus error %s (%s)", __FUNCTION__, err.name, |
| err.message); |
| } |
| } else { |
| result = JNI_TRUE; |
| } |
| |
| env->ReleaseStringUTFChars(address, c_address); |
| dbus_error_free(&err); |
| if (reply) dbus_message_unref(reply); |
| } |
| #endif |
| return result; |
| } |
| |
| static jobjectArray listBondingsNative(JNIEnv *env, jobject object) { |
| #ifdef HAVE_BLUETOOTH |
| LOGV(__FUNCTION__); |
| native_data_t *nat = get_native_data(env, object); |
| if (nat) { |
| DBusMessage *reply = |
| dbus_func_args(env, nat->conn, nat->adapter, |
| DBUS_CLASS_NAME, "ListBondings", |
| DBUS_TYPE_INVALID); |
| // return String[] |
| return reply ? dbus_returns_array_of_strings(env, reply) : NULL; |
| } |
| #endif |
| return NULL; |
| } |
| |
| static jobjectArray listConnectionsNative(JNIEnv *env, jobject object) { |
| #ifdef HAVE_BLUETOOTH |
| LOGV(__FUNCTION__); |
| native_data_t *nat = get_native_data(env, object); |
| if (nat) { |
| DBusMessage *reply = |
| dbus_func_args(env, nat->conn, nat->adapter, |
| DBUS_CLASS_NAME, "ListConnections", |
| DBUS_TYPE_INVALID); |
| // return String[] |
| return reply ? dbus_returns_array_of_strings(env, reply) : NULL; |
| } |
| #endif |
| return NULL; |
| } |
| |
| static jobjectArray listRemoteDevicesNative(JNIEnv *env, jobject object) { |
| #ifdef HAVE_BLUETOOTH |
| LOGV(__FUNCTION__); |
| native_data_t *nat = get_native_data(env, object); |
| if (nat) { |
| DBusMessage *reply = |
| dbus_func_args(env, nat->conn, nat->adapter, |
| DBUS_CLASS_NAME, "ListRemoteDevices", |
| DBUS_TYPE_INVALID); |
| return reply ? dbus_returns_array_of_strings(env, reply) : NULL; |
| } |
| #endif |
| return NULL; |
| } |
| |
| static jstring common_Get(JNIEnv *env, jobject object, const char *func) { |
| LOGV("%s:%s", __FUNCTION__, func); |
| #ifdef HAVE_BLUETOOTH |
| native_data_t *nat = get_native_data(env, object); |
| if (nat) { |
| DBusError err; |
| dbus_error_init(&err); |
| DBusMessage *reply = |
| dbus_func_args_error(env, nat->conn, &err, nat->adapter, |
| DBUS_CLASS_NAME, func, |
| DBUS_TYPE_INVALID); |
| if (reply) { |
| return dbus_returns_string(env, reply); |
| } else { |
| LOG_AND_FREE_DBUS_ERROR(&err); |
| return NULL; |
| } |
| } |
| #endif |
| return NULL; |
| } |
| |
| static jstring getAddressNative(JNIEnv *env, jobject obj) { |
| return common_Get(env, obj, "GetAddress"); |
| } |
| |
| static jstring getVersionNative(JNIEnv *env, jobject obj) { |
| return common_Get(env, obj, "GetVersion"); |
| } |
| |
| static jstring getRevisionNative(JNIEnv *env, jobject obj) { |
| return common_Get(env, obj, "GetRevision"); |
| } |
| |
| static jstring getManufacturerNative(JNIEnv *env, jobject obj) { |
| return common_Get(env, obj, "GetManufacturer"); |
| } |
| |
| static jstring getCompanyNative(JNIEnv *env, jobject obj) { |
| return common_Get(env, obj, "GetCompany"); |
| } |
| |
| static jboolean setNameNative(JNIEnv *env, jobject obj, jstring name) { |
| #ifdef HAVE_BLUETOOTH |
| LOGV(__FUNCTION__); |
| native_data_t *nat = get_native_data(env, obj); |
| if (nat) { |
| const char *c_name = env->GetStringUTFChars(name, NULL); |
| DBusMessage *reply = dbus_func_args(env, nat->conn, nat->adapter, |
| DBUS_CLASS_NAME, "SetName", |
| DBUS_TYPE_STRING, &c_name, |
| DBUS_TYPE_INVALID); |
| env->ReleaseStringUTFChars(name, c_name); |
| if (reply) { |
| dbus_message_unref(reply); |
| return JNI_TRUE; |
| } |
| } |
| #endif |
| return JNI_FALSE; |
| } |
| |
| static jstring common_getRemote(JNIEnv *env, jobject object, const char *func, |
| jstring address) { |
| LOGV("%s:%s", __FUNCTION__, func); |
| #ifdef HAVE_BLUETOOTH |
| native_data_t *nat = get_native_data(env, object); |
| if (nat) { |
| const char *c_address = env->GetStringUTFChars(address, NULL); |
| DBusError err; |
| dbus_error_init(&err); |
| |
| LOGV("... address = %s", c_address); |
| |
| DBusMessage *reply = |
| dbus_func_args_error(env, nat->conn, &err, nat->adapter, |
| DBUS_CLASS_NAME, func, |
| DBUS_TYPE_STRING, &c_address, |
| DBUS_TYPE_INVALID); |
| env->ReleaseStringUTFChars(address, c_address); |
| if (reply) { |
| return dbus_returns_string(env, reply); |
| } else if (!strcmp(func, "GetRemoteName") && |
| dbus_error_has_name(&err, "org.bluez.Error.RequestDeferred")) { |
| // This error occurs if we request name during device discovery, |
| // its fine |
| LOGV("... %s: %s", func, err.message); |
| dbus_error_free(&err); |
| return NULL; |
| } else { |
| LOG_AND_FREE_DBUS_ERROR(&err); |
| return NULL; |
| } |
| } |
| #endif |
| return NULL; |
| } |
| |
| static jstring getRemoteVersionNative(JNIEnv *env, jobject obj, jstring address) { |
| return common_getRemote(env, obj, "GetRemoteVersion", address); |
| } |
| |
| static jstring getRemoteRevisionNative(JNIEnv *env, jobject obj, jstring address) { |
| return common_getRemote(env, obj, "GetRemoteRevision", address); |
| } |
| |
| static jstring getRemoteManufacturerNative(JNIEnv *env, jobject obj, jstring address) { |
| return common_getRemote(env, obj, "GetRemoteManufacturer", address); |
| } |
| |
| static jstring getRemoteCompanyNative(JNIEnv *env, jobject obj, jstring address) { |
| return common_getRemote(env, obj, "GetRemoteCompany", address); |
| } |
| |
| static jstring getRemoteNameNative(JNIEnv *env, jobject obj, jstring address) { |
| return common_getRemote(env, obj, "GetRemoteName", address); |
| } |
| |
| static jstring lastSeenNative(JNIEnv *env, jobject obj, jstring address) { |
| return common_getRemote(env, obj, "LastSeen", address); |
| } |
| |
| static jstring lastUsedNative(JNIEnv *env, jobject obj, jstring address) { |
| return common_getRemote(env, obj, "LastUsed", address); |
| } |
| |
| static jint getRemoteClassNative(JNIEnv *env, jobject object, jstring address) { |
| jint result = BLUETOOTH_CLASS_ERROR; |
| #ifdef HAVE_BLUETOOTH |
| LOGV(__FUNCTION__); |
| native_data_t *nat = get_native_data(env, object); |
| if (nat) { |
| const char *c_address = env->GetStringUTFChars(address, NULL); |
| |
| LOGV("... address = %s", c_address); |
| |
| DBusMessage *reply = |
| dbus_func_args(env, nat->conn, nat->adapter, |
| DBUS_CLASS_NAME, "GetRemoteClass", |
| DBUS_TYPE_STRING, &c_address, |
| DBUS_TYPE_INVALID); |
| env->ReleaseStringUTFChars(address, c_address); |
| if (reply) |
| { |
| DBusError err; |
| dbus_error_init(&err); |
| if (!dbus_message_get_args(reply, &err, |
| DBUS_TYPE_UINT32, &result, |
| DBUS_TYPE_INVALID)) { |
| LOG_AND_FREE_DBUS_ERROR_WITH_MSG(&err, reply); |
| } |
| dbus_message_unref(reply); |
| } |
| } |
| #endif |
| return result; |
| } |
| |
| static jbyteArray getRemoteFeaturesNative(JNIEnv *env, jobject object, |
| jstring address) { |
| #ifdef HAVE_BLUETOOTH |
| LOGV(__FUNCTION__); |
| native_data_t *nat = get_native_data(env, object); |
| if (nat) { |
| const char *c_address = env->GetStringUTFChars(address, NULL); |
| |
| LOGV("... address = %s", c_address); |
| |
| DBusMessage *reply = |
| dbus_func_args(env, nat->conn, nat->adapter, |
| DBUS_CLASS_NAME, "GetRemoteFeatures", |
| DBUS_TYPE_STRING, &c_address, |
| DBUS_TYPE_INVALID); |
| env->ReleaseStringUTFChars(address, c_address); |
| /* array of DBUS_TYPE_BYTE_AS_STRING */ |
| return reply ? dbus_returns_array_of_bytes(env, reply) : NULL; |
| } |
| #endif |
| return NULL; |
| } |
| |
| static jintArray getRemoteServiceHandlesNative(JNIEnv *env, jobject object, |
| jstring address, jstring match) { |
| #ifdef HAVE_BLUETOOTH |
| LOGV(__FUNCTION__); |
| native_data_t *nat = get_native_data(env, object); |
| if (nat) { |
| jintArray intArray = NULL; |
| const char *c_address = env->GetStringUTFChars(address, NULL); |
| const char *c_match = env->GetStringUTFChars(match, NULL); |
| |
| LOGV("... address = %s match = %s", c_address, c_match); |
| |
| DBusMessage *reply = |
| dbus_func_args(env, nat->conn, nat->adapter, |
| DBUS_CLASS_NAME, "GetRemoteServiceHandles", |
| DBUS_TYPE_STRING, &c_address, |
| DBUS_TYPE_STRING, &c_match, |
| DBUS_TYPE_INVALID); |
| env->ReleaseStringUTFChars(address, c_address); |
| env->ReleaseStringUTFChars(match, c_match); |
| if (reply) |
| { |
| DBusError err; |
| jint *list; |
| int i, len; |
| |
| dbus_error_init(&err); |
| if (dbus_message_get_args (reply, &err, |
| DBUS_TYPE_ARRAY, DBUS_TYPE_UINT32, |
| &list, &len, |
| DBUS_TYPE_INVALID)) { |
| if (len) { |
| intArray = env->NewIntArray(len); |
| if (intArray) |
| env->SetIntArrayRegion(intArray, 0, len, list); |
| } |
| } else { |
| LOG_AND_FREE_DBUS_ERROR_WITH_MSG(&err, reply); |
| } |
| |
| dbus_message_unref(reply); |
| } |
| return intArray; |
| } |
| #endif |
| return NULL; |
| } |
| |
| static jbyteArray getRemoteServiceRecordNative(JNIEnv *env, jobject object, |
| jstring address, jint handle) { |
| #ifdef HAVE_BLUETOOTH |
| LOGV(__FUNCTION__); |
| native_data_t *nat = get_native_data(env, object); |
| if (nat) { |
| const char *c_address = env->GetStringUTFChars(address, NULL); |
| |
| LOGV("... address = %s", c_address); |
| |
| DBusMessage *reply = |
| dbus_func_args(env, nat->conn, nat->adapter, |
| DBUS_CLASS_NAME, "GetRemoteServiceRecord", |
| DBUS_TYPE_STRING, &c_address, |
| DBUS_TYPE_UINT32, &handle, |
| DBUS_TYPE_INVALID); |
| env->ReleaseStringUTFChars(address, c_address); |
| return reply ? dbus_returns_array_of_bytes(env, reply) : NULL; |
| } |
| #endif |
| return NULL; |
| } |
| |
| static jboolean getRemoteServiceChannelNative(JNIEnv *env, jobject object, |
| jstring address, jshort uuid16) { |
| #ifdef HAVE_BLUETOOTH |
| LOGV(__FUNCTION__); |
| native_data_t *nat = get_native_data(env, object); |
| jobject eventLoop = env->GetObjectField(object, field_mEventLoop); |
| struct event_loop_native_data_t *eventLoopNat = |
| get_EventLoop_native_data(env, eventLoop); |
| if (nat && eventLoopNat) { |
| const char *c_address = env->GetStringUTFChars(address, NULL); |
| char *context_address = (char *)calloc(BTADDR_SIZE, sizeof(char)); |
| strlcpy(context_address, c_address, BTADDR_SIZE); |
| |
| LOGV("... address = %s", c_address); |
| LOGV("... uuid16 = %#X", uuid16); |
| |
| bool ret = dbus_func_args_async(env, nat->conn, 20000, // ms |
| onGetRemoteServiceChannelResult, context_address, |
| eventLoopNat, |
| nat->adapter, |
| DBUS_CLASS_NAME, "GetRemoteServiceChannel", |
| DBUS_TYPE_STRING, &c_address, |
| DBUS_TYPE_UINT16, &uuid16, |
| DBUS_TYPE_INVALID); |
| env->ReleaseStringUTFChars(address, c_address); |
| return ret ? JNI_TRUE : JNI_FALSE; |
| } |
| #endif |
| return JNI_FALSE; |
| } |
| |
| static jint enableNative(JNIEnv *env, jobject object) { |
| #ifdef HAVE_BLUETOOTH |
| LOGV(__FUNCTION__); |
| return bt_enable(); |
| #endif |
| return -1; |
| } |
| |
| static jint disableNative(JNIEnv *env, jobject object) { |
| #ifdef HAVE_BLUETOOTH |
| LOGV(__FUNCTION__); |
| return bt_disable(); |
| #endif |
| return -1; |
| } |
| |
| static jint isEnabledNative(JNIEnv *env, jobject object) { |
| #ifdef HAVE_BLUETOOTH |
| LOGV(__FUNCTION__); |
| return bt_is_enabled(); |
| #endif |
| return -1; |
| } |
| |
| static jboolean setPinNative(JNIEnv *env, jobject object, jstring address, |
| jstring pin, int nativeData) { |
| #ifdef HAVE_BLUETOOTH |
| LOGV(__FUNCTION__); |
| native_data_t *nat = get_native_data(env, object); |
| if (nat) { |
| DBusMessage *msg = (DBusMessage *)nativeData; |
| DBusMessage *reply = dbus_message_new_method_return(msg); |
| if (!reply) { |
| LOGE("%s: Cannot create message reply to return PIN code to " |
| "D-Bus\n", __FUNCTION__); |
| dbus_message_unref(msg); |
| return JNI_FALSE; |
| } |
| |
| const char *c_pin = env->GetStringUTFChars(pin, NULL); |
| |
| dbus_message_append_args(reply, DBUS_TYPE_STRING, &c_pin, |
| DBUS_TYPE_INVALID); |
| |
| dbus_connection_send(nat->conn, reply, NULL); |
| dbus_message_unref(msg); |
| dbus_message_unref(reply); |
| env->ReleaseStringUTFChars(pin, c_pin); |
| return JNI_TRUE; |
| } |
| #endif |
| return JNI_FALSE; |
| } |
| |
| static jboolean cancelPinNative(JNIEnv *env, jobject object, jstring address, |
| int nativeData) { |
| #ifdef HAVE_BLUETOOTH |
| LOGV(__FUNCTION__); |
| native_data_t *nat = get_native_data(env, object); |
| if (nat) { |
| DBusMessage *msg = (DBusMessage *)nativeData; |
| DBusMessage *reply = dbus_message_new_error(msg, |
| "org.bluez.Error.Canceled", "PIN Entry was canceled"); |
| if (!reply) { |
| LOGE("%s: Cannot create message reply to return PIN cancel to " |
| "D-BUS\n", __FUNCTION__); |
| dbus_message_unref(msg); |
| return JNI_FALSE; |
| } |
| |
| dbus_connection_send(nat->conn, reply, NULL); |
| dbus_message_unref(msg); |
| dbus_message_unref(reply); |
| return JNI_TRUE; |
| } |
| #endif |
| return JNI_FALSE; |
| } |
| |
| static JNINativeMethod sMethods[] = { |
| /* name, signature, funcPtr */ |
| {"classInitNative", "()V", (void*)classInitNative}, |
| {"initializeNativeDataNative", "()V", (void *)initializeNativeDataNative}, |
| {"cleanupNativeDataNative", "()V", (void *)cleanupNativeDataNative}, |
| {"getAdapterPathNative", "()Ljava/lang/String;", (void*)getAdapterPathNative}, |
| |
| {"isEnabledNative", "()I", (void *)isEnabledNative}, |
| {"enableNative", "()I", (void *)enableNative}, |
| {"disableNative", "()I", (void *)disableNative}, |
| |
| {"getAddressNative", "()Ljava/lang/String;", (void *)getAddressNative}, |
| {"getNameNative", "()Ljava/lang/String;", (void*)getNameNative}, |
| {"setNameNative", "(Ljava/lang/String;)Z", (void *)setNameNative}, |
| {"getVersionNative", "()Ljava/lang/String;", (void *)getVersionNative}, |
| {"getRevisionNative", "()Ljava/lang/String;", (void *)getRevisionNative}, |
| {"getManufacturerNative", "()Ljava/lang/String;", (void *)getManufacturerNative}, |
| {"getCompanyNative", "()Ljava/lang/String;", (void *)getCompanyNative}, |
| |
| {"getModeNative", "()Ljava/lang/String;", (void *)getModeNative}, |
| {"setModeNative", "(Ljava/lang/String;)Z", (void *)setModeNative}, |
| |
| {"getDiscoverableTimeoutNative", "()I", (void *)getDiscoverableTimeoutNative}, |
| {"setDiscoverableTimeoutNative", "(I)Z", (void *)setDiscoverableTimeoutNative}, |
| |
| {"startDiscoveryNative", "(Z)Z", (void*)startDiscoveryNative}, |
| {"cancelDiscoveryNative", "()Z", (void *)cancelDiscoveryNative}, |
| {"startPeriodicDiscoveryNative", "()Z", (void *)startPeriodicDiscoveryNative}, |
| {"stopPeriodicDiscoveryNative", "()Z", (void *)stopPeriodicDiscoveryNative}, |
| {"isPeriodicDiscoveryNative", "()Z", (void *)isPeriodicDiscoveryNative}, |
| {"listRemoteDevicesNative", "()[Ljava/lang/String;", (void *)listRemoteDevicesNative}, |
| |
| {"listConnectionsNative", "()[Ljava/lang/String;", (void *)listConnectionsNative}, |
| {"isConnectedNative", "(Ljava/lang/String;)Z", (void *)isConnectedNative}, |
| {"disconnectRemoteDeviceNative", "(Ljava/lang/String;)Z", (void *)disconnectRemoteDeviceNative}, |
| |
| {"createBondingNative", "(Ljava/lang/String;I)Z", (void *)createBondingNative}, |
| {"cancelBondingProcessNative", "(Ljava/lang/String;)Z", (void *)cancelBondingProcessNative}, |
| {"listBondingsNative", "()[Ljava/lang/String;", (void *)listBondingsNative}, |
| {"removeBondingNative", "(Ljava/lang/String;)Z", (void *)removeBondingNative}, |
| |
| {"getRemoteNameNative", "(Ljava/lang/String;)Ljava/lang/String;", (void *)getRemoteNameNative}, |
| {"getRemoteVersionNative", "(Ljava/lang/String;)Ljava/lang/String;", (void *)getRemoteVersionNative}, |
| {"getRemoteRevisionNative", "(Ljava/lang/String;)Ljava/lang/String;", (void *)getRemoteRevisionNative}, |
| {"getRemoteClassNative", "(Ljava/lang/String;)I", (void *)getRemoteClassNative}, |
| {"getRemoteManufacturerNative", "(Ljava/lang/String;)Ljava/lang/String;", (void *)getRemoteManufacturerNative}, |
| {"getRemoteCompanyNative", "(Ljava/lang/String;)Ljava/lang/String;", (void *)getRemoteCompanyNative}, |
| {"getRemoteServiceChannelNative", "(Ljava/lang/String;S)Z", (void *)getRemoteServiceChannelNative}, |
| {"getRemoteFeaturesNative", "(Ljava/lang/String;)[B", (void *)getRemoteFeaturesNative}, |
| {"lastSeenNative", "(Ljava/lang/String;)Ljava/lang/String;", (void *)lastSeenNative}, |
| {"lastUsedNative", "(Ljava/lang/String;)Ljava/lang/String;", (void *)lastUsedNative}, |
| {"setPinNative", "(Ljava/lang/String;Ljava/lang/String;I)Z", (void *)setPinNative}, |
| {"cancelPinNative", "(Ljava/lang/String;I)Z", (void *)cancelPinNative}, |
| }; |
| |
| int register_android_server_BluetoothDeviceService(JNIEnv *env) { |
| return AndroidRuntime::registerNativeMethods(env, |
| "android/server/BluetoothDeviceService", sMethods, NELEM(sMethods)); |
| } |
| |
| } /* namespace android */ |