diff --git a/android/app/.clang-format b/android/app/.clang-format
new file mode 100644
index 0000000000000000000000000000000000000000..85098f2a60c122814316bf6b34ebf23851ba373a
--- /dev/null
+++ b/android/app/.clang-format
@@ -0,0 +1,30 @@
+#
+# Copyright (C) 2016 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.
+#
+
+#
+# Below are some minor deviations from the default Google style to
+# accommodate for handling of the large legacy code base.
+#
+
+BasedOnStyle: Google
+CommentPragmas: NOLINT:.*
+DerivePointerAlignment: false
+
+---
+Language: Java
+# Java format is handled by check_style hook
+DisableFormat: true
+SortIncludes: false
diff --git a/android/app/Android.bp b/android/app/Android.bp
new file mode 100644
index 0000000000000000000000000000000000000000..347b22b03964f2cc89621bcc162e98d5e63e2e4b
--- /dev/null
+++ b/android/app/Android.bp
@@ -0,0 +1,142 @@
+// MAP API module
+
+package {
+ default_applicable_licenses: ["packages_apps_Bluetooth_license"],
+}
+
+// Added automatically by a large-scale-change that took the approach of
+// 'apply every license found to every target'. While this makes sure we respect
+// every license restriction, it may not be entirely correct.
+//
+// e.g. GPL in an MIT project might only apply to the contrib/ directory.
+//
+// Please consider splitting the single license below into multiple licenses,
+// taking care not to lose any license_kind information, and overriding the
+// default license using the 'licenses: [...]' property on targets as needed.
+//
+// For unused files, consider creating a 'fileGroup' with "//visibility:private"
+// to attach the license to, and including a comment whether the files may be
+// used in the current project.
+// See: http://go/android-license-faq
+license {
+ name: "packages_apps_Bluetooth_license",
+ visibility: [":__subpackages__"],
+ license_kinds: [
+ "SPDX-license-identifier-Apache-2.0",
+ "SPDX-license-identifier-BSD",
+ ],
+ // large-scale-change unable to identify any license_text files
+}
+
+java_library {
+ name: "bluetooth.mapsapi",
+
+ srcs: ["lib/mapapi/**/*.java"],
+}
+
+java_library {
+ name: "mmslib",
+
+ srcs: [":framework-mms-shared-srcs"],
+ libs: ["unsupportedappusage"],
+}
+
+// Bluetooth JNI
+
+cc_library_shared {
+ name: "libbluetooth_jni",
+ srcs: ["jni/**/*.cpp"],
+ header_libs: [
+ "jni_headers",
+ "libbluetooth_headers",
+ ],
+ include_dirs: [
+ "packages/modules/Bluetooth/system/types",
+ ],
+ shared_libs: [
+ "libbase",
+ "libchrome",
+ "liblog",
+ "libnativehelper",
+ ],
+ static_libs: [
+ "libbluetooth-types",
+ ],
+ cflags: [
+ "-Wall",
+ "-Werror",
+ "-Wextra",
+ "-Wno-unused-parameter",
+ ],
+ sanitize: {
+ scs: true,
+ },
+}
+
+// Bluetooth APK
+
+android_app {
+ name: "Bluetooth",
+ defaults: ["platform_app_defaults"],
+
+ srcs: [
+ "src/**/*.java",
+ ":statslog-bluetooth-java-gen",
+ ],
+ platform_apis: true,
+ certificate: "platform",
+
+ jni_libs: ["libbluetooth_jni"],
+ libs: [
+ "javax.obex",
+ "services.net",
+ ],
+ static_libs: [
+ "com.android.vcard",
+ "bluetooth.mapsapi",
+ "sap-api-java-static",
+ "services.net",
+ "libprotobuf-java-lite",
+ "bluetooth-protos-lite",
+ "androidx.core_core",
+ "androidx.legacy_legacy-support-v4",
+ "androidx.lifecycle_lifecycle-livedata",
+ "modules-utils-statemachine",
+ "androidx.room_room-runtime",
+ "guava",
+ "android.hardware.radio-V1.0-java",
+ "mmslib",
+ "modules-utils-backgroundthread",
+ "modules-utils-bytesmatcher",
+ ],
+
+ plugins: [
+ "androidx.room_room-compiler-plugin",
+ ],
+
+ // Add in path to Bluetooth directory because local path does not exist
+ javacflags: ["-Aroom.schemaLocation=packages/apps/Bluetooth/tests/unit/src/com/android/bluetooth/btservice/storage/schemas"],
+
+ optimize: {
+ enabled: false,
+ },
+ required: ["libbluetooth"],
+ apex_available: [
+ "//apex_available:platform",
+ "com.android.bluetooth.updatable",
+ ],
+ errorprone: {
+ javacflags: [
+ // "-Xep:AndroidFrameworkRequiresPermission:ERROR",
+ ],
+ },
+}
+
+genrule {
+ name: "statslog-bluetooth-java-gen",
+ tools: ["stats-log-api-gen"],
+ cmd: "$(location stats-log-api-gen) --java $(out) --module bluetooth"
+ + " --javaPackage com.android.bluetooth --javaClass BluetoothStatsLog"
+ + " --minApiLevel 32",
+ out: ["com/android/bluetooth/BluetoothStatsLog.java"],
+}
diff --git a/android/app/AndroidManifest.xml b/android/app/AndroidManifest.xml
new file mode 100644
index 0000000000000000000000000000000000000000..c4cd0511621f4cfcb2a952aac5c864ba49fb1c2d
--- /dev/null
+++ b/android/app/AndroidManifest.xml
@@ -0,0 +1,475 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/android/app/OWNERS b/android/app/OWNERS
new file mode 100644
index 0000000000000000000000000000000000000000..4e4e11988b6239917e8a8a01aef13b84a39a5f8e
--- /dev/null
+++ b/android/app/OWNERS
@@ -0,0 +1 @@
+baligh@google.com
diff --git a/android/app/PREUPLOAD.cfg b/android/app/PREUPLOAD.cfg
new file mode 100644
index 0000000000000000000000000000000000000000..d0c92d327c3a7d82a9b06805fa46810aa8d7f108
--- /dev/null
+++ b/android/app/PREUPLOAD.cfg
@@ -0,0 +1,12 @@
+[Options]
+ignore_merged_commits = true
+
+[Builtin Hooks]
+clang_format = true
+
+[Hook Scripts]
+checkstyle_hook = ${REPO_ROOT}/prebuilts/checkstyle/checkstyle.py --sha ${PREUPLOAD_COMMIT}
+ -fw src/com/android/bluetooth/
+ lib/mapapi/com/android/bluetooth/mapapi/
+ tests/src/com/android/bluetooth/
+aosp_first = ${REPO_ROOT}/frameworks/base/tools/aosp/aosp_sha.sh ${PREUPLOAD_COMMIT} ${PREUPLOAD_FILES}
diff --git a/android/app/jni/com_android_bluetooth.h b/android/app/jni/com_android_bluetooth.h
new file mode 100644
index 0000000000000000000000000000000000000000..5c16f042f2ad46472b1152d80f87dd0736727379
--- /dev/null
+++ b/android/app/jni/com_android_bluetooth.h
@@ -0,0 +1,168 @@
+/*
+ * Copyright (c) 2014 The Android Open Source Project
+ * 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.
+ */
+
+#ifndef COM_ANDROID_BLUETOOTH_H
+#define COM_ANDROID_BLUETOOTH_H
+
+#include "hardware/bluetooth.h"
+#include "hardware/hardware.h"
+#include "jni.h"
+#include "jni_logging.h"
+#include "nativehelper/ScopedLocalRef.h"
+#include "utils/Log.h"
+
+namespace android {
+
+JNIEnv* getCallbackEnv();
+bool isCallbackThread();
+
+class CallbackEnv {
+public:
+ CallbackEnv(const char *methodName) : mName(methodName) {
+ mCallbackEnv = getCallbackEnv();
+ }
+
+ ~CallbackEnv() {
+ if (mCallbackEnv && mCallbackEnv->ExceptionCheck()) {
+ ALOGE("An exception was thrown by callback '%s'.", mName);
+ LOGE_EX(mCallbackEnv);
+ mCallbackEnv->ExceptionClear();
+ }
+ }
+
+ bool valid() const {
+ if (!mCallbackEnv || !isCallbackThread()) {
+ ALOGE("%s: Callback env fail", mName);
+ return false;
+ }
+ return true;
+ }
+
+ // stolen from art/runtime/jni/check_jni.cc
+ bool isValidUtf(const char* bytes) const {
+ while (*bytes != '\0') {
+ const uint8_t* utf8 = reinterpret_cast(bytes++);
+ // Switch on the high four bits.
+ switch (*utf8 >> 4) {
+ case 0x00:
+ case 0x01:
+ case 0x02:
+ case 0x03:
+ case 0x04:
+ case 0x05:
+ case 0x06:
+ case 0x07:
+ // Bit pattern 0xxx. No need for any extra bytes.
+ break;
+ case 0x08:
+ case 0x09:
+ case 0x0a:
+ case 0x0b:
+ // Bit patterns 10xx, which are illegal start bytes.
+ return false;
+ case 0x0f:
+ // Bit pattern 1111, which might be the start of a 4 byte sequence.
+ if ((*utf8 & 0x08) == 0) {
+ // Bit pattern 1111 0xxx, which is the start of a 4 byte sequence.
+ // We consume one continuation byte here, and fall through to
+ // consume two more.
+ utf8 = reinterpret_cast(bytes++);
+ if ((*utf8 & 0xc0) != 0x80) {
+ return false;
+ }
+ } else {
+ return false;
+ }
+ // Fall through to the cases below to consume two more continuation
+ // bytes.
+ FALLTHROUGH_INTENDED;
+ case 0x0e:
+ // Bit pattern 1110, so there are two additional bytes.
+ utf8 = reinterpret_cast(bytes++);
+ if ((*utf8 & 0xc0) != 0x80) {
+ return false;
+ }
+ // Fall through to consume one more continuation byte.
+ FALLTHROUGH_INTENDED;
+ case 0x0c:
+ case 0x0d:
+ // Bit pattern 110x, so there is one additional byte.
+ utf8 = reinterpret_cast(bytes++);
+ if ((*utf8 & 0xc0) != 0x80) {
+ return false;
+ }
+ break;
+ }
+ }
+ return true;
+ }
+
+ JNIEnv *operator-> () const {
+ return mCallbackEnv;
+ }
+
+ JNIEnv *get() const {
+ return mCallbackEnv;
+ }
+
+private:
+ JNIEnv *mCallbackEnv;
+ const char *mName;
+
+ DISALLOW_COPY_AND_ASSIGN(CallbackEnv);
+};
+
+const bt_interface_t* getBluetoothInterface();
+
+int register_com_android_bluetooth_hfp(JNIEnv* env);
+
+int register_com_android_bluetooth_hfpclient(JNIEnv* env);
+
+int register_com_android_bluetooth_a2dp(JNIEnv* env);
+
+int register_com_android_bluetooth_a2dp_sink(JNIEnv* env);
+
+int register_com_android_bluetooth_avrcp(JNIEnv* env);
+
+int register_com_android_bluetooth_avrcp_target(JNIEnv* env);
+
+int register_com_android_bluetooth_avrcp_controller(JNIEnv* env);
+
+int register_com_android_bluetooth_hid_host(JNIEnv* env);
+
+int register_com_android_bluetooth_hid_device(JNIEnv* env);
+
+int register_com_android_bluetooth_pan(JNIEnv* env);
+
+int register_com_android_bluetooth_gatt(JNIEnv* env);
+
+int register_com_android_bluetooth_sdp(JNIEnv* env);
+
+int register_com_android_bluetooth_hearing_aid(JNIEnv* env);
+
+int register_com_android_bluetooth_btservice_BluetoothKeystore(JNIEnv* env);
+
+int register_com_android_bluetooth_btservice_activity_attribution(JNIEnv* env);
+
+int register_com_android_bluetooth_le_audio(JNIEnv* env);
+
+int register_com_android_bluetooth_vc(JNIEnv* env);
+
+int register_com_android_bluetooth_csip_set_coordinator(JNIEnv* env);
+} // namespace android
+
+#endif /* COM_ANDROID_BLUETOOTH_H */
diff --git a/android/app/jni/com_android_bluetooth_a2dp.cpp b/android/app/jni/com_android_bluetooth_a2dp.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..abc8830b518198eb0132c6e4a12c1fdbe7b5983b
--- /dev/null
+++ b/android/app/jni/com_android_bluetooth_a2dp.cpp
@@ -0,0 +1,525 @@
+/*
+ * 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 "BluetoothA2dpServiceJni"
+
+#define LOG_NDEBUG 0
+
+#include "com_android_bluetooth.h"
+#include "hardware/bt_av.h"
+#include "utils/Log.h"
+
+#include
+#include
+
+namespace android {
+static jmethodID method_onConnectionStateChanged;
+static jmethodID method_onAudioStateChanged;
+static jmethodID method_onCodecConfigChanged;
+static jmethodID method_isMandatoryCodecPreferred;
+
+static struct {
+ jclass clazz;
+ jmethodID constructor;
+ jmethodID getCodecType;
+ jmethodID getCodecPriority;
+ jmethodID getSampleRate;
+ jmethodID getBitsPerSample;
+ jmethodID getChannelMode;
+ jmethodID getCodecSpecific1;
+ jmethodID getCodecSpecific2;
+ jmethodID getCodecSpecific3;
+ jmethodID getCodecSpecific4;
+} android_bluetooth_BluetoothCodecConfig;
+
+static const btav_source_interface_t* sBluetoothA2dpInterface = nullptr;
+static std::shared_timed_mutex interface_mutex;
+
+static jobject mCallbacksObj = nullptr;
+static std::shared_timed_mutex callbacks_mutex;
+
+static void bta2dp_connection_state_callback(const RawAddress& bd_addr,
+ btav_connection_state_t state) {
+ ALOGI("%s", __func__);
+
+ std::shared_lock lock(callbacks_mutex);
+ CallbackEnv sCallbackEnv(__func__);
+ if (!sCallbackEnv.valid() || mCallbacksObj == nullptr) return;
+
+ ScopedLocalRef addr(
+ sCallbackEnv.get(), sCallbackEnv->NewByteArray(sizeof(RawAddress)));
+ if (!addr.get()) {
+ ALOGE("%s: Fail to new jbyteArray bd addr", __func__);
+ return;
+ }
+
+ sCallbackEnv->SetByteArrayRegion(
+ addr.get(), 0, sizeof(RawAddress),
+ reinterpret_cast(bd_addr.address));
+ sCallbackEnv->CallVoidMethod(mCallbacksObj, method_onConnectionStateChanged,
+ addr.get(), (jint)state);
+}
+
+static void bta2dp_audio_state_callback(const RawAddress& bd_addr,
+ btav_audio_state_t state) {
+ ALOGI("%s", __func__);
+
+ std::shared_lock lock(callbacks_mutex);
+ CallbackEnv sCallbackEnv(__func__);
+ if (!sCallbackEnv.valid() || mCallbacksObj == nullptr) return;
+
+ ScopedLocalRef addr(
+ sCallbackEnv.get(), sCallbackEnv->NewByteArray(sizeof(RawAddress)));
+ if (!addr.get()) {
+ ALOGE("%s: Fail to new jbyteArray bd addr", __func__);
+ return;
+ }
+
+ sCallbackEnv->SetByteArrayRegion(
+ addr.get(), 0, sizeof(RawAddress),
+ reinterpret_cast(bd_addr.address));
+ sCallbackEnv->CallVoidMethod(mCallbacksObj, method_onAudioStateChanged,
+ addr.get(), (jint)state);
+}
+
+static void bta2dp_audio_config_callback(
+ const RawAddress& bd_addr, btav_a2dp_codec_config_t codec_config,
+ std::vector codecs_local_capabilities,
+ std::vector codecs_selectable_capabilities) {
+ ALOGI("%s", __func__);
+
+ std::shared_lock lock(callbacks_mutex);
+ CallbackEnv sCallbackEnv(__func__);
+ if (!sCallbackEnv.valid() || mCallbacksObj == nullptr) return;
+
+ jobject codecConfigObj = sCallbackEnv->NewObject(
+ android_bluetooth_BluetoothCodecConfig.clazz,
+ android_bluetooth_BluetoothCodecConfig.constructor,
+ (jint)codec_config.codec_type, (jint)codec_config.codec_priority,
+ (jint)codec_config.sample_rate, (jint)codec_config.bits_per_sample,
+ (jint)codec_config.channel_mode, (jlong)codec_config.codec_specific_1,
+ (jlong)codec_config.codec_specific_2,
+ (jlong)codec_config.codec_specific_3,
+ (jlong)codec_config.codec_specific_4);
+
+ jsize i = 0;
+ jobjectArray local_capabilities_array = sCallbackEnv->NewObjectArray(
+ (jsize)codecs_local_capabilities.size(),
+ android_bluetooth_BluetoothCodecConfig.clazz, nullptr);
+ for (auto const& cap : codecs_local_capabilities) {
+ jobject capObj = sCallbackEnv->NewObject(
+ android_bluetooth_BluetoothCodecConfig.clazz,
+ android_bluetooth_BluetoothCodecConfig.constructor,
+ (jint)cap.codec_type, (jint)cap.codec_priority, (jint)cap.sample_rate,
+ (jint)cap.bits_per_sample, (jint)cap.channel_mode,
+ (jlong)cap.codec_specific_1, (jlong)cap.codec_specific_2,
+ (jlong)cap.codec_specific_3, (jlong)cap.codec_specific_4);
+ sCallbackEnv->SetObjectArrayElement(local_capabilities_array, i++, capObj);
+ sCallbackEnv->DeleteLocalRef(capObj);
+ }
+
+ i = 0;
+ jobjectArray selectable_capabilities_array = sCallbackEnv->NewObjectArray(
+ (jsize)codecs_selectable_capabilities.size(),
+ android_bluetooth_BluetoothCodecConfig.clazz, nullptr);
+ for (auto const& cap : codecs_selectable_capabilities) {
+ jobject capObj = sCallbackEnv->NewObject(
+ android_bluetooth_BluetoothCodecConfig.clazz,
+ android_bluetooth_BluetoothCodecConfig.constructor,
+ (jint)cap.codec_type, (jint)cap.codec_priority, (jint)cap.sample_rate,
+ (jint)cap.bits_per_sample, (jint)cap.channel_mode,
+ (jlong)cap.codec_specific_1, (jlong)cap.codec_specific_2,
+ (jlong)cap.codec_specific_3, (jlong)cap.codec_specific_4);
+ sCallbackEnv->SetObjectArrayElement(selectable_capabilities_array, i++,
+ capObj);
+ sCallbackEnv->DeleteLocalRef(capObj);
+ }
+
+ ScopedLocalRef addr(
+ sCallbackEnv.get(), sCallbackEnv->NewByteArray(RawAddress::kLength));
+ if (!addr.get()) {
+ ALOGE("%s: Fail to new jbyteArray bd addr", __func__);
+ return;
+ }
+ sCallbackEnv->SetByteArrayRegion(
+ addr.get(), 0, RawAddress::kLength,
+ reinterpret_cast(bd_addr.address));
+
+ sCallbackEnv->CallVoidMethod(
+ mCallbacksObj, method_onCodecConfigChanged, addr.get(), codecConfigObj,
+ local_capabilities_array, selectable_capabilities_array);
+}
+
+static bool bta2dp_mandatory_codec_preferred_callback(
+ const RawAddress& bd_addr) {
+ ALOGI("%s", __func__);
+
+ std::shared_lock lock(callbacks_mutex);
+ CallbackEnv sCallbackEnv(__func__);
+ if (!sCallbackEnv.valid() || mCallbacksObj == nullptr) return false;
+
+ ScopedLocalRef addr(
+ sCallbackEnv.get(), sCallbackEnv->NewByteArray(RawAddress::kLength));
+ if (!addr.get()) {
+ ALOGE("%s: Fail to new jbyteArray bd addr", __func__);
+ return false;
+ }
+ sCallbackEnv->SetByteArrayRegion(
+ addr.get(), 0, RawAddress::kLength,
+ reinterpret_cast(bd_addr.address));
+ return sCallbackEnv->CallBooleanMethod(
+ mCallbacksObj, method_isMandatoryCodecPreferred, addr.get());
+}
+
+static btav_source_callbacks_t sBluetoothA2dpCallbacks = {
+ sizeof(sBluetoothA2dpCallbacks),
+ bta2dp_connection_state_callback,
+ bta2dp_audio_state_callback,
+ bta2dp_audio_config_callback,
+ bta2dp_mandatory_codec_preferred_callback,
+};
+
+static void classInitNative(JNIEnv* env, jclass clazz) {
+ jclass jniBluetoothCodecConfigClass =
+ env->FindClass("android/bluetooth/BluetoothCodecConfig");
+ android_bluetooth_BluetoothCodecConfig.constructor =
+ env->GetMethodID(jniBluetoothCodecConfigClass, "", "(IIIIIJJJJ)V");
+ android_bluetooth_BluetoothCodecConfig.getCodecType =
+ env->GetMethodID(jniBluetoothCodecConfigClass, "getCodecType", "()I");
+ android_bluetooth_BluetoothCodecConfig.getCodecPriority =
+ env->GetMethodID(jniBluetoothCodecConfigClass, "getCodecPriority", "()I");
+ android_bluetooth_BluetoothCodecConfig.getSampleRate =
+ env->GetMethodID(jniBluetoothCodecConfigClass, "getSampleRate", "()I");
+ android_bluetooth_BluetoothCodecConfig.getBitsPerSample =
+ env->GetMethodID(jniBluetoothCodecConfigClass, "getBitsPerSample", "()I");
+ android_bluetooth_BluetoothCodecConfig.getChannelMode =
+ env->GetMethodID(jniBluetoothCodecConfigClass, "getChannelMode", "()I");
+ android_bluetooth_BluetoothCodecConfig.getCodecSpecific1 = env->GetMethodID(
+ jniBluetoothCodecConfigClass, "getCodecSpecific1", "()J");
+ android_bluetooth_BluetoothCodecConfig.getCodecSpecific2 = env->GetMethodID(
+ jniBluetoothCodecConfigClass, "getCodecSpecific2", "()J");
+ android_bluetooth_BluetoothCodecConfig.getCodecSpecific3 = env->GetMethodID(
+ jniBluetoothCodecConfigClass, "getCodecSpecific3", "()J");
+ android_bluetooth_BluetoothCodecConfig.getCodecSpecific4 = env->GetMethodID(
+ jniBluetoothCodecConfigClass, "getCodecSpecific4", "()J");
+
+ method_onConnectionStateChanged =
+ env->GetMethodID(clazz, "onConnectionStateChanged", "([BI)V");
+
+ method_onAudioStateChanged =
+ env->GetMethodID(clazz, "onAudioStateChanged", "([BI)V");
+
+ method_onCodecConfigChanged =
+ env->GetMethodID(clazz, "onCodecConfigChanged",
+ "([BLandroid/bluetooth/BluetoothCodecConfig;"
+ "[Landroid/bluetooth/BluetoothCodecConfig;"
+ "[Landroid/bluetooth/BluetoothCodecConfig;)V");
+
+ method_isMandatoryCodecPreferred =
+ env->GetMethodID(clazz, "isMandatoryCodecPreferred", "([B)Z");
+
+ ALOGI("%s: succeeds", __func__);
+}
+
+static std::vector prepareCodecPreferences(
+ JNIEnv* env, jobject object, jobjectArray codecConfigArray) {
+ std::vector codec_preferences;
+
+ int numConfigs = env->GetArrayLength(codecConfigArray);
+ for (int i = 0; i < numConfigs; i++) {
+ jobject jcodecConfig = env->GetObjectArrayElement(codecConfigArray, i);
+ if (jcodecConfig == nullptr) continue;
+ if (!env->IsInstanceOf(jcodecConfig,
+ android_bluetooth_BluetoothCodecConfig.clazz)) {
+ ALOGE("%s: Invalid BluetoothCodecConfig instance", __func__);
+ continue;
+ }
+ jint codecType = env->CallIntMethod(
+ jcodecConfig, android_bluetooth_BluetoothCodecConfig.getCodecType);
+ jint codecPriority = env->CallIntMethod(
+ jcodecConfig, android_bluetooth_BluetoothCodecConfig.getCodecPriority);
+ jint sampleRate = env->CallIntMethod(
+ jcodecConfig, android_bluetooth_BluetoothCodecConfig.getSampleRate);
+ jint bitsPerSample = env->CallIntMethod(
+ jcodecConfig, android_bluetooth_BluetoothCodecConfig.getBitsPerSample);
+ jint channelMode = env->CallIntMethod(
+ jcodecConfig, android_bluetooth_BluetoothCodecConfig.getChannelMode);
+ jlong codecSpecific1 = env->CallLongMethod(
+ jcodecConfig, android_bluetooth_BluetoothCodecConfig.getCodecSpecific1);
+ jlong codecSpecific2 = env->CallLongMethod(
+ jcodecConfig, android_bluetooth_BluetoothCodecConfig.getCodecSpecific2);
+ jlong codecSpecific3 = env->CallLongMethod(
+ jcodecConfig, android_bluetooth_BluetoothCodecConfig.getCodecSpecific3);
+ jlong codecSpecific4 = env->CallLongMethod(
+ jcodecConfig, android_bluetooth_BluetoothCodecConfig.getCodecSpecific4);
+
+ btav_a2dp_codec_config_t codec_config = {
+ .codec_type = static_cast(codecType),
+ .codec_priority =
+ static_cast(codecPriority),
+ .sample_rate = static_cast(sampleRate),
+ .bits_per_sample =
+ static_cast(bitsPerSample),
+ .channel_mode =
+ static_cast(channelMode),
+ .codec_specific_1 = codecSpecific1,
+ .codec_specific_2 = codecSpecific2,
+ .codec_specific_3 = codecSpecific3,
+ .codec_specific_4 = codecSpecific4};
+
+ codec_preferences.push_back(codec_config);
+ }
+ return codec_preferences;
+}
+
+static void initNative(JNIEnv* env, jobject object,
+ jint maxConnectedAudioDevices,
+ jobjectArray codecConfigArray,
+ jobjectArray codecOffloadingArray) {
+ std::unique_lock interface_lock(interface_mutex);
+ std::unique_lock callbacks_lock(callbacks_mutex);
+
+ const bt_interface_t* btInf = getBluetoothInterface();
+ if (btInf == nullptr) {
+ ALOGE("%s: Bluetooth module is not loaded", __func__);
+ return;
+ }
+
+ if (sBluetoothA2dpInterface != nullptr) {
+ ALOGW("%s: Cleaning up A2DP Interface before initializing...", __func__);
+ sBluetoothA2dpInterface->cleanup();
+ sBluetoothA2dpInterface = nullptr;
+ }
+
+ if (mCallbacksObj != nullptr) {
+ ALOGW("%s: Cleaning up A2DP callback object", __func__);
+ env->DeleteGlobalRef(mCallbacksObj);
+ mCallbacksObj = nullptr;
+ }
+
+ if ((mCallbacksObj = env->NewGlobalRef(object)) == nullptr) {
+ ALOGE("%s: Failed to allocate Global Ref for A2DP Callbacks", __func__);
+ return;
+ }
+
+ android_bluetooth_BluetoothCodecConfig.clazz = (jclass)env->NewGlobalRef(
+ env->FindClass("android/bluetooth/BluetoothCodecConfig"));
+ if (android_bluetooth_BluetoothCodecConfig.clazz == nullptr) {
+ ALOGE("%s: Failed to allocate Global Ref for BluetoothCodecConfig class",
+ __func__);
+ return;
+ }
+
+ sBluetoothA2dpInterface =
+ (btav_source_interface_t*)btInf->get_profile_interface(
+ BT_PROFILE_ADVANCED_AUDIO_ID);
+ if (sBluetoothA2dpInterface == nullptr) {
+ ALOGE("%s: Failed to get Bluetooth A2DP Interface", __func__);
+ return;
+ }
+
+ std::vector codec_priorities =
+ prepareCodecPreferences(env, object, codecConfigArray);
+
+ std::vector codec_offloading =
+ prepareCodecPreferences(env, object, codecOffloadingArray);
+
+ bt_status_t status = sBluetoothA2dpInterface->init(
+ &sBluetoothA2dpCallbacks, maxConnectedAudioDevices, codec_priorities,
+ codec_offloading);
+ if (status != BT_STATUS_SUCCESS) {
+ ALOGE("%s: Failed to initialize Bluetooth A2DP, status: %d", __func__,
+ status);
+ sBluetoothA2dpInterface = nullptr;
+ return;
+ }
+}
+
+static void cleanupNative(JNIEnv* env, jobject object) {
+ std::unique_lock interface_lock(interface_mutex);
+ std::unique_lock callbacks_lock(callbacks_mutex);
+
+ const bt_interface_t* btInf = getBluetoothInterface();
+ if (btInf == nullptr) {
+ ALOGE("%s: Bluetooth module is not loaded", __func__);
+ return;
+ }
+
+ if (sBluetoothA2dpInterface != nullptr) {
+ sBluetoothA2dpInterface->cleanup();
+ sBluetoothA2dpInterface = nullptr;
+ }
+
+ env->DeleteGlobalRef(android_bluetooth_BluetoothCodecConfig.clazz);
+ android_bluetooth_BluetoothCodecConfig.clazz = nullptr;
+
+ if (mCallbacksObj != nullptr) {
+ env->DeleteGlobalRef(mCallbacksObj);
+ mCallbacksObj = nullptr;
+ }
+}
+
+static jboolean connectA2dpNative(JNIEnv* env, jobject object,
+ jbyteArray address) {
+ ALOGI("%s: sBluetoothA2dpInterface: %p", __func__, sBluetoothA2dpInterface);
+ std::shared_lock lock(interface_mutex);
+ if (!sBluetoothA2dpInterface) {
+ ALOGE("%s: Failed to get the Bluetooth A2DP Interface", __func__);
+ return JNI_FALSE;
+ }
+
+ jbyte* addr = env->GetByteArrayElements(address, nullptr);
+ if (!addr) {
+ jniThrowIOException(env, EINVAL);
+ return JNI_FALSE;
+ }
+
+ RawAddress bd_addr;
+ bd_addr.FromOctets(reinterpret_cast(addr));
+ bt_status_t status = sBluetoothA2dpInterface->connect(bd_addr);
+ if (status != BT_STATUS_SUCCESS) {
+ ALOGE("%s: Failed A2DP connection, status: %d", __func__, status);
+ }
+ env->ReleaseByteArrayElements(address, addr, 0);
+ return (status == BT_STATUS_SUCCESS) ? JNI_TRUE : JNI_FALSE;
+}
+
+static jboolean disconnectA2dpNative(JNIEnv* env, jobject object,
+ jbyteArray address) {
+ ALOGI("%s: sBluetoothA2dpInterface: %p", __func__, sBluetoothA2dpInterface);
+ std::shared_lock lock(interface_mutex);
+ if (!sBluetoothA2dpInterface) {
+ ALOGE("%s: Failed to get the Bluetooth A2DP Interface", __func__);
+ return JNI_FALSE;
+ }
+
+ jbyte* addr = env->GetByteArrayElements(address, nullptr);
+ if (!addr) {
+ jniThrowIOException(env, EINVAL);
+ return JNI_FALSE;
+ }
+
+ RawAddress bd_addr;
+ bd_addr.FromOctets(reinterpret_cast(addr));
+ bt_status_t status = sBluetoothA2dpInterface->disconnect(bd_addr);
+ if (status != BT_STATUS_SUCCESS) {
+ ALOGE("%s: Failed A2DP disconnection, status: %d", __func__, status);
+ }
+ env->ReleaseByteArrayElements(address, addr, 0);
+ return (status == BT_STATUS_SUCCESS) ? JNI_TRUE : JNI_FALSE;
+}
+
+static jboolean setSilenceDeviceNative(JNIEnv* env, jobject object,
+ jbyteArray address, jboolean silence) {
+ ALOGI("%s: sBluetoothA2dpInterface: %p", __func__, sBluetoothA2dpInterface);
+ std::shared_lock lock(interface_mutex);
+ if (!sBluetoothA2dpInterface) {
+ ALOGE("%s: Failed to get the Bluetooth A2DP Interface", __func__);
+ return JNI_FALSE;
+ }
+
+ jbyte* addr = env->GetByteArrayElements(address, nullptr);
+
+ RawAddress bd_addr = RawAddress::kEmpty;
+ if (addr) {
+ bd_addr.FromOctets(reinterpret_cast(addr));
+ }
+ if (bd_addr == RawAddress::kEmpty) {
+ return JNI_FALSE;
+ }
+ bt_status_t status =
+ sBluetoothA2dpInterface->set_silence_device(bd_addr, silence);
+ if (status != BT_STATUS_SUCCESS) {
+ ALOGE("%s: Failed A2DP set_silence_device, status: %d", __func__, status);
+ }
+ env->ReleaseByteArrayElements(address, addr, 0);
+ return (status == BT_STATUS_SUCCESS) ? JNI_TRUE : JNI_FALSE;
+}
+
+static jboolean setActiveDeviceNative(JNIEnv* env, jobject object,
+ jbyteArray address) {
+ ALOGI("%s: sBluetoothA2dpInterface: %p", __func__, sBluetoothA2dpInterface);
+ std::shared_lock lock(interface_mutex);
+ if (!sBluetoothA2dpInterface) {
+ ALOGE("%s: Failed to get the Bluetooth A2DP Interface", __func__);
+ return JNI_FALSE;
+ }
+
+ jbyte* addr = env->GetByteArrayElements(address, nullptr);
+
+ RawAddress bd_addr = RawAddress::kEmpty;
+ if (addr) {
+ bd_addr.FromOctets(reinterpret_cast(addr));
+ }
+ bt_status_t status = sBluetoothA2dpInterface->set_active_device(bd_addr);
+ if (status != BT_STATUS_SUCCESS) {
+ ALOGE("%s: Failed A2DP set_active_device, status: %d", __func__, status);
+ }
+ env->ReleaseByteArrayElements(address, addr, 0);
+ return (status == BT_STATUS_SUCCESS) ? JNI_TRUE : JNI_FALSE;
+}
+
+static jboolean setCodecConfigPreferenceNative(JNIEnv* env, jobject object,
+ jbyteArray address,
+ jobjectArray codecConfigArray) {
+ ALOGI("%s: sBluetoothA2dpInterface: %p", __func__, sBluetoothA2dpInterface);
+ std::shared_lock lock(interface_mutex);
+ if (!sBluetoothA2dpInterface) {
+ ALOGE("%s: Failed to get the Bluetooth A2DP Interface", __func__);
+ return JNI_FALSE;
+ }
+
+ jbyte* addr = env->GetByteArrayElements(address, nullptr);
+ if (!addr) {
+ jniThrowIOException(env, EINVAL);
+ return JNI_FALSE;
+ }
+
+ RawAddress bd_addr;
+ bd_addr.FromOctets(reinterpret_cast(addr));
+ std::vector codec_preferences =
+ prepareCodecPreferences(env, object, codecConfigArray);
+
+ bt_status_t status =
+ sBluetoothA2dpInterface->config_codec(bd_addr, codec_preferences);
+ if (status != BT_STATUS_SUCCESS) {
+ ALOGE("%s: Failed codec configuration, status: %d", __func__, status);
+ }
+ env->ReleaseByteArrayElements(address, addr, 0);
+ return (status == BT_STATUS_SUCCESS) ? JNI_TRUE : JNI_FALSE;
+}
+
+static JNINativeMethod sMethods[] = {
+ {"classInitNative", "()V", (void*)classInitNative},
+ {"initNative",
+ "(I[Landroid/bluetooth/BluetoothCodecConfig;[Landroid/bluetooth/BluetoothCodecConfig;)V",
+ (void*)initNative},
+ {"cleanupNative", "()V", (void*)cleanupNative},
+ {"connectA2dpNative", "([B)Z", (void*)connectA2dpNative},
+ {"disconnectA2dpNative", "([B)Z", (void*)disconnectA2dpNative},
+ {"setSilenceDeviceNative", "([BZ)Z", (void*)setSilenceDeviceNative},
+ {"setActiveDeviceNative", "([B)Z", (void*)setActiveDeviceNative},
+ {"setCodecConfigPreferenceNative",
+ "([B[Landroid/bluetooth/BluetoothCodecConfig;)Z",
+ (void*)setCodecConfigPreferenceNative},
+};
+
+int register_com_android_bluetooth_a2dp(JNIEnv* env) {
+ return jniRegisterNativeMethods(
+ env, "com/android/bluetooth/a2dp/A2dpNativeInterface", sMethods,
+ NELEM(sMethods));
+}
+}
diff --git a/android/app/jni/com_android_bluetooth_a2dp_sink.cpp b/android/app/jni/com_android_bluetooth_a2dp_sink.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..a74b18d6fbab4ba81fbe57ca148002f9856b66ee
--- /dev/null
+++ b/android/app/jni/com_android_bluetooth_a2dp_sink.cpp
@@ -0,0 +1,262 @@
+/*
+ * 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 "BluetoothA2dpSinkServiceJni"
+
+#define LOG_NDEBUG 0
+
+#include "com_android_bluetooth.h"
+#include "hardware/bt_av.h"
+#include "utils/Log.h"
+
+#include
+
+namespace android {
+static jmethodID method_onConnectionStateChanged;
+static jmethodID method_onAudioStateChanged;
+static jmethodID method_onAudioConfigChanged;
+
+static const btav_sink_interface_t* sBluetoothA2dpInterface = NULL;
+static jobject mCallbacksObj = NULL;
+
+static void bta2dp_connection_state_callback(const RawAddress& bd_addr,
+ btav_connection_state_t state) {
+ ALOGI("%s", __func__);
+ CallbackEnv sCallbackEnv(__func__);
+ if (!sCallbackEnv.valid()) return;
+
+ ScopedLocalRef addr(
+ sCallbackEnv.get(), sCallbackEnv->NewByteArray(sizeof(RawAddress)));
+ if (!addr.get()) {
+ ALOGE("Fail to new jbyteArray bd addr for connection state");
+ return;
+ }
+
+ sCallbackEnv->SetByteArrayRegion(addr.get(), 0, sizeof(RawAddress),
+ (const jbyte*)bd_addr.address);
+ sCallbackEnv->CallVoidMethod(mCallbacksObj, method_onConnectionStateChanged,
+ addr.get(), (jint)state);
+}
+
+static void bta2dp_audio_state_callback(const RawAddress& bd_addr,
+ btav_audio_state_t state) {
+ ALOGI("%s", __func__);
+ CallbackEnv sCallbackEnv(__func__);
+ if (!sCallbackEnv.valid()) return;
+
+ ScopedLocalRef addr(
+ sCallbackEnv.get(), sCallbackEnv->NewByteArray(sizeof(RawAddress)));
+ if (!addr.get()) {
+ ALOGE("Fail to new jbyteArray bd addr for connection state");
+ return;
+ }
+
+ sCallbackEnv->SetByteArrayRegion(addr.get(), 0, sizeof(RawAddress),
+ (const jbyte*)bd_addr.address);
+ sCallbackEnv->CallVoidMethod(mCallbacksObj, method_onAudioStateChanged,
+ addr.get(), (jint)state);
+}
+
+static void bta2dp_audio_config_callback(const RawAddress& bd_addr,
+ uint32_t sample_rate,
+ uint8_t channel_count) {
+ ALOGI("%s", __func__);
+ CallbackEnv sCallbackEnv(__func__);
+ if (!sCallbackEnv.valid()) return;
+
+ ScopedLocalRef addr(
+ sCallbackEnv.get(), sCallbackEnv->NewByteArray(sizeof(RawAddress)));
+ if (!addr.get()) {
+ ALOGE("Fail to new jbyteArray bd addr for connection state");
+ return;
+ }
+
+ sCallbackEnv->SetByteArrayRegion(addr.get(), 0, sizeof(RawAddress),
+ (const jbyte*)bd_addr.address);
+ sCallbackEnv->CallVoidMethod(mCallbacksObj, method_onAudioConfigChanged,
+ addr.get(), (jint)sample_rate,
+ (jint)channel_count);
+}
+
+static btav_sink_callbacks_t sBluetoothA2dpCallbacks = {
+ sizeof(sBluetoothA2dpCallbacks), bta2dp_connection_state_callback,
+ bta2dp_audio_state_callback, bta2dp_audio_config_callback,
+};
+
+static void classInitNative(JNIEnv* env, jclass clazz) {
+ method_onConnectionStateChanged =
+ env->GetMethodID(clazz, "onConnectionStateChanged", "([BI)V");
+
+ method_onAudioStateChanged =
+ env->GetMethodID(clazz, "onAudioStateChanged", "([BI)V");
+
+ method_onAudioConfigChanged =
+ env->GetMethodID(clazz, "onAudioConfigChanged", "([BII)V");
+
+ ALOGI("%s: succeeds", __func__);
+}
+
+static void initNative(JNIEnv* env, jobject object,
+ jint maxConnectedAudioDevices) {
+ const bt_interface_t* btInf = getBluetoothInterface();
+ if (btInf == NULL) {
+ ALOGE("Bluetooth module is not loaded");
+ return;
+ }
+
+ if (sBluetoothA2dpInterface != NULL) {
+ ALOGW("Cleaning up A2DP Interface before initializing...");
+ sBluetoothA2dpInterface->cleanup();
+ sBluetoothA2dpInterface = NULL;
+ }
+
+ if (mCallbacksObj != NULL) {
+ ALOGW("Cleaning up A2DP callback object");
+ env->DeleteGlobalRef(mCallbacksObj);
+ mCallbacksObj = NULL;
+ }
+
+ sBluetoothA2dpInterface =
+ (btav_sink_interface_t*)btInf->get_profile_interface(
+ BT_PROFILE_ADVANCED_AUDIO_SINK_ID);
+ if (sBluetoothA2dpInterface == NULL) {
+ ALOGE("Failed to get Bluetooth A2DP Sink Interface");
+ return;
+ }
+
+ bt_status_t status = sBluetoothA2dpInterface->init(&sBluetoothA2dpCallbacks,
+ maxConnectedAudioDevices);
+ if (status != BT_STATUS_SUCCESS) {
+ ALOGE("Failed to initialize Bluetooth A2DP Sink, status: %d", status);
+ sBluetoothA2dpInterface = NULL;
+ return;
+ }
+
+ mCallbacksObj = env->NewGlobalRef(object);
+}
+
+static void cleanupNative(JNIEnv* env, jobject object) {
+ const bt_interface_t* btInf = getBluetoothInterface();
+
+ if (btInf == NULL) {
+ ALOGE("Bluetooth module is not loaded");
+ return;
+ }
+
+ if (sBluetoothA2dpInterface != NULL) {
+ sBluetoothA2dpInterface->cleanup();
+ sBluetoothA2dpInterface = NULL;
+ }
+
+ if (mCallbacksObj != NULL) {
+ env->DeleteGlobalRef(mCallbacksObj);
+ mCallbacksObj = NULL;
+ }
+}
+
+static jboolean connectA2dpNative(JNIEnv* env, jobject object,
+ jbyteArray address) {
+ ALOGI("%s: sBluetoothA2dpInterface: %p", __func__, sBluetoothA2dpInterface);
+ if (!sBluetoothA2dpInterface) return JNI_FALSE;
+
+ jbyte* addr = env->GetByteArrayElements(address, NULL);
+ if (!addr) {
+ jniThrowIOException(env, EINVAL);
+ return JNI_FALSE;
+ }
+
+ RawAddress bd_addr;
+ bd_addr.FromOctets(reinterpret_cast(addr));
+ bt_status_t status = sBluetoothA2dpInterface->connect(bd_addr);
+ if (status != BT_STATUS_SUCCESS) {
+ ALOGE("Failed HF connection, status: %d", status);
+ }
+ env->ReleaseByteArrayElements(address, addr, 0);
+ return (status == BT_STATUS_SUCCESS) ? JNI_TRUE : JNI_FALSE;
+}
+
+static jboolean disconnectA2dpNative(JNIEnv* env, jobject object,
+ jbyteArray address) {
+ if (!sBluetoothA2dpInterface) return JNI_FALSE;
+
+ jbyte* addr = env->GetByteArrayElements(address, NULL);
+ if (!addr) {
+ jniThrowIOException(env, EINVAL);
+ return JNI_FALSE;
+ }
+
+ RawAddress bd_addr;
+ bd_addr.FromOctets(reinterpret_cast(addr));
+ bt_status_t status = sBluetoothA2dpInterface->disconnect(bd_addr);
+ if (status != BT_STATUS_SUCCESS) {
+ ALOGE("Failed HF disconnection, status: %d", status);
+ }
+ env->ReleaseByteArrayElements(address, addr, 0);
+ return (status == BT_STATUS_SUCCESS) ? JNI_TRUE : JNI_FALSE;
+}
+
+static void informAudioFocusStateNative(JNIEnv* env, jobject object,
+ jint focus_state) {
+ if (!sBluetoothA2dpInterface) return;
+ sBluetoothA2dpInterface->set_audio_focus_state((uint8_t)focus_state);
+}
+
+static void informAudioTrackGainNative(JNIEnv* env, jobject object,
+ jfloat gain) {
+ if (!sBluetoothA2dpInterface) return;
+ sBluetoothA2dpInterface->set_audio_track_gain((float)gain);
+}
+
+static jboolean setActiveDeviceNative(JNIEnv* env, jobject object,
+ jbyteArray address) {
+ if (!sBluetoothA2dpInterface) return JNI_FALSE;
+
+ ALOGI("%s: sBluetoothA2dpInterface: %p", __func__, sBluetoothA2dpInterface);
+
+ jbyte* addr = env->GetByteArrayElements(address, NULL);
+ if (!addr) {
+ jniThrowIOException(env, EINVAL);
+ return JNI_FALSE;
+ }
+
+ RawAddress rawAddress;
+ rawAddress.FromOctets((uint8_t*)addr);
+ bt_status_t status = sBluetoothA2dpInterface->set_active_device(rawAddress);
+ if (status != BT_STATUS_SUCCESS) {
+ ALOGE("Failed sending passthru command, status: %d", status);
+ }
+ env->ReleaseByteArrayElements(address, addr, 0);
+
+ return (status == BT_STATUS_SUCCESS) ? JNI_TRUE : JNI_FALSE;
+}
+
+static JNINativeMethod sMethods[] = {
+ {"classInitNative", "()V", (void*)classInitNative},
+ {"initNative", "(I)V", (void*)initNative},
+ {"cleanupNative", "()V", (void*)cleanupNative},
+ {"connectA2dpNative", "([B)Z", (void*)connectA2dpNative},
+ {"disconnectA2dpNative", "([B)Z", (void*)disconnectA2dpNative},
+ {"informAudioFocusStateNative", "(I)V", (void*)informAudioFocusStateNative},
+ {"informAudioTrackGainNative", "(F)V", (void*)informAudioTrackGainNative},
+ {"setActiveDeviceNative", "([B)Z", (void*)setActiveDeviceNative},
+};
+
+int register_com_android_bluetooth_a2dp_sink(JNIEnv* env) {
+ return jniRegisterNativeMethods(
+ env, "com/android/bluetooth/a2dpsink/A2dpSinkNativeInterface", sMethods,
+ NELEM(sMethods));
+}
+}
diff --git a/android/app/jni/com_android_bluetooth_avrcp_controller.cpp b/android/app/jni/com_android_bluetooth_avrcp_controller.cpp
new file mode 100755
index 0000000000000000000000000000000000000000..86bf0687913e240dc2d33de8985bb739eec585c7
--- /dev/null
+++ b/android/app/jni/com_android_bluetooth_avrcp_controller.cpp
@@ -0,0 +1,1316 @@
+/*
+ * Copyright (C) 2016 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 "BluetoothAvrcpControllerJni"
+
+#define LOG_NDEBUG 0
+
+#include "com_android_bluetooth.h"
+#include "hardware/bt_rc.h"
+#include "utils/Log.h"
+
+#include
+#include
+
+namespace android {
+static jmethodID method_handlePassthroughRsp;
+static jmethodID method_onConnectionStateChanged;
+static jmethodID method_getRcFeatures;
+static jmethodID method_setplayerappsettingrsp;
+static jmethodID method_handleplayerappsetting;
+static jmethodID method_handleplayerappsettingchanged;
+static jmethodID method_handleSetAbsVolume;
+static jmethodID method_handleRegisterNotificationAbsVol;
+static jmethodID method_handletrackchanged;
+static jmethodID method_handleplaypositionchanged;
+static jmethodID method_handleplaystatuschanged;
+static jmethodID method_handleGetFolderItemsRsp;
+static jmethodID method_handleGetPlayerItemsRsp;
+static jmethodID method_handleGroupNavigationRsp;
+static jmethodID method_createFromNativeMediaItem;
+static jmethodID method_createFromNativeFolderItem;
+static jmethodID method_createFromNativePlayerItem;
+static jmethodID method_handleChangeFolderRsp;
+static jmethodID method_handleSetBrowsedPlayerRsp;
+static jmethodID method_handleSetAddressedPlayerRsp;
+static jmethodID method_handleAddressedPlayerChanged;
+static jmethodID method_handleNowPlayingContentChanged;
+static jmethodID method_onAvailablePlayerChanged;
+static jmethodID method_getRcPsm;
+
+static jclass class_AvrcpItem;
+static jclass class_AvrcpPlayer;
+
+static const btrc_ctrl_interface_t* sBluetoothAvrcpInterface = NULL;
+static jobject sCallbacksObj = NULL;
+static std::shared_timed_mutex sCallbacks_mutex;
+
+static void btavrcp_passthrough_response_callback(const RawAddress& bd_addr,
+ int id, int pressed) {
+ ALOGI("%s: id: %d, pressed: %d", __func__, id, pressed);
+ std::shared_lock lock(sCallbacks_mutex);
+ CallbackEnv sCallbackEnv(__func__);
+ if (!sCallbackEnv.valid()) return;
+ if (!sCallbacksObj) {
+ ALOGE("%s: sCallbacksObj is null", __func__);
+ return;
+ }
+
+ ScopedLocalRef addr(
+ sCallbackEnv.get(), sCallbackEnv->NewByteArray(sizeof(RawAddress)));
+ if (!addr.get()) {
+ ALOGE("%s: Failed to allocate a new byte array", __func__);
+ return;
+ }
+
+ sCallbackEnv->SetByteArrayRegion(addr.get(), 0, sizeof(RawAddress),
+ (jbyte*)&bd_addr.address);
+ sCallbackEnv->CallVoidMethod(sCallbacksObj, method_handlePassthroughRsp,
+ (jint)id, (jint)pressed, addr.get());
+}
+
+static void btavrcp_groupnavigation_response_callback(int id, int pressed) {
+ ALOGV("%s", __func__);
+ std::shared_lock lock(sCallbacks_mutex);
+ CallbackEnv sCallbackEnv(__func__);
+ if (!sCallbackEnv.valid()) return;
+ if (!sCallbacksObj) {
+ ALOGE("%s: sCallbacksObj is null", __func__);
+ return;
+ }
+
+ sCallbackEnv->CallVoidMethod(sCallbacksObj, method_handleGroupNavigationRsp,
+ (jint)id, (jint)pressed);
+}
+
+static void btavrcp_connection_state_callback(bool rc_connect, bool br_connect,
+ const RawAddress& bd_addr) {
+ ALOGI("%s: conn state: rc: %d br: %d", __func__, rc_connect, br_connect);
+ std::shared_lock lock(sCallbacks_mutex);
+ CallbackEnv sCallbackEnv(__func__);
+ if (!sCallbackEnv.valid()) return;
+ if (!sCallbacksObj) {
+ ALOGE("%s: sCallbacksObj is null", __func__);
+ return;
+ }
+
+ ScopedLocalRef addr(
+ sCallbackEnv.get(), sCallbackEnv->NewByteArray(sizeof(RawAddress)));
+ if (!addr.get()) {
+ ALOGE("%s: Failed to allocate a new byte array", __func__);
+ return;
+ }
+
+ sCallbackEnv->SetByteArrayRegion(addr.get(), 0, sizeof(RawAddress),
+ (jbyte*)bd_addr.address);
+ sCallbackEnv->CallVoidMethod(sCallbacksObj, method_onConnectionStateChanged,
+ (jboolean)rc_connect, (jboolean)br_connect,
+ addr.get());
+}
+
+static void btavrcp_get_rcfeatures_callback(const RawAddress& bd_addr,
+ int features) {
+ ALOGV("%s", __func__);
+ std::shared_lock lock(sCallbacks_mutex);
+ CallbackEnv sCallbackEnv(__func__);
+ if (!sCallbackEnv.valid()) return;
+ if (!sCallbacksObj) {
+ ALOGE("%s: sCallbacksObj is null", __func__);
+ return;
+ }
+
+ ScopedLocalRef addr(
+ sCallbackEnv.get(), sCallbackEnv->NewByteArray(sizeof(RawAddress)));
+ if (!addr.get()) {
+ ALOGE("%s: Failed to allocate a new byte array", __func__);
+ return;
+ }
+
+ sCallbackEnv->SetByteArrayRegion(addr.get(), 0, sizeof(RawAddress),
+ (jbyte*)&bd_addr.address);
+ sCallbackEnv->CallVoidMethod(sCallbacksObj, method_getRcFeatures, addr.get(),
+ (jint)features);
+}
+
+static void btavrcp_setplayerapplicationsetting_rsp_callback(
+ const RawAddress& bd_addr, uint8_t accepted) {
+ ALOGV("%s", __func__);
+ std::shared_lock lock(sCallbacks_mutex);
+ CallbackEnv sCallbackEnv(__func__);
+ if (!sCallbackEnv.valid()) return;
+ if (!sCallbacksObj) {
+ ALOGE("%s: sCallbacksObj is null", __func__);
+ return;
+ }
+
+ ScopedLocalRef addr(
+ sCallbackEnv.get(), sCallbackEnv->NewByteArray(sizeof(RawAddress)));
+ if (!addr.get()) {
+ ALOGE("%s: Failed to allocate a new byte array", __func__);
+ return;
+ }
+
+ sCallbackEnv->SetByteArrayRegion(addr.get(), 0, sizeof(RawAddress),
+ (jbyte*)&bd_addr.address);
+ sCallbackEnv->CallVoidMethod(sCallbacksObj, method_setplayerappsettingrsp,
+ addr.get(), (jint)accepted);
+}
+
+static void btavrcp_playerapplicationsetting_callback(
+ const RawAddress& bd_addr, uint8_t num_attr,
+ btrc_player_app_attr_t* app_attrs, uint8_t num_ext_attr,
+ btrc_player_app_ext_attr_t* ext_attrs) {
+ ALOGI("%s", __func__);
+ std::shared_lock lock(sCallbacks_mutex);
+ CallbackEnv sCallbackEnv(__func__);
+ if (!sCallbackEnv.valid()) return;
+ if (!sCallbacksObj) {
+ ALOGE("%s: sCallbacksObj is null", __func__);
+ return;
+ }
+
+ ScopedLocalRef addr(
+ sCallbackEnv.get(), sCallbackEnv->NewByteArray(sizeof(RawAddress)));
+ if (!addr.get()) {
+ ALOGE("%s: Failed to allocate a new byte array", __func__);
+ return;
+ }
+ sCallbackEnv->SetByteArrayRegion(addr.get(), 0, sizeof(RawAddress),
+ (jbyte*)&bd_addr.address);
+ /* TODO ext attrs
+ * Flattening defined attributes:
+ */
+ jint arraylen = 0;
+ for (int i = 0; i < num_attr; i++) {
+ /*2 bytes for id and num */
+ arraylen += 2 + app_attrs[i].num_val;
+ }
+ ALOGV(" arraylen %d", arraylen);
+
+ ScopedLocalRef playerattribs(
+ sCallbackEnv.get(), sCallbackEnv->NewByteArray(arraylen));
+ if (!playerattribs.get()) {
+ ALOGE("%s: Failed to allocate a new byte array", __func__);
+ return;
+ }
+
+ for (int i = 0, k = 0; (i < num_attr) && (k < arraylen); i++) {
+ sCallbackEnv->SetByteArrayRegion(playerattribs.get(), k, 1,
+ (jbyte*)&(app_attrs[i].attr_id));
+ k++;
+ sCallbackEnv->SetByteArrayRegion(playerattribs.get(), k, 1,
+ (jbyte*)&(app_attrs[i].num_val));
+ k++;
+ sCallbackEnv->SetByteArrayRegion(playerattribs.get(), k,
+ app_attrs[i].num_val,
+ (jbyte*)(app_attrs[i].attr_val));
+ k = k + app_attrs[i].num_val;
+ }
+ sCallbackEnv->CallVoidMethod(sCallbacksObj, method_handleplayerappsetting,
+ addr.get(), playerattribs.get(), (jint)arraylen);
+}
+
+static void btavrcp_playerapplicationsetting_changed_callback(
+ const RawAddress& bd_addr, const btrc_player_settings_t& vals) {
+ ALOGI("%s", __func__);
+ std::shared_lock lock(sCallbacks_mutex);
+ CallbackEnv sCallbackEnv(__func__);
+ if (!sCallbackEnv.valid()) return;
+ if (!sCallbacksObj) {
+ ALOGE("%s: sCallbacksObj is null", __func__);
+ return;
+ }
+
+ ScopedLocalRef addr(
+ sCallbackEnv.get(), sCallbackEnv->NewByteArray(sizeof(RawAddress)));
+ if (!addr.get()) {
+ ALOGE("%s: Failed to allocate a new byte array", __func__);
+ return;
+ }
+ sCallbackEnv->SetByteArrayRegion(addr.get(), 0, sizeof(RawAddress),
+ (jbyte*)&bd_addr.address);
+
+ int arraylen = vals.num_attr * 2;
+ ScopedLocalRef playerattribs(
+ sCallbackEnv.get(), sCallbackEnv->NewByteArray(arraylen));
+ if (!playerattribs.get()) {
+ ALOGE("Fail to new jbyteArray playerattribs ");
+ return;
+ }
+ /*
+ * Flatening format:
+ */
+ for (int i = 0, k = 0; (i < vals.num_attr) && (k < arraylen); i++) {
+ sCallbackEnv->SetByteArrayRegion(playerattribs.get(), k, 1,
+ (jbyte*)&(vals.attr_ids[i]));
+ k++;
+ sCallbackEnv->SetByteArrayRegion(playerattribs.get(), k, 1,
+ (jbyte*)&(vals.attr_values[i]));
+ k++;
+ }
+ sCallbackEnv->CallVoidMethod(sCallbacksObj,
+ method_handleplayerappsettingchanged, addr.get(),
+ playerattribs.get(), (jint)arraylen);
+}
+
+static void btavrcp_set_abs_vol_cmd_callback(const RawAddress& bd_addr,
+ uint8_t abs_vol, uint8_t label) {
+ ALOGI("%s", __func__);
+ std::shared_lock lock(sCallbacks_mutex);
+ CallbackEnv sCallbackEnv(__func__);
+ if (!sCallbackEnv.valid()) return;
+ if (!sCallbacksObj) {
+ ALOGE("%s: sCallbacksObj is null", __func__);
+ return;
+ }
+
+ ScopedLocalRef addr(
+ sCallbackEnv.get(), sCallbackEnv->NewByteArray(sizeof(RawAddress)));
+ if (!addr.get()) {
+ ALOGE("%s: Failed to allocate a new byte array", __func__);
+ return;
+ }
+
+ sCallbackEnv->SetByteArrayRegion(addr.get(), 0, sizeof(RawAddress),
+ (jbyte*)&bd_addr.address);
+ sCallbackEnv->CallVoidMethod(sCallbacksObj, method_handleSetAbsVolume,
+ addr.get(), (jbyte)abs_vol, (jbyte)label);
+}
+
+static void btavrcp_register_notification_absvol_callback(
+ const RawAddress& bd_addr, uint8_t label) {
+ ALOGI("%s", __func__);
+ std::shared_lock lock(sCallbacks_mutex);
+ CallbackEnv sCallbackEnv(__func__);
+ if (!sCallbackEnv.valid()) return;
+ if (!sCallbacksObj) {
+ ALOGE("%s: sCallbacksObj is null", __func__);
+ return;
+ }
+
+ ScopedLocalRef addr(
+ sCallbackEnv.get(), sCallbackEnv->NewByteArray(sizeof(RawAddress)));
+ if (!addr.get()) {
+ ALOGE("%s: Failed to allocate a new byte array", __func__);
+ return;
+ }
+
+ sCallbackEnv->SetByteArrayRegion(addr.get(), 0, sizeof(RawAddress),
+ (jbyte*)&bd_addr.address);
+ sCallbackEnv->CallVoidMethod(sCallbacksObj,
+ method_handleRegisterNotificationAbsVol,
+ addr.get(), (jbyte)label);
+}
+
+static void btavrcp_track_changed_callback(const RawAddress& bd_addr,
+ uint8_t num_attr,
+ btrc_element_attr_val_t* p_attrs) {
+ /*
+ * byteArray will be formatted like this: id,len,string
+ * Assuming text feild to be null terminated.
+ */
+ ALOGI("%s", __func__);
+ std::shared_lock lock(sCallbacks_mutex);
+ CallbackEnv sCallbackEnv(__func__);
+ if (!sCallbackEnv.valid()) return;
+ if (!sCallbacksObj) {
+ ALOGE("%s: sCallbacksObj is null", __func__);
+ return;
+ }
+
+ ScopedLocalRef addr(
+ sCallbackEnv.get(), sCallbackEnv->NewByteArray(sizeof(RawAddress)));
+ if (!addr.get()) {
+ ALOGE("%s: Failed to allocate a new byte array", __func__);
+ return;
+ }
+
+ ScopedLocalRef attribIds(sCallbackEnv.get(),
+ sCallbackEnv->NewIntArray(num_attr));
+ if (!attribIds.get()) {
+ ALOGE(" failed to set new array for attribIds");
+ return;
+ }
+ sCallbackEnv->SetByteArrayRegion(addr.get(), 0, sizeof(RawAddress),
+ (jbyte*)&bd_addr.address);
+
+ jclass strclazz = sCallbackEnv->FindClass("java/lang/String");
+ ScopedLocalRef stringArray(
+ sCallbackEnv.get(),
+ sCallbackEnv->NewObjectArray((jint)num_attr, strclazz, 0));
+ if (!stringArray.get()) {
+ ALOGE(" failed to get String array");
+ return;
+ }
+
+ for (jint i = 0; i < num_attr; i++) {
+ ScopedLocalRef str(
+ sCallbackEnv.get(),
+ sCallbackEnv->NewStringUTF((char*)(p_attrs[i].text)));
+ if (!str.get()) {
+ ALOGE("Unable to get str");
+ return;
+ }
+ sCallbackEnv->SetIntArrayRegion(attribIds.get(), i, 1,
+ (jint*)&(p_attrs[i].attr_id));
+ sCallbackEnv->SetObjectArrayElement(stringArray.get(), i, str.get());
+ }
+
+ sCallbackEnv->CallVoidMethod(sCallbacksObj, method_handletrackchanged,
+ addr.get(), (jbyte)(num_attr), attribIds.get(),
+ stringArray.get());
+}
+
+static void btavrcp_play_position_changed_callback(const RawAddress& bd_addr,
+ uint32_t song_len,
+ uint32_t song_pos) {
+ ALOGI("%s", __func__);
+ std::shared_lock lock(sCallbacks_mutex);
+ CallbackEnv sCallbackEnv(__func__);
+ if (!sCallbackEnv.valid()) return;
+ if (!sCallbacksObj) {
+ ALOGE("%s: sCallbacksObj is null", __func__);
+ return;
+ }
+
+ ScopedLocalRef addr(
+ sCallbackEnv.get(), sCallbackEnv->NewByteArray(sizeof(RawAddress)));
+ if (!addr.get()) {
+ ALOGE("%s: Failed to allocate a new byte array", __func__);
+ return;
+ }
+ sCallbackEnv->SetByteArrayRegion(addr.get(), 0, sizeof(RawAddress),
+ (jbyte*)&bd_addr.address);
+ sCallbackEnv->CallVoidMethod(sCallbacksObj, method_handleplaypositionchanged,
+ addr.get(), (jint)(song_len), (jint)song_pos);
+}
+
+static void btavrcp_play_status_changed_callback(
+ const RawAddress& bd_addr, btrc_play_status_t play_status) {
+ ALOGI("%s", __func__);
+ std::shared_lock lock(sCallbacks_mutex);
+ CallbackEnv sCallbackEnv(__func__);
+ if (!sCallbackEnv.valid()) return;
+ if (!sCallbacksObj) {
+ ALOGE("%s: sCallbacksObj is null", __func__);
+ return;
+ }
+
+ ScopedLocalRef addr(
+ sCallbackEnv.get(), sCallbackEnv->NewByteArray(sizeof(RawAddress)));
+ if (!addr.get()) {
+ ALOGE("%s: Failed to allocate a new byte array", __func__);
+ return;
+ }
+ sCallbackEnv->SetByteArrayRegion(addr.get(), 0, sizeof(RawAddress),
+ (jbyte*)&bd_addr.address);
+ sCallbackEnv->CallVoidMethod(sCallbacksObj, method_handleplaystatuschanged,
+ addr.get(), (jbyte)play_status);
+}
+
+static void btavrcp_get_folder_items_callback(
+ const RawAddress& bd_addr, btrc_status_t status,
+ const btrc_folder_items_t* folder_items, uint8_t count) {
+ /* Folder items are list of items that can be either BTRC_ITEM_PLAYER
+ * BTRC_ITEM_MEDIA, BTRC_ITEM_FOLDER. Here we translate them to their java
+ * counterparts by calling the java constructor for each of the items.
+ */
+ ALOGV("%s count %d", __func__, count);
+ std::shared_lock lock(sCallbacks_mutex);
+ CallbackEnv sCallbackEnv(__func__);
+ if (!sCallbackEnv.valid()) return;
+ if (!sCallbacksObj) {
+ ALOGE("%s: sCallbacksObj is null", __func__);
+ return;
+ }
+
+ ScopedLocalRef addr(
+ sCallbackEnv.get(), sCallbackEnv->NewByteArray(sizeof(RawAddress)));
+ if (!addr.get()) {
+ ALOGE("%s: Failed to allocate a new byte array", __func__);
+ return;
+ }
+
+ sCallbackEnv->SetByteArrayRegion(addr.get(), 0, sizeof(RawAddress),
+ (jbyte*)&bd_addr.address);
+
+ // Inspect if the first element is a folder/item or player listing. They are
+ // always exclusive.
+ bool isPlayerListing =
+ count > 0 && (folder_items[0].item_type == BTRC_ITEM_PLAYER);
+
+ // Initialize arrays for Folder OR Player listing.
+ ScopedLocalRef itemArray(sCallbackEnv.get(), NULL);
+ if (isPlayerListing) {
+ itemArray.reset(
+ sCallbackEnv->NewObjectArray((jint)count, class_AvrcpPlayer, 0));
+ } else {
+ itemArray.reset(sCallbackEnv->NewObjectArray(
+ (jint)count, class_AvrcpItem, 0));
+ }
+ if (!itemArray.get()) {
+ ALOGE("%s itemArray allocation failed.", __func__);
+ return;
+ }
+ for (int i = 0; i < count; i++) {
+ const btrc_folder_items_t* item = &(folder_items[i]);
+ ALOGV("%s item type %d", __func__, item->item_type);
+ switch (item->item_type) {
+ case BTRC_ITEM_MEDIA: {
+ // Parse name
+ ScopedLocalRef mediaName(
+ sCallbackEnv.get(),
+ sCallbackEnv->NewStringUTF((const char*)item->media.name));
+ if (!mediaName.get()) {
+ ALOGE("%s can't allocate media name string!", __func__);
+ return;
+ }
+ // Parse UID
+ long long uid = *(long long*)item->media.uid;
+ // Parse Attrs
+ ScopedLocalRef attrIdArray(
+ sCallbackEnv.get(),
+ sCallbackEnv->NewIntArray(item->media.num_attrs));
+ if (!attrIdArray.get()) {
+ ALOGE("%s can't allocate attr id array!", __func__);
+ return;
+ }
+ ScopedLocalRef attrValArray(
+ sCallbackEnv.get(),
+ sCallbackEnv->NewObjectArray(
+ item->media.num_attrs,
+ sCallbackEnv->FindClass("java/lang/String"), 0));
+ if (!attrValArray.get()) {
+ ALOGE("%s can't allocate attr val array!", __func__);
+ return;
+ }
+
+ for (int j = 0; j < item->media.num_attrs; j++) {
+ sCallbackEnv->SetIntArrayRegion(
+ attrIdArray.get(), j, 1,
+ (jint*)&(item->media.p_attrs[j].attr_id));
+ ScopedLocalRef attrValStr(
+ sCallbackEnv.get(),
+ sCallbackEnv->NewStringUTF((char*)(item->media.p_attrs[j].text)));
+ sCallbackEnv->SetObjectArrayElement(attrValArray.get(), j,
+ attrValStr.get());
+ }
+
+ ScopedLocalRef mediaObj(
+ sCallbackEnv.get(),
+ (jobject)sCallbackEnv->CallObjectMethod(
+ sCallbacksObj, method_createFromNativeMediaItem, addr.get(),
+ uid, (jint)item->media.type, mediaName.get(),
+ attrIdArray.get(), attrValArray.get()));
+ if (!mediaObj.get()) {
+ ALOGE("%s failed to create AvrcpItem for type ITEM_MEDIA", __func__);
+ return;
+ }
+ sCallbackEnv->SetObjectArrayElement(itemArray.get(), i, mediaObj.get());
+ break;
+ }
+
+ case BTRC_ITEM_FOLDER: {
+ // Parse name
+ ScopedLocalRef folderName(
+ sCallbackEnv.get(),
+ sCallbackEnv->NewStringUTF((const char*)item->folder.name));
+ if (!folderName.get()) {
+ ALOGE("%s can't allocate folder name string!", __func__);
+ return;
+ }
+ // Parse UID
+ long long uid = *(long long*)item->folder.uid;
+ ScopedLocalRef folderObj(
+ sCallbackEnv.get(),
+ (jobject)sCallbackEnv->CallObjectMethod(
+ sCallbacksObj, method_createFromNativeFolderItem, addr.get(),
+ uid, (jint)item->folder.type, folderName.get(),
+ (jint)item->folder.playable));
+ if (!folderObj.get()) {
+ ALOGE("%s failed to create AvrcpItem for type ITEM_FOLDER", __func__);
+ return;
+ }
+ sCallbackEnv->SetObjectArrayElement(itemArray.get(), i,
+ folderObj.get());
+ break;
+ }
+
+ case BTRC_ITEM_PLAYER: {
+ // Parse name
+ isPlayerListing = true;
+ jint id = (jint)item->player.player_id;
+ jint playerType = (jint)item->player.major_type;
+ jint playStatus = (jint)item->player.play_status;
+ ScopedLocalRef featureBitArray(
+ sCallbackEnv.get(),
+ sCallbackEnv->NewByteArray(BTRC_FEATURE_BIT_MASK_SIZE *
+ sizeof(uint8_t)));
+ if (!featureBitArray.get()) {
+ ALOGE("%s failed to allocate featureBitArray", __func__);
+ return;
+ }
+ sCallbackEnv->SetByteArrayRegion(
+ featureBitArray.get(), 0,
+ sizeof(uint8_t) * BTRC_FEATURE_BIT_MASK_SIZE,
+ (jbyte*)item->player.features);
+ ScopedLocalRef playerName(
+ sCallbackEnv.get(),
+ sCallbackEnv->NewStringUTF((const char*)item->player.name));
+ if (!playerName.get()) {
+ ALOGE("%s can't allocate player name string!", __func__);
+ return;
+ }
+ ScopedLocalRef playerObj(
+ sCallbackEnv.get(),
+ (jobject)sCallbackEnv->CallObjectMethod(
+ sCallbacksObj, method_createFromNativePlayerItem, addr.get(),
+ id, playerName.get(), featureBitArray.get(), playStatus,
+ playerType));
+ if (!playerObj.get()) {
+ ALOGE("%s failed to create AvrcpPlayer from ITEM_PLAYER", __func__);
+ return;
+ }
+ sCallbackEnv->SetObjectArrayElement(itemArray.get(), i,
+ playerObj.get());
+ break;
+ }
+
+ default:
+ ALOGE("%s cannot understand type %d", __func__, item->item_type);
+ }
+ }
+
+ if (isPlayerListing) {
+ sCallbackEnv->CallVoidMethod(sCallbacksObj, method_handleGetPlayerItemsRsp,
+ addr.get(), itemArray.get());
+ } else {
+ sCallbackEnv->CallVoidMethod(sCallbacksObj, method_handleGetFolderItemsRsp,
+ addr.get(), status, itemArray.get());
+ }
+}
+
+static void btavrcp_change_path_callback(const RawAddress& bd_addr,
+ uint32_t count) {
+ ALOGI("%s count %d", __func__, count);
+ std::shared_lock lock(sCallbacks_mutex);
+ CallbackEnv sCallbackEnv(__func__);
+ if (!sCallbackEnv.valid()) return;
+ if (!sCallbacksObj) {
+ ALOGE("%s: sCallbacksObj is null", __func__);
+ return;
+ }
+ ScopedLocalRef addr(
+ sCallbackEnv.get(), sCallbackEnv->NewByteArray(sizeof(RawAddress)));
+ if (!addr.get()) {
+ ALOGE("%s: Failed to allocate a new byte array", __func__);
+ return;
+ }
+
+ sCallbackEnv->SetByteArrayRegion(addr.get(), 0, sizeof(RawAddress),
+ (jbyte*)&bd_addr.address);
+
+ sCallbackEnv->CallVoidMethod(sCallbacksObj, method_handleChangeFolderRsp,
+ addr.get(), (jint)count);
+}
+
+static void btavrcp_set_browsed_player_callback(const RawAddress& bd_addr,
+ uint8_t num_items,
+ uint8_t depth) {
+ ALOGI("%s items %d depth %d", __func__, num_items, depth);
+ std::shared_lock lock(sCallbacks_mutex);
+ CallbackEnv sCallbackEnv(__func__);
+ if (!sCallbackEnv.valid()) return;
+ if (!sCallbacksObj) {
+ ALOGE("%s: sCallbacksObj is null", __func__);
+ return;
+ }
+ ScopedLocalRef addr(
+ sCallbackEnv.get(), sCallbackEnv->NewByteArray(sizeof(RawAddress)));
+ if (!addr.get()) {
+ ALOGE("%s: Failed to allocate a new byte array", __func__);
+ return;
+ }
+
+ sCallbackEnv->SetByteArrayRegion(addr.get(), 0, sizeof(RawAddress),
+ (jbyte*)&bd_addr.address);
+
+ sCallbackEnv->CallVoidMethod(sCallbacksObj, method_handleSetBrowsedPlayerRsp,
+ addr.get(), (jint)num_items, (jint)depth);
+}
+
+static void btavrcp_set_addressed_player_callback(const RawAddress& bd_addr,
+ uint8_t status) {
+ ALOGI("%s status %d", __func__, status);
+ std::shared_lock lock(sCallbacks_mutex);
+ CallbackEnv sCallbackEnv(__func__);
+ if (!sCallbackEnv.valid()) return;
+ if (!sCallbacksObj) {
+ ALOGE("%s: sCallbacksObj is null", __func__);
+ return;
+ }
+ ScopedLocalRef addr(
+ sCallbackEnv.get(), sCallbackEnv->NewByteArray(sizeof(RawAddress)));
+ if (!addr.get()) {
+ ALOGE("%s: Failed to allocate a new byte array", __func__);
+ return;
+ }
+
+ sCallbackEnv->SetByteArrayRegion(addr.get(), 0, sizeof(RawAddress),
+ (jbyte*)&bd_addr.address);
+
+ sCallbackEnv->CallVoidMethod(sCallbacksObj,
+ method_handleSetAddressedPlayerRsp, addr.get(),
+ (jint)status);
+}
+
+static void btavrcp_addressed_player_changed_callback(const RawAddress& bd_addr,
+ uint16_t id) {
+ ALOGI("%s status %d", __func__, id);
+ std::shared_lock lock(sCallbacks_mutex);
+ CallbackEnv sCallbackEnv(__func__);
+ if (!sCallbackEnv.valid()) return;
+ if (!sCallbacksObj) {
+ ALOGE("%s: sCallbacksObj is null", __func__);
+ return;
+ }
+ ScopedLocalRef addr(
+ sCallbackEnv.get(), sCallbackEnv->NewByteArray(sizeof(RawAddress)));
+ if (!addr.get()) {
+ ALOGE("%s: Failed to allocate a new byte array", __func__);
+ return;
+ }
+
+ sCallbackEnv->SetByteArrayRegion(addr.get(), 0, sizeof(RawAddress),
+ (jbyte*)&bd_addr.address);
+
+ sCallbackEnv->CallVoidMethod(
+ sCallbacksObj, method_handleAddressedPlayerChanged, addr.get(), (jint)id);
+}
+
+static void btavrcp_now_playing_content_changed_callback(
+ const RawAddress& bd_addr) {
+ ALOGI("%s", __func__);
+
+ CallbackEnv sCallbackEnv(__func__);
+ if (!sCallbackEnv.valid()) return;
+ ScopedLocalRef addr(
+ sCallbackEnv.get(), sCallbackEnv->NewByteArray(sizeof(RawAddress)));
+ if (!addr.get()) {
+ ALOGE("%s: Failed to allocate a new byte array", __func__);
+ return;
+ }
+
+ sCallbackEnv->SetByteArrayRegion(addr.get(), 0, sizeof(RawAddress),
+ (jbyte*)&bd_addr.address);
+
+ sCallbackEnv->CallVoidMethod(
+ sCallbacksObj, method_handleNowPlayingContentChanged, addr.get());
+}
+
+static void btavrcp_available_player_changed_callback (
+ const RawAddress& bd_addr) {
+ ALOGI("%s", __func__);
+ std::shared_lock lock(sCallbacks_mutex);
+ CallbackEnv sCallbackEnv(__func__);
+ if (!sCallbacksObj) {
+ ALOGE("%s: sCallbacksObj is null", __func__);
+ return;
+ }
+ if (!sCallbackEnv.valid()) return;
+
+ ScopedLocalRef addr(
+ sCallbackEnv.get(), sCallbackEnv->NewByteArray(sizeof(RawAddress)));
+ if (!addr.get()) {
+ ALOGE("%s: Failed to allocate a new byte array", __func__);
+ return;
+ }
+
+ sCallbackEnv->SetByteArrayRegion(addr.get(), 0, sizeof(RawAddress),
+ (jbyte*)&bd_addr);
+ sCallbackEnv->CallVoidMethod(
+ sCallbacksObj, method_onAvailablePlayerChanged, addr.get());
+}
+
+static void btavrcp_get_rcpsm_callback(const RawAddress& bd_addr,
+ uint16_t psm) {
+ ALOGE("%s -> psm received of %d", __func__, psm);
+ std::shared_lock lock(sCallbacks_mutex);
+ CallbackEnv sCallbackEnv(__func__);
+ if (!sCallbacksObj) {
+ ALOGE("%s: sCallbacksObj is null", __func__);
+ return;
+ }
+ if (!sCallbackEnv.valid()) return;
+
+ ScopedLocalRef addr(
+ sCallbackEnv.get(), sCallbackEnv->NewByteArray(sizeof(RawAddress)));
+ if (!addr.get()) {
+ ALOGE("%s: Failed to allocate a new byte array", __func__);
+ return;
+ }
+
+ sCallbackEnv->SetByteArrayRegion(addr.get(), 0, sizeof(RawAddress),
+ (jbyte*)&bd_addr.address);
+ sCallbackEnv->CallVoidMethod(sCallbacksObj, method_getRcPsm, addr.get(),
+ (jint)psm);
+}
+
+static btrc_ctrl_callbacks_t sBluetoothAvrcpCallbacks = {
+ sizeof(sBluetoothAvrcpCallbacks),
+ btavrcp_passthrough_response_callback,
+ btavrcp_groupnavigation_response_callback,
+ btavrcp_connection_state_callback,
+ btavrcp_get_rcfeatures_callback,
+ btavrcp_setplayerapplicationsetting_rsp_callback,
+ btavrcp_playerapplicationsetting_callback,
+ btavrcp_playerapplicationsetting_changed_callback,
+ btavrcp_set_abs_vol_cmd_callback,
+ btavrcp_register_notification_absvol_callback,
+ btavrcp_track_changed_callback,
+ btavrcp_play_position_changed_callback,
+ btavrcp_play_status_changed_callback,
+ btavrcp_get_folder_items_callback,
+ btavrcp_change_path_callback,
+ btavrcp_set_browsed_player_callback,
+ btavrcp_set_addressed_player_callback,
+ btavrcp_addressed_player_changed_callback,
+ btavrcp_now_playing_content_changed_callback,
+ btavrcp_available_player_changed_callback,
+ btavrcp_get_rcpsm_callback};
+
+static void classInitNative(JNIEnv* env, jclass clazz) {
+ method_handlePassthroughRsp =
+ env->GetMethodID(clazz, "handlePassthroughRsp", "(II[B)V");
+
+ method_handleGroupNavigationRsp =
+ env->GetMethodID(clazz, "handleGroupNavigationRsp", "(II)V");
+
+ method_onConnectionStateChanged =
+ env->GetMethodID(clazz, "onConnectionStateChanged", "(ZZ[B)V");
+
+ method_getRcFeatures = env->GetMethodID(clazz, "getRcFeatures", "([BI)V");
+
+ method_getRcPsm = env->GetMethodID(clazz, "getRcPsm", "([BI)V");
+
+ method_setplayerappsettingrsp =
+ env->GetMethodID(clazz, "setPlayerAppSettingRsp", "([BB)V");
+
+ method_handleplayerappsetting =
+ env->GetMethodID(clazz, "handlePlayerAppSetting", "([B[BI)V");
+
+ method_handleplayerappsettingchanged =
+ env->GetMethodID(clazz, "onPlayerAppSettingChanged", "([B[BI)V");
+
+ method_handleSetAbsVolume =
+ env->GetMethodID(clazz, "handleSetAbsVolume", "([BBB)V");
+
+ method_handleRegisterNotificationAbsVol =
+ env->GetMethodID(clazz, "handleRegisterNotificationAbsVol", "([BB)V");
+
+ method_handletrackchanged =
+ env->GetMethodID(clazz, "onTrackChanged", "([BB[I[Ljava/lang/String;)V");
+
+ method_handleplaypositionchanged =
+ env->GetMethodID(clazz, "onPlayPositionChanged", "([BII)V");
+
+ method_handleplaystatuschanged =
+ env->GetMethodID(clazz, "onPlayStatusChanged", "([BB)V");
+
+ method_handleGetFolderItemsRsp =
+ env->GetMethodID(clazz, "handleGetFolderItemsRsp",
+ "([BI[Lcom/android/bluetooth/avrcpcontroller/"
+ "AvrcpItem;)V");
+ method_handleGetPlayerItemsRsp = env->GetMethodID(
+ clazz, "handleGetPlayerItemsRsp",
+ "([B[Lcom/android/bluetooth/avrcpcontroller/AvrcpPlayer;)V");
+
+ method_createFromNativeMediaItem =
+ env->GetMethodID(clazz, "createFromNativeMediaItem",
+ "([BJILjava/lang/String;[I[Ljava/lang/String;)Lcom/"
+ "android/bluetooth/avrcpcontroller/AvrcpItem;");
+ method_createFromNativeFolderItem = env->GetMethodID(
+ clazz, "createFromNativeFolderItem",
+ "([BJILjava/lang/String;I)Lcom/android/bluetooth/avrcpcontroller/"
+ "AvrcpItem;");
+ method_createFromNativePlayerItem =
+ env->GetMethodID(clazz, "createFromNativePlayerItem",
+ "([BILjava/lang/String;[BII)Lcom/android/bluetooth/"
+ "avrcpcontroller/AvrcpPlayer;");
+ method_handleChangeFolderRsp =
+ env->GetMethodID(clazz, "handleChangeFolderRsp", "([BI)V");
+ method_handleSetBrowsedPlayerRsp =
+ env->GetMethodID(clazz, "handleSetBrowsedPlayerRsp", "([BII)V");
+ method_handleSetAddressedPlayerRsp =
+ env->GetMethodID(clazz, "handleSetAddressedPlayerRsp", "([BI)V");
+ method_handleAddressedPlayerChanged =
+ env->GetMethodID(clazz, "handleAddressedPlayerChanged", "([BI)V");
+ method_handleNowPlayingContentChanged =
+ env->GetMethodID(clazz, "handleNowPlayingContentChanged", "([B)V");
+ method_onAvailablePlayerChanged =
+ env->GetMethodID(clazz, "onAvailablePlayerChanged", "([B)V");
+
+ ALOGI("%s: succeeds", __func__);
+}
+
+static void initNative(JNIEnv* env, jobject object) {
+ std::unique_lock lock(sCallbacks_mutex);
+
+ jclass tmpAvrcpItem =
+ env->FindClass("com/android/bluetooth/avrcpcontroller/AvrcpItem");
+ class_AvrcpItem = (jclass)env->NewGlobalRef(tmpAvrcpItem);
+
+ jclass tmpBtPlayer =
+ env->FindClass("com/android/bluetooth/avrcpcontroller/AvrcpPlayer");
+ class_AvrcpPlayer = (jclass)env->NewGlobalRef(tmpBtPlayer);
+
+ const bt_interface_t* btInf = getBluetoothInterface();
+ if (btInf == NULL) {
+ ALOGE("Bluetooth module is not loaded");
+ return;
+ }
+
+ if (sBluetoothAvrcpInterface != NULL) {
+ ALOGW("Cleaning up Avrcp Interface before initializing...");
+ sBluetoothAvrcpInterface->cleanup();
+ sBluetoothAvrcpInterface = NULL;
+ }
+
+ if (sCallbacksObj != NULL) {
+ ALOGW("Cleaning up Avrcp callback object");
+ env->DeleteGlobalRef(sCallbacksObj);
+ sCallbacksObj = NULL;
+ }
+
+ sBluetoothAvrcpInterface =
+ (btrc_ctrl_interface_t*)btInf->get_profile_interface(
+ BT_PROFILE_AV_RC_CTRL_ID);
+ if (sBluetoothAvrcpInterface == NULL) {
+ ALOGE("Failed to get Bluetooth Avrcp Controller Interface");
+ return;
+ }
+
+ bt_status_t status =
+ sBluetoothAvrcpInterface->init(&sBluetoothAvrcpCallbacks);
+ if (status != BT_STATUS_SUCCESS) {
+ ALOGE("Failed to initialize Bluetooth Avrcp Controller, status: %d",
+ status);
+ sBluetoothAvrcpInterface = NULL;
+ return;
+ }
+
+ sCallbacksObj = env->NewGlobalRef(object);
+}
+
+static void cleanupNative(JNIEnv* env, jobject object) {
+ std::unique_lock lock(sCallbacks_mutex);
+
+ const bt_interface_t* btInf = getBluetoothInterface();
+ if (btInf == NULL) {
+ ALOGE("Bluetooth module is not loaded");
+ return;
+ }
+
+ if (sBluetoothAvrcpInterface != NULL) {
+ sBluetoothAvrcpInterface->cleanup();
+ sBluetoothAvrcpInterface = NULL;
+ }
+
+ if (sCallbacksObj != NULL) {
+ env->DeleteGlobalRef(sCallbacksObj);
+ sCallbacksObj = NULL;
+ }
+}
+
+static jboolean sendPassThroughCommandNative(JNIEnv* env, jobject object,
+ jbyteArray address, jint key_code,
+ jint key_state) {
+ if (!sBluetoothAvrcpInterface) return JNI_FALSE;
+
+ ALOGI("%s: sBluetoothAvrcpInterface: %p", __func__, sBluetoothAvrcpInterface);
+
+ ALOGI("key_code: %d, key_state: %d", key_code, key_state);
+
+ jbyte* addr = env->GetByteArrayElements(address, NULL);
+ if (!addr) {
+ jniThrowIOException(env, EINVAL);
+ return JNI_FALSE;
+ }
+
+ RawAddress rawAddress;
+ rawAddress.FromOctets((uint8_t*)addr);
+ bt_status_t status = sBluetoothAvrcpInterface->send_pass_through_cmd(
+ rawAddress, (uint8_t)key_code, (uint8_t)key_state);
+ if (status != BT_STATUS_SUCCESS) {
+ ALOGE("Failed sending passthru command, status: %d", status);
+ }
+ env->ReleaseByteArrayElements(address, addr, 0);
+
+ return (status == BT_STATUS_SUCCESS) ? JNI_TRUE : JNI_FALSE;
+}
+
+static jboolean sendGroupNavigationCommandNative(JNIEnv* env, jobject object,
+ jbyteArray address,
+ jint key_code,
+ jint key_state) {
+ if (!sBluetoothAvrcpInterface) return JNI_FALSE;
+
+ ALOGI("%s: sBluetoothAvrcpInterface: %p", __func__, sBluetoothAvrcpInterface);
+
+ ALOGI("key_code: %d, key_state: %d", key_code, key_state);
+
+ jbyte* addr = env->GetByteArrayElements(address, NULL);
+ if (!addr) {
+ jniThrowIOException(env, EINVAL);
+ return JNI_FALSE;
+ }
+ RawAddress rawAddress;
+ rawAddress.FromOctets((uint8_t*)addr);
+
+ bt_status_t status = sBluetoothAvrcpInterface->send_group_navigation_cmd(
+ rawAddress, (uint8_t)key_code, (uint8_t)key_state);
+ if (status != BT_STATUS_SUCCESS) {
+ ALOGE("Failed sending Grp Navigation command, status: %d", status);
+ }
+ env->ReleaseByteArrayElements(address, addr, 0);
+
+ return (status == BT_STATUS_SUCCESS) ? JNI_TRUE : JNI_FALSE;
+}
+
+static void setPlayerApplicationSettingValuesNative(JNIEnv* env, jobject object,
+ jbyteArray address,
+ jbyte num_attrib,
+ jbyteArray attrib_ids,
+ jbyteArray attrib_val) {
+ ALOGI("%s: sBluetoothAvrcpInterface: %p", __func__, sBluetoothAvrcpInterface);
+ if (!sBluetoothAvrcpInterface) return;
+
+ jbyte* addr = env->GetByteArrayElements(address, NULL);
+ if (!addr) {
+ jniThrowIOException(env, EINVAL);
+ return;
+ }
+
+ uint8_t* pAttrs = new uint8_t[num_attrib];
+ uint8_t* pAttrsVal = new uint8_t[num_attrib];
+ if ((!pAttrs) || (!pAttrsVal)) {
+ delete[] pAttrs;
+ ALOGE("setPlayerApplicationSettingValuesNative: not have enough memeory");
+ return;
+ }
+
+ jbyte* attr = env->GetByteArrayElements(attrib_ids, NULL);
+ jbyte* attr_val = env->GetByteArrayElements(attrib_val, NULL);
+ if ((!attr) || (!attr_val)) {
+ delete[] pAttrs;
+ delete[] pAttrsVal;
+ jniThrowIOException(env, EINVAL);
+ return;
+ }
+
+ int i;
+ for (i = 0; i < num_attrib; ++i) {
+ pAttrs[i] = (uint8_t)attr[i];
+ pAttrsVal[i] = (uint8_t)attr_val[i];
+ }
+ RawAddress rawAddress;
+ rawAddress.FromOctets((uint8_t*)addr);
+
+ bt_status_t status = sBluetoothAvrcpInterface->set_player_app_setting_cmd(
+ rawAddress, (uint8_t)num_attrib, pAttrs, pAttrsVal);
+ if (status != BT_STATUS_SUCCESS) {
+ ALOGE("Failed sending setPlAppSettValNative command, status: %d", status);
+ }
+ delete[] pAttrs;
+ delete[] pAttrsVal;
+ env->ReleaseByteArrayElements(attrib_ids, attr, 0);
+ env->ReleaseByteArrayElements(attrib_val, attr_val, 0);
+ env->ReleaseByteArrayElements(address, addr, 0);
+}
+
+static void sendAbsVolRspNative(JNIEnv* env, jobject object, jbyteArray address,
+ jint abs_vol, jint label) {
+ if (!sBluetoothAvrcpInterface) return;
+
+ jbyte* addr = env->GetByteArrayElements(address, NULL);
+ if (!addr) {
+ jniThrowIOException(env, EINVAL);
+ return;
+ }
+
+ ALOGI("%s: sBluetoothAvrcpInterface: %p", __func__, sBluetoothAvrcpInterface);
+ RawAddress rawAddress;
+ rawAddress.FromOctets((uint8_t*)addr);
+
+ bt_status_t status = sBluetoothAvrcpInterface->set_volume_rsp(
+ rawAddress, (uint8_t)abs_vol, (uint8_t)label);
+ if (status != BT_STATUS_SUCCESS) {
+ ALOGE("Failed sending sendAbsVolRspNative command, status: %d", status);
+ }
+ env->ReleaseByteArrayElements(address, addr, 0);
+}
+
+static void sendRegisterAbsVolRspNative(JNIEnv* env, jobject object,
+ jbyteArray address, jbyte rsp_type,
+ jint abs_vol, jint label) {
+ if (!sBluetoothAvrcpInterface) return;
+
+ jbyte* addr = env->GetByteArrayElements(address, NULL);
+ if (!addr) {
+ jniThrowIOException(env, EINVAL);
+ return;
+ }
+ ALOGI("%s: sBluetoothAvrcpInterface: %p", __func__, sBluetoothAvrcpInterface);
+ RawAddress rawAddress;
+ rawAddress.FromOctets((uint8_t*)addr);
+
+ bt_status_t status = sBluetoothAvrcpInterface->register_abs_vol_rsp(
+ rawAddress, (btrc_notification_type_t)rsp_type, (uint8_t)abs_vol,
+ (uint8_t)label);
+ if (status != BT_STATUS_SUCCESS) {
+ ALOGE("Failed sending sendRegisterAbsVolRspNative command, status: %d",
+ status);
+ }
+ env->ReleaseByteArrayElements(address, addr, 0);
+}
+
+static void getCurrentMetadataNative(JNIEnv* env, jobject object,
+ jbyteArray address) {
+ if (!sBluetoothAvrcpInterface) return;
+
+ jbyte* addr = env->GetByteArrayElements(address, NULL);
+ if (!addr) {
+ jniThrowIOException(env, EINVAL);
+ return;
+ }
+ ALOGV("%s: sBluetoothAvrcpInterface: %p", __func__, sBluetoothAvrcpInterface);
+ RawAddress rawAddress;
+ rawAddress.FromOctets((uint8_t*)addr);
+
+ bt_status_t status =
+ sBluetoothAvrcpInterface->get_current_metadata_cmd(rawAddress);
+ if (status != BT_STATUS_SUCCESS) {
+ ALOGE("Failed sending getCurrentMetadataNative command, status: %d", status);
+ }
+ env->ReleaseByteArrayElements(address, addr, 0);
+}
+
+static void getPlaybackStateNative(JNIEnv* env, jobject object,
+ jbyteArray address) {
+ if (!sBluetoothAvrcpInterface) return;
+
+ jbyte* addr = env->GetByteArrayElements(address, NULL);
+ if (!addr) {
+ jniThrowIOException(env, EINVAL);
+ return;
+ }
+ ALOGV("%s: sBluetoothAvrcpInterface: %p", __func__, sBluetoothAvrcpInterface);
+ RawAddress rawAddress;
+ rawAddress.FromOctets((uint8_t*)addr);
+
+ bt_status_t status =
+ sBluetoothAvrcpInterface->get_playback_state_cmd(rawAddress);
+ if (status != BT_STATUS_SUCCESS) {
+ ALOGE("Failed sending getPlaybackStateNative command, status: %d", status);
+ }
+ env->ReleaseByteArrayElements(address, addr, 0);
+}
+
+static void getNowPlayingListNative(JNIEnv* env, jobject object,
+ jbyteArray address, jint start, jint end) {
+ if (!sBluetoothAvrcpInterface) return;
+ jbyte* addr = env->GetByteArrayElements(address, NULL);
+ if (!addr) {
+ jniThrowIOException(env, EINVAL);
+ return;
+ }
+ ALOGV("%s: sBluetoothAvrcpInterface: %p", __func__, sBluetoothAvrcpInterface);
+ RawAddress rawAddress;
+ rawAddress.FromOctets((uint8_t*)addr);
+
+ bt_status_t status = sBluetoothAvrcpInterface->get_now_playing_list_cmd(
+ rawAddress, start, end);
+ if (status != BT_STATUS_SUCCESS) {
+ ALOGE("Failed sending getNowPlayingListNative command, status: %d", status);
+ }
+ env->ReleaseByteArrayElements(address, addr, 0);
+}
+
+static void getFolderListNative(JNIEnv* env, jobject object, jbyteArray address,
+ jint start, jint end) {
+ if (!sBluetoothAvrcpInterface) return;
+ jbyte* addr = env->GetByteArrayElements(address, NULL);
+ if (!addr) {
+ jniThrowIOException(env, EINVAL);
+ return;
+ }
+ ALOGV("%s: sBluetoothAvrcpInterface: %p", __func__, sBluetoothAvrcpInterface);
+ RawAddress rawAddress;
+ rawAddress.FromOctets((uint8_t*)addr);
+
+ bt_status_t status =
+ sBluetoothAvrcpInterface->get_folder_list_cmd(rawAddress, start, end);
+ if (status != BT_STATUS_SUCCESS) {
+ ALOGE("Failed sending getFolderListNative command, status: %d", status);
+ }
+ env->ReleaseByteArrayElements(address, addr, 0);
+}
+
+static void getPlayerListNative(JNIEnv* env, jobject object, jbyteArray address,
+ jint start, jint end) {
+ if (!sBluetoothAvrcpInterface) return;
+ jbyte* addr = env->GetByteArrayElements(address, NULL);
+ if (!addr) {
+ jniThrowIOException(env, EINVAL);
+ return;
+ }
+ ALOGI("%s: sBluetoothAvrcpInterface: %p", __func__, sBluetoothAvrcpInterface);
+ RawAddress rawAddress;
+ rawAddress.FromOctets((uint8_t*)addr);
+
+ bt_status_t status =
+ sBluetoothAvrcpInterface->get_player_list_cmd(rawAddress, start, end);
+ if (status != BT_STATUS_SUCCESS) {
+ ALOGE("Failed sending getPlayerListNative command, status: %d", status);
+ }
+ env->ReleaseByteArrayElements(address, addr, 0);
+}
+
+static void changeFolderPathNative(JNIEnv* env, jobject object,
+ jbyteArray address, jbyte direction,
+ jlong uid) {
+ if (!sBluetoothAvrcpInterface) return;
+ jbyte* addr = env->GetByteArrayElements(address, NULL);
+ if (!addr) {
+ jniThrowIOException(env, EINVAL);
+ return;
+ }
+
+ // jbyte* uid = env->GetByteArrayElements(uidarr, NULL);
+ // if (!uid) {
+ // jniThrowIOException(env, EINVAL);
+ // return;
+ //}
+
+ ALOGI("%s: sBluetoothAvrcpInterface: %p", __func__, sBluetoothAvrcpInterface);
+ RawAddress rawAddress;
+ rawAddress.FromOctets((uint8_t*)addr);
+
+ bt_status_t status = sBluetoothAvrcpInterface->change_folder_path_cmd(
+ rawAddress, (uint8_t)direction, (uint8_t*)&uid);
+ if (status != BT_STATUS_SUCCESS) {
+ ALOGE("Failed sending changeFolderPathNative command, status: %d", status);
+ }
+ // env->ReleaseByteArrayElements(address, addr, 0);
+}
+
+static void setBrowsedPlayerNative(JNIEnv* env, jobject object,
+ jbyteArray address, jint id) {
+ if (!sBluetoothAvrcpInterface) return;
+ jbyte* addr = env->GetByteArrayElements(address, NULL);
+ if (!addr) {
+ jniThrowIOException(env, EINVAL);
+ return;
+ }
+ RawAddress rawAddress;
+ rawAddress.FromOctets((uint8_t*)addr);
+
+ ALOGI("%s: sBluetoothAvrcpInterface: %p", __func__, sBluetoothAvrcpInterface);
+ bt_status_t status = sBluetoothAvrcpInterface->set_browsed_player_cmd(
+ rawAddress, (uint16_t)id);
+ if (status != BT_STATUS_SUCCESS) {
+ ALOGE("Failed sending setBrowsedPlayerNative command, status: %d", status);
+ }
+ env->ReleaseByteArrayElements(address, addr, 0);
+}
+
+static void setAddressedPlayerNative(JNIEnv* env, jobject object,
+ jbyteArray address, jint id) {
+ if (!sBluetoothAvrcpInterface) return;
+ jbyte* addr = env->GetByteArrayElements(address, NULL);
+ if (!addr) {
+ jniThrowIOException(env, EINVAL);
+ return;
+ }
+ RawAddress rawAddress;
+ rawAddress.FromOctets((uint8_t*)addr);
+
+ ALOGI("%s: sBluetoothAvrcpInterface: %p", __func__, sBluetoothAvrcpInterface);
+ bt_status_t status = sBluetoothAvrcpInterface->set_addressed_player_cmd(
+ rawAddress, (uint16_t)id);
+ if (status != BT_STATUS_SUCCESS) {
+ ALOGE("Failed sending setAddressedPlayerNative command, status: %d",
+ status);
+ }
+ env->ReleaseByteArrayElements(address, addr, 0);
+}
+
+static void playItemNative(JNIEnv* env, jobject object, jbyteArray address,
+ jbyte scope, jlong uid, jint uidCounter) {
+ if (!sBluetoothAvrcpInterface) return;
+ jbyte* addr = env->GetByteArrayElements(address, NULL);
+ if (!addr) {
+ jniThrowIOException(env, EINVAL);
+ return;
+ }
+
+ // jbyte* uid = env->GetByteArrayElements(uidArr, NULL);
+ // if (!uid) {
+ // jniThrowIOException(env, EINVAL);
+ // return;
+ // }
+ RawAddress rawAddress;
+ rawAddress.FromOctets((uint8_t*)addr);
+
+ ALOGI("%s: sBluetoothAvrcpInterface: %p", __func__, sBluetoothAvrcpInterface);
+ bt_status_t status = sBluetoothAvrcpInterface->play_item_cmd(
+ rawAddress, (uint8_t)scope, (uint8_t*)&uid, (uint16_t)uidCounter);
+ if (status != BT_STATUS_SUCCESS) {
+ ALOGE("Failed sending playItemNative command, status: %d", status);
+ }
+ env->ReleaseByteArrayElements(address, addr, 0);
+}
+
+static JNINativeMethod sMethods[] = {
+ {"classInitNative", "()V", (void*)classInitNative},
+ {"initNative", "()V", (void*)initNative},
+ {"cleanupNative", "()V", (void*)cleanupNative},
+ {"sendPassThroughCommandNative", "([BII)Z",
+ (void*)sendPassThroughCommandNative},
+ {"sendGroupNavigationCommandNative", "([BII)Z",
+ (void*)sendGroupNavigationCommandNative},
+ {"setPlayerApplicationSettingValuesNative", "([BB[B[B)V",
+ (void*)setPlayerApplicationSettingValuesNative},
+ {"sendAbsVolRspNative", "([BII)V", (void*)sendAbsVolRspNative},
+ {"sendRegisterAbsVolRspNative", "([BBII)V",
+ (void*)sendRegisterAbsVolRspNative},
+ {"getCurrentMetadataNative", "([B)V", (void*)getCurrentMetadataNative},
+ {"getPlaybackStateNative", "([B)V", (void*)getPlaybackStateNative},
+ {"getNowPlayingListNative", "([BII)V", (void*)getNowPlayingListNative},
+ {"getFolderListNative", "([BII)V", (void*)getFolderListNative},
+ {"getPlayerListNative", "([BII)V", (void*)getPlayerListNative},
+ {"changeFolderPathNative", "([BBJ)V", (void*)changeFolderPathNative},
+ {"playItemNative", "([BBJI)V", (void*)playItemNative},
+ {"setBrowsedPlayerNative", "([BI)V", (void*)setBrowsedPlayerNative},
+ {"setAddressedPlayerNative", "([BI)V", (void*)setAddressedPlayerNative},
+};
+
+int register_com_android_bluetooth_avrcp_controller(JNIEnv* env) {
+ return jniRegisterNativeMethods(
+ env, "com/android/bluetooth/avrcpcontroller/AvrcpControllerService",
+ sMethods, NELEM(sMethods));
+}
+}
diff --git a/android/app/jni/com_android_bluetooth_avrcp_target.cpp b/android/app/jni/com_android_bluetooth_avrcp_target.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..fb8c56086d0316b4000ca46da3d59216dc97367e
--- /dev/null
+++ b/android/app/jni/com_android_bluetooth_avrcp_target.cpp
@@ -0,0 +1,931 @@
+/*
+ * Copyright (C) 2018 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 "AvrcpTargetJni"
+
+#include
+#include
+#include