You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
1857 lines
62 KiB
1857 lines
62 KiB
/*
|
|
* Copyright (C) 2012 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 "BluetoothServiceJni"
|
|
#include "com_android_bluetooth.h"
|
|
#include "hardware/bt_sock.h"
|
|
#include "utils/Log.h"
|
|
#include "utils/misc.h"
|
|
|
|
#include <dlfcn.h>
|
|
#include <errno.h>
|
|
#include <pthread.h>
|
|
#include <string.h>
|
|
|
|
#include <fcntl.h>
|
|
#include <sys/prctl.h>
|
|
#include <sys/stat.h>
|
|
|
|
#include <hardware/bluetooth.h>
|
|
#include <nativehelper/JNIPlatformHelp.h>
|
|
#include <mutex>
|
|
|
|
#include <pthread.h>
|
|
|
|
using bluetooth::Uuid;
|
|
|
|
namespace android {
|
|
// Both
|
|
// OOB_ADDRESS_SIZE is 6 bytes address + 1 byte address type
|
|
#define OOB_ADDRESS_SIZE 7
|
|
#define OOB_C_SIZE 16
|
|
#define OOB_R_SIZE 16
|
|
#define OOB_NAME_MAX_SIZE 256
|
|
// Classic
|
|
#define OOB_DATA_LEN_SIZE 2
|
|
#define OOB_COD_SIZE 3
|
|
// LE
|
|
#define OOB_TK_SIZE 16
|
|
#define OOB_LE_FLAG_SIZE 1
|
|
#define OOB_LE_ROLE_SIZE 1
|
|
#define OOB_LE_APPEARANCE_SIZE 2
|
|
|
|
#define TRANSPORT_AUTO 0
|
|
#define TRANSPORT_BREDR 1
|
|
#define TRANSPORT_LE 2
|
|
|
|
const jint INVALID_FD = -1;
|
|
|
|
static jmethodID method_oobDataReceivedCallback;
|
|
static jmethodID method_stateChangeCallback;
|
|
static jmethodID method_adapterPropertyChangedCallback;
|
|
static jmethodID method_devicePropertyChangedCallback;
|
|
static jmethodID method_deviceFoundCallback;
|
|
static jmethodID method_pinRequestCallback;
|
|
static jmethodID method_sspRequestCallback;
|
|
static jmethodID method_bondStateChangeCallback;
|
|
static jmethodID method_aclStateChangeCallback;
|
|
static jmethodID method_discoveryStateChangeCallback;
|
|
static jmethodID method_linkQualityReportCallback;
|
|
static jmethodID method_setWakeAlarm;
|
|
static jmethodID method_acquireWakeLock;
|
|
static jmethodID method_releaseWakeLock;
|
|
static jmethodID method_energyInfo;
|
|
|
|
static struct {
|
|
jclass clazz;
|
|
jmethodID constructor;
|
|
} android_bluetooth_UidTraffic;
|
|
|
|
static const bt_interface_t* sBluetoothInterface = NULL;
|
|
static const btsock_interface_t* sBluetoothSocketInterface = NULL;
|
|
static JavaVM* vm = NULL;
|
|
static JNIEnv* callbackEnv = NULL;
|
|
static pthread_t sCallbackThread;
|
|
static bool sHaveCallbackThread;
|
|
|
|
static jobject sJniAdapterServiceObj;
|
|
static jobject sJniCallbacksObj;
|
|
static jfieldID sJniCallbacksField;
|
|
|
|
const bt_interface_t* getBluetoothInterface() { return sBluetoothInterface; }
|
|
|
|
JNIEnv* getCallbackEnv() { return callbackEnv; }
|
|
|
|
bool isCallbackThread() {
|
|
return sHaveCallbackThread && pthread_equal(sCallbackThread, pthread_self());
|
|
}
|
|
|
|
static void adapter_state_change_callback(bt_state_t status) {
|
|
CallbackEnv sCallbackEnv(__func__);
|
|
if (!sCallbackEnv.valid()) return;
|
|
ALOGV("%s: Status is: %d", __func__, status);
|
|
|
|
sCallbackEnv->CallVoidMethod(sJniCallbacksObj, method_stateChangeCallback,
|
|
(jint)status);
|
|
}
|
|
|
|
static int get_properties(int num_properties, bt_property_t* properties,
|
|
jintArray* types, jobjectArray* props) {
|
|
for (int i = 0; i < num_properties; i++) {
|
|
ScopedLocalRef<jbyteArray> propVal(
|
|
callbackEnv, callbackEnv->NewByteArray(properties[i].len));
|
|
if (!propVal.get()) {
|
|
ALOGE("Error while allocation of array in %s", __func__);
|
|
return -1;
|
|
}
|
|
|
|
callbackEnv->SetByteArrayRegion(propVal.get(), 0, properties[i].len,
|
|
(jbyte*)properties[i].val);
|
|
callbackEnv->SetObjectArrayElement(*props, i, propVal.get());
|
|
callbackEnv->SetIntArrayRegion(*types, i, 1, (jint*)&properties[i].type);
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
static void adapter_properties_callback(bt_status_t status, int num_properties,
|
|
bt_property_t* properties) {
|
|
CallbackEnv sCallbackEnv(__func__);
|
|
if (!sCallbackEnv.valid()) return;
|
|
|
|
ALOGV("%s: Status is: %d, Properties: %d", __func__, status, num_properties);
|
|
|
|
if (status != BT_STATUS_SUCCESS) {
|
|
ALOGE("%s: Status %d is incorrect", __func__, status);
|
|
return;
|
|
}
|
|
|
|
ScopedLocalRef<jbyteArray> val(
|
|
sCallbackEnv.get(),
|
|
(jbyteArray)sCallbackEnv->NewByteArray(num_properties));
|
|
if (!val.get()) {
|
|
ALOGE("%s: Error allocating byteArray", __func__);
|
|
return;
|
|
}
|
|
|
|
ScopedLocalRef<jclass> mclass(sCallbackEnv.get(),
|
|
sCallbackEnv->GetObjectClass(val.get()));
|
|
|
|
/* (BT) Initialize the jobjectArray and jintArray here itself and send the
|
|
initialized array pointers alone to get_properties */
|
|
|
|
ScopedLocalRef<jobjectArray> props(
|
|
sCallbackEnv.get(),
|
|
sCallbackEnv->NewObjectArray(num_properties, mclass.get(), NULL));
|
|
if (!props.get()) {
|
|
ALOGE("%s: Error allocating object Array for properties", __func__);
|
|
return;
|
|
}
|
|
|
|
ScopedLocalRef<jintArray> types(
|
|
sCallbackEnv.get(), (jintArray)sCallbackEnv->NewIntArray(num_properties));
|
|
if (!types.get()) {
|
|
ALOGE("%s: Error allocating int Array for values", __func__);
|
|
return;
|
|
}
|
|
|
|
jintArray typesPtr = types.get();
|
|
jobjectArray propsPtr = props.get();
|
|
if (get_properties(num_properties, properties, &typesPtr, &propsPtr) < 0) {
|
|
return;
|
|
}
|
|
|
|
sCallbackEnv->CallVoidMethod(sJniCallbacksObj,
|
|
method_adapterPropertyChangedCallback,
|
|
types.get(), props.get());
|
|
}
|
|
|
|
static void remote_device_properties_callback(bt_status_t status,
|
|
RawAddress* bd_addr,
|
|
int num_properties,
|
|
bt_property_t* properties) {
|
|
CallbackEnv sCallbackEnv(__func__);
|
|
if (!sCallbackEnv.valid()) return;
|
|
|
|
ALOGV("%s: Status is: %d, Properties: %d", __func__, status, num_properties);
|
|
|
|
if (status != BT_STATUS_SUCCESS) {
|
|
ALOGE("%s: Status %d is incorrect", __func__, status);
|
|
return;
|
|
}
|
|
|
|
ScopedLocalRef<jbyteArray> val(
|
|
sCallbackEnv.get(),
|
|
(jbyteArray)sCallbackEnv->NewByteArray(num_properties));
|
|
if (!val.get()) {
|
|
ALOGE("%s: Error allocating byteArray", __func__);
|
|
return;
|
|
}
|
|
|
|
ScopedLocalRef<jclass> mclass(sCallbackEnv.get(),
|
|
sCallbackEnv->GetObjectClass(val.get()));
|
|
|
|
/* Initialize the jobjectArray and jintArray here itself and send the
|
|
initialized array pointers alone to get_properties */
|
|
|
|
ScopedLocalRef<jobjectArray> props(
|
|
sCallbackEnv.get(),
|
|
sCallbackEnv->NewObjectArray(num_properties, mclass.get(), NULL));
|
|
if (!props.get()) {
|
|
ALOGE("%s: Error allocating object Array for properties", __func__);
|
|
return;
|
|
}
|
|
|
|
ScopedLocalRef<jintArray> types(
|
|
sCallbackEnv.get(), (jintArray)sCallbackEnv->NewIntArray(num_properties));
|
|
if (!types.get()) {
|
|
ALOGE("%s: Error allocating int Array for values", __func__);
|
|
return;
|
|
}
|
|
|
|
ScopedLocalRef<jbyteArray> addr(
|
|
sCallbackEnv.get(), sCallbackEnv->NewByteArray(sizeof(RawAddress)));
|
|
if (!addr.get()) {
|
|
ALOGE("Error while allocation byte array in %s", __func__);
|
|
return;
|
|
}
|
|
|
|
sCallbackEnv->SetByteArrayRegion(addr.get(), 0, sizeof(RawAddress),
|
|
(jbyte*)bd_addr);
|
|
|
|
jintArray typesPtr = types.get();
|
|
jobjectArray propsPtr = props.get();
|
|
if (get_properties(num_properties, properties, &typesPtr, &propsPtr) < 0) {
|
|
return;
|
|
}
|
|
|
|
sCallbackEnv->CallVoidMethod(sJniCallbacksObj,
|
|
method_devicePropertyChangedCallback, addr.get(),
|
|
types.get(), props.get());
|
|
}
|
|
|
|
static void device_found_callback(int num_properties,
|
|
bt_property_t* properties) {
|
|
CallbackEnv sCallbackEnv(__func__);
|
|
if (!sCallbackEnv.valid()) return;
|
|
|
|
ScopedLocalRef<jbyteArray> addr(sCallbackEnv.get(), NULL);
|
|
int addr_index;
|
|
for (int i = 0; i < num_properties; i++) {
|
|
if (properties[i].type == BT_PROPERTY_BDADDR) {
|
|
addr.reset(sCallbackEnv->NewByteArray(properties[i].len));
|
|
if (!addr.get()) {
|
|
ALOGE("Address is NULL (unable to allocate) in %s", __func__);
|
|
return;
|
|
}
|
|
sCallbackEnv->SetByteArrayRegion(addr.get(), 0, properties[i].len,
|
|
(jbyte*)properties[i].val);
|
|
addr_index = i;
|
|
}
|
|
}
|
|
if (!addr.get()) {
|
|
ALOGE("Address is NULL in %s", __func__);
|
|
return;
|
|
}
|
|
|
|
ALOGV("%s: Properties: %d, Address: %s", __func__, num_properties,
|
|
(const char*)properties[addr_index].val);
|
|
|
|
remote_device_properties_callback(BT_STATUS_SUCCESS,
|
|
(RawAddress*)properties[addr_index].val,
|
|
num_properties, properties);
|
|
|
|
sCallbackEnv->CallVoidMethod(sJniCallbacksObj, method_deviceFoundCallback,
|
|
addr.get());
|
|
}
|
|
|
|
static void bond_state_changed_callback(bt_status_t status, RawAddress* bd_addr,
|
|
bt_bond_state_t state) {
|
|
CallbackEnv sCallbackEnv(__func__);
|
|
if (!sCallbackEnv.valid()) return;
|
|
|
|
if (!bd_addr) {
|
|
ALOGE("Address is null in %s", __func__);
|
|
return;
|
|
}
|
|
|
|
ScopedLocalRef<jbyteArray> addr(
|
|
sCallbackEnv.get(), sCallbackEnv->NewByteArray(sizeof(RawAddress)));
|
|
if (!addr.get()) {
|
|
ALOGE("Address allocation failed in %s", __func__);
|
|
return;
|
|
}
|
|
sCallbackEnv->SetByteArrayRegion(addr.get(), 0, sizeof(RawAddress),
|
|
(jbyte*)bd_addr);
|
|
|
|
sCallbackEnv->CallVoidMethod(sJniCallbacksObj, method_bondStateChangeCallback,
|
|
(jint)status, addr.get(), (jint)state);
|
|
}
|
|
|
|
static void acl_state_changed_callback(bt_status_t status, RawAddress* bd_addr,
|
|
bt_acl_state_t state, bt_hci_error_code_t hci_reason) {
|
|
if (!bd_addr) {
|
|
ALOGE("Address is null in %s", __func__);
|
|
return;
|
|
}
|
|
|
|
CallbackEnv sCallbackEnv(__func__);
|
|
if (!sCallbackEnv.valid()) return;
|
|
|
|
ScopedLocalRef<jbyteArray> addr(
|
|
sCallbackEnv.get(), sCallbackEnv->NewByteArray(sizeof(RawAddress)));
|
|
if (!addr.get()) {
|
|
ALOGE("Address allocation failed in %s", __func__);
|
|
return;
|
|
}
|
|
sCallbackEnv->SetByteArrayRegion(addr.get(), 0, sizeof(RawAddress),
|
|
(jbyte*)bd_addr);
|
|
|
|
sCallbackEnv->CallVoidMethod(sJniCallbacksObj, method_aclStateChangeCallback,
|
|
(jint)status, addr.get(), (jint)state, (jint)hci_reason);
|
|
}
|
|
|
|
static void discovery_state_changed_callback(bt_discovery_state_t state) {
|
|
CallbackEnv sCallbackEnv(__func__);
|
|
if (!sCallbackEnv.valid()) return;
|
|
|
|
ALOGV("%s: DiscoveryState:%d ", __func__, state);
|
|
|
|
sCallbackEnv->CallVoidMethod(
|
|
sJniCallbacksObj, method_discoveryStateChangeCallback, (jint)state);
|
|
}
|
|
|
|
static void pin_request_callback(RawAddress* bd_addr, bt_bdname_t* bdname,
|
|
uint32_t cod, bool min_16_digits) {
|
|
if (!bd_addr) {
|
|
ALOGE("Address is null in %s", __func__);
|
|
return;
|
|
}
|
|
|
|
CallbackEnv sCallbackEnv(__func__);
|
|
if (!sCallbackEnv.valid()) return;
|
|
|
|
ScopedLocalRef<jbyteArray> addr(
|
|
sCallbackEnv.get(), sCallbackEnv->NewByteArray(sizeof(RawAddress)));
|
|
if (!addr.get()) {
|
|
ALOGE("Error while allocating in: %s", __func__);
|
|
return;
|
|
}
|
|
|
|
sCallbackEnv->SetByteArrayRegion(addr.get(), 0, sizeof(RawAddress),
|
|
(jbyte*)bd_addr);
|
|
|
|
ScopedLocalRef<jbyteArray> devname(
|
|
sCallbackEnv.get(), sCallbackEnv->NewByteArray(sizeof(bt_bdname_t)));
|
|
if (!devname.get()) {
|
|
ALOGE("Error while allocating in: %s", __func__);
|
|
return;
|
|
}
|
|
|
|
sCallbackEnv->SetByteArrayRegion(devname.get(), 0, sizeof(bt_bdname_t),
|
|
(jbyte*)bdname);
|
|
|
|
sCallbackEnv->CallVoidMethod(sJniCallbacksObj, method_pinRequestCallback,
|
|
addr.get(), devname.get(), cod, min_16_digits);
|
|
}
|
|
|
|
static void ssp_request_callback(RawAddress* bd_addr, bt_bdname_t* bdname,
|
|
uint32_t cod, bt_ssp_variant_t pairing_variant,
|
|
uint32_t pass_key) {
|
|
if (!bd_addr) {
|
|
ALOGE("Address is null in %s", __func__);
|
|
return;
|
|
}
|
|
CallbackEnv sCallbackEnv(__func__);
|
|
if (!sCallbackEnv.valid()) return;
|
|
|
|
ScopedLocalRef<jbyteArray> addr(
|
|
sCallbackEnv.get(), sCallbackEnv->NewByteArray(sizeof(RawAddress)));
|
|
if (!addr.get()) {
|
|
ALOGE("Error while allocating in: %s", __func__);
|
|
return;
|
|
}
|
|
|
|
sCallbackEnv->SetByteArrayRegion(addr.get(), 0, sizeof(RawAddress),
|
|
(jbyte*)bd_addr);
|
|
|
|
ScopedLocalRef<jbyteArray> devname(
|
|
sCallbackEnv.get(), sCallbackEnv->NewByteArray(sizeof(bt_bdname_t)));
|
|
if (!devname.get()) {
|
|
ALOGE("Error while allocating in: %s", __func__);
|
|
return;
|
|
}
|
|
|
|
sCallbackEnv->SetByteArrayRegion(devname.get(), 0, sizeof(bt_bdname_t),
|
|
(jbyte*)bdname);
|
|
|
|
sCallbackEnv->CallVoidMethod(sJniCallbacksObj, method_sspRequestCallback,
|
|
addr.get(), devname.get(), cod,
|
|
(jint)pairing_variant, pass_key);
|
|
}
|
|
|
|
static jobject createClassicOobDataObject(JNIEnv* env, bt_oob_data_t oob_data) {
|
|
ALOGV("%s", __func__);
|
|
jclass classicBuilderClass =
|
|
env->FindClass("android/bluetooth/OobData$ClassicBuilder");
|
|
|
|
jbyteArray confirmationHash = env->NewByteArray(OOB_C_SIZE);
|
|
env->SetByteArrayRegion(confirmationHash, 0, OOB_C_SIZE,
|
|
reinterpret_cast<jbyte*>(oob_data.c));
|
|
|
|
jbyteArray oobDataLength = env->NewByteArray(OOB_DATA_LEN_SIZE);
|
|
env->SetByteArrayRegion(oobDataLength, 0, OOB_DATA_LEN_SIZE,
|
|
reinterpret_cast<jbyte*>(oob_data.oob_data_length));
|
|
|
|
jbyteArray address = env->NewByteArray(OOB_ADDRESS_SIZE);
|
|
env->SetByteArrayRegion(address, 0, OOB_ADDRESS_SIZE,
|
|
reinterpret_cast<jbyte*>(oob_data.address));
|
|
|
|
jmethodID classicBuilderConstructor =
|
|
env->GetMethodID(classicBuilderClass, "<init>", "([B[B[B)V");
|
|
|
|
jobject oobDataClassicBuilder =
|
|
env->NewObject(classicBuilderClass, classicBuilderConstructor,
|
|
confirmationHash, oobDataLength, address);
|
|
|
|
jmethodID setRMethod =
|
|
env->GetMethodID(classicBuilderClass, "setRandomizerHash",
|
|
"([B)Landroid/bluetooth/OobData$ClassicBuilder;");
|
|
jbyteArray randomizerHash = env->NewByteArray(OOB_R_SIZE);
|
|
env->SetByteArrayRegion(randomizerHash, 0, OOB_R_SIZE,
|
|
reinterpret_cast<jbyte*>(oob_data.r));
|
|
|
|
oobDataClassicBuilder =
|
|
env->CallObjectMethod(oobDataClassicBuilder, setRMethod, randomizerHash);
|
|
|
|
jmethodID setNameMethod =
|
|
env->GetMethodID(classicBuilderClass, "setDeviceName",
|
|
"([B)Landroid/bluetooth/OobData$ClassicBuilder;");
|
|
|
|
int name_char_count = 0;
|
|
for (int i = 0; i < OOB_NAME_MAX_SIZE; i++) {
|
|
if (oob_data.device_name[i] == 0) {
|
|
name_char_count = i;
|
|
break;
|
|
}
|
|
}
|
|
|
|
jbyteArray deviceName = env->NewByteArray(name_char_count);
|
|
env->SetByteArrayRegion(deviceName, 0, name_char_count,
|
|
reinterpret_cast<jbyte*>(oob_data.device_name));
|
|
|
|
oobDataClassicBuilder =
|
|
env->CallObjectMethod(oobDataClassicBuilder, setNameMethod, deviceName);
|
|
|
|
jmethodID buildMethod = env->GetMethodID(classicBuilderClass, "build",
|
|
"()Landroid/bluetooth/OobData;");
|
|
|
|
return env->CallObjectMethod(oobDataClassicBuilder, buildMethod);
|
|
}
|
|
|
|
static jobject createLeOobDataObject(JNIEnv* env, bt_oob_data_t oob_data) {
|
|
ALOGV("%s", __func__);
|
|
|
|
jclass leBuilderClass = env->FindClass("android/bluetooth/OobData$LeBuilder");
|
|
|
|
jbyteArray confirmationHash = env->NewByteArray(OOB_C_SIZE);
|
|
env->SetByteArrayRegion(confirmationHash, 0, OOB_C_SIZE,
|
|
reinterpret_cast<jbyte*>(oob_data.c));
|
|
|
|
jbyteArray address = env->NewByteArray(OOB_ADDRESS_SIZE);
|
|
env->SetByteArrayRegion(address, 0, OOB_ADDRESS_SIZE,
|
|
reinterpret_cast<jbyte*>(oob_data.address));
|
|
|
|
jint le_role = (jint)oob_data.le_device_role;
|
|
|
|
jmethodID leBuilderConstructor =
|
|
env->GetMethodID(leBuilderClass, "<init>", "([B[BI)V");
|
|
|
|
jobject oobDataLeBuilder = env->NewObject(
|
|
leBuilderClass, leBuilderConstructor, confirmationHash, address, le_role);
|
|
|
|
jmethodID setRMethod =
|
|
env->GetMethodID(leBuilderClass, "setRandomizerHash",
|
|
"([B)Landroid/bluetooth/OobData$LeBuilder;");
|
|
jbyteArray randomizerHash = env->NewByteArray(OOB_R_SIZE);
|
|
env->SetByteArrayRegion(randomizerHash, 0, OOB_R_SIZE,
|
|
reinterpret_cast<jbyte*>(oob_data.r));
|
|
|
|
oobDataLeBuilder =
|
|
env->CallObjectMethod(oobDataLeBuilder, setRMethod, randomizerHash);
|
|
|
|
jmethodID setNameMethod =
|
|
env->GetMethodID(leBuilderClass, "setDeviceName",
|
|
"([B)Landroid/bluetooth/OobData$LeBuilder;");
|
|
|
|
int name_char_count = 0;
|
|
for (int i = 0; i < OOB_NAME_MAX_SIZE; i++) {
|
|
if (oob_data.device_name[i] == 0) {
|
|
name_char_count = i;
|
|
break;
|
|
}
|
|
}
|
|
|
|
jbyteArray deviceName = env->NewByteArray(name_char_count);
|
|
env->SetByteArrayRegion(deviceName, 0, name_char_count,
|
|
reinterpret_cast<jbyte*>(oob_data.device_name));
|
|
|
|
oobDataLeBuilder =
|
|
env->CallObjectMethod(oobDataLeBuilder, setNameMethod, deviceName);
|
|
|
|
jmethodID buildMethod = env->GetMethodID(leBuilderClass, "build",
|
|
"()Landroid/bluetooth/OobData;");
|
|
|
|
return env->CallObjectMethod(oobDataLeBuilder, buildMethod);
|
|
}
|
|
|
|
static void generate_local_oob_data_callback(tBT_TRANSPORT transport,
|
|
bt_oob_data_t oob_data) {
|
|
ALOGV("%s", __func__);
|
|
CallbackEnv sCallbackEnv(__func__);
|
|
if (!sCallbackEnv.valid()) return;
|
|
|
|
if (transport == TRANSPORT_BREDR) {
|
|
sCallbackEnv->CallVoidMethod(
|
|
sJniCallbacksObj, method_oobDataReceivedCallback, (jint)transport,
|
|
((oob_data.is_valid)
|
|
? createClassicOobDataObject(sCallbackEnv.get(), oob_data)
|
|
: nullptr));
|
|
} else if (transport == TRANSPORT_LE) {
|
|
sCallbackEnv->CallVoidMethod(
|
|
sJniCallbacksObj, method_oobDataReceivedCallback, (jint)transport,
|
|
((oob_data.is_valid)
|
|
? createLeOobDataObject(sCallbackEnv.get(), oob_data)
|
|
: nullptr));
|
|
} else {
|
|
// TRANSPORT_AUTO is a concept, however, the host stack doesn't fully
|
|
// implement it So passing it from the java layer is currently useless until
|
|
// the implementation and concept of TRANSPORT_AUTO is fleshed out.
|
|
ALOGE("TRANSPORT: %d not implemented", transport);
|
|
sCallbackEnv->CallVoidMethod(sJniCallbacksObj,
|
|
method_oobDataReceivedCallback,
|
|
(jint)transport, nullptr);
|
|
}
|
|
}
|
|
|
|
static void link_quality_report_callback(
|
|
uint64_t timestamp, int report_id, int rssi, int snr,
|
|
int retransmission_count, int packets_not_receive_count,
|
|
int negative_acknowledgement_count) {
|
|
CallbackEnv sCallbackEnv(__func__);
|
|
if (!sCallbackEnv.valid()) return;
|
|
|
|
ALOGV("%s: LinkQualityReportCallback: %d %d %d %d %d %d", __func__,
|
|
report_id, rssi, snr, retransmission_count, packets_not_receive_count,
|
|
negative_acknowledgement_count);
|
|
|
|
sCallbackEnv->CallVoidMethod(
|
|
sJniCallbacksObj, method_linkQualityReportCallback,
|
|
(jlong)timestamp, (jint)report_id, (jint)rssi, (jint)snr,
|
|
(jint)retransmission_count, (jint)packets_not_receive_count,
|
|
(jint)negative_acknowledgement_count);
|
|
}
|
|
|
|
static void callback_thread_event(bt_cb_thread_evt event) {
|
|
if (event == ASSOCIATE_JVM) {
|
|
JavaVMAttachArgs args;
|
|
char name[] = "BT Service Callback Thread";
|
|
args.version = JNI_VERSION_1_6;
|
|
args.name = name;
|
|
args.group = NULL;
|
|
vm->AttachCurrentThread(&callbackEnv, &args);
|
|
sHaveCallbackThread = true;
|
|
sCallbackThread = pthread_self();
|
|
ALOGV("Callback thread attached: %p", callbackEnv);
|
|
} else if (event == DISASSOCIATE_JVM) {
|
|
if (!isCallbackThread()) {
|
|
ALOGE("Callback: '%s' is not called on the correct thread", __func__);
|
|
return;
|
|
}
|
|
vm->DetachCurrentThread();
|
|
sHaveCallbackThread = false;
|
|
}
|
|
}
|
|
|
|
static void dut_mode_recv_callback(uint16_t opcode, uint8_t* buf, uint8_t len) {
|
|
|
|
}
|
|
|
|
static void le_test_mode_recv_callback(bt_status_t status,
|
|
uint16_t packet_count) {
|
|
ALOGV("%s: status:%d packet_count:%d ", __func__, status, packet_count);
|
|
}
|
|
|
|
static void energy_info_recv_callback(bt_activity_energy_info* p_energy_info,
|
|
bt_uid_traffic_t* uid_data) {
|
|
CallbackEnv sCallbackEnv(__func__);
|
|
if (!sCallbackEnv.valid()) return;
|
|
|
|
jsize len = 0;
|
|
for (bt_uid_traffic_t* data = uid_data; data->app_uid != -1; data++) {
|
|
len++;
|
|
}
|
|
|
|
ScopedLocalRef<jobjectArray> array(
|
|
sCallbackEnv.get(), sCallbackEnv->NewObjectArray(
|
|
len, android_bluetooth_UidTraffic.clazz, NULL));
|
|
jsize i = 0;
|
|
for (bt_uid_traffic_t* data = uid_data; data->app_uid != -1; data++) {
|
|
ScopedLocalRef<jobject> uidObj(
|
|
sCallbackEnv.get(),
|
|
sCallbackEnv->NewObject(android_bluetooth_UidTraffic.clazz,
|
|
android_bluetooth_UidTraffic.constructor,
|
|
(jint)data->app_uid, (jlong)data->rx_bytes,
|
|
(jlong)data->tx_bytes));
|
|
sCallbackEnv->SetObjectArrayElement(array.get(), i++, uidObj.get());
|
|
}
|
|
|
|
sCallbackEnv->CallVoidMethod(
|
|
sJniAdapterServiceObj, method_energyInfo, p_energy_info->status,
|
|
p_energy_info->ctrl_state, p_energy_info->tx_time, p_energy_info->rx_time,
|
|
p_energy_info->idle_time, p_energy_info->energy_used, array.get());
|
|
}
|
|
|
|
static bt_callbacks_t sBluetoothCallbacks = {
|
|
sizeof(sBluetoothCallbacks), adapter_state_change_callback,
|
|
adapter_properties_callback, remote_device_properties_callback,
|
|
device_found_callback, discovery_state_changed_callback,
|
|
pin_request_callback, ssp_request_callback,
|
|
bond_state_changed_callback, acl_state_changed_callback,
|
|
callback_thread_event, dut_mode_recv_callback,
|
|
le_test_mode_recv_callback, energy_info_recv_callback,
|
|
link_quality_report_callback, generate_local_oob_data_callback};
|
|
|
|
// The callback to call when the wake alarm fires.
|
|
static alarm_cb sAlarmCallback;
|
|
|
|
// The data to pass to the wake alarm callback.
|
|
static void* sAlarmCallbackData;
|
|
|
|
class JNIThreadAttacher {
|
|
public:
|
|
JNIThreadAttacher(JavaVM* vm) : vm_(vm), env_(nullptr) {
|
|
status_ = vm_->GetEnv((void**)&env_, JNI_VERSION_1_6);
|
|
|
|
if (status_ != JNI_OK && status_ != JNI_EDETACHED) {
|
|
ALOGE(
|
|
"JNIThreadAttacher: unable to get environment for JNI CALL, "
|
|
"status: %d",
|
|
status_);
|
|
env_ = nullptr;
|
|
return;
|
|
}
|
|
|
|
if (status_ == JNI_EDETACHED) {
|
|
char name[17] = {0};
|
|
if (prctl(PR_GET_NAME, (unsigned long)name) != 0) {
|
|
ALOGE(
|
|
"JNIThreadAttacher: unable to grab previous thread name, error: %s",
|
|
strerror(errno));
|
|
env_ = nullptr;
|
|
return;
|
|
}
|
|
|
|
JavaVMAttachArgs args = {
|
|
.version = JNI_VERSION_1_6, .name = name, .group = nullptr};
|
|
if (vm_->AttachCurrentThread(&env_, &args) != 0) {
|
|
ALOGE("JNIThreadAttacher: unable to attach thread to VM");
|
|
env_ = nullptr;
|
|
return;
|
|
}
|
|
}
|
|
}
|
|
|
|
~JNIThreadAttacher() {
|
|
if (status_ == JNI_EDETACHED) vm_->DetachCurrentThread();
|
|
}
|
|
|
|
JNIEnv* getEnv() { return env_; }
|
|
|
|
private:
|
|
JavaVM* vm_;
|
|
JNIEnv* env_;
|
|
jint status_;
|
|
};
|
|
|
|
static bool set_wake_alarm_callout(uint64_t delay_millis, bool should_wake,
|
|
alarm_cb cb, void* data) {
|
|
JNIThreadAttacher attacher(vm);
|
|
JNIEnv* env = attacher.getEnv();
|
|
|
|
if (env == nullptr) {
|
|
ALOGE("%s: Unable to get JNI Env", __func__);
|
|
return false;
|
|
}
|
|
|
|
sAlarmCallback = cb;
|
|
sAlarmCallbackData = data;
|
|
|
|
jboolean jshould_wake = should_wake ? JNI_TRUE : JNI_FALSE;
|
|
jboolean ret =
|
|
env->CallBooleanMethod(sJniAdapterServiceObj, method_setWakeAlarm,
|
|
(jlong)delay_millis, jshould_wake);
|
|
if (!ret) {
|
|
sAlarmCallback = NULL;
|
|
sAlarmCallbackData = NULL;
|
|
}
|
|
|
|
return (ret == JNI_TRUE);
|
|
}
|
|
|
|
static int acquire_wake_lock_callout(const char* lock_name) {
|
|
JNIThreadAttacher attacher(vm);
|
|
JNIEnv* env = attacher.getEnv();
|
|
|
|
if (env == nullptr) {
|
|
ALOGE("%s: Unable to get JNI Env", __func__);
|
|
return BT_STATUS_JNI_THREAD_ATTACH_ERROR;
|
|
}
|
|
|
|
jint ret = BT_STATUS_SUCCESS;
|
|
{
|
|
ScopedLocalRef<jstring> lock_name_jni(env, env->NewStringUTF(lock_name));
|
|
if (lock_name_jni.get()) {
|
|
bool acquired = env->CallBooleanMethod(
|
|
sJniAdapterServiceObj, method_acquireWakeLock, lock_name_jni.get());
|
|
if (!acquired) ret = BT_STATUS_WAKELOCK_ERROR;
|
|
} else {
|
|
ALOGE("%s unable to allocate string: %s", __func__, lock_name);
|
|
ret = BT_STATUS_NOMEM;
|
|
}
|
|
}
|
|
|
|
return ret;
|
|
}
|
|
|
|
static int release_wake_lock_callout(const char* lock_name) {
|
|
JNIThreadAttacher attacher(vm);
|
|
JNIEnv* env = attacher.getEnv();
|
|
|
|
if (env == nullptr) {
|
|
ALOGE("%s: Unable to get JNI Env", __func__);
|
|
return BT_STATUS_JNI_THREAD_ATTACH_ERROR;
|
|
}
|
|
|
|
jint ret = BT_STATUS_SUCCESS;
|
|
{
|
|
ScopedLocalRef<jstring> lock_name_jni(env, env->NewStringUTF(lock_name));
|
|
if (lock_name_jni.get()) {
|
|
bool released = env->CallBooleanMethod(
|
|
sJniAdapterServiceObj, method_releaseWakeLock, lock_name_jni.get());
|
|
if (!released) ret = BT_STATUS_WAKELOCK_ERROR;
|
|
} else {
|
|
ALOGE("%s unable to allocate string: %s", __func__, lock_name);
|
|
ret = BT_STATUS_NOMEM;
|
|
}
|
|
}
|
|
|
|
return ret;
|
|
}
|
|
|
|
// Called by Java code when alarm is fired. A wake lock is held by the caller
|
|
// over the duration of this callback.
|
|
static void alarmFiredNative(JNIEnv* env, jobject obj) {
|
|
if (sAlarmCallback) {
|
|
sAlarmCallback(sAlarmCallbackData);
|
|
} else {
|
|
ALOGE("%s() - Alarm fired with callback not set!", __func__);
|
|
}
|
|
}
|
|
|
|
static bt_os_callouts_t sBluetoothOsCallouts = {
|
|
sizeof(sBluetoothOsCallouts), set_wake_alarm_callout,
|
|
acquire_wake_lock_callout, release_wake_lock_callout,
|
|
};
|
|
|
|
int hal_util_load_bt_library(const bt_interface_t** interface) {
|
|
const char* sym = BLUETOOTH_INTERFACE_STRING;
|
|
bt_interface_t* itf = nullptr;
|
|
|
|
// The library name is not set by default, so the preset library name is used.
|
|
void* handle = dlopen("libbluetooth.so", RTLD_NOW);
|
|
if (!handle) {
|
|
const char* err_str = dlerror();
|
|
ALOGE("%s: failed to load Bluetooth library, error=%s", __func__,
|
|
err_str ? err_str : "error unknown");
|
|
goto error;
|
|
}
|
|
|
|
// Get the address of the bt_interface_t.
|
|
itf = (bt_interface_t*)dlsym(handle, sym);
|
|
if (!itf) {
|
|
ALOGE("%s: failed to load symbol from Bluetooth library %s", __func__, sym);
|
|
goto error;
|
|
}
|
|
|
|
// Success.
|
|
ALOGI("%s: loaded Bluetooth library successfully", __func__);
|
|
*interface = itf;
|
|
return 0;
|
|
|
|
error:
|
|
*interface = NULL;
|
|
if (handle) dlclose(handle);
|
|
|
|
return -EINVAL;
|
|
}
|
|
|
|
static void classInitNative(JNIEnv* env, jclass clazz) {
|
|
jclass jniUidTrafficClass = env->FindClass("android/bluetooth/UidTraffic");
|
|
android_bluetooth_UidTraffic.constructor =
|
|
env->GetMethodID(jniUidTrafficClass, "<init>", "(IJJ)V");
|
|
|
|
jclass jniCallbackClass =
|
|
env->FindClass("com/android/bluetooth/btservice/JniCallbacks");
|
|
sJniCallbacksField = env->GetFieldID(
|
|
clazz, "mJniCallbacks", "Lcom/android/bluetooth/btservice/JniCallbacks;");
|
|
|
|
method_oobDataReceivedCallback =
|
|
env->GetMethodID(jniCallbackClass, "oobDataReceivedCallback",
|
|
"(ILandroid/bluetooth/OobData;)V");
|
|
|
|
method_stateChangeCallback =
|
|
env->GetMethodID(jniCallbackClass, "stateChangeCallback", "(I)V");
|
|
|
|
method_adapterPropertyChangedCallback = env->GetMethodID(
|
|
jniCallbackClass, "adapterPropertyChangedCallback", "([I[[B)V");
|
|
method_discoveryStateChangeCallback = env->GetMethodID(
|
|
jniCallbackClass, "discoveryStateChangeCallback", "(I)V");
|
|
|
|
method_devicePropertyChangedCallback = env->GetMethodID(
|
|
jniCallbackClass, "devicePropertyChangedCallback", "([B[I[[B)V");
|
|
method_deviceFoundCallback =
|
|
env->GetMethodID(jniCallbackClass, "deviceFoundCallback", "([B)V");
|
|
method_pinRequestCallback =
|
|
env->GetMethodID(jniCallbackClass, "pinRequestCallback", "([B[BIZ)V");
|
|
method_sspRequestCallback =
|
|
env->GetMethodID(jniCallbackClass, "sspRequestCallback", "([B[BIII)V");
|
|
|
|
method_bondStateChangeCallback =
|
|
env->GetMethodID(jniCallbackClass, "bondStateChangeCallback", "(I[BI)V");
|
|
|
|
method_aclStateChangeCallback =
|
|
env->GetMethodID(jniCallbackClass, "aclStateChangeCallback", "(I[BII)V");
|
|
|
|
method_linkQualityReportCallback = env->GetMethodID(
|
|
jniCallbackClass, "linkQualityReportCallback", "(JIIIIII)V");
|
|
|
|
method_setWakeAlarm = env->GetMethodID(clazz, "setWakeAlarm", "(JZ)Z");
|
|
method_acquireWakeLock =
|
|
env->GetMethodID(clazz, "acquireWakeLock", "(Ljava/lang/String;)Z");
|
|
method_releaseWakeLock =
|
|
env->GetMethodID(clazz, "releaseWakeLock", "(Ljava/lang/String;)Z");
|
|
method_energyInfo = env->GetMethodID(
|
|
clazz, "energyInfoCallback", "(IIJJJJ[Landroid/bluetooth/UidTraffic;)V");
|
|
|
|
if (env->GetJavaVM(&vm) != JNI_OK) {
|
|
ALOGE("Could not get JavaVM");
|
|
}
|
|
|
|
if (hal_util_load_bt_library((bt_interface_t const**)&sBluetoothInterface)) {
|
|
ALOGE("No Bluetooth Library found");
|
|
}
|
|
}
|
|
|
|
static bool initNative(JNIEnv* env, jobject obj, jboolean isGuest,
|
|
jboolean isCommonCriteriaMode, int configCompareResult,
|
|
jobjectArray initFlags, jboolean isAtvDevice) {
|
|
ALOGV("%s", __func__);
|
|
|
|
android_bluetooth_UidTraffic.clazz =
|
|
(jclass)env->NewGlobalRef(env->FindClass("android/bluetooth/UidTraffic"));
|
|
|
|
sJniAdapterServiceObj = env->NewGlobalRef(obj);
|
|
sJniCallbacksObj =
|
|
env->NewGlobalRef(env->GetObjectField(obj, sJniCallbacksField));
|
|
|
|
if (!sBluetoothInterface) {
|
|
return JNI_FALSE;
|
|
}
|
|
|
|
int flagCount = env->GetArrayLength(initFlags);
|
|
jstring* flagObjs = new jstring[flagCount];
|
|
const char** flags = nullptr;
|
|
if (flagCount > 0) {
|
|
flags = new const char*[flagCount + 1];
|
|
flags[flagCount] = nullptr;
|
|
}
|
|
|
|
for (int i = 0; i < flagCount; i++) {
|
|
flagObjs[i] = (jstring)env->GetObjectArrayElement(initFlags, i);
|
|
flags[i] = env->GetStringUTFChars(flagObjs[i], NULL);
|
|
}
|
|
|
|
int ret = sBluetoothInterface->init(
|
|
&sBluetoothCallbacks, isGuest == JNI_TRUE ? 1 : 0,
|
|
isCommonCriteriaMode == JNI_TRUE ? 1 : 0, configCompareResult, flags,
|
|
isAtvDevice == JNI_TRUE ? 1 : 0);
|
|
|
|
for (int i = 0; i < flagCount; i++) {
|
|
env->ReleaseStringUTFChars(flagObjs[i], flags[i]);
|
|
}
|
|
|
|
delete[] flags;
|
|
delete[] flagObjs;
|
|
|
|
if (ret != BT_STATUS_SUCCESS) {
|
|
ALOGE("Error while setting the callbacks: %d\n", ret);
|
|
sBluetoothInterface = NULL;
|
|
return JNI_FALSE;
|
|
}
|
|
ret = sBluetoothInterface->set_os_callouts(&sBluetoothOsCallouts);
|
|
if (ret != BT_STATUS_SUCCESS) {
|
|
ALOGE("Error while setting Bluetooth callouts: %d\n", ret);
|
|
sBluetoothInterface->cleanup();
|
|
sBluetoothInterface = NULL;
|
|
return JNI_FALSE;
|
|
}
|
|
|
|
sBluetoothSocketInterface =
|
|
(btsock_interface_t*)sBluetoothInterface->get_profile_interface(
|
|
BT_PROFILE_SOCKETS_ID);
|
|
if (sBluetoothSocketInterface == NULL) {
|
|
ALOGE("Error getting socket interface");
|
|
}
|
|
|
|
return JNI_TRUE;
|
|
}
|
|
|
|
static bool cleanupNative(JNIEnv* env, jobject obj) {
|
|
ALOGV("%s", __func__);
|
|
|
|
if (!sBluetoothInterface) return JNI_FALSE;
|
|
|
|
sBluetoothInterface->cleanup();
|
|
ALOGI("%s: return from cleanup", __func__);
|
|
|
|
if (sJniCallbacksObj) {
|
|
env->DeleteGlobalRef(sJniCallbacksObj);
|
|
sJniCallbacksObj = NULL;
|
|
}
|
|
|
|
if (sJniAdapterServiceObj) {
|
|
env->DeleteGlobalRef(sJniAdapterServiceObj);
|
|
sJniAdapterServiceObj = NULL;
|
|
}
|
|
|
|
if (android_bluetooth_UidTraffic.clazz) {
|
|
env->DeleteGlobalRef(android_bluetooth_UidTraffic.clazz);
|
|
android_bluetooth_UidTraffic.clazz = NULL;
|
|
}
|
|
return JNI_TRUE;
|
|
}
|
|
|
|
static jboolean enableNative(JNIEnv* env, jobject obj) {
|
|
ALOGV("%s", __func__);
|
|
|
|
if (!sBluetoothInterface) return JNI_FALSE;
|
|
int ret = sBluetoothInterface->enable();
|
|
return (ret == BT_STATUS_SUCCESS || ret == BT_STATUS_DONE) ? JNI_TRUE
|
|
: JNI_FALSE;
|
|
}
|
|
|
|
static jboolean disableNative(JNIEnv* env, jobject obj) {
|
|
ALOGV("%s", __func__);
|
|
|
|
if (!sBluetoothInterface) return JNI_FALSE;
|
|
|
|
int ret = sBluetoothInterface->disable();
|
|
/* Retrun JNI_FALSE only when BTIF explicitly reports
|
|
BT_STATUS_FAIL. It is fine for the BT_STATUS_NOT_READY
|
|
case which indicates that stack had not been enabled.
|
|
*/
|
|
return (ret == BT_STATUS_FAIL) ? JNI_FALSE : JNI_TRUE;
|
|
}
|
|
|
|
static jboolean startDiscoveryNative(JNIEnv* env, jobject obj) {
|
|
ALOGV("%s", __func__);
|
|
|
|
if (!sBluetoothInterface) return JNI_FALSE;
|
|
|
|
int ret = sBluetoothInterface->start_discovery();
|
|
return (ret == BT_STATUS_SUCCESS) ? JNI_TRUE : JNI_FALSE;
|
|
}
|
|
|
|
static jboolean cancelDiscoveryNative(JNIEnv* env, jobject obj) {
|
|
ALOGV("%s", __func__);
|
|
|
|
if (!sBluetoothInterface) return JNI_FALSE;
|
|
|
|
int ret = sBluetoothInterface->cancel_discovery();
|
|
return (ret == BT_STATUS_SUCCESS) ? JNI_TRUE : JNI_FALSE;
|
|
}
|
|
|
|
static jboolean createBondNative(JNIEnv* env, jobject obj, jbyteArray address,
|
|
jint transport) {
|
|
ALOGV("%s", __func__);
|
|
|
|
if (!sBluetoothInterface) return JNI_FALSE;
|
|
|
|
jbyte* addr = env->GetByteArrayElements(address, NULL);
|
|
if (addr == NULL) {
|
|
jniThrowIOException(env, EINVAL);
|
|
return JNI_FALSE;
|
|
}
|
|
|
|
int ret = sBluetoothInterface->create_bond((RawAddress*)addr, transport);
|
|
env->ReleaseByteArrayElements(address, addr, 0);
|
|
return (ret == BT_STATUS_SUCCESS) ? JNI_TRUE : JNI_FALSE;
|
|
}
|
|
|
|
static jbyteArray callByteArrayGetter(JNIEnv* env, jobject object,
|
|
const char* className,
|
|
const char* methodName) {
|
|
jclass myClass = env->FindClass(className);
|
|
jmethodID myMethod = env->GetMethodID(myClass, methodName, "()[B");
|
|
return (jbyteArray)env->CallObjectMethod(object, myMethod);
|
|
}
|
|
|
|
static jint callIntGetter(JNIEnv* env, jobject object, const char* className,
|
|
const char* methodName) {
|
|
jclass myClass = env->FindClass(className);
|
|
jmethodID myMethod = env->GetMethodID(myClass, methodName, "()I");
|
|
return env->CallIntMethod(object, myMethod);
|
|
}
|
|
|
|
static jboolean set_data(JNIEnv* env, bt_oob_data_t& oob_data, jobject oobData,
|
|
jint transport) {
|
|
// Need both arguments to be non NULL
|
|
if (oobData == NULL) {
|
|
ALOGE("%s: oobData is null! Nothing to do.", __func__);
|
|
return JNI_FALSE;
|
|
}
|
|
|
|
memset(&oob_data, 0, sizeof(oob_data));
|
|
|
|
jbyteArray address = callByteArrayGetter(
|
|
env, oobData, "android/bluetooth/OobData", "getDeviceAddressWithType");
|
|
|
|
// Check the data
|
|
int len = env->GetArrayLength(address);
|
|
if (len != OOB_ADDRESS_SIZE) {
|
|
ALOGE("%s: addressBytes must be 7 bytes in length (address plus type) 6+1!",
|
|
__func__);
|
|
jniThrowIOException(env, EINVAL);
|
|
return JNI_FALSE;
|
|
}
|
|
|
|
// Convert the address from byte[]
|
|
jbyte* addressBytes = env->GetByteArrayElements(address, NULL);
|
|
if (addressBytes == NULL) {
|
|
ALOGE("%s: addressBytes cannot be null!", __func__);
|
|
jniThrowIOException(env, EINVAL);
|
|
return JNI_FALSE;
|
|
}
|
|
memcpy(oob_data.address, addressBytes, len);
|
|
|
|
// Get the device name byte[] java object
|
|
jbyteArray deviceName = callByteArrayGetter(
|
|
env, oobData, "android/bluetooth/OobData", "getDeviceName");
|
|
|
|
// Optional
|
|
// Convert it to a jbyte* and copy it to the struct
|
|
jbyte* deviceNameBytes = NULL;
|
|
if (deviceName != NULL) {
|
|
deviceNameBytes = env->GetByteArrayElements(deviceName, NULL);
|
|
int len = env->GetArrayLength(deviceName);
|
|
if (len > OOB_NAME_MAX_SIZE) {
|
|
ALOGI(
|
|
"%s: wrong length of deviceName, should be empty or less than or "
|
|
"equal to %d bytes.",
|
|
__func__, OOB_NAME_MAX_SIZE);
|
|
jniThrowIOException(env, EINVAL);
|
|
env->ReleaseByteArrayElements(deviceName, deviceNameBytes, 0);
|
|
return JNI_FALSE;
|
|
}
|
|
memcpy(oob_data.device_name, deviceNameBytes, len);
|
|
env->ReleaseByteArrayElements(deviceName, deviceNameBytes, 0);
|
|
}
|
|
// Used by both classic and LE
|
|
jbyteArray confirmation = callByteArrayGetter(
|
|
env, oobData, "android/bluetooth/OobData", "getConfirmationHash");
|
|
if (confirmation == NULL) {
|
|
ALOGE("%s: confirmation cannot be null!", __func__);
|
|
jniThrowIOException(env, EINVAL);
|
|
return JNI_FALSE;
|
|
}
|
|
|
|
// Confirmation is mandatory
|
|
jbyte* confirmationBytes = NULL;
|
|
confirmationBytes = env->GetByteArrayElements(confirmation, NULL);
|
|
len = env->GetArrayLength(confirmation);
|
|
if (confirmationBytes == NULL || len != OOB_C_SIZE) {
|
|
ALOGI(
|
|
"%s: wrong length of Confirmation, should be empty or %d "
|
|
"bytes.",
|
|
__func__, OOB_C_SIZE);
|
|
jniThrowIOException(env, EINVAL);
|
|
env->ReleaseByteArrayElements(confirmation, confirmationBytes, 0);
|
|
return JNI_FALSE;
|
|
}
|
|
memcpy(oob_data.c, confirmationBytes, len);
|
|
env->ReleaseByteArrayElements(confirmation, confirmationBytes, 0);
|
|
|
|
// Random is supposedly optional according to the specification
|
|
jbyteArray randomizer = callByteArrayGetter(
|
|
env, oobData, "android/bluetooth/OobData", "getRandomizerHash");
|
|
jbyte* randomizerBytes = NULL;
|
|
if (randomizer != NULL) {
|
|
randomizerBytes = env->GetByteArrayElements(randomizer, NULL);
|
|
int len = env->GetArrayLength(randomizer);
|
|
if (randomizerBytes == NULL || len != OOB_R_SIZE) {
|
|
ALOGI("%s: wrong length of Random, should be empty or %d bytes.",
|
|
__func__, OOB_R_SIZE);
|
|
jniThrowIOException(env, EINVAL);
|
|
env->ReleaseByteArrayElements(randomizer, randomizerBytes, 0);
|
|
return JNI_FALSE;
|
|
}
|
|
memcpy(oob_data.r, randomizerBytes, len);
|
|
env->ReleaseByteArrayElements(randomizer, randomizerBytes, 0);
|
|
}
|
|
|
|
// Transport specific data fetching/setting
|
|
if (transport == TRANSPORT_BREDR) {
|
|
// Classic
|
|
// Not optional
|
|
jbyteArray oobDataLength = callByteArrayGetter(
|
|
env, oobData, "android/bluetooth/OobData", "getClassicLength");
|
|
jbyte* oobDataLengthBytes = NULL;
|
|
if (oobDataLength == NULL ||
|
|
env->GetArrayLength(oobDataLength) != OOB_DATA_LEN_SIZE) {
|
|
ALOGI("%s: wrong length of oobDataLength, should be empty or %d bytes.",
|
|
__func__, OOB_DATA_LEN_SIZE);
|
|
jniThrowIOException(env, EINVAL);
|
|
env->ReleaseByteArrayElements(oobDataLength, oobDataLengthBytes, 0);
|
|
return JNI_FALSE;
|
|
}
|
|
|
|
oobDataLengthBytes = env->GetByteArrayElements(oobDataLength, NULL);
|
|
memcpy(oob_data.oob_data_length, oobDataLengthBytes, len);
|
|
env->ReleaseByteArrayElements(oobDataLength, oobDataLengthBytes, 0);
|
|
|
|
// Optional
|
|
jbyteArray classOfDevice = callByteArrayGetter(
|
|
env, oobData, "android/bluetooth/OobData", "getClassOfDevice");
|
|
jbyte* classOfDeviceBytes = NULL;
|
|
if (classOfDevice != NULL) {
|
|
classOfDeviceBytes = env->GetByteArrayElements(classOfDevice, NULL);
|
|
int len = env->GetArrayLength(classOfDevice);
|
|
if (len != OOB_COD_SIZE) {
|
|
ALOGI("%s: wrong length of classOfDevice, should be empty or %d bytes.",
|
|
__func__, OOB_COD_SIZE);
|
|
jniThrowIOException(env, EINVAL);
|
|
env->ReleaseByteArrayElements(classOfDevice, classOfDeviceBytes, 0);
|
|
return JNI_FALSE;
|
|
}
|
|
memcpy(oob_data.class_of_device, classOfDeviceBytes, len);
|
|
env->ReleaseByteArrayElements(classOfDevice, classOfDeviceBytes, 0);
|
|
}
|
|
} else if (transport == TRANSPORT_LE) {
|
|
// LE
|
|
jbyteArray temporaryKey = callByteArrayGetter(
|
|
env, oobData, "android/bluetooth/OobData", "getLeTemporaryKey");
|
|
jbyte* temporaryKeyBytes = NULL;
|
|
if (temporaryKey != NULL) {
|
|
temporaryKeyBytes = env->GetByteArrayElements(temporaryKey, NULL);
|
|
int len = env->GetArrayLength(temporaryKey);
|
|
if (len != OOB_TK_SIZE) {
|
|
ALOGI("%s: wrong length of temporaryKey, should be empty or %d bytes.",
|
|
__func__, OOB_TK_SIZE);
|
|
jniThrowIOException(env, EINVAL);
|
|
env->ReleaseByteArrayElements(temporaryKey, temporaryKeyBytes, 0);
|
|
return JNI_FALSE;
|
|
}
|
|
memcpy(oob_data.sm_tk, temporaryKeyBytes, len);
|
|
env->ReleaseByteArrayElements(temporaryKey, temporaryKeyBytes, 0);
|
|
}
|
|
|
|
jbyteArray leAppearance = callByteArrayGetter(
|
|
env, oobData, "android/bluetooth/OobData", "getLeAppearance");
|
|
jbyte* leAppearanceBytes = NULL;
|
|
if (leAppearance != NULL) {
|
|
leAppearanceBytes = env->GetByteArrayElements(leAppearance, NULL);
|
|
int len = env->GetArrayLength(leAppearance);
|
|
if (len != OOB_LE_APPEARANCE_SIZE) {
|
|
ALOGI("%s: wrong length of leAppearance, should be empty or %d bytes.",
|
|
__func__, OOB_LE_APPEARANCE_SIZE);
|
|
jniThrowIOException(env, EINVAL);
|
|
env->ReleaseByteArrayElements(leAppearance, leAppearanceBytes, 0);
|
|
return JNI_FALSE;
|
|
}
|
|
memcpy(oob_data.le_appearance, leAppearanceBytes, len);
|
|
env->ReleaseByteArrayElements(leAppearance, leAppearanceBytes, 0);
|
|
}
|
|
|
|
jint leRole = callIntGetter(env, oobData, "android/bluetooth/OobData",
|
|
"getLeDeviceRole");
|
|
oob_data.le_device_role = leRole;
|
|
|
|
jint leFlag =
|
|
callIntGetter(env, oobData, "android/bluetooth/OobData", "getLeFlags");
|
|
oob_data.le_flags = leFlag;
|
|
}
|
|
return JNI_TRUE;
|
|
}
|
|
|
|
static void generateLocalOobDataNative(JNIEnv* env, jobject obj,
|
|
jint transport) {
|
|
// No BT interface? Can't do anything.
|
|
if (!sBluetoothInterface) return;
|
|
|
|
if (sBluetoothInterface->generate_local_oob_data(transport) !=
|
|
BT_STATUS_SUCCESS) {
|
|
ALOGE("%s: Call to generate_local_oob_data failed!", __func__);
|
|
bt_oob_data_t oob_data;
|
|
oob_data.is_valid = false;
|
|
generate_local_oob_data_callback(transport, oob_data);
|
|
}
|
|
}
|
|
|
|
static jboolean createBondOutOfBandNative(JNIEnv* env, jobject obj,
|
|
jbyteArray address, jint transport,
|
|
jobject p192Data, jobject p256Data) {
|
|
// No BT interface? Can't do anything.
|
|
if (!sBluetoothInterface) return JNI_FALSE;
|
|
|
|
// No data? Can't do anything
|
|
if (p192Data == NULL && p256Data == NULL) {
|
|
ALOGE("%s: All OOB Data are null! Nothing to do.", __func__);
|
|
jniThrowIOException(env, EINVAL);
|
|
return JNI_FALSE;
|
|
}
|
|
|
|
// This address is already reversed which is why its being passed...
|
|
// In the future we want to remove this and just reverse the address
|
|
// for the oobdata in the host stack.
|
|
if (address == NULL) {
|
|
ALOGE("%s: Address cannot be null! Nothing to do.", __func__);
|
|
jniThrowIOException(env, EINVAL);
|
|
return JNI_FALSE;
|
|
}
|
|
|
|
// Check the data
|
|
int len = env->GetArrayLength(address);
|
|
if (len != 6) {
|
|
ALOGE("%s: addressBytes must be 6 bytes in length (address plus type) 6+1!",
|
|
__func__);
|
|
jniThrowIOException(env, EINVAL);
|
|
return JNI_FALSE;
|
|
}
|
|
|
|
jbyte* addr = env->GetByteArrayElements(address, NULL);
|
|
if (addr == NULL) {
|
|
jniThrowIOException(env, EINVAL);
|
|
return JNI_FALSE;
|
|
}
|
|
|
|
// Convert P192 data from Java POJO to C Struct
|
|
bt_oob_data_t p192_data;
|
|
if (p192Data != NULL) {
|
|
if (set_data(env, p192_data, p192Data, transport) == JNI_FALSE) {
|
|
jniThrowIOException(env, EINVAL);
|
|
return JNI_FALSE;
|
|
}
|
|
}
|
|
|
|
// Convert P256 data from Java POJO to C Struct
|
|
bt_oob_data_t p256_data;
|
|
if (p256Data != NULL) {
|
|
if (set_data(env, p256_data, p256Data, transport) == JNI_FALSE) {
|
|
jniThrowIOException(env, EINVAL);
|
|
return JNI_FALSE;
|
|
}
|
|
}
|
|
|
|
return ((sBluetoothInterface->create_bond_out_of_band(
|
|
(RawAddress*)addr, transport, &p192_data, &p256_data)) ==
|
|
BT_STATUS_SUCCESS)
|
|
? JNI_TRUE
|
|
: JNI_FALSE;
|
|
}
|
|
|
|
static jboolean removeBondNative(JNIEnv* env, jobject obj, jbyteArray address) {
|
|
ALOGV("%s", __func__);
|
|
|
|
if (!sBluetoothInterface) return JNI_FALSE;
|
|
|
|
jbyte* addr = env->GetByteArrayElements(address, NULL);
|
|
if (addr == NULL) {
|
|
jniThrowIOException(env, EINVAL);
|
|
return JNI_FALSE;
|
|
}
|
|
|
|
int ret = sBluetoothInterface->remove_bond((RawAddress*)addr);
|
|
env->ReleaseByteArrayElements(address, addr, 0);
|
|
|
|
return (ret == BT_STATUS_SUCCESS) ? JNI_TRUE : JNI_FALSE;
|
|
}
|
|
|
|
static jboolean cancelBondNative(JNIEnv* env, jobject obj, jbyteArray address) {
|
|
ALOGV("%s", __func__);
|
|
|
|
if (!sBluetoothInterface) return JNI_FALSE;
|
|
|
|
jbyte* addr = env->GetByteArrayElements(address, NULL);
|
|
if (addr == NULL) {
|
|
jniThrowIOException(env, EINVAL);
|
|
return JNI_FALSE;
|
|
}
|
|
|
|
int ret = sBluetoothInterface->cancel_bond((RawAddress*)addr);
|
|
env->ReleaseByteArrayElements(address, addr, 0);
|
|
return (ret == BT_STATUS_SUCCESS) ? JNI_TRUE : JNI_FALSE;
|
|
}
|
|
|
|
static int getConnectionStateNative(JNIEnv* env, jobject obj,
|
|
jbyteArray address) {
|
|
ALOGV("%s", __func__);
|
|
if (!sBluetoothInterface) return JNI_FALSE;
|
|
|
|
jbyte* addr = env->GetByteArrayElements(address, NULL);
|
|
if (addr == NULL) {
|
|
jniThrowIOException(env, EINVAL);
|
|
return JNI_FALSE;
|
|
}
|
|
|
|
int ret = sBluetoothInterface->get_connection_state((RawAddress*)addr);
|
|
env->ReleaseByteArrayElements(address, addr, 0);
|
|
|
|
return ret;
|
|
}
|
|
|
|
static jboolean pinReplyNative(JNIEnv* env, jobject obj, jbyteArray address,
|
|
jboolean accept, jint len, jbyteArray pinArray) {
|
|
ALOGV("%s", __func__);
|
|
|
|
if (!sBluetoothInterface) return JNI_FALSE;
|
|
|
|
jbyte* addr = env->GetByteArrayElements(address, NULL);
|
|
if (addr == NULL) {
|
|
jniThrowIOException(env, EINVAL);
|
|
return JNI_FALSE;
|
|
}
|
|
|
|
jbyte* pinPtr = NULL;
|
|
if (accept) {
|
|
pinPtr = env->GetByteArrayElements(pinArray, NULL);
|
|
if (pinPtr == NULL) {
|
|
jniThrowIOException(env, EINVAL);
|
|
env->ReleaseByteArrayElements(address, addr, 0);
|
|
return JNI_FALSE;
|
|
}
|
|
}
|
|
|
|
int ret = sBluetoothInterface->pin_reply((RawAddress*)addr, accept, len,
|
|
(bt_pin_code_t*)pinPtr);
|
|
env->ReleaseByteArrayElements(address, addr, 0);
|
|
env->ReleaseByteArrayElements(pinArray, pinPtr, 0);
|
|
|
|
return (ret == BT_STATUS_SUCCESS) ? JNI_TRUE : JNI_FALSE;
|
|
}
|
|
|
|
static jboolean sspReplyNative(JNIEnv* env, jobject obj, jbyteArray address,
|
|
jint type, jboolean accept, jint passkey) {
|
|
ALOGV("%s", __func__);
|
|
|
|
if (!sBluetoothInterface) return JNI_FALSE;
|
|
|
|
jbyte* addr = env->GetByteArrayElements(address, NULL);
|
|
if (addr == NULL) {
|
|
jniThrowIOException(env, EINVAL);
|
|
return JNI_FALSE;
|
|
}
|
|
|
|
int ret = sBluetoothInterface->ssp_reply(
|
|
(RawAddress*)addr, (bt_ssp_variant_t)type, accept, passkey);
|
|
env->ReleaseByteArrayElements(address, addr, 0);
|
|
|
|
return (ret == BT_STATUS_SUCCESS) ? JNI_TRUE : JNI_FALSE;
|
|
}
|
|
|
|
static jboolean setAdapterPropertyNative(JNIEnv* env, jobject obj, jint type,
|
|
jbyteArray value) {
|
|
ALOGV("%s", __func__);
|
|
|
|
if (!sBluetoothInterface) return JNI_FALSE;
|
|
|
|
jbyte* val = env->GetByteArrayElements(value, NULL);
|
|
bt_property_t prop;
|
|
prop.type = (bt_property_type_t)type;
|
|
prop.len = env->GetArrayLength(value);
|
|
prop.val = val;
|
|
|
|
int ret = sBluetoothInterface->set_adapter_property(&prop);
|
|
env->ReleaseByteArrayElements(value, val, 0);
|
|
|
|
return (ret == BT_STATUS_SUCCESS) ? JNI_TRUE : JNI_FALSE;
|
|
}
|
|
|
|
static jboolean getAdapterPropertiesNative(JNIEnv* env, jobject obj) {
|
|
ALOGV("%s", __func__);
|
|
|
|
if (!sBluetoothInterface) return JNI_FALSE;
|
|
|
|
int ret = sBluetoothInterface->get_adapter_properties();
|
|
return (ret == BT_STATUS_SUCCESS) ? JNI_TRUE : JNI_FALSE;
|
|
}
|
|
|
|
static jboolean getAdapterPropertyNative(JNIEnv* env, jobject obj, jint type) {
|
|
ALOGV("%s", __func__);
|
|
|
|
if (!sBluetoothInterface) return JNI_FALSE;
|
|
|
|
int ret = sBluetoothInterface->get_adapter_property((bt_property_type_t)type);
|
|
return (ret == BT_STATUS_SUCCESS) ? JNI_TRUE : JNI_FALSE;
|
|
}
|
|
|
|
static jboolean getDevicePropertyNative(JNIEnv* env, jobject obj,
|
|
jbyteArray address, jint type) {
|
|
ALOGV("%s", __func__);
|
|
|
|
if (!sBluetoothInterface) return JNI_FALSE;
|
|
|
|
jbyte* addr = env->GetByteArrayElements(address, NULL);
|
|
if (addr == NULL) {
|
|
jniThrowIOException(env, EINVAL);
|
|
return JNI_FALSE;
|
|
}
|
|
|
|
int ret = sBluetoothInterface->get_remote_device_property(
|
|
(RawAddress*)addr, (bt_property_type_t)type);
|
|
env->ReleaseByteArrayElements(address, addr, 0);
|
|
return (ret == BT_STATUS_SUCCESS) ? JNI_TRUE : JNI_FALSE;
|
|
}
|
|
|
|
static jboolean setDevicePropertyNative(JNIEnv* env, jobject obj,
|
|
jbyteArray address, jint type,
|
|
jbyteArray value) {
|
|
ALOGV("%s", __func__);
|
|
|
|
if (!sBluetoothInterface) return JNI_FALSE;
|
|
|
|
jbyte* val = env->GetByteArrayElements(value, NULL);
|
|
if (val == NULL) {
|
|
jniThrowIOException(env, EINVAL);
|
|
return JNI_FALSE;
|
|
}
|
|
|
|
jbyte* addr = env->GetByteArrayElements(address, NULL);
|
|
if (addr == NULL) {
|
|
env->ReleaseByteArrayElements(value, val, 0);
|
|
jniThrowIOException(env, EINVAL);
|
|
return JNI_FALSE;
|
|
}
|
|
|
|
bt_property_t prop;
|
|
prop.type = (bt_property_type_t)type;
|
|
prop.len = env->GetArrayLength(value);
|
|
prop.val = val;
|
|
|
|
int ret =
|
|
sBluetoothInterface->set_remote_device_property((RawAddress*)addr, &prop);
|
|
env->ReleaseByteArrayElements(value, val, 0);
|
|
env->ReleaseByteArrayElements(address, addr, 0);
|
|
|
|
return (ret == BT_STATUS_SUCCESS) ? JNI_TRUE : JNI_FALSE;
|
|
}
|
|
|
|
static jboolean getRemoteServicesNative(JNIEnv* env, jobject obj,
|
|
jbyteArray address) {
|
|
ALOGV("%s", __func__);
|
|
|
|
if (!sBluetoothInterface) return JNI_FALSE;
|
|
|
|
jbyte* addr = addr = env->GetByteArrayElements(address, NULL);
|
|
if (addr == NULL) {
|
|
jniThrowIOException(env, EINVAL);
|
|
return JNI_FALSE;
|
|
}
|
|
|
|
int ret = sBluetoothInterface->get_remote_services((RawAddress*)addr);
|
|
env->ReleaseByteArrayElements(address, addr, 0);
|
|
return (ret == BT_STATUS_SUCCESS) ? JNI_TRUE : JNI_FALSE;
|
|
}
|
|
|
|
static int readEnergyInfo() {
|
|
ALOGV("%s", __func__);
|
|
|
|
if (!sBluetoothInterface) return JNI_FALSE;
|
|
int ret = sBluetoothInterface->read_energy_info();
|
|
return (ret == BT_STATUS_SUCCESS) ? JNI_TRUE : JNI_FALSE;
|
|
}
|
|
|
|
static void dumpNative(JNIEnv* env, jobject obj, jobject fdObj,
|
|
jobjectArray argArray) {
|
|
ALOGV("%s", __func__);
|
|
if (!sBluetoothInterface) return;
|
|
|
|
int fd = jniGetFDFromFileDescriptor(env, fdObj);
|
|
if (fd < 0) return;
|
|
|
|
int numArgs = env->GetArrayLength(argArray);
|
|
|
|
jstring* argObjs = new jstring[numArgs];
|
|
const char** args = nullptr;
|
|
if (numArgs > 0) {
|
|
args = new const char*[numArgs + 1];
|
|
args[numArgs] = nullptr;
|
|
}
|
|
|
|
for (int i = 0; i < numArgs; i++) {
|
|
argObjs[i] = (jstring)env->GetObjectArrayElement(argArray, i);
|
|
args[i] = env->GetStringUTFChars(argObjs[i], NULL);
|
|
}
|
|
|
|
sBluetoothInterface->dump(fd, args);
|
|
|
|
for (int i = 0; i < numArgs; i++) {
|
|
env->ReleaseStringUTFChars(argObjs[i], args[i]);
|
|
}
|
|
|
|
delete[] args;
|
|
delete[] argObjs;
|
|
}
|
|
|
|
static jbyteArray dumpMetricsNative(JNIEnv* env, jobject obj) {
|
|
ALOGI("%s", __func__);
|
|
if (!sBluetoothInterface) return env->NewByteArray(0);
|
|
|
|
std::string output;
|
|
sBluetoothInterface->dumpMetrics(&output);
|
|
jsize output_size = output.size() * sizeof(char);
|
|
jbyteArray output_bytes = env->NewByteArray(output_size);
|
|
env->SetByteArrayRegion(output_bytes, 0, output_size,
|
|
(const jbyte*)output.data());
|
|
return output_bytes;
|
|
}
|
|
|
|
static jboolean factoryResetNative(JNIEnv* env, jobject obj) {
|
|
ALOGV("%s", __func__);
|
|
if (!sBluetoothInterface) return JNI_FALSE;
|
|
int ret = sBluetoothInterface->config_clear();
|
|
return (ret == BT_STATUS_SUCCESS) ? JNI_TRUE : JNI_FALSE;
|
|
}
|
|
|
|
static void interopDatabaseClearNative(JNIEnv* env, jobject obj) {
|
|
ALOGV("%s", __func__);
|
|
if (!sBluetoothInterface) return;
|
|
sBluetoothInterface->interop_database_clear();
|
|
}
|
|
|
|
static void interopDatabaseAddNative(JNIEnv* env, jobject obj, int feature,
|
|
jbyteArray address, int length) {
|
|
ALOGV("%s", __func__);
|
|
if (!sBluetoothInterface) return;
|
|
|
|
jbyte* addr = env->GetByteArrayElements(address, NULL);
|
|
if (addr == NULL) {
|
|
jniThrowIOException(env, EINVAL);
|
|
return;
|
|
}
|
|
|
|
sBluetoothInterface->interop_database_add(feature, (RawAddress*)addr, length);
|
|
env->ReleaseByteArrayElements(address, addr, 0);
|
|
}
|
|
|
|
static jbyteArray obfuscateAddressNative(JNIEnv* env, jobject obj,
|
|
jbyteArray address) {
|
|
ALOGV("%s", __func__);
|
|
if (!sBluetoothInterface) return env->NewByteArray(0);
|
|
jbyte* addr = env->GetByteArrayElements(address, nullptr);
|
|
if (addr == nullptr) {
|
|
jniThrowIOException(env, EINVAL);
|
|
return env->NewByteArray(0);
|
|
}
|
|
RawAddress addr_obj = {};
|
|
addr_obj.FromOctets((uint8_t*)addr);
|
|
std::string output = sBluetoothInterface->obfuscate_address(addr_obj);
|
|
jsize output_size = output.size() * sizeof(char);
|
|
jbyteArray output_bytes = env->NewByteArray(output_size);
|
|
env->SetByteArrayRegion(output_bytes, 0, output_size,
|
|
(const jbyte*)output.data());
|
|
return output_bytes;
|
|
}
|
|
|
|
static jboolean setBufferLengthMillisNative(JNIEnv* env, jobject obj,
|
|
jint codec, jint size) {
|
|
ALOGV("%s", __func__);
|
|
|
|
if (!sBluetoothInterface) return JNI_FALSE;
|
|
|
|
int ret = sBluetoothInterface->set_dynamic_audio_buffer_size(codec, size);
|
|
return (ret == BT_STATUS_SUCCESS) ? JNI_TRUE : JNI_FALSE;
|
|
}
|
|
|
|
static jint connectSocketNative(JNIEnv* env, jobject obj, jbyteArray address,
|
|
jint type, jbyteArray uuid, jint port,
|
|
jint flag, jint callingUid) {
|
|
int socket_fd = INVALID_FD;
|
|
jbyte* addr = nullptr;
|
|
jbyte* uuidBytes = nullptr;
|
|
Uuid btUuid;
|
|
|
|
if (!sBluetoothSocketInterface) {
|
|
goto done;
|
|
}
|
|
addr = env->GetByteArrayElements(address, nullptr);
|
|
uuidBytes = env->GetByteArrayElements(uuid, nullptr);
|
|
if (addr == nullptr || uuidBytes == nullptr) {
|
|
jniThrowIOException(env, EINVAL);
|
|
goto done;
|
|
}
|
|
|
|
btUuid = Uuid::From128BitBE((uint8_t*)uuidBytes);
|
|
if (sBluetoothSocketInterface->connect((RawAddress*)addr, (btsock_type_t)type,
|
|
&btUuid, port, &socket_fd, flag,
|
|
callingUid) != BT_STATUS_SUCCESS) {
|
|
socket_fd = INVALID_FD;
|
|
}
|
|
|
|
done:
|
|
if (addr) env->ReleaseByteArrayElements(address, addr, 0);
|
|
if (uuidBytes) env->ReleaseByteArrayElements(uuid, uuidBytes, 0);
|
|
return socket_fd;
|
|
}
|
|
|
|
static jint createSocketChannelNative(JNIEnv* env, jobject obj, jint type,
|
|
jstring serviceName, jbyteArray uuid,
|
|
jint port, jint flag, jint callingUid) {
|
|
int socket_fd = INVALID_FD;
|
|
jbyte* uuidBytes = nullptr;
|
|
Uuid btUuid;
|
|
const char* nativeServiceName = nullptr;
|
|
|
|
if (!sBluetoothSocketInterface) {
|
|
goto done;
|
|
}
|
|
uuidBytes = env->GetByteArrayElements(uuid, nullptr);
|
|
if (serviceName != nullptr) {
|
|
nativeServiceName = env->GetStringUTFChars(serviceName, nullptr);
|
|
}
|
|
if (uuidBytes == nullptr) {
|
|
jniThrowIOException(env, EINVAL);
|
|
goto done;
|
|
}
|
|
btUuid = Uuid::From128BitBE((uint8_t*)uuidBytes);
|
|
|
|
if (sBluetoothSocketInterface->listen((btsock_type_t)type, nativeServiceName,
|
|
&btUuid, port, &socket_fd, flag,
|
|
callingUid) != BT_STATUS_SUCCESS) {
|
|
socket_fd = INVALID_FD;
|
|
}
|
|
|
|
done:
|
|
if (uuidBytes) env->ReleaseByteArrayElements(uuid, uuidBytes, 0);
|
|
if (nativeServiceName)
|
|
env->ReleaseStringUTFChars(serviceName, nativeServiceName);
|
|
return socket_fd;
|
|
}
|
|
|
|
static void requestMaximumTxDataLengthNative(JNIEnv* env, jobject obj,
|
|
jbyteArray address) {
|
|
if (!sBluetoothSocketInterface) {
|
|
return;
|
|
}
|
|
jbyte* addr = env->GetByteArrayElements(address, nullptr);
|
|
if (addr == nullptr) {
|
|
jniThrowIOException(env, EINVAL);
|
|
return;
|
|
}
|
|
|
|
RawAddress addressVar = *(RawAddress*)addr;
|
|
sBluetoothSocketInterface->request_max_tx_data_length(addressVar);
|
|
env->ReleaseByteArrayElements(address, addr, 1);
|
|
}
|
|
|
|
static int getMetricIdNative(JNIEnv* env, jobject obj, jbyteArray address) {
|
|
ALOGV("%s", __func__);
|
|
if (!sBluetoothInterface) return 0; // 0 is invalid id
|
|
jbyte* addr = env->GetByteArrayElements(address, nullptr);
|
|
if (addr == nullptr) {
|
|
jniThrowIOException(env, EINVAL);
|
|
return 0;
|
|
}
|
|
RawAddress addr_obj = {};
|
|
addr_obj.FromOctets((uint8_t*)addr);
|
|
return sBluetoothInterface->get_metric_id(addr_obj);
|
|
}
|
|
|
|
static JNINativeMethod sMethods[] = {
|
|
/* name, signature, funcPtr */
|
|
{"classInitNative", "()V", (void*)classInitNative},
|
|
{"initNative", "(ZZI[Ljava/lang/String;Z)Z", (void*)initNative},
|
|
{"cleanupNative", "()V", (void*)cleanupNative},
|
|
{"enableNative", "()Z", (void*)enableNative},
|
|
{"disableNative", "()Z", (void*)disableNative},
|
|
{"setAdapterPropertyNative", "(I[B)Z", (void*)setAdapterPropertyNative},
|
|
{"getAdapterPropertiesNative", "()Z", (void*)getAdapterPropertiesNative},
|
|
{"getAdapterPropertyNative", "(I)Z", (void*)getAdapterPropertyNative},
|
|
{"getDevicePropertyNative", "([BI)Z", (void*)getDevicePropertyNative},
|
|
{"setDevicePropertyNative", "([BI[B)Z", (void*)setDevicePropertyNative},
|
|
{"startDiscoveryNative", "()Z", (void*)startDiscoveryNative},
|
|
{"cancelDiscoveryNative", "()Z", (void*)cancelDiscoveryNative},
|
|
{"createBondNative", "([BI)Z", (void*)createBondNative},
|
|
{"createBondOutOfBandNative",
|
|
"([BILandroid/bluetooth/OobData;Landroid/bluetooth/OobData;)Z",
|
|
(void*)createBondOutOfBandNative},
|
|
{"removeBondNative", "([B)Z", (void*)removeBondNative},
|
|
{"cancelBondNative", "([B)Z", (void*)cancelBondNative},
|
|
{"generateLocalOobDataNative", "(I)V", (void*)generateLocalOobDataNative},
|
|
{"getConnectionStateNative", "([B)I", (void*)getConnectionStateNative},
|
|
{"pinReplyNative", "([BZI[B)Z", (void*)pinReplyNative},
|
|
{"sspReplyNative", "([BIZI)Z", (void*)sspReplyNative},
|
|
{"getRemoteServicesNative", "([B)Z", (void*)getRemoteServicesNative},
|
|
{"alarmFiredNative", "()V", (void*)alarmFiredNative},
|
|
{"readEnergyInfo", "()I", (void*)readEnergyInfo},
|
|
{"dumpNative", "(Ljava/io/FileDescriptor;[Ljava/lang/String;)V",
|
|
(void*)dumpNative},
|
|
{"dumpMetricsNative", "()[B", (void*)dumpMetricsNative},
|
|
{"factoryResetNative", "()Z", (void*)factoryResetNative},
|
|
{"interopDatabaseClearNative", "()V", (void*)interopDatabaseClearNative},
|
|
{"interopDatabaseAddNative", "(I[BI)V", (void*)interopDatabaseAddNative},
|
|
{"obfuscateAddressNative", "([B)[B", (void*)obfuscateAddressNative},
|
|
{"setBufferLengthMillisNative", "(II)Z",
|
|
(void*)setBufferLengthMillisNative},
|
|
{"getMetricIdNative", "([B)I", (void*)getMetricIdNative},
|
|
{"connectSocketNative", "([BI[BIII)I", (void*)connectSocketNative},
|
|
{"createSocketChannelNative", "(ILjava/lang/String;[BIII)I",
|
|
(void*)createSocketChannelNative},
|
|
{"requestMaximumTxDataLengthNative", "([B)V",
|
|
(void*)requestMaximumTxDataLengthNative}};
|
|
|
|
int register_com_android_bluetooth_btservice_AdapterService(JNIEnv* env) {
|
|
return jniRegisterNativeMethods(
|
|
env, "com/android/bluetooth/btservice/AdapterService", sMethods,
|
|
NELEM(sMethods));
|
|
}
|
|
|
|
} /* namespace android */
|
|
|
|
/*
|
|
* JNI Initialization
|
|
*/
|
|
jint JNI_OnLoad(JavaVM* jvm, void* reserved) {
|
|
JNIEnv* e;
|
|
int status;
|
|
|
|
ALOGV("Bluetooth Adapter Service : loading JNI\n");
|
|
|
|
// Check JNI version
|
|
if (jvm->GetEnv((void**)&e, JNI_VERSION_1_6)) {
|
|
ALOGE("JNI version mismatch error");
|
|
return JNI_ERR;
|
|
}
|
|
|
|
status = android::register_com_android_bluetooth_btservice_AdapterService(e);
|
|
if (status < 0) {
|
|
ALOGE("jni adapter service registration failure, status: %d", status);
|
|
return JNI_ERR;
|
|
}
|
|
|
|
status =
|
|
android::register_com_android_bluetooth_btservice_activity_attribution(e);
|
|
if (status < 0) {
|
|
ALOGE("jni activity attribution registration failure: %d", status);
|
|
return JNI_ERR;
|
|
}
|
|
|
|
status =
|
|
android::register_com_android_bluetooth_btservice_BluetoothKeystore(e);
|
|
if (status < 0) {
|
|
ALOGE("jni BluetoothKeyStore registration failure: %d", status);
|
|
return JNI_ERR;
|
|
}
|
|
|
|
status = android::register_com_android_bluetooth_hfp(e);
|
|
if (status < 0) {
|
|
ALOGE("jni hfp registration failure, status: %d", status);
|
|
return JNI_ERR;
|
|
}
|
|
|
|
status = android::register_com_android_bluetooth_hfpclient(e);
|
|
if (status < 0) {
|
|
ALOGE("jni hfp client registration failure, status: %d", status);
|
|
return JNI_ERR;
|
|
}
|
|
|
|
status = android::register_com_android_bluetooth_a2dp(e);
|
|
if (status < 0) {
|
|
ALOGE("jni a2dp source registration failure: %d", status);
|
|
return JNI_ERR;
|
|
}
|
|
|
|
status = android::register_com_android_bluetooth_a2dp_sink(e);
|
|
if (status < 0) {
|
|
ALOGE("jni a2dp sink registration failure: %d", status);
|
|
return JNI_ERR;
|
|
}
|
|
|
|
status = android::register_com_android_bluetooth_avrcp_target(e);
|
|
if (status < 0) {
|
|
ALOGE("jni new avrcp target registration failure: %d", status);
|
|
}
|
|
|
|
status = android::register_com_android_bluetooth_avrcp_controller(e);
|
|
if (status < 0) {
|
|
ALOGE("jni avrcp controller registration failure: %d", status);
|
|
return JNI_ERR;
|
|
}
|
|
|
|
status = android::register_com_android_bluetooth_hid_host(e);
|
|
if (status < 0) {
|
|
ALOGE("jni hid registration failure: %d", status);
|
|
return JNI_ERR;
|
|
}
|
|
|
|
status = android::register_com_android_bluetooth_hid_device(e);
|
|
if (status < 0) {
|
|
ALOGE("jni hidd registration failure: %d", status);
|
|
return JNI_ERR;
|
|
}
|
|
|
|
status = android::register_com_android_bluetooth_pan(e);
|
|
if (status < 0) {
|
|
ALOGE("jni pan registration failure: %d", status);
|
|
return JNI_ERR;
|
|
}
|
|
|
|
status = android::register_com_android_bluetooth_gatt(e);
|
|
if (status < 0) {
|
|
ALOGE("jni gatt registration failure: %d", status);
|
|
return JNI_ERR;
|
|
}
|
|
|
|
status = android::register_com_android_bluetooth_sdp(e);
|
|
if (status < 0) {
|
|
ALOGE("jni sdp registration failure: %d", status);
|
|
return JNI_ERR;
|
|
}
|
|
|
|
status = android::register_com_android_bluetooth_hearing_aid(e);
|
|
if (status < 0) {
|
|
ALOGE("jni hearing aid registration failure: %d", status);
|
|
return JNI_ERR;
|
|
}
|
|
|
|
status = android::register_com_android_bluetooth_le_audio(e);
|
|
if (status < 0) {
|
|
ALOGE("jni le_audio registration failure: %d", status);
|
|
return JNI_ERR;
|
|
}
|
|
|
|
return JNI_VERSION_1_6;
|
|
}
|