/* ** 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 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 #include #include #include #include #include #ifdef HAVE_BLUETOOTH #include #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}, {"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} }; 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}, }; 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; } typedef struct { void (*user_cb)(DBusMessage *, void *, void *); void *user; void *nat; JNIEnv *env; } dbus_async_call_t; 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; } 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; } 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); } //TODO(): Remove code duplication between parse_properties and //parse_property_change jobjectArray parse_properties(JNIEnv *env, DBusMessageIter *iter, Properties *properties, const int max_num_properties) { DBusMessageIter dict_entry, dict, prop_val, device_val, array_val_iter; jobjectArray strArray = NULL; char * property; char values[max_num_properties][256]; char **uuid_array = NULL; char **device_path = NULL; char **array_elements = NULL; char *string_val; uint32_t int_val, bool_val; int i, j, k, type, array_type, num_array_elements = 0; int ret, num_properties = 0, num_uuids = 0, num_devices = 0; jclass stringClass = env->FindClass("java/lang/String"); DBusError err; dbus_error_init(&err); for (i = 0; i < max_num_properties; i++) memset(values[i], '\0', 128 * sizeof(char)); if(dbus_message_iter_get_arg_type(iter) != DBUS_TYPE_ARRAY) goto failure; dbus_message_iter_recurse(iter, &dict); do { if (dbus_message_iter_get_arg_type(&dict) != DBUS_TYPE_DICT_ENTRY) goto failure; dbus_message_iter_recurse(&dict, &dict_entry); if (dbus_message_iter_get_arg_type(&dict_entry) != DBUS_TYPE_STRING) goto failure; dbus_message_iter_get_basic(&dict_entry, &property); if (!dbus_message_iter_next(&dict_entry)) goto failure; if (dbus_message_iter_get_arg_type(&dict_entry) != DBUS_TYPE_VARIANT) goto failure; for (i = 0; i < max_num_properties; i++) { if (!strncmp(properties[i].name, property, strlen(property))) { num_properties ++; break; } } if (i == max_num_properties) goto failure; type = properties[i].type; dbus_message_iter_recurse(&dict_entry, &prop_val); if(dbus_message_iter_get_arg_type(&prop_val) != type) { LOGE("Property type mismatch in parse_properties: %d, expected:%d", dbus_message_iter_get_arg_type(&prop_val), type); goto failure; } switch(type) { case DBUS_TYPE_STRING: case DBUS_TYPE_OBJECT_PATH: dbus_message_iter_get_basic(&prop_val, &string_val); strcpy(values[i], string_val); break; case DBUS_TYPE_UINT32: case DBUS_TYPE_INT16: dbus_message_iter_get_basic(&prop_val, &int_val); sprintf(values[i], "%d", int_val); break; case DBUS_TYPE_BOOLEAN: dbus_message_iter_get_basic(&prop_val, &bool_val); sprintf(values[i], "%s", bool_val ? "true" : "false"); 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); num_array_elements = 0; if (array_type == DBUS_TYPE_OBJECT_PATH || array_type == DBUS_TYPE_STRING){ do { num_array_elements++; } while(dbus_message_iter_next(&array_val_iter)); dbus_message_iter_recurse(&prop_val, &array_val_iter); // Allocate an array array_elements = (char **)malloc(sizeof(char *) * num_array_elements); if (!array_elements) goto failure; j = 0; do { dbus_message_iter_get_basic(&array_val_iter, &array_elements[j]); j++; } while(dbus_message_iter_next(&array_val_iter)); if (!strncmp(property, "UUIDs", strlen("UUIDs"))) { num_uuids = num_array_elements; uuid_array = array_elements; } else { num_devices = num_array_elements; device_path = array_elements; } } break; default: goto failure; } } while(dbus_message_iter_next(&dict)); // Convert it to a array of strings. strArray = env->NewObjectArray((num_properties + num_array_elements) * 2, stringClass, NULL); j = 0; for (i = 0; i < max_num_properties; i++) { if (properties[i].type == DBUS_TYPE_ARRAY) { if (!strncmp(properties[i].name, "UUIDs", strlen("UUIDs"))) { num_array_elements = num_uuids; array_elements = uuid_array; } else { num_array_elements = num_devices; array_elements = device_path; } for (k = 0; k < num_array_elements; k++) { set_object_array_element(env, strArray, properties[i].name, j++); set_object_array_element(env, strArray, array_elements[k], j++); } } else if (values[i][0] != '\0') { set_object_array_element(env, strArray, properties[i].name, j++); set_object_array_element(env, strArray, values[i], j++); } } if (uuid_array) free(uuid_array); if (device_path) free(device_path); return strArray; failure: if (dbus_error_is_set(&err)) LOG_AND_FREE_DBUS_ERROR(&err); if (uuid_array) free(uuid_array); if (device_path) free(device_path); return NULL; } jobjectArray create_prop_array(JNIEnv *env, Properties *properties, int prop_index, void *value, int len ) { jclass stringClass= env->FindClass("java/lang/String"); char **prop_val = NULL; char buf[32] = {'\0'}; int i, j; jobjectArray strArray = env->NewObjectArray(1 + len, stringClass, NULL); j = 0; set_object_array_element(env, strArray, properties[prop_index].name, j++); if (properties[prop_index].type == DBUS_TYPE_UINT32) { sprintf(buf, "%d", *(int *) value); set_object_array_element(env, strArray, buf, j++); } else if (properties[prop_index].type == DBUS_TYPE_BOOLEAN) { sprintf(buf, "%s", *(int *) value ? "true" : "false"); set_object_array_element(env, strArray, buf, j++); } else if (properties[prop_index].type == DBUS_TYPE_ARRAY) { prop_val = (char **) value; for (i = 0; i < len; i++) set_object_array_element(env, strArray, prop_val[i], j++); } else { set_object_array_element(env, strArray, (const char *) value, j++); } if (prop_val) free (prop_val); return strArray; } jobjectArray parse_property_change(JNIEnv *env, DBusMessage *msg, Properties *properties, int max_num_properties) { DBusMessageIter iter, prop_val, array_val_iter; DBusError err; void *value; char *property; uint32_t array_type; int i, j, type, len, prop_index; dbus_error_init(&err); if (!dbus_message_iter_init(msg, &iter)) goto failure; if (dbus_message_iter_get_arg_type(&iter) != DBUS_TYPE_STRING) goto failure; dbus_message_iter_get_basic(&iter, &property); if (!dbus_message_iter_next(&iter)) goto failure; if (dbus_message_iter_get_arg_type(&iter) != DBUS_TYPE_VARIANT) goto failure; for (i = 0; i < max_num_properties; i++) { if (!strncmp(property, properties[i].name, strlen(properties[i].name))) break; } prop_index = i; if (i == max_num_properties) goto failure; 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 parse_properties: %d, expected:%d", dbus_message_iter_get_arg_type(&prop_val), type); goto failure; } switch(type) { case DBUS_TYPE_STRING: case DBUS_TYPE_OBJECT_PATH: dbus_message_iter_get_basic(&prop_val, &value); len = 1; break; case DBUS_TYPE_UINT32: case DBUS_TYPE_INT16: case DBUS_TYPE_BOOLEAN: uint32_t int_val; dbus_message_iter_get_basic(&prop_val, &int_val); value = &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; if (array_type == DBUS_TYPE_OBJECT_PATH || array_type == DBUS_TYPE_STRING){ do { len ++; } while(dbus_message_iter_next(&array_val_iter)); dbus_message_iter_recurse(&prop_val, &array_val_iter); // Allocate an array of char * char **tmp = (char **)malloc(sizeof(char *) * len); if (!tmp) goto failure; j = 0; do { dbus_message_iter_get_basic(&array_val_iter, &tmp[j]); j ++; } while(dbus_message_iter_next(&array_val_iter)); value = (char **) tmp; } break; default: goto failure; } return create_prop_array(env, properties, prop_index, value, len); 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)); } 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 */