946 lines
31 KiB
C++
946 lines
31 KiB
C++
|
/*
|
||
|
** Copyright 2006, The Android Open Source Project
|
||
|
** Copyright (c) 2011, The Linux Foundation. All rights reserved.
|
||
|
**
|
||
|
** 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 LOG_TAG "bluetooth_common.cpp"
|
||
|
|
||
|
#include "android_bluetooth_common.h"
|
||
|
#include "JNIHelp.h"
|
||
|
#include "jni.h"
|
||
|
#include "utils/Log.h"
|
||
|
#include "utils/misc.h"
|
||
|
|
||
|
#include <stdio.h>
|
||
|
#include <string.h>
|
||
|
#include <stdlib.h>
|
||
|
#include <errno.h>
|
||
|
#include <unistd.h>
|
||
|
#include <cutils/properties.h>
|
||
|
|
||
|
#ifdef HAVE_BLUETOOTH
|
||
|
#include <dbus/dbus.h>
|
||
|
#endif
|
||
|
|
||
|
namespace android {
|
||
|
|
||
|
#ifdef HAVE_BLUETOOTH
|
||
|
|
||
|
static Properties remote_device_properties[] = {
|
||
|
{"Address", DBUS_TYPE_STRING},
|
||
|
{"Name", DBUS_TYPE_STRING},
|
||
|
{"Icon", DBUS_TYPE_STRING},
|
||
|
{"Class", DBUS_TYPE_UINT32},
|
||
|
{"UUIDs", DBUS_TYPE_ARRAY},
|
||
|
{"Paired", DBUS_TYPE_BOOLEAN},
|
||
|
{"Connected", DBUS_TYPE_BOOLEAN},
|
||
|
{"Trusted", DBUS_TYPE_BOOLEAN},
|
||
|
{"Blocked", DBUS_TYPE_BOOLEAN},
|
||
|
{"Alias", DBUS_TYPE_STRING},
|
||
|
{"Nodes", DBUS_TYPE_ARRAY},
|
||
|
{"Adapter", DBUS_TYPE_OBJECT_PATH},
|
||
|
{"LegacyPairing", DBUS_TYPE_BOOLEAN},
|
||
|
{"RSSI", DBUS_TYPE_INT16},
|
||
|
{"TX", DBUS_TYPE_UINT32},
|
||
|
{"Type", DBUS_TYPE_STRING},
|
||
|
{"Broadcaster", DBUS_TYPE_BOOLEAN},
|
||
|
{"Services", DBUS_TYPE_ARRAY}
|
||
|
};
|
||
|
|
||
|
static Properties adapter_properties[] = {
|
||
|
{"Address", DBUS_TYPE_STRING},
|
||
|
{"Name", DBUS_TYPE_STRING},
|
||
|
{"Class", DBUS_TYPE_UINT32},
|
||
|
{"Powered", DBUS_TYPE_BOOLEAN},
|
||
|
{"Discoverable", DBUS_TYPE_BOOLEAN},
|
||
|
{"DiscoverableTimeout", DBUS_TYPE_UINT32},
|
||
|
{"Pairable", DBUS_TYPE_BOOLEAN},
|
||
|
{"PairableTimeout", DBUS_TYPE_UINT32},
|
||
|
{"Discovering", DBUS_TYPE_BOOLEAN},
|
||
|
{"Devices", DBUS_TYPE_ARRAY},
|
||
|
{"UUIDs", DBUS_TYPE_ARRAY},
|
||
|
{"Type", DBUS_TYPE_STRING},
|
||
|
};
|
||
|
|
||
|
static Properties gatt_service_properties[] = {
|
||
|
{"Name", DBUS_TYPE_STRING},
|
||
|
{"Description", DBUS_TYPE_STRING},
|
||
|
{"UUID", DBUS_TYPE_STRING},
|
||
|
{"Characteristics", DBUS_TYPE_ARRAY},
|
||
|
};
|
||
|
|
||
|
static Properties gatt_characteristic_properties[] = {
|
||
|
{"UUID", DBUS_TYPE_STRING},
|
||
|
{"Name", DBUS_TYPE_STRING},
|
||
|
{"Description", DBUS_TYPE_STRING},
|
||
|
{"Format", DBUS_TYPE_STRUCT},
|
||
|
{"Value", DBUS_TYPE_ARRAY},
|
||
|
{"Representation", DBUS_TYPE_STRING},
|
||
|
{"ClientConfiguration", DBUS_TYPE_UINT16},
|
||
|
{"Properties", DBUS_TYPE_BYTE},
|
||
|
};
|
||
|
|
||
|
typedef union {
|
||
|
char *str_val;
|
||
|
int int_val;
|
||
|
char **array_val;
|
||
|
} property_value;
|
||
|
|
||
|
jfieldID get_field(JNIEnv *env, jclass clazz, const char *member,
|
||
|
const char *mtype) {
|
||
|
jfieldID field = env->GetFieldID(clazz, member, mtype);
|
||
|
if (field == NULL) {
|
||
|
LOGE("Can't find member %s", member);
|
||
|
}
|
||
|
return field;
|
||
|
}
|
||
|
|
||
|
|
||
|
void dbus_func_args_async_callback(DBusPendingCall *call, void *data) {
|
||
|
|
||
|
dbus_async_call_t *req = (dbus_async_call_t *)data;
|
||
|
DBusMessage *msg;
|
||
|
|
||
|
/* This is guaranteed to be non-NULL, because this function is called only
|
||
|
when once the remote method invokation returns. */
|
||
|
msg = dbus_pending_call_steal_reply(call);
|
||
|
|
||
|
if (msg) {
|
||
|
if (req->user_cb) {
|
||
|
// The user may not deref the message object.
|
||
|
req->user_cb(msg, req->user, req->nat);
|
||
|
}
|
||
|
dbus_message_unref(msg);
|
||
|
}
|
||
|
|
||
|
//dbus_message_unref(req->method);
|
||
|
dbus_pending_call_cancel(call);
|
||
|
dbus_pending_call_unref(call);
|
||
|
free(req);
|
||
|
}
|
||
|
|
||
|
static dbus_bool_t dbus_func_args_async_valist(JNIEnv *env,
|
||
|
DBusConnection *conn,
|
||
|
int timeout_ms,
|
||
|
void (*user_cb)(DBusMessage *,
|
||
|
void *,
|
||
|
void*),
|
||
|
void *user,
|
||
|
void *nat,
|
||
|
const char *path,
|
||
|
const char *ifc,
|
||
|
const char *func,
|
||
|
int first_arg_type,
|
||
|
va_list args) {
|
||
|
DBusMessage *msg = NULL;
|
||
|
const char *name;
|
||
|
dbus_async_call_t *pending;
|
||
|
dbus_bool_t reply = FALSE;
|
||
|
|
||
|
/* Compose the command */
|
||
|
msg = dbus_message_new_method_call(BLUEZ_DBUS_BASE_IFC, path, ifc, func);
|
||
|
|
||
|
if (msg == NULL) {
|
||
|
LOGE("Could not allocate D-Bus message object!");
|
||
|
goto done;
|
||
|
}
|
||
|
|
||
|
/* append arguments */
|
||
|
if (!dbus_message_append_args_valist(msg, first_arg_type, args)) {
|
||
|
LOGE("Could not append argument to method call!");
|
||
|
goto done;
|
||
|
}
|
||
|
|
||
|
/* Make the call. */
|
||
|
pending = (dbus_async_call_t *)malloc(sizeof(dbus_async_call_t));
|
||
|
if (pending) {
|
||
|
DBusPendingCall *call;
|
||
|
|
||
|
pending->env = env;
|
||
|
pending->user_cb = user_cb;
|
||
|
pending->user = user;
|
||
|
pending->nat = nat;
|
||
|
//pending->method = msg;
|
||
|
|
||
|
reply = dbus_connection_send_with_reply(conn, msg,
|
||
|
&call,
|
||
|
timeout_ms);
|
||
|
if (reply == TRUE) {
|
||
|
dbus_pending_call_set_notify(call,
|
||
|
dbus_func_args_async_callback,
|
||
|
pending,
|
||
|
NULL);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
done:
|
||
|
if (msg) dbus_message_unref(msg);
|
||
|
return reply;
|
||
|
}
|
||
|
|
||
|
dbus_bool_t dbus_func_args_async(JNIEnv *env,
|
||
|
DBusConnection *conn,
|
||
|
int timeout_ms,
|
||
|
void (*reply)(DBusMessage *, void *, void*),
|
||
|
void *user,
|
||
|
void *nat,
|
||
|
const char *path,
|
||
|
const char *ifc,
|
||
|
const char *func,
|
||
|
int first_arg_type,
|
||
|
...) {
|
||
|
dbus_bool_t ret;
|
||
|
va_list lst;
|
||
|
va_start(lst, first_arg_type);
|
||
|
ret = dbus_func_args_async_valist(env, conn,
|
||
|
timeout_ms,
|
||
|
reply, user, nat,
|
||
|
path, ifc, func,
|
||
|
first_arg_type, lst);
|
||
|
va_end(lst);
|
||
|
return ret;
|
||
|
}
|
||
|
|
||
|
// If err is NULL, then any errors will be LOGE'd, and free'd and the reply
|
||
|
// will be NULL.
|
||
|
// If err is not NULL, then it is assumed that dbus_error_init was already
|
||
|
// called, and error's will be returned to the caller without logging. The
|
||
|
// return value is NULL iff an error was set. The client must free the error if
|
||
|
// set.
|
||
|
DBusMessage * dbus_func_args_timeout_valist(JNIEnv *env,
|
||
|
DBusConnection *conn,
|
||
|
int timeout_ms,
|
||
|
DBusError *err,
|
||
|
const char *path,
|
||
|
const char *ifc,
|
||
|
const char *func,
|
||
|
int first_arg_type,
|
||
|
va_list args) {
|
||
|
|
||
|
DBusMessage *msg = NULL, *reply = NULL;
|
||
|
const char *name;
|
||
|
bool return_error = (err != NULL);
|
||
|
|
||
|
if (!return_error) {
|
||
|
err = (DBusError*)malloc(sizeof(DBusError));
|
||
|
dbus_error_init(err);
|
||
|
}
|
||
|
|
||
|
/* Compose the command */
|
||
|
msg = dbus_message_new_method_call(BLUEZ_DBUS_BASE_IFC, path, ifc, func);
|
||
|
|
||
|
if (msg == NULL) {
|
||
|
LOGE("Could not allocate D-Bus message object!");
|
||
|
goto done;
|
||
|
}
|
||
|
|
||
|
/* append arguments */
|
||
|
if (!dbus_message_append_args_valist(msg, first_arg_type, args)) {
|
||
|
LOGE("Could not append argument to method call!");
|
||
|
goto done;
|
||
|
}
|
||
|
|
||
|
/* Make the call. */
|
||
|
reply = dbus_connection_send_with_reply_and_block(conn, msg, timeout_ms, err);
|
||
|
if (!return_error && dbus_error_is_set(err)) {
|
||
|
LOG_AND_FREE_DBUS_ERROR_WITH_MSG(err, msg);
|
||
|
}
|
||
|
|
||
|
done:
|
||
|
if (!return_error) {
|
||
|
free(err);
|
||
|
}
|
||
|
if (msg) dbus_message_unref(msg);
|
||
|
return reply;
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
* This generic version of the function will take the service path
|
||
|
* as an argument and call the methods on the given service, path and
|
||
|
* interface.
|
||
|
* This can be used to call any interface on any services that are
|
||
|
* connected to D-Bus
|
||
|
*/
|
||
|
DBusMessage * dbus_func_args_generic_timeout_valist(JNIEnv *env,
|
||
|
DBusConnection *conn,
|
||
|
int timeout_ms,
|
||
|
DBusError *err,
|
||
|
const char *service,
|
||
|
const char *path,
|
||
|
const char *ifc,
|
||
|
const char *func,
|
||
|
int first_arg_type,
|
||
|
va_list args) {
|
||
|
|
||
|
DBusMessage *msg = NULL, *reply = NULL;
|
||
|
const char *name;
|
||
|
bool return_error = (err != NULL);
|
||
|
|
||
|
if (!return_error) {
|
||
|
err = (DBusError*)malloc(sizeof(DBusError));
|
||
|
dbus_error_init(err);
|
||
|
}
|
||
|
|
||
|
/* Compose the command */
|
||
|
msg = dbus_message_new_method_call(service, path, ifc, func);
|
||
|
|
||
|
if (msg == NULL) {
|
||
|
LOGE("Could not allocate D-Bus message object!");
|
||
|
goto done;
|
||
|
}
|
||
|
|
||
|
/* append arguments */
|
||
|
if (!dbus_message_append_args_valist(msg, first_arg_type, args)) {
|
||
|
LOGE("Could not append argument to method call!");
|
||
|
goto done;
|
||
|
}
|
||
|
|
||
|
/* Make the call. */
|
||
|
reply = dbus_connection_send_with_reply_and_block(conn, msg, timeout_ms, err);
|
||
|
if (!return_error && dbus_error_is_set(err)) {
|
||
|
LOG_AND_FREE_DBUS_ERROR_WITH_MSG(err, msg);
|
||
|
}
|
||
|
|
||
|
done:
|
||
|
if (!return_error) {
|
||
|
free(err);
|
||
|
}
|
||
|
if (msg) dbus_message_unref(msg);
|
||
|
return reply;
|
||
|
}
|
||
|
|
||
|
DBusMessage * dbus_func_args_timeout(JNIEnv *env,
|
||
|
DBusConnection *conn,
|
||
|
int timeout_ms,
|
||
|
const char *path,
|
||
|
const char *ifc,
|
||
|
const char *func,
|
||
|
int first_arg_type,
|
||
|
...) {
|
||
|
DBusMessage *ret;
|
||
|
va_list lst;
|
||
|
va_start(lst, first_arg_type);
|
||
|
ret = dbus_func_args_timeout_valist(env, conn, timeout_ms, NULL,
|
||
|
path, ifc, func,
|
||
|
first_arg_type, lst);
|
||
|
va_end(lst);
|
||
|
return ret;
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
* This generic version of the function will take the service path
|
||
|
* as an argument and call the methods on the given service, path and
|
||
|
* interface.
|
||
|
* This can be used to call any interface on any services that are
|
||
|
* connected to D-Bus
|
||
|
*/
|
||
|
DBusMessage * dbus_func_args_generic(JNIEnv *env,
|
||
|
DBusConnection *conn,
|
||
|
const char *service,
|
||
|
const char *path,
|
||
|
const char *ifc,
|
||
|
const char *func,
|
||
|
int first_arg_type,
|
||
|
...) {
|
||
|
DBusMessage *ret;
|
||
|
va_list lst;
|
||
|
va_start(lst, first_arg_type);
|
||
|
ret = dbus_func_args_generic_timeout_valist(env, conn, -1, NULL,
|
||
|
service, path, ifc, func,
|
||
|
first_arg_type, lst);
|
||
|
va_end(lst);
|
||
|
return ret;
|
||
|
}
|
||
|
|
||
|
DBusMessage * dbus_func_args(JNIEnv *env,
|
||
|
DBusConnection *conn,
|
||
|
const char *path,
|
||
|
const char *ifc,
|
||
|
const char *func,
|
||
|
int first_arg_type,
|
||
|
...) {
|
||
|
DBusMessage *ret;
|
||
|
va_list lst;
|
||
|
va_start(lst, first_arg_type);
|
||
|
ret = dbus_func_args_timeout_valist(env, conn, -1, NULL,
|
||
|
path, ifc, func,
|
||
|
first_arg_type, lst);
|
||
|
va_end(lst);
|
||
|
return ret;
|
||
|
}
|
||
|
|
||
|
DBusMessage * dbus_func_args_error(JNIEnv *env,
|
||
|
DBusConnection *conn,
|
||
|
DBusError *err,
|
||
|
const char *path,
|
||
|
const char *ifc,
|
||
|
const char *func,
|
||
|
int first_arg_type,
|
||
|
...) {
|
||
|
DBusMessage *ret;
|
||
|
va_list lst;
|
||
|
va_start(lst, first_arg_type);
|
||
|
ret = dbus_func_args_timeout_valist(env, conn, -1, err,
|
||
|
path, ifc, func,
|
||
|
first_arg_type, lst);
|
||
|
va_end(lst);
|
||
|
return ret;
|
||
|
}
|
||
|
|
||
|
jint dbus_returns_int32(JNIEnv *env, DBusMessage *reply) {
|
||
|
|
||
|
DBusError err;
|
||
|
jint ret = -1;
|
||
|
|
||
|
dbus_error_init(&err);
|
||
|
if (!dbus_message_get_args(reply, &err,
|
||
|
DBUS_TYPE_INT32, &ret,
|
||
|
DBUS_TYPE_INVALID)) {
|
||
|
LOG_AND_FREE_DBUS_ERROR_WITH_MSG(&err, reply);
|
||
|
}
|
||
|
dbus_message_unref(reply);
|
||
|
return ret;
|
||
|
}
|
||
|
|
||
|
jint dbus_returns_uint32(JNIEnv *env, DBusMessage *reply) {
|
||
|
|
||
|
DBusError err;
|
||
|
jint ret = -1;
|
||
|
|
||
|
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;
|
||
|
}
|
||
|
|
||
|
jstring dbus_returns_string(JNIEnv *env, DBusMessage *reply) {
|
||
|
|
||
|
DBusError err;
|
||
|
jstring ret = NULL;
|
||
|
const char *name;
|
||
|
|
||
|
dbus_error_init(&err);
|
||
|
if (dbus_message_get_args(reply, &err,
|
||
|
DBUS_TYPE_STRING, &name,
|
||
|
DBUS_TYPE_INVALID)) {
|
||
|
ret = env->NewStringUTF(name);
|
||
|
} else {
|
||
|
LOG_AND_FREE_DBUS_ERROR_WITH_MSG(&err, reply);
|
||
|
}
|
||
|
dbus_message_unref(reply);
|
||
|
|
||
|
return ret;
|
||
|
}
|
||
|
|
||
|
jboolean dbus_returns_boolean(JNIEnv *env, DBusMessage *reply) {
|
||
|
DBusError err;
|
||
|
jboolean ret = JNI_FALSE;
|
||
|
dbus_bool_t val = FALSE;
|
||
|
|
||
|
dbus_error_init(&err);
|
||
|
|
||
|
/* Check the return value. */
|
||
|
if (dbus_message_get_args(reply, &err,
|
||
|
DBUS_TYPE_BOOLEAN, &val,
|
||
|
DBUS_TYPE_INVALID)) {
|
||
|
ret = val == TRUE ? JNI_TRUE : JNI_FALSE;
|
||
|
} else {
|
||
|
LOG_AND_FREE_DBUS_ERROR_WITH_MSG(&err, reply);
|
||
|
}
|
||
|
|
||
|
dbus_message_unref(reply);
|
||
|
return ret;
|
||
|
}
|
||
|
|
||
|
static void set_object_array_element(JNIEnv *env, jobjectArray strArray,
|
||
|
const char *value, int index) {
|
||
|
jstring obj;
|
||
|
obj = env->NewStringUTF(value);
|
||
|
env->SetObjectArrayElement(strArray, index, obj);
|
||
|
env->DeleteLocalRef(obj);
|
||
|
}
|
||
|
|
||
|
jobjectArray dbus_returns_array_of_object_path(JNIEnv *env,
|
||
|
DBusMessage *reply) {
|
||
|
|
||
|
DBusError err;
|
||
|
char **list;
|
||
|
int i, len;
|
||
|
jobjectArray strArray = NULL;
|
||
|
|
||
|
dbus_error_init(&err);
|
||
|
if (dbus_message_get_args (reply,
|
||
|
&err,
|
||
|
DBUS_TYPE_ARRAY, DBUS_TYPE_OBJECT_PATH,
|
||
|
&list, &len,
|
||
|
DBUS_TYPE_INVALID)) {
|
||
|
jclass stringClass;
|
||
|
jstring classNameStr;
|
||
|
|
||
|
stringClass = env->FindClass("java/lang/String");
|
||
|
strArray = env->NewObjectArray(len, stringClass, NULL);
|
||
|
|
||
|
for (i = 0; i < len; i++)
|
||
|
set_object_array_element(env, strArray, list[i], i);
|
||
|
} else {
|
||
|
LOG_AND_FREE_DBUS_ERROR_WITH_MSG(&err, reply);
|
||
|
}
|
||
|
|
||
|
dbus_message_unref(reply);
|
||
|
return strArray;
|
||
|
}
|
||
|
|
||
|
jobjectArray dbus_returns_array_of_strings(JNIEnv *env, DBusMessage *reply) {
|
||
|
|
||
|
DBusError err;
|
||
|
char **list;
|
||
|
int i, len;
|
||
|
jobjectArray strArray = NULL;
|
||
|
|
||
|
dbus_error_init(&err);
|
||
|
if (dbus_message_get_args (reply,
|
||
|
&err,
|
||
|
DBUS_TYPE_ARRAY, DBUS_TYPE_STRING,
|
||
|
&list, &len,
|
||
|
DBUS_TYPE_INVALID)) {
|
||
|
jclass stringClass;
|
||
|
jstring classNameStr;
|
||
|
|
||
|
//LOGV("%s: there are %d elements in string array!", __FUNCTION__, len);
|
||
|
|
||
|
stringClass = env->FindClass("java/lang/String");
|
||
|
strArray = env->NewObjectArray(len, stringClass, NULL);
|
||
|
|
||
|
for (i = 0; i < len; i++)
|
||
|
set_object_array_element(env, strArray, list[i], i);
|
||
|
} else {
|
||
|
LOG_AND_FREE_DBUS_ERROR_WITH_MSG(&err, reply);
|
||
|
}
|
||
|
|
||
|
dbus_message_unref(reply);
|
||
|
return strArray;
|
||
|
}
|
||
|
|
||
|
jbyteArray dbus_returns_array_of_bytes(JNIEnv *env, DBusMessage *reply) {
|
||
|
|
||
|
DBusError err;
|
||
|
int i, len;
|
||
|
jbyte *list;
|
||
|
jbyteArray byteArray = NULL;
|
||
|
|
||
|
dbus_error_init(&err);
|
||
|
if (dbus_message_get_args(reply, &err,
|
||
|
DBUS_TYPE_ARRAY, DBUS_TYPE_BYTE, &list, &len,
|
||
|
DBUS_TYPE_INVALID)) {
|
||
|
//LOGV("%s: there are %d elements in byte array!", __FUNCTION__, len);
|
||
|
byteArray = env->NewByteArray(len);
|
||
|
if (byteArray)
|
||
|
env->SetByteArrayRegion(byteArray, 0, len, list);
|
||
|
|
||
|
} else {
|
||
|
LOG_AND_FREE_DBUS_ERROR_WITH_MSG(&err, reply);
|
||
|
}
|
||
|
|
||
|
dbus_message_unref(reply);
|
||
|
return byteArray;
|
||
|
}
|
||
|
|
||
|
void append_variant(DBusMessageIter *iter, int type, void *val)
|
||
|
{
|
||
|
DBusMessageIter value_iter;
|
||
|
char var_type[2] = { type, '\0'};
|
||
|
dbus_message_iter_open_container(iter, DBUS_TYPE_VARIANT, var_type, &value_iter);
|
||
|
dbus_message_iter_append_basic(&value_iter, type, val);
|
||
|
dbus_message_iter_close_container(iter, &value_iter);
|
||
|
}
|
||
|
|
||
|
void append_array_variant(DBusMessageIter *iter, int type, void *val,
|
||
|
int n_elements)
|
||
|
{
|
||
|
DBusMessageIter variant, array;
|
||
|
char type_sig[2] = { type, '\0' };
|
||
|
char array_sig[3] = { DBUS_TYPE_ARRAY, type, '\0' };
|
||
|
|
||
|
dbus_message_iter_open_container(iter, DBUS_TYPE_VARIANT,
|
||
|
array_sig, &variant);
|
||
|
|
||
|
dbus_message_iter_open_container(&variant, DBUS_TYPE_ARRAY,
|
||
|
type_sig, &array);
|
||
|
|
||
|
if (type == DBUS_TYPE_BYTE) {
|
||
|
jbyte *v_ptr = (jbyte *) val;
|
||
|
for (int i = 0; i < n_elements; i++) {
|
||
|
dbus_message_iter_append_basic(&array, type, &(v_ptr[i]));
|
||
|
}
|
||
|
} else if (type == DBUS_TYPE_STRING || type == DBUS_TYPE_OBJECT_PATH) {
|
||
|
const char ***str_array = (const char ***)val;
|
||
|
int i;
|
||
|
|
||
|
for (i = 0; i < n_elements; i++)
|
||
|
dbus_message_iter_append_basic(&array, type,
|
||
|
&((*str_array)[i]));
|
||
|
}
|
||
|
|
||
|
dbus_message_iter_close_container(&variant, &array);
|
||
|
|
||
|
dbus_message_iter_close_container(iter, &variant);
|
||
|
}
|
||
|
|
||
|
int get_property(DBusMessageIter iter, Properties *properties, int max_num_properties,
|
||
|
int *prop_index, property_value *value, int *value_type, int *len) {
|
||
|
DBusMessageIter prop_val, array_val_iter;
|
||
|
char *property = NULL;
|
||
|
uint32_t array_type;
|
||
|
char *str_val;
|
||
|
int i, j, type, int_val;
|
||
|
|
||
|
if (dbus_message_iter_get_arg_type(&iter) != DBUS_TYPE_STRING)
|
||
|
return -1;
|
||
|
dbus_message_iter_get_basic(&iter, &property);
|
||
|
if (!dbus_message_iter_next(&iter))
|
||
|
return -1;
|
||
|
if (dbus_message_iter_get_arg_type(&iter) != DBUS_TYPE_VARIANT)
|
||
|
return -1;
|
||
|
for (i = 0; i < max_num_properties; i++) {
|
||
|
if (!strncmp(property, properties[i].name, strlen(property)))
|
||
|
break;
|
||
|
}
|
||
|
*prop_index = i;
|
||
|
if (i == max_num_properties)
|
||
|
return -1;
|
||
|
|
||
|
dbus_message_iter_recurse(&iter, &prop_val);
|
||
|
type = properties[*prop_index].type;
|
||
|
if (dbus_message_iter_get_arg_type(&prop_val) != type) {
|
||
|
LOGE("Property type mismatch in get_property: %d, expected:%d, index:%d",
|
||
|
dbus_message_iter_get_arg_type(&prop_val), type, *prop_index);
|
||
|
return -1;
|
||
|
}
|
||
|
|
||
|
switch(type) {
|
||
|
case DBUS_TYPE_STRING:
|
||
|
case DBUS_TYPE_OBJECT_PATH:
|
||
|
dbus_message_iter_get_basic(&prop_val, &value->str_val);
|
||
|
*len = 1;
|
||
|
break;
|
||
|
case DBUS_TYPE_BYTE:
|
||
|
uint8_t uint8_val;
|
||
|
dbus_message_iter_get_basic(&prop_val, &uint8_val);
|
||
|
value->int_val = uint8_val;
|
||
|
*len = 1;
|
||
|
break;
|
||
|
case DBUS_TYPE_UINT16:
|
||
|
uint16_t uint16_val;
|
||
|
dbus_message_iter_get_basic(&prop_val, &uint16_val);
|
||
|
value->int_val = uint16_val;
|
||
|
*len = 1;
|
||
|
break;
|
||
|
case DBUS_TYPE_UINT32:
|
||
|
case DBUS_TYPE_INT16:
|
||
|
case DBUS_TYPE_BOOLEAN:
|
||
|
dbus_message_iter_get_basic(&prop_val, &int_val);
|
||
|
value->int_val = int_val;
|
||
|
*len = 1;
|
||
|
break;
|
||
|
case DBUS_TYPE_ARRAY:
|
||
|
dbus_message_iter_recurse(&prop_val, &array_val_iter);
|
||
|
array_type = dbus_message_iter_get_arg_type(&array_val_iter);
|
||
|
*len = 0;
|
||
|
value->array_val = NULL;
|
||
|
*value_type = array_type;
|
||
|
if (array_type == DBUS_TYPE_OBJECT_PATH ||
|
||
|
array_type == DBUS_TYPE_STRING){
|
||
|
j = 0;
|
||
|
do {
|
||
|
j ++;
|
||
|
} while(dbus_message_iter_next(&array_val_iter));
|
||
|
dbus_message_iter_recurse(&prop_val, &array_val_iter);
|
||
|
// Allocate an array of char *
|
||
|
*len = j;
|
||
|
char **tmp = (char **)malloc(sizeof(char *) * *len);
|
||
|
if (!tmp)
|
||
|
return -1;
|
||
|
j = 0;
|
||
|
do {
|
||
|
dbus_message_iter_get_basic(&array_val_iter, &tmp[j]);
|
||
|
j ++;
|
||
|
} while(dbus_message_iter_next(&array_val_iter));
|
||
|
value->array_val = tmp;
|
||
|
} else if (array_type == DBUS_TYPE_BYTE) {
|
||
|
char * tempPtr;
|
||
|
char * tmpValueArray;
|
||
|
int size = 0;
|
||
|
dbus_message_iter_get_fixed_array(&array_val_iter, &tempPtr, &size);
|
||
|
|
||
|
tmpValueArray = (char *) malloc(sizeof(char) * ( (2*size) + 1 ));
|
||
|
if (!tmpValueArray) {
|
||
|
return -1;
|
||
|
}
|
||
|
|
||
|
char *tmpPos = tmpValueArray;
|
||
|
|
||
|
for ( j=0; j<size; j++) {
|
||
|
sprintf(tmpPos, "%02x", tempPtr[j]);
|
||
|
tmpPos+=2;
|
||
|
}
|
||
|
tmpValueArray[2*size] = '\0';
|
||
|
value->str_val = tmpValueArray;
|
||
|
*len = 1;
|
||
|
}
|
||
|
break;
|
||
|
default:
|
||
|
return -1;
|
||
|
}
|
||
|
return 0;
|
||
|
}
|
||
|
|
||
|
void create_prop_array(JNIEnv *env, jobjectArray strArray, Properties *property,
|
||
|
property_value *value, int value_type, int len,
|
||
|
int *array_index ) {
|
||
|
char **prop_val = NULL;
|
||
|
char buf[32] = {'\0'}, buf1[32] = {'\0'};
|
||
|
int i;
|
||
|
|
||
|
char *name = property->name;
|
||
|
int prop_type = property->type;
|
||
|
|
||
|
set_object_array_element(env, strArray, name, *array_index);
|
||
|
*array_index += 1;
|
||
|
|
||
|
if (prop_type == DBUS_TYPE_UINT32 || prop_type == DBUS_TYPE_INT16 ||
|
||
|
prop_type == DBUS_TYPE_UINT16 || prop_type == DBUS_TYPE_BYTE) {
|
||
|
sprintf(buf, "%d", value->int_val);
|
||
|
set_object_array_element(env, strArray, buf, *array_index);
|
||
|
*array_index += 1;
|
||
|
} else if (prop_type == DBUS_TYPE_BOOLEAN) {
|
||
|
sprintf(buf, "%s", value->int_val ? "true" : "false");
|
||
|
|
||
|
set_object_array_element(env, strArray, buf, *array_index);
|
||
|
*array_index += 1;
|
||
|
} else if (prop_type == DBUS_TYPE_ARRAY) {
|
||
|
if (value_type == DBUS_TYPE_BYTE) {
|
||
|
set_object_array_element(env, strArray, (const char *) value->str_val,
|
||
|
*array_index);
|
||
|
*array_index += 1;
|
||
|
} else {
|
||
|
// Write the length first
|
||
|
sprintf(buf1, "%d", len);
|
||
|
set_object_array_element(env, strArray, buf1, *array_index);
|
||
|
*array_index += 1;
|
||
|
|
||
|
prop_val = value->array_val;
|
||
|
for (i = 0; i < len; i++) {
|
||
|
set_object_array_element(env, strArray, prop_val[i], *array_index);
|
||
|
*array_index += 1;
|
||
|
}
|
||
|
}
|
||
|
} else {
|
||
|
set_object_array_element(env, strArray, (const char *) value->str_val, *array_index);
|
||
|
*array_index += 1;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
jobjectArray parse_properties(JNIEnv *env, DBusMessageIter *iter, Properties *properties,
|
||
|
const int max_num_properties) {
|
||
|
DBusMessageIter dict_entry, dict;
|
||
|
jobjectArray strArray = NULL;
|
||
|
property_value value;
|
||
|
int i, size = 0,array_index = 0, value_type;
|
||
|
int len = 0, prop_type = DBUS_TYPE_INVALID, prop_index = -1, type;
|
||
|
struct {
|
||
|
property_value value;
|
||
|
int value_type;
|
||
|
int len;
|
||
|
bool used;
|
||
|
} values[max_num_properties];
|
||
|
int t, j;
|
||
|
|
||
|
jclass stringClass = env->FindClass("java/lang/String");
|
||
|
DBusError err;
|
||
|
dbus_error_init(&err);
|
||
|
|
||
|
for (i = 0; i < max_num_properties; i++) {
|
||
|
values[i].used = false;
|
||
|
}
|
||
|
|
||
|
if(dbus_message_iter_get_arg_type(iter) != DBUS_TYPE_ARRAY)
|
||
|
goto failure;
|
||
|
dbus_message_iter_recurse(iter, &dict);
|
||
|
do {
|
||
|
len = 0;
|
||
|
value_type = 0;
|
||
|
if (dbus_message_iter_get_arg_type(&dict) != DBUS_TYPE_DICT_ENTRY)
|
||
|
goto failure;
|
||
|
dbus_message_iter_recurse(&dict, &dict_entry);
|
||
|
|
||
|
if (!get_property(dict_entry, properties, max_num_properties, &prop_index,
|
||
|
&value, &value_type, &len)) {
|
||
|
size += 2;
|
||
|
if (properties[prop_index].type == DBUS_TYPE_ARRAY &&
|
||
|
value_type != DBUS_TYPE_BYTE)
|
||
|
size += len;
|
||
|
values[prop_index].value = value;
|
||
|
values[prop_index].value_type = value_type;
|
||
|
values[prop_index].len = len;
|
||
|
values[prop_index].used = true;
|
||
|
} else {
|
||
|
goto failure;
|
||
|
}
|
||
|
} while(dbus_message_iter_next(&dict));
|
||
|
|
||
|
strArray = env->NewObjectArray(size, stringClass, NULL);
|
||
|
|
||
|
for (i = 0; i < max_num_properties; i++) {
|
||
|
if (values[i].used) {
|
||
|
create_prop_array(env, strArray, &properties[i], &values[i].value,
|
||
|
values[i].value_type, values[i].len, &array_index);
|
||
|
|
||
|
if (properties[i].type == DBUS_TYPE_ARRAY && values[i].used) {
|
||
|
if (values[i].value_type == DBUS_TYPE_BYTE &&
|
||
|
values[i].value.str_val != NULL) {
|
||
|
free(values[i].value.str_val);
|
||
|
} else if (values[i].value.array_val != NULL) {
|
||
|
free(values[i].value.array_val);
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
return strArray;
|
||
|
|
||
|
failure:
|
||
|
if (dbus_error_is_set(&err))
|
||
|
LOG_AND_FREE_DBUS_ERROR(&err);
|
||
|
for (i = 0; i < max_num_properties; i++)
|
||
|
if (properties[i].type == DBUS_TYPE_ARRAY && values[i].used == true
|
||
|
&& values[i].value.array_val != NULL)
|
||
|
free(values[i].value.array_val);
|
||
|
return NULL;
|
||
|
}
|
||
|
|
||
|
jobjectArray parse_property_change(JNIEnv *env, DBusMessage *msg,
|
||
|
Properties *properties, int max_num_properties) {
|
||
|
DBusMessageIter iter;
|
||
|
DBusError err;
|
||
|
jobjectArray strArray = NULL;
|
||
|
jclass stringClass= env->FindClass("java/lang/String");
|
||
|
int i, len = 0, prop_index = -1;
|
||
|
int array_index = 0, size = 0, value_type = 0;
|
||
|
property_value value;
|
||
|
|
||
|
dbus_error_init(&err);
|
||
|
if (!dbus_message_iter_init(msg, &iter))
|
||
|
goto failure;
|
||
|
|
||
|
if (!get_property(iter, properties, max_num_properties,
|
||
|
&prop_index, &value, &value_type, &len)) {
|
||
|
size += 2;
|
||
|
if (properties[prop_index].type == DBUS_TYPE_ARRAY &&
|
||
|
value_type != DBUS_TYPE_BYTE) {
|
||
|
size += len;
|
||
|
}
|
||
|
|
||
|
strArray = env->NewObjectArray(size, stringClass, NULL);
|
||
|
|
||
|
create_prop_array(env, strArray, &properties[prop_index],
|
||
|
&value, value_type, len, &array_index);
|
||
|
|
||
|
if (properties[prop_index].type == DBUS_TYPE_ARRAY) {
|
||
|
if (value_type == DBUS_TYPE_BYTE &&
|
||
|
value.str_val != NULL) {
|
||
|
free(value.str_val);
|
||
|
} else if (value.array_val != NULL) {
|
||
|
free(value.array_val);
|
||
|
}
|
||
|
}
|
||
|
return strArray;
|
||
|
}
|
||
|
failure:
|
||
|
LOG_AND_FREE_DBUS_ERROR_WITH_MSG(&err, msg);
|
||
|
return NULL;
|
||
|
}
|
||
|
|
||
|
jobjectArray parse_adapter_property_change(JNIEnv *env, DBusMessage *msg) {
|
||
|
return parse_property_change(env, msg, (Properties *) &adapter_properties,
|
||
|
sizeof(adapter_properties) / sizeof(Properties));
|
||
|
}
|
||
|
|
||
|
jobjectArray parse_remote_device_property_change(JNIEnv *env, DBusMessage *msg) {
|
||
|
return parse_property_change(env, msg, (Properties *) &remote_device_properties,
|
||
|
sizeof(remote_device_properties) / sizeof(Properties));
|
||
|
}
|
||
|
|
||
|
jobjectArray parse_adapter_properties(JNIEnv *env, DBusMessageIter *iter) {
|
||
|
return parse_properties(env, iter, (Properties *) &adapter_properties,
|
||
|
sizeof(adapter_properties) / sizeof(Properties));
|
||
|
}
|
||
|
|
||
|
jobjectArray parse_remote_device_properties(JNIEnv *env, DBusMessageIter *iter) {
|
||
|
return parse_properties(env, iter, (Properties *) &remote_device_properties,
|
||
|
sizeof(remote_device_properties) / sizeof(Properties));
|
||
|
}
|
||
|
|
||
|
jobjectArray parse_gatt_service_properties(JNIEnv *env, DBusMessageIter *iter) {
|
||
|
return parse_properties(env, iter, (Properties *) &gatt_service_properties,
|
||
|
sizeof(gatt_service_properties) / sizeof(Properties));
|
||
|
}
|
||
|
|
||
|
jobjectArray parse_gatt_characteristic_properties(JNIEnv *env, DBusMessageIter *iter) {
|
||
|
return parse_properties(env, iter, (Properties *) &gatt_characteristic_properties,
|
||
|
sizeof(gatt_characteristic_properties) / sizeof(Properties));
|
||
|
}
|
||
|
|
||
|
int get_bdaddr(const char *str, bdaddr_t *ba) {
|
||
|
char *d = ((char *)ba) + 5, *endp;
|
||
|
int i;
|
||
|
for(i = 0; i < 6; i++) {
|
||
|
*d-- = strtol(str, &endp, 16);
|
||
|
if (*endp != ':' && i != 5) {
|
||
|
memset(ba, 0, sizeof(bdaddr_t));
|
||
|
return -1;
|
||
|
}
|
||
|
str = endp + 1;
|
||
|
}
|
||
|
return 0;
|
||
|
}
|
||
|
|
||
|
void get_bdaddr_as_string(const bdaddr_t *ba, char *str) {
|
||
|
const uint8_t *b = (const uint8_t *)ba;
|
||
|
sprintf(str, "%2.2X:%2.2X:%2.2X:%2.2X:%2.2X:%2.2X",
|
||
|
b[5], b[4], b[3], b[2], b[1], b[0]);
|
||
|
}
|
||
|
|
||
|
bool debug_no_encrypt() {
|
||
|
return false;
|
||
|
#if 0
|
||
|
char value[PROPERTY_VALUE_MAX] = "";
|
||
|
|
||
|
property_get("debug.bt.no_encrypt", value, "");
|
||
|
if (!strncmp("true", value, PROPERTY_VALUE_MAX) ||
|
||
|
!strncmp("1", value, PROPERTY_VALUE_MAX)) {
|
||
|
LOGD("mandatory bluetooth encryption disabled");
|
||
|
return true;
|
||
|
} else {
|
||
|
return false;
|
||
|
}
|
||
|
#endif
|
||
|
}
|
||
|
#endif
|
||
|
|
||
|
} /* namespace android */
|