diff options
author | Mathias Agopian <mathias@google.com> | 2009-06-16 12:38:55 -0700 |
---|---|---|
committer | Mathias Agopian <mathias@google.com> | 2009-06-16 12:38:55 -0700 |
commit | 69f066c8fc42b9f0acc5c41f8ffd972f8d6d0584 (patch) | |
tree | 3a8bc941200fa85a32a21657ac69ec9beb9a6463 /core/jni | |
parent | 151e859e0fc3a930bdf6d270d275e69e9eba0cbf (diff) | |
parent | b0b160ae50497966666bcdcaf974eca2643acfd3 (diff) | |
download | frameworks_base-69f066c8fc42b9f0acc5c41f8ffd972f8d6d0584.zip frameworks_base-69f066c8fc42b9f0acc5c41f8ffd972f8d6d0584.tar.gz frameworks_base-69f066c8fc42b9f0acc5c41f8ffd972f8d6d0584.tar.bz2 |
Merge commit 'goog/master' into merge_master
Diffstat (limited to 'core/jni')
-rw-r--r-- | core/jni/Android.mk | 1 | ||||
-rw-r--r-- | core/jni/AndroidRuntime.cpp | 2 | ||||
-rw-r--r-- | core/jni/android_backup_BackupDataInput.cpp | 173 | ||||
-rw-r--r-- | core/jni/android_backup_BackupDataOutput.cpp | 51 | ||||
-rw-r--r-- | core/jni/android_backup_FileBackupHelper.cpp | 28 | ||||
-rw-r--r-- | core/jni/android_bluetooth_common.cpp | 363 | ||||
-rw-r--r-- | core/jni/android_bluetooth_common.h | 17 | ||||
-rw-r--r-- | core/jni/android_hardware_Camera.cpp | 14 | ||||
-rw-r--r-- | core/jni/android_location_GpsLocationProvider.cpp | 7 | ||||
-rw-r--r-- | core/jni/android_net_wifi_Wifi.cpp | 17 | ||||
-rw-r--r-- | core/jni/android_server_BluetoothA2dpService.cpp | 322 | ||||
-rw-r--r-- | core/jni/android_server_BluetoothDeviceService.cpp | 954 | ||||
-rw-r--r-- | core/jni/android_server_BluetoothEventLoop.cpp | 637 | ||||
-rw-r--r-- | core/jni/android_util_Process.cpp | 54 | ||||
-rw-r--r-- | core/jni/com_google_android_gles_jni_GLImpl.cpp | 45 |
15 files changed, 1321 insertions, 1364 deletions
diff --git a/core/jni/Android.mk b/core/jni/Android.mk index f6f4c70..9066233 100644 --- a/core/jni/Android.mk +++ b/core/jni/Android.mk @@ -117,6 +117,7 @@ LOCAL_SRC_FILES:= \ android_location_GpsLocationProvider.cpp \ com_android_internal_os_ZygoteInit.cpp \ com_android_internal_graphics_NativeUtils.cpp \ + android_backup_BackupDataInput.cpp \ android_backup_BackupDataOutput.cpp \ android_backup_FileBackupHelper.cpp diff --git a/core/jni/AndroidRuntime.cpp b/core/jni/AndroidRuntime.cpp index 6de37f0..4512fef 100644 --- a/core/jni/AndroidRuntime.cpp +++ b/core/jni/AndroidRuntime.cpp @@ -153,6 +153,7 @@ extern int register_android_ddm_DdmHandleNativeHeap(JNIEnv *env); extern int register_com_android_internal_os_ZygoteInit(JNIEnv* env); extern int register_android_util_Base64(JNIEnv* env); extern int register_android_location_GpsLocationProvider(JNIEnv* env); +extern int register_android_backup_BackupDataInput(JNIEnv *env); extern int register_android_backup_BackupDataOutput(JNIEnv *env); extern int register_android_backup_FileBackupHelper(JNIEnv *env); @@ -1168,6 +1169,7 @@ static const RegJNIRec gRegJNI[] = { REG_JNI(register_android_ddm_DdmHandleNativeHeap), REG_JNI(register_android_util_Base64), REG_JNI(register_android_location_GpsLocationProvider), + REG_JNI(register_android_backup_BackupDataInput), REG_JNI(register_android_backup_BackupDataOutput), REG_JNI(register_android_backup_FileBackupHelper), }; diff --git a/core/jni/android_backup_BackupDataInput.cpp b/core/jni/android_backup_BackupDataInput.cpp new file mode 100644 index 0000000..5b2fb73 --- /dev/null +++ b/core/jni/android_backup_BackupDataInput.cpp @@ -0,0 +1,173 @@ +/* + * Copyright (C) 2009 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 "FileBackupHelper_native" +#include <utils/Log.h> + +#include "JNIHelp.h" +#include <android_runtime/AndroidRuntime.h> + +#include <utils/BackupHelpers.h> + +namespace android +{ + +// java.io.FileDescriptor +static jfieldID s_descriptorField = 0; + +// android.backup.BackupDataInput$EntityHeader +static jfieldID s_keyField = 0; +static jfieldID s_dataSizeField = 0; + +static int +ctor_native(JNIEnv* env, jobject clazz, jobject fileDescriptor) +{ + int err; + + int fd = env->GetIntField(fileDescriptor, s_descriptorField); + if (fd == -1) { + return NULL; + } + + return (int)new BackupDataReader(fd); +} + +static void +dtor_native(JNIEnv* env, jobject clazz, int r) +{ + delete (BackupDataReader*)r; +} + +static jint +readNextHeader_native(JNIEnv* env, jobject clazz, int r, jobject entity) +{ + int err; + BackupDataReader* reader = (BackupDataReader*)r; + + err = reader->Status(); + if (err != 0) { + return err < 0 ? err : -1; + } + + int type = 0; + + err = reader->ReadNextHeader(&type); + if (err == EIO) { + // Clean EOF with no footer block; just claim we're done + return 1; + } + + if (err != 0) { + return err < 0 ? err : -1; + } + + switch (type) { + case BACKUP_HEADER_APP_V1: + { + String8 packageName; + int cookie; + err = reader->ReadAppHeader(&packageName, &cookie); + if (err != 0) { + LOGD("ReadAppHeader() returned %d; aborting", err); + return err < 0 ? err : -1; + } + break; + } + case BACKUP_HEADER_ENTITY_V1: + { + String8 key; + size_t dataSize; + err = reader->ReadEntityHeader(&key, &dataSize); + if (err != 0) { + LOGD("ReadEntityHeader(); aborting", err); + return err < 0 ? err : -1; + } + // TODO: Set the fields in the entity object + jstring keyStr = env->NewStringUTF(key.string()); + env->SetObjectField(entity, s_keyField, keyStr); + env->SetIntField(entity, s_dataSizeField, dataSize); + return 0; + } + case BACKUP_FOOTER_APP_V1: + { + break; + } + default: + LOGD("Unknown header type: 0x%08x\n", type); + return -1; + } + + // done + return 1; +} + +static jint +readEntityData_native(JNIEnv* env, jobject clazz, int r, jbyteArray data, int size) +{ + int err; + BackupDataReader* reader = (BackupDataReader*)r; + + if (env->GetArrayLength(data) < size) { + // size mismatch + return -1; + } + + jbyte* dataBytes = env->GetByteArrayElements(data, NULL); + if (dataBytes == NULL) { + return -2; + } + + err = reader->ReadEntityData(dataBytes, size); + + env->ReleaseByteArrayElements(data, dataBytes, 0); + + return err; +} + +static const JNINativeMethod g_methods[] = { + { "ctor", "(Ljava/io/FileDescriptor;)I", (void*)ctor_native }, + { "dtor", "(I)V", (void*)dtor_native }, + { "readNextHeader_native", "(ILandroid/backup/BackupDataInput$EntityHeader;)I", + (void*)readNextHeader_native }, + { "readEntityData_native", "(I[BI)I", (void*)readEntityData_native }, +}; + +int register_android_backup_BackupDataInput(JNIEnv* env) +{ + //LOGD("register_android_backup_BackupDataInput"); + + jclass clazz; + + clazz = env->FindClass("java/io/FileDescriptor"); + LOG_FATAL_IF(clazz == NULL, "Unable to find class java.io.FileDescriptor"); + s_descriptorField = env->GetFieldID(clazz, "descriptor", "I"); + LOG_FATAL_IF(s_descriptorField == NULL, + "Unable to find descriptor field in java.io.FileDescriptor"); + + clazz = env->FindClass("android/backup/BackupDataInput$EntityHeader"); + LOG_FATAL_IF(clazz == NULL, "Unable to find class android.backup.BackupDataInput.EntityHeader"); + s_keyField = env->GetFieldID(clazz, "key", "Ljava/lang/String;"); + LOG_FATAL_IF(s_keyField == NULL, + "Unable to find key field in android.backup.BackupDataInput.EntityHeader"); + s_dataSizeField = env->GetFieldID(clazz, "dataSize", "I"); + LOG_FATAL_IF(s_dataSizeField == NULL, + "Unable to find dataSize field in android.backup.BackupDataInput.EntityHeader"); + + return AndroidRuntime::registerNativeMethods(env, "android/backup/BackupDataInput", + g_methods, NELEM(g_methods)); +} + +} diff --git a/core/jni/android_backup_BackupDataOutput.cpp b/core/jni/android_backup_BackupDataOutput.cpp index aab1233..6362439 100644 --- a/core/jni/android_backup_BackupDataOutput.cpp +++ b/core/jni/android_backup_BackupDataOutput.cpp @@ -28,7 +28,7 @@ namespace android static jfieldID s_descriptorField = 0; static int -ctor_native(JNIEnv* env, jobject This, jobject fileDescriptor) +ctor_native(JNIEnv* env, jobject clazz, jobject fileDescriptor) { int err; @@ -41,19 +41,62 @@ ctor_native(JNIEnv* env, jobject This, jobject fileDescriptor) } static void -dtor_native(JNIEnv* env, jobject This, int fd) +dtor_native(JNIEnv* env, jobject clazz, int w) { - delete (BackupDataWriter*)fd; + delete (BackupDataWriter*)w; +} + +static jint +writeEntityHeader_native(JNIEnv* env, jobject clazz, int w, jstring key, int dataSize) +{ + int err; + BackupDataWriter* writer = (BackupDataWriter*)w; + + const char* keyUTF = env->GetStringUTFChars(key, NULL); + if (keyUTF == NULL) { + return -1; + } + + err = writer->WriteEntityHeader(String8(keyUTF), dataSize); + + env->ReleaseStringUTFChars(key, keyUTF); + + return err; +} + +static jint +writeEntityData_native(JNIEnv* env, jobject clazz, int w, jbyteArray data, int size) +{ + int err; + BackupDataWriter* writer = (BackupDataWriter*)w; + + if (env->GetArrayLength(data) > size) { + // size mismatch + return -1; + } + + jbyte* dataBytes = env->GetByteArrayElements(data, NULL); + if (dataBytes == NULL) { + return -1; + } + + err = writer->WriteEntityData(dataBytes, size); + + env->ReleaseByteArrayElements(data, dataBytes, JNI_ABORT); + + return err; } static const JNINativeMethod g_methods[] = { { "ctor", "(Ljava/io/FileDescriptor;)I", (void*)ctor_native }, { "dtor", "(I)V", (void*)dtor_native }, + { "writeEntityHeader_native", "(ILjava/lang/String;I)I", (void*)writeEntityHeader_native }, + { "writeEntityData_native", "(I[BI)I", (void*)writeEntityData_native }, }; int register_android_backup_BackupDataOutput(JNIEnv* env) { - LOGD("register_android_backup_BackupDataOutput"); + //LOGD("register_android_backup_BackupDataOutput"); jclass clazz; diff --git a/core/jni/android_backup_FileBackupHelper.cpp b/core/jni/android_backup_FileBackupHelper.cpp index a46f37b..418db8a 100644 --- a/core/jni/android_backup_FileBackupHelper.cpp +++ b/core/jni/android_backup_FileBackupHelper.cpp @@ -25,49 +25,55 @@ namespace android { +// java.io.FileDescriptor static jfieldID s_descriptorField = 0; static int -performBackup_native(JNIEnv* env, jobject clazz, jstring basePath, jobject oldState, int data, - jobject newState, jobjectArray files) +performBackup_native(JNIEnv* env, jobject clazz, jobject oldState, int data, + jobject newState, jobjectArray files, jobjectArray keys) { int err; // all parameters have already been checked against null - LOGD("oldState=%p newState=%p data=%p\n", oldState, newState, data); int oldStateFD = oldState != NULL ? env->GetIntField(oldState, s_descriptorField) : -1; int newStateFD = env->GetIntField(newState, s_descriptorField); BackupDataWriter* dataStream = (BackupDataWriter*)data; - char const* basePathUTF = env->GetStringUTFChars(basePath, NULL); - LOGD("basePathUTF=\"%s\"\n", basePathUTF); const int fileCount = env->GetArrayLength(files); char const** filesUTF = (char const**)malloc(sizeof(char*)*fileCount); for (int i=0; i<fileCount; i++) { filesUTF[i] = env->GetStringUTFChars((jstring)env->GetObjectArrayElement(files, i), NULL); } - err = back_up_files(oldStateFD, dataStream, newStateFD, basePathUTF, filesUTF, fileCount); + const int keyCount = env->GetArrayLength(keys); + char const** keysUTF = (char const**)malloc(sizeof(char*)*keyCount); + for (int i=0; i<keyCount; i++) { + keysUTF[i] = env->GetStringUTFChars((jstring)env->GetObjectArrayElement(keys, i), NULL); + } + + err = back_up_files(oldStateFD, dataStream, newStateFD, filesUTF, keysUTF, fileCount); for (int i=0; i<fileCount; i++) { env->ReleaseStringUTFChars((jstring)env->GetObjectArrayElement(files, i), filesUTF[i]); } free(filesUTF); - env->ReleaseStringUTFChars(basePath, basePathUTF); + + for (int i=0; i<keyCount; i++) { + env->ReleaseStringUTFChars((jstring)env->GetObjectArrayElement(keys, i), keysUTF[i]); + } + free(keysUTF); return err; } static const JNINativeMethod g_methods[] = { { "performBackup_native", - "(Ljava/lang/String;Ljava/io/FileDescriptor;ILjava/io/FileDescriptor;[Ljava/lang/String;)I", - (void*)performBackup_native }, + "(Ljava/io/FileDescriptor;ILjava/io/FileDescriptor;[Ljava/lang/String;[Ljava/lang/String;)I", + (void*)performBackup_native }, }; int register_android_backup_FileBackupHelper(JNIEnv* env) { - LOGD("register_android_backup_FileBackupHelper"); - jclass clazz; clazz = env->FindClass("java/io/FileDescriptor"); diff --git a/core/jni/android_bluetooth_common.cpp b/core/jni/android_bluetooth_common.cpp index ee672f7..8361212 100644 --- a/core/jni/android_bluetooth_common.cpp +++ b/core/jni/android_bluetooth_common.cpp @@ -36,6 +36,38 @@ 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); @@ -332,6 +364,44 @@ jboolean dbus_returns_boolean(JNIEnv *env, DBusMessage *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; @@ -353,11 +423,8 @@ jobjectArray dbus_returns_array_of_strings(JNIEnv *env, DBusMessage *reply) { stringClass = env->FindClass("java/lang/String"); strArray = env->NewObjectArray(len, stringClass, NULL); - for (i = 0; i < len; i++) { - //LOGV("%s: array[%d] = [%s]", __FUNCTION__, i, list[i]); - env->SetObjectArrayElement(strArray, i, - env->NewStringUTF(list[i])); - } + 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); } @@ -390,6 +457,292 @@ jbyteArray dbus_returns_array_of_bytes(JNIEnv *env, DBusMessage *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; diff --git a/core/jni/android_bluetooth_common.h b/core/jni/android_bluetooth_common.h index e5b8813..ef9b66b 100644 --- a/core/jni/android_bluetooth_common.h +++ b/core/jni/android_bluetooth_common.h @@ -68,6 +68,7 @@ jfieldID get_field(JNIEnv *env, struct event_loop_native_data_t { DBusConnection *conn; + const char *adapter; /* protects the thread */ pthread_mutex_t thread_mutex; @@ -89,6 +90,12 @@ struct event_loop_native_data_t { jobject me; }; +struct _Properties { + char name[32]; + int type; +}; +typedef struct _Properties Properties; + dbus_bool_t dbus_func_args_async(JNIEnv *env, DBusConnection *conn, int timeout_ms, @@ -142,8 +149,18 @@ jint dbus_returns_uint32(JNIEnv *env, DBusMessage *reply); jstring dbus_returns_string(JNIEnv *env, DBusMessage *reply); jboolean dbus_returns_boolean(JNIEnv *env, DBusMessage *reply); jobjectArray dbus_returns_array_of_strings(JNIEnv *env, DBusMessage *reply); +jobjectArray dbus_returns_array_of_object_path(JNIEnv *env, DBusMessage *reply); jbyteArray dbus_returns_array_of_bytes(JNIEnv *env, DBusMessage *reply); +jobjectArray parse_properties(JNIEnv *env, DBusMessageIter *iter, Properties *properties, + const int max_num_properties); +jobjectArray parse_property_change(JNIEnv *env, DBusMessage *msg, + Properties *properties, int max_num_properties); +jobjectArray parse_adapter_properties(JNIEnv *env, DBusMessageIter *iter); +jobjectArray parse_remote_device_properties(JNIEnv *env, DBusMessageIter *iter); +jobjectArray parse_remote_device_property_change(JNIEnv *env, DBusMessage *msg); +jobjectArray parse_adapter_property_change(JNIEnv *env, DBusMessage *msg); +void append_variant(DBusMessageIter *iter, int type, void *val); int get_bdaddr(const char *str, bdaddr_t *ba); void get_bdaddr_as_string(const bdaddr_t *ba, char *str); diff --git a/core/jni/android_hardware_Camera.cpp b/core/jni/android_hardware_Camera.cpp index 31086b8..217e649 100644 --- a/core/jni/android_hardware_Camera.cpp +++ b/core/jni/android_hardware_Camera.cpp @@ -115,15 +115,13 @@ static void android_hardware_Camera_native_setup(JNIEnv *env, jobject thiz, jobj // make sure camera hardware is alive if (camera->getStatus() != NO_ERROR) { - jniThrowException(env, "java/io/IOException", "Camera initialization failed"); + jniThrowException(env, "java/lang/RuntimeException", "Camera initialization failed"); return; } jclass clazz = env->GetObjectClass(thiz); if (clazz == NULL) { - LOGE("Can't find android/hardware/Camera"); - // XXX no idea what to throw here, can this even happen? - jniThrowException(env, "java/lang/Exception", NULL); + jniThrowException(env, "java/lang/RuntimeException", "Can't find android/hardware/Camera"); return; } @@ -241,7 +239,7 @@ static void android_hardware_Camera_startPreview(JNIEnv *env, jobject thiz) if (camera == 0) return; if (camera->startPreview() != NO_ERROR) { - jniThrowException(env, "java/io/IOException", "startPreview failed"); + jniThrowException(env, "java/lang/RuntimeException", "startPreview failed"); return; } } @@ -305,7 +303,7 @@ static void android_hardware_Camera_autoFocus(JNIEnv *env, jobject thiz) c->setAutoFocusCallback(autofocus_callback_impl, context); if (c->autoFocus() != NO_ERROR) { - jniThrowException(env, "java/io/IOException", "autoFocus failed"); + jniThrowException(env, "java/lang/RuntimeException", "autoFocus failed"); } } @@ -398,7 +396,7 @@ static void android_hardware_Camera_takePicture(JNIEnv *env, jobject thiz) camera->setRawCallback(raw_callback, context); camera->setJpegCallback(jpeg_callback, context); if (camera->takePicture() != NO_ERROR) { - jniThrowException(env, "java/io/IOException", "takePicture failed"); + jniThrowException(env, "java/lang/RuntimeException", "takePicture failed"); return; } @@ -418,7 +416,7 @@ static void android_hardware_Camera_setParameters(JNIEnv *env, jobject thiz, jst env->ReleaseStringCritical(params, str); } if (camera->setParameters(params8) != NO_ERROR) { - jniThrowException(env, "java/lang/IllegalArgumentException", "setParameters failed"); + jniThrowException(env, "java/lang/RuntimeException", "setParameters failed"); return; } } diff --git a/core/jni/android_location_GpsLocationProvider.cpp b/core/jni/android_location_GpsLocationProvider.cpp index 0858741..5c4fb22 100644 --- a/core/jni/android_location_GpsLocationProvider.cpp +++ b/core/jni/android_location_GpsLocationProvider.cpp @@ -270,6 +270,12 @@ static void android_location_GpsLocationProvider_inject_time(JNIEnv* env, jobjec sGpsInterface->inject_time(time, timeReference, uncertainty); } +static void android_location_GpsLocationProvider_inject_location(JNIEnv* env, jobject obj, + jdouble latitude, jdouble longitude, jfloat accuracy) +{ + sGpsInterface->inject_location(latitude, longitude, accuracy); +} + static jboolean android_location_GpsLocationProvider_supports_xtra(JNIEnv* env, jobject obj) { if (!sGpsXtraInterface) { @@ -353,6 +359,7 @@ static JNINativeMethod sMethods[] = { {"native_wait_for_event", "()V", (void*)android_location_GpsLocationProvider_wait_for_event}, {"native_read_sv_status", "([I[F[F[F[I)I", (void*)android_location_GpsLocationProvider_read_sv_status}, {"native_inject_time", "(JJI)V", (void*)android_location_GpsLocationProvider_inject_time}, + {"native_inject_location", "(DDF)V", (void*)android_location_GpsLocationProvider_inject_location}, {"native_supports_xtra", "()Z", (void*)android_location_GpsLocationProvider_supports_xtra}, {"native_inject_xtra_data", "([BI)V", (void*)android_location_GpsLocationProvider_inject_xtra_data}, {"native_agps_data_conn_open", "(Ljava/lang/String;)V", (void*)android_location_GpsLocationProvider_agps_data_conn_open}, diff --git a/core/jni/android_net_wifi_Wifi.cpp b/core/jni/android_net_wifi_Wifi.cpp index 9f93e2f..75ae4d9 100644 --- a/core/jni/android_net_wifi_Wifi.cpp +++ b/core/jni/android_net_wifi_Wifi.cpp @@ -307,14 +307,15 @@ static jboolean android_net_wifi_stopPacketFiltering(JNIEnv* env, jobject clazz) return result; } -static jint android_net_wifi_getRssiCommand(JNIEnv* env, jobject clazz) +static jint android_net_wifi_getRssiHelper(const char *cmd) { char reply[256]; int rssi = -200; - if (doCommand("DRIVER RSSI", reply, sizeof(reply)) != 0) { + if (doCommand(cmd, reply, sizeof(reply)) != 0) { return (jint)-1; } + // reply comes back in the form "<SSID> rssi XX" where XX is the // number we're interested in. if we're associating, it returns "OK". // beware - <SSID> can contain spaces. @@ -328,6 +329,16 @@ static jint android_net_wifi_getRssiCommand(JNIEnv* env, jobject clazz) return (jint)rssi; } +static jint android_net_wifi_getRssiCommand(JNIEnv* env, jobject clazz) +{ + return android_net_wifi_getRssiHelper("DRIVER RSSI"); +} + +static jint android_net_wifi_getRssiApproxCommand(JNIEnv* env, jobject clazz) +{ + return android_net_wifi_getRssiHelper("DRIVER RSSI-APPROX"); +} + static jint android_net_wifi_getLinkSpeedCommand(JNIEnv* env, jobject clazz) { char reply[256]; @@ -523,6 +534,8 @@ static JNINativeMethod gWifiMethods[] = { { "setBluetoothCoexistenceScanModeCommand", "(Z)Z", (void*) android_net_wifi_setBluetoothCoexistenceScanModeCommand }, { "getRssiCommand", "()I", (void*) android_net_wifi_getRssiCommand }, + { "getRssiApproxCommand", "()I", + (void*) android_net_wifi_getRssiApproxCommand}, { "getLinkSpeedCommand", "()I", (void*) android_net_wifi_getLinkSpeedCommand }, { "getMacAddressCommand", "()Ljava/lang/String;", (void*) android_net_wifi_getMacAddressCommand }, { "saveConfigCommand", "()Z", (void*) android_net_wifi_saveConfigCommand }, diff --git a/core/jni/android_server_BluetoothA2dpService.cpp b/core/jni/android_server_BluetoothA2dpService.cpp index fe94642..153d16e 100644 --- a/core/jni/android_server_BluetoothA2dpService.cpp +++ b/core/jni/android_server_BluetoothA2dpService.cpp @@ -37,12 +37,7 @@ namespace android { #ifdef HAVE_BLUETOOTH -static jmethodID method_onHeadsetCreated; -static jmethodID method_onHeadsetRemoved; -static jmethodID method_onSinkConnected; -static jmethodID method_onSinkDisconnected; -static jmethodID method_onSinkPlaying; -static jmethodID method_onSinkStopped; +static jmethodID method_onSinkPropertyChanged; typedef struct { JavaVM *vm; @@ -53,11 +48,11 @@ typedef struct { static native_data_t *nat = NULL; // global native data -#endif - -#ifdef HAVE_BLUETOOTH -static void onConnectSinkResult(DBusMessage *msg, void *user, void *nat); -static void onDisconnectSinkResult(DBusMessage *msg, void *user, void *nat); +static Properties sink_properties[] = { + {"State", DBUS_TYPE_STRING}, + {"Connected", DBUS_TYPE_BOOLEAN}, + {"Playing", DBUS_TYPE_BOOLEAN}, + }; #endif /* Returns true on success (even if adapter is present but disabled). @@ -99,91 +94,58 @@ static void cleanupNative(JNIEnv* env, jobject object) { } #endif } -static jobjectArray listHeadsetsNative(JNIEnv *env, jobject object) { -#ifdef HAVE_BLUETOOTH - LOGV(__FUNCTION__); - if (nat) { - DBusMessage *reply = - dbus_func_args(env, nat->conn, "/org/bluez/audio", - "org.bluez.audio.Manager", "ListHeadsets", - DBUS_TYPE_INVALID); - return reply ? dbus_returns_array_of_strings(env, reply) : NULL; - } -#endif - return NULL; -} -static jstring createHeadsetNative(JNIEnv *env, jobject object, - jstring address) { +static jobjectArray getSinkPropertiesNative(JNIEnv *env, jobject object, + jstring path) { #ifdef HAVE_BLUETOOTH LOGV(__FUNCTION__); if (nat) { - const char *c_address = env->GetStringUTFChars(address, NULL); - LOGV("... address = %s\n", c_address); - DBusMessage *reply = - dbus_func_args(env, nat->conn, "/org/bluez/audio", - "org.bluez.audio.Manager", "CreateHeadset", - DBUS_TYPE_STRING, &c_address, - DBUS_TYPE_INVALID); - env->ReleaseStringUTFChars(address, c_address); - return reply ? dbus_returns_string(env, reply) : NULL; - } -#endif - return NULL; -} + DBusMessage *msg, *reply; + DBusError err; + dbus_error_init(&err); -static jstring removeHeadsetNative(JNIEnv *env, jobject object, jstring path) { -#ifdef HAVE_BLUETOOTH - LOGV(__FUNCTION__); - if (nat) { const char *c_path = env->GetStringUTFChars(path, NULL); - DBusMessage *reply = - dbus_func_args(env, nat->conn, "/org/bluez/audio", - "org.bluez.audio.Manager", "RemoveHeadset", - DBUS_TYPE_STRING, &c_path, - DBUS_TYPE_INVALID); + reply = dbus_func_args_timeout(env, + nat->conn, -1, c_path, + "org.bluez.AudioSink", "GetProperties", + DBUS_TYPE_INVALID); env->ReleaseStringUTFChars(path, c_path); - return reply ? dbus_returns_string(env, reply) : NULL; + if (!reply && dbus_error_is_set(&err)) { + LOG_AND_FREE_DBUS_ERROR_WITH_MSG(&err, reply); + return NULL; + } else if (!reply) { + LOGE("DBus reply is NULL in function %s", __FUNCTION__); + return NULL; + } + DBusMessageIter iter; + if (dbus_message_iter_init(reply, &iter)) + return parse_properties(env, &iter, (Properties *)&sink_properties, + sizeof(sink_properties) / sizeof(Properties)); } #endif return NULL; } -static jstring getAddressNative(JNIEnv *env, jobject object, jstring path) { -#ifdef HAVE_BLUETOOTH - LOGV(__FUNCTION__); - if (nat) { - const char *c_path = env->GetStringUTFChars(path, NULL); - DBusMessage *reply = - dbus_func_args(env, nat->conn, c_path, - "org.bluez.audio.Device", "GetAddress", - DBUS_TYPE_INVALID); - env->ReleaseStringUTFChars(path, c_path); - return reply ? dbus_returns_string(env, reply) : NULL; - } -#endif - return NULL; -} static jboolean connectSinkNative(JNIEnv *env, jobject object, jstring path) { #ifdef HAVE_BLUETOOTH LOGV(__FUNCTION__); if (nat) { const char *c_path = env->GetStringUTFChars(path, NULL); - size_t path_sz = env->GetStringUTFLength(path) + 1; - char *c_path_copy = (char *)malloc(path_sz); // callback data - strncpy(c_path_copy, c_path, path_sz); - - bool ret = - dbus_func_args_async(env, nat->conn, -1, - onConnectSinkResult, (void *)c_path_copy, nat, - c_path, - "org.bluez.audio.Sink", "Connect", - DBUS_TYPE_INVALID); + DBusError err; + dbus_error_init(&err); + DBusMessage *reply = + dbus_func_args_timeout(env, nat->conn, -1, c_path, + "org.bluez.AudioSink", "Connect", + DBUS_TYPE_INVALID); env->ReleaseStringUTFChars(path, c_path); - if (!ret) { - free(c_path_copy); + + if (!reply && dbus_error_is_set(&err)) { + LOG_AND_FREE_DBUS_ERROR_WITH_MSG(&err, reply); + return JNI_FALSE; + } else if (!reply) { + LOGE("DBus reply is NULL in function %s", __FUNCTION__); return JNI_FALSE; } return JNI_TRUE; @@ -198,19 +160,20 @@ static jboolean disconnectSinkNative(JNIEnv *env, jobject object, LOGV(__FUNCTION__); if (nat) { const char *c_path = env->GetStringUTFChars(path, NULL); - size_t path_sz = env->GetStringUTFLength(path) + 1; - char *c_path_copy = (char *)malloc(path_sz); // callback data - strncpy(c_path_copy, c_path, path_sz); - - bool ret = - dbus_func_args_async(env, nat->conn, -1, - onDisconnectSinkResult, (void *)c_path_copy, nat, - c_path, - "org.bluez.audio.Sink", "Disconnect", - DBUS_TYPE_INVALID); + DBusError err; + dbus_error_init(&err); + + DBusMessage *reply = + dbus_func_args_timeout(env, nat->conn, -1, c_path, + "org.bluez.AudioSink", "Disconnect", + DBUS_TYPE_INVALID); env->ReleaseStringUTFChars(path, c_path); - if (!ret) { - free(c_path_copy); + + if (!reply && dbus_error_is_set(&err)) { + LOG_AND_FREE_DBUS_ERROR_WITH_MSG(&err, reply); + return JNI_FALSE; + } else if (!reply) { + LOGE("DBus reply is NULL in function %s", __FUNCTION__); return JNI_FALSE; } return JNI_TRUE; @@ -219,93 +182,7 @@ static jboolean disconnectSinkNative(JNIEnv *env, jobject object, return JNI_FALSE; } -static jboolean isSinkConnectedNative(JNIEnv *env, jobject object, jstring path) { -#ifdef HAVE_BLUETOOTH - LOGV(__FUNCTION__); - if (nat) { - const char *c_path = env->GetStringUTFChars(path, NULL); - DBusMessage *reply = - dbus_func_args(env, nat->conn, c_path, - "org.bluez.audio.Sink", "IsConnected", - DBUS_TYPE_INVALID); - env->ReleaseStringUTFChars(path, c_path); - return reply ? dbus_returns_boolean(env, reply) : JNI_FALSE; - } -#endif - return JNI_FALSE; -} - #ifdef HAVE_BLUETOOTH -static void onConnectSinkResult(DBusMessage *msg, void *user, void *natData) { - LOGV(__FUNCTION__); - - char *c_path = (char *)user; - DBusError err; - JNIEnv *env; - - if (nat->vm->GetEnv((void**)&env, nat->envVer) < 0) { - LOGE("%s: error finding Env for our VM\n", __FUNCTION__); - return; - } - - dbus_error_init(&err); - - LOGV("... path = %s", c_path); - if (dbus_set_error_from_message(&err, msg)) { - /* if (!strcmp(err.name, BLUEZ_DBUS_BASE_IFC ".Error.AuthenticationFailed")) */ - LOGE("%s: D-Bus error: %s (%s)\n", __FUNCTION__, err.name, err.message); - dbus_error_free(&err); - env->CallVoidMethod(nat->me, - method_onSinkDisconnected, - env->NewStringUTF(c_path)); - if (env->ExceptionCheck()) { - LOGE("VM Exception occurred in native function %s (%s:%d)", - __FUNCTION__, __FILE__, __LINE__); - } - } // else Java callback is triggered by signal in a2dp_event_filter - - free(c_path); -} - -static void onDisconnectSinkResult(DBusMessage *msg, void *user, void *natData) { - LOGV(__FUNCTION__); - - char *c_path = (char *)user; - DBusError err; - JNIEnv *env; - - if (nat->vm->GetEnv((void**)&env, nat->envVer) < 0) { - LOGE("%s: error finding Env for our VM\n", __FUNCTION__); - return; - } - - dbus_error_init(&err); - - LOGV("... path = %s", c_path); - if (dbus_set_error_from_message(&err, msg)) { - /* if (!strcmp(err.name, BLUEZ_DBUS_BASE_IFC ".Error.AuthenticationFailed")) */ - LOGE("%s: D-Bus error: %s (%s)\n", __FUNCTION__, err.name, err.message); - if (strcmp(err.name, "org.bluez.Error.NotConnected") == 0) { - // we were already disconnected, so report disconnect - env->CallVoidMethod(nat->me, - method_onSinkDisconnected, - env->NewStringUTF(c_path)); - } else { - // Assume it is still connected - env->CallVoidMethod(nat->me, - method_onSinkConnected, - env->NewStringUTF(c_path)); - } - dbus_error_free(&err); - if (env->ExceptionCheck()) { - LOGE("VM Exception occurred in native function %s (%s:%d)", - __FUNCTION__, __FILE__, __LINE__); - } - } // else Java callback is triggered by signal in a2dp_event_filter - - free(c_path); -} - DBusHandlerResult a2dp_event_filter(DBusMessage *msg, JNIEnv *env) { DBusError err; @@ -323,71 +200,23 @@ DBusHandlerResult a2dp_event_filter(DBusMessage *msg, JNIEnv *env) { DBusHandlerResult result = DBUS_HANDLER_RESULT_NOT_YET_HANDLED; - if (dbus_message_is_signal(msg, - "org.bluez.audio.Manager", - "HeadsetCreated")) { - char *c_path; - if (dbus_message_get_args(msg, &err, - DBUS_TYPE_STRING, &c_path, - DBUS_TYPE_INVALID)) { - LOGV("... path = %s", c_path); - env->CallVoidMethod(nat->me, - method_onHeadsetCreated, - env->NewStringUTF(c_path)); - } else LOG_AND_FREE_DBUS_ERROR_WITH_MSG(&err, msg); - result = DBUS_HANDLER_RESULT_HANDLED; - } else if (dbus_message_is_signal(msg, - "org.bluez.audio.Manager", - "HeadsetRemoved")) { - char *c_path; - if (dbus_message_get_args(msg, &err, - DBUS_TYPE_STRING, &c_path, - DBUS_TYPE_INVALID)) { - LOGV("... path = %s", c_path); - env->CallVoidMethod(nat->me, - method_onHeadsetRemoved, - env->NewStringUTF(c_path)); - } else LOG_AND_FREE_DBUS_ERROR_WITH_MSG(&err, msg); - result = DBUS_HANDLER_RESULT_HANDLED; - } else if (dbus_message_is_signal(msg, - "org.bluez.audio.Sink", - "Connected")) { + if (dbus_message_is_signal(msg, "org.bluez.AudioSink", + "PropertyChanged")) { + jobjectArray str_array = + parse_property_change(env, msg, (Properties *)&sink_properties, + sizeof(sink_properties) / sizeof(Properties)); const char *c_path = dbus_message_get_path(msg); - LOGV("... path = %s", c_path); env->CallVoidMethod(nat->me, - method_onSinkConnected, - env->NewStringUTF(c_path)); - result = DBUS_HANDLER_RESULT_HANDLED; - } else if (dbus_message_is_signal(msg, - "org.bluez.audio.Sink", - "Disconnected")) { - const char *c_path = dbus_message_get_path(msg); - LOGV("... path = %s", c_path); - env->CallVoidMethod(nat->me, - method_onSinkDisconnected, - env->NewStringUTF(c_path)); - result = DBUS_HANDLER_RESULT_HANDLED; - } else if (dbus_message_is_signal(msg, - "org.bluez.audio.Sink", - "Playing")) { - const char *c_path = dbus_message_get_path(msg); - LOGV("... path = %s", c_path); - env->CallVoidMethod(nat->me, - method_onSinkPlaying, - env->NewStringUTF(c_path)); - result = DBUS_HANDLER_RESULT_HANDLED; - } else if (dbus_message_is_signal(msg, - "org.bluez.audio.Sink", - "Stopped")) { - const char *c_path = dbus_message_get_path(msg); - LOGV("... path = %s", c_path); - env->CallVoidMethod(nat->me, - method_onSinkStopped, - env->NewStringUTF(c_path)); + method_onSinkPropertyChanged, + env->NewStringUTF(c_path), + str_array); + for (int i = 0; i < env->GetArrayLength(str_array); i++) { + env->DeleteLocalRef(env->GetObjectArrayElement(str_array, i)); + } + env->DeleteLocalRef(str_array); result = DBUS_HANDLER_RESULT_HANDLED; - } - - if (result == DBUS_HANDLER_RESULT_NOT_YET_HANDLED) { + return result; + } else { LOGV("... ignored"); } if (env->ExceptionCheck()) { @@ -406,14 +235,11 @@ static JNINativeMethod sMethods[] = { {"initNative", "()Z", (void *)initNative}, {"cleanupNative", "()V", (void *)cleanupNative}, - /* Bluez audio 3.36 API */ - {"listHeadsetsNative", "()[Ljava/lang/String;", (void*)listHeadsetsNative}, - {"createHeadsetNative", "(Ljava/lang/String;)Ljava/lang/String;", (void*)createHeadsetNative}, - {"removeHeadsetNative", "(Ljava/lang/String;)Z", (void*)removeHeadsetNative}, - {"getAddressNative", "(Ljava/lang/String;)Ljava/lang/String;", (void*)getAddressNative}, - {"connectSinkNative", "(Ljava/lang/String;)Z", (void*)connectSinkNative}, - {"disconnectSinkNative", "(Ljava/lang/String;)Z", (void*)disconnectSinkNative}, - {"isSinkConnectedNative", "(Ljava/lang/String;)Z", (void*)isSinkConnectedNative}, + /* Bluez audio 4.40 API */ + {"connectSinkNative", "(Ljava/lang/String;)Z", (void *)connectSinkNative}, + {"disconnectSinkNative", "(Ljava/lang/String;)Z", (void *)disconnectSinkNative}, + {"getSinkPropertiesNative", "(Ljava/lang/String;)[Ljava/lang/Object;", + (void *)getSinkPropertiesNative}, }; int register_android_server_BluetoothA2dpService(JNIEnv *env) { @@ -424,12 +250,8 @@ int register_android_server_BluetoothA2dpService(JNIEnv *env) { } #ifdef HAVE_BLUETOOTH - method_onHeadsetCreated = env->GetMethodID(clazz, "onHeadsetCreated", "(Ljava/lang/String;)V"); - method_onHeadsetRemoved = env->GetMethodID(clazz, "onHeadsetRemoved", "(Ljava/lang/String;)V"); - method_onSinkConnected = env->GetMethodID(clazz, "onSinkConnected", "(Ljava/lang/String;)V"); - method_onSinkDisconnected = env->GetMethodID(clazz, "onSinkDisconnected", "(Ljava/lang/String;)V"); - method_onSinkPlaying = env->GetMethodID(clazz, "onSinkPlaying", "(Ljava/lang/String;)V"); - method_onSinkStopped = env->GetMethodID(clazz, "onSinkStopped", "(Ljava/lang/String;)V"); + method_onSinkPropertyChanged = env->GetMethodID(clazz, "onSinkPropertyChanged", + "(Ljava/lang/String;[Ljava/lang/String;)V"); #endif return AndroidRuntime::registerNativeMethods(env, diff --git a/core/jni/android_server_BluetoothDeviceService.cpp b/core/jni/android_server_BluetoothDeviceService.cpp index b6e9798..16bfc9c 100644 --- a/core/jni/android_server_BluetoothDeviceService.cpp +++ b/core/jni/android_server_BluetoothDeviceService.cpp @@ -14,7 +14,8 @@ ** limitations under the License. */ -#define DBUS_CLASS_NAME BLUEZ_DBUS_BASE_IFC ".Adapter" +#define DBUS_ADAPTER_IFACE BLUEZ_DBUS_BASE_IFC ".Adapter" +#define DBUS_DEVICE_IFACE BLUEZ_DBUS_BASE_IFC ".Device" #define LOG_TAG "BluetoothDeviceService.cpp" #include "android_bluetooth_common.h" @@ -60,8 +61,11 @@ typedef struct { 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); +extern DBusHandlerResult agent_event_filter(DBusConnection *conn, + DBusMessage *msg, + void *data); +void onCreatePairedDeviceResult(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 @@ -109,36 +113,71 @@ static bool initializeNativeDataNative(JNIEnv* env, jobject object) { dbus_error_free(&err); return false; } - - nat->adapter = BLUEZ_ADAPTER_OBJECT_NAME; #endif /*HAVE_BLUETOOTH*/ return true; } -static void cleanupNativeDataNative(JNIEnv* env, jobject object) { +static const char *get_adapter_path(JNIEnv* env, jobject object) { +#ifdef HAVE_BLUETOOTH + event_loop_native_data_t *event_nat = + get_EventLoop_native_data(env, env->GetObjectField(object, + field_mEventLoop)); + if (event_nat == NULL) + return NULL; + return event_nat->adapter; +#else + return NULL; +#endif +} + +// This function is called when the adapter is enabled. +static jboolean setupNativeDataNative(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; + event_loop_native_data_t *event_nat = + get_EventLoop_native_data(env, env->GetObjectField(object, + field_mEventLoop)); + // Register agent for remote devices. + const char *device_agent_path = "/android/bluetooth/remote_device_agent"; + static const DBusObjectPathVTable agent_vtable = { + NULL, agent_event_filter, NULL, NULL, NULL, NULL }; + + if (!dbus_connection_register_object_path(nat->conn, device_agent_path, + &agent_vtable, event_nat)) { + LOGE("%s: Can't register object path %s for remote device agent!", + __FUNCTION__, device_agent_path); + return JNI_FALSE; } -#endif +#endif /*HAVE_BLUETOOTH*/ + return JNI_TRUE; } -static jstring getNameNative(JNIEnv *env, jobject object){ +static jboolean tearDownNativeDataNative(JNIEnv *env, jobject object) { LOGV(__FUNCTION__); #ifdef HAVE_BLUETOOTH - native_data_t *nat = get_native_data(env, object); + native_data_t *nat = + (native_data_t *)env->GetIntField(object, field_mNativeData); + if (nat != NULL) { + const char *device_agent_path = + "/android/bluetooth/remote_device_agent"; + dbus_connection_unregister_object_path (nat->conn, device_agent_path); + } +#endif /*HAVE_BLUETOOTH*/ + return JNI_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) { - 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; + free(nat); + nat = NULL; } #endif - return NULL; } static jstring getAdapterPathNative(JNIEnv *env, jobject object) { @@ -146,7 +185,7 @@ static jstring getAdapterPathNative(JNIEnv *env, jobject object) { #ifdef HAVE_BLUETOOTH native_data_t *nat = get_native_data(env, object); if (nat) { - return (env->NewStringUTF(nat->adapter)); + return (env->NewStringUTF(get_adapter_path(env, object))); } #endif return NULL; @@ -170,28 +209,23 @@ static jboolean startDiscoveryNative(JNIEnv *env, jobject object) { dbus_error_init(&err); /* Compose the command */ - msg = dbus_message_new_method_call(BLUEZ_DBUS_BASE_IFC, nat->adapter, - DBUS_CLASS_NAME, "DiscoverDevices"); + msg = dbus_message_new_method_call(BLUEZ_DBUS_BASE_IFC, + get_adapter_path(env, object), + DBUS_ADAPTER_IFACE, "StartDiscovery"); if (msg == NULL) { - LOGE("%s: Could not allocate D-Bus message object!", __FUNCTION__); + if (dbus_error_is_set(&err)) { + LOG_AND_FREE_DBUS_ERROR_WITH_MSG(&err, msg); + } 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; - } + LOG_AND_FREE_DBUS_ERROR_WITH_MSG(&err, msg); + ret = JNI_FALSE; + goto done; } ret = JNI_TRUE; @@ -204,7 +238,7 @@ done: #endif } -static void cancelDiscoveryNative(JNIEnv *env, jobject object) { +static void stopDiscoveryNative(JNIEnv *env, jobject object) { LOGV(__FUNCTION__); #ifdef HAVE_BLUETOOTH DBusMessage *msg = NULL; @@ -222,18 +256,20 @@ static void cancelDiscoveryNative(JNIEnv *env, jobject object) { } /* Compose the command */ - msg = dbus_message_new_method_call(BLUEZ_DBUS_BASE_IFC, nat->adapter, - DBUS_CLASS_NAME, "CancelDiscovery"); - + msg = dbus_message_new_method_call(BLUEZ_DBUS_BASE_IFC, + get_adapter_path(env, object), + DBUS_ADAPTER_IFACE, "StopDiscovery"); if (msg == NULL) { - LOGE("%s: Could not allocate D-Bus message object!", __FUNCTION__); + if (dbus_error_is_set(&err)) + LOG_AND_FREE_DBUS_ERROR_WITH_MSG(&err, msg); 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) { + if(strncmp(err.name, BLUEZ_DBUS_BASE_IFC ".Error.NotAuthorized", + strlen(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); @@ -248,232 +284,8 @@ done: #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) { +static jboolean createPairedDeviceNative(JNIEnv *env, jobject object, + jstring address, jint timeout_ms) { LOGV(__FUNCTION__); #ifdef HAVE_BLUETOOTH native_data_t *nat = get_native_data(env, object); @@ -485,14 +297,20 @@ static jboolean createBondingNative(JNIEnv *env, jobject object, const char *c_address = env->GetStringUTFChars(address, NULL); LOGV("... address = %s", c_address); char *context_address = (char *)calloc(BTADDR_SIZE, sizeof(char)); + const char *capabilities = "DisplayYesNo"; + const char *agent_path = "/android/bluetooth/remote_device_agent"; + strlcpy(context_address, c_address, BTADDR_SIZE); // for callback bool ret = dbus_func_args_async(env, nat->conn, (int)timeout_ms, - onCreateBondingResult, // callback + onCreatePairedDeviceResult, // callback context_address, eventLoopNat, - nat->adapter, - DBUS_CLASS_NAME, "CreateBonding", + get_adapter_path(env, object), + DBUS_ADAPTER_IFACE, + "CreatePairedDevice", DBUS_TYPE_STRING, &c_address, + DBUS_TYPE_OBJECT_PATH, &agent_path, + DBUS_TYPE_STRING, &capabilities, DBUS_TYPE_INVALID); env->ReleaseStringUTFChars(address, c_address); return ret ? JNI_TRUE : JNI_FALSE; @@ -502,479 +320,324 @@ static jboolean createBondingNative(JNIEnv *env, jobject object, return JNI_FALSE; } -static jboolean cancelBondingProcessNative(JNIEnv *env, jobject object, - jstring address) { +static jint getDeviceServiceChannelNative(JNIEnv *env, jobject object, + jstring path, + jstring pattern, jint attr_id) { +#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_pattern = env->GetStringUTFChars(pattern, NULL); + const char *c_path = env->GetStringUTFChars(path, NULL); + LOGV("... pattern = %s", c_pattern); + LOGV("... attr_id = %#X", attr_id); + DBusMessage *reply = + dbus_func_args(env, nat->conn, c_path, + DBUS_DEVICE_IFACE, "GetServiceAttributeValue", + DBUS_TYPE_STRING, &c_pattern, + DBUS_TYPE_UINT16, &attr_id, + DBUS_TYPE_INVALID); + env->ReleaseStringUTFChars(pattern, c_pattern); + env->ReleaseStringUTFChars(path, c_path); + return reply ? dbus_returns_int32(env, reply) : -1; + } +#endif + return -1; +} + +static jboolean cancelDeviceCreationNative(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); + DBusError err; + dbus_error_init(&err); LOGV("... address = %s", c_address); DBusMessage *reply = - dbus_func_args_timeout(env, nat->conn, -1, nat->adapter, - DBUS_CLASS_NAME, "CancelBondingProcess", + dbus_func_args_timeout(env, nat->conn, -1, + get_adapter_path(env, object), + DBUS_ADAPTER_IFACE, "CancelDeviceCreation", DBUS_TYPE_STRING, &c_address, DBUS_TYPE_INVALID); env->ReleaseStringUTFChars(address, c_address); - if (reply) { - dbus_message_unref(reply); + if (!reply) { + if (dbus_error_is_set(&err)) { + LOG_AND_FREE_DBUS_ERROR(&err); + } else + LOGE("DBus reply is NULL in function %s", __FUNCTION__); + return JNI_FALSE; + } else { + result = JNI_TRUE; } - return JNI_TRUE; + dbus_message_unref(reply); } #endif return JNI_FALSE; } -static jboolean removeBondingNative(JNIEnv *env, jobject object, jstring address) { +static jboolean removeDeviceNative(JNIEnv *env, jobject object, jstring object_path) { 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); + const char *c_object_path = env->GetStringUTFChars(object_path, NULL); 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_func_args_error(env, nat->conn, &err, + get_adapter_path(env, object), + DBUS_ADAPTER_IFACE, "RemoveDevice", + DBUS_TYPE_OBJECT_PATH, &c_object_path, 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); - } + if (!reply) { + if (dbus_error_is_set(&err)) { + LOG_AND_FREE_DBUS_ERROR(&err); + } else + LOGE("DBus reply is NULL in function %s", __FUNCTION__); + result = JNI_FALSE; } else { result = JNI_TRUE; } - - env->ReleaseStringUTFChars(address, c_address); - dbus_error_free(&err); + env->ReleaseStringUTFChars(object_path, c_object_path); if (reply) dbus_message_unref(reply); } #endif return result; } -static jobjectArray listBondingsNative(JNIEnv *env, jobject object) { +static jint enableNative(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; - } + return bt_enable(); #endif - return NULL; + return -1; } -static jobjectArray listConnectionsNative(JNIEnv *env, jobject object) { +static jint disableNative(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; - } + return bt_disable(); #endif - return NULL; + return -1; } -static jobjectArray listRemoteDevicesNative(JNIEnv *env, jobject object) { +static jint isEnabledNative(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; - } + return bt_is_enabled(); #endif - return NULL; + return -1; } -static jstring common_Get(JNIEnv *env, jobject object, const char *func) { - LOGV("%s:%s", __FUNCTION__, func); +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) { - 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; + 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; } - } -#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"); -} + const char *c_pin = env->GetStringUTFChars(pin, NULL); -static jstring getManufacturerNative(JNIEnv *env, jobject obj) { - return common_Get(env, obj, "GetManufacturer"); -} + dbus_message_append_args(reply, DBUS_TYPE_STRING, &c_pin, + DBUS_TYPE_INVALID); -static jstring getCompanyNative(JNIEnv *env, jobject obj) { - return common_Get(env, obj, "GetCompany"); + 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 setNameNative(JNIEnv *env, jobject obj, jstring name) { +static jboolean cancelPinNative(JNIEnv *env, jobject object, jstring address, + int nativeData) { #ifdef HAVE_BLUETOOTH LOGV(__FUNCTION__); - native_data_t *nat = get_native_data(env, obj); + native_data_t *nat = get_native_data(env, object); 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; + 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 jstring common_getRemote(JNIEnv *env, jobject object, const char *func, - jstring address) { - LOGV("%s:%s", __FUNCTION__, func); +static jobjectArray getDevicePropertiesNative(JNIEnv *env, jobject object, + jstring path) +{ #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 *msg, *reply; DBusError err; dbus_error_init(&err); - LOGV("... address = %s", c_address); + const char *c_path = env->GetStringUTFChars(path, NULL); + reply = dbus_func_args_timeout(env, + nat->conn, -1, c_path, + DBUS_DEVICE_IFACE, "GetProperties", + DBUS_TYPE_INVALID); + env->ReleaseStringUTFChars(path, c_path); - 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); + if (!reply) { + if (dbus_error_is_set(&err)) { + LOG_AND_FREE_DBUS_ERROR(&err); + } else + LOGE("DBus reply is NULL in function %s", __FUNCTION__); return NULL; } + DBusMessageIter iter; + jobjectArray str_array = NULL; + if (dbus_message_iter_init(reply, &iter)) + str_array = parse_remote_device_properties(env, &iter); + dbus_message_unref(reply); + return str_array; } #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; +static jobjectArray getAdapterPropertiesNative(JNIEnv *env, jobject object) { #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 *msg, *reply; + DBusError err; + dbus_error_init(&err); - 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); + reply = dbus_func_args_timeout(env, + nat->conn, -1, get_adapter_path(env, object), + DBUS_ADAPTER_IFACE, "GetProperties", + DBUS_TYPE_INVALID); + if (!reply) { + if (dbus_error_is_set(&err)) { + LOG_AND_FREE_DBUS_ERROR(&err); + } else + LOGE("DBus reply is NULL in function %s", __FUNCTION__); + return NULL; } - } -#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; + DBusMessageIter iter; + jobjectArray str_array = NULL; + if (dbus_message_iter_init(reply, &iter)) + str_array = parse_adapter_properties(env, &iter); + dbus_message_unref(reply); + return str_array; } #endif return NULL; } -static jintArray getRemoteServiceHandlesNative(JNIEnv *env, jobject object, - jstring address, jstring match) { +static jboolean setAdapterPropertyNative(JNIEnv *env, jobject object, jstring key, + void *value, jint type) { #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, *msg; + DBusMessageIter iter; + DBusError err; + const char *c_key = env->GetStringUTFChars(key, NULL); + dbus_error_init(&err); - 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); + msg = dbus_message_new_method_call(BLUEZ_DBUS_BASE_IFC, + get_adapter_path(env, object), + DBUS_ADAPTER_IFACE, "SetProperty"); + if (!msg) { + LOGE("%s: Can't allocate new method call for GetProperties!", + __FUNCTION__); + return JNI_FALSE; } - 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); + dbus_message_append_args(msg, DBUS_TYPE_STRING, &c_key, DBUS_TYPE_INVALID); + dbus_message_iter_init_append(msg, &iter); + append_variant(&iter, type, value); - 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; -} + reply = dbus_connection_send_with_reply_and_block(nat->conn, msg, -1, &err); + dbus_message_unref(msg); -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); + env->ReleaseStringUTFChars(key, c_key); - 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; + if (!reply) { + if (dbus_error_is_set(&err)) { + LOG_AND_FREE_DBUS_ERROR(&err); + } else + LOGE("DBus reply is NULL in function %s", __FUNCTION__); + return JNI_FALSE; + } + return JNI_TRUE; } #endif return JNI_FALSE; } -static jint enableNative(JNIEnv *env, jobject object) { +static jboolean setAdapterPropertyStringNative(JNIEnv *env, jobject object, jstring key, + jstring value) { #ifdef HAVE_BLUETOOTH - LOGV(__FUNCTION__); - return bt_enable(); + const char *c_value = env->GetStringUTFChars(value, NULL); + jboolean ret = setAdapterPropertyNative(env, object, key, (void *)&c_value, DBUS_TYPE_STRING); + env->ReleaseStringUTFChars(value, (char *)c_value); + return ret; +#else + return JNI_FALSE; #endif - return -1; } -static jint disableNative(JNIEnv *env, jobject object) { +static jboolean setAdapterPropertyIntegerNative(JNIEnv *env, jobject object, jstring key, + jint value) { #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(); + return setAdapterPropertyNative(env, object, key, (void *)&value, DBUS_TYPE_UINT32); +#else + return JNI_FALSE; #endif - return -1; } -static jboolean setPinNative(JNIEnv *env, jobject object, jstring address, - jstring pin, int nativeData) { +static jboolean setAdapterPropertyBooleanNative(JNIEnv *env, jobject object, jstring key, + jint value) { #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 setAdapterPropertyNative(env, object, key, (void *)&value, DBUS_TYPE_BOOLEAN); +#else 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}, + {"setupNativeDataNative", "()Z", (void *)setupNativeDataNative}, + {"tearDownNativeDataNative", "()Z", (void *)tearDownNativeDataNative}, {"cleanupNativeDataNative", "()V", (void *)cleanupNativeDataNative}, {"getAdapterPathNative", "()Ljava/lang/String;", (void*)getAdapterPathNative}, @@ -982,46 +645,25 @@ static JNINativeMethod sMethods[] = { {"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}, + {"getAdapterPropertiesNative", "()[Ljava/lang/Object;", (void *)getAdapterPropertiesNative}, + {"getDevicePropertiesNative", "(Ljava/lang/String;)[Ljava/lang/Object;", + (void *)getDevicePropertiesNative}, + {"setAdapterPropertyStringNative", "(Ljava/lang/String;Ljava/lang/String;)Z", + (void *)setAdapterPropertyStringNative}, + {"setAdapterPropertyBooleanNative", "(Ljava/lang/String;I)Z", + (void *)setAdapterPropertyBooleanNative}, + {"setAdapterPropertyIntegerNative", "(Ljava/lang/String;I)Z", + (void *)setAdapterPropertyIntegerNative}, + + {"startDiscoveryNative", "()Z", (void*)startDiscoveryNative}, + {"stopDiscoveryNative", "()Z", (void *)stopDiscoveryNative}, + + {"createPairedDeviceNative", "(Ljava/lang/String;I)Z", (void *)createPairedDeviceNative}, + {"cancelDeviceCreationNative", "(Ljava/lang/String;)Z", (void *)cancelDeviceCreationNative}, + {"removeDeviceNative", "(Ljava/lang/String;)Z", (void *)removeDeviceNative}, + {"getDeviceServiceChannelNative", "(Ljava/lang/String;Ljava/lang/String;I)I", + (void *)getDeviceServiceChannelNative}, + {"setPinNative", "(Ljava/lang/String;Ljava/lang/String;I)Z", (void *)setPinNative}, {"cancelPinNative", "(Ljava/lang/String;I)Z", (void *)cancelPinNative}, }; diff --git a/core/jni/android_server_BluetoothEventLoop.cpp b/core/jni/android_server_BluetoothEventLoop.cpp index 7c5da5b..e0ea788 100644 --- a/core/jni/android_server_BluetoothEventLoop.cpp +++ b/core/jni/android_server_BluetoothEventLoop.cpp @@ -39,30 +39,19 @@ namespace android { #ifdef HAVE_BLUETOOTH static jfieldID field_mNativeData; -static jmethodID method_onModeChanged; -static jmethodID method_onNameChanged; -static jmethodID method_onDiscoveryStarted; -static jmethodID method_onDiscoveryCompleted; -static jmethodID method_onRemoteDeviceFound; -static jmethodID method_onRemoteDeviceDisappeared; -static jmethodID method_onRemoteClassUpdated; -static jmethodID method_onRemoteNameUpdated; -static jmethodID method_onRemoteNameFailed; -static jmethodID method_onRemoteDeviceConnected; -static jmethodID method_onRemoteDeviceDisconnectRequested; -static jmethodID method_onRemoteDeviceDisconnected; -static jmethodID method_onBondingCreated; -static jmethodID method_onBondingRemoved; - -static jmethodID method_onCreateBondingResult; -static jmethodID method_onGetRemoteServiceChannelResult; - -static jmethodID method_onPasskeyAgentRequest; -static jmethodID method_onPasskeyAgentCancel; -static jmethodID method_onAuthAgentAuthorize; -static jmethodID method_onAuthAgentCancel; - -static jmethodID method_onRestartRequired; +static jmethodID method_onPropertyChanged; +static jmethodID method_onDevicePropertyChanged; +static jmethodID method_onDeviceFound; +static jmethodID method_onDeviceDisappeared; +static jmethodID method_onDeviceCreated; +static jmethodID method_onDeviceRemoved; + +static jmethodID method_onCreatePairedDeviceResult; +static jmethodID method_onGetDeviceServiceChannelResult; + +static jmethodID method_onRequestPinCode; +static jmethodID method_onAgentAuthorize; +static jmethodID method_onAgentCancel; typedef event_loop_native_data_t native_data_t; @@ -80,30 +69,26 @@ static void classInitNative(JNIEnv* env, jclass clazz) { LOGV(__FUNCTION__); #ifdef HAVE_BLUETOOTH - method_onModeChanged = env->GetMethodID(clazz, "onModeChanged", "(Ljava/lang/String;)V"); - method_onNameChanged = env->GetMethodID(clazz, "onNameChanged", "(Ljava/lang/String;)V"); - method_onDiscoveryStarted = env->GetMethodID(clazz, "onDiscoveryStarted", "()V"); - method_onDiscoveryCompleted = env->GetMethodID(clazz, "onDiscoveryCompleted", "()V"); - method_onRemoteDeviceFound = env->GetMethodID(clazz, "onRemoteDeviceFound", "(Ljava/lang/String;IS)V"); - method_onRemoteDeviceDisappeared = env->GetMethodID(clazz, "onRemoteDeviceDisappeared", "(Ljava/lang/String;)V"); - method_onRemoteClassUpdated = env->GetMethodID(clazz, "onRemoteClassUpdated", "(Ljava/lang/String;I)V"); - method_onRemoteNameUpdated = env->GetMethodID(clazz, "onRemoteNameUpdated", "(Ljava/lang/String;Ljava/lang/String;)V"); - method_onRemoteNameFailed = env->GetMethodID(clazz, "onRemoteNameFailed", "(Ljava/lang/String;)V"); - method_onRemoteDeviceConnected = env->GetMethodID(clazz, "onRemoteDeviceConnected", "(Ljava/lang/String;)V"); - method_onRemoteDeviceDisconnectRequested = env->GetMethodID(clazz, "onRemoteDeviceDisconnectRequested", "(Ljava/lang/String;)V"); - method_onRemoteDeviceDisconnected = env->GetMethodID(clazz, "onRemoteDeviceDisconnected", "(Ljava/lang/String;)V"); - method_onBondingCreated = env->GetMethodID(clazz, "onBondingCreated", "(Ljava/lang/String;)V"); - method_onBondingRemoved = env->GetMethodID(clazz, "onBondingRemoved", "(Ljava/lang/String;)V"); - - method_onCreateBondingResult = env->GetMethodID(clazz, "onCreateBondingResult", "(Ljava/lang/String;I)V"); - - method_onPasskeyAgentRequest = env->GetMethodID(clazz, "onPasskeyAgentRequest", "(Ljava/lang/String;I)V"); - method_onPasskeyAgentCancel = env->GetMethodID(clazz, "onPasskeyAgentCancel", "(Ljava/lang/String;)V"); - method_onAuthAgentAuthorize = env->GetMethodID(clazz, "onAuthAgentAuthorize", "(Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;)Z"); - method_onAuthAgentCancel = env->GetMethodID(clazz, "onAuthAgentCancel", "(Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;)V"); - method_onGetRemoteServiceChannelResult = env->GetMethodID(clazz, "onGetRemoteServiceChannelResult", "(Ljava/lang/String;I)V"); - - method_onRestartRequired = env->GetMethodID(clazz, "onRestartRequired", "()V"); + method_onPropertyChanged = env->GetMethodID(clazz, "onPropertyChanged", + "([Ljava/lang/String;)V"); + method_onDevicePropertyChanged = env->GetMethodID(clazz, + "onDevicePropertyChanged", + "(Ljava/lang/String;[Ljava/lang/String;)V"); + method_onDeviceFound = env->GetMethodID(clazz, "onDeviceFound", + "(Ljava/lang/String;[Ljava/lang/String;)V"); + method_onDeviceDisappeared = env->GetMethodID(clazz, "onDeviceDisappeared", + "(Ljava/lang/String;)V"); + method_onDeviceCreated = env->GetMethodID(clazz, "onDeviceCreated", "(Ljava/lang/String;)V"); + method_onDeviceRemoved = env->GetMethodID(clazz, "onDeviceRemoved", "(Ljava/lang/String;)V"); + + method_onCreatePairedDeviceResult = env->GetMethodID(clazz, "onCreatePairedDeviceResult", + "(Ljava/lang/String;I)V"); + + method_onAgentAuthorize = env->GetMethodID(clazz, "onAgentAuthorize", + "(Ljava/lang/String;Ljava/lang/String;)Z"); + method_onAgentCancel = env->GetMethodID(clazz, "onAgentCancel", "()V"); + method_onRequestPinCode = env->GetMethodID(clazz, "onRequestPinCode", + "(Ljava/lang/String;I)V"); field_mNativeData = env->GetFieldID(clazz, "mNativeData", "I"); #endif @@ -153,9 +138,11 @@ static void cleanupNativeDataNative(JNIEnv* env, jobject object) { #ifdef HAVE_BLUETOOTH static DBusHandlerResult event_filter(DBusConnection *conn, DBusMessage *msg, void *data); -static DBusHandlerResult agent_event_filter(DBusConnection *conn, - DBusMessage *msg, - void *data); +DBusHandlerResult agent_event_filter(DBusConnection *conn, + DBusMessage *msg, + void *data); +static int register_agent(native_data_t *nat, + const char *agent_path, const char *capabilities); static const DBusObjectPathVTable agent_vtable = { NULL, agent_event_filter, NULL, NULL, NULL, NULL @@ -164,11 +151,12 @@ static const DBusObjectPathVTable agent_vtable = { static jboolean setUpEventLoop(native_data_t *nat) { LOGV(__FUNCTION__); - dbus_threads_init_default(); - DBusError err; - dbus_error_init(&err); if (nat != NULL && nat->conn != NULL) { + dbus_threads_init_default(); + DBusError err; + dbus_error_init(&err); + // Add a filter for all incoming messages if (!dbus_connection_add_filter(nat->conn, event_filter, nat, NULL)){ return JNI_FALSE; @@ -190,108 +178,143 @@ static jboolean setUpEventLoop(native_data_t *nat) { return JNI_FALSE; } dbus_bus_add_match(nat->conn, - "type='signal',interface='org.bluez.audio.Manager'", + "type='signal',interface='"BLUEZ_DBUS_BASE_IFC".Device'", &err); if (dbus_error_is_set(&err)) { LOG_AND_FREE_DBUS_ERROR(&err); return JNI_FALSE; } dbus_bus_add_match(nat->conn, - "type='signal',interface='org.bluez.audio.Device'", - &err); - if (dbus_error_is_set(&err)) { - LOG_AND_FREE_DBUS_ERROR(&err); - return JNI_FALSE; - } - dbus_bus_add_match(nat->conn, - "type='signal',interface='org.bluez.audio.Sink'", + "type='signal',interface='org.bluez.AudioSink'", &err); if (dbus_error_is_set(&err)) { LOG_AND_FREE_DBUS_ERROR(&err); return JNI_FALSE; } - // Add an object handler for passkey agent method calls - const char *path = "/android/bluetooth/Agent"; - if (!dbus_connection_register_object_path(nat->conn, path, - &agent_vtable, nat)) { - LOGE("%s: Can't register object path %s for agent!", - __FUNCTION__, path); + const char *agent_path = "/android/bluetooth/agent"; + const char *capabilities = "DisplayYesNo"; + if (register_agent(nat, agent_path, capabilities) < 0) { + dbus_connection_unregister_object_path (nat->conn, agent_path); return JNI_FALSE; } + return JNI_TRUE; + } + return JNI_FALSE; +} - // RegisterDefaultPasskeyAgent() will fail until hcid is up, so keep - // trying for 10 seconds. - int attempt; - for (attempt = 0; attempt < 1000; attempt++) { - DBusMessage *reply = dbus_func_args_error(NULL, nat->conn, &err, - BLUEZ_DBUS_BASE_PATH, - "org.bluez.Security", "RegisterDefaultPasskeyAgent", - DBUS_TYPE_STRING, &path, - DBUS_TYPE_INVALID); - if (reply) { - // Success - dbus_message_unref(reply); - LOGV("Registered agent on attempt %d of 1000\n", attempt); - break; - } else if (dbus_error_has_name(&err, - "org.freedesktop.DBus.Error.ServiceUnknown")) { - // hcid is still down, retry - dbus_error_free(&err); - usleep(10000); // 10 ms - } else { - // Some other error we weren't expecting - LOG_AND_FREE_DBUS_ERROR(&err); - return JNI_FALSE; - } + +const char * get_adapter_path(DBusConnection *conn) { + DBusMessage *msg, *reply; + DBusError err; + const char *device_path = NULL; + msg = dbus_message_new_method_call("org.bluez", "/", + "org.bluez.Manager", "DefaultAdapter"); + if (!msg) { + LOGE("%s: Can't allocate new method call for GetProperties!", + __FUNCTION__); + return NULL; + } + dbus_message_append_args(msg, DBUS_TYPE_INVALID); + + dbus_error_init(&err); + reply = dbus_connection_send_with_reply_and_block(conn, msg, -1, &err); + dbus_message_unref(msg); + + if (!reply) { + if (dbus_error_is_set(&err)) { + LOG_AND_FREE_DBUS_ERROR(&err); } - if (attempt == 1000) { - LOGE("Time-out trying to call RegisterDefaultPasskeyAgent(), " - "is hcid running?"); - return JNI_FALSE; + return NULL; + } + if (!dbus_message_get_args(reply, &err, DBUS_TYPE_OBJECT_PATH, + &device_path, DBUS_TYPE_INVALID) + || !device_path){ + if (dbus_error_is_set(&err)) { + LOG_AND_FREE_DBUS_ERROR(&err); } + return NULL; + } + return device_path; +} - // Now register the Auth agent - DBusMessage *reply = dbus_func_args_error(NULL, nat->conn, &err, - BLUEZ_DBUS_BASE_PATH, - "org.bluez.Security", "RegisterDefaultAuthorizationAgent", - DBUS_TYPE_STRING, &path, - DBUS_TYPE_INVALID); - if (!reply) { +static int register_agent(native_data_t *nat, + const char * agent_path, const char * capabilities) +{ + DBusMessage *msg, *reply; + DBusError err; + + if (!dbus_connection_register_object_path(nat->conn, agent_path, + &agent_vtable, nat)) { + LOGE("%s: Can't register object path %s for agent!", + __FUNCTION__, agent_path); + return -1; + } + + nat->adapter = get_adapter_path(nat->conn); + msg = dbus_message_new_method_call("org.bluez", nat->adapter, + "org.bluez.Adapter", "RegisterAgent"); + if (!msg) { + LOGE("%s: Can't allocate new method call for agent!", + __FUNCTION__); + return -1; + } + dbus_message_append_args(msg, DBUS_TYPE_OBJECT_PATH, &agent_path, + DBUS_TYPE_STRING, &capabilities, + DBUS_TYPE_INVALID); + + dbus_error_init(&err); + reply = dbus_connection_send_with_reply_and_block(nat->conn, msg, -1, &err); + dbus_message_unref(msg); + + if (!reply) { + LOGE("%s: Can't register agent!", __FUNCTION__); + if (dbus_error_is_set(&err)) { LOG_AND_FREE_DBUS_ERROR(&err); - return JNI_FALSE; } - - dbus_message_unref(reply); - return JNI_TRUE; + return -1; } - return JNI_FALSE; + dbus_message_unref(reply); + dbus_connection_flush(nat->conn); + + return 0; } static void tearDownEventLoop(native_data_t *nat) { LOGV(__FUNCTION__); if (nat != NULL && nat->conn != NULL) { + DBusMessage *msg, *reply; DBusError err; dbus_error_init(&err); + const char * agent_path = "/android/bluetooth/agent"; + + msg = dbus_message_new_method_call("org.bluez", + nat->adapter, + "org.bluez.Adapter", + "UnregisterAgent"); + if (msg != NULL) { + dbus_message_append_args(msg, DBUS_TYPE_OBJECT_PATH, &agent_path, + DBUS_TYPE_INVALID); + reply = dbus_connection_send_with_reply_and_block(nat->conn, + msg, -1, &err); - const char *path = "/android/bluetooth/Agent"; - DBusMessage *reply = - dbus_func_args(NULL, nat->conn, BLUEZ_DBUS_BASE_PATH, - "org.bluez.Security", "UnregisterDefaultPasskeyAgent", - DBUS_TYPE_STRING, &path, - DBUS_TYPE_INVALID); - if (reply) dbus_message_unref(reply); - - reply = - dbus_func_args(NULL, nat->conn, BLUEZ_DBUS_BASE_PATH, - "org.bluez.Security", "UnregisterDefaultAuthorizationAgent", - DBUS_TYPE_STRING, &path, - DBUS_TYPE_INVALID); - if (reply) dbus_message_unref(reply); + if (!reply) { + if (dbus_error_is_set(&err)) { + LOG_AND_FREE_DBUS_ERROR(&err); + dbus_error_free(&err); + } + } else { + dbus_message_unref(reply); + } + dbus_message_unref(msg); + } else { + LOGE("%s: Can't create new method call!", __FUNCTION__); + } - dbus_connection_unregister_object_path(nat->conn, path); + dbus_connection_flush(nat->conn); + dbus_connection_unregister_object_path(nat->conn, agent_path); dbus_bus_remove_match(nat->conn, "type='signal',interface='org.bluez.audio.Sink'", @@ -649,267 +672,129 @@ static DBusHandlerResult event_filter(DBusConnection *conn, DBusMessage *msg, return DBUS_HANDLER_RESULT_NOT_YET_HANDLED; } - LOGV("%s: Received signal %s:%s from %s", __FUNCTION__, - dbus_message_get_interface(msg), dbus_message_get_member(msg), - dbus_message_get_path(msg)); + // STOPSHIP: Change to LOGV + LOGE("%s: Received signal %s:%s from %s", __FUNCTION__, + dbus_message_get_interface(msg), dbus_message_get_member(msg), + dbus_message_get_path(msg)); if (dbus_message_is_signal(msg, "org.bluez.Adapter", - "RemoteDeviceFound")) { + "DeviceFound")) { char *c_address; - int n_class; - short n_rssi; - if (dbus_message_get_args(msg, &err, - DBUS_TYPE_STRING, &c_address, - DBUS_TYPE_UINT32, &n_class, - DBUS_TYPE_INT16, &n_rssi, - DBUS_TYPE_INVALID)) { - LOGV("... address = %s class = %#X rssi = %hd", c_address, n_class, - n_rssi); + DBusMessageIter iter; + jobjectArray str_array = NULL; + if (dbus_message_iter_init(msg, &iter)) { + dbus_message_iter_get_basic(&iter, &c_address); + if (dbus_message_iter_next(&iter)) + str_array = + parse_remote_device_properties(env, &iter); + } + if (str_array != NULL) { env->CallVoidMethod(nat->me, - method_onRemoteDeviceFound, + method_onDeviceFound, env->NewStringUTF(c_address), - (jint)n_class, - (jshort)n_rssi); - } else LOG_AND_FREE_DBUS_ERROR_WITH_MSG(&err, msg); - return DBUS_HANDLER_RESULT_HANDLED; - } else if (dbus_message_is_signal(msg, - "org.bluez.Adapter", - "DiscoveryStarted")) { - LOGI("DiscoveryStarted signal received"); - env->CallVoidMethod(nat->me, method_onDiscoveryStarted); - return DBUS_HANDLER_RESULT_HANDLED; - } else if (dbus_message_is_signal(msg, - "org.bluez.Adapter", - "DiscoveryCompleted")) { - LOGI("DiscoveryCompleted signal received"); - env->CallVoidMethod(nat->me, method_onDiscoveryCompleted); - return DBUS_HANDLER_RESULT_HANDLED; - } else if (dbus_message_is_signal(msg, - "org.bluez.Adapter", - "RemoteDeviceDisappeared")) { - char *c_address; - if (dbus_message_get_args(msg, &err, - DBUS_TYPE_STRING, &c_address, - DBUS_TYPE_INVALID)) { - LOGV("... address = %s", c_address); - env->CallVoidMethod(nat->me, method_onRemoteDeviceDisappeared, - env->NewStringUTF(c_address)); - } else LOG_AND_FREE_DBUS_ERROR_WITH_MSG(&err, msg); - return DBUS_HANDLER_RESULT_HANDLED; - } else if (dbus_message_is_signal(msg, - "org.bluez.Adapter", - "RemoteClassUpdated")) { - char *c_address; - int n_class; - if (dbus_message_get_args(msg, &err, - DBUS_TYPE_STRING, &c_address, - DBUS_TYPE_UINT32, &n_class, - DBUS_TYPE_INVALID)) { - LOGV("... address = %s", c_address); - env->CallVoidMethod(nat->me, method_onRemoteClassUpdated, - env->NewStringUTF(c_address), (jint)n_class); - } else LOG_AND_FREE_DBUS_ERROR_WITH_MSG(&err, msg); - return DBUS_HANDLER_RESULT_HANDLED; - } else if (dbus_message_is_signal(msg, - "org.bluez.Adapter", - "RemoteNameUpdated")) { - char *c_address; - char *c_name; - if (dbus_message_get_args(msg, &err, - DBUS_TYPE_STRING, &c_address, - DBUS_TYPE_STRING, &c_name, - DBUS_TYPE_INVALID)) { - LOGV("... address = %s, name = %s", c_address, c_name); - env->CallVoidMethod(nat->me, - method_onRemoteNameUpdated, - env->NewStringUTF(c_address), - env->NewStringUTF(c_name)); - } else LOG_AND_FREE_DBUS_ERROR_WITH_MSG(&err, msg); - return DBUS_HANDLER_RESULT_HANDLED; - } else if (dbus_message_is_signal(msg, - "org.bluez.Adapter", - "RemoteNameFailed")) { - char *c_address; - if (dbus_message_get_args(msg, &err, - DBUS_TYPE_STRING, &c_address, - DBUS_TYPE_INVALID)) { - LOGV("... address = %s", c_address); - env->CallVoidMethod(nat->me, - method_onRemoteNameFailed, - env->NewStringUTF(c_address)); - } else LOG_AND_FREE_DBUS_ERROR_WITH_MSG(&err, msg); + str_array); + } else + LOG_AND_FREE_DBUS_ERROR_WITH_MSG(&err, msg); return DBUS_HANDLER_RESULT_HANDLED; } else if (dbus_message_is_signal(msg, - "org.bluez.Adapter", - "RemoteDeviceConnected")) { + "org.bluez.Adapter", + "DeviceDisappeared")) { char *c_address; if (dbus_message_get_args(msg, &err, DBUS_TYPE_STRING, &c_address, DBUS_TYPE_INVALID)) { LOGV("... address = %s", c_address); - env->CallVoidMethod(nat->me, - method_onRemoteDeviceConnected, + env->CallVoidMethod(nat->me, method_onDeviceDisappeared, env->NewStringUTF(c_address)); } else LOG_AND_FREE_DBUS_ERROR_WITH_MSG(&err, msg); return DBUS_HANDLER_RESULT_HANDLED; } else if (dbus_message_is_signal(msg, - "org.bluez.Adapter", - "RemoteDeviceDisconnectRequested")) { - char *c_address; + "org.bluez.Adapter", + "DeviceCreated")) { + char *c_object_path; if (dbus_message_get_args(msg, &err, - DBUS_TYPE_STRING, &c_address, + DBUS_TYPE_OBJECT_PATH, &c_object_path, DBUS_TYPE_INVALID)) { - LOGV("... address = %s", c_address); + LOGV("... address = %s", c_object_path); env->CallVoidMethod(nat->me, - method_onRemoteDeviceDisconnectRequested, - env->NewStringUTF(c_address)); + method_onDeviceCreated, + env->NewStringUTF(c_object_path)); } else LOG_AND_FREE_DBUS_ERROR_WITH_MSG(&err, msg); return DBUS_HANDLER_RESULT_HANDLED; } else if (dbus_message_is_signal(msg, - "org.bluez.Adapter", - "RemoteDeviceDisconnected")) { - char *c_address; + "org.bluez.Adapter", + "DeviceRemoved")) { + char *c_object_path; if (dbus_message_get_args(msg, &err, - DBUS_TYPE_STRING, &c_address, - DBUS_TYPE_INVALID)) { - LOGV("... address = %s", c_address); - env->CallVoidMethod(nat->me, - method_onRemoteDeviceDisconnected, - env->NewStringUTF(c_address)); - } else LOG_AND_FREE_DBUS_ERROR_WITH_MSG(&err, msg); - return DBUS_HANDLER_RESULT_HANDLED; - } else if (dbus_message_is_signal(msg, - "org.bluez.Adapter", - "BondingCreated")) { - char *c_address; - if (dbus_message_get_args(msg, &err, - DBUS_TYPE_STRING, &c_address, - DBUS_TYPE_INVALID)) { - LOGV("... address = %s", c_address); - env->CallVoidMethod(nat->me, - method_onBondingCreated, - env->NewStringUTF(c_address)); - } else LOG_AND_FREE_DBUS_ERROR_WITH_MSG(&err, msg); - return DBUS_HANDLER_RESULT_HANDLED; - } else if (dbus_message_is_signal(msg, - "org.bluez.Adapter", - "BondingRemoved")) { - char *c_address; - if (dbus_message_get_args(msg, &err, - DBUS_TYPE_STRING, &c_address, - DBUS_TYPE_INVALID)) { - LOGV("... address = %s", c_address); - env->CallVoidMethod(nat->me, - method_onBondingRemoved, - env->NewStringUTF(c_address)); + DBUS_TYPE_OBJECT_PATH, &c_object_path, + DBUS_TYPE_INVALID)) { + LOGV("... Object Path = %s", c_object_path); + env->CallVoidMethod(nat->me, + method_onDeviceRemoved, + env->NewStringUTF(c_object_path)); } else LOG_AND_FREE_DBUS_ERROR_WITH_MSG(&err, msg); + return DBUS_HANDLER_RESULT_HANDLED; } else if (dbus_message_is_signal(msg, "org.bluez.Adapter", - "ModeChanged")) { - char *c_mode; - if (dbus_message_get_args(msg, &err, - DBUS_TYPE_STRING, &c_mode, - DBUS_TYPE_INVALID)) { - LOGV("... mode = %s", c_mode); + "PropertyChanged")) { + jobjectArray str_array = parse_adapter_property_change(env, msg); + if (str_array != NULL) { + /* Check if bluetoothd has (re)started, if so update the path. */ + jstring property =(jstring) env->GetObjectArrayElement(str_array, 0); + const char *c_property = env->GetStringUTFChars(property, NULL); + if (!strncmp(c_property, "Powered", strlen("Powered"))) { + jstring value = + (jstring) env->GetObjectArrayElement(str_array, 1); + const char *c_value = env->GetStringUTFChars(value, NULL); + if (!strncmp(c_value, "true", strlen("true"))) + nat->adapter = get_adapter_path(nat->conn); + env->ReleaseStringUTFChars(value, c_value); + } + env->ReleaseStringUTFChars(property, c_property); + env->CallVoidMethod(nat->me, - method_onModeChanged, - env->NewStringUTF(c_mode)); + method_onPropertyChanged, + str_array); } else LOG_AND_FREE_DBUS_ERROR_WITH_MSG(&err, msg); return DBUS_HANDLER_RESULT_HANDLED; } else if (dbus_message_is_signal(msg, - "org.bluez.Adapter", - "NameChanged")) { - char *c_name; - if (dbus_message_get_args(msg, &err, - DBUS_TYPE_STRING, &c_name, - DBUS_TYPE_INVALID)) { - LOGV("... name = %s", c_name); + "org.bluez.Device", + "PropertyChanged")) { + jobjectArray str_array = parse_remote_device_property_change(env, msg); + if (str_array != NULL) { + const char *remote_device_path = dbus_message_get_path(msg); env->CallVoidMethod(nat->me, - method_onNameChanged, - env->NewStringUTF(c_name)); - } else LOG_AND_FREE_DBUS_ERROR_WITH_MSG(&err, msg); - return DBUS_HANDLER_RESULT_HANDLED; - } else if (dbus_message_is_signal(msg, - "org.freedesktop.DBus", - "NameOwnerChanged")) { - char *c_name; - char *c_old_owner; - char *c_new_owner; - if (dbus_message_get_args(msg, &err, - DBUS_TYPE_STRING, &c_name, - DBUS_TYPE_STRING, &c_old_owner, - DBUS_TYPE_STRING, &c_new_owner, - DBUS_TYPE_INVALID)) { - LOGV("... name = %s", c_name); - LOGV("... old_owner = %s", c_old_owner); - LOGV("... new_owner = %s", c_new_owner); - if (!strcmp(c_name, "org.bluez") && c_new_owner[0] != '\0') { - // New owner of org.bluez. This can only happen when hcid - // restarts. Need to restart framework BT services to recover. - LOGE("Looks like hcid restarted"); - env->CallVoidMethod(nat->me, method_onRestartRequired); - } + method_onDevicePropertyChanged, + env->NewStringUTF(remote_device_path), + str_array); } else LOG_AND_FREE_DBUS_ERROR_WITH_MSG(&err, msg); return DBUS_HANDLER_RESULT_HANDLED; - } + } return a2dp_event_filter(msg, env); } // Called by dbus during WaitForAndDispatchEventNative() -static DBusHandlerResult agent_event_filter(DBusConnection *conn, - DBusMessage *msg, void *data) { +DBusHandlerResult agent_event_filter(DBusConnection *conn, + DBusMessage *msg, void *data) { native_data_t *nat = (native_data_t *)data; JNIEnv *env; - nat->vm->GetEnv((void**)&env, nat->envVer); if (dbus_message_get_type(msg) != DBUS_MESSAGE_TYPE_METHOD_CALL) { LOGV("%s: not interested (not a method call).", __FUNCTION__); return DBUS_HANDLER_RESULT_NOT_YET_HANDLED; } - LOGV("%s: Received method %s:%s", __FUNCTION__, + LOGI("%s: Received method %s:%s", __FUNCTION__, dbus_message_get_interface(msg), dbus_message_get_member(msg)); - if (dbus_message_is_method_call(msg, - "org.bluez.PasskeyAgent", "Request")) { - - const char *adapter; - const char *address; - if (!dbus_message_get_args(msg, NULL, - DBUS_TYPE_STRING, &adapter, - DBUS_TYPE_STRING, &address, - DBUS_TYPE_INVALID)) { - LOGE("%s: Invalid arguments for Request() method", __FUNCTION__); - return DBUS_HANDLER_RESULT_NOT_YET_HANDLED; - } - - LOGV("... address = %s", address); - - dbus_message_ref(msg); // increment refcount because we pass to java + if (nat == NULL) return DBUS_HANDLER_RESULT_HANDLED; - env->CallVoidMethod(nat->me, method_onPasskeyAgentRequest, - env->NewStringUTF(address), (int)msg); - - return DBUS_HANDLER_RESULT_HANDLED; - - } else if (dbus_message_is_method_call(msg, - "org.bluez.PasskeyAgent", "Cancel")) { - - const char *adapter; - const char *address; - if (!dbus_message_get_args(msg, NULL, - DBUS_TYPE_STRING, &adapter, - DBUS_TYPE_STRING, &address, - DBUS_TYPE_INVALID)) { - LOGE("%s: Invalid arguments for Cancel() method", __FUNCTION__); - return DBUS_HANDLER_RESULT_NOT_YET_HANDLED; - } - - LOGV("... address = %s", address); + nat->vm->GetEnv((void**)&env, nat->envVer); + if (dbus_message_is_method_call(msg, + "org.bluez.Agent", "Cancel")) { - env->CallVoidMethod(nat->me, method_onPasskeyAgentCancel, - env->NewStringUTF(address)); + env->CallVoidMethod(nat->me, method_onAgentCancel); // reply DBusMessage *reply = dbus_message_new_method_return(msg); @@ -922,41 +807,23 @@ static DBusHandlerResult agent_event_filter(DBusConnection *conn, return DBUS_HANDLER_RESULT_HANDLED; } else if (dbus_message_is_method_call(msg, - "org.bluez.PasskeyAgent", "Release")) { - LOGW("We are no longer the passkey agent!"); - - // reply - DBusMessage *reply = dbus_message_new_method_return(msg); - if (!reply) { - LOGE("%s: Cannot create message reply\n", __FUNCTION__); - return DBUS_HANDLER_RESULT_NOT_YET_HANDLED; - } - dbus_connection_send(nat->conn, reply, NULL); - dbus_message_unref(reply); - return DBUS_HANDLER_RESULT_HANDLED; - } else if (dbus_message_is_method_call(msg, - "org.bluez.AuthorizationAgent", "Authorize")) { - const char *adapter; - const char *address; - const char *service; + "org.bluez.Agent", "Authorize")) { + char *object_path; const char *uuid; if (!dbus_message_get_args(msg, NULL, - DBUS_TYPE_STRING, &adapter, - DBUS_TYPE_STRING, &address, - DBUS_TYPE_STRING, &service, + DBUS_TYPE_OBJECT_PATH, &object_path, DBUS_TYPE_STRING, &uuid, DBUS_TYPE_INVALID)) { LOGE("%s: Invalid arguments for Authorize() method", __FUNCTION__); return DBUS_HANDLER_RESULT_NOT_YET_HANDLED; } - LOGV("... address = %s", address); - LOGV("... service = %s", service); + LOGV("... object_path = %s", object_path); LOGV("... uuid = %s", uuid); - bool auth_granted = env->CallBooleanMethod(nat->me, - method_onAuthAgentAuthorize, env->NewStringUTF(address), - env->NewStringUTF(service), env->NewStringUTF(uuid)); + bool auth_granted = + env->CallBooleanMethod(nat->me, method_onAgentAuthorize, + env->NewStringUTF(object_path), env->NewStringUTF(uuid)); // reply if (auth_granted) { @@ -979,43 +846,22 @@ static DBusHandlerResult agent_event_filter(DBusConnection *conn, } return DBUS_HANDLER_RESULT_HANDLED; } else if (dbus_message_is_method_call(msg, - "org.bluez.AuthorizationAgent", "Cancel")) { - const char *adapter; - const char *address; - const char *service; - const char *uuid; + "org.bluez.Agent", "RequestPinCode")) { + char *object_path; if (!dbus_message_get_args(msg, NULL, - DBUS_TYPE_STRING, &adapter, - DBUS_TYPE_STRING, &address, - DBUS_TYPE_STRING, &service, - DBUS_TYPE_STRING, &uuid, + DBUS_TYPE_OBJECT_PATH, &object_path, DBUS_TYPE_INVALID)) { - LOGE("%s: Invalid arguments for Cancel() method", __FUNCTION__); + LOGE("%s: Invalid arguments for RequestPinCode() method", __FUNCTION__); return DBUS_HANDLER_RESULT_NOT_YET_HANDLED; } - LOGV("... address = %s", address); - LOGV("... service = %s", service); - LOGV("... uuid = %s", uuid); - - env->CallVoidMethod(nat->me, - method_onAuthAgentCancel, env->NewStringUTF(address), - env->NewStringUTF(service), env->NewStringUTF(uuid)); - - // reply - DBusMessage *reply = dbus_message_new_method_return(msg); - if (!reply) { - LOGE("%s: Cannot create message reply\n", __FUNCTION__); - return DBUS_HANDLER_RESULT_NOT_YET_HANDLED; - } - dbus_connection_send(nat->conn, reply, NULL); - dbus_message_unref(reply); + dbus_message_ref(msg); // increment refcount because we pass to java + env->CallVoidMethod(nat->me, method_onRequestPinCode, + env->NewStringUTF(object_path), + int(msg)); return DBUS_HANDLER_RESULT_HANDLED; - } else if (dbus_message_is_method_call(msg, - "org.bluez.AuthorizationAgent", "Release")) { - LOGW("We are no longer the auth agent!"); - + "org.bluez.Agent", "Release")) { // reply DBusMessage *reply = dbus_message_new_method_return(msg); if (!reply) { @@ -1026,7 +872,7 @@ static DBusHandlerResult agent_event_filter(DBusConnection *conn, dbus_message_unref(reply); return DBUS_HANDLER_RESULT_HANDLED; } else { - LOGV("... ignored"); + LOGV("%s:%s is ignored", dbus_message_get_interface(msg), dbus_message_get_member(msg)); } return DBUS_HANDLER_RESULT_NOT_YET_HANDLED; @@ -1044,7 +890,7 @@ static DBusHandlerResult agent_event_filter(DBusConnection *conn, #define BOND_RESULT_REMOTE_DEVICE_DOWN 4 #define BOND_RESULT_DISCOVERY_IN_PROGRESS 5 -void onCreateBondingResult(DBusMessage *msg, void *user, void *n) { +void onCreatePairedDeviceResult(DBusMessage *msg, void *user, void *n) { LOGV(__FUNCTION__); native_data_t *nat = (native_data_t *)n; @@ -1095,7 +941,7 @@ void onCreateBondingResult(DBusMessage *msg, void *user, void *n) { } env->CallVoidMethod(nat->me, - method_onCreateBondingResult, + method_onCreatePairedDeviceResult, env->NewStringUTF(address), result); done: @@ -1103,7 +949,7 @@ done: free(user); } -void onGetRemoteServiceChannelResult(DBusMessage *msg, void *user, void *n) { +void onGetDeviceServiceChannelResult(DBusMessage *msg, void *user, void *n) { LOGV(__FUNCTION__); const char *address = (const char *) user; @@ -1122,14 +968,13 @@ void onGetRemoteServiceChannelResult(DBusMessage *msg, void *user, void *n) { !dbus_message_get_args(msg, &err, DBUS_TYPE_INT32, &channel, DBUS_TYPE_INVALID)) { - /* if (!strcmp(err.name, BLUEZ_DBUS_BASE_IFC ".Error.AuthenticationFailed")) */ LOGE("%s: D-Bus error: %s (%s)\n", __FUNCTION__, err.name, err.message); dbus_error_free(&err); } done: env->CallVoidMethod(nat->me, - method_onGetRemoteServiceChannelResult, + method_onGetDeviceServiceChannelResult, env->NewStringUTF(address), channel); free(user); diff --git a/core/jni/android_util_Process.cpp b/core/jni/android_util_Process.cpp index e8bffa4..1c9ee7d 100644 --- a/core/jni/android_util_Process.cpp +++ b/core/jni/android_util_Process.cpp @@ -50,8 +50,6 @@ pid_t gettid() { return syscall(__NR_gettid);} #undef __KERNEL__ #endif -#define ENABLE_CGROUP_ERR_LOGGING 0 - /* * List of cgroup names which map to ANDROID_TGROUP_ values in Thread.h * and Process.java @@ -198,31 +196,24 @@ jint android_os_Process_getGidForName(JNIEnv* env, jobject clazz, jstring name) static int add_pid_to_cgroup(int pid, int grp) { - FILE *fp; + int fd; char path[255]; - int rc; - - sprintf(path, "/dev/cpuctl/%s/tasks", (cgroup_names[grp] ? cgroup_names[grp] : "")); + char text[64]; - if (!(fp = fopen(path, "w"))) { -#if ENABLE_CGROUP_ERR_LOGGING - LOGW("Unable to open %s (%s)\n", path, strerror(errno)); -#endif - return -errno; - } + sprintf(path, "/dev/cpuctl/%s/tasks", + (cgroup_names[grp] ? cgroup_names[grp] : "")); - rc = fprintf(fp, "%d", pid); - fclose(fp); + if ((fd = open(path, O_WRONLY)) < 0) + return -1; - if (rc < 0) { -#if ENABLE_CGROUP_ERR_LOGGING - LOGW("Unable to move pid %d to cgroup %s (%s)\n", pid, - (cgroup_names[grp] ? cgroup_names[grp] : "<default>"), - strerror(errno)); -#endif + sprintf(text, "%d", pid); + if (write(fd, text, strlen(text)) < 0) { + close(fd); + return -1; } - return (rc < 0) ? errno : 0; + close(fd); + return 0; } void android_os_Process_setThreadGroup(JNIEnv* env, jobject clazz, int pid, jint grp) @@ -232,8 +223,11 @@ void android_os_Process_setThreadGroup(JNIEnv* env, jobject clazz, int pid, jint return; } - if (add_pid_to_cgroup(pid, grp)) - signalExceptionForGroupError(env, clazz, errno); + if (add_pid_to_cgroup(pid, grp)) { + // If the thread exited on us, don't generate an exception + if (errno != ESRCH && errno != ENOENT) + signalExceptionForGroupError(env, clazz, errno); + } } void android_os_Process_setProcessGroup(JNIEnv* env, jobject clazz, int pid, jint grp) @@ -250,17 +244,23 @@ void android_os_Process_setProcessGroup(JNIEnv* env, jobject clazz, int pid, jin sprintf(proc_path, "/proc/%d/task", pid); if (!(d = opendir(proc_path))) { - signalExceptionForGroupError(env, clazz, errno); + // If the process exited on us, don't generate an exception + if (errno != ENOENT) + signalExceptionForGroupError(env, clazz, errno); return; } while ((de = readdir(d))) { if (de->d_name[0] == '.') continue; + if (add_pid_to_cgroup(atoi(de->d_name), grp)) { - signalExceptionForGroupError(env, clazz, errno); - closedir(d); - return; + // If the thread exited on us, ignore it and keep going + if (errno != ESRCH && errno != ENOENT) { + signalExceptionForGroupError(env, clazz, errno); + closedir(d); + return; + } } } closedir(d); diff --git a/core/jni/com_google_android_gles_jni_GLImpl.cpp b/core/jni/com_google_android_gles_jni_GLImpl.cpp index 15e3a81..89b1f96 100644 --- a/core/jni/com_google_android_gles_jni_GLImpl.cpp +++ b/core/jni/com_google_android_gles_jni_GLImpl.cpp @@ -45,9 +45,11 @@ static jclass OOMEClass; static jclass UOEClass; static jclass IAEClass; static jclass AIOOBEClass; +static jclass G11ImplClass; static jmethodID getBasePointerID; static jmethodID getBaseArrayID; static jmethodID getBaseArrayOffsetID; +static jmethodID allowIndirectBuffersID; static jfieldID positionID; static jfieldID limitID; static jfieldID elementSizeShiftID; @@ -63,13 +65,17 @@ nativeClassInitBuffer(JNIEnv *_env) jclass bufferClassLocal = _env->FindClass("java/nio/Buffer"); bufferClass = (jclass) _env->NewGlobalRef(bufferClassLocal); + jclass g11impClassLocal = _env->FindClass("com/google/android/gles_jni/GLImpl"); + G11ImplClass = (jclass) _env->NewGlobalRef(g11impClassLocal); + getBasePointerID = _env->GetStaticMethodID(nioAccessClass, "getBasePointer", "(Ljava/nio/Buffer;)J"); getBaseArrayID = _env->GetStaticMethodID(nioAccessClass, "getBaseArray", "(Ljava/nio/Buffer;)Ljava/lang/Object;"); getBaseArrayOffsetID = _env->GetStaticMethodID(nioAccessClass, "getBaseArrayOffset", "(Ljava/nio/Buffer;)I"); - + allowIndirectBuffersID = _env->GetStaticMethodID(g11impClassLocal, + "allowIndirectBuffers", "(Ljava/lang/String;)Z"); positionID = _env->GetFieldID(bufferClass, "position", "I"); limitID = _env->GetFieldID(bufferClass, "limit", "I"); elementSizeShiftID = @@ -119,6 +125,9 @@ getPointer(JNIEnv *_env, jobject buffer, jarray *array, jint *remaining) *array = (jarray) _env->CallStaticObjectMethod(nioAccessClass, getBaseArrayID, buffer); + if (*array == NULL) { + return (void*) NULL; + } offset = _env->CallStaticIntMethod(nioAccessClass, getBaseArrayOffsetID, buffer); data = _env->GetPrimitiveArrayCritical(*array, (jboolean *) 0); @@ -133,17 +142,43 @@ releasePointer(JNIEnv *_env, jarray array, void *data, jboolean commit) commit ? 0 : JNI_ABORT); } +extern "C" { +extern char* __progname; +} + +static bool +allowIndirectBuffers(JNIEnv *_env) { + static jint sIndirectBufferCompatability; + if (sIndirectBufferCompatability == 0) { + jobject appName = _env->NewStringUTF(::__progname); + sIndirectBufferCompatability = _env->CallStaticBooleanMethod(G11ImplClass, allowIndirectBuffersID, appName) ? 2 : 1; + } + return sIndirectBufferCompatability == 2; +} + static void * getDirectBufferPointer(JNIEnv *_env, jobject buffer) { - char* buf = (char*) _env->GetDirectBufferAddress(buffer); + if (!buffer) { + return NULL; + } + void* buf = _env->GetDirectBufferAddress(buffer); if (buf) { jint position = _env->GetIntField(buffer, positionID); jint elementSizeShift = _env->GetIntField(buffer, elementSizeShiftID); - buf += position << elementSizeShift; + buf = ((char*) buf) + (position << elementSizeShift); } else { - _env->ThrowNew(IAEClass, "Must use a native order direct Buffer"); + if (allowIndirectBuffers(_env)) { + jarray array = 0; + jint remaining; + buf = getPointer(_env, buffer, &array, &remaining); + if (array) { + releasePointer(_env, array, buf, 0); + } + } else { + _env->ThrowNew(IAEClass, "Must use a native order direct Buffer"); + } } - return (void*) buf; + return buf; } static int |