blob: 61a4a26aad0ec41b09db35a199c90ffda2d53063 [file] [log] [blame]
/*
** 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 {
#ifdef HAVE_BLUETOOTH
// We initialize these variables when we load class
// android.server.BluetoothDeviceService
static jfieldID field_mNativeData;
typedef struct {
JNIEnv *env;
DBusConnection *conn;
const char *adapter; // dbus object name of the local adapter
} native_data_t;
void onCreateBondingResult(DBusMessage *msg, void *user);
void onGetRemoteServiceChannelResult(DBusMessage *msg, void *user);
/** 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");
#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;
}
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 jboolean isConnectableNative(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, "IsConnectable",
DBUS_TYPE_INVALID);
return reply ? dbus_returns_boolean(env, reply) : JNI_FALSE;
}
#endif
return JNI_FALSE;
}
static jboolean isDiscoverableNative(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, "IsDiscoverable",
DBUS_TYPE_INVALID);
return reply ? dbus_returns_boolean(env, reply) : JNI_FALSE;
}
#endif
return JNI_FALSE;
}
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 void common_Bonding(JNIEnv *env, jobject object, int timeout_ms,
const char *func, jstring address) {
#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, timeout_ms, nat->adapter,
DBUS_CLASS_NAME, func,
DBUS_TYPE_STRING, &c_address,
DBUS_TYPE_INVALID);
env->ReleaseStringUTFChars(address, c_address);
if (reply) {
dbus_message_unref(reply);
}
}
#endif
}
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);
if (nat) {
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, // user data
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 void cancelBondingProcessNative(JNIEnv *env, jobject object,
jstring address) {
LOGV(__FUNCTION__);
common_Bonding(env, object, -1, "CancelBondingProcess", address);
}
static void removeBondingNative(JNIEnv *env, jobject object, jstring address) {
LOGV(__FUNCTION__);
common_Bonding(env, object, -1, "RemoveBonding", address);
}
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 getMajorClassNative(JNIEnv *env, jobject obj) {
return common_Get(env, obj, "GetMajorClass");
}
static jstring getMinorClassNative(JNIEnv *env, jobject obj) {
return common_Get(env, obj, "GetMinorClass");
}
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 getRemoteAliasNative(JNIEnv *env, jobject obj, jstring address) {
return common_getRemote(env, obj, "GetRemoteAlias", address);
}
static jboolean setRemoteAliasNative(JNIEnv *env, jobject obj,
jstring address, jstring alias) {
#ifdef HAVE_BLUETOOTH
LOGV(__FUNCTION__);
native_data_t *nat = get_native_data(env, obj);
if (nat) {
const char *c_address = env->GetStringUTFChars(address, NULL);
const char *c_alias = env->GetStringUTFChars(alias, NULL);
LOGV("... address = %s alias = %s", c_address, c_alias);
DBusMessage *reply = dbus_func_args(env, nat->conn, nat->adapter,
DBUS_CLASS_NAME, "SetRemoteAlias",
DBUS_TYPE_STRING, &c_address,
DBUS_TYPE_STRING, &c_alias,
DBUS_TYPE_INVALID);
env->ReleaseStringUTFChars(address, c_address);
env->ReleaseStringUTFChars(alias, c_alias);
if (reply)
{
dbus_message_unref(reply);
return JNI_TRUE;
}
return JNI_FALSE;
}
#endif
return JNI_FALSE;
}
static jboolean clearRemoteAliasNative(JNIEnv *env, jobject obj, jstring address) {
#ifdef HAVE_BLUETOOTH
LOGV(__FUNCTION__);
native_data_t *nat = get_native_data(env, obj);
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, "ClearRemoteAlias",
DBUS_TYPE_STRING, &c_address,
DBUS_TYPE_INVALID);
env->ReleaseStringUTFChars(address, c_address);
if (reply)
{
dbus_message_unref(reply);
return JNI_TRUE;
}
return JNI_FALSE;
}
#endif
return JNI_FALSE;
}
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 getRemoteMajorClassNative(JNIEnv *env, jobject obj, jstring address) {
return common_getRemote(env, obj, "GetRemoteMajorClass", address);
}
static jstring getRemoteMinorClassNative(JNIEnv *env, jobject obj, jstring address) {
return common_getRemote(env, obj, "GetRemoteMinorClass", 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 jobjectArray getRemoteServiceClassesNative(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, "GetRemoteServiceClasses",
DBUS_TYPE_STRING, &c_address,
DBUS_TYPE_INVALID);
env->ReleaseStringUTFChars(address, c_address);
return reply ? dbus_returns_array_of_strings(env, reply) : NULL;
}
#endif
return NULL;
}
static jint getRemoteClassNative(JNIEnv *env, jobject object, jstring address) {
#ifdef HAVE_BLUETOOTH
LOGV(__FUNCTION__);
native_data_t *nat = get_native_data(env, object);
if (nat) {
jint ret = 0;
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, &ret,
DBUS_TYPE_INVALID)) {
LOG_AND_FREE_DBUS_ERROR_WITH_MSG(&err, reply);
}
dbus_message_unref(reply);
}
return ret;
}
#endif
return 0;
}
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);
if (nat) {
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,
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},
{"getMajorClassNative", "()Ljava/lang/String;", (void *)getMajorClassNative},
{"getMinorClassNative", "()Ljava/lang/String;", (void *)getMinorClassNative},
{"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},
{"getRemoteAliasNative", "(Ljava/lang/String;)Ljava/lang/String;", (void *)getRemoteAliasNative},
{"setRemoteAliasNative", "(Ljava/lang/String;Ljava/lang/String;)Z", (void *)setRemoteAliasNative},
{"clearRemoteAliasNative", "(Ljava/lang/String;)Z", (void *)clearRemoteAliasNative},
{"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},
{"getRemoteMajorClassNative", "(Ljava/lang/String;)Ljava/lang/String;", (void *)getRemoteMajorClassNative},
{"getRemoteMinorClassNative", "(Ljava/lang/String;)Ljava/lang/String;", (void *)getRemoteMinorClassNative},
{"getRemoteServiceClassesNative", "(Ljava/lang/String;)[Ljava/lang/String;", (void *)getRemoteServiceClassesNative},
{"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 */