Merge 242126c60df7843460161d9189ccb09c02f74a5f on remote branch
Change-Id: I355a05383e58b8f7ab67ea596bcf29c73824fca2
diff --git a/packages_apps_bluetooth_ext/com_android_bluetooth_avrcp.cpp b/packages_apps_bluetooth_ext/com_android_bluetooth_avrcp.cpp
new file mode 100644
index 0000000..fc72517
--- /dev/null
+++ b/packages_apps_bluetooth_ext/com_android_bluetooth_avrcp.cpp
@@ -0,0 +1,1554 @@
+/*
+ * 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 "BluetoothAvrcpServiceJni"
+
+#define LOG_NDEBUG 0
+
+#include "android_runtime/AndroidRuntime.h"
+#include "com_android_bluetooth.h"
+#include "hardware/bt_rc.h"
+#include "utils/Log.h"
+
+#include <inttypes.h>
+#include <string.h>
+#include <mutex>
+#include <shared_mutex>
+
+namespace android {
+static jmethodID method_getRcFeatures;
+static jmethodID method_getPlayStatus;
+static jmethodID method_getElementAttr;
+static jmethodID method_registerNotification;
+static jmethodID method_volumeChangeCallback;
+static jmethodID method_handlePassthroughCmd;
+static jmethodID method_getFolderItemsCallback;
+static jmethodID method_setAddressedPlayerCallback;
+
+static jmethodID method_setBrowsedPlayerCallback;
+static jmethodID method_changePathCallback;
+static jmethodID method_searchCallback;
+static jmethodID method_playItemCallback;
+static jmethodID method_getItemAttrCallback;
+static jmethodID method_addToPlayListCallback;
+static jmethodID method_getTotalNumOfItemsCallback;
+
+static const btrc_interface_t* sBluetoothAvrcpInterface = NULL;
+static jobject mCallbacksObj = NULL;
+static std::shared_timed_mutex callbacks_mutex;
+
+/* Function declarations */
+static bool copy_item_attributes(JNIEnv* env, jobject object,
+ btrc_folder_items_t* pitem,
+ jint* p_attributesIds,
+ jobjectArray attributesArray, int item_idx,
+ int attribCopiedIndex);
+
+static bool copy_jstring(uint8_t* str, int maxBytes, jstring jstr, JNIEnv* env);
+
+static void cleanup_items(btrc_folder_items_t* p_items, int numItems);
+
+static void btavrcp_remote_features_callback(const RawAddress& bd_addr,
+ btrc_remote_features_t features) {
+ CallbackEnv sCallbackEnv(__func__);
+ std::shared_lock<std::shared_timed_mutex> lock(callbacks_mutex);
+ if (!sCallbackEnv.valid()) return;
+
+ if (!mCallbacksObj) {
+ ALOGE("%s: mCallbacksObj is null", __func__);
+ return;
+ }
+
+ ScopedLocalRef<jbyteArray> addr(
+ sCallbackEnv.get(), sCallbackEnv->NewByteArray(sizeof(RawAddress)));
+ if (!addr.get()) {
+ ALOGE("Unable to allocate byte array for bd_addr");
+ return;
+ }
+
+ sCallbackEnv->SetByteArrayRegion(addr.get(), 0, sizeof(RawAddress),
+ (jbyte*)bd_addr.address);
+ sCallbackEnv->CallVoidMethod(mCallbacksObj, method_getRcFeatures, addr.get(),
+ (jint)features);
+}
+
+/** Callback for play status request */
+static void btavrcp_get_play_status_callback(const RawAddress& bd_addr) {
+ CallbackEnv sCallbackEnv(__func__);
+ std::shared_lock<std::shared_timed_mutex> lock(callbacks_mutex);
+ if (!sCallbackEnv.valid()) return;
+
+ if (!mCallbacksObj) {
+ ALOGE("%s: mCallbacksObj is null", __func__);
+ return;
+ }
+
+ ScopedLocalRef<jbyteArray> addr(
+ sCallbackEnv.get(), sCallbackEnv->NewByteArray(sizeof(RawAddress)));
+ if (!addr.get()) {
+ ALOGE("Fail to new jbyteArray bd addr for get_play_status command");
+ return;
+ }
+
+ sCallbackEnv->SetByteArrayRegion(addr.get(), 0, sizeof(RawAddress),
+ (jbyte*)bd_addr.address);
+ sCallbackEnv->CallVoidMethod(mCallbacksObj, method_getPlayStatus, addr.get());
+}
+
+static void btavrcp_get_element_attr_callback(uint8_t num_attr,
+ btrc_media_attr_t* p_attrs,
+ const RawAddress& bd_addr) {
+ CallbackEnv sCallbackEnv(__func__);
+ std::shared_lock<std::shared_timed_mutex> lock(callbacks_mutex);
+ if (!sCallbackEnv.valid()) return;
+
+ if (!mCallbacksObj) {
+ ALOGE("%s: mCallbacksObj is null", __func__);
+ return;
+ }
+
+ ScopedLocalRef<jbyteArray> addr(
+ sCallbackEnv.get(), sCallbackEnv->NewByteArray(sizeof(RawAddress)));
+ if (!addr.get()) {
+ ALOGE("Fail to new jbyteArray bd addr for get_element_attr command");
+ return;
+ }
+
+ ScopedLocalRef<jintArray> attrs(
+ sCallbackEnv.get(), (jintArray)sCallbackEnv->NewIntArray(num_attr));
+ if (!attrs.get()) {
+ ALOGE("Fail to new jintArray for attrs");
+ return;
+ }
+
+ sCallbackEnv->SetIntArrayRegion(attrs.get(), 0, num_attr, (jint*)p_attrs);
+
+ sCallbackEnv->SetByteArrayRegion(addr.get(), 0, sizeof(RawAddress),
+ (jbyte*)bd_addr.address);
+ sCallbackEnv->CallVoidMethod(mCallbacksObj, method_getElementAttr, addr.get(),
+ (jbyte)num_attr, attrs.get());
+}
+
+static void btavrcp_register_notification_callback(btrc_event_id_t event_id,
+ uint32_t param,
+ const RawAddress& bd_addr) {
+ CallbackEnv sCallbackEnv(__func__);
+ std::shared_lock<std::shared_timed_mutex> lock(callbacks_mutex);
+ if (!sCallbackEnv.valid()) return;
+
+ if (!mCallbacksObj) {
+ ALOGE("%s: mCallbacksObj is null", __func__);
+ return;
+ }
+
+ ScopedLocalRef<jbyteArray> addr(
+ sCallbackEnv.get(), sCallbackEnv->NewByteArray(sizeof(RawAddress)));
+ if (!addr.get()) {
+ ALOGE("Fail to new jbyteArray bd addr for register_notification command");
+ return;
+ }
+
+ sCallbackEnv->SetByteArrayRegion(addr.get(), 0, sizeof(RawAddress),
+ (jbyte*)bd_addr.address);
+ sCallbackEnv->CallVoidMethod(mCallbacksObj, method_registerNotification,
+ addr.get(), (jint)event_id, (jint)param);
+}
+
+static void btavrcp_volume_change_callback(uint8_t volume, uint8_t ctype,
+ const RawAddress& bd_addr) {
+ CallbackEnv sCallbackEnv(__func__);
+ std::shared_lock<std::shared_timed_mutex> lock(callbacks_mutex);
+ if (!sCallbackEnv.valid()) return;
+
+ if (!mCallbacksObj) {
+ ALOGE("%s: mCallbacksObj is null", __func__);
+ return;
+ }
+
+ ScopedLocalRef<jbyteArray> addr(
+ sCallbackEnv.get(), sCallbackEnv->NewByteArray(sizeof(RawAddress)));
+ if (!addr.get()) {
+ ALOGE("Fail to new jbyteArray bd addr for volume_change command");
+ return;
+ }
+
+ sCallbackEnv->SetByteArrayRegion(addr.get(), 0, sizeof(RawAddress),
+ (jbyte*)bd_addr.address);
+
+ sCallbackEnv->CallVoidMethod(mCallbacksObj, method_volumeChangeCallback,
+ addr.get(), (jint)volume, (jint)ctype);
+}
+
+static void btavrcp_passthrough_command_callback(int id, int pressed,
+ const RawAddress& bd_addr) {
+ CallbackEnv sCallbackEnv(__func__);
+ std::shared_lock<std::shared_timed_mutex> lock(callbacks_mutex);
+ if (!sCallbackEnv.valid()) return;
+
+ if (!mCallbacksObj) {
+ ALOGE("%s: mCallbacksObj is null", __func__);
+ return;
+ }
+
+ ScopedLocalRef<jbyteArray> addr(
+ sCallbackEnv.get(), sCallbackEnv->NewByteArray(sizeof(RawAddress)));
+ if (!addr.get()) {
+ ALOGE("Fail to new jbyteArray bd addr for passthrough_command command");
+ return;
+ }
+ sCallbackEnv->SetByteArrayRegion(addr.get(), 0, sizeof(RawAddress),
+ (jbyte*)bd_addr.address);
+
+ sCallbackEnv->CallVoidMethod(mCallbacksObj, method_handlePassthroughCmd,
+ addr.get(), (jint)id, (jint)pressed);
+}
+
+static void btavrcp_set_addressed_player_callback(uint16_t player_id,
+ const RawAddress& bd_addr) {
+ CallbackEnv sCallbackEnv(__func__);
+ std::shared_lock<std::shared_timed_mutex> lock(callbacks_mutex);
+ if (!sCallbackEnv.valid()) return;
+
+ if (!mCallbacksObj) {
+ ALOGE("%s: mCallbacksObj is null", __func__);
+ return;
+ }
+
+ ScopedLocalRef<jbyteArray> addr(
+ sCallbackEnv.get(), sCallbackEnv->NewByteArray(sizeof(RawAddress)));
+ if (!addr.get()) {
+ ALOGE("Fail to new jbyteArray bd addr for set_addressed_player command");
+ return;
+ }
+
+ sCallbackEnv->SetByteArrayRegion(addr.get(), 0, sizeof(RawAddress),
+ (jbyte*)bd_addr.address);
+ sCallbackEnv->CallVoidMethod(mCallbacksObj, method_setAddressedPlayerCallback,
+ addr.get(), (jint)player_id);
+}
+
+static void btavrcp_set_browsed_player_callback(uint16_t player_id,
+ const RawAddress& bd_addr) {
+ CallbackEnv sCallbackEnv(__func__);
+ std::shared_lock<std::shared_timed_mutex> lock(callbacks_mutex);
+ if (!sCallbackEnv.valid()) return;
+ if (!mCallbacksObj) {
+ ALOGE("%s: mCallbacksObj is null", __func__);
+ return;
+ }
+
+ ScopedLocalRef<jbyteArray> addr(
+ sCallbackEnv.get(), sCallbackEnv->NewByteArray(sizeof(RawAddress)));
+ if (!addr.get()) {
+ ALOGE("Fail to new jbyteArray bd addr for set_browsed_player command");
+ return;
+ }
+ sCallbackEnv->SetByteArrayRegion(addr.get(), 0, sizeof(RawAddress),
+ (jbyte*)bd_addr.address);
+
+ sCallbackEnv->CallVoidMethod(mCallbacksObj, method_setBrowsedPlayerCallback,
+ addr.get(), (jint)player_id);
+}
+
+static void btavrcp_get_folder_items_callback(
+ uint8_t scope, uint32_t start_item, uint32_t end_item, uint8_t num_attr,
+ uint32_t* p_attr_ids, const RawAddress& bd_addr) {
+ CallbackEnv sCallbackEnv(__func__);
+ std::shared_lock<std::shared_timed_mutex> lock(callbacks_mutex);
+ if (!sCallbackEnv.valid()) return;
+
+ if (!mCallbacksObj) {
+ ALOGE("%s: mCallbacksObj is null", __func__);
+ return;
+ }
+
+ ScopedLocalRef<jbyteArray> addr(
+ sCallbackEnv.get(), sCallbackEnv->NewByteArray(sizeof(RawAddress)));
+ if (!addr.get()) {
+ ALOGE("Fail to new jbyteArray bd addr for get_folder_items command");
+ return;
+ }
+
+ uint32_t* puiAttr = (uint32_t*)p_attr_ids;
+ ScopedLocalRef<jintArray> attr_ids(sCallbackEnv.get(), NULL);
+ sCallbackEnv->SetByteArrayRegion(addr.get(), 0, sizeof(RawAddress),
+ (jbyte*)bd_addr.address);
+
+ /* check number of attributes requested by remote device */
+ if ((num_attr != BTRC_NUM_ATTR_ALL) && (num_attr != BTRC_NUM_ATTR_NONE)) {
+ /* allocate memory for attr_ids only if some attributes passed from below
+ * layer */
+ attr_ids.reset((jintArray)sCallbackEnv->NewIntArray(num_attr));
+ if (!attr_ids.get()) {
+ ALOGE("Fail to allocate new jintArray for attrs");
+ return;
+ }
+ sCallbackEnv->SetIntArrayRegion(attr_ids.get(), 0, num_attr,
+ (jint*)puiAttr);
+ }
+
+ sCallbackEnv->CallVoidMethod(
+ mCallbacksObj, method_getFolderItemsCallback, addr.get(), (jbyte)scope,
+ (jlong)start_item, (jlong)end_item, (jbyte)num_attr, attr_ids.get());
+}
+
+static void btavrcp_change_path_callback(uint8_t direction, uint8_t* folder_uid,
+ const RawAddress& bd_addr) {
+ CallbackEnv sCallbackEnv(__func__);
+ std::shared_lock<std::shared_timed_mutex> lock(callbacks_mutex);
+ if (!sCallbackEnv.valid()) return;
+
+ if (!mCallbacksObj) {
+ ALOGE("%s: mCallbacksObj is null", __func__);
+ return;
+ }
+
+ ScopedLocalRef<jbyteArray> attrs(sCallbackEnv.get(),
+ sCallbackEnv->NewByteArray(BTRC_UID_SIZE));
+ if (!attrs.get()) {
+ ALOGE("Fail to new jintArray for attrs");
+ return;
+ }
+
+ ScopedLocalRef<jbyteArray> addr(
+ sCallbackEnv.get(), sCallbackEnv->NewByteArray(sizeof(RawAddress)));
+ if (!addr.get()) {
+ ALOGE("Fail to new jbyteArray bd addr for change_path command");
+ return;
+ }
+
+ sCallbackEnv->SetByteArrayRegion(addr.get(), 0, sizeof(RawAddress),
+ (jbyte*)bd_addr.address);
+ sCallbackEnv->SetByteArrayRegion(
+ attrs.get(), 0, sizeof(uint8_t) * BTRC_UID_SIZE, (jbyte*)folder_uid);
+ sCallbackEnv->CallVoidMethod(mCallbacksObj, method_changePathCallback,
+ addr.get(), (jbyte)direction, attrs.get());
+}
+
+static void btavrcp_get_item_attr_callback(uint8_t scope, uint8_t* uid,
+ uint16_t uid_counter,
+ uint8_t num_attr,
+ btrc_media_attr_t* p_attrs,
+ const RawAddress& bd_addr) {
+ CallbackEnv sCallbackEnv(__func__);
+ std::shared_lock<std::shared_timed_mutex> lock(callbacks_mutex);
+ if (!sCallbackEnv.valid()) return;
+
+ if (!mCallbacksObj) {
+ ALOGE("%s: mCallbacksObj is null", __func__);
+ return;
+ }
+
+ ScopedLocalRef<jbyteArray> attr_uid(
+ sCallbackEnv.get(), sCallbackEnv->NewByteArray(BTRC_UID_SIZE));
+ if (!attr_uid.get()) {
+ ALOGE("Fail to new jintArray for attr_uid");
+ return;
+ }
+
+ ScopedLocalRef<jbyteArray> addr(
+ sCallbackEnv.get(), sCallbackEnv->NewByteArray(sizeof(RawAddress)));
+ if (!addr.get()) {
+ ALOGE("Fail to new jbyteArray bd addr for get_item_attr command");
+ return;
+ }
+
+ ScopedLocalRef<jintArray> attrs(
+ sCallbackEnv.get(), (jintArray)sCallbackEnv->NewIntArray(num_attr));
+ if (!attrs.get()) {
+ ALOGE("Fail to new jintArray for attrs");
+ return;
+ }
+
+ sCallbackEnv->SetByteArrayRegion(addr.get(), 0, sizeof(RawAddress),
+ (jbyte*)bd_addr.address);
+ sCallbackEnv->SetIntArrayRegion(attrs.get(), 0, num_attr, (jint*)p_attrs);
+ sCallbackEnv->SetByteArrayRegion(
+ attr_uid.get(), 0, sizeof(uint8_t) * BTRC_UID_SIZE, (jbyte*)uid);
+
+ sCallbackEnv->CallVoidMethod(mCallbacksObj, method_getItemAttrCallback,
+ addr.get(), (jbyte)scope, attr_uid.get(),
+ (jint)uid_counter, (jbyte)num_attr, attrs.get());
+}
+
+static void btavrcp_play_item_callback(uint8_t scope, uint16_t uid_counter,
+ uint8_t* uid,
+ const RawAddress& bd_addr) {
+ CallbackEnv sCallbackEnv(__func__);
+ std::shared_lock<std::shared_timed_mutex> lock(callbacks_mutex);
+ if (!sCallbackEnv.valid()) return;
+ if (!mCallbacksObj) {
+ ALOGE("%s: mCallbacksObj is null", __func__);
+ return;
+ }
+
+ ScopedLocalRef<jbyteArray> attrs(sCallbackEnv.get(),
+ sCallbackEnv->NewByteArray(BTRC_UID_SIZE));
+ if (!attrs.get()) {
+ ALOGE("%s: Fail to new jByteArray attrs for play_item command", __func__);
+ return;
+ }
+
+ ScopedLocalRef<jbyteArray> addr(
+ sCallbackEnv.get(), sCallbackEnv->NewByteArray(sizeof(RawAddress)));
+ if (!addr.get()) {
+ ALOGE("Fail to new jbyteArray bd addr for play_item command");
+ return;
+ }
+
+ sCallbackEnv->SetByteArrayRegion(addr.get(), 0, sizeof(RawAddress),
+ (jbyte*)bd_addr.address);
+ sCallbackEnv->SetByteArrayRegion(
+ attrs.get(), 0, sizeof(uint8_t) * BTRC_UID_SIZE, (jbyte*)uid);
+ sCallbackEnv->CallVoidMethod(mCallbacksObj, method_playItemCallback,
+ addr.get(), (jbyte)scope, (jint)uid_counter,
+ attrs.get());
+}
+
+static void btavrcp_get_total_num_items_callback(uint8_t scope,
+ const RawAddress& bd_addr) {
+ CallbackEnv sCallbackEnv(__func__);
+ std::shared_lock<std::shared_timed_mutex> lock(callbacks_mutex);
+ if (!sCallbackEnv.valid()) return;
+ if (!mCallbacksObj) {
+ ALOGE("%s: mCallbacksObj is null", __func__);
+ return;
+ }
+
+ ScopedLocalRef<jbyteArray> addr(
+ sCallbackEnv.get(), sCallbackEnv->NewByteArray(sizeof(RawAddress)));
+ if (!addr.get()) {
+ ALOGE("Fail to new jbyteArray bd addr for get_total_num_items command");
+ return;
+ }
+
+ sCallbackEnv->SetByteArrayRegion(addr.get(), 0, sizeof(RawAddress),
+ (jbyte*)bd_addr.address);
+ sCallbackEnv->CallVoidMethod(mCallbacksObj, method_getTotalNumOfItemsCallback,
+ addr.get(), (jbyte)scope);
+}
+
+static void btavrcp_search_callback(uint16_t charset_id, uint16_t str_len,
+ uint8_t* p_str, const RawAddress& bd_addr) {
+ CallbackEnv sCallbackEnv(__func__);
+ std::shared_lock<std::shared_timed_mutex> lock(callbacks_mutex);
+ if (!sCallbackEnv.valid()) return;
+ if (!mCallbacksObj) {
+ ALOGE("%s: mCallbacksObj is null", __func__);
+ return;
+ }
+
+ ScopedLocalRef<jbyteArray> attrs(sCallbackEnv.get(),
+ sCallbackEnv->NewByteArray(str_len));
+ if (!attrs.get()) {
+ ALOGE("Fail to new jintArray for attrs");
+ return;
+ }
+
+ ScopedLocalRef<jbyteArray> addr(
+ sCallbackEnv.get(), sCallbackEnv->NewByteArray(sizeof(RawAddress)));
+ if (!addr.get()) {
+ ALOGE("Fail to new jbyteArray bd addr for search command");
+ return;
+ }
+
+ sCallbackEnv->SetByteArrayRegion(addr.get(), 0, sizeof(RawAddress),
+ (jbyte*)bd_addr.address);
+ sCallbackEnv->SetByteArrayRegion(attrs.get(), 0, str_len * sizeof(uint8_t),
+ (jbyte*)p_str);
+ sCallbackEnv->CallVoidMethod(mCallbacksObj, method_searchCallback, addr.get(),
+ (jint)charset_id, attrs.get());
+}
+
+static void btavrcp_add_to_play_list_callback(uint8_t scope, uint8_t* uid,
+ uint16_t uid_counter,
+ const RawAddress& bd_addr) {
+ CallbackEnv sCallbackEnv(__func__);
+ std::shared_lock<std::shared_timed_mutex> lock(callbacks_mutex);
+ if (!sCallbackEnv.valid()) return;
+ if (!mCallbacksObj) {
+ ALOGE("%s: mCallbacksObj is null", __func__);
+ return;
+ }
+
+ ScopedLocalRef<jbyteArray> addr(
+ sCallbackEnv.get(), sCallbackEnv->NewByteArray(sizeof(RawAddress)));
+ if (!addr.get()) {
+ ALOGE("Fail to new jbyteArray bd addr for add_to_play_list command");
+ return;
+ }
+
+ ScopedLocalRef<jbyteArray> attrs(sCallbackEnv.get(),
+ sCallbackEnv->NewByteArray(BTRC_UID_SIZE));
+ if (!attrs.get()) {
+ ALOGE("Fail to new jByteArray for attrs");
+ return;
+ }
+
+ sCallbackEnv->SetByteArrayRegion(addr.get(), 0, sizeof(RawAddress),
+ (jbyte*)bd_addr.address);
+ sCallbackEnv->SetByteArrayRegion(
+ attrs.get(), 0, sizeof(uint8_t) * BTRC_UID_SIZE, (jbyte*)uid);
+ sCallbackEnv->CallVoidMethod(mCallbacksObj, method_addToPlayListCallback,
+ addr.get(), (jbyte)scope, attrs.get(),
+ (jint)uid_counter);
+}
+
+static btrc_callbacks_t sBluetoothAvrcpCallbacks = {
+ sizeof(sBluetoothAvrcpCallbacks),
+ btavrcp_remote_features_callback,
+ btavrcp_get_play_status_callback,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ btavrcp_get_element_attr_callback,
+ btavrcp_register_notification_callback,
+ btavrcp_volume_change_callback,
+ btavrcp_passthrough_command_callback,
+ btavrcp_set_addressed_player_callback,
+ btavrcp_set_browsed_player_callback,
+ btavrcp_get_folder_items_callback,
+ btavrcp_change_path_callback,
+ btavrcp_get_item_attr_callback,
+ btavrcp_play_item_callback,
+ btavrcp_get_total_num_items_callback,
+ btavrcp_search_callback,
+ btavrcp_add_to_play_list_callback,
+};
+
+static void classInitNative(JNIEnv* env, jclass clazz) {
+ method_getRcFeatures =
+ env->GetMethodID(clazz, "getRcFeaturesRequestFromNative", "([BI)V");
+ method_getPlayStatus =
+ env->GetMethodID(clazz, "getPlayStatusRequestFromNative", "([B)V");
+
+ method_getElementAttr =
+ env->GetMethodID(clazz, "getElementAttrRequestFromNative", "([BB[I)V");
+
+ method_registerNotification = env->GetMethodID(
+ clazz, "registerNotificationRequestFromNative", "([BII)V");
+
+ method_volumeChangeCallback =
+ env->GetMethodID(clazz, "volumeChangeRequestFromNative", "([BII)V");
+
+ method_handlePassthroughCmd = env->GetMethodID(
+ clazz, "handlePassthroughCmdRequestFromNative", "([BII)V");
+
+ method_setAddressedPlayerCallback =
+ env->GetMethodID(clazz, "setAddressedPlayerRequestFromNative", "([BI)V");
+
+ method_setBrowsedPlayerCallback =
+ env->GetMethodID(clazz, "setBrowsedPlayerRequestFromNative", "([BI)V");
+
+ method_getFolderItemsCallback =
+ env->GetMethodID(clazz, "getFolderItemsRequestFromNative", "([BBJJB[I)V");
+
+ method_changePathCallback =
+ env->GetMethodID(clazz, "changePathRequestFromNative", "([BB[B)V");
+
+ method_getItemAttrCallback =
+ env->GetMethodID(clazz, "getItemAttrRequestFromNative", "([BB[BIB[I)V");
+
+ method_playItemCallback =
+ env->GetMethodID(clazz, "playItemRequestFromNative", "([BBI[B)V");
+
+ method_getTotalNumOfItemsCallback =
+ env->GetMethodID(clazz, "getTotalNumOfItemsRequestFromNative", "([BB)V");
+
+ method_searchCallback =
+ env->GetMethodID(clazz, "searchRequestFromNative", "([BI[B)V");
+
+ method_addToPlayListCallback =
+ env->GetMethodID(clazz, "addToPlayListRequestFromNative", "([BB[BI)V");
+
+ ALOGI("%s: succeeds", __func__);
+}
+
+static void initNative(JNIEnv* env, jobject object) {
+ std::unique_lock<std::shared_timed_mutex> lock(callbacks_mutex);
+ 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 (mCallbacksObj != NULL) {
+ ALOGW("Cleaning up Avrcp callback object");
+ env->DeleteGlobalRef(mCallbacksObj);
+ mCallbacksObj = NULL;
+ }
+
+ sBluetoothAvrcpInterface =
+ (btrc_interface_t*)btInf->get_profile_interface(BT_PROFILE_AV_RC_ID);
+ if (sBluetoothAvrcpInterface == NULL) {
+ ALOGE("Failed to get Bluetooth Avrcp Interface");
+ return;
+ }
+
+ bt_status_t status =
+ sBluetoothAvrcpInterface->init(&sBluetoothAvrcpCallbacks);
+ if (status != BT_STATUS_SUCCESS) {
+ ALOGE("Failed to initialize Bluetooth Avrcp, status: %d", status);
+ sBluetoothAvrcpInterface = NULL;
+ return;
+ }
+
+ mCallbacksObj = env->NewGlobalRef(object);
+}
+
+static void cleanupNative(JNIEnv* env, jobject object) {
+ std::unique_lock<std::shared_timed_mutex> lock(callbacks_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 (mCallbacksObj != NULL) {
+ env->DeleteGlobalRef(mCallbacksObj);
+ mCallbacksObj = NULL;
+ }
+}
+
+static jboolean getPlayStatusRspNative(JNIEnv* env, jobject object,
+ jbyteArray address, jint playStatus,
+ jint songLen, jint songPos) {
+ if (!sBluetoothAvrcpInterface) {
+ ALOGE("%s: sBluetoothAvrcpInterface is null", __func__);
+ return JNI_FALSE;
+ }
+
+ 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->get_play_status_rsp(
+ rawAddress, (btrc_play_status_t)playStatus, songLen, songPos);
+ if (status != BT_STATUS_SUCCESS) {
+ ALOGE("Failed get_play_status_rsp, status: %d", status);
+ }
+ env->ReleaseByteArrayElements(address, addr, 0);
+ return (status == BT_STATUS_SUCCESS) ? JNI_TRUE : JNI_FALSE;
+}
+
+static jboolean getElementAttrRspNative(JNIEnv* env, jobject object,
+ jbyteArray address, jbyte numAttr,
+ jintArray attrIds,
+ jobjectArray textArray) {
+ if (!sBluetoothAvrcpInterface) {
+ ALOGE("%s: sBluetoothAvrcpInterface is null", __func__);
+ return JNI_FALSE;
+ }
+
+ if (numAttr > BTRC_MAX_ELEM_ATTR_SIZE) {
+ ALOGE("get_element_attr_rsp: number of attributes exceed maximum");
+ return JNI_FALSE;
+ }
+
+ jbyte* addr = env->GetByteArrayElements(address, NULL);
+ if (!addr) {
+ jniThrowIOException(env, EINVAL);
+ return JNI_FALSE;
+ }
+
+ btrc_element_attr_val_t* pAttrs = new btrc_element_attr_val_t[numAttr];
+ if (!pAttrs) {
+ ALOGE("get_element_attr_rsp: not have enough memeory");
+ env->ReleaseByteArrayElements(address, addr, 0);
+ return JNI_FALSE;
+ }
+
+ jint* attr = env->GetIntArrayElements(attrIds, NULL);
+ if (!attr) {
+ delete[] pAttrs;
+ jniThrowIOException(env, EINVAL);
+ env->ReleaseByteArrayElements(address, addr, 0);
+ return JNI_FALSE;
+ }
+
+ int attr_cnt;
+ for (attr_cnt = 0; attr_cnt < numAttr; ++attr_cnt) {
+ pAttrs[attr_cnt].attr_id = attr[attr_cnt];
+ ScopedLocalRef<jstring> text(
+ env, (jstring)env->GetObjectArrayElement(textArray, attr_cnt));
+
+ if (!copy_jstring(pAttrs[attr_cnt].text, BTRC_MAX_ATTR_STR_LEN, text.get(),
+ env)) {
+ break;
+ }
+ }
+
+ if (attr_cnt < numAttr) {
+ delete[] pAttrs;
+ env->ReleaseIntArrayElements(attrIds, attr, 0);
+ ALOGE("%s: Failed to copy attributes", __func__);
+ return JNI_FALSE;
+ }
+
+ RawAddress rawAddress;
+ rawAddress.FromOctets((uint8_t*)addr);
+ bt_status_t status = sBluetoothAvrcpInterface->get_element_attr_rsp(
+ rawAddress, numAttr, pAttrs);
+ if (status != BT_STATUS_SUCCESS) {
+ ALOGE("Failed get_element_attr_rsp, status: %d", status);
+ }
+
+ delete[] pAttrs;
+ env->ReleaseIntArrayElements(attrIds, attr, 0);
+ env->ReleaseByteArrayElements(address, addr, 0);
+ return (status == BT_STATUS_SUCCESS) ? JNI_TRUE : JNI_FALSE;
+}
+
+static jboolean getItemAttrRspNative(JNIEnv* env, jobject object,
+ jbyteArray address, jint rspStatus,
+ jbyte numAttr, jintArray attrIds,
+ jobjectArray textArray) {
+ if (!sBluetoothAvrcpInterface) {
+ ALOGE("%s: sBluetoothAvrcpInterface is null", __func__);
+ return JNI_FALSE;
+ }
+
+ jbyte* addr = env->GetByteArrayElements(address, NULL);
+ if (!addr) {
+ jniThrowIOException(env, EINVAL);
+ return JNI_FALSE;
+ }
+
+ if (numAttr > BTRC_MAX_ELEM_ATTR_SIZE) {
+ ALOGE("get_element_attr_rsp: number of attributes exceed maximum");
+ return JNI_FALSE;
+ }
+
+ btrc_element_attr_val_t* pAttrs = new btrc_element_attr_val_t[numAttr];
+ if (!pAttrs) {
+ ALOGE("%s: not have enough memory", __func__);
+ env->ReleaseByteArrayElements(address, addr, 0);
+ return JNI_FALSE;
+ }
+
+ jint* attr = NULL;
+ if (attrIds != NULL) {
+ attr = env->GetIntArrayElements(attrIds, NULL);
+ if (!attr) {
+ delete[] pAttrs;
+ jniThrowIOException(env, EINVAL);
+ env->ReleaseByteArrayElements(address, addr, 0);
+ return JNI_FALSE;
+ }
+
+ for (int attr_cnt = 0; attr_cnt < numAttr; ++attr_cnt) {
+ pAttrs[attr_cnt].attr_id = attr[attr_cnt];
+ ScopedLocalRef<jstring> text(
+ env, (jstring)env->GetObjectArrayElement(textArray, attr_cnt));
+
+ if (!copy_jstring(pAttrs[attr_cnt].text, BTRC_MAX_ATTR_STR_LEN, text.get(),
+ env)) {
+ rspStatus = BTRC_STS_INTERNAL_ERR;
+ ALOGE("%s: Failed to copy attributes", __func__);
+ break;
+ }
+ }
+ }
+ RawAddress rawAddress;
+ rawAddress.FromOctets((uint8_t*)addr);
+
+ bt_status_t status = sBluetoothAvrcpInterface->get_item_attr_rsp(
+ rawAddress, (btrc_status_t)rspStatus, numAttr, pAttrs);
+ if (status != BT_STATUS_SUCCESS)
+ ALOGE("Failed get_item_attr_rsp, status: %d", status);
+
+ if (pAttrs) delete[] pAttrs;
+ if (attr) env->ReleaseIntArrayElements(attrIds, attr, 0);
+ env->ReleaseByteArrayElements(address, addr, 0);
+
+ return (status == BT_STATUS_SUCCESS) ? JNI_TRUE : JNI_FALSE;
+}
+
+static jboolean registerNotificationRspPlayStatusNative(JNIEnv* env,
+ jobject object,
+ jint type,
+ jint playStatus) {
+ if (!sBluetoothAvrcpInterface) {
+ ALOGE("%s: sBluetoothAvrcpInterface is null", __func__);
+ return JNI_FALSE;
+ }
+
+ btrc_register_notification_t param;
+ param.play_status = (btrc_play_status_t)playStatus;
+
+ bt_status_t status = sBluetoothAvrcpInterface->register_notification_rsp(
+ BTRC_EVT_PLAY_STATUS_CHANGED, (btrc_notification_type_t)type, ¶m);
+ if (status != BT_STATUS_SUCCESS) {
+ ALOGE("Failed register_notification_rsp play status, status: %d", status);
+ }
+
+ return (status == BT_STATUS_SUCCESS) ? JNI_TRUE : JNI_FALSE;
+}
+
+static jboolean registerNotificationRspTrackChangeNative(JNIEnv* env,
+ jobject object,
+ jint type,
+ jbyteArray track) {
+ if (!sBluetoothAvrcpInterface) {
+ ALOGE("%s: sBluetoothAvrcpInterface is null", __func__);
+ return JNI_FALSE;
+ }
+
+ jbyte* trk = env->GetByteArrayElements(track, NULL);
+ if (!trk) {
+ jniThrowIOException(env, EINVAL);
+ return JNI_FALSE;
+ }
+
+ btrc_register_notification_t param;
+ uint64_t uid = 0;
+ for (int uid_idx = 0; uid_idx < BTRC_UID_SIZE; ++uid_idx) {
+ param.track[uid_idx] = trk[uid_idx];
+ uid = uid + (trk[uid_idx] << (BTRC_UID_SIZE - 1 - uid_idx));
+ }
+
+ ALOGV("%s: Sending track change notification: %d -> %" PRIu64, __func__, type,
+ uid);
+
+ bt_status_t status = sBluetoothAvrcpInterface->register_notification_rsp(
+ BTRC_EVT_TRACK_CHANGE, (btrc_notification_type_t)type, ¶m);
+ if (status != BT_STATUS_SUCCESS) {
+ ALOGE("Failed register_notification_rsp track change, status: %d", status);
+ }
+
+ env->ReleaseByteArrayElements(track, trk, 0);
+ return (status == BT_STATUS_SUCCESS) ? JNI_TRUE : JNI_FALSE;
+}
+
+static jboolean registerNotificationRspPlayPosNative(JNIEnv* env,
+ jobject object, jint type,
+ jint playPos) {
+ if (!sBluetoothAvrcpInterface) {
+ ALOGE("%s: sBluetoothAvrcpInterface is null", __func__);
+ return JNI_FALSE;
+ }
+
+ btrc_register_notification_t param;
+ param.song_pos = (uint32_t)playPos;
+
+ bt_status_t status = sBluetoothAvrcpInterface->register_notification_rsp(
+ BTRC_EVT_PLAY_POS_CHANGED, (btrc_notification_type_t)type, ¶m);
+ if (status != BT_STATUS_SUCCESS) {
+ ALOGE("Failed register_notification_rsp play position, status: %d", status);
+ }
+
+ return (status == BT_STATUS_SUCCESS) ? JNI_TRUE : JNI_FALSE;
+}
+
+static jboolean registerNotificationRspNowPlayingChangedNative(JNIEnv* env,
+ jobject object,
+ jint type) {
+ if (!sBluetoothAvrcpInterface) {
+ ALOGE("%s: sBluetoothAvrcpInterface is null", __func__);
+ return JNI_FALSE;
+ }
+
+ btrc_register_notification_t param;
+ bt_status_t status = sBluetoothAvrcpInterface->register_notification_rsp(
+ BTRC_EVT_NOW_PLAYING_CONTENT_CHANGED, (btrc_notification_type_t)type,
+ ¶m);
+ if (status != BT_STATUS_SUCCESS) {
+ ALOGE("Failed register_notification_rsp, nowPlaying Content status: %d",
+ status);
+ }
+ return (status == BT_STATUS_SUCCESS) ? JNI_TRUE : JNI_FALSE;
+}
+
+static jboolean registerNotificationRspUIDsChangedNative(JNIEnv* env,
+ jobject object,
+ jint type,
+ jint uidCounter) {
+ if (!sBluetoothAvrcpInterface) {
+ ALOGE("%s: sBluetoothAvrcpInterface is null", __func__);
+ return JNI_FALSE;
+ }
+
+ btrc_register_notification_t param;
+ param.uids_changed.uid_counter = (uint16_t)uidCounter;
+
+ bt_status_t status = sBluetoothAvrcpInterface->register_notification_rsp(
+ BTRC_EVT_UIDS_CHANGED, (btrc_notification_type_t)type, ¶m);
+ if (status != BT_STATUS_SUCCESS) {
+ ALOGE("Failed register_notification_rsp, uids changed status: %d", status);
+ }
+
+ return (status == BT_STATUS_SUCCESS) ? JNI_TRUE : JNI_FALSE;
+}
+
+static jboolean registerNotificationRspAddrPlayerChangedNative(
+ JNIEnv* env, jobject object, jint type, jint playerId, jint uidCounter) {
+ if (!sBluetoothAvrcpInterface) {
+ ALOGE("%s: sBluetoothAvrcpInterface is null", __func__);
+ return JNI_FALSE;
+ }
+
+ btrc_register_notification_t param;
+ param.addr_player_changed.player_id = (uint16_t)playerId;
+ param.addr_player_changed.uid_counter = (uint16_t)uidCounter;
+
+ bt_status_t status = sBluetoothAvrcpInterface->register_notification_rsp(
+ BTRC_EVT_ADDR_PLAYER_CHANGE, (btrc_notification_type_t)type, ¶m);
+ if (status != BT_STATUS_SUCCESS) {
+ ALOGE("Failed register_notification_rsp address player changed status: %d",
+ status);
+ }
+
+ return (status == BT_STATUS_SUCCESS) ? JNI_TRUE : JNI_FALSE;
+}
+
+static jboolean registerNotificationRspAvalPlayerChangedNative(JNIEnv* env,
+ jobject object,
+ jint type) {
+ if (!sBluetoothAvrcpInterface) {
+ ALOGE("%s: sBluetoothAvrcpInterface is null", __func__);
+ return JNI_FALSE;
+ }
+
+ btrc_register_notification_t param;
+ bt_status_t status = sBluetoothAvrcpInterface->register_notification_rsp(
+ BTRC_EVT_AVAL_PLAYER_CHANGE, (btrc_notification_type_t)type, ¶m);
+ if (status != BT_STATUS_SUCCESS) {
+ ALOGE(
+ "Failed register_notification_rsp available player changed status, "
+ "status: %d",
+ status);
+ }
+
+ return (status == BT_STATUS_SUCCESS) ? JNI_TRUE : JNI_FALSE;
+}
+
+static jboolean setVolumeNative(JNIEnv* env, jobject object, jint volume) {
+ if (!sBluetoothAvrcpInterface) {
+ ALOGE("%s: sBluetoothAvrcpInterface is null", __func__);
+ return JNI_FALSE;
+ }
+
+ bt_status_t status = sBluetoothAvrcpInterface->set_volume((uint8_t)volume);
+ if (status != BT_STATUS_SUCCESS) {
+ ALOGE("Failed set_volume, status: %d", status);
+ }
+
+ return (status == BT_STATUS_SUCCESS) ? JNI_TRUE : JNI_FALSE;
+}
+
+/* native response for scope as Media player */
+static jboolean mediaPlayerListRspNative(
+ JNIEnv* env, jobject object, jbyteArray address, jint rspStatus,
+ jint uidCounter, jbyte itemType, jint numItems, jintArray playerIds,
+ jbyteArray playerTypes, jintArray playerSubtypes,
+ jbyteArray playStatusValues, jshortArray featureBitmask,
+ jobjectArray textArray) {
+ if (!sBluetoothAvrcpInterface) {
+ ALOGE("%s: sBluetoothAvrcpInterface is null", __func__);
+ return JNI_FALSE;
+ }
+
+ jbyte* addr = env->GetByteArrayElements(address, NULL);
+ if (!addr) {
+ jniThrowIOException(env, EINVAL);
+ return JNI_FALSE;
+ }
+
+ jbyte *p_playerTypes = NULL, *p_PlayStatusValues = NULL;
+ jshort* p_FeatBitMaskValues = NULL;
+ jint *p_playerIds = NULL, *p_playerSubTypes = NULL;
+ btrc_folder_items_t* p_items = NULL;
+ if (rspStatus == BTRC_STS_NO_ERROR) {
+ /* allocate memory */
+ p_playerIds = env->GetIntArrayElements(playerIds, NULL);
+ p_playerTypes = env->GetByteArrayElements(playerTypes, NULL);
+ p_playerSubTypes = env->GetIntArrayElements(playerSubtypes, NULL);
+ p_PlayStatusValues = env->GetByteArrayElements(playStatusValues, NULL);
+ p_FeatBitMaskValues = env->GetShortArrayElements(featureBitmask, NULL);
+ p_items = new btrc_folder_items_t[numItems];
+ /* deallocate memory and return if allocation failed */
+ if (!p_playerIds || !p_playerTypes || !p_playerSubTypes ||
+ !p_PlayStatusValues || !p_FeatBitMaskValues || !p_items) {
+ if (p_playerIds) env->ReleaseIntArrayElements(playerIds, p_playerIds, 0);
+ if (p_playerTypes)
+ env->ReleaseByteArrayElements(playerTypes, p_playerTypes, 0);
+ if (p_playerSubTypes)
+ env->ReleaseIntArrayElements(playerSubtypes, p_playerSubTypes, 0);
+ if (p_PlayStatusValues)
+ env->ReleaseByteArrayElements(playStatusValues, p_PlayStatusValues, 0);
+ if (p_FeatBitMaskValues)
+ env->ReleaseShortArrayElements(featureBitmask, p_FeatBitMaskValues, 0);
+ if (p_items) delete[] p_items;
+
+ jniThrowIOException(env, EINVAL);
+ ALOGE("%s: not have enough memory", __func__);
+ return JNI_FALSE;
+ }
+
+ p_items->item_type = (uint8_t)itemType;
+
+ /* copy list of media players along with other parameters */
+ int itemIdx;
+ for (itemIdx = 0; itemIdx < numItems; ++itemIdx) {
+ p_items[itemIdx].player.player_id = p_playerIds[itemIdx];
+ p_items[itemIdx].player.major_type = p_playerTypes[itemIdx];
+ p_items[itemIdx].player.sub_type = p_playerSubTypes[itemIdx];
+ p_items[itemIdx].player.play_status = p_PlayStatusValues[itemIdx];
+ p_items[itemIdx].player.charset_id = BTRC_CHARSET_ID_UTF8;
+
+ ScopedLocalRef<jstring> text(
+ env, (jstring)env->GetObjectArrayElement(textArray, itemIdx));
+ /* copy player name */
+ if (!copy_jstring(p_items[itemIdx].player.name, BTRC_MAX_ATTR_STR_LEN,
+ text.get(), env))
+ break;
+
+ /* Feature bit mask is 128-bit value each */
+ for (int InnCnt = 0; InnCnt < 16; InnCnt++) {
+ p_items[itemIdx].player.features[InnCnt] =
+ (uint8_t)p_FeatBitMaskValues[(itemIdx * 16) + InnCnt];
+ }
+ }
+
+ /* failed to copy list of media players */
+ if (itemIdx < numItems) {
+ rspStatus = BTRC_STS_INTERNAL_ERR;
+ ALOGE("%s: Failed to copy Media player attributes", __func__);
+ }
+ }
+
+ RawAddress* btAddr = (RawAddress*)addr;
+ bt_status_t status = sBluetoothAvrcpInterface->get_folder_items_list_rsp(
+ *btAddr, (btrc_status_t)rspStatus, uidCounter, numItems, p_items);
+ if (status != BT_STATUS_SUCCESS) {
+ ALOGE("Failed get_folder_items_list_rsp, status: %d", status);
+ }
+
+ /* release allocated memory */
+ if (p_items) delete[] p_items;
+ if (p_playerTypes)
+ env->ReleaseByteArrayElements(playerTypes, p_playerTypes, 0);
+ if (p_playerSubTypes)
+ env->ReleaseIntArrayElements(playerSubtypes, p_playerSubTypes, 0);
+ if (p_PlayStatusValues)
+ env->ReleaseByteArrayElements(playStatusValues, p_PlayStatusValues, 0);
+ if (p_FeatBitMaskValues) {
+ env->ReleaseShortArrayElements(featureBitmask, p_FeatBitMaskValues, 0);
+ }
+ env->ReleaseByteArrayElements(address, addr, 0);
+
+ return (status == BT_STATUS_SUCCESS) ? JNI_TRUE : JNI_FALSE;
+}
+
+static jboolean getFolderItemsRspNative(
+ JNIEnv* env, jobject object, jbyteArray address, jint rspStatus,
+ jshort uidCounter, jbyte scope, jint numItems, jbyteArray folderType,
+ jbyteArray playable, jbyteArray itemType, jbyteArray itemUidArray,
+ jobjectArray displayNameArray, jintArray numAttrs, jintArray attributesIds,
+ jobjectArray attributesArray) {
+ if (!sBluetoothAvrcpInterface) {
+ ALOGE("%s: sBluetoothAvrcpInterface is null", __func__);
+ return JNI_FALSE;
+ }
+
+ jbyte* addr = env->GetByteArrayElements(address, NULL);
+ if (!addr) {
+ jniThrowIOException(env, EINVAL);
+ return JNI_FALSE;
+ }
+
+ jbyte *p_playable = NULL, *p_item_uid = NULL;
+ jbyte* p_item_types = NULL; /* Folder or Media Item */
+ jint* p_attributesIds = NULL;
+ jbyte* p_folder_types =
+ NULL; /* Folder properties like Album/Genre/Artists etc */
+ jint* p_num_attrs = NULL;
+ btrc_folder_items_t* p_items = NULL;
+ /* none of the parameters should be null when no error */
+ if (rspStatus == BTRC_STS_NO_ERROR) {
+ /* allocate memory to each rsp item */
+ if (folderType != NULL)
+ p_folder_types = env->GetByteArrayElements(folderType, NULL);
+ if (playable != NULL)
+ p_playable = env->GetByteArrayElements(playable, NULL);
+ if (itemType != NULL)
+ p_item_types = env->GetByteArrayElements(itemType, NULL);
+ if (NULL != numAttrs)
+ p_num_attrs = env->GetIntArrayElements(numAttrs, NULL);
+ if (NULL != attributesIds)
+ p_attributesIds = env->GetIntArrayElements(attributesIds, NULL);
+ if (itemUidArray != NULL)
+ p_item_uid = (jbyte*)env->GetByteArrayElements(itemUidArray, NULL);
+
+ p_items = new btrc_folder_items_t[numItems];
+
+ /* if memory alloc failed, release memory */
+ if (p_items && p_folder_types && p_playable && p_item_types && p_item_uid &&
+ /* attributes can be null if remote requests 0 attributes */
+ ((numAttrs != NULL && p_num_attrs) || (!numAttrs && !p_num_attrs)) &&
+ ((attributesIds != NULL && p_attributesIds) ||
+ (!attributesIds && !p_attributesIds))) {
+ memset(p_items, 0, sizeof(btrc_folder_items_t) * numItems);
+ if (scope == BTRC_SCOPE_FILE_SYSTEM || scope == BTRC_SCOPE_SEARCH ||
+ scope == BTRC_SCOPE_NOW_PLAYING) {
+ int attribCopiedIndex = 0;
+ for (int item_idx = 0; item_idx < numItems; item_idx++) {
+ if (BTRC_ITEM_FOLDER == p_item_types[item_idx]) {
+ btrc_folder_items_t* pitem = &p_items[item_idx];
+
+ memcpy(pitem->folder.uid, p_item_uid + item_idx * BTRC_UID_SIZE,
+ BTRC_UID_SIZE);
+ pitem->item_type = (uint8_t)BTRC_ITEM_FOLDER;
+ pitem->folder.charset_id = BTRC_CHARSET_ID_UTF8;
+ pitem->folder.type = p_folder_types[item_idx];
+ pitem->folder.playable = p_playable[item_idx];
+
+ ScopedLocalRef<jstring> text(
+ env, (jstring)env->GetObjectArrayElement(displayNameArray,
+ item_idx));
+ if (!copy_jstring(pitem->folder.name, BTRC_MAX_ATTR_STR_LEN,
+ text.get(), env)) {
+ rspStatus = BTRC_STS_INTERNAL_ERR;
+ ALOGE("%s: failed to copy display name of folder item", __func__);
+ break;
+ }
+ } else if (BTRC_ITEM_MEDIA == p_item_types[item_idx]) {
+ btrc_folder_items_t* pitem = &p_items[item_idx];
+ memcpy(pitem->media.uid, p_item_uid + item_idx * BTRC_UID_SIZE,
+ BTRC_UID_SIZE);
+
+ pitem->item_type = (uint8_t)BTRC_ITEM_MEDIA;
+ pitem->media.charset_id = BTRC_CHARSET_ID_UTF8;
+ pitem->media.type = BTRC_MEDIA_TYPE_AUDIO;
+ pitem->media.num_attrs =
+ (p_num_attrs != NULL) ? p_num_attrs[item_idx] : 0;
+
+ ScopedLocalRef<jstring> text(
+ env, (jstring)env->GetObjectArrayElement(displayNameArray,
+ item_idx));
+ if (!copy_jstring(pitem->media.name, BTRC_MAX_ATTR_STR_LEN,
+ text.get(), env)) {
+ rspStatus = BTRC_STS_INTERNAL_ERR;
+ ALOGE("%s: failed to copy display name of media item", __func__);
+ break;
+ }
+
+ /* copy item attributes */
+ if (!copy_item_attributes(env, object, pitem, p_attributesIds,
+ attributesArray, item_idx,
+ attribCopiedIndex)) {
+ ALOGE("%s: error in copying attributes of item = %s", __func__,
+ pitem->media.name);
+ rspStatus = BTRC_STS_INTERNAL_ERR;
+ break;
+ }
+ attribCopiedIndex += pitem->media.num_attrs;
+ }
+ }
+ }
+ } else {
+ rspStatus = BTRC_STS_INTERNAL_ERR;
+ ALOGE("%s: unable to allocate memory", __func__);
+ }
+ }
+
+ RawAddress* btAddr = (RawAddress*)addr;
+ bt_status_t status = sBluetoothAvrcpInterface->get_folder_items_list_rsp(
+ *btAddr, (btrc_status_t)rspStatus, uidCounter, numItems, p_items);
+ if (status != BT_STATUS_SUCCESS)
+ ALOGE("Failed get_folder_items_list_rsp, status: %d", status);
+
+ /* Release allocated memory for all attributes in each media item */
+ if (p_items) cleanup_items(p_items, numItems);
+
+ /* Release allocated memory */
+ if (p_folder_types)
+ env->ReleaseByteArrayElements(folderType, p_folder_types, 0);
+ if (p_playable) env->ReleaseByteArrayElements(playable, p_playable, 0);
+ if (p_item_types) env->ReleaseByteArrayElements(itemType, p_item_types, 0);
+ if (p_num_attrs) env->ReleaseIntArrayElements(numAttrs, p_num_attrs, 0);
+ if (p_attributesIds)
+ env->ReleaseIntArrayElements(attributesIds, p_attributesIds, 0);
+ if (p_item_uid) env->ReleaseByteArrayElements(itemUidArray, p_item_uid, 0);
+ if (p_items) delete[] p_items;
+ env->ReleaseByteArrayElements(address, addr, 0);
+
+ return (status == BT_STATUS_SUCCESS) ? JNI_TRUE : JNI_FALSE;
+}
+
+static jboolean setAddressedPlayerRspNative(JNIEnv* env, jobject object,
+ jbyteArray address,
+ jint rspStatus) {
+ if (!sBluetoothAvrcpInterface) {
+ ALOGE("%s: sBluetoothAvrcpInterface is null", __func__);
+ return JNI_FALSE;
+ }
+
+ jbyte* addr = env->GetByteArrayElements(address, NULL);
+ if (!addr) {
+ jniThrowIOException(env, EINVAL);
+ return JNI_FALSE;
+ }
+
+ RawAddress* btAddr = (RawAddress*)addr;
+ bt_status_t status = sBluetoothAvrcpInterface->set_addressed_player_rsp(
+ *btAddr, (btrc_status_t)rspStatus);
+ if (status != BT_STATUS_SUCCESS) {
+ ALOGE("Failed set_addressed_player_rsp, status: %d", status);
+ }
+ env->ReleaseByteArrayElements(address, addr, 0);
+
+ return (status == BT_STATUS_SUCCESS) ? JNI_TRUE : JNI_FALSE;
+}
+
+static jboolean setBrowsedPlayerRspNative(JNIEnv* env, jobject object,
+ jbyteArray address, jint rspStatus,
+ jbyte depth, jint numItems,
+ jobjectArray textArray) {
+ if (!sBluetoothAvrcpInterface) {
+ ALOGE("%s: sBluetoothAvrcpInterface is null", __func__);
+ return JNI_FALSE;
+ }
+
+ jbyte* addr = env->GetByteArrayElements(address, NULL);
+ if (!addr) {
+ jniThrowIOException(env, EINVAL);
+ return JNI_FALSE;
+ }
+
+ btrc_br_folder_name_t* p_folders = NULL;
+ if (rspStatus == BTRC_STS_NO_ERROR) {
+ if (depth > 0) {
+ p_folders = new btrc_br_folder_name_t[depth];
+
+ if (!p_folders ) {
+ jniThrowIOException(env, EINVAL);
+ ALOGE("%s: not have enough memeory", __func__);
+ return JNI_FALSE;
+ }
+ for (int folder_idx = 0; folder_idx < depth; folder_idx++) {
+ /* copy folder names */
+ ScopedLocalRef<jstring> text(
+ env, (jstring)env->GetObjectArrayElement(textArray, folder_idx));
+
+ if (!copy_jstring(p_folders[folder_idx].p_str, BTRC_MAX_ATTR_STR_LEN,
+ text.get(), env)) {
+ rspStatus = BTRC_STS_INTERNAL_ERR;
+ delete[] p_folders;
+ env->ReleaseByteArrayElements(address, addr, 0);
+ ALOGE("%s: Failed to copy folder name", __func__);
+ return JNI_FALSE;
+ }
+
+ p_folders[folder_idx].str_len =
+ strlen((char*)p_folders[folder_idx].p_str);
+ }
+ }
+ }
+
+ uint8_t folder_depth =
+ depth; /* folder_depth is 0 if current folder is root */
+ uint16_t charset_id = BTRC_CHARSET_ID_UTF8;
+ RawAddress* btAddr = (RawAddress*)addr;
+ bt_status_t status = sBluetoothAvrcpInterface->set_browsed_player_rsp(
+ *btAddr, (btrc_status_t)rspStatus, numItems, charset_id, folder_depth,
+ p_folders);
+ if (status != BT_STATUS_SUCCESS) {
+ ALOGE("%s: Failed set_browsed_player_rsp, status: %d", __func__, status);
+ }
+
+ if (depth > 0) {
+ delete[] p_folders;
+ }
+
+ env->ReleaseByteArrayElements(address, addr, 0);
+ return (status == BT_STATUS_SUCCESS) ? JNI_TRUE : JNI_FALSE;
+}
+
+static jboolean changePathRspNative(JNIEnv* env, jobject object,
+ jbyteArray address, jint rspStatus,
+ jint numItems) {
+ if (!sBluetoothAvrcpInterface) {
+ ALOGE("%s: sBluetoothAvrcpInterface is null", __func__);
+ return JNI_FALSE;
+ }
+
+ jbyte* addr = env->GetByteArrayElements(address, NULL);
+ if (!addr) {
+ jniThrowIOException(env, EINVAL);
+ return JNI_FALSE;
+ }
+
+ uint32_t nItems = (uint32_t)numItems;
+ RawAddress* btAddr = (RawAddress*)addr;
+ bt_status_t status = sBluetoothAvrcpInterface->change_path_rsp(
+ *btAddr, (btrc_status_t)rspStatus, (uint32_t)nItems);
+ if (status != BT_STATUS_SUCCESS) {
+ ALOGE("Failed change_path_rsp, status: %d", status);
+ }
+ env->ReleaseByteArrayElements(address, addr, 0);
+
+ return (status == BT_STATUS_SUCCESS) ? JNI_TRUE : JNI_FALSE;
+}
+
+static jboolean searchRspNative(JNIEnv* env, jobject object, jbyteArray address,
+ jint rspStatus, jint uidCounter,
+ jint numItems) {
+ if (!sBluetoothAvrcpInterface) {
+ ALOGE("%s: sBluetoothAvrcpInterface is null", __func__);
+ return JNI_FALSE;
+ }
+
+ jbyte* addr = env->GetByteArrayElements(address, NULL);
+ if (!addr) {
+ jniThrowIOException(env, EINVAL);
+ return JNI_FALSE;
+ }
+
+ uint32_t nItems = (uint32_t)numItems;
+ RawAddress* btAddr = (RawAddress*)addr;
+ bt_status_t status = sBluetoothAvrcpInterface->search_rsp(
+ *btAddr, (btrc_status_t)rspStatus, (uint32_t)uidCounter,
+ (uint32_t)nItems);
+ if (status != BT_STATUS_SUCCESS) {
+ ALOGE("Failed search_rsp, status: %d", status);
+ }
+
+ env->ReleaseByteArrayElements(address, addr, 0);
+
+ return (status == BT_STATUS_SUCCESS) ? JNI_TRUE : JNI_FALSE;
+}
+
+static jboolean playItemRspNative(JNIEnv* env, jobject object,
+ jbyteArray address, jint rspStatus) {
+ if (!sBluetoothAvrcpInterface) {
+ ALOGE("%s: sBluetoothAvrcpInterface is null", __func__);
+ return JNI_FALSE;
+ }
+
+ jbyte* addr = env->GetByteArrayElements(address, NULL);
+ if (!addr) {
+ jniThrowIOException(env, EINVAL);
+ return JNI_FALSE;
+ }
+
+ RawAddress* btAddr = (RawAddress*)addr;
+ bt_status_t status = sBluetoothAvrcpInterface->play_item_rsp(
+ *btAddr, (btrc_status_t)rspStatus);
+ if (status != BT_STATUS_SUCCESS) {
+ ALOGE("Failed play_item_rsp, status: %d", status);
+ }
+ env->ReleaseByteArrayElements(address, addr, 0);
+
+ return (status == BT_STATUS_SUCCESS) ? JNI_TRUE : JNI_FALSE;
+}
+
+static jboolean getTotalNumOfItemsRspNative(JNIEnv* env, jobject object,
+ jbyteArray address, jint rspStatus,
+ jint uidCounter, jint numItems) {
+ if (!sBluetoothAvrcpInterface) {
+ ALOGE("%s: sBluetoothAvrcpInterface is null", __func__);
+ return JNI_FALSE;
+ }
+
+ jbyte* addr = env->GetByteArrayElements(address, NULL);
+ if (!addr) {
+ jniThrowIOException(env, EINVAL);
+ return JNI_FALSE;
+ }
+
+ uint32_t nItems = (uint32_t)numItems;
+ RawAddress* btAddr = (RawAddress*)addr;
+ bt_status_t status = sBluetoothAvrcpInterface->get_total_num_of_items_rsp(
+ *btAddr, (btrc_status_t)rspStatus, (uint32_t)uidCounter,
+ (uint32_t)nItems);
+ if (status != BT_STATUS_SUCCESS) {
+ ALOGE("Failed get_total_num_of_items_rsp, status: %d", status);
+ }
+ env->ReleaseByteArrayElements(address, addr, 0);
+
+ return (status == BT_STATUS_SUCCESS) ? JNI_TRUE : JNI_FALSE;
+}
+
+static jboolean addToNowPlayingRspNative(JNIEnv* env, jobject object,
+ jbyteArray address, jint rspStatus) {
+ if (!sBluetoothAvrcpInterface) {
+ ALOGE("%s: sBluetoothAvrcpInterface is null", __func__);
+ return JNI_FALSE;
+ }
+
+ jbyte* addr = env->GetByteArrayElements(address, NULL);
+ if (!addr) {
+ jniThrowIOException(env, EINVAL);
+ return JNI_FALSE;
+ }
+
+ RawAddress* btAddr = (RawAddress*)addr;
+ bt_status_t status = sBluetoothAvrcpInterface->add_to_now_playing_rsp(
+ *btAddr, (btrc_status_t)rspStatus);
+ if (status != BT_STATUS_SUCCESS) {
+ ALOGE("Failed add_to_now_playing_rsp, 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", "()V", (void*)initNative},
+ {"cleanupNative", "()V", (void*)cleanupNative},
+ {"getPlayStatusRspNative", "([BIII)Z", (void*)getPlayStatusRspNative},
+ {"getElementAttrRspNative", "([BB[I[Ljava/lang/String;)Z",
+ (void*)getElementAttrRspNative},
+ {"registerNotificationRspPlayStatusNative", "(II)Z",
+ (void*)registerNotificationRspPlayStatusNative},
+ {"registerNotificationRspTrackChangeNative", "(I[B)Z",
+ (void*)registerNotificationRspTrackChangeNative},
+ {"registerNotificationRspPlayPosNative", "(II)Z",
+ (void*)registerNotificationRspPlayPosNative},
+ {"setVolumeNative", "(I)Z", (void*)setVolumeNative},
+
+ {"setAddressedPlayerRspNative", "([BI)Z",
+ (void*)setAddressedPlayerRspNative},
+
+ {"setBrowsedPlayerRspNative", "([BIBI[Ljava/lang/String;)Z",
+ (void*)setBrowsedPlayerRspNative},
+
+ {"mediaPlayerListRspNative", "([BIIBI[I[B[I[B[S[Ljava/lang/String;)Z",
+ (void*)mediaPlayerListRspNative},
+
+ {"getFolderItemsRspNative",
+ "([BISBI[B[B[B[B[Ljava/lang/String;[I[I[Ljava/lang/String;)Z",
+ (void*)getFolderItemsRspNative},
+
+ {"changePathRspNative", "([BII)Z", (void*)changePathRspNative},
+
+ {"getItemAttrRspNative", "([BIB[I[Ljava/lang/String;)Z",
+ (void*)getItemAttrRspNative},
+
+ {"playItemRspNative", "([BI)Z", (void*)playItemRspNative},
+
+ {"getTotalNumOfItemsRspNative", "([BIII)Z",
+ (void*)getTotalNumOfItemsRspNative},
+
+ {"searchRspNative", "([BIII)Z", (void*)searchRspNative},
+
+ {"addToNowPlayingRspNative", "([BI)Z", (void*)addToNowPlayingRspNative},
+
+ {"registerNotificationRspAddrPlayerChangedNative", "(III)Z",
+ (void*)registerNotificationRspAddrPlayerChangedNative},
+
+ {"registerNotificationRspAvalPlayerChangedNative", "(I)Z",
+ (void*)registerNotificationRspAvalPlayerChangedNative},
+
+ {"registerNotificationRspUIDsChangedNative", "(II)Z",
+ (void*)registerNotificationRspUIDsChangedNative},
+
+ {"registerNotificationRspNowPlayingChangedNative", "(I)Z",
+ (void*)registerNotificationRspNowPlayingChangedNative}};
+
+int register_com_android_bluetooth_avrcp(JNIEnv* env) {
+ return jniRegisterNativeMethods(env, "com/android/bluetooth/avrcp/Avrcp",
+ sMethods, NELEM(sMethods));
+}
+
+/* Helper function to copy attributes of item.
+ * Assumes that all items in response have same number of attributes
+ *
+ * returns true on succes, false otherwise.
+*/
+static bool copy_item_attributes(JNIEnv* env, jobject object,
+ btrc_folder_items_t* pitem,
+ jint* p_attributesIds,
+ jobjectArray attributesArray, int item_idx,
+ int attribCopiedIndex) {
+ bool success = true;
+
+ /* copy attributes of the item */
+ if (0 < pitem->media.num_attrs) {
+ int num_attrs = pitem->media.num_attrs;
+ ALOGI("%s num_attr = %d", __func__, num_attrs);
+ pitem->media.p_attrs = new btrc_element_attr_val_t[num_attrs];
+ if (!pitem->media.p_attrs) {
+ return false;
+ }
+
+ for (int tempAtrCount = 0; tempAtrCount < pitem->media.num_attrs;
+ ++tempAtrCount) {
+ pitem->media.p_attrs[tempAtrCount].attr_id =
+ p_attributesIds[attribCopiedIndex + tempAtrCount];
+
+ ScopedLocalRef<jstring> text(
+ env, (jstring)env->GetObjectArrayElement(
+ attributesArray, attribCopiedIndex + tempAtrCount));
+
+ if (!copy_jstring(pitem->media.p_attrs[tempAtrCount].text,
+ BTRC_MAX_ATTR_STR_LEN, text.get(), env)) {
+ success = false;
+ ALOGE("%s: failed to copy attributes", __func__);
+ break;
+ }
+ }
+ }
+ return success;
+}
+
+/* Helper function to copy String data from java to native
+ *
+ * returns true on succes, false otherwise
+ */
+static bool copy_jstring(uint8_t* str, int maxBytes, jstring jstr,
+ JNIEnv* env) {
+ if (str == NULL || jstr == NULL || env == NULL) return false;
+
+ memset(str, 0, maxBytes);
+ const char* p_str = env->GetStringUTFChars(jstr, NULL);
+ size_t len = strnlen(p_str, maxBytes - 1);
+ memcpy(str, p_str, len);
+
+ env->ReleaseStringUTFChars(jstr, p_str);
+ return true;
+}
+
+/* Helper function to cleanup items */
+static void cleanup_items(btrc_folder_items_t* p_items, int numItems) {
+ for (int item_idx = 0; item_idx < numItems; item_idx++) {
+ /* release memory for attributes in case item is media item */
+ if ((BTRC_ITEM_MEDIA == p_items[item_idx].item_type) &&
+ p_items[item_idx].media.p_attrs != NULL)
+ delete[] p_items[item_idx].media.p_attrs;
+ }
+}
+}
diff --git a/packages_apps_bluetooth_ext/jni/Android.bp b/packages_apps_bluetooth_ext/jni/Android.bp
index 6848cb2..cb15b77 100644
--- a/packages_apps_bluetooth_ext/jni/Android.bp
+++ b/packages_apps_bluetooth_ext/jni/Android.bp
@@ -25,5 +25,9 @@
"com_android_bluetooth_btservice_vendor_socket.cpp",
"com_android_bluetooth_avrcp_ext.cpp",
"com_android_bluetooth_ba.cpp",
+ "com_android_bluetooth_hf_vendor.cpp",
],
+ sanitize: {
+ never: true,
+ },
}
diff --git a/packages_apps_bluetooth_ext/jni/com_android_bluetooth_avrcp_ext.cpp b/packages_apps_bluetooth_ext/jni/com_android_bluetooth_avrcp_ext.cpp
index 9774d2a..cf4e968 100644
--- a/packages_apps_bluetooth_ext/jni/com_android_bluetooth_avrcp_ext.cpp
+++ b/packages_apps_bluetooth_ext/jni/com_android_bluetooth_avrcp_ext.cpp
@@ -59,6 +59,7 @@
static const btrc_interface_t *sBluetoothAvrcpInterface = NULL;
static jobject mCallbacksObj = NULL;
static std::shared_timed_mutex callbacks_mutex;
+static std::shared_timed_mutex interface_mutex;
/* Function declarations */
static bool copy_item_attributes(JNIEnv* env, jobject object,
@@ -122,6 +123,7 @@
RawAddress* bd_addr) {
ALOGI("%s", __FUNCTION__);
CallbackEnv sCallbackEnv(__func__);
+ std::shared_lock<std::shared_timed_mutex> lock(callbacks_mutex);
if (!sCallbackEnv.valid()) return;
if (!mCallbacksObj) {
@@ -145,6 +147,7 @@
static void btavrcp_get_player_attribute_id_callback(RawAddress* bd_addr) {
ALOGI("%s", __FUNCTION__);
CallbackEnv sCallbackEnv(__func__);
+ std::shared_lock<std::shared_timed_mutex> lock(callbacks_mutex);
if (!sCallbackEnv.valid()) return;
if (!mCallbacksObj) {
@@ -169,6 +172,7 @@
RawAddress* bd_addr) {
ALOGI("%s", __FUNCTION__);
CallbackEnv sCallbackEnv(__func__);
+ std::shared_lock<std::shared_timed_mutex> lock(callbacks_mutex);
if (!sCallbackEnv.valid()) return;
if (!mCallbacksObj) {
@@ -202,6 +206,7 @@
{
ALOGI("%s", __FUNCTION__);
CallbackEnv sCallbackEnv(__func__);
+ std::shared_lock<std::shared_timed_mutex> lock(callbacks_mutex);
if (!sCallbackEnv.valid()) return;
if (!mCallbacksObj) {
@@ -242,6 +247,7 @@
{
ALOGI("%s", __FUNCTION__);
CallbackEnv sCallbackEnv(__func__);
+ std::shared_lock<std::shared_timed_mutex> lock(callbacks_mutex);
if (!sCallbackEnv.valid()) return;
if (!mCallbacksObj) {
@@ -274,6 +280,7 @@
{
ALOGI("%s", __FUNCTION__);
CallbackEnv sCallbackEnv(__func__);
+ std::shared_lock<std::shared_timed_mutex> lock(callbacks_mutex);
if (!sCallbackEnv.valid()) return;
if (!mCallbacksObj) {
@@ -822,6 +829,7 @@
static void initNative(JNIEnv* env, jobject object,
jint maxAvrcpConnections) {
std::unique_lock<std::shared_timed_mutex> lock(callbacks_mutex);
+ std::unique_lock<std::shared_timed_mutex> interface_lock(interface_mutex);
const bt_interface_t* btInf = getBluetoothInterface();
if (btInf == NULL) {
ALOGE("Bluetooth module is not loaded");
@@ -861,6 +869,7 @@
static void cleanupNative(JNIEnv* env, jobject object) {
std::unique_lock<std::shared_timed_mutex> lock(callbacks_mutex);
+ std::unique_lock<std::shared_timed_mutex> interface_lock(interface_mutex);
const bt_interface_t* btInf = getBluetoothInterface();
if (btInf == NULL) {
ALOGE("Bluetooth module is not loaded");
@@ -881,6 +890,7 @@
static jboolean getPlayStatusRspNative(JNIEnv* env, jobject object,
jbyteArray address, jint playStatus,
jint songLen, jint songPos) {
+ std::unique_lock<std::shared_timed_mutex> interface_lock(interface_mutex);
if (!sBluetoothAvrcpInterface) {
ALOGE("%s: sBluetoothAvrcpInterface is null", __func__);
return JNI_FALSE;
@@ -907,6 +917,7 @@
}
static jboolean updatePlayStatusToStack(JNIEnv *env ,jobject object, jint playStatus) {
ALOGE("%s",__func__);
+ std::unique_lock<std::shared_timed_mutex> interface_lock(interface_mutex);
if (!sBluetoothAvrcpInterface) {
ALOGE("%s: sBluetoothAvrcpInterface is null ", __func__);
return JNI_FALSE;
@@ -926,6 +937,7 @@
int i;
jbyte *attr;
+ std::unique_lock<std::shared_timed_mutex> interface_lock(interface_mutex);
if (!sBluetoothAvrcpInterface) return JNI_FALSE;
if (!address) {
@@ -983,6 +995,7 @@
int i;
jbyte *attr;
+ std::unique_lock<std::shared_timed_mutex> interface_lock(interface_mutex);
if (!sBluetoothAvrcpInterface) return JNI_FALSE;
if (!address) {
@@ -1041,6 +1054,7 @@
int i;
jbyte *attr;
+ std::unique_lock<std::shared_timed_mutex> interface_lock(interface_mutex);
if (!sBluetoothAvrcpInterface) return JNI_FALSE;
if (!address) {
@@ -1092,6 +1106,7 @@
jbyte *addr;
btrc_status_t player_rsp = (btrc_status_t) attr_status;
+ std::unique_lock<std::shared_timed_mutex> interface_lock(interface_mutex);
if (!sBluetoothAvrcpInterface) return JNI_FALSE;
if (!address) {
@@ -1124,6 +1139,7 @@
const char* textStr;
jbyte *arr ;
+ std::unique_lock<std::shared_timed_mutex> interface_lock(interface_mutex);
if (!sBluetoothAvrcpInterface) return JNI_FALSE;
if (!address) {
@@ -1191,7 +1207,8 @@
const char* textStr;
jbyte *arr ;
- //ALOGE("sendValueTextRspNative");
+ ALOGE("sendValueTextRspNative");
+ std::unique_lock<std::shared_timed_mutex> interface_lock(interface_mutex);
if (!sBluetoothAvrcpInterface) return JNI_FALSE;
if (!address) {
@@ -1250,6 +1267,7 @@
jbyteArray address, jbyte numAttr,
jintArray attrIds,
jobjectArray textArray) {
+ std::unique_lock<std::shared_timed_mutex> interface_lock(interface_mutex);
if (!sBluetoothAvrcpInterface) {
ALOGE("%s: sBluetoothAvrcpInterface is null", __func__);
return JNI_FALSE;
@@ -1322,6 +1340,7 @@
jbyteArray address, jint rspStatus,
jbyte numAttr, jintArray attrIds,
jobjectArray textArray) {
+ std::unique_lock<std::shared_timed_mutex> interface_lock(interface_mutex);
if (!sBluetoothAvrcpInterface) {
ALOGE("%s: sBluetoothAvrcpInterface is null", __func__);
return JNI_FALSE;
@@ -1395,6 +1414,7 @@
jbyte *attr;
btrc_register_notification_t *param= NULL;
+ std::unique_lock<std::shared_timed_mutex> interface_lock(interface_mutex);
if (!sBluetoothAvrcpInterface) return JNI_FALSE;
if (!address) {
@@ -1448,6 +1468,7 @@
jint type,
jint playStatus,
jbyteArray address) {
+ std::unique_lock<std::shared_timed_mutex> interface_lock(interface_mutex);
if (!sBluetoothAvrcpInterface) {
ALOGE("%s: sBluetoothAvrcpInterface is null", __func__);
return JNI_FALSE;
@@ -1483,6 +1504,7 @@
jint type,
jbyteArray track,
jbyteArray address) {
+ std::unique_lock<std::shared_timed_mutex> interface_lock(interface_mutex);
if (!sBluetoothAvrcpInterface) {
ALOGE("%s: sBluetoothAvrcpInterface is null", __func__);
return JNI_FALSE;
@@ -1531,6 +1553,7 @@
jobject object, jint type,
jint playPos,
jbyteArray address) {
+ std::unique_lock<std::shared_timed_mutex> interface_lock(interface_mutex);
if (!sBluetoothAvrcpInterface) {
ALOGE("%s: sBluetoothAvrcpInterface is null", __func__);
return JNI_FALSE;
@@ -1565,6 +1588,7 @@
jobject object,
jint type,
jbyteArray address) {
+ std::unique_lock<std::shared_timed_mutex> interface_lock(interface_mutex);
if (!sBluetoothAvrcpInterface) {
ALOGE("%s: sBluetoothAvrcpInterface is null", __func__);
return JNI_FALSE;
@@ -1598,6 +1622,7 @@
jint type,
jint uidCounter,
jbyteArray address) {
+ std::unique_lock<std::shared_timed_mutex> interface_lock(interface_mutex);
if (!sBluetoothAvrcpInterface) {
ALOGE("%s: sBluetoothAvrcpInterface is null", __func__);
return JNI_FALSE;
@@ -1631,6 +1656,7 @@
static jboolean registerNotificationRspAddrPlayerChangedNative(
JNIEnv* env, jobject object, jint type, jint playerId, jint uidCounter,
jbyteArray address) {
+ std::unique_lock<std::shared_timed_mutex> interface_lock(interface_mutex);
if (!sBluetoothAvrcpInterface) {
ALOGE("%s: sBluetoothAvrcpInterface is null", __func__);
return JNI_FALSE;
@@ -1667,6 +1693,7 @@
jobject object,
jint type,
jbyteArray address) {
+ std::unique_lock<std::shared_timed_mutex> interface_lock(interface_mutex);
if (!sBluetoothAvrcpInterface) {
ALOGE("%s: sBluetoothAvrcpInterface is null", __func__);
return JNI_FALSE;
@@ -1699,6 +1726,7 @@
}
static jboolean setVolumeNative(JNIEnv* env, jobject object, jint volume, jbyteArray address) {
+ std::unique_lock<std::shared_timed_mutex> interface_lock(interface_mutex);
if (!sBluetoothAvrcpInterface) {
ALOGE("%s: sBluetoothAvrcpInterface is null", __func__);
return JNI_FALSE;
@@ -1733,6 +1761,7 @@
jbyteArray playerTypes, jintArray playerSubtypes,
jbyteArray playStatusValues, jshortArray featureBitmask,
jobjectArray textArray) {
+ std::unique_lock<std::shared_timed_mutex> interface_lock(interface_mutex);
if (!sBluetoothAvrcpInterface) {
ALOGE("%s: sBluetoothAvrcpInterface is null", __func__);
return JNI_FALSE;
@@ -1841,6 +1870,7 @@
jbyteArray playable, jbyteArray itemType, jbyteArray itemUidArray,
jobjectArray displayNameArray, jintArray numAttrs, jintArray attributesIds,
jobjectArray attributesArray) {
+ std::unique_lock<std::shared_timed_mutex> interface_lock(interface_mutex);
if (!sBluetoothAvrcpInterface) {
ALOGE("%s: sBluetoothAvrcpInterface is null", __func__);
return JNI_FALSE;
@@ -1980,6 +2010,7 @@
static jboolean setAddressedPlayerRspNative(JNIEnv* env, jobject object,
jbyteArray address,
jint rspStatus) {
+ std::unique_lock<std::shared_timed_mutex> interface_lock(interface_mutex);
if (!sBluetoothAvrcpInterface) {
ALOGE("%s: sBluetoothAvrcpInterface is null", __func__);
return JNI_FALSE;
@@ -2011,6 +2042,7 @@
jbyteArray address, jint rspStatus,
jbyte depth, jint numItems,
jobjectArray textArray) {
+ std::unique_lock<std::shared_timed_mutex> interface_lock(interface_mutex);
if (!sBluetoothAvrcpInterface) {
ALOGE("%s: sBluetoothAvrcpInterface is null", __func__);
return JNI_FALSE;
@@ -2080,6 +2112,7 @@
static jboolean changePathRspNative(JNIEnv* env, jobject object,
jbyteArray address, jint rspStatus,
jint numItems) {
+ std::unique_lock<std::shared_timed_mutex> interface_lock(interface_mutex);
if (!sBluetoothAvrcpInterface) {
ALOGE("%s: sBluetoothAvrcpInterface is null", __func__);
return JNI_FALSE;
@@ -2111,6 +2144,7 @@
static jboolean searchRspNative(JNIEnv* env, jobject object, jbyteArray address,
jint rspStatus, jint uidCounter,
jint numItems) {
+ std::unique_lock<std::shared_timed_mutex> interface_lock(interface_mutex);
if (!sBluetoothAvrcpInterface) {
ALOGE("%s: sBluetoothAvrcpInterface is null", __func__);
return JNI_FALSE;
@@ -2142,6 +2176,7 @@
static jboolean playItemRspNative(JNIEnv* env, jobject object,
jbyteArray address, jint rspStatus) {
+ std::unique_lock<std::shared_timed_mutex> interface_lock(interface_mutex);
if (!sBluetoothAvrcpInterface) {
ALOGE("%s: sBluetoothAvrcpInterface is null", __func__);
return JNI_FALSE;
@@ -2172,6 +2207,7 @@
static jboolean getTotalNumOfItemsRspNative(JNIEnv* env, jobject object,
jbyteArray address, jint rspStatus,
jint uidCounter, jint numItems) {
+ std::unique_lock<std::shared_timed_mutex> interface_lock(interface_mutex);
if (!sBluetoothAvrcpInterface) {
ALOGE("%s: sBluetoothAvrcpInterface is null", __func__);
return JNI_FALSE;
@@ -2202,6 +2238,7 @@
static jboolean addToNowPlayingRspNative(JNIEnv* env, jobject object,
jbyteArray address, jint rspStatus) {
+ std::unique_lock<std::shared_timed_mutex> interface_lock(interface_mutex);
if (!sBluetoothAvrcpInterface) {
ALOGE("%s: sBluetoothAvrcpInterface is null", __func__);
return JNI_FALSE;
@@ -2235,6 +2272,7 @@
bt_status_t status = BT_STATUS_SUCCESS;
jbyte *addr;
+ std::unique_lock<std::shared_timed_mutex> interface_lock(interface_mutex);
if (!sBluetoothAvrcpInterface) return JNI_FALSE;
if (!address) {
diff --git a/packages_apps_bluetooth_ext/jni/com_android_bluetooth_btservice_vendor.cpp b/packages_apps_bluetooth_ext/jni/com_android_bluetooth_btservice_vendor.cpp
index 66b00c8..e4b775d 100644
--- a/packages_apps_bluetooth_ext/jni/com_android_bluetooth_btservice_vendor.cpp
+++ b/packages_apps_bluetooth_ext/jni/com_android_bluetooth_btservice_vendor.cpp
@@ -477,12 +477,13 @@
int load_bt_configstore_lib() {
const char* sym = BT_CONFIG_STORE_INTERFACE_STRING;
+ const char* err = "error unknown";
bt_configstore_lib_handle = dlopen("libbtconfigstore.so", RTLD_NOW);
if (!bt_configstore_lib_handle) {
const char* err_str = dlerror();
LOG(ERROR) << __func__ << ": failed to load Bt Config store library, error="
- << (err_str ? err_str : "error unknown");
+ << (err_str ? err_str : err);
goto error;
}
diff --git a/packages_apps_bluetooth_ext/jni/com_android_bluetooth_hf_vendor.cpp b/packages_apps_bluetooth_ext/jni/com_android_bluetooth_hf_vendor.cpp
new file mode 100644
index 0000000..f11d4cd
--- /dev/null
+++ b/packages_apps_bluetooth_ext/jni/com_android_bluetooth_hf_vendor.cpp
@@ -0,0 +1,165 @@
+/*
+ * Copyright (c) 2019, The Linux Foundation. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above
+ * copyright notice, this list of conditions and the following
+ * disclaimer in the documentation and/or other materials provided
+ * with the distribution.
+ * * Neither the name of The Linux Foundation nor the names of its
+ * contributors may be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED "AS IS" AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS
+ * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
+ * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
+ * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN
+ * IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#define LOG_TAG "BluetoothVendorHfJni"
+
+#include "com_android_bluetooth.h"
+#include <hardware/vendor_hf.h>
+#include "utils/Log.h"
+#include "android_runtime/AndroidRuntime.h"
+
+namespace android {
+static btvendor_hf_interface_t *sBluetoothVendorHfInterface = NULL;
+static jobject mCallbacksObj = NULL;
+static jmethodID method_onSWB;
+
+static jbyteArray marshall_bda(RawAddress* bd_addr) {
+ CallbackEnv sCallbackEnv(__func__);
+ if (!sCallbackEnv.valid()) return nullptr;
+
+ jbyteArray addr = sCallbackEnv->NewByteArray(sizeof(RawAddress));
+ if (!addr) {
+ ALOGE("Fail to new jbyteArray bd addr");
+ return nullptr;
+ }
+ sCallbackEnv->SetByteArrayRegion(addr, 0, sizeof(RawAddress),
+ (jbyte*)bd_addr);
+ return addr;
+}
+
+static void SwbCallback(uint16_t swb_config, RawAddress* bd_addr) {
+
+ ALOGI("%s", __FUNCTION__);
+ CallbackEnv sCallbackEnv(__func__);
+ if (!sCallbackEnv.valid()) return;
+
+ ScopedLocalRef<jbyteArray> addr(sCallbackEnv.get(), marshall_bda(bd_addr));
+ if (addr.get() == nullptr) return;
+ sCallbackEnv->CallVoidMethod(mCallbacksObj, method_onSWB, swb_config,
+ addr.get());
+}
+
+static btvendor_hf_callbacks_t sBluetoothVendorHfCallbacks = {
+ sizeof(sBluetoothVendorHfCallbacks),
+ SwbCallback,
+};
+
+static void classInitNative(JNIEnv* env, jclass clazz) {
+
+ method_onSWB = env->GetMethodID(clazz, "onSWB", "(I[B)V");
+ ALOGI("%s: succeeds", __FUNCTION__);
+}
+
+static void initNative(JNIEnv *env, jobject object) {
+ const bt_interface_t* btInf;
+ bt_status_t status;
+
+ if ( (btInf = getBluetoothInterface()) == NULL) {
+ ALOGE("Bluetooth module is not loaded");
+ return;
+ }
+
+ if (mCallbacksObj != NULL) {
+ ALOGW("Cleaning up Bluetooth Vendor callback object");
+ env->DeleteGlobalRef(mCallbacksObj);
+ mCallbacksObj = NULL;
+ }
+
+ if ( (sBluetoothVendorHfInterface = (btvendor_hf_interface_t *)
+ btInf->get_profile_interface(BT_PROFILE_VENDOR_HF_ID)) == NULL) {
+ ALOGE("Failed to get Bluetooth Vendor Interface");
+ return;
+ }
+
+ if ( (status = sBluetoothVendorHfInterface->init(&sBluetoothVendorHfCallbacks))
+ != BT_STATUS_SUCCESS) {
+ ALOGE("Failed to initialize Bluetooth Vendor, status: %d", status);
+ sBluetoothVendorHfInterface = NULL;
+ return;
+ }
+ mCallbacksObj = env->NewGlobalRef(object);
+}
+
+static void cleanupNative(JNIEnv *env, jobject object) {
+ const bt_interface_t* btInf;
+
+ if ( (btInf = getBluetoothInterface()) == NULL) {
+ ALOGE("Bluetooth module is not loaded");
+ return;
+ }
+
+ if (sBluetoothVendorHfInterface !=NULL) {
+ ALOGW("Cleaning up Bluetooth Vendor Interface...");
+ sBluetoothVendorHfInterface->cleanup();
+ sBluetoothVendorHfInterface = NULL;
+ }
+
+ if (mCallbacksObj != NULL) {
+ ALOGW("Cleaning up Bluetooth Vendor callback object");
+ env->DeleteGlobalRef(mCallbacksObj);
+ mCallbacksObj = NULL;
+ }
+
+}
+
+/* native interface */
+static jint enableSwbNative(JNIEnv* env, jobject thiz, jboolean enable)
+{
+ if (sBluetoothVendorHfInterface == NULL) {
+ ALOGE("No Interface initialized");
+ return JNI_FALSE;
+ }
+
+ int ret = sBluetoothVendorHfInterface->enable_swb(enable);
+
+ if (ret != 0) {
+ ALOGE("%s: Failure", __func__);
+ return JNI_FALSE;
+ } else {
+ ALOGV("%s: Success :%d", __func__, enable);
+ }
+
+ return JNI_TRUE;
+}
+
+static JNINativeMethod sMethods[] = {
+ {"classInitNative", "()V", (void *) classInitNative},
+ {"initNative", "()V", (void *) initNative},
+ {"cleanupNative", "()V", (void *) cleanupNative},
+ { "enableSwbNative", "(Z)I", (void*)enableSwbNative},
+};
+
+int register_com_android_bluetooth_hfp_vendorhfservice(JNIEnv* env)
+{
+ ALOGE("%s:",__FUNCTION__);
+ return jniRegisterNativeMethods(env, "com/android/bluetooth/hfp/vendorhfservice",
+ sMethods, NELEM(sMethods));
+}
+
+} /* namespace android */
diff --git a/packages_apps_bluetooth_ext/src/avrcp/AddressedMediaPlayer_ext.java b/packages_apps_bluetooth_ext/src/avrcp/AddressedMediaPlayer_ext.java
new file mode 100644
index 0000000..a650069
--- /dev/null
+++ b/packages_apps_bluetooth_ext/src/avrcp/AddressedMediaPlayer_ext.java
@@ -0,0 +1,646 @@
+/*
+ * 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.
+ */
+
+package com.android.bluetooth.avrcp;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.media.MediaDescription;
+import android.media.MediaMetadata;
+import android.media.session.MediaSession;
+import android.media.session.MediaSession.QueueItem;
+import android.media.session.PlaybackState;
+import android.os.Bundle;
+import android.util.Log;
+
+import com.android.bluetooth.Utils;
+import com.android.bluetooth.btservice.ProfileService;
+
+import java.nio.ByteBuffer;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.List;
+
+/*************************************************************************************************
+ * Provides functionality required for Addressed Media Player, like Now Playing List related
+ * browsing commands, control commands to the current addressed player(playItem, play, pause, etc)
+ * Acts as an Interface to communicate with media controller APIs for NowPlayingItems.
+ ************************************************************************************************/
+
+public class AddressedMediaPlayer_ext {
+ private static final String TAG = "AddressedMediaPlayer_ext";
+ private static final Boolean DEBUG = true;
+
+ private static final long SINGLE_QID = 1;
+ private static final String UNKNOWN_TITLE = "(unknown)";
+
+ static private final String GPM_BUNDLE_METADATA_KEY =
+ "com.google.android.music.mediasession.music_metadata";
+
+ private AvrcpMediaRspInterface_ext mMediaInterface;
+ private Avrcp_ext mAvrcp = null;
+ @NonNull private List<MediaSession.QueueItem> mNowPlayingList;
+
+ private final List<MediaSession.QueueItem> mEmptyNowPlayingList;
+
+ private long mLastTrackIdSent;
+
+ public AddressedMediaPlayer_ext(AvrcpMediaRspInterface_ext mediaInterface) {
+ mEmptyNowPlayingList = new ArrayList<MediaSession.QueueItem>();
+ mNowPlayingList = mEmptyNowPlayingList;
+ mMediaInterface = mediaInterface;
+ mLastTrackIdSent = MediaSession.QueueItem.UNKNOWN_ID;
+ }
+
+ public AddressedMediaPlayer_ext(AvrcpMediaRspInterface_ext mediaInterface, Avrcp_ext mAvrcp_ext) {
+ this(mediaInterface);
+ mAvrcp = mAvrcp_ext;
+ }
+
+ void cleanup() {
+ if (DEBUG) {
+ Log.v(TAG, "cleanup");
+ }
+ mNowPlayingList = mEmptyNowPlayingList;
+ mMediaInterface = null;
+ mLastTrackIdSent = MediaSession.QueueItem.UNKNOWN_ID;
+ }
+
+ /* get now playing list from addressed player */
+ void getFolderItemsNowPlaying(byte[] bdaddr, AvrcpCmd_ext.FolderItemsCmd reqObj,
+ @Nullable MediaController mediaController) {
+ if (mediaController == null) {
+ // No players (if a player exists, we would have selected it)
+ Log.e(TAG, "mediaController = null, sending no available players response");
+ mMediaInterface.folderItemsRsp(bdaddr, AvrcpConstants_ext.RSP_NO_AVBL_PLAY, null);
+ return;
+ }
+ List<MediaSession.QueueItem> items = updateNowPlayingList(mediaController);
+ getFolderItemsFilterAttr(bdaddr, reqObj, items, AvrcpConstants_ext.BTRC_SCOPE_NOW_PLAYING,
+ reqObj.mStartItem, reqObj.mEndItem, mediaController);
+ }
+
+ /* get item attributes for item in now playing list */
+ void getItemAttr(byte[] bdaddr, AvrcpCmd_ext.ItemAttrCmd itemAttr,
+ @Nullable MediaController mediaController) {
+ int status = AvrcpConstants_ext.RSP_NO_ERROR;
+ long mediaId = ByteBuffer.wrap(itemAttr.mUid).getLong();
+ List<MediaSession.QueueItem> items = updateNowPlayingList(mediaController);
+
+ // NOTE: this is out-of-spec (AVRCP 1.6.1 sec 6.10.4.3, p90) but we answer it anyway
+ // because some CTs ask for it.
+ if (Arrays.equals(itemAttr.mUid, AvrcpConstants_ext.TRACK_IS_SELECTED)) {
+ mediaId = getActiveQueueItemId(mediaController);
+ if (DEBUG) {
+ Log.d(TAG, "getItemAttr: Remote requests for now playing contents, sending UID: "
+ + mediaId);
+ }
+ }
+
+ if (DEBUG) {
+ Log.d(TAG, "getItemAttr-UID: 0x" + Utils.byteArrayToString(itemAttr.mUid));
+ }
+ for (MediaSession.QueueItem item : items) {
+ if (item.getQueueId() == mediaId) {
+ getItemAttrFilterAttr(bdaddr, itemAttr, item, mediaController);
+ return;
+ }
+ }
+
+ // Couldn't find it, so the id is invalid
+ mMediaInterface.getItemAttrRsp(bdaddr, AvrcpConstants_ext.RSP_INV_ITEM, null);
+ }
+
+ /* Refresh and get the queue of now playing.
+ */
+ @NonNull
+ List<MediaSession.QueueItem> updateNowPlayingList(@Nullable MediaController mediaController) {
+ if (mediaController == null) {
+ return mEmptyNowPlayingList;
+ }
+ List<MediaSession.QueueItem> items = mediaController.getQueue();
+ if (items == null) {
+ Log.i(TAG, "null queue from " + mediaController.getPackageName()
+ + ", constructing single-item list");
+
+ // Because we are database-unaware, we can just number the item here whatever we want
+ // because they have to re-poll it every time.
+ MediaMetadata metadata = mediaController.getMetadata();
+ if (metadata == null) {
+ Log.w(TAG, "Controller has no metadata!? Making an empty one");
+ metadata = (new MediaMetadata.Builder()).build();
+ }
+
+ MediaDescription.Builder bob = new MediaDescription.Builder();
+ MediaDescription desc = metadata.getDescription();
+
+ // set the simple ones that MediaMetadata builds for us
+ bob.setMediaId(desc.getMediaId());
+ bob.setTitle(desc.getTitle());
+ bob.setSubtitle(desc.getSubtitle());
+ bob.setDescription(desc.getDescription());
+ // fill the ones that we use later
+ bob.setExtras(fillBundle(metadata, desc.getExtras()));
+
+ // build queue item with the new metadata
+ MediaSession.QueueItem current = new QueueItem(bob.build(), SINGLE_QID);
+
+ items = new ArrayList<MediaSession.QueueItem>();
+ items.add(current);
+ }
+
+ if (!items.equals(mNowPlayingList)) {
+ sendNowPlayingListChanged();
+ }
+ mNowPlayingList = items;
+
+ return mNowPlayingList;
+ }
+
+ private void sendNowPlayingListChanged() {
+ if (mMediaInterface == null) {
+ return;
+ }
+ if (DEBUG) {
+ Log.d(TAG, "sendNowPlayingListChanged()");
+ }
+ mMediaInterface.nowPlayingChangedRsp(AvrcpConstants_ext.NOTIFICATION_TYPE_CHANGED);
+ }
+
+ private Bundle fillBundle(MediaMetadata metadata, Bundle currentExtras) {
+ if (metadata == null) {
+ Log.i(TAG, "fillBundle: metadata is null");
+ return currentExtras;
+ }
+
+ Bundle bundle = currentExtras;
+ if (bundle == null) {
+ bundle = new Bundle();
+ }
+
+ String[] stringKeys = {
+ MediaMetadata.METADATA_KEY_TITLE,
+ MediaMetadata.METADATA_KEY_ARTIST,
+ MediaMetadata.METADATA_KEY_ALBUM,
+ MediaMetadata.METADATA_KEY_GENRE
+ };
+ for (String key : stringKeys) {
+ String current = bundle.getString(key);
+ if (current == null) {
+ bundle.putString(key, metadata.getString(key));
+ }
+ }
+
+ String[] longKeys = {
+ MediaMetadata.METADATA_KEY_TRACK_NUMBER,
+ MediaMetadata.METADATA_KEY_NUM_TRACKS,
+ MediaMetadata.METADATA_KEY_DURATION
+ };
+ for (String key : longKeys) {
+ if (!bundle.containsKey(key)) {
+ bundle.putLong(key, metadata.getLong(key));
+ }
+ }
+ return bundle;
+ }
+
+ /* Instructs media player to play particular media item */
+ void playItem(byte[] bdaddr, byte[] uid, @Nullable MediaController mediaController) {
+ long qid = ByteBuffer.wrap(uid).getLong();
+ List<MediaSession.QueueItem> items = updateNowPlayingList(mediaController);
+
+ if (mediaController == null) {
+ Log.e(TAG, "No mediaController when PlayItem " + qid + " requested");
+ mMediaInterface.playItemRsp(bdaddr, AvrcpConstants_ext.RSP_INTERNAL_ERR);
+ return;
+ }
+
+ MediaController.TransportControls mediaControllerCntrl =
+ mediaController.getTransportControls();
+
+ if (items == null) {
+ Log.w(TAG, "nowPlayingItems is null");
+ mMediaInterface.playItemRsp(bdaddr, AvrcpConstants_ext.RSP_INTERNAL_ERR);
+ return;
+ }
+
+ for (MediaSession.QueueItem item : items) {
+ if (qid == item.getQueueId()) {
+ if (DEBUG) {
+ Log.d(TAG, "Skipping to ID " + qid);
+ }
+ mediaControllerCntrl.skipToQueueItem(qid);
+ mMediaInterface.playItemRsp(bdaddr, AvrcpConstants_ext.RSP_NO_ERROR);
+ return;
+ }
+ }
+
+ Log.w(TAG, "Invalid now playing Queue ID " + qid);
+ mMediaInterface.playItemRsp(bdaddr, AvrcpConstants_ext.RSP_INV_ITEM);
+ }
+
+ void getTotalNumOfItems(byte[] bdaddr, @Nullable MediaController mediaController) {
+ List<MediaSession.QueueItem> items = updateNowPlayingList(mediaController);
+ if (DEBUG) {
+ Log.d(TAG, "getTotalNumOfItems: " + items.size() + " items.");
+ }
+ mMediaInterface.getTotalNumOfItemsRsp(bdaddr, AvrcpConstants_ext.RSP_NO_ERROR, 0, items.size());
+ }
+
+ void sendTrackChangeWithId(int type, @Nullable MediaController mediaController) {
+ Log.d(TAG, "sendTrackChangeWithId (" + type + "): controller " + mediaController);
+ long qid = getActiveQueueItemId(mediaController);
+ byte[] track = ByteBuffer.allocate(AvrcpConstants_ext.UID_SIZE).putLong(qid).array();
+ Log.d(TAG, "qid: " + qid );
+ if ((type == AvrcpConstants_ext.NOTIFICATION_TYPE_INTERIM) &&
+ (qid != MediaSession.QueueItem.UNKNOWN_ID) &&
+ (qid != mLastTrackIdSent)) {
+ byte[] lastTrack =
+ ByteBuffer.allocate(AvrcpConstants_ext.UID_SIZE).putLong(mLastTrackIdSent).array();
+ mMediaInterface.trackChangedRsp(type, lastTrack);
+ type = AvrcpConstants_ext.NOTIFICATION_TYPE_CHANGED;
+ }
+ // The nowPlayingList changed: the new list has the full data for the current item
+ Log.d(TAG, "last_sent_qid: " + mLastTrackIdSent);
+ mMediaInterface.trackChangedRsp(type, track);
+ mLastTrackIdSent = qid;
+ }
+
+ void sendTrackChangeWithId(int type, @Nullable MediaController mediaController, byte[] bdaddr) {
+ Log.d(TAG, "sendTrackChangeWithId (" + type + "): controller " + mediaController);
+ if(mAvrcp == null) {
+ sendTrackChangeWithId(type, mediaController);
+ return;
+ }
+ long qid = getActiveQueueItemId(mediaController);
+ byte[] track = ByteBuffer.allocate(AvrcpConstants_ext.UID_SIZE).putLong(qid).array();
+ Log.d(TAG, "qid: " + qid );
+ if ((type == AvrcpConstants_ext.NOTIFICATION_TYPE_INTERIM) &&
+ (qid != MediaSession.QueueItem.UNKNOWN_ID) &&
+ (qid != mLastTrackIdSent)) {
+ byte[] lastTrack =
+ ByteBuffer.allocate(AvrcpConstants_ext.UID_SIZE).putLong(mLastTrackIdSent).array();
+ mAvrcp.trackChangedAddressedRsp(type, lastTrack, bdaddr);
+ type = AvrcpConstants_ext.NOTIFICATION_TYPE_CHANGED;
+ }
+ // The nowPlayingList changed: the new list has the full data for the current item
+ Log.d(TAG, "last_sent_qid: " + mLastTrackIdSent);
+ mAvrcp.trackChangedAddressedRsp(type, track, bdaddr);
+ mLastTrackIdSent = qid;
+ }
+
+ /*
+ * helper method to check if startItem and endItem index is with range of
+ * MediaItem list. (Resultset containing all items in current path)
+ */
+ @Nullable
+ private List<MediaSession.QueueItem> getQueueSubset(@NonNull List<MediaSession.QueueItem> items,
+ long startItem, long endItem) {
+ if (endItem > items.size()) {
+ endItem = items.size() - 1;
+ }
+ if (startItem > Integer.MAX_VALUE) {
+ startItem = Integer.MAX_VALUE;
+ }
+ try {
+ List<MediaSession.QueueItem> selected =
+ items.subList((int) startItem, (int) Math.min(items.size(), endItem + 1));
+ if (selected.isEmpty()) {
+ Log.i(TAG, "itemsSubList is empty.");
+ return null;
+ }
+ return selected;
+ } catch (IndexOutOfBoundsException ex) {
+ Log.i(TAG, "Range (" + startItem + ", " + endItem + ") invalid");
+ } catch (IllegalArgumentException ex) {
+ Log.i(TAG, "Range start " + startItem + " > size (" + items.size() + ")");
+ }
+ return null;
+ }
+
+ /*
+ * helper method to filter required attibutes before sending GetFolderItems
+ * response
+ */
+ private void getFolderItemsFilterAttr(byte[] bdaddr, AvrcpCmd_ext.FolderItemsCmd folderItemsReqObj,
+ @NonNull List<MediaSession.QueueItem> items, byte scope, long startItem, long endItem,
+ @NonNull MediaController mediaController) {
+ if (DEBUG) {
+ Log.d(TAG,
+ "getFolderItemsFilterAttr: startItem =" + startItem + ", endItem = " + endItem);
+ }
+
+ List<MediaSession.QueueItem> resultItems = getQueueSubset(items, startItem, endItem);
+ /* check for index out of bound errors */
+ if (resultItems == null) {
+ Log.w(TAG, "getFolderItemsFilterAttr: resultItems is empty");
+ mMediaInterface.folderItemsRsp(bdaddr, AvrcpConstants_ext.RSP_INV_RANGE, null);
+ return;
+ }
+
+ FolderItemsData_ext folderDataNative = new FolderItemsData_ext(resultItems.size());
+
+ /* variables to accumulate attrs */
+ ArrayList<String> attrArray = new ArrayList<String>();
+ ArrayList<Integer> attrId = new ArrayList<Integer>();
+
+ for (int itemIndex = 0; itemIndex < resultItems.size(); itemIndex++) {
+ MediaSession.QueueItem item = resultItems.get(itemIndex);
+ // get the queue id
+ long qid = item.getQueueId();
+ byte[] uid = ByteBuffer.allocate(AvrcpConstants_ext.UID_SIZE).putLong(qid).array();
+
+ // get the array of uid from 2d to array 1D array
+ for (int idx = 0; idx < AvrcpConstants_ext.UID_SIZE; idx++) {
+ folderDataNative.mItemUid[itemIndex * AvrcpConstants_ext.UID_SIZE + idx] = uid[idx];
+ }
+
+ /* Set display name for current item */
+ folderDataNative.mDisplayNames[itemIndex] =
+ getAttrValue(bdaddr, AvrcpConstants_ext.ATTRID_TITLE, item, mediaController);
+
+ int maxAttributesRequested = 0;
+ boolean isAllAttribRequested = false;
+ /* check if remote requested for attributes */
+ if (folderItemsReqObj.mNumAttr != AvrcpConstants_ext.NUM_ATTR_NONE) {
+ int attrCnt = 0;
+
+ /* add requested attr ids to a temp array */
+ if (folderItemsReqObj.mNumAttr == AvrcpConstants_ext.NUM_ATTR_ALL) {
+ isAllAttribRequested = true;
+ maxAttributesRequested = AvrcpConstants_ext.MAX_NUM_ATTR;
+ } else {
+ /* get only the requested attribute ids from the request */
+ maxAttributesRequested = folderItemsReqObj.mNumAttr;
+ }
+
+ /* lookup and copy values of attributes for ids requested above */
+ for (int idx = 0; idx < maxAttributesRequested; idx++) {
+ /* check if media player provided requested attributes */
+ String value = null;
+
+ int attribId =
+ isAllAttribRequested ? (idx + 1) : folderItemsReqObj.mAttrIDs[idx];
+ value = getAttrValue(bdaddr, attribId, item, mediaController);
+ if (value != null) {
+ attrArray.add(value);
+ attrId.add(attribId);
+ attrCnt++;
+ }
+ }
+ /* add num attr actually received from media player for a particular item */
+ folderDataNative.mAttributesNum[itemIndex] = attrCnt;
+ }
+ }
+
+ /* copy filtered attr ids and attr values to response parameters */
+ if (folderItemsReqObj.mNumAttr != AvrcpConstants_ext.NUM_ATTR_NONE) {
+ folderDataNative.mAttrIds = new int[attrId.size()];
+ for (int attrIndex = 0; attrIndex < attrId.size(); attrIndex++) {
+ folderDataNative.mAttrIds[attrIndex] = attrId.get(attrIndex);
+ }
+ folderDataNative.mAttrValues = attrArray.toArray(new String[attrArray.size()]);
+ }
+ for (int attrIndex = 0; attrIndex < folderDataNative.mAttributesNum.length; attrIndex++) {
+ if (DEBUG) {
+ Log.d(TAG, "folderDataNative.mAttributesNum"
+ + folderDataNative.mAttributesNum[attrIndex] + " attrIndex " + attrIndex);
+ }
+ }
+
+ /* create rsp object and send response to remote device */
+ FolderItemsRsp_ext rspObj =
+ new FolderItemsRsp_ext(AvrcpConstants_ext.RSP_NO_ERROR, Avrcp_ext.sUIDCounter, scope,
+ folderDataNative.mNumItems, folderDataNative.mFolderTypes,
+ folderDataNative.mPlayable, folderDataNative.mItemTypes,
+ folderDataNative.mItemUid, folderDataNative.mDisplayNames,
+ folderDataNative.mAttributesNum, folderDataNative.mAttrIds,
+ folderDataNative.mAttrValues);
+ mMediaInterface.folderItemsRsp(bdaddr, AvrcpConstants_ext.RSP_NO_ERROR, rspObj);
+ }
+
+ private String getAttrValue(byte []bdaddr, int attr, MediaSession.QueueItem item,
+ @Nullable MediaController mediaController) {
+ String attrValue = null;
+ if (item == null) {
+ if (DEBUG) {
+ Log.d(TAG, "getAttrValue received null item");
+ }
+ return null;
+ }
+ try {
+ MediaDescription desc = item.getDescription();
+ Bundle extras = desc.getExtras();
+ boolean isCurrentTrack = item.getQueueId() == getActiveQueueItemId(mediaController);
+ MediaMetadata data = null;
+ if (isCurrentTrack) {
+ if (DEBUG) {
+ Log.d(TAG, "getAttrValue: item is active, using current data");
+ }
+ data = mediaController.getMetadata();
+ if (data == null) {
+ Log.e(TAG, "getMetadata didn't give us any metadata for the current track");
+ }
+ }
+
+ if (data == null) {
+ // TODO: This code can be removed when b/63117921 is resolved
+ data = (MediaMetadata) extras.get(GPM_BUNDLE_METADATA_KEY);
+ extras = null; // We no longer need the data in here
+ }
+
+ extras = fillBundle(data, extras);
+
+ if (DEBUG) {
+ Log.d(TAG, "getAttrValue: item " + item + " : " + desc);
+ }
+ switch (attr) {
+ case AvrcpConstants_ext.ATTRID_TITLE:
+ /* Title is mandatory attribute */
+ if (isCurrentTrack) {
+ attrValue = extras.getString(MediaMetadata.METADATA_KEY_TITLE);
+ } else {
+ attrValue = desc.getTitle().toString();
+ }
+ if (attrValue == null)
+ attrValue = "<Unknown Title>";
+ break;
+
+ case AvrcpConstants_ext.ATTRID_ARTIST:
+ attrValue = extras.getString(MediaMetadata.METADATA_KEY_ARTIST);
+ break;
+
+ case AvrcpConstants_ext.ATTRID_ALBUM:
+ attrValue = extras.getString(MediaMetadata.METADATA_KEY_ALBUM);
+ break;
+
+ case AvrcpConstants_ext.ATTRID_TRACK_NUM:
+ attrValue =
+ Long.toString(extras.getLong(MediaMetadata.METADATA_KEY_TRACK_NUMBER));
+ break;
+
+ case AvrcpConstants_ext.ATTRID_NUM_TRACKS:
+ attrValue =
+ Long.toString(extras.getLong(MediaMetadata.METADATA_KEY_NUM_TRACKS));
+ break;
+
+ case AvrcpConstants_ext.ATTRID_GENRE:
+ attrValue = extras.getString(MediaMetadata.METADATA_KEY_GENRE);
+ break;
+
+ case AvrcpConstants_ext.ATTRID_PLAY_TIME:
+ attrValue = Long.toString(extras.getLong(MediaMetadata.METADATA_KEY_DURATION));
+ break;
+
+ case AvrcpConstants_ext.ATTRID_COVER_ART:
+ if (mAvrcp != null) {
+ attrValue = mAvrcp.getImgHandleFromTitle(bdaddr,
+ desc.getTitle().toString());
+ } else {
+ if (DEBUG) Log.d(TAG, " mAvrcp null ");
+ }
+ break;
+
+ default:
+ Log.e(TAG, "getAttrValue: Unknown attribute ID requested: " + attr);
+ return null;
+ }
+ } catch (NullPointerException ex) {
+ Log.w(TAG, "getAttrValue: attr id not found in result");
+ /* checking if attribute is title, then it is mandatory and cannot send null */
+ if (attr == AvrcpConstants_ext.ATTRID_TITLE) {
+ attrValue = "<Unknown Title>";
+ } else {
+ return null;
+ }
+ }
+ if (DEBUG) {
+ Log.d(TAG, "getAttrValue: attrvalue = " + attrValue + ", attr id: " + attr);
+ }
+ return attrValue;
+ }
+
+ private void getItemAttrFilterAttr(byte[] bdaddr, AvrcpCmd_ext.ItemAttrCmd mItemAttrReqObj,
+ MediaSession.QueueItem mediaItem, @Nullable MediaController mediaController) {
+ /* Response parameters */
+ int[] attrIds = null; /* array of attr ids */
+ String[] attrValues = null; /* array of attr values */
+
+ /* variables to temperorily add attrs */
+ ArrayList<String> attrArray = new ArrayList<String>();
+ ArrayList<Integer> attrId = new ArrayList<Integer>();
+ ArrayList<Integer> attrTempId = new ArrayList<Integer>();
+
+ /* check if remote device has requested for attributes */
+ if (mItemAttrReqObj.mNumAttr != AvrcpConstants_ext.NUM_ATTR_NONE) {
+ if (mItemAttrReqObj.mNumAttr == AvrcpConstants_ext.NUM_ATTR_ALL) {
+ for (int idx = 1; idx < AvrcpConstants_ext.MAX_NUM_ATTR; idx++) {
+ attrTempId.add(idx); /* attr id 0x00 is unused */
+ }
+ } else {
+ /* get only the requested attribute ids from the request */
+ for (int idx = 0; idx < mItemAttrReqObj.mNumAttr; idx++) {
+ if (DEBUG) {
+ Log.d(TAG, "getItemAttrFilterAttr: attr id[" + idx + "] :"
+ + mItemAttrReqObj.mAttrIDs[idx]);
+ }
+ attrTempId.add(mItemAttrReqObj.mAttrIDs[idx]);
+ }
+ }
+ }
+
+ if (DEBUG) {
+ Log.d(TAG, "getItemAttrFilterAttr: attr id list size:" + attrTempId.size());
+ }
+ /* lookup and copy values of attributes for ids requested above */
+ for (int idx = 0; idx < attrTempId.size(); idx++) {
+ /* check if media player provided requested attributes */
+ String value = getAttrValue(bdaddr, attrTempId.get(idx), mediaItem, mediaController);
+ if (value != null) {
+ attrArray.add(value);
+ attrId.add(attrTempId.get(idx));
+ }
+ }
+
+ /* copy filtered attr ids and attr values to response parameters */
+ if (mItemAttrReqObj.mNumAttr != AvrcpConstants_ext.NUM_ATTR_NONE) {
+ attrIds = new int[attrId.size()];
+
+ for (int attrIndex = 0; attrIndex < attrId.size(); attrIndex++) {
+ attrIds[attrIndex] = attrId.get(attrIndex);
+ }
+
+ attrValues = attrArray.toArray(new String[attrId.size()]);
+
+ /* create rsp object and send response */
+ ItemAttrRsp_ext rspObj = new ItemAttrRsp_ext(AvrcpConstants_ext.RSP_NO_ERROR, attrIds, attrValues);
+ mMediaInterface.getItemAttrRsp(bdaddr, AvrcpConstants_ext.RSP_NO_ERROR, rspObj);
+ return;
+ }
+ }
+
+ private long getActiveQueueItemId(@Nullable MediaController controller) {
+ if (controller == null) {
+ return MediaSession.QueueItem.UNKNOWN_ID;
+ }
+ PlaybackState state = controller.getPlaybackState();
+ if (state == null || state.getState() == PlaybackState.STATE_NONE) {
+ return MediaSession.QueueItem.UNKNOWN_ID;
+ }
+ long qid = state.getActiveQueueItemId();
+ if (qid != MediaSession.QueueItem.UNKNOWN_ID) {
+ return qid;
+ }
+ // Check if we're presenting a "one item queue"
+ if (controller.getMetadata() != null) {
+ return SINGLE_QID;
+ }
+ return MediaSession.QueueItem.UNKNOWN_ID;
+ }
+
+ String displayMediaItem(MediaSession.QueueItem item) {
+ StringBuilder sb = new StringBuilder();
+ sb.append("#");
+ sb.append(item.getQueueId());
+ sb.append(": ");
+ sb.append(Utils.ellipsize(getAttrValue(null, AvrcpConstants_ext.ATTRID_TITLE, item, null)));
+ sb.append(" - ");
+ sb.append(Utils.ellipsize(getAttrValue(null, AvrcpConstants_ext.ATTRID_ALBUM, item, null)));
+ sb.append(" by ");
+ sb.append(Utils.ellipsize(getAttrValue(null, AvrcpConstants_ext.ATTRID_ARTIST, item, null)));
+ sb.append(" (");
+ sb.append(getAttrValue(null, AvrcpConstants_ext.ATTRID_PLAY_TIME, item, null));
+ sb.append(" ");
+ sb.append(getAttrValue(null, AvrcpConstants_ext.ATTRID_TRACK_NUM, item, null));
+ sb.append("/");
+ sb.append(getAttrValue(null, AvrcpConstants_ext.ATTRID_NUM_TRACKS, item, null));
+ sb.append(") ");
+ sb.append(getAttrValue(null, AvrcpConstants_ext.ATTRID_GENRE, item, null));
+ return sb.toString();
+ }
+
+ public void dump(StringBuilder sb, @Nullable MediaController mediaController) {
+ ProfileService.println(sb, "AddressedPlayer info:");
+ ProfileService.println(sb, "mLastTrackIdSent: " + mLastTrackIdSent);
+ ProfileService.println(sb, "mNowPlayingList: " + mNowPlayingList.size() + " elements");
+ long currentQueueId = getActiveQueueItemId(mediaController);
+ for (MediaSession.QueueItem item : mNowPlayingList) {
+ long itemId = item.getQueueId();
+ ProfileService.println(sb,
+ (itemId == currentQueueId ? "*" : " ") + displayMediaItem(item));
+ }
+ }
+}
diff --git a/packages_apps_bluetooth_ext/src/avrcp/AvrcpConstants_ext.java b/packages_apps_bluetooth_ext/src/avrcp/AvrcpConstants_ext.java
new file mode 100644
index 0000000..82ef436
--- /dev/null
+++ b/packages_apps_bluetooth_ext/src/avrcp/AvrcpConstants_ext.java
@@ -0,0 +1,179 @@
+/*
+ * 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.
+ */
+
+package com.android.bluetooth.avrcp;
+
+import android.util.Log;
+
+/*************************************************************************************************
+ * Grouped all HAL constants into a file to be consistent with the stack.
+ * Moved the constants used in Avrcp to this new file to be used across multiple files.
+ * Helps in easier modifications and future enhancements in the constants.
+ ************************************************************************************************/
+
+/*
+ * @hide
+ */
+final class AvrcpConstants_ext {
+
+ /* Do not modify without upating the HAL bt_rc.h file */
+ /** Response Error codes **/
+ static final byte RSP_BAD_CMD = 0x00; /* Invalid command */
+ static final byte RSP_BAD_PARAM = 0x01; /* Invalid parameter */
+ static final byte RSP_NOT_FOUND = 0x02; /* Specified parameter is
+ * wrong or not found */
+ static final byte RSP_INTERNAL_ERR = 0x03; /* Internal Error */
+ static final byte RSP_NO_ERROR = 0x04; /* Operation Success */
+ static final byte RSP_UID_CHANGED = 0x05; /* UIDs changed */
+ static final byte RSP_RESERVED = 0x06; /* Reserved */
+ static final byte RSP_INV_DIRN = 0x07; /* Invalid direction */
+ static final byte RSP_INV_DIRECTORY = 0x08; /* Invalid directory */
+ static final byte RSP_INV_ITEM = 0x09; /* Invalid Item */
+ static final byte RSP_INV_SCOPE = 0x0a; /* Invalid scope */
+ static final byte RSP_INV_RANGE = 0x0b; /* Invalid range */
+ static final byte RSP_DIRECTORY = 0x0c; /* UID is a directory */
+ static final byte RSP_MEDIA_IN_USE = 0x0d; /* Media in use */
+ static final byte RSP_PLAY_LIST_FULL = 0x0e; /* Playing list full */
+ static final byte RSP_SRCH_NOT_SPRTD = 0x0f; /* Search not supported */
+ static final byte RSP_SRCH_IN_PROG = 0x10; /* Search in progress */
+ static final byte RSP_INV_PLAYER = 0x11; /* Invalid player */
+ static final byte RSP_PLAY_NOT_BROW = 0x12; /* Player not browsable */
+ static final byte RSP_PLAY_NOT_ADDR = 0x13; /* Player not addressed */
+ static final byte RSP_INV_RESULTS = 0x14; /* Invalid results */
+ static final byte RSP_NO_AVBL_PLAY = 0x15; /* No available players */
+ static final byte RSP_ADDR_PLAY_CHGD = 0x16; /* Addressed player changed */
+
+ /* valid scopes for get_folder_items */
+ static final byte BTRC_SCOPE_PLAYER_LIST = 0x00; /* Media Player List */
+ static final byte BTRC_SCOPE_FILE_SYSTEM = 0x01; /* Virtual File System */
+ static final byte BTRC_SCOPE_SEARCH = 0x02; /* Search */
+ static final byte BTRC_SCOPE_NOW_PLAYING = 0x03; /* Now Playing */
+
+ /* valid directions for change path */
+ static final byte DIR_UP = 0x00;
+ static final byte DIR_DOWN = 0x01;
+
+ /* item type to browse */
+ static final byte BTRC_ITEM_PLAYER = 0x01;
+ static final byte BTRC_ITEM_FOLDER = 0x02;
+ static final byte BTRC_ITEM_MEDIA = 0x03;
+
+ /* valid folder types */
+ static final byte FOLDER_TYPE_MIXED = 0x00;
+ static final byte FOLDER_TYPE_TITLES = 0x01;
+ static final byte FOLDER_TYPE_ALBUMS = 0x02;
+ static final byte FOLDER_TYPE_ARTISTS = 0x03;
+ static final byte FOLDER_TYPE_GENRES = 0x04;
+ static final byte FOLDER_TYPE_PLAYLISTS = 0x05;
+ static final byte FOLDER_TYPE_YEARS = 0x06;
+
+ /* valid playable flags */
+ static final byte ITEM_NOT_PLAYABLE = 0x00;
+ static final byte ITEM_PLAYABLE = 0x01;
+
+ /* valid Attribute ids for media elements */
+ static final int ATTRID_TITLE = 0x01;
+ static final int ATTRID_ARTIST = 0x02;
+ static final int ATTRID_ALBUM = 0x03;
+ static final int ATTRID_TRACK_NUM = 0x04;
+ static final int ATTRID_NUM_TRACKS = 0x05;
+ static final int ATTRID_GENRE = 0x06;
+ static final int ATTRID_PLAY_TIME = 0x07;
+ static final int ATTRID_COVER_ART = 0x08;
+
+ /* constants to send in Track change response */
+ static final byte[] NO_TRACK_SELECTED = {
+ (byte) 0xFF,
+ (byte) 0xFF,
+ (byte) 0xFF,
+ (byte) 0xFF,
+ (byte) 0xFF,
+ (byte) 0xFF,
+ (byte) 0xFF,
+ (byte) 0xFF
+ };
+ static final byte[] TRACK_IS_SELECTED = {
+ (byte) 0x00,
+ (byte) 0x00,
+ (byte) 0x00,
+ (byte) 0x00,
+ (byte) 0x00,
+ (byte) 0x00,
+ (byte) 0x00,
+ (byte) 0x00
+ };
+
+ /* UID size */
+ static final int UID_SIZE = 8;
+
+ static final short DEFAULT_UID_COUNTER = 0x0000;
+
+ /* Bitmask size for Media Players */
+ static final int AVRC_FEATURE_MASK_SIZE = 16;
+
+ /* Maximum attributes for media item */
+ static final int MAX_NUM_ATTR = 8;
+
+ /* notification types for remote device */
+ static final int NOTIFICATION_TYPE_INTERIM = 0;
+ static final int NOTIFICATION_TYPE_CHANGED = 1;
+
+ static final int TRACK_ID_SIZE = 8;
+
+ /* player feature bit mask constants */
+ static final short AVRC_PF_PLAY_BIT_NO = 40;
+ static final short AVRC_PF_STOP_BIT_NO = 41;
+ static final short AVRC_PF_PAUSE_BIT_NO = 42;
+ static final short AVRC_PF_REWIND_BIT_NO = 44;
+ static final short AVRC_PF_FAST_FWD_BIT_NO = 45;
+ static final short AVRC_PF_FORWARD_BIT_NO = 47;
+ static final short AVRC_PF_BACKWARD_BIT_NO = 48;
+ static final short AVRC_PF_ADV_CTRL_BIT_NO = 58;
+ static final short AVRC_PF_BROWSE_BIT_NO = 59;
+ static final short AVRC_PF_ADD2NOWPLAY_BIT_NO = 61;
+ static final short AVRC_PF_UID_UNIQUE_BIT_NO = 62;
+ static final short AVRC_PF_NOW_PLAY_BIT_NO = 65;
+ static final short AVRC_PF_GET_NUM_OF_ITEMS_BIT_NO = 67;
+ static final short AVRC_PF_COVER_ART_BIT_NO = 68;
+ static final byte PLAYER_TYPE_AUDIO = 1;
+ static final int PLAYER_SUBTYPE_NONE = 0;
+
+ // match up with btrc_play_status_t enum of bt_rc.h
+ static final int PLAYSTATUS_STOPPED = 0;
+ static final int PLAYSTATUS_PLAYING = 1;
+ static final int PLAYSTATUS_PAUSED = 2;
+ static final int PLAYSTATUS_FWD_SEEK = 3;
+ static final int PLAYSTATUS_REV_SEEK = 4;
+ static final int PLAYSTATUS_ERROR = 255;
+
+ static final byte NUM_ATTR_ALL = (byte) 0x00;
+ static final byte NUM_ATTR_NONE = (byte) 0xFF;
+
+ static final int KEY_STATE_PRESS = 1;
+ static final int KEY_STATE_RELEASE = 0;
+
+ static final int GET_ATTRIBUTE_IDS = 0;
+ static final int GET_VALUE_IDS = 1;
+ static final int GET_ATTRIBUTE_TEXT = 2;
+ static final int GET_VALUE_TEXT = 3;
+ static final int GET_ATTRIBUTE_VALUES = 4;
+ static final int NOTIFY_ATTRIBUTE_VALUES = 5;
+ static final int SET_ATTRIBUTE_VALUES = 6;
+ static final int GET_INVALID = 0xff;
+
+ public static final String TAG = "Avrcp";
+ public static final boolean VERBOSE = Log.isLoggable(TAG, Log.VERBOSE);
+}
diff --git a/packages_apps_bluetooth_ext/src/avrcp/AvrcpHelperClasses_ext.java b/packages_apps_bluetooth_ext/src/avrcp/AvrcpHelperClasses_ext.java
new file mode 100644
index 0000000..8aca354
--- /dev/null
+++ b/packages_apps_bluetooth_ext/src/avrcp/AvrcpHelperClasses_ext.java
@@ -0,0 +1,451 @@
+/*
+ * 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.
+ */
+
+package com.android.bluetooth.avrcp;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+
+import com.android.bluetooth.Utils;
+
+import java.util.ArrayDeque;
+import java.util.Arrays;
+import java.util.Collection;
+
+/*************************************************************************************************
+ * Helper classes used for callback/response of browsing commands:-
+ * 1) To bundle parameters for native callbacks/response.
+ * 2) Stores information of Addressed and Browsed Media Players.
+ ************************************************************************************************/
+
+class AvrcpCmd_ext {
+
+ AvrcpCmd_ext() {}
+
+ /* Helper classes to pass parameters from callbacks to Avrcp handler */
+ class FolderItemsCmd {
+ byte mScope;
+ long mStartItem;
+ long mEndItem;
+ byte mNumAttr;
+ int[] mAttrIDs;
+ public byte[] mAddress;
+
+ FolderItemsCmd(byte[] address, byte scope, long startItem, long endItem, byte numAttr,
+ int[] attrIds) {
+ mAddress = address;
+ this.mScope = scope;
+ this.mStartItem = startItem;
+ this.mEndItem = endItem;
+ this.mNumAttr = numAttr;
+ this.mAttrIDs = attrIds;
+ }
+
+ @Override
+ public String toString() {
+ StringBuilder sb = new StringBuilder();
+ sb.append("[FolderItemCmd: scope " + mScope);
+ sb.append(" start " + mStartItem);
+ sb.append(" end " + mEndItem);
+ sb.append(" numAttr " + mNumAttr);
+ sb.append(" attrs: ");
+ for (int i = 0; i < mNumAttr; i++) {
+ sb.append(mAttrIDs[i] + " ");
+ }
+ return sb.toString();
+ }
+ }
+
+ class ItemAttrCmd {
+ byte mScope;
+ byte[] mUid;
+ int mUidCounter;
+ byte mNumAttr;
+ int[] mAttrIDs;
+ public byte[] mAddress;
+
+ ItemAttrCmd(byte[] address, byte scope, byte[] uid, int uidCounter, byte numAttr,
+ int[] attrIDs) {
+ mAddress = address;
+ mScope = scope;
+ mUid = uid;
+ mUidCounter = uidCounter;
+ mNumAttr = numAttr;
+ mAttrIDs = attrIDs;
+ }
+
+ @Override
+ public String toString() {
+ StringBuilder sb = new StringBuilder();
+ sb.append("[ItemAttrCmd: scope " + mScope);
+ sb.append(" uid " + Utils.byteArrayToString(mUid));
+ sb.append(" numAttr " + mNumAttr);
+ sb.append(" attrs: ");
+ for (int i = 0; i < mNumAttr; i++) {
+ sb.append(mAttrIDs[i] + " ");
+ }
+ return sb.toString();
+ }
+ }
+
+ class ElementAttrCmd {
+ byte mNumAttr;
+ int[] mAttrIDs;
+ public byte[] mAddress;
+
+ ElementAttrCmd(byte[] address, byte numAttr, int[] attrIDs) {
+ mAddress = address;
+ mNumAttr = numAttr;
+ mAttrIDs = attrIDs;
+ }
+ }
+}
+
+/* Helper classes to pass parameters to native response */
+class MediaPlayerListRsp_ext {
+ byte mStatus;
+ short mUIDCounter;
+ byte mItemType;
+ int[] mPlayerIds;
+ byte[] mPlayerTypes;
+ int[] mPlayerSubTypes;
+ byte[] mPlayStatusValues;
+ short[] mFeatureBitMaskValues;
+ String[] mPlayerNameList;
+ int mNumItems;
+
+ MediaPlayerListRsp_ext(byte status, short uidCounter, int numItems, byte itemType, int[] playerIds,
+ byte[] playerTypes, int[] playerSubTypes, byte[] playStatusValues,
+ short[] featureBitMaskValues, String[] playerNameList) {
+ this.mStatus = status;
+ this.mUIDCounter = uidCounter;
+ this.mNumItems = numItems;
+ this.mItemType = itemType;
+ this.mPlayerIds = playerIds;
+ this.mPlayerTypes = playerTypes;
+ this.mPlayerSubTypes = new int[numItems];
+ this.mPlayerSubTypes = playerSubTypes;
+ this.mPlayStatusValues = new byte[numItems];
+ this.mPlayStatusValues = playStatusValues;
+ int bitMaskSize = AvrcpConstants_ext.AVRC_FEATURE_MASK_SIZE;
+ this.mFeatureBitMaskValues = new short[numItems * bitMaskSize];
+ for (int bitMaskIndex = 0; bitMaskIndex < (numItems * bitMaskSize); bitMaskIndex++) {
+ this.mFeatureBitMaskValues[bitMaskIndex] = featureBitMaskValues[bitMaskIndex];
+ }
+ this.mPlayerNameList = playerNameList;
+ }
+}
+
+class FolderItemsRsp_ext {
+ byte mStatus;
+ short mUIDCounter;
+ byte mScope;
+ int mNumItems;
+ byte[] mFolderTypes;
+ byte[] mPlayable;
+ byte[] mItemTypes;
+ byte[] mItemUid;
+ String[] mDisplayNames; /* display name of the item. Eg: Folder name or song name */
+ int[] mAttributesNum;
+ int[] mAttrIds;
+ String[] mAttrValues;
+
+ FolderItemsRsp_ext(byte status, short uidCounter, byte scope, int numItems, byte[] folderTypes,
+ byte[] playable, byte[] itemTypes, byte[] itemsUid, String[] displayNameArray,
+ int[] attributesNum, int[] attrIds, String[] attrValues) {
+ this.mStatus = status;
+ this.mUIDCounter = uidCounter;
+ this.mScope = scope;
+ this.mNumItems = numItems;
+ this.mFolderTypes = folderTypes;
+ this.mPlayable = playable;
+ this.mItemTypes = itemTypes;
+ this.mItemUid = itemsUid;
+ this.mDisplayNames = displayNameArray;
+ this.mAttributesNum = attributesNum;
+ this.mAttrIds = attrIds;
+ this.mAttrValues = attrValues;
+ }
+}
+
+class ItemAttrRsp_ext {
+ byte mStatus;
+ byte mNumAttr;
+ int[] mAttributesIds;
+ String[] mAttributesArray;
+
+ ItemAttrRsp_ext(byte status, int[] attributesIds, String[] attributesArray) {
+ mStatus = status;
+ mNumAttr = (byte) attributesIds.length;
+ mAttributesIds = attributesIds;
+ mAttributesArray = attributesArray;
+ }
+}
+
+/* stores information of Media Players in the system */
+class MediaPlayerInfo_ext {
+
+ private byte mMajorType;
+ private int mSubType;
+ private byte mPlayStatus;
+ private short[] mFeatureBitMask;
+ @NonNull private String mPackageName;
+ @NonNull private String mDisplayableName;
+ @Nullable private MediaController mMediaController;
+
+ MediaPlayerInfo_ext(@Nullable MediaController controller, byte majorType, int subType,
+ byte playStatus, short[] featureBitMask, @NonNull String packageName,
+ @Nullable String displayableName) {
+ this.setMajorType(majorType);
+ this.setSubType(subType);
+ this.mPlayStatus = playStatus;
+ // store a copy the FeatureBitMask array
+ this.mFeatureBitMask = Arrays.copyOf(featureBitMask, featureBitMask.length);
+ Arrays.sort(this.mFeatureBitMask);
+ this.setPackageName(packageName);
+ this.setDisplayableName(displayableName);
+ this.setMediaController(controller);
+ }
+
+ /* getters and setters */
+ byte getPlayStatus() {
+ return mPlayStatus;
+ }
+
+ void setPlayStatus(byte playStatus) {
+ this.mPlayStatus = playStatus;
+ }
+
+ MediaController getMediaController() {
+ return mMediaController;
+ }
+
+ void setMediaController(MediaController mediaController) {
+ if (mediaController != null) {
+ this.mPackageName = mediaController.getPackageName();
+ }
+ this.mMediaController = mediaController;
+ }
+
+ void setPackageName(@NonNull String name) {
+ // Controller determines package name when it is set.
+ if (mMediaController != null) {
+ return;
+ }
+ this.mPackageName = name;
+ }
+
+ String getPackageName() {
+ if (mMediaController != null) {
+ return mMediaController.getPackageName();
+ } else if (mPackageName != null) {
+ return mPackageName;
+ }
+ return null;
+ }
+
+ byte getMajorType() {
+ return mMajorType;
+ }
+
+ void setMajorType(byte majorType) {
+ this.mMajorType = majorType;
+ }
+
+ int getSubType() {
+ return mSubType;
+ }
+
+ void setSubType(int subType) {
+ this.mSubType = subType;
+ }
+
+ String getDisplayableName() {
+ return mDisplayableName;
+ }
+
+ void setDisplayableName(@Nullable String displayableName) {
+ if (displayableName == null) {
+ displayableName = "";
+ }
+ this.mDisplayableName = displayableName;
+ }
+
+ short[] getFeatureBitMask() {
+ return mFeatureBitMask;
+ }
+
+ void setFeatureBitMask(short[] featureBitMask) {
+ synchronized (this) {
+ this.mFeatureBitMask = Arrays.copyOf(featureBitMask, featureBitMask.length);
+ Arrays.sort(this.mFeatureBitMask);
+ }
+ }
+
+ boolean isBrowseSupported() {
+ synchronized (this) {
+ if (this.mFeatureBitMask == null) {
+ return false;
+ }
+ for (short bit : this.mFeatureBitMask) {
+ if (bit == AvrcpConstants_ext.AVRC_PF_BROWSE_BIT_NO) {
+ return true;
+ }
+ }
+ }
+ return false;
+ }
+
+ /** Tests if the view of this player presented to the controller is different enough to
+ * justify sending an Available Players Changed update */
+ public boolean equalView(MediaPlayerInfo_ext other) {
+ return (this.mMajorType == other.getMajorType()) && (this.mSubType == other.getSubType())
+ && Arrays.equals(this.mFeatureBitMask, other.getFeatureBitMask())
+ && this.mDisplayableName.equals(other.getDisplayableName());
+ }
+
+ @Override
+ public String toString() {
+ StringBuilder sb = new StringBuilder();
+ sb.append("MediaPlayerInfo ");
+ sb.append(getPackageName());
+ sb.append(" (as '" + getDisplayableName() + "')");
+ sb.append(" Type = " + getMajorType());
+ sb.append(", SubType = " + getSubType());
+ sb.append(", Status = " + mPlayStatus);
+ sb.append(" Feature Bits [");
+ short[] bits = getFeatureBitMask();
+ for (int i = 0; i < bits.length; i++) {
+ if (i != 0) {
+ sb.append(" ");
+ }
+ sb.append(bits[i]);
+ }
+ sb.append("] Controller: ");
+ sb.append(getMediaController());
+ return sb.toString();
+ }
+}
+
+/* stores information for browsable Media Players available in the system */
+class BrowsePlayerInfo_ext {
+ public String packageName;
+ public String displayableName;
+ public String serviceClass;
+
+ BrowsePlayerInfo_ext(String packageName, String displayableName, String serviceClass) {
+ this.packageName = packageName;
+ this.displayableName = displayableName;
+ this.serviceClass = serviceClass;
+ }
+
+ @Override
+ public String toString() {
+ StringBuilder sb = new StringBuilder();
+ sb.append("BrowsePlayerInfo_ext ");
+ sb.append(packageName);
+ sb.append(" ( as '" + displayableName + "')");
+ sb.append(" service " + serviceClass);
+ return sb.toString();
+ }
+}
+
+class FolderItemsData_ext {
+ /* initialize sizes for rsp parameters */ int mNumItems;
+ int[] mAttributesNum;
+ byte[] mFolderTypes;
+ byte[] mItemTypes;
+ byte[] mPlayable;
+ byte[] mItemUid;
+ String[] mDisplayNames;
+ int[] mAttrIds;
+ String[] mAttrValues;
+ int mAttrCounter;
+
+ FolderItemsData_ext(int size) {
+ mNumItems = size;
+ mAttributesNum = new int[size];
+
+ mFolderTypes = new byte[size]; /* folderTypes */
+ mItemTypes = new byte[size]; /* folder or media item */
+ mPlayable = new byte[size];
+ Arrays.fill(mFolderTypes, AvrcpConstants_ext.FOLDER_TYPE_MIXED);
+ Arrays.fill(mItemTypes, AvrcpConstants_ext.BTRC_ITEM_MEDIA);
+ Arrays.fill(mPlayable, AvrcpConstants_ext.ITEM_PLAYABLE);
+
+ mItemUid = new byte[size * AvrcpConstants_ext.UID_SIZE];
+ mDisplayNames = new String[size];
+
+ mAttrIds = null; /* array of attr ids */
+ mAttrValues = null; /* array of attr values */
+ }
+}
+
+/** A queue that evicts the first element when you add an element to the end when it reaches a
+ * maximum size.
+ * This is useful for keeping a FIFO queue of items where the items drop off the front, i.e. a log
+ * with a maximum size.
+ */
+class EvictingQueue_ext<E> extends ArrayDeque<E> {
+ private int mMaxSize;
+
+ EvictingQueue_ext(int maxSize) {
+ super();
+ mMaxSize = maxSize;
+ }
+
+ EvictingQueue_ext(int maxSize, int initialElements) {
+ super(initialElements);
+ mMaxSize = maxSize;
+ }
+
+ EvictingQueue_ext(int maxSize, Collection<? extends E> c) {
+ super(c);
+ mMaxSize = maxSize;
+ }
+
+ @Override
+ public void addFirst(E e) {
+ if (super.size() == mMaxSize) {
+ return;
+ }
+ super.addFirst(e);
+ }
+
+ @Override
+ public void addLast(E e) {
+ if (super.size() == mMaxSize) {
+ super.remove();
+ }
+ super.addLast(e);
+ }
+
+ @Override
+ public boolean offerFirst(E e) {
+ if (super.size() == mMaxSize) {
+ return false;
+ }
+ return super.offerFirst(e);
+ }
+
+ @Override
+ public boolean offerLast(E e) {
+ if (super.size() == mMaxSize) {
+ super.remove();
+ }
+ return super.offerLast(e);
+ }
+}
diff --git a/packages_apps_bluetooth_ext/src/avrcp/AvrcpMediaRspInterface_ext.java b/packages_apps_bluetooth_ext/src/avrcp/AvrcpMediaRspInterface_ext.java
new file mode 100644
index 0000000..0eaa2fa
--- /dev/null
+++ b/packages_apps_bluetooth_ext/src/avrcp/AvrcpMediaRspInterface_ext.java
@@ -0,0 +1,53 @@
+/*
+ * 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.
+ */
+
+package com.android.bluetooth.avrcp;
+
+
+/*************************************************************************************************
+ * Interface for classes which handle callbacks from AvrcpMediaManager.
+ * These callbacks should map to native responses and used to communicate with the native layer.
+ ************************************************************************************************/
+
+public interface AvrcpMediaRspInterface_ext {
+ void setAddrPlayerRsp(byte[] address, int rspStatus);
+
+ void setBrowsedPlayerRsp(byte[] address, int rspStatus, byte depth, int numItems,
+ String[] textArray);
+
+ void mediaPlayerListRsp(byte[] address, int rspStatus, MediaPlayerListRsp_ext rspObj);
+
+ void folderItemsRsp(byte[] address, int rspStatus, FolderItemsRsp_ext rspObj);
+
+ void changePathRsp(byte[] address, int rspStatus, int numItems);
+
+ void getItemAttrRsp(byte[] address, int rspStatus, ItemAttrRsp_ext rspObj);
+
+ void playItemRsp(byte[] address, int rspStatus);
+
+ void getTotalNumOfItemsRsp(byte[] address, int rspStatus, int uidCounter, int numItems);
+
+ void addrPlayerChangedRsp(int type, int playerId, int uidCounter);
+
+ void avalPlayerChangedRsp(byte[] address, int type);
+
+ void uidsChangedRsp(int type);
+
+ void nowPlayingChangedRsp(int type);
+
+ void trackChangedRsp(int type, byte[] uid);
+}
+
diff --git a/packages_apps_bluetooth_ext/src/avrcp/AvrcpPlayerAppSettings.java b/packages_apps_bluetooth_ext/src/avrcp/AvrcpPlayerAppSettings.java
index 18fcd7e..0ca60a0 100644
--- a/packages_apps_bluetooth_ext/src/avrcp/AvrcpPlayerAppSettings.java
+++ b/packages_apps_bluetooth_ext/src/avrcp/AvrcpPlayerAppSettings.java
@@ -31,7 +31,7 @@
import android.content.Context;
import android.bluetooth.BluetoothDevice;
import com.android.bluetooth.avrcp.AvrcpPlayerAppSettingsRspInterface;
-import com.android.bluetooth.avrcp.AvrcpConstants;
+import com.android.bluetooth.avrcp.AvrcpConstants_ext;
import android.content.BroadcastReceiver;
import com.android.bluetooth.Utils;
import java.util.ArrayList;
@@ -164,7 +164,7 @@
String action = intent.getAction();
if (action.equals(PLAYERSETTINGS_RESPONSE)) {
int getResponse = intent.getIntExtra(EXTRA_GET_RESPONSE,
- AvrcpConstants.GET_INVALID);
+ AvrcpConstants_ext.GET_INVALID);
byte [] data;
String [] text;
boolean isSetAttrValRsp = false;
@@ -173,7 +173,7 @@
synchronized (mPendingCmds) {
Integer val = new Integer(getResponse);
if (mPendingCmds.contains(val)) {
- if (getResponse == AvrcpConstants.SET_ATTRIBUTE_VALUES) {
+ if (getResponse == AvrcpConstants_ext.SET_ATTRIBUTE_VALUES) {
isSetAttrValRsp = true;
if (DEBUG) Log.v(TAG,"Response received for SET_ATTRIBUTE_VALUES");
}
@@ -187,7 +187,7 @@
if (DEBUG)
Log.v(TAG,"getResponse" + getResponse);
switch (getResponse) {
- case AvrcpConstants.GET_ATTRIBUTE_IDS:
+ case AvrcpConstants_ext.GET_ATTRIBUTE_IDS:
if (device == null) {
Log.e(TAG,"ERROR!!! device is null");
return;
@@ -200,7 +200,7 @@
data ,getByteAddress(device));
break;
- case AvrcpConstants.GET_VALUE_IDS:
+ case AvrcpConstants_ext.GET_VALUE_IDS:
if (device == null) {
Log.e(TAG,"ERROR!!! device is null");
return;
@@ -220,7 +220,7 @@
mAvrcpPlayerAppSettingsRspInterface.getPlayerAppValueRsp(numAttr, data,
getByteAddress(device));
break;
- case AvrcpConstants.GET_ATTRIBUTE_VALUES:
+ case AvrcpConstants_ext.GET_ATTRIBUTE_VALUES:
if (device == null) {
Log.e(TAG,"ERROR!!! device is null");
return;
@@ -233,7 +233,7 @@
mAvrcpPlayerAppSettingsRspInterface.SendCurrentPlayerValueRsp(numAttr ,
data, getByteAddress(device));
break;
- case AvrcpConstants.SET_ATTRIBUTE_VALUES:
+ case AvrcpConstants_ext.SET_ATTRIBUTE_VALUES:
boolean send_change_rsp_only = true;
data = intent.getByteArrayExtra(EXTRA_ATTRIB_VALUE_PAIRS);
updateLocalPlayerSettings(data);
@@ -243,19 +243,19 @@
Log.v(TAG,"Respond to SET_ATTRIBUTE_VALUES request");
if (checkPlayerAttributeResponse(data)) {
mAvrcpPlayerAppSettingsRspInterface.SendSetPlayerAppRsp(
- AvrcpConstants.RSP_NO_ERROR, getByteAddress(device));
+ AvrcpConstants_ext.RSP_NO_ERROR, getByteAddress(device));
} else {
mAvrcpPlayerAppSettingsRspInterface.SendSetPlayerAppRsp(
- AvrcpConstants.RSP_INTERNAL_ERR, getByteAddress(device));
+ AvrcpConstants_ext.RSP_INTERNAL_ERR, getByteAddress(device));
}
mPendingSetAttributes.clear();
}
if (send_change_rsp_only) {
mAvrcpPlayerAppSettingsRspInterface.SendSetPlayerAppRsp(
- AvrcpConstants.RSP_NO_ERROR, null);
+ AvrcpConstants_ext.RSP_NO_ERROR, null);
}
break;
- case AvrcpConstants.GET_ATTRIBUTE_TEXT:
+ case AvrcpConstants_ext.GET_ATTRIBUTE_TEXT:
text = intent.getStringArrayExtra(EXTRA_ATTRIBUTE_STRING_ARRAY);
if (device == null) {
Log.e(TAG," ERROR!!! device is null");
@@ -268,7 +268,7 @@
Log.v(TAG,"mPlayerSettings.attrIds"
+ mPlayerSettings.attrIds.length);
break;
- case AvrcpConstants.GET_VALUE_TEXT:
+ case AvrcpConstants_ext.GET_VALUE_TEXT:
text = intent.getStringArrayExtra(EXTRA_VALUE_STRING_ARRAY);
if (device == null) {
Log.e(TAG,"ERROR!!! device is null");
@@ -293,12 +293,12 @@
mPendingCmds.remove(val);
}
switch (cmd) {
- case AvrcpConstants.GET_ATTRIBUTE_IDS:
+ case AvrcpConstants_ext.GET_ATTRIBUTE_IDS:
mAvrcpPlayerAppSettingsRspInterface.getListPlayerappAttrRsp(
(byte)def_attrib.length ,
def_attrib, getByteAddress(device));
break;
- case AvrcpConstants.GET_VALUE_IDS:
+ case AvrcpConstants_ext.GET_VALUE_IDS:
byte attrib = 0;
if (!mPlayerSettingCmds.isEmpty()) {
attrib = mPlayerSettingCmds.get(0);
@@ -326,7 +326,7 @@
break;
}
break;
- case AvrcpConstants.GET_ATTRIBUTE_VALUES:
+ case AvrcpConstants_ext.GET_ATTRIBUTE_VALUES:
int j = 0;
byte [] retVal = new byte [mPlayerSettings.attrIds.length*2];
for (int i = 0; i < mPlayerSettings.attrIds.length; i++) {
@@ -342,11 +342,11 @@
mAvrcpPlayerAppSettingsRspInterface.SendCurrentPlayerValueRsp((byte)retVal.length,
retVal, getByteAddress(device));
break;
- case AvrcpConstants.SET_ATTRIBUTE_VALUES :
+ case AvrcpConstants_ext.SET_ATTRIBUTE_VALUES :
mAvrcpPlayerAppSettingsRspInterface.SendSetPlayerAppRsp(
- AvrcpConstants.RSP_INTERNAL_ERR, getByteAddress(device));
+ AvrcpConstants_ext.RSP_INTERNAL_ERR, getByteAddress(device));
break;
- case AvrcpConstants.GET_ATTRIBUTE_TEXT:
+ case AvrcpConstants_ext.GET_ATTRIBUTE_TEXT:
String [] attribText = new String [mPlayerSettings.attrIds.length];
for (int i = 0; i < mPlayerSettings.attrIds.length; i++) {
attribText[i] = "";
@@ -355,7 +355,7 @@
mPlayerSettings.attrIds.length, mPlayerSettings.attrIds,
attribText.length, attribText, getByteAddress(device));
break;
- case AvrcpConstants.GET_VALUE_TEXT:
+ case AvrcpConstants_ext.GET_VALUE_TEXT:
String [] valueText = new String [mPlayerSettings.attrIds.length];
for (int i = 0; i < mPlayerSettings.attrIds.length; i++) {
valueText[i] = "";
@@ -374,19 +374,19 @@
public void onListPlayerAttributeRequest(byte[] address) {
Intent intent = new Intent(PLAYERSETTINGS_REQUEST);
intent.putExtra(COMMAND, CMDGET);
- intent.putExtra(EXTRA_GET_COMMAND, AvrcpConstants.GET_ATTRIBUTE_IDS);
+ intent.putExtra(EXTRA_GET_COMMAND, AvrcpConstants_ext.GET_ATTRIBUTE_IDS);
mContext.sendBroadcast(intent, BLUETOOTH_PERM);
- mPendingCmds.add(new Integer(AvrcpConstants.GET_ATTRIBUTE_IDS));
+ mPendingCmds.add(new Integer(AvrcpConstants_ext.GET_ATTRIBUTE_IDS));
}
public void onListPlayerAttributeValues (byte attr, byte[] address) {
Intent intent = new Intent(PLAYERSETTINGS_REQUEST);
intent.putExtra(COMMAND, CMDGET);
- intent.putExtra(EXTRA_GET_COMMAND, AvrcpConstants.GET_VALUE_IDS);
+ intent.putExtra(EXTRA_GET_COMMAND, AvrcpConstants_ext.GET_VALUE_IDS);
intent.putExtra(EXTRA_ATTRIBUTE_ID, attr);
mContext.sendBroadcast(intent, BLUETOOTH_PERM);
mPlayerSettingCmds.add(attr);
- mPendingCmds.add(new Integer(AvrcpConstants.GET_VALUE_IDS));
+ mPendingCmds.add(new Integer(AvrcpConstants_ext.GET_VALUE_IDS));
}
public void onGetPlayerAttributeValues (byte attr ,int[] arr ,
@@ -400,10 +400,10 @@
mPlayerSettings.attrIds[i] = barray[i];
Intent intent = new Intent(PLAYERSETTINGS_REQUEST);
intent.putExtra(COMMAND, CMDGET);
- intent.putExtra(EXTRA_GET_COMMAND, AvrcpConstants.GET_ATTRIBUTE_VALUES);
+ intent.putExtra(EXTRA_GET_COMMAND, AvrcpConstants_ext.GET_ATTRIBUTE_VALUES);
intent.putExtra(EXTRA_ATTIBUTE_ID_ARRAY, barray);
mContext.sendBroadcast(intent, BLUETOOTH_PERM);
- mPendingCmds.add(new Integer(AvrcpConstants.GET_ATTRIBUTE_VALUES));
+ mPendingCmds.add(new Integer(AvrcpConstants_ext.GET_ATTRIBUTE_VALUES));
}
public void setPlayerAppSetting( byte num, byte [] attr_id, byte [] attr_val,
@@ -419,34 +419,34 @@
intent.putExtra(COMMAND, CMDSET);
intent.putExtra(EXTRA_ATTRIB_VALUE_PAIRS, array);
mContext.sendBroadcast(intent, BLUETOOTH_PERM);
- mPendingCmds.add(new Integer(AvrcpConstants.SET_ATTRIBUTE_VALUES));
+ mPendingCmds.add(new Integer(AvrcpConstants_ext.SET_ATTRIBUTE_VALUES));
}
public void getplayerattribute_text(byte attr , byte [] attrIds,
byte[] address) {
Intent intent = new Intent(PLAYERSETTINGS_REQUEST);
intent.putExtra(COMMAND, CMDGET);
- intent.putExtra(EXTRA_GET_COMMAND, AvrcpConstants.GET_ATTRIBUTE_TEXT);
+ intent.putExtra(EXTRA_GET_COMMAND, AvrcpConstants_ext.GET_ATTRIBUTE_TEXT);
intent.putExtra(EXTRA_ATTIBUTE_ID_ARRAY, attrIds);
mPlayerSettings.attrIds = new byte [attr];
for (int i = 0; i < attr; i++)
mPlayerSettings.attrIds[i] = attrIds[i];
mContext.sendBroadcast(intent, BLUETOOTH_PERM);
- mPendingCmds.add(new Integer(AvrcpConstants.GET_ATTRIBUTE_TEXT));
+ mPendingCmds.add(new Integer(AvrcpConstants_ext.GET_ATTRIBUTE_TEXT));
}
public void getplayervalue_text(byte attr_id , byte num_value , byte [] value,
byte[] address) {
Intent intent = new Intent(PLAYERSETTINGS_REQUEST);
intent.putExtra(COMMAND, CMDGET);
- intent.putExtra(EXTRA_GET_COMMAND, AvrcpConstants.GET_VALUE_TEXT);
+ intent.putExtra(EXTRA_GET_COMMAND, AvrcpConstants_ext.GET_VALUE_TEXT);
intent.putExtra(EXTRA_ATTRIBUTE_ID, attr_id);
intent.putExtra(EXTRA_VALUE_ID_ARRAY, value);
mPlayerSettings.attrIds = new byte [num_value];
for (int i = 0; i < num_value; i++)
mPlayerSettings.attrIds[i] = value[i];
mContext.sendBroadcast(intent, BLUETOOTH_PERM);
- mPendingCmds.add(new Integer(AvrcpConstants.GET_VALUE_TEXT));
+ mPendingCmds.add(new Integer(AvrcpConstants_ext.GET_VALUE_TEXT));
}
public void sendPlayerAppChangedRsp(int rsptype, BluetoothDevice device) {
diff --git a/packages_apps_bluetooth_ext/src/avrcp/Avrcp_ext.java b/packages_apps_bluetooth_ext/src/avrcp/Avrcp_ext.java
index 0e127bd..8903fcf 100644
--- a/packages_apps_bluetooth_ext/src/avrcp/Avrcp_ext.java
+++ b/packages_apps_bluetooth_ext/src/avrcp/Avrcp_ext.java
@@ -8,7 +8,7 @@
* 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
@@ -169,7 +169,7 @@
private static final String [] BlacklistDeviceAddrToMediaAttr = {"00:17:53"/*Toyota Etios*/};
private boolean ignore_play;
private byte changePathFolderType;
- private FolderItemsRsp saveRspObj;
+ private FolderItemsRsp_ext saveRspObj;
private int changePathDepth;
private byte changePathDirection;
HashMap<BluetoothDevice, Integer> mVolumeMap = new HashMap();
@@ -191,7 +191,7 @@
};
/* UID counter to be shared across different files. */
- static short sUIDCounter = AvrcpConstants.DEFAULT_UID_COUNTER;
+ static short sUIDCounter = AvrcpConstants_ext.DEFAULT_UID_COUNTER;
/* BTRC features */
public static final int BTRC_FEAT_METADATA = 0x01;
@@ -236,7 +236,6 @@
private final static int MESSAGE_SET_MEDIA_SESSION = 24;
private final static int MSG_SET_AVRCP_CONNECTED_DEVICE = 25;
private final static int MESSAGE_UPDATE_ABS_VOLUME_STATUS = 31;
- private final static int MESSAGE_UPDATE_ABSOLUTE_VOLUME = 32;
private static final int MSG_PLAY_STATUS_CMD_TIMEOUT = 33;
private final static int MESSAGE_START_SHO = 34;
@@ -251,17 +250,17 @@
private static final int SET_MEDIA_SESSION_DELAY = 300;
/* Communicates with MediaPlayer to fetch media content */
- private BrowsedMediaPlayer mBrowsedMediaPlayer;
+ private BrowsedMediaPlayer_ext mBrowsedMediaPlayer;
/* Addressed player handling */
- private AddressedMediaPlayer mAddressedMediaPlayer;
+ private AddressedMediaPlayer_ext mAddressedMediaPlayer;
/* List of Media player instances, useful for retrieving MediaPlayerList or MediaPlayerInfo */
- private SortedMap<Integer, MediaPlayerInfo> mMediaPlayerInfoList;
+ private SortedMap<Integer, MediaPlayerInfo_ext> mMediaPlayerInfoList;
private boolean mAvailablePlayerViewChanged;
/* List of media players which supports browse */
- private List<BrowsePlayerInfo> mBrowsePlayerInfoList;
+ private List<BrowsePlayerInfo_ext> mBrowsePlayerInfoList;
/* Manage browsed players */
private AvrcpBrowseManager mAvrcpBrowseManager;
@@ -278,7 +277,7 @@
/* Recording passthrough key dispatches */
static private final int PASSTHROUGH_LOG_MAX_SIZE = DEBUG ? 50 : 10;
- private EvictingQueue<MediaKeyLog> mPassthroughLogs; // Passthorugh keys dispatched
+ private EvictingQueue_ext<MediaKeyLog> mPassthroughLogs; // Passthorugh keys dispatched
private List<MediaKeyLog> mPassthroughPending; // Passthrough keys sent not dispatched yet
private int mPassthroughDispatched; // Number of keys dispatched
@@ -369,16 +368,16 @@
mContext = context;
mCurrentDevice = null;
mCurrentPlayState = new PlaybackState.Builder().setState(PlaybackState.STATE_NONE, -1L, 0.0f).build();;
- mPlayStatusChangedNT = AvrcpConstants.NOTIFICATION_TYPE_CHANGED;
- mNowPlayingListChangedNT = AvrcpConstants.NOTIFICATION_TYPE_CHANGED;
- mAddrPlayerChangedNT = AvrcpConstants.NOTIFICATION_TYPE_CHANGED;
- mTrackChangedNT = AvrcpConstants.NOTIFICATION_TYPE_CHANGED;
- mPlayerStatusChangeNT = AvrcpConstants.NOTIFICATION_TYPE_CHANGED;
+ mPlayStatusChangedNT = AvrcpConstants_ext.NOTIFICATION_TYPE_CHANGED;
+ mNowPlayingListChangedNT = AvrcpConstants_ext.NOTIFICATION_TYPE_CHANGED;
+ mAddrPlayerChangedNT = AvrcpConstants_ext.NOTIFICATION_TYPE_CHANGED;
+ mTrackChangedNT = AvrcpConstants_ext.NOTIFICATION_TYPE_CHANGED;
+ mPlayerStatusChangeNT = AvrcpConstants_ext.NOTIFICATION_TYPE_CHANGED;
mNextPosMs = -1;
mPrevPosMs = -1;
mPlaybackIntervalMs = 0L;
mLastReportedPosition = -1;
- mPlayPosChangedNT = AvrcpConstants.NOTIFICATION_TYPE_CHANGED;
+ mPlayPosChangedNT = AvrcpConstants_ext.NOTIFICATION_TYPE_CHANGED;
mFeatures = 0;
mLastDirection = 0;
mAbsoluteVolume = -1;
@@ -388,12 +387,12 @@
mVolCmdSetInProgress = false;
isAbsoluteVolumeSupportingDevice = false;
mAbsVolRetryTimes = 0;
- keyPressState = AvrcpConstants.KEY_STATE_RELEASE; //Key release state
+ keyPressState = AvrcpConstants_ext.KEY_STATE_RELEASE; //Key release state
mRemoteVolume = -1;
mMusicAppCmdResponsePending = new HashMap<Integer, Integer>();
- mAvailablePlayersChangedNT = AvrcpConstants.NOTIFICATION_TYPE_CHANGED;
- mAddrPlayerChangedNT = AvrcpConstants.NOTIFICATION_TYPE_CHANGED;
- mUidsChangedNT = AvrcpConstants.NOTIFICATION_TYPE_CHANGED;
+ mAvailablePlayersChangedNT = AvrcpConstants_ext.NOTIFICATION_TYPE_CHANGED;
+ mAddrPlayerChangedNT = AvrcpConstants_ext.NOTIFICATION_TYPE_CHANGED;
+ mUidsChangedNT = AvrcpConstants_ext.NOTIFICATION_TYPE_CHANGED;
mLastStateUpdate = -1;
mInitialRemoteVolume = -1;
mBlackListVolume = -1;
@@ -432,12 +431,12 @@
mReportedPlayStatus = PLAYSTATUS_ERROR;
mA2dpState = BluetoothA2dp.STATE_NOT_PLAYING;
mAudioManagerIsPlaying = false;
- mPlayStatusChangedNT = AvrcpConstants.NOTIFICATION_TYPE_CHANGED;
- mPlayerStatusChangeNT = AvrcpConstants.NOTIFICATION_TYPE_CHANGED;
- mTrackChangedNT = AvrcpConstants.NOTIFICATION_TYPE_CHANGED;
- mPlayPosChangedNT = AvrcpConstants.NOTIFICATION_TYPE_CHANGED;
- mAddrPlayerChangedNT = AvrcpConstants.NOTIFICATION_TYPE_CHANGED;
- mNowPlayingListChangedNT = AvrcpConstants.NOTIFICATION_TYPE_CHANGED;
+ mPlayStatusChangedNT = AvrcpConstants_ext.NOTIFICATION_TYPE_CHANGED;
+ mPlayerStatusChangeNT = AvrcpConstants_ext.NOTIFICATION_TYPE_CHANGED;
+ mTrackChangedNT = AvrcpConstants_ext.NOTIFICATION_TYPE_CHANGED;
+ mPlayPosChangedNT = AvrcpConstants_ext.NOTIFICATION_TYPE_CHANGED;
+ mAddrPlayerChangedNT = AvrcpConstants_ext.NOTIFICATION_TYPE_CHANGED;
+ mNowPlayingListChangedNT = AvrcpConstants_ext.NOTIFICATION_TYPE_CHANGED;
mPlaybackIntervalMs = 0L;
mLastReportedPosition = -1;
mNextPosMs = -1;
@@ -538,11 +537,11 @@
mMediaControllerCb = new MediaControllerListener();
mAvrcpMediaRsp = new AvrcpMediaRsp();
mAvrcpPlayerAppSettingsRsp = new AvrcpPlayerAppSettingsRsp();
- mMediaPlayerInfoList = new TreeMap<Integer, MediaPlayerInfo>();
+ mMediaPlayerInfoList = new TreeMap<Integer, MediaPlayerInfo_ext>();
mAvailablePlayerViewChanged = false;
- mBrowsePlayerInfoList = Collections.synchronizedList(new ArrayList<BrowsePlayerInfo>());
+ mBrowsePlayerInfoList = Collections.synchronizedList(new ArrayList<BrowsePlayerInfo_ext>());
mPassthroughDispatched = 0;
- mPassthroughLogs = new EvictingQueue<MediaKeyLog>(PASSTHROUGH_LOG_MAX_SIZE);
+ mPassthroughLogs = new EvictingQueue_ext<MediaKeyLog>(PASSTHROUGH_LOG_MAX_SIZE);
mPassthroughPending = Collections.synchronizedList(new ArrayList<MediaKeyLog>());
if (mMediaSessionManager != null) {
mMediaSessionManager.addOnActiveSessionsChangedListener(mActiveSessionListener, null,
@@ -582,7 +581,7 @@
}
/* create object to communicate with addressed player */
- mAddressedMediaPlayer = new AddressedMediaPlayer(mAvrcpMediaRsp, mAvrcp);
+ mAddressedMediaPlayer = new AddressedMediaPlayer_ext(mAvrcpMediaRsp, mAvrcp);
/* initialize BrowseMananger which manages Browse commands and response */
mAvrcpBrowseManager = new AvrcpBrowseManager(mContext, mAvrcpMediaRsp);
@@ -675,7 +674,7 @@
Log.d(TAG, "Enter clearDeviceDependentFeature()");
for (int i = 0; i < maxAvrcpConnections; i++) {
deviceFeatures[i].keyPressState =
- AvrcpConstants.KEY_STATE_RELEASE; //Key release state
+ AvrcpConstants_ext.KEY_STATE_RELEASE; //Key release state
if (deviceFeatures[i].mMusicAppCmdResponsePending != null)
deviceFeatures[i].mMusicAppCmdResponsePending.clear();
}
@@ -805,20 +804,17 @@
break;
mAudioManager.avrcpSupportsAbsoluteVolume(device.getAddress(),
isAbsoluteVolumeSupported(deviceIndex));
- if (isAbsoluteVolumeSupported(deviceIndex)) {
- if (mAbsVolThreshold > 0 && mAbsVolThreshold < mAudioStreamMax &&
+ if (mAbsVolThreshold > 0 && mAbsVolThreshold < mAudioStreamMax &&
vol > mAbsVolThreshold) {
if (DEBUG) Log.v(TAG, "remote inital volume too high " + vol + ">" +
mAbsVolThreshold);
vol = mAbsVolThreshold;
notifyVolumeChanged(vol, false);
- }
}
if (vol >= 0) {
vol = convertToAvrcpVolume(vol);
Log.d(TAG,"vol = " + vol + "rem vol = " + deviceFeatures[deviceIndex].mRemoteVolume);
if(vol != deviceFeatures[deviceIndex].mRemoteVolume &&
- deviceFeatures[deviceIndex].isAbsoluteVolumeSupportingDevice &&
deviceFeatures[deviceIndex].mCurrentDevice != null) {
setVolumeNative(vol , getByteAddress(deviceFeatures[deviceIndex].mCurrentDevice));
if (deviceFeatures[deviceIndex].mCurrentDevice.isTwsPlusDevice()) {
@@ -838,19 +834,6 @@
break;
}
- case MESSAGE_UPDATE_ABSOLUTE_VOLUME:
- {
- int vol = msg.arg2;
- deviceIndex = msg.arg1;
- Log.e(TAG, "Device switch: setting volume: " + vol);
- if(deviceFeatures[deviceIndex].isAbsoluteVolumeSupportingDevice) {
- notifyVolumeChanged(vol, false);
- } else {
- mAudioManager.setStreamVolume(AudioManager.STREAM_MUSIC, vol, 0);
- }
- break;
- }
-
case MSG_NATIVE_REQ_GET_RC_FEATURES:
{
String address = (String) msg.obj;
@@ -898,7 +881,7 @@
}
} else
mAudioManager.avrcpSupportsAbsoluteVolume(device.getAddress(), true);
- } else {
+ } else if (deviceFeatures[deviceIndex].isActiveDevice) {
mAudioManager.avrcpSupportsAbsoluteVolume(device.getAddress(),
isAbsoluteVolumeSupported(deviceIndex));
Log.v(TAG,"update audio manager for abs vol state = "
@@ -1018,7 +1001,7 @@
case MSG_NATIVE_REQ_GET_ELEM_ATTRS:
{
String[] textArray;
- AvrcpCmd.ElementAttrCmd elem = (AvrcpCmd.ElementAttrCmd) msg.obj;
+ AvrcpCmd_ext.ElementAttrCmd elem = (AvrcpCmd_ext.ElementAttrCmd) msg.obj;
byte numAttr = elem.mNumAttr;
int[] attrIds = elem.mAttrIDs;
byte[] remoteAddr = elem.mAddress;
@@ -1052,9 +1035,9 @@
textArray[i]= new String();
}
responseDebug.append("[" + attrIds[i] + "=");
- if (attrIds[i] == AvrcpConstants.ATTRID_TITLE
- || attrIds[i] == AvrcpConstants.ATTRID_ARTIST
- || attrIds[i] == AvrcpConstants.ATTRID_ALBUM) {
+ if (attrIds[i] == AvrcpConstants_ext.ATTRID_TITLE
+ || attrIds[i] == AvrcpConstants_ext.ATTRID_ARTIST
+ || attrIds[i] == AvrcpConstants_ext.ATTRID_ALBUM) {
responseDebug.append(Utils.ellipsize(textArray[i]) + "] ");
} else {
responseDebug.append(textArray[i] + "] ");
@@ -1182,8 +1165,10 @@
//Don't show media UI when device connected.
isShowUI = false;
deviceFeatures[deviceIndex].mInitialRemoteVolume = absVol;
- //Avoid fluction of volume during device add in blacklist
- if(deviceFeatures[deviceIndex].mBlackListVolume != -1) {
+ //Avoid fluctuation of volume during device added in blacklist
+ // use send setAbsolute volume for blacklisted volume
+ if(deviceFeatures[deviceIndex].mBlackListVolume != -1 &&
+ deviceFeatures[deviceIndex].isActiveDevice) {
resetBlackList(address);
if (DEBUG) Log.v(TAG, "remote initial volume as audio stream volume : " +
deviceFeatures[deviceIndex].mBlackListVolume);
@@ -1195,18 +1180,29 @@
deviceFeatures[deviceIndex].mLocalVolume = deviceFeatures[deviceIndex].mBlackListVolume;
deviceFeatures[deviceIndex].mBlackListVolume = -1;
break;
- }
- else if (mAbsVolThreshold > 0 && mAbsVolThreshold < mAudioStreamMax &&
- volIndex > mAbsVolThreshold) {
- if (DEBUG) Log.v(TAG, "remote inital volume too high " + volIndex + ">" +
- mAbsVolThreshold);
- Message msg1 = mHandler.obtainMessage(MSG_SET_ABSOLUTE_VOLUME,
- mAbsVolThreshold , 0);
- mHandler.sendMessage(msg1);
- deviceFeatures[deviceIndex].mRemoteVolume = absVol;
- deviceFeatures[deviceIndex].mLocalVolume = volIndex;
- deviceFeatures[deviceIndex].mLastRequestedVolume = -1;
- break;
+ } else if (deviceFeatures[deviceIndex].isActiveDevice) {
+ /*Avoid send set absolute volume for store volume untill volume registration
+ complete and making synchronization to send only one setAbsolute volume
+ during connection*/
+ if(getVolume(deviceFeatures[deviceIndex].mCurrentDevice) != -1) {
+ setAbsVolumeFlag(deviceFeatures[deviceIndex].mCurrentDevice);
+ break;
+ }
+ /*if volume is stored than no need to send setAbsoluteVolume use register volume*/
+ else if(mAbsVolThreshold > 0 && mAbsVolThreshold < mAudioStreamMax &&
+ volIndex > mAbsVolThreshold) {
+ //To handle volume when device volume is not stored during
+ // paring
+ if (DEBUG) Log.v(TAG, "remote inital volume too high " + volIndex + ">" +
+ mAbsVolThreshold);
+ Message msg1 = mHandler.obtainMessage(MSG_SET_ABSOLUTE_VOLUME,
+ mAbsVolThreshold , 0);
+ mHandler.sendMessage(msg1);
+ deviceFeatures[deviceIndex].mRemoteVolume = absVol;
+ deviceFeatures[deviceIndex].mLocalVolume = volIndex;
+ deviceFeatures[deviceIndex].mLastRequestedVolume = -1;
+ break;
+ }
}
}
if (deviceFeatures[deviceIndex].mLocalVolume != volIndex &&
@@ -1423,21 +1419,21 @@
break;
case MSG_NATIVE_REQ_GET_FOLDER_ITEMS: {
- AvrcpCmd.FolderItemsCmd folderObj = (AvrcpCmd.FolderItemsCmd) msg.obj;
+ AvrcpCmd_ext.FolderItemsCmd folderObj = (AvrcpCmd_ext.FolderItemsCmd) msg.obj;
if (DEBUG) Log.v(TAG, "MSG_NATIVE_REQ_GET_FOLDER_ITEMS " + folderObj);
switch (folderObj.mScope) {
- case AvrcpConstants.BTRC_SCOPE_PLAYER_LIST:
+ case AvrcpConstants_ext.BTRC_SCOPE_PLAYER_LIST:
handleMediaPlayerListRsp(folderObj);
break;
- case AvrcpConstants.BTRC_SCOPE_FILE_SYSTEM:
- case AvrcpConstants.BTRC_SCOPE_NOW_PLAYING:
+ case AvrcpConstants_ext.BTRC_SCOPE_FILE_SYSTEM:
+ case AvrcpConstants_ext.BTRC_SCOPE_NOW_PLAYING:
handleGetFolderItemBrowseResponse(folderObj, folderObj.mAddress);
break;
default:
Log.e(TAG, "unknown scope for getfolderitems. scope = "
+ folderObj.mScope);
getFolderItemsRspNative(folderObj.mAddress,
- AvrcpConstants.RSP_INV_SCOPE, (short) 0, (byte) 0, 0,
+ AvrcpConstants_ext.RSP_INV_SCOPE, (short) 0, (byte) 0, 0,
null, null, null, null, null, null, null, null);
}
break;
@@ -1451,7 +1447,7 @@
case MSG_NATIVE_REQ_GET_ITEM_ATTR:
// msg object contains the item attribute object
- AvrcpCmd.ItemAttrCmd cmd = (AvrcpCmd.ItemAttrCmd) msg.obj;
+ AvrcpCmd_ext.ItemAttrCmd cmd = (AvrcpCmd_ext.ItemAttrCmd) msg.obj;
if (DEBUG) Log.v(TAG, "MSG_NATIVE_REQ_GET_ITEM_ATTR " + cmd);
handleGetItemAttr(cmd);
break;
@@ -1470,7 +1466,7 @@
byte[] bdaddr = data.getByteArray("BdAddress");
byte[] folderUid = data.getByteArray("folderUid");
byte direction = data.getByte("direction");
- byte[] tempUid = new byte[AvrcpConstants.UID_SIZE];
+ byte[] tempUid = new byte[AvrcpConstants_ext.UID_SIZE];
if (mAvrcpBrowseManager.getBrowsedMediaPlayer(bdaddr) != null) {
changePathDirection = direction;
if(direction == 1)
@@ -1479,8 +1475,8 @@
direction);
if ((direction == 1) && (changePathDepth > 0)) {
for (int index=0; index<saveRspObj.mDisplayNames.length; index++) {
- for (int size=0; size < AvrcpConstants.UID_SIZE; size++)
- tempUid[size] = saveRspObj.mItemUid[index* AvrcpConstants.UID_SIZE + size];
+ for (int size=0; size < AvrcpConstants_ext.UID_SIZE; size++)
+ tempUid[size] = saveRspObj.mItemUid[index* AvrcpConstants_ext.UID_SIZE + size];
if (Arrays.equals(folderUid, tempUid)) {
changePathFolderType = saveRspObj.mFolderTypes[index];
break;
@@ -1495,7 +1491,7 @@
} else {
Log.e(TAG, "Remote requesting change path before setbrowsedplayer");
- changePathRspNative(bdaddr, AvrcpConstants.RSP_BAD_CMD, 0);
+ changePathRspNative(bdaddr, AvrcpConstants_ext.RSP_BAD_CMD, 0);
}
break;
}
@@ -1607,7 +1603,7 @@
byte newStatus = getBluetoothPlayState(state);
/* update play status in global media player list */
- MediaPlayerInfo player = getAddressedPlayerInfo();
+ MediaPlayerInfo_ext player = getAddressedPlayerInfo();
if (player != null) {
player.setPlayStatus(newStatus);
}
@@ -1654,11 +1650,11 @@
deviceFeatures[deviceIndex].mLastPassthroughcmd = KeyEvent.KEYCODE_UNKNOWN;
if ((deviceFeatures[deviceIndex].mPlayStatusChangedNT ==
- AvrcpConstants.NOTIFICATION_TYPE_INTERIM) &&
+ AvrcpConstants_ext.NOTIFICATION_TYPE_INTERIM) &&
(oldPlayStatus != newPlayStatus) && deviceFeatures[deviceIndex].mCurrentDevice != null) {
Log.w(TAG, "Sending PlayStatus CHANGED Rsp !!!");
deviceFeatures[deviceIndex].mPlayStatusChangedNT =
- AvrcpConstants.NOTIFICATION_TYPE_CHANGED;
+ AvrcpConstants_ext.NOTIFICATION_TYPE_CHANGED;
registerNotificationRspPlayStatusNative(
deviceFeatures[deviceIndex].mPlayStatusChangedNT,
newPlayStatus,
@@ -2104,31 +2100,31 @@
Log.v(TAG, "Update player id: " + deviceFeatures[index].mReportedPlayerID +
"-> " + mCurrAddrPlayerID);
if (deviceFeatures[index].mAvailablePlayersChangedNT ==
- AvrcpConstants.NOTIFICATION_TYPE_INTERIM) {
+ AvrcpConstants_ext.NOTIFICATION_TYPE_INTERIM) {
registerNotificationRspAvalPlayerChangedNative(
- AvrcpConstants.NOTIFICATION_TYPE_CHANGED, addr);
+ AvrcpConstants_ext.NOTIFICATION_TYPE_CHANGED, addr);
mAvailablePlayerViewChanged = false;
deviceFeatures[index].mAvailablePlayersChangedNT =
- AvrcpConstants.NOTIFICATION_TYPE_CHANGED;
+ AvrcpConstants_ext.NOTIFICATION_TYPE_CHANGED;
}
if (deviceFeatures[index].mAddrPlayerChangedNT ==
- AvrcpConstants.NOTIFICATION_TYPE_INTERIM) {
+ AvrcpConstants_ext.NOTIFICATION_TYPE_INTERIM) {
registerNotificationRspAddrPlayerChangedNative(
- AvrcpConstants.NOTIFICATION_TYPE_CHANGED, mCurrAddrPlayerID,
+ AvrcpConstants_ext.NOTIFICATION_TYPE_CHANGED, mCurrAddrPlayerID,
sUIDCounter, addr);
deviceFeatures[index].mAddrPlayerChangedNT =
- AvrcpConstants.NOTIFICATION_TYPE_CHANGED;
+ AvrcpConstants_ext.NOTIFICATION_TYPE_CHANGED;
}
deviceFeatures[index].mReportedPlayerID = mCurrAddrPlayerID;
// Update the now playing list without sending the notification
- deviceFeatures[index].mNowPlayingListChangedNT = AvrcpConstants.NOTIFICATION_TYPE_CHANGED;
+ deviceFeatures[index].mNowPlayingListChangedNT = AvrcpConstants_ext.NOTIFICATION_TYPE_CHANGED;
mAddressedMediaPlayer.updateNowPlayingList(mMediaController);
- deviceFeatures[index].mNowPlayingListChangedNT = AvrcpConstants.NOTIFICATION_TYPE_INTERIM;
+ deviceFeatures[index].mNowPlayingListChangedNT = AvrcpConstants_ext.NOTIFICATION_TYPE_INTERIM;
}
// Dont send now playing list changed if the player doesn't support browsing
- MediaPlayerInfo info = getAddressedPlayerInfo();
+ MediaPlayerInfo_ext info = getAddressedPlayerInfo();
if (info != null && info.isBrowseSupported()) {
Log.v(TAG, "Check if NowPlayingList is updated");
mAddressedMediaPlayer.updateNowPlayingList(mMediaController);
@@ -2144,7 +2140,7 @@
mLastQueueId = newQueueId;
for (int i = 0; i < maxAvrcpConnections; i++) {
if ((deviceFeatures[i].mCurrentDevice != null) &&
- (deviceFeatures[i].mTrackChangedNT == AvrcpConstants.NOTIFICATION_TYPE_INTERIM)) {
+ (deviceFeatures[i].mTrackChangedNT == AvrcpConstants_ext.NOTIFICATION_TYPE_INTERIM)) {
deviceFeatures[i].mTracksPlayed++;
Log.v(TAG,"sending track change for device " + i);
sendTrackChangedRsp(false, deviceFeatures[i].mCurrentDevice);
@@ -2201,8 +2197,8 @@
private void getElementAttrRequestFromNative(byte[] address, byte numAttr, int[] attrs) {
if (DEBUG) Log.v(TAG, "getElementAttrRequestFromNative: numAttr=" + numAttr);
- AvrcpCmd avrcpCmdobj = new AvrcpCmd();
- AvrcpCmd.ElementAttrCmd elemAttr = avrcpCmdobj.new ElementAttrCmd(address, numAttr, attrs);
+ AvrcpCmd_ext avrcpCmdobj = new AvrcpCmd_ext();
+ AvrcpCmd_ext.ElementAttrCmd elemAttr = avrcpCmdobj.new ElementAttrCmd(address, numAttr, attrs);
Message msg = mHandler.obtainMessage(MSG_NATIVE_REQ_GET_ELEM_ATTRS);
msg.obj = elemAttr;
mHandler.sendMessage(msg);
@@ -2245,7 +2241,7 @@
return;
}
deviceFeatures[deviceIndex].mPlayStatusChangedNT =
- AvrcpConstants.NOTIFICATION_TYPE_INTERIM;
+ AvrcpConstants_ext.NOTIFICATION_TYPE_INTERIM;
mHandler.removeMessages(MSG_PLAY_STATUS_CMD_TIMEOUT);
deviceFeatures[deviceIndex].isPlayStatusTimeOut = false;
if(avrcp_playstatus_blacklist && isPlayerStateUpdateBlackListed(
@@ -2281,7 +2277,7 @@
Log.d(TAG, "playback Status has changed from last playstatus response " +
"send CHANGED event with current playback status");
deviceFeatures[deviceIndex].mPlayStatusChangedNT =
- AvrcpConstants.NOTIFICATION_TYPE_CHANGED;
+ AvrcpConstants_ext.NOTIFICATION_TYPE_CHANGED;
if (!deviceFeatures[deviceIndex].isPlayStatusTimeOut) {
Message msg = mHandler.obtainMessage(MSG_PLAY_STATUS_CMD_TIMEOUT,
0, 0, deviceFeatures[deviceIndex].mCurrentDevice);
@@ -2296,7 +2292,7 @@
getByteAddress(deviceFeatures[deviceIndex].mCurrentDevice));
Log.v(TAG, "Sending Stopped in INTERIM response when current_play_status is playing and device just got connected");
deviceFeatures[deviceIndex].mPlayStatusChangedNT =
- AvrcpConstants.NOTIFICATION_TYPE_CHANGED;
+ AvrcpConstants_ext.NOTIFICATION_TYPE_CHANGED;
if (!deviceFeatures[deviceIndex].isPlayStatusTimeOut) {
Message msg = mHandler.obtainMessage(MSG_PLAY_STATUS_CMD_TIMEOUT,
0, 0, deviceFeatures[deviceIndex].mCurrentDevice);
@@ -2317,7 +2313,7 @@
return;
}
deviceFeatures[deviceIndex].mTrackChangedNT =
- AvrcpConstants.NOTIFICATION_TYPE_INTERIM;
+ AvrcpConstants_ext.NOTIFICATION_TYPE_INTERIM;
sendTrackChangedRsp(true, deviceFeatures[deviceIndex].mCurrentDevice);
break;
@@ -2352,7 +2348,7 @@
update_interval = SystemProperties.getLong("persist.vendor.btstack.avrcp.pos_time", 1000L);
}
deviceFeatures[deviceIndex].mPlayPosChangedNT =
- AvrcpConstants.NOTIFICATION_TYPE_INTERIM;
+ AvrcpConstants_ext.NOTIFICATION_TYPE_INTERIM;
update_interval = Math.max((long)param * 1000L, update_interval);
deviceFeatures[deviceIndex].mPlaybackIntervalMs = update_interval;
sendPlayPosNotificationRsp(true, deviceIndex);
@@ -2363,7 +2359,7 @@
case EVT_APP_SETTINGS_CHANGED:
deviceFeatures[deviceIndex].mPlayerStatusChangeNT =
- AvrcpConstants.NOTIFICATION_TYPE_INTERIM;
+ AvrcpConstants_ext.NOTIFICATION_TYPE_INTERIM;
mAvrcpPlayerAppSettings.sendPlayerAppChangedRsp(
deviceFeatures[deviceIndex].mPlayerStatusChangeNT, device);
break;
@@ -2376,9 +2372,9 @@
return;
}
deviceFeatures[deviceIndex].mAvailablePlayersChangedNT =
- AvrcpConstants.NOTIFICATION_TYPE_INTERIM;
+ AvrcpConstants_ext.NOTIFICATION_TYPE_INTERIM;
registerNotificationRspAvalPlayerChangedNative(
- AvrcpConstants.NOTIFICATION_TYPE_INTERIM,
+ AvrcpConstants_ext.NOTIFICATION_TYPE_INTERIM,
getByteAddress(deviceFeatures[deviceIndex].mCurrentDevice));
break;
@@ -2390,9 +2386,9 @@
return;
}
deviceFeatures[deviceIndex].mAddrPlayerChangedNT =
- AvrcpConstants.NOTIFICATION_TYPE_INTERIM;
+ AvrcpConstants_ext.NOTIFICATION_TYPE_INTERIM;
registerNotificationRspAddrPlayerChangedNative(
- AvrcpConstants.NOTIFICATION_TYPE_INTERIM,
+ AvrcpConstants_ext.NOTIFICATION_TYPE_INTERIM,
mCurrAddrPlayerID, sUIDCounter,
getByteAddress(deviceFeatures[deviceIndex].mCurrentDevice));
deviceFeatures[deviceIndex].mReportedPlayerID = mCurrAddrPlayerID;
@@ -2405,9 +2401,9 @@
return;
}
deviceFeatures[deviceIndex].mUidsChangedNT =
- AvrcpConstants.NOTIFICATION_TYPE_INTERIM;
+ AvrcpConstants_ext.NOTIFICATION_TYPE_INTERIM;
registerNotificationRspUIDsChangedNative(
- AvrcpConstants.NOTIFICATION_TYPE_INTERIM, sUIDCounter,
+ AvrcpConstants_ext.NOTIFICATION_TYPE_INTERIM, sUIDCounter,
getByteAddress(deviceFeatures[deviceIndex].mCurrentDevice));
break;
@@ -2418,9 +2414,9 @@
return;
}
/* send interim response to remote device */
- mNowPlayingListChangedNT = AvrcpConstants.NOTIFICATION_TYPE_INTERIM;
+ mNowPlayingListChangedNT = AvrcpConstants_ext.NOTIFICATION_TYPE_INTERIM;
if (!registerNotificationRspNowPlayingChangedNative(
- AvrcpConstants.NOTIFICATION_TYPE_INTERIM,
+ AvrcpConstants_ext.NOTIFICATION_TYPE_INTERIM,
getByteAddress(deviceFeatures[deviceIndex].mCurrentDevice))) {
Log.e(TAG, "EVENT_NOW_PLAYING_CONTENT_CHANGED: " +
"registerNotificationRspNowPlayingChangedNative for Interim rsp failed!");
@@ -2443,23 +2439,23 @@
private void sendTrackChangedRsp(boolean registering, BluetoothDevice device) {
int deviceIndex = getIndexForDevice(device);
Log.d(TAG, "Enter sendTrackChangedRsp");
- if (deviceFeatures[deviceIndex].mTrackChangedNT != AvrcpConstants.NOTIFICATION_TYPE_INTERIM
+ if (deviceFeatures[deviceIndex].mTrackChangedNT != AvrcpConstants_ext.NOTIFICATION_TYPE_INTERIM
&& !registering) {
if (DEBUG) Log.d(TAG, "sendTrackChangedRsp: Not registered or registering.");
return;
}
- deviceFeatures[deviceIndex].mTrackChangedNT = AvrcpConstants.NOTIFICATION_TYPE_CHANGED;
+ deviceFeatures[deviceIndex].mTrackChangedNT = AvrcpConstants_ext.NOTIFICATION_TYPE_CHANGED;
if (registering)
- deviceFeatures[deviceIndex].mTrackChangedNT = AvrcpConstants.NOTIFICATION_TYPE_INTERIM;
+ deviceFeatures[deviceIndex].mTrackChangedNT = AvrcpConstants_ext.NOTIFICATION_TYPE_INTERIM;
- MediaPlayerInfo info = getAddressedPlayerInfo();
+ MediaPlayerInfo_ext info = getAddressedPlayerInfo();
byte[] byteAddr = getByteAddress(deviceFeatures[deviceIndex].mCurrentDevice);
// for non-browsable players or no player
if ((info != null && !info.isBrowseSupported()) ||
(deviceFeatures[deviceIndex].mFeatures & BTRC_FEAT_BROWSE) == 0) {
- byte[] track = AvrcpConstants.TRACK_IS_SELECTED;
- if (!mMediaAttributes.exists) track = AvrcpConstants.NO_TRACK_SELECTED;
+ byte[] track = AvrcpConstants_ext.TRACK_IS_SELECTED;
+ if (!mMediaAttributes.exists) track = AvrcpConstants_ext.NO_TRACK_SELECTED;
registerNotificationRspTrackChangeNative(
deviceFeatures[deviceIndex].mTrackChangedNT,
track,
@@ -2596,7 +2592,7 @@
*/
private void sendPlayPosNotificationRsp(boolean requested, int i) {
Log.d(TAG, "Enter sendPlayPosNotificationRsp");
- if (!requested && deviceFeatures[i].mPlayPosChangedNT != AvrcpConstants.NOTIFICATION_TYPE_INTERIM) {
+ if (!requested && deviceFeatures[i].mPlayPosChangedNT != AvrcpConstants_ext.NOTIFICATION_TYPE_INTERIM) {
if (DEBUG) Log.d(TAG, "sendPlayPosNotificationRsp: Not registered or requesting.");
return;
}
@@ -2625,7 +2621,7 @@
}
if (requested || ((deviceFeatures[i].mLastReportedPosition != playPositionMs) &&
((playPositionMs >= deviceFeatures[i].mNextPosMs) ||
- (playPositionMs <= deviceFeatures[i].mPrevPosMs)))) {
+ (playPositionMs <= deviceFeatures[i].mPrevPosMs))) && deviceFeatures[i].isActiveDevice) {
if (!requested) deviceFeatures[i].mPlayPosChangedNT = AvrcpConstants.NOTIFICATION_TYPE_CHANGED;
if (deviceFeatures[i].mCurrentDevice != null)
registerNotificationRspPlayPosNative(deviceFeatures[i].mPlayPosChangedNT,
@@ -2642,7 +2638,7 @@
mHandler.removeMessages(currMsgPlayIntervalTimeout);
if ((deviceFeatures[i].mCurrentDevice != null) &&
- (deviceFeatures[i].mPlayPosChangedNT == AvrcpConstants.NOTIFICATION_TYPE_INTERIM) &&
+ (deviceFeatures[i].mPlayPosChangedNT == AvrcpConstants_ext.NOTIFICATION_TYPE_INTERIM) &&
(isPlayingState(deviceFeatures[i].mCurrentPlayState)) && !isPlayerPaused()) {
Message msg = mHandler.obtainMessage(currMsgPlayIntervalTimeout, 0, 0,
deviceFeatures[i].mCurrentDevice);
@@ -2737,18 +2733,18 @@
Log.d(TAG, "Vol Passthrough Up");
avrcpCtrlService.sendPassThroughCmd(
deviceFeatures[i].mCurrentDevice, AVRC_ID_VOL_UP,
- AvrcpConstants.KEY_STATE_PRESS);
+ AvrcpConstants_ext.KEY_STATE_PRESS);
avrcpCtrlService.sendPassThroughCmd(
deviceFeatures[i].mCurrentDevice, AVRC_ID_VOL_UP,
- AvrcpConstants.KEY_STATE_RELEASE);
+ AvrcpConstants_ext.KEY_STATE_RELEASE);
} else if (volume < mLocalVolume) {
Log.d(TAG, "Vol Passthrough Down");
avrcpCtrlService.sendPassThroughCmd(
deviceFeatures[i].mCurrentDevice, AVRC_ID_VOL_DOWN,
- AvrcpConstants.KEY_STATE_PRESS);
+ AvrcpConstants_ext.KEY_STATE_PRESS);
avrcpCtrlService.sendPassThroughCmd(
deviceFeatures[i].mCurrentDevice, AVRC_ID_VOL_DOWN,
- AvrcpConstants.KEY_STATE_RELEASE);
+ AvrcpConstants_ext.KEY_STATE_RELEASE);
}
mLocalVolume = volume;
}
@@ -2784,9 +2780,9 @@
private void getFolderItemsRequestFromNative(
byte[] address, byte scope, long startItem, long endItem, byte numAttr, int[] attrIds) {
if (DEBUG) Log.v(TAG, "getFolderItemsRequestFromNative: scope=" + scope + ", numAttr=" + numAttr);
- AvrcpCmd avrcpCmdobj = new AvrcpCmd();
+ AvrcpCmd_ext avrcpCmdobj = new AvrcpCmd_ext();
Log.v(TAG, "Enter getFolderItemsRequestFromNative");
- AvrcpCmd.FolderItemsCmd folderObj = avrcpCmdobj.new FolderItemsCmd(address, scope,
+ AvrcpCmd_ext.FolderItemsCmd folderObj = avrcpCmdobj.new FolderItemsCmd(address, scope,
startItem, endItem, numAttr, attrIds);
Message msg = mHandler.obtainMessage(MSG_NATIVE_REQ_GET_FOLDER_ITEMS, 0, 0);
msg.obj = folderObj;
@@ -2824,9 +2820,9 @@
private void getItemAttrRequestFromNative(byte[] address, byte scope, byte[] itemUid, int uidCounter,
byte numAttr, int[] attrs) {
- AvrcpCmd avrcpCmdobj = new AvrcpCmd();
+ AvrcpCmd_ext avrcpCmdobj = new AvrcpCmd_ext();
Log.v(TAG, "Enter getItemAttrRequestFromNative");
- AvrcpCmd.ItemAttrCmd itemAttr = avrcpCmdobj.new ItemAttrCmd(address, scope,
+ AvrcpCmd_ext.ItemAttrCmd itemAttr = avrcpCmdobj.new ItemAttrCmd(address, scope,
itemUid, uidCounter, numAttr, attrs);
Message msg = mHandler.obtainMessage(MSG_NATIVE_REQ_GET_ITEM_ATTR);
msg.obj = itemAttr;
@@ -2837,7 +2833,7 @@
private void searchRequestFromNative(byte[] address, int charsetId, byte[] searchStr) {
/* Search is not supported */
Log.w(TAG, "searchRequestFromNative: search is not supported");
- searchRspNative(address, AvrcpConstants.RSP_SRCH_NOT_SPRTD, 0, 0);
+ searchRspNative(address, AvrcpConstants_ext.RSP_SRCH_NOT_SPRTD, 0, 0);
}
private void playItemRequestFromNative(byte[] address, byte scope, int uidCounter, byte[] uid) {
@@ -2856,7 +2852,7 @@
private void addToPlayListRequestFromNative(byte[] address, byte scope, byte[] uid, int uidCounter) {
/* add to NowPlaying not supported */
Log.w(TAG, "addToPlayListRequestFromNative: not supported! scope=" + scope);
- addToNowPlayingRspNative(address, AvrcpConstants.RSP_INTERNAL_ERR);
+ addToNowPlayingRspNative(address, AvrcpConstants_ext.RSP_INTERNAL_ERR);
}
private void getTotalNumOfItemsRequestFromNative(byte[] address, byte scope) {
@@ -2899,10 +2895,10 @@
Log.e(TAG,"invalid index for device");
return;
}
- CreateMusicSettingsAppCmdLookupOrUpdate(AvrcpConstants.GET_ATTRIBUTE_IDS,
+ CreateMusicSettingsAppCmdLookupOrUpdate(AvrcpConstants_ext.GET_ATTRIBUTE_IDS,
deviceIndex, true);
mAvrcpPlayerAppSettings.onListPlayerAttributeRequest(address);
- SendPlayerSettingMsg(AvrcpConstants.GET_ATTRIBUTE_IDS, address);
+ SendPlayerSettingMsg(AvrcpConstants_ext.GET_ATTRIBUTE_IDS, address);
}
//PDU ID 0x12
@@ -2915,9 +2911,9 @@
Log.e(TAG,"invalid index for device");
return;
}
- CreateMusicSettingsAppCmdLookupOrUpdate(AvrcpConstants.GET_VALUE_IDS, deviceIndex, true);
+ CreateMusicSettingsAppCmdLookupOrUpdate(AvrcpConstants_ext.GET_VALUE_IDS, deviceIndex, true);
mAvrcpPlayerAppSettings.onListPlayerAttributeValues(attr, address);
- SendPlayerSettingMsg(AvrcpConstants.GET_VALUE_IDS, address);
+ SendPlayerSettingMsg(AvrcpConstants_ext.GET_VALUE_IDS, address);
}
//PDU ID 0x13
@@ -2933,10 +2929,10 @@
Log.e(TAG,"invalid index for device");
return;
}
- CreateMusicSettingsAppCmdLookupOrUpdate(AvrcpConstants.GET_ATTRIBUTE_VALUES,
+ CreateMusicSettingsAppCmdLookupOrUpdate(AvrcpConstants_ext.GET_ATTRIBUTE_VALUES,
deviceIndex, true);
mAvrcpPlayerAppSettings.onGetPlayerAttributeValues(attr, arr, address);
- SendPlayerSettingMsg(AvrcpConstants.GET_ATTRIBUTE_VALUES, address);
+ SendPlayerSettingMsg(AvrcpConstants_ext.GET_ATTRIBUTE_VALUES, address);
}
//PDU 0x14
@@ -2953,15 +2949,15 @@
return;
}
if (!deviceFeatures[deviceIndex].isActiveDevice) {
- SendSetPlayerAppRspNative(AvrcpConstants.RSP_INTERNAL_ERR, address);
+ SendSetPlayerAppRspNative(AvrcpConstants_ext.RSP_INTERNAL_ERR, address);
Log.e(TAG,"Set Command from inactive device reject it");
return;
}
- CreateMusicSettingsAppCmdLookupOrUpdate(AvrcpConstants.SET_ATTRIBUTE_VALUES,
+ CreateMusicSettingsAppCmdLookupOrUpdate(AvrcpConstants_ext.SET_ATTRIBUTE_VALUES,
deviceIndex, true);
mAvrcpPlayerAppSettings.setPlayerAppSetting(num, attr_id, attr_val, address);
- SendPlayerSettingMsg(AvrcpConstants.SET_ATTRIBUTE_VALUES, address);
+ SendPlayerSettingMsg(AvrcpConstants_ext.SET_ATTRIBUTE_VALUES, address);
}
//PDU 0x15
@@ -2977,10 +2973,10 @@
Log.e(TAG,"invalid index for device");
return;
}
- CreateMusicSettingsAppCmdLookupOrUpdate(AvrcpConstants.GET_ATTRIBUTE_TEXT,
+ CreateMusicSettingsAppCmdLookupOrUpdate(AvrcpConstants_ext.GET_ATTRIBUTE_TEXT,
deviceIndex, true);
mAvrcpPlayerAppSettings.getplayerattribute_text(attr, attrIds, address);
- SendPlayerSettingMsg(AvrcpConstants.GET_ATTRIBUTE_TEXT, address);
+ SendPlayerSettingMsg(AvrcpConstants_ext.GET_ATTRIBUTE_TEXT, address);
}
//PDU 0x16
@@ -2994,9 +2990,9 @@
Log.e(TAG,"invalid index for device");
return;
}
- CreateMusicSettingsAppCmdLookupOrUpdate(AvrcpConstants.GET_VALUE_TEXT, deviceIndex, true);
+ CreateMusicSettingsAppCmdLookupOrUpdate(AvrcpConstants_ext.GET_VALUE_TEXT, deviceIndex, true);
mAvrcpPlayerAppSettings.getplayervalue_text(attr_id, num_value, value, address);
- SendPlayerSettingMsg(AvrcpConstants.GET_VALUE_TEXT, address);
+ SendPlayerSettingMsg(AvrcpConstants_ext.GET_VALUE_TEXT, address);
}
private void SendPlayerSettingMsg(Integer cmd, byte[] address) {
@@ -3217,8 +3213,6 @@
(mDevice.isTwsPlusDevice() && device.isTwsPlusDevice()))) {
setActiveDevice(mDevice);
//below line to send setAbsolute volume if device is suporting absolute volume
- if (mDevice.equals(deviceFeatures[index].mCurrentDevice))
- setAbsVolumeFlag(mDevice);//Do not call this funciton for second EB connect
//When A2dp playing on DUT and Remote got connected, send proper playstatus
if (isPlayingState(mCurrentPlayerState) &&
mA2dpService.isA2dpPlaying(device)) {
@@ -3400,7 +3394,7 @@
int browseInfoID = 0;
synchronized (this) {
synchronized (mBrowsePlayerInfoList) {
- for (BrowsePlayerInfo info : mBrowsePlayerInfoList) {
+ for (BrowsePlayerInfo_ext info : mBrowsePlayerInfoList) {
if (info.packageName.equals(packageName)) {
response = true;
break;
@@ -3427,34 +3421,34 @@
synchronized (mMediaPlayerInfoList) {
if (mMediaPlayerInfoList.isEmpty()) {
Log.w(TAG, functionTag + "no players, send no available players");
- setAddressedPlayerRspNative(bdaddr, AvrcpConstants.RSP_NO_AVBL_PLAY);
+ setAddressedPlayerRspNative(bdaddr, AvrcpConstants_ext.RSP_NO_AVBL_PLAY);
return;
}
if (selectedId == NO_PLAYER_ID) {
Log.w(TAG, functionTag + "Respond dummy pass response ");
- setAddressedPlayerRspNative(bdaddr, AvrcpConstants.RSP_NO_ERROR);
+ setAddressedPlayerRspNative(bdaddr, AvrcpConstants_ext.RSP_NO_ERROR);
return;
}
if (!mMediaPlayerInfoList.containsKey(selectedId)) {
Log.w(TAG, functionTag + "invalid id, sending response back ");
- setAddressedPlayerRspNative(bdaddr, AvrcpConstants.RSP_INV_PLAYER);
+ setAddressedPlayerRspNative(bdaddr, AvrcpConstants_ext.RSP_INV_PLAYER);
return;
}
if (isPlayerAlreadyAddressed(selectedId)) {
- MediaPlayerInfo info = getAddressedPlayerInfo();
+ MediaPlayerInfo_ext info = getAddressedPlayerInfo();
Log.i(TAG, functionTag + "player already addressed: " + info);
- setAddressedPlayerRspNative(bdaddr, AvrcpConstants.RSP_NO_ERROR);
+ setAddressedPlayerRspNative(bdaddr, AvrcpConstants_ext.RSP_NO_ERROR);
return;
}
// register new Media Controller Callback and update the current IDs
if (!updateCurrentController(selectedId, mCurrBrowsePlayerID)) {
Log.e(TAG, functionTag + "updateCurrentController failed!");
- setAddressedPlayerRspNative(bdaddr, AvrcpConstants.RSP_INTERNAL_ERR);
+ setAddressedPlayerRspNative(bdaddr, AvrcpConstants_ext.RSP_INTERNAL_ERR);
return;
}
// If we don't have a controller, try to launch the player
- MediaPlayerInfo info = getAddressedPlayerInfo();
+ MediaPlayerInfo_ext info = getAddressedPlayerInfo();
if (info.getMediaController() == null) {
Intent launch = mPackageManager.getLaunchIntentForPackage(info.getPackageName());
Log.i(TAG, functionTag + "launching player " + launch);
@@ -3462,12 +3456,12 @@
}
}
}
- setAddressedPlayerRspNative(bdaddr, AvrcpConstants.RSP_NO_ERROR);
+ setAddressedPlayerRspNative(bdaddr, AvrcpConstants_ext.RSP_NO_ERROR);
Log.d(TAG, "Exit setAddressedPlayer");
}
private void setBrowsedPlayer(byte[] bdaddr, int selectedId) {
- int status = AvrcpConstants.RSP_NO_ERROR;
+ int status = AvrcpConstants_ext.RSP_NO_ERROR;
Log.d(TAG, "Enter setBrowsedPlayer");
String address = Utils.getAddressStringFromByte(bdaddr);
@@ -3475,10 +3469,10 @@
// checking for error cases
BluetoothDevice device = BluetoothAdapter.getDefaultAdapter().getRemoteDevice(bdaddr);
if (mBrowsingActiveDevice != null && !device.equals(mBrowsingActiveDevice)) {
- status = AvrcpConstants.RSP_INTERNAL_ERR;
+ status = AvrcpConstants_ext.RSP_INTERNAL_ERR;
Log.w(TAG, "setBrowsedPlayer: Cmd from browse inactive device reject it");
} else if (mMediaPlayerInfoList.isEmpty()) {
- status = AvrcpConstants.RSP_NO_AVBL_PLAY;
+ status = AvrcpConstants_ext.RSP_NO_AVBL_PLAY;
Log.w(TAG, "setBrowsedPlayer: No available players! ");
} else {
// Workaround for broken controllers selecting ID 0
@@ -3493,19 +3487,19 @@
if (!isPackageNameValid(browsedPackage)) {
Log.w(TAG, " Invalid package for id:" + mCurrBrowsePlayerID);
- status = AvrcpConstants.RSP_INV_PLAYER;
+ status = AvrcpConstants_ext.RSP_INV_PLAYER;
} else if (!isBrowseSupported(browsedPackage)) {
Log.w(TAG, "Browse unsupported for id:" + mCurrBrowsePlayerID
+ ", packagename : " + browsedPackage);
- status = AvrcpConstants.RSP_PLAY_NOT_BROW;
+ status = AvrcpConstants_ext.RSP_PLAY_NOT_BROW;
} else if (!startBrowseService(bdaddr, browsedPackage)) {
Log.e(TAG, "service cannot be started for browse player id:" + mCurrBrowsePlayerID
+ ", packagename : " + browsedPackage);
- status = AvrcpConstants.RSP_INTERNAL_ERR;
+ status = AvrcpConstants_ext.RSP_INTERNAL_ERR;
}
}
- if (status != AvrcpConstants.RSP_NO_ERROR) {
+ if (status != AvrcpConstants_ext.RSP_NO_ERROR) {
setBrowsedPlayerRspNative(bdaddr, status, (byte) 0x00, 0, null);
}
@@ -3570,7 +3564,7 @@
synchronized (this) {
synchronized (mMediaPlayerInfoList) {
- for (Map.Entry<Integer, MediaPlayerInfo> entry : mMediaPlayerInfoList.entrySet()) {
+ for (Map.Entry<Integer, MediaPlayerInfo_ext> entry : mMediaPlayerInfoList.entrySet()) {
if (entry.getValue().getPackageName().equals(packageName)) {
int newAddrID = entry.getKey();
if (DEBUG) Log.v(TAG, "Set addressed #" + newAddrID + " " + entry.getValue());
@@ -3676,10 +3670,10 @@
String packageName = info.serviceInfo.packageName;
if (DEBUG) Log.d(TAG, "Adding " + serviceName + " to list of browsable players");
- BrowsePlayerInfo currentPlayer =
- new BrowsePlayerInfo(packageName, displayableName, serviceName);
+ BrowsePlayerInfo_ext currentPlayer =
+ new BrowsePlayerInfo_ext(packageName, displayableName, serviceName);
mBrowsePlayerInfoList.add(currentPlayer);
- MediaPlayerInfo playerInfo = getMediaPlayerInfo(packageName);
+ MediaPlayerInfo_ext playerInfo = getMediaPlayerInfo(packageName);
MediaController controller =
(playerInfo == null) ? null : playerInfo.getMediaController();
// Refresh the media player entry so it notices we can browse
@@ -3730,7 +3724,7 @@
new ArrayList<android.media.session.MediaController>();
synchronized (this) {
synchronized (mMediaPlayerInfoList) {
- for (MediaPlayerInfo info : mMediaPlayerInfoList.values()) {
+ for (MediaPlayerInfo_ext info : mMediaPlayerInfoList.values()) {
MediaController controller = info.getMediaController();
if (controller != null) {
controllers.add(controller.getWrappedInstance());
@@ -3743,8 +3737,8 @@
/** Add (or update) a player to the media player list without a controller */
private boolean addMediaPlayerPackage(String packageName) {
- MediaPlayerInfo info = new MediaPlayerInfo(null, AvrcpConstants.PLAYER_TYPE_AUDIO,
- AvrcpConstants.PLAYER_SUBTYPE_NONE, PLAYSTATUS_STOPPED,
+ MediaPlayerInfo_ext info = new MediaPlayerInfo_ext(null, AvrcpConstants_ext.PLAYER_TYPE_AUDIO,
+ AvrcpConstants_ext.PLAYER_SUBTYPE_NONE, PLAYSTATUS_STOPPED,
getFeatureBitMask(packageName), packageName, getAppLabel(packageName));
return addMediaPlayerInfo(info);
}
@@ -3752,8 +3746,8 @@
/** Add (or update) a player to the media player list given an active controller */
private boolean addMediaPlayerController(android.media.session.MediaController controller) {
String packageName = controller.getPackageName();
- MediaPlayerInfo info = new MediaPlayerInfo(MediaControllerFactory.wrap(controller), /*MediaController not present*/
- AvrcpConstants.PLAYER_TYPE_AUDIO, AvrcpConstants.PLAYER_SUBTYPE_NONE,
+ MediaPlayerInfo_ext info = new MediaPlayerInfo_ext(MediaControllerFactory.wrap(controller), /*MediaController not present*/
+ AvrcpConstants_ext.PLAYER_TYPE_AUDIO, AvrcpConstants_ext.PLAYER_SUBTYPE_NONE,
getBluetoothPlayState(controller.getPlaybackState()),
getFeatureBitMask(packageName), controller.getPackageName(),
getAppLabel(packageName));
@@ -3763,7 +3757,7 @@
/** Add or update a player to the media player list given the MediaPlayerInfo object.
* @return true if an item was updated, false if it was added instead
*/
- private boolean addMediaPlayerInfo(MediaPlayerInfo info) {
+ private boolean addMediaPlayerInfo(MediaPlayerInfo_ext info) {
int updateId = -1;
boolean updated = false;
boolean currentRemoved = false;
@@ -3773,8 +3767,8 @@
}
synchronized (this) {
synchronized (mMediaPlayerInfoList) {
- for (Map.Entry<Integer, MediaPlayerInfo> entry : mMediaPlayerInfoList.entrySet()) {
- MediaPlayerInfo current = entry.getValue();
+ for (Map.Entry<Integer, MediaPlayerInfo_ext> entry : mMediaPlayerInfoList.entrySet()) {
+ MediaPlayerInfo_ext current = entry.getValue();
int id = entry.getKey();
if (info.getPackageName().equals(current.getPackageName())) {
if (!current.equalView(info)) {
@@ -3806,11 +3800,11 @@
}
/** Remove all players related to |packageName| from the media player info list */
- private MediaPlayerInfo removeMediaPlayerInfo(String packageName) {
+ private MediaPlayerInfo_ext removeMediaPlayerInfo(String packageName) {
synchronized (this) {
synchronized (mMediaPlayerInfoList) {
int removeKey = -1;
- for (Map.Entry<Integer, MediaPlayerInfo> entry : mMediaPlayerInfoList.entrySet()) {
+ for (Map.Entry<Integer, MediaPlayerInfo_ext> entry : mMediaPlayerInfoList.entrySet()) {
if (entry.getValue().getPackageName().equals(packageName)) {
removeKey = entry.getKey();
break;
@@ -3833,8 +3827,8 @@
if (controller == null) return;
synchronized (this) {
synchronized (mMediaPlayerInfoList) {
- for (Map.Entry<Integer, MediaPlayerInfo> entry : mMediaPlayerInfoList.entrySet()) {
- MediaPlayerInfo info = entry.getValue();
+ for (Map.Entry<Integer, MediaPlayerInfo_ext> entry : mMediaPlayerInfoList.entrySet()) {
+ MediaPlayerInfo_ext info = entry.getValue();
MediaController c = info.getMediaController();
if (c != null && c.equals(controller)) {
info.setMediaController(null);
@@ -3894,23 +3888,23 @@
ArrayList<Short> featureBitsList = new ArrayList<Short>();
/* adding default feature bits */
- featureBitsList.add(AvrcpConstants.AVRC_PF_PLAY_BIT_NO);
- featureBitsList.add(AvrcpConstants.AVRC_PF_STOP_BIT_NO);
- featureBitsList.add(AvrcpConstants.AVRC_PF_PAUSE_BIT_NO);
- featureBitsList.add(AvrcpConstants.AVRC_PF_REWIND_BIT_NO);
- featureBitsList.add(AvrcpConstants.AVRC_PF_FAST_FWD_BIT_NO);
- featureBitsList.add(AvrcpConstants.AVRC_PF_FORWARD_BIT_NO);
- featureBitsList.add(AvrcpConstants.AVRC_PF_BACKWARD_BIT_NO);
- featureBitsList.add(AvrcpConstants.AVRC_PF_ADV_CTRL_BIT_NO);
+ featureBitsList.add(AvrcpConstants_ext.AVRC_PF_PLAY_BIT_NO);
+ featureBitsList.add(AvrcpConstants_ext.AVRC_PF_STOP_BIT_NO);
+ featureBitsList.add(AvrcpConstants_ext.AVRC_PF_PAUSE_BIT_NO);
+ featureBitsList.add(AvrcpConstants_ext.AVRC_PF_REWIND_BIT_NO);
+ featureBitsList.add(AvrcpConstants_ext.AVRC_PF_FAST_FWD_BIT_NO);
+ featureBitsList.add(AvrcpConstants_ext.AVRC_PF_FORWARD_BIT_NO);
+ featureBitsList.add(AvrcpConstants_ext.AVRC_PF_BACKWARD_BIT_NO);
+ featureBitsList.add(AvrcpConstants_ext.AVRC_PF_ADV_CTRL_BIT_NO);
/* Add/Modify browse player supported features. */
if (isBrowseSupported(packageName)) {
- featureBitsList.add(AvrcpConstants.AVRC_PF_BROWSE_BIT_NO);
- featureBitsList.add(AvrcpConstants.AVRC_PF_UID_UNIQUE_BIT_NO);
- featureBitsList.add(AvrcpConstants.AVRC_PF_NOW_PLAY_BIT_NO);
- featureBitsList.add(AvrcpConstants.AVRC_PF_GET_NUM_OF_ITEMS_BIT_NO);
+ featureBitsList.add(AvrcpConstants_ext.AVRC_PF_BROWSE_BIT_NO);
+ featureBitsList.add(AvrcpConstants_ext.AVRC_PF_UID_UNIQUE_BIT_NO);
+ featureBitsList.add(AvrcpConstants_ext.AVRC_PF_NOW_PLAY_BIT_NO);
+ featureBitsList.add(AvrcpConstants_ext.AVRC_PF_GET_NUM_OF_ITEMS_BIT_NO);
if (mAvrcpBipRsp != null)
- featureBitsList.add(AvrcpConstants.AVRC_PF_COVER_ART_BIT_NO);
+ featureBitsList.add(AvrcpConstants_ext.AVRC_PF_COVER_ART_BIT_NO);
}
// converting arraylist to array for response
@@ -3933,7 +3927,7 @@
synchronized (this) {
synchronized (mBrowsePlayerInfoList) {
/* check if Browsable Player's list contains this package name */
- for (BrowsePlayerInfo info : mBrowsePlayerInfoList) {
+ for (BrowsePlayerInfo_ext info : mBrowsePlayerInfoList) {
if (info.packageName.equals(packageName)) {
if (DEBUG) Log.v(TAG, "isBrowseSupported for " + packageName + ": true");
return true;
@@ -3947,7 +3941,7 @@
}
private String getPackageName(int id) {
- MediaPlayerInfo player = null;
+ MediaPlayerInfo_ext player = null;
synchronized (this) {
synchronized (mMediaPlayerInfoList) {
player = mMediaPlayerInfoList.getOrDefault(id, null);
@@ -3970,7 +3964,7 @@
private String getCurrentBrowsedPlayer(byte[] bdaddr) {
String browsedPlayerPackage = "";
- Map<String, BrowsedMediaPlayer> connList = mAvrcpBrowseManager.getConnList();
+ Map<String, BrowsedMediaPlayer_ext> connList = mAvrcpBrowseManager.getConnList();
String bdaddrStr = new String(bdaddr);
if(connList.containsKey(bdaddrStr)){
browsedPlayerPackage = connList.get(bdaddrStr).getPackageName();
@@ -3980,7 +3974,7 @@
}
/* Returns the MediaPlayerInfo for the currently addressed media player */
- private MediaPlayerInfo getAddressedPlayerInfo() {
+ private MediaPlayerInfo_ext getAddressedPlayerInfo() {
synchronized (this) {
synchronized (mMediaPlayerInfoList) {
return mMediaPlayerInfoList.getOrDefault(mCurrAddrPlayerID, null);
@@ -3992,7 +3986,7 @@
* Utility function to get the Media player info from package name returns
* null if package name not found in media players list
*/
- private MediaPlayerInfo getMediaPlayerInfo(String packageName) {
+ private MediaPlayerInfo_ext getMediaPlayerInfo(String packageName) {
synchronized (this) {
synchronized (mMediaPlayerInfoList) {
if (mMediaPlayerInfoList.isEmpty()) {
@@ -4000,7 +3994,7 @@
return null;
}
- for (MediaPlayerInfo info : mMediaPlayerInfoList.values()) {
+ for (MediaPlayerInfo_ext info : mMediaPlayerInfoList.values()) {
if (packageName.equals(info.getPackageName())) {
if (DEBUG) Log.v(TAG, "getMediaPlayerInfo: Found " + packageName);
return info;
@@ -4013,7 +4007,7 @@
}
/* prepare media list & return the media player list response object */
- private MediaPlayerListRsp prepareMediaPlayerRspObj() {
+ private MediaPlayerListRsp_ext prepareMediaPlayerRspObj() {
synchronized (this) {
synchronized (mMediaPlayerInfoList) {
// TODO(apanicke): This hack will go away as soon as a developer
@@ -4029,18 +4023,18 @@
String[] displayableNameArray = new String[numPlayers];
byte[] playStatusValues = new byte[numPlayers];
short[] featureBitMaskValues =
- new short[numPlayers * AvrcpConstants.AVRC_FEATURE_MASK_SIZE];
+ new short[numPlayers * AvrcpConstants_ext.AVRC_FEATURE_MASK_SIZE];
// Reserve the first spot for the currently addressed player if
// we have one
int players = mMediaPlayerInfoList.containsKey(mCurrAddrPlayerID) ? 1 : 0;
- for (Map.Entry<Integer, MediaPlayerInfo> entry : mMediaPlayerInfoList.entrySet()) {
+ for (Map.Entry<Integer, MediaPlayerInfo_ext> entry : mMediaPlayerInfoList.entrySet()) {
int idx = players;
if (entry.getKey() == mCurrAddrPlayerID)
idx = 0;
else
continue; // TODO(apanicke): Remove, see above note
- MediaPlayerInfo info = entry.getValue();
+ MediaPlayerInfo_ext info = entry.getValue();
playerIds[idx] = entry.getKey();
playerTypes[idx] = info.getMajorType();
playerSubTypes[idx] = info.getSubType();
@@ -4053,7 +4047,7 @@
byte octet = (byte) (featureBits[numBit] / 8);
/* gives the bit position within the octet */
byte bit = (byte) (featureBits[numBit] % 8);
- featureBitMaskValues[(idx * AvrcpConstants.AVRC_FEATURE_MASK_SIZE) + octet] |=
+ featureBitMaskValues[(idx * AvrcpConstants_ext.AVRC_FEATURE_MASK_SIZE) + octet] |=
(1 << bit);
}
@@ -4069,28 +4063,28 @@
if (DEBUG) Log.d(TAG, "prepareMediaPlayerRspObj: numPlayers = " + numPlayers);
- return new MediaPlayerListRsp(AvrcpConstants.RSP_NO_ERROR, sUIDCounter, players,
- AvrcpConstants.BTRC_ITEM_PLAYER, playerIds, playerTypes, playerSubTypes,
+ return new MediaPlayerListRsp_ext(AvrcpConstants_ext.RSP_NO_ERROR, sUIDCounter, players,
+ AvrcpConstants_ext.BTRC_ITEM_PLAYER, playerIds, playerTypes, playerSubTypes,
playStatusValues, featureBitMaskValues, displayableNameArray);
}
}
}
/* build media player list and send it to remote. */
- private void handleMediaPlayerListRsp(AvrcpCmd.FolderItemsCmd folderObj) {
- MediaPlayerListRsp rspObj = null;
+ private void handleMediaPlayerListRsp(AvrcpCmd_ext.FolderItemsCmd folderObj) {
+ MediaPlayerListRsp_ext rspObj = null;
synchronized (this) {
synchronized (mMediaPlayerInfoList) {
int numPlayers = mMediaPlayerInfoList.size();
if (numPlayers == 0) {
- mediaPlayerListRspNative(folderObj.mAddress, AvrcpConstants.RSP_NO_AVBL_PLAY,
+ mediaPlayerListRspNative(folderObj.mAddress, AvrcpConstants_ext.RSP_NO_AVBL_PLAY,
(short) 0, (byte) 0, 0, null, null, null, null, null, null);
return;
}
if (folderObj.mStartItem >= numPlayers || folderObj.mStartItem >= 1) {
Log.i(TAG, "handleMediaPlayerListRsp: start = " + folderObj.mStartItem
+ " > num of items = " + numPlayers);
- mediaPlayerListRspNative(folderObj.mAddress, AvrcpConstants.RSP_INV_RANGE,
+ mediaPlayerListRspNative(folderObj.mAddress, AvrcpConstants_ext.RSP_INV_RANGE,
(short) 0, (byte) 0, 0, null, null, null, null, null, null);
return;
}
@@ -4098,9 +4092,9 @@
short[] featureBitsArray = {0x00, 0x00, 0x00, 0x00, 0x00, 0xb7, 0x01, 0x04,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00};
Log.i(TAG, "handleMediaPlayerListRsp: Send dummy player response");
- mediaPlayerListRspNative(folderObj.mAddress, (int)AvrcpConstants.RSP_NO_ERROR,
- (int)sUIDCounter, AvrcpConstants.BTRC_ITEM_PLAYER, 1, new int[] {0},
- new byte[] {AvrcpConstants.PLAYER_TYPE_AUDIO}, new int[] {1},
+ mediaPlayerListRspNative(folderObj.mAddress, (int)AvrcpConstants_ext.RSP_NO_ERROR,
+ (int)sUIDCounter, AvrcpConstants_ext.BTRC_ITEM_PLAYER, 1, new int[] {0},
+ new byte[] {AvrcpConstants_ext.PLAYER_TYPE_AUDIO}, new int[] {1},
new byte[] {PLAYSTATUS_STOPPED}, featureBitsArray,
new String[] {"Dummy Player"});
return;
@@ -4124,7 +4118,7 @@
updateNewIds(addrId, browseId);
MediaController newController = null;
- MediaPlayerInfo info = getAddressedPlayerInfo();
+ MediaPlayerInfo_ext info = getAddressedPlayerInfo();
if (info != null) newController = info.getMediaController();
if (DEBUG)
@@ -4148,36 +4142,36 @@
}
/* Handle getfolderitems for scope = VFS, Search, NowPlayingList */
- private void handleGetFolderItemBrowseResponse(AvrcpCmd.FolderItemsCmd folderObj, byte[] bdaddr) {
- int status = AvrcpConstants.RSP_NO_ERROR;
+ private void handleGetFolderItemBrowseResponse(AvrcpCmd_ext.FolderItemsCmd folderObj, byte[] bdaddr) {
+ int status = AvrcpConstants_ext.RSP_NO_ERROR;
BluetoothDevice device = BluetoothAdapter.getDefaultAdapter().getRemoteDevice(bdaddr);
/* Browsed player is already set */
- if (folderObj.mScope == AvrcpConstants.BTRC_SCOPE_FILE_SYSTEM) {
+ if (folderObj.mScope == AvrcpConstants_ext.BTRC_SCOPE_FILE_SYSTEM) {
if (mBrowsingActiveDevice != null && !device.equals(mBrowsingActiveDevice)) {
Log.e(TAG, "handleGetFolderItemBrowse: Cmd from browse inactive device, reject it");
- getFolderItemsRspNative(bdaddr, AvrcpConstants.RSP_INTERNAL_ERR, (short) 0,
+ getFolderItemsRspNative(bdaddr, AvrcpConstants_ext.RSP_INTERNAL_ERR, (short) 0,
(byte) 0x00, 0, null, null, null, null, null, null, null, null);
return;
}
if (mAvrcpBrowseManager.getBrowsedMediaPlayer(bdaddr) == null) {
Log.e(TAG, "handleGetFolderItemBrowseResponse: no browsed player set for "
+ Utils.getAddressStringFromByte(bdaddr));
- getFolderItemsRspNative(bdaddr, AvrcpConstants.RSP_INTERNAL_ERR, (short) 0,
+ getFolderItemsRspNative(bdaddr, AvrcpConstants_ext.RSP_INTERNAL_ERR, (short) 0,
(byte) 0x00, 0, null, null, null, null, null, null, null, null);
return;
}
mAvrcpBrowseManager.getBrowsedMediaPlayer(bdaddr).getFolderItemsVFS(folderObj);
return;
}
- if (folderObj.mScope == AvrcpConstants.BTRC_SCOPE_NOW_PLAYING) {
+ if (folderObj.mScope == AvrcpConstants_ext.BTRC_SCOPE_NOW_PLAYING) {
mAddressedMediaPlayer.getFolderItemsNowPlaying(bdaddr, folderObj, mMediaController);
return;
}
/* invalid scope */
Log.e(TAG, "handleGetFolderItemBrowseResponse: unknown scope " + folderObj.mScope);
- getFolderItemsRspNative(bdaddr, AvrcpConstants.RSP_INV_SCOPE, (short) 0, (byte) 0x00, 0,
+ getFolderItemsRspNative(bdaddr, AvrcpConstants_ext.RSP_INV_SCOPE, (short) 0, (byte) 0x00, 0,
null, null, null, null, null, null, null, null);
}
@@ -4208,24 +4202,24 @@
HeadsetService mService = HeadsetService.getHeadsetService();
if ((mService != null) && mService.isScoOrCallActive()) {
Log.w(TAG, "Remote requesting play item while call is active");
- playItemRspNative(bdaddr, AvrcpConstants.RSP_MEDIA_IN_USE);
+ playItemRspNative(bdaddr, AvrcpConstants_ext.RSP_MEDIA_IN_USE);
return;
}
if (mBrowsingActiveDevice != null && !device.equals(mBrowsingActiveDevice)) {
Log.w(TAG, "play item Cmd from browse inactive device, reject it");
- playItemRspNative(bdaddr, AvrcpConstants.RSP_INTERNAL_ERR);
+ playItemRspNative(bdaddr, AvrcpConstants_ext.RSP_INTERNAL_ERR);
return;
}
- if (scope == AvrcpConstants.BTRC_SCOPE_NOW_PLAYING) {
+ if (scope == AvrcpConstants_ext.BTRC_SCOPE_NOW_PLAYING) {
mAddressedMediaPlayer.playItem(bdaddr, uid, mMediaController);
}
else {
if(!isAddrPlayerSameAsBrowsed(bdaddr)) {
Log.w(TAG, "Remote requesting play item on uid which may not be recognized by" +
"current addressed player");
- playItemRspNative(bdaddr, AvrcpConstants.RSP_INV_ITEM);
+ playItemRspNative(bdaddr, AvrcpConstants_ext.RSP_INV_ITEM);
return;
}
@@ -4234,12 +4228,12 @@
} else {
Log.e(TAG, "handlePlayItemResponse: Remote requested playitem " +
"before setbrowsedplayer");
- playItemRspNative(bdaddr, AvrcpConstants.RSP_INTERNAL_ERR);
+ playItemRspNative(bdaddr, AvrcpConstants_ext.RSP_INTERNAL_ERR);
}
}
}
- private void handleGetItemAttr(AvrcpCmd.ItemAttrCmd itemAttr) {
+ private void handleGetItemAttr(AvrcpCmd_ext.ItemAttrCmd itemAttr) {
if (itemAttr.mUidCounter != sUIDCounter) {
itemAttr.mUidCounter = sUIDCounter;
Log.e(TAG, "handleGetItemAttr: invalid uid counter, assign new value = " + itemAttr.mUidCounter);
@@ -4249,13 +4243,13 @@
if (mBrowsingActiveDevice != null && !device.equals(mBrowsingActiveDevice)) {
Log.e(TAG, "Item attributes from browse inactive device, reject it");
getItemAttrRspNative(
- itemAttr.mAddress, AvrcpConstants.RSP_INTERNAL_ERR, (byte) 0, null, null);
+ itemAttr.mAddress, AvrcpConstants_ext.RSP_INTERNAL_ERR, (byte) 0, null, null);
return;
}
- if (itemAttr.mScope == AvrcpConstants.BTRC_SCOPE_NOW_PLAYING) {
+ if (itemAttr.mScope == AvrcpConstants_ext.BTRC_SCOPE_NOW_PLAYING) {
if (mCurrAddrPlayerID == NO_PLAYER_ID) {
getItemAttrRspNative(
- itemAttr.mAddress, AvrcpConstants.RSP_NO_AVBL_PLAY, (byte) 0, null, null);
+ itemAttr.mAddress, AvrcpConstants_ext.RSP_NO_AVBL_PLAY, (byte) 0, null, null);
return;
}
mAddressedMediaPlayer.getItemAttr(itemAttr.mAddress, itemAttr, mMediaController);
@@ -4267,20 +4261,20 @@
} else {
Log.e(TAG, "Could not get attributes. mBrowsedMediaPlayer is null");
getItemAttrRspNative(
- itemAttr.mAddress, AvrcpConstants.RSP_INTERNAL_ERR, (byte) 0, null, null);
+ itemAttr.mAddress, AvrcpConstants_ext.RSP_INTERNAL_ERR, (byte) 0, null, null);
}
}
private void handleGetTotalNumOfItemsResponse(byte[] bdaddr, byte scope) {
BluetoothDevice device = BluetoothAdapter.getDefaultAdapter().getRemoteDevice(bdaddr);
if (mBrowsingActiveDevice != null && !device.equals(mBrowsingActiveDevice)) {
- getTotalNumOfItemsRspNative(bdaddr, AvrcpConstants.RSP_INTERNAL_ERR, 0, 0);
+ getTotalNumOfItemsRspNative(bdaddr, AvrcpConstants_ext.RSP_INTERNAL_ERR, 0, 0);
Log.w(TAG, "GetTotalNumOfItems: Cmd from browse inactive device reject it");
return;
}
// for scope as media player list
- if (scope == AvrcpConstants.BTRC_SCOPE_PLAYER_LIST) {
+ if (scope == AvrcpConstants_ext.BTRC_SCOPE_PLAYER_LIST) {
int numPlayers = 0;
synchronized(this) {
synchronized (mMediaPlayerInfoList) {
@@ -4288,11 +4282,11 @@
}
}
if (DEBUG) Log.d(TAG, "handleGetTotalNumOfItemsResponse: " + numPlayers + " players.");
- getTotalNumOfItemsRspNative(bdaddr, AvrcpConstants.RSP_NO_ERROR, 0, numPlayers);
- } else if (scope == AvrcpConstants.BTRC_SCOPE_NOW_PLAYING) {
+ getTotalNumOfItemsRspNative(bdaddr, AvrcpConstants_ext.RSP_NO_ERROR, 0, numPlayers);
+ } else if (scope == AvrcpConstants_ext.BTRC_SCOPE_NOW_PLAYING) {
if (mMediaController == null) {
Log.e(TAG, "Could not get Total NumOfItems. mMediaController is null");
- getTotalNumOfItemsRspNative(bdaddr, AvrcpConstants.RSP_NO_AVBL_PLAY, 0, 0);
+ getTotalNumOfItemsRspNative(bdaddr, AvrcpConstants_ext.RSP_NO_AVBL_PLAY, 0, 0);
return;
}
mAddressedMediaPlayer.getTotalNumOfItems(bdaddr, mMediaController);
@@ -4302,7 +4296,7 @@
mAvrcpBrowseManager.getBrowsedMediaPlayer(bdaddr).getTotalNumOfItems(scope);
} else {
Log.e(TAG, "Could not get Total NumOfItems. mBrowsedMediaPlayer is null");
- getTotalNumOfItemsRspNative(bdaddr, AvrcpConstants.RSP_INTERNAL_ERR, 0, 0);
+ getTotalNumOfItemsRspNative(bdaddr, AvrcpConstants_ext.RSP_INTERNAL_ERR, 0, 0);
}
}
@@ -4317,7 +4311,7 @@
return false;
}
- MediaPlayerInfo info = getAddressedPlayerInfo();
+ MediaPlayerInfo_ext info = getAddressedPlayerInfo();
String packageName = (info == null) ? "<none>" : info.getPackageName();
if (info == null || !packageName.equals(browsedPlayer)) {
if (DEBUG) Log.d(TAG, browsedPlayer + " is not addressed player " + packageName);
@@ -4350,12 +4344,12 @@
Log.i(TAG,"cleanupDeviceFeaturesIndex index:" + index);
deviceFeatures[index].mCurrentDevice = null;
deviceFeatures[index].mCurrentPlayState = new PlaybackState.Builder().setState(PlaybackState.STATE_NONE, -1L, 0.0f).build();;
- deviceFeatures[index].mNowPlayingListChangedNT = AvrcpConstants.NOTIFICATION_TYPE_CHANGED;
- deviceFeatures[index].mPlayStatusChangedNT = AvrcpConstants.NOTIFICATION_TYPE_CHANGED;
- deviceFeatures[index].mPlayerStatusChangeNT = AvrcpConstants.NOTIFICATION_TYPE_CHANGED;
- deviceFeatures[index].mTrackChangedNT = AvrcpConstants.NOTIFICATION_TYPE_CHANGED;
+ deviceFeatures[index].mNowPlayingListChangedNT = AvrcpConstants_ext.NOTIFICATION_TYPE_CHANGED;
+ deviceFeatures[index].mPlayStatusChangedNT = AvrcpConstants_ext.NOTIFICATION_TYPE_CHANGED;
+ deviceFeatures[index].mPlayerStatusChangeNT = AvrcpConstants_ext.NOTIFICATION_TYPE_CHANGED;
+ deviceFeatures[index].mTrackChangedNT = AvrcpConstants_ext.NOTIFICATION_TYPE_CHANGED;
deviceFeatures[index].mPlaybackIntervalMs = 0L;
- deviceFeatures[index].mPlayPosChangedNT = AvrcpConstants.NOTIFICATION_TYPE_CHANGED;
+ deviceFeatures[index].mPlayPosChangedNT = AvrcpConstants_ext.NOTIFICATION_TYPE_CHANGED;
deviceFeatures[index].mFeatures = 0;
deviceFeatures[index].mAbsoluteVolume = -1;
deviceFeatures[index].mLastRspPlayStatus = -1;
@@ -4364,13 +4358,13 @@
deviceFeatures[index].mVolCmdSetInProgress = false;
deviceFeatures[index].mVolCmdAdjustInProgress = false;
deviceFeatures[index].mAbsVolRetryTimes = 0;
- deviceFeatures[index].mAvailablePlayersChangedNT = AvrcpConstants.NOTIFICATION_TYPE_CHANGED;
+ deviceFeatures[index].mAvailablePlayersChangedNT = AvrcpConstants_ext.NOTIFICATION_TYPE_CHANGED;
deviceFeatures[index].isActiveDevice = false;
- deviceFeatures[index].mAddrPlayerChangedNT = AvrcpConstants.NOTIFICATION_TYPE_CHANGED;
- deviceFeatures[index].mUidsChangedNT = AvrcpConstants.NOTIFICATION_TYPE_CHANGED;
+ deviceFeatures[index].mAddrPlayerChangedNT = AvrcpConstants_ext.NOTIFICATION_TYPE_CHANGED;
+ deviceFeatures[index].mUidsChangedNT = AvrcpConstants_ext.NOTIFICATION_TYPE_CHANGED;
deviceFeatures[index].mLastPassthroughcmd = KeyEvent.KEYCODE_UNKNOWN;
deviceFeatures[index].isAbsoluteVolumeSupportingDevice = false;
- deviceFeatures[index].keyPressState = AvrcpConstants.KEY_STATE_RELEASE; //Key release state
+ deviceFeatures[index].keyPressState = AvrcpConstants_ext.KEY_STATE_RELEASE; //Key release state
deviceFeatures[index].mReportedPlayerID = NO_PLAYER_ID;
deviceFeatures[index].isPlayStatusTimeOut = false;
deviceFeatures[index].mInitialRemoteVolume = -1;
@@ -4440,7 +4434,7 @@
ProfileService.println(sb, "Media Players:");
synchronized (this) {
synchronized (mMediaPlayerInfoList) {
- for (Map.Entry<Integer, MediaPlayerInfo> entry : mMediaPlayerInfoList.entrySet()) {
+ for (Map.Entry<Integer, MediaPlayerInfo_ext> entry : mMediaPlayerInfoList.entrySet()) {
int key = entry.getKey();
ProfileService.println(sb, ((mCurrAddrPlayerID == key) ? " *#" : " #")
+ entry.getKey() + ": " + entry.getValue());
@@ -4482,11 +4476,11 @@
}
public class AvrcpBrowseManager {
- Map<String, BrowsedMediaPlayer> connList = new HashMap<String, BrowsedMediaPlayer>();
- private AvrcpMediaRspInterface mMediaInterface;
+ Map<String, BrowsedMediaPlayer_ext> connList = new HashMap<String, BrowsedMediaPlayer_ext>();
+ private AvrcpMediaRspInterface_ext mMediaInterface;
private Context mContext;
- public AvrcpBrowseManager(Context context, AvrcpMediaRspInterface mediaInterface) {
+ public AvrcpBrowseManager(Context context, AvrcpMediaRspInterface_ext mediaInterface) {
mContext = context;
mMediaInterface = mediaInterface;
}
@@ -4495,7 +4489,7 @@
Iterator entries = connList.entrySet().iterator();
while (entries.hasNext()) {
Map.Entry entry = (Map.Entry) entries.next();
- BrowsedMediaPlayer browsedMediaPlayer = (BrowsedMediaPlayer) entry.getValue();
+ BrowsedMediaPlayer_ext browsedMediaPlayer = (BrowsedMediaPlayer_ext) entry.getValue();
if (browsedMediaPlayer != null) {
browsedMediaPlayer.cleanup();
}
@@ -4507,13 +4501,13 @@
// get the a free media player interface based on the passed bd address
// if the no items is found for the passed media player then it assignes a
// available media player interface
- public BrowsedMediaPlayer getBrowsedMediaPlayer(byte[] bdaddr) {
- BrowsedMediaPlayer mediaPlayer;
+ public BrowsedMediaPlayer_ext getBrowsedMediaPlayer(byte[] bdaddr) {
+ BrowsedMediaPlayer_ext mediaPlayer;
String bdaddrStr = new String(bdaddr);
if (connList.containsKey(bdaddrStr)) {
mediaPlayer = connList.get(bdaddrStr);
} else {
- mediaPlayer = new BrowsedMediaPlayer(bdaddr, mContext, mMediaInterface);
+ mediaPlayer = new BrowsedMediaPlayer_ext(bdaddr, mContext, mMediaInterface);
connList.put(bdaddrStr, mediaPlayer);
}
return mediaPlayer;
@@ -4529,7 +4523,7 @@
return false;
}
- public Map<String, BrowsedMediaPlayer> getConnList() {
+ public Map<String, BrowsedMediaPlayer_ext> getConnList() {
return connList;
}
@@ -4567,14 +4561,14 @@
}
public void SendSetPlayerAppRsp(int attr_status, byte[] address) {
- if (attr_status != AvrcpConstants.RSP_INTERNAL_ERR) {
+ if (attr_status != AvrcpConstants_ext.RSP_INTERNAL_ERR) {
for (int i = 0; i < maxAvrcpConnections; i++) {
if (deviceFeatures[i].mCurrentDevice != null &&
deviceFeatures[i].mPlayerStatusChangeNT ==
- AvrcpConstants.NOTIFICATION_TYPE_INTERIM) {
+ AvrcpConstants_ext.NOTIFICATION_TYPE_INTERIM) {
Log.v(TAG,"device has registered for mPlayerAppSettingStatusChangeNT");
deviceFeatures[i].mPlayerStatusChangeNT =
- AvrcpConstants.NOTIFICATION_TYPE_CHANGED;
+ AvrcpConstants_ext.NOTIFICATION_TYPE_CHANGED;
mAvrcpPlayerAppSettings.sendPlayerAppChangedRsp(
deviceFeatures[i].mPlayerStatusChangeNT,
deviceFeatures[i].mCurrentDevice);
@@ -4629,7 +4623,7 @@
* private class which handles responses from AvrcpMediaManager. Maps responses to native
* responses. This class implements the AvrcpMediaRspInterface interface.
*/
- private class AvrcpMediaRsp implements AvrcpMediaRspInterface {
+ private class AvrcpMediaRsp implements AvrcpMediaRspInterface_ext {
private static final String TAG = "AvrcpMediaRsp";
public void setAddrPlayerRsp(byte[] address, int rspStatus) {
@@ -4651,8 +4645,8 @@
}
}
- public void mediaPlayerListRsp(byte[] address, int rspStatus, MediaPlayerListRsp rspObj) {
- if (rspObj != null && rspStatus == AvrcpConstants.RSP_NO_ERROR) {
+ public void mediaPlayerListRsp(byte[] address, int rspStatus, MediaPlayerListRsp_ext rspObj) {
+ if (rspObj != null && rspStatus == AvrcpConstants_ext.RSP_NO_ERROR) {
if (!mediaPlayerListRspNative(address, rspStatus, sUIDCounter, rspObj.mItemType,
rspObj.mNumItems, rspObj.mPlayerIds, rspObj.mPlayerTypes,
rspObj.mPlayerSubTypes, rspObj.mPlayStatusValues,
@@ -4666,24 +4660,24 @@
}
}
- public void folderItemsRsp(byte[] address, int rspStatus, FolderItemsRsp rspObj) {
- if (rspObj != null && rspStatus == AvrcpConstants.RSP_NO_ERROR) {
+ public void folderItemsRsp(byte[] address, int rspStatus, FolderItemsRsp_ext rspObj) {
+ if (rspObj != null && rspStatus == AvrcpConstants_ext.RSP_NO_ERROR) {
String Album = new String("Albums");
String Artist = new String("Artists");
String Playlist = new String("Playlists");
for (int index = 0; index < rspObj.mDisplayNames.length; index++) {
if (rspObj.mDisplayNames[index].equals(Album))
- rspObj.mFolderTypes[index] = AvrcpConstants.FOLDER_TYPE_ALBUMS;
+ rspObj.mFolderTypes[index] = AvrcpConstants_ext.FOLDER_TYPE_ALBUMS;
else if (rspObj.mDisplayNames[index].equals(Artist))
- rspObj.mFolderTypes[index] = AvrcpConstants.FOLDER_TYPE_ARTISTS;
+ rspObj.mFolderTypes[index] = AvrcpConstants_ext.FOLDER_TYPE_ARTISTS;
else if (rspObj.mDisplayNames[index].equals(Playlist))
- rspObj.mFolderTypes[index] = AvrcpConstants.FOLDER_TYPE_PLAYLISTS;
+ rspObj.mFolderTypes[index] = AvrcpConstants_ext.FOLDER_TYPE_PLAYLISTS;
/*by default for every folder filling folder type Titles*/
else
if (changePathFolderType > 0)
rspObj.mFolderTypes[index] = changePathFolderType;
else
- rspObj.mFolderTypes[index] = AvrcpConstants.FOLDER_TYPE_TITLES;
+ rspObj.mFolderTypes[index] = AvrcpConstants_ext.FOLDER_TYPE_TITLES;
}
Log.v(TAG, " changePathDepth " + changePathDepth +
" changePathFolderType " + changePathFolderType);
@@ -4707,7 +4701,7 @@
public void changePathRsp(byte[] address, int rspStatus, int numItems) {
/*to handle changePath invalid uid scenario or any error sceanrio */
- if (rspStatus != AvrcpConstants.RSP_NO_ERROR && changePathDepth>0) {
+ if (rspStatus != AvrcpConstants_ext.RSP_NO_ERROR && changePathDepth>0) {
if(changePathDirection == 1)
changePathDepth--;
else
@@ -4717,8 +4711,8 @@
Log.e(TAG, "changePathRspNative failed!");
}
- public void getItemAttrRsp(byte[] address, int rspStatus, ItemAttrRsp rspObj) {
- if (rspObj != null && rspStatus == AvrcpConstants.RSP_NO_ERROR) {
+ public void getItemAttrRsp(byte[] address, int rspStatus, ItemAttrRsp_ext rspObj) {
+ if (rspObj != null && rspStatus == AvrcpConstants_ext.RSP_NO_ERROR) {
if (!getItemAttrRspNative(address, rspStatus, rspObj.mNumAttr,
rspObj.mAttributesIds, rspObj.mAttributesArray))
Log.e(TAG, "getItemAttrRspNative failed!");
@@ -4739,7 +4733,7 @@
return;
}
- if((rspStatus == AvrcpConstants.RSP_NO_ERROR) && ((mA2dpService != null) &&
+ if((rspStatus == AvrcpConstants_ext.RSP_NO_ERROR) && ((mA2dpService != null) &&
!Objects.equals(mA2dpService.getActiveDevice(), device))) {
Log.d(TAG, "Trigger Handoff by playItem");
startSHO(device, true);
@@ -4786,7 +4780,7 @@
}
if ((index != INVALID_DEVICE_INDEX) &&
- (deviceFeatures[index].mUidsChangedNT != AvrcpConstants.NOTIFICATION_TYPE_INTERIM)) {
+ (deviceFeatures[index].mUidsChangedNT != AvrcpConstants_ext.NOTIFICATION_TYPE_INTERIM)) {
if (DEBUG) Log.d(TAG, "uidsChangedRsp: Not registered or requesting.");
return;
}
@@ -4799,7 +4793,7 @@
Log.e(TAG, "registerNotificationRspUIDsChangedNative failed!");
}
if (index != INVALID_DEVICE_INDEX)
- deviceFeatures[index].mUidsChangedNT = AvrcpConstants.NOTIFICATION_TYPE_CHANGED;
+ deviceFeatures[index].mUidsChangedNT = AvrcpConstants_ext.NOTIFICATION_TYPE_CHANGED;
}
public void nowPlayingChangedRsp(int type) {
@@ -4819,7 +4813,7 @@
Log.e(TAG,"uidsChangedRsp:No active device found");
return;
}
- if (mNowPlayingListChangedNT != AvrcpConstants.NOTIFICATION_TYPE_INTERIM) {
+ if (mNowPlayingListChangedNT != AvrcpConstants_ext.NOTIFICATION_TYPE_INTERIM) {
if (DEBUG) Log.d(TAG, "NowPlayingListChanged: Not registered or requesting.");
return;
}
@@ -4827,7 +4821,7 @@
if (!registerNotificationRspNowPlayingChangedNative(type, addr)) {
Log.e(TAG, "registerNotificationRspNowPlayingChangedNative failed!");
}
- mNowPlayingListChangedNT = AvrcpConstants.NOTIFICATION_TYPE_CHANGED;
+ mNowPlayingListChangedNT = AvrcpConstants_ext.NOTIFICATION_TYPE_CHANGED;
}
public void trackChangedRsp(int type, byte[] uid) {
@@ -5004,12 +4998,6 @@
//to keep volume copy for setting volume
deviceFeatures[deviceIndex].mLocalVolume = getVolume(device);
- if(deviceFeatures[deviceIndex].mLocalVolume == -1) {
- //volume is not stored so need to copy stream music
- //to send setAbsolute request
- deviceFeatures[deviceIndex].mLocalVolume =
- mAudioManager.getStreamVolume(AudioManager.STREAM_MUSIC);
- }
if ((maxAvrcpConnections > 1) && (deviceFeatures[deviceIndex].mCurrentDevice != null) &&
(deviceFeatures[deviceIndex].mReportedPlayerID != mCurrAddrPlayerID)) {
Log.d(TAG,"Update cached browsing events to latest active device, deviceFeatures[" +
@@ -5017,21 +5005,21 @@
deviceFeatures[deviceIndex].mReportedPlayerID +
", mCurrAddrPlayerID: " + mCurrAddrPlayerID);
if (deviceFeatures[deviceIndex].mAvailablePlayersChangedNT ==
- AvrcpConstants.NOTIFICATION_TYPE_INTERIM) {
+ AvrcpConstants_ext.NOTIFICATION_TYPE_INTERIM) {
registerNotificationRspAvalPlayerChangedNative(
- AvrcpConstants.NOTIFICATION_TYPE_CHANGED,
+ AvrcpConstants_ext.NOTIFICATION_TYPE_CHANGED,
getByteAddress(deviceFeatures[deviceIndex].mCurrentDevice));
mAvailablePlayerViewChanged = false;
deviceFeatures[deviceIndex].mAvailablePlayersChangedNT =
- AvrcpConstants.NOTIFICATION_TYPE_CHANGED;
+ AvrcpConstants_ext.NOTIFICATION_TYPE_CHANGED;
}
if (deviceFeatures[deviceIndex].mAddrPlayerChangedNT ==
- AvrcpConstants.NOTIFICATION_TYPE_INTERIM) {
+ AvrcpConstants_ext.NOTIFICATION_TYPE_INTERIM) {
registerNotificationRspAddrPlayerChangedNative(
- AvrcpConstants.NOTIFICATION_TYPE_CHANGED, mCurrAddrPlayerID,
+ AvrcpConstants_ext.NOTIFICATION_TYPE_CHANGED, mCurrAddrPlayerID,
sUIDCounter, getByteAddress(deviceFeatures[deviceIndex].mCurrentDevice));
deviceFeatures[deviceIndex].mAddrPlayerChangedNT =
- AvrcpConstants.NOTIFICATION_TYPE_CHANGED;
+ AvrcpConstants_ext.NOTIFICATION_TYPE_CHANGED;
// send track change event becasue some carkits will refresh metadata
// while receive addressed player change event. Track change event to
// make remote get metadata correctly.
@@ -5083,16 +5071,22 @@
Log.e(TAG,"Invalid device index for setAbsVolumeFlag");
return;
}
- /* Delay updating absVolume support notification to audiomanager
- * as absVolume flag in audio audioservice is reset when device disconnect is
- * notified to audiomanager in SHO is processed late. Resulting in not sending
- * setABsVolume cmd to remote
- */
+ //updating abs volume supported or not to audio when active device change is success
+ mAudioManager.avrcpSupportsAbsoluteVolume(device.getAddress(),
+ isAbsoluteVolumeSupported(deviceIndex));
+ if(deviceFeatures[deviceIndex].isAbsoluteVolumeSupportingDevice == false) {
+ Log.d(TAG,"isAbsoluteVolumeSupportingDevice is false or volume is not stored");
+ return;
+ }
+ if(deviceFeatures[deviceIndex].mInitialRemoteVolume == -1 || getVolume(device) == -1) {
+ Log.e(TAG,"intial volume is not updated or volume is not stored");
+ return;
+ }
Message msg = mHandler.obtainMessage();
msg.what = MESSAGE_UPDATE_ABS_VOLUME_STATUS;
msg.arg1 = deviceIndex;
msg.arg2 = deviceFeatures[deviceIndex].mLocalVolume;
- mHandler.sendMessageDelayed(msg, 100);
+ mHandler.sendMessage(msg);
Log.d(TAG,"setAbsVolumeFlag = " + isAbsoluteVolumeSupported(deviceIndex));
return;
}
@@ -5129,7 +5123,7 @@
Log.d(TAG, "Active device: " + mA2dpService.getActiveDevice());
int action = KeyEvent.ACTION_DOWN;
- if (state == AvrcpConstants.KEY_STATE_RELEASE) action = KeyEvent.ACTION_UP;
+ if (state == AvrcpConstants_ext.KEY_STATE_RELEASE) action = KeyEvent.ACTION_UP;
BluetoothDevice a2dp_active_device = null;
boolean skip = false;;
if (mA2dpService != null) a2dp_active_device = mA2dpService.getActiveDevice();
@@ -5224,7 +5218,7 @@
} else {
if (code == KeyEvent.KEYCODE_MEDIA_FAST_FORWARD) {
if ((state == deviceFeatures[deviceIndex].keyPressState) &&
- (state == AvrcpConstants.KEY_STATE_RELEASE)) {
+ (state == AvrcpConstants_ext.KEY_STATE_RELEASE)) {
Log.e(TAG, "Ignore fast forward key release event");
return;
}
@@ -5236,7 +5230,7 @@
deviceFeatures[deviceIndex].keyPressState = state;
} else if (code == KeyEvent.KEYCODE_MEDIA_REWIND) {
if ((state == deviceFeatures[deviceIndex].keyPressState) &&
- (state == AvrcpConstants.KEY_STATE_RELEASE)) {
+ (state == AvrcpConstants_ext.KEY_STATE_RELEASE)) {
Log.e(TAG, "Ignore rewind key release event");
return;
}
@@ -5256,7 +5250,7 @@
* changed response at the time of Release of Fast-Forward/Rewind Button */
if ((code == KeyEvent.KEYCODE_MEDIA_FAST_FORWARD || code == KeyEvent.KEYCODE_MEDIA_REWIND)
&& (deviceFeatures[deviceIndex].mPlayStatusChangedNT ==
- AvrcpConstants.NOTIFICATION_TYPE_INTERIM) && (action == KeyEvent.ACTION_UP)) {
+ AvrcpConstants_ext.NOTIFICATION_TYPE_INTERIM) && (action == KeyEvent.ACTION_UP)) {
int currentPlayState =
convertPlayStateToPlayStatus(deviceFeatures[deviceIndex].mCurrentPlayState);
Log.d(TAG, " currentPlayState: " + currentPlayState + " mLastRspPlayStatus: " +
@@ -5264,7 +5258,7 @@
if (deviceFeatures[deviceIndex].mCurrentDevice != null &&
deviceFeatures[deviceIndex].mLastRspPlayStatus != currentPlayState) {
deviceFeatures[deviceIndex].mPlayStatusChangedNT =
- AvrcpConstants.NOTIFICATION_TYPE_CHANGED;
+ AvrcpConstants_ext.NOTIFICATION_TYPE_CHANGED;
registerNotificationRspPlayStatusNative(deviceFeatures[deviceIndex].mPlayStatusChangedNT
,currentPlayState,
getByteAddress(deviceFeatures[deviceIndex].mCurrentDevice));
diff --git a/packages_apps_bluetooth_ext/src/avrcp/BrowsedMediaPlayer_ext.java b/packages_apps_bluetooth_ext/src/avrcp/BrowsedMediaPlayer_ext.java
new file mode 100644
index 0000000..8f1801d
--- /dev/null
+++ b/packages_apps_bluetooth_ext/src/avrcp/BrowsedMediaPlayer_ext.java
@@ -0,0 +1,1075 @@
+/*
+ * 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.
+ */
+
+package com.android.bluetooth.avrcp;
+
+import android.annotation.NonNull;
+import android.content.ComponentName;
+import android.content.Context;
+import android.media.MediaDescription;
+import android.media.MediaMetadata;
+import android.media.browse.MediaBrowser;
+import android.media.browse.MediaBrowser.MediaItem;
+import android.media.session.MediaSession;
+import android.os.Bundle;
+import android.util.Log;
+
+import java.math.BigInteger;
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Stack;
+
+/*************************************************************************************************
+ * Provides functionality required for Browsed Media Player like browsing Virtual File System, get
+ * Item Attributes, play item from the file system, etc.
+ * Acts as an Interface to communicate with Media Browsing APIs for browsing FileSystem.
+ ************************************************************************************************/
+
+class BrowsedMediaPlayer_ext {
+ private static final boolean DEBUG = true;
+ private static final String TAG = "BrowsedMediaPlayer_ext";
+
+ /* connection state with MediaBrowseService */
+ private static final int DISCONNECTED = 0;
+ private static final int CONNECTED = 1;
+ private static final int SUSPENDED = 2;
+
+ private static final int BROWSED_ITEM_ID_INDEX = 2;
+ private static final int BROWSED_FOLDER_ID_INDEX = 4;
+ private static final String[] ROOT_FOLDER = {"root"};
+ private static boolean mPlayerRoot = false;
+ /* package and service name of target Media Player which is set for browsing */
+ private String mPackageName;
+ private String mConnectingPackageName;
+ private String mClassName;
+ private Context mContext;
+ private AvrcpMediaRspInterface_ext mMediaInterface;
+ private byte[] mBDAddr;
+
+ private String mCurrentBrowsePackage;
+ private String mCurrentBrowseClass;
+
+ /* Object used to connect to MediaBrowseService of Media Player */
+ private MediaBrowser mMediaBrowser = null;
+ private MediaController mMediaController = null;
+
+ /* The mediaId to be used for subscribing for children using the MediaBrowser */
+ private String mMediaId = null;
+ private String mRootFolderUid = null;
+ private int mConnState = DISCONNECTED;
+
+ /* stores the path trail during changePath */
+ private Stack<String> mPathStack = null;
+ private Stack<String> mLocalPathCache = null;
+ /* Number of items in current folder */
+ private int mCurrFolderNumItems = 0;
+
+ /* store mapping between uid(Avrcp) and mediaId(Media Player) for Media Item */
+ private HashMap<Integer, String> mMediaHmap = new HashMap<Integer, String>();
+
+ /* store mapping between uid(Avrcp) and mediaId(Media Player) for Folder Item */
+ private HashMap<Integer, String> mFolderHmap = new HashMap<Integer, String>();
+
+ /* command objects from avrcp handler */
+ private AvrcpCmd_ext.FolderItemsCmd mFolderItemsReqObj;
+
+ /* store result of getfolderitems with scope="vfs" */
+ private List<MediaBrowser.MediaItem> mFolderItems = null;
+
+ /* Connection state callback handler */
+ class MediaConnectionCallback extends MediaBrowser.ConnectionCallback {
+ private String mCallbackPackageName;
+ private MediaBrowser mBrowser;
+
+ MediaConnectionCallback(String packageName) {
+ this.mCallbackPackageName = packageName;
+ }
+
+ public void setBrowser(MediaBrowser b) {
+ mBrowser = b;
+ }
+
+ @Override
+ public void onConnected() {
+ mConnState = CONNECTED;
+ if (DEBUG) {
+ Log.d(TAG, "mediaBrowser CONNECTED to " + mPackageName);
+ }
+ /* perform init tasks and set player as browsed player on successful connection */
+ onBrowseConnect(mCallbackPackageName, mBrowser);
+
+ // Remove what could be a circular dependency causing GC to never happen on this object
+ mBrowser = null;
+ }
+
+ @Override
+ public void onConnectionFailed() {
+ mConnState = DISCONNECTED;
+ // Remove what could be a circular dependency causing GC to never happen on this object
+ mBrowser = null;
+ Log.e(TAG, "mediaBrowser Connection failed with " + mPackageName
+ + ", Sending fail response!");
+ mMediaInterface.setBrowsedPlayerRsp(mBDAddr, AvrcpConstants_ext.RSP_INTERNAL_ERR,
+ (byte) 0x00, 0, null);
+ }
+
+ @Override
+ public void onConnectionSuspended() {
+ mBrowser = null;
+ mConnState = SUSPENDED;
+ Log.e(TAG, "mediaBrowser SUSPENDED connection with " + mPackageName);
+ }
+ }
+
+ /* Subscription callback handler. Subscribe to a folder to get its contents */
+ private MediaBrowser.SubscriptionCallback mFolderItemsCb =
+ new MediaBrowser.SubscriptionCallback() {
+
+ @Override
+ public void onChildrenLoaded(String parentId,
+ List<MediaBrowser.MediaItem> children) {
+ if (DEBUG) {
+ Log.d(TAG, "OnChildren Loaded folder items: childrens= " + children.size());
+ }
+
+ /*
+ * cache current folder items and send as rsp when remote requests
+ * get_folder_items (scope = vfs)
+ */
+ if (mFolderItems == null) {
+ if (DEBUG) {
+ Log.d(TAG, "sending setbrowsed player rsp");
+ }
+ Log.w(TAG, "sending setbrowsed player rsp");
+ mFolderItems = children;
+ mMediaInterface.setBrowsedPlayerRsp(mBDAddr, AvrcpConstants_ext.RSP_NO_ERROR,
+ (byte) 0x00, children.size(), ROOT_FOLDER);
+ } else {
+ mFolderItems = children;
+ mCurrFolderNumItems = mFolderItems.size();
+ mMediaInterface.changePathRsp(mBDAddr, AvrcpConstants_ext.RSP_NO_ERROR,
+ mCurrFolderNumItems);
+ }
+ refreshFolderItems(mFolderItems);
+ mMediaBrowser.unsubscribe(parentId);
+ }
+
+ /* UID is invalid */
+ @Override
+ public void onError(String id) {
+ Log.e(TAG, "set browsed player rsp. Could not get root folder items");
+ mMediaInterface.setBrowsedPlayerRsp(mBDAddr, AvrcpConstants_ext.RSP_INTERNAL_ERR,
+ (byte) 0x00, 0, null);
+ }
+ };
+
+ /* callback from media player in response to getitemAttr request */
+ private class ItemAttribSubscriber extends MediaBrowser.SubscriptionCallback {
+ private String mMediaId;
+ private AvrcpCmd_ext.ItemAttrCmd mAttrReq;
+
+ ItemAttribSubscriber(@NonNull AvrcpCmd_ext.ItemAttrCmd attrReq, @NonNull String mediaId) {
+ mAttrReq = attrReq;
+ mMediaId = mediaId;
+ }
+
+ @Override
+ public void onChildrenLoaded(String parentId, List<MediaBrowser.MediaItem> children) {
+ String logprefix = "ItemAttribSubscriber(" + mMediaId + "): ";
+ if (DEBUG) {
+ Log.d(TAG, logprefix + "OnChildren Loaded");
+ }
+ int status = AvrcpConstants_ext.RSP_INV_ITEM;
+
+ if (children == null) {
+ Log.w(TAG, logprefix + "children list is null parentId: " + parentId);
+ } else {
+ /* find the item in the folder */
+ for (MediaBrowser.MediaItem item : children) {
+ if (item.getMediaId().equals(mMediaId)) {
+ if (DEBUG) {
+ Log.d(TAG, logprefix + "found item");
+ }
+ getItemAttrFilterAttr(item);
+ status = AvrcpConstants_ext.RSP_NO_ERROR;
+ break;
+ }
+ }
+ }
+ /* Send only error from here, in case of success, getItemAttrFilterAttr sends */
+ if (status != AvrcpConstants_ext.RSP_NO_ERROR) {
+ Log.e(TAG, logprefix + "not able to find item from " + parentId);
+ mMediaInterface.getItemAttrRsp(mBDAddr, status, null);
+ }
+ mMediaBrowser.unsubscribe(parentId);
+ }
+
+ @Override
+ public void onError(String id) {
+ Log.e(TAG, "Could not get attributes from media player id: " + id);
+ mMediaInterface.getItemAttrRsp(mBDAddr, AvrcpConstants_ext.RSP_INTERNAL_ERR, null);
+ }
+
+ /* helper method to filter required attibuteand send GetItemAttr response */
+ private void getItemAttrFilterAttr(@NonNull MediaBrowser.MediaItem mediaItem) {
+ /* Response parameters */
+ int[] attrIds = null; /* array of attr ids */
+ String[] attrValues = null; /* array of attr values */
+
+ /* variables to temperorily add attrs */
+ ArrayList<Integer> attrIdArray = new ArrayList<Integer>();
+ ArrayList<String> attrValueArray = new ArrayList<String>();
+ ArrayList<Integer> attrReqIds = new ArrayList<Integer>();
+
+ if (mAttrReq.mNumAttr == AvrcpConstants_ext.NUM_ATTR_NONE) {
+ // Note(jamuraa): the stack should never send this, remove?
+ Log.i(TAG, "getItemAttrFilterAttr: No attributes requested");
+ mMediaInterface.getItemAttrRsp(mBDAddr, AvrcpConstants_ext.RSP_BAD_PARAM, null);
+ return;
+ }
+
+ /* check if remote device has requested all attributes */
+ if (mAttrReq.mNumAttr == AvrcpConstants_ext.NUM_ATTR_ALL
+ || mAttrReq.mNumAttr == AvrcpConstants_ext.MAX_NUM_ATTR) {
+ for (int idx = 1; idx <= AvrcpConstants_ext.MAX_NUM_ATTR; idx++) {
+ attrReqIds.add(idx); /* attr id 0x00 is unused */
+ }
+ } else {
+ /* get only the requested attribute ids from the request */
+ for (int idx = 0; idx < mAttrReq.mNumAttr; idx++) {
+ attrReqIds.add(mAttrReq.mAttrIDs[idx]);
+ }
+ }
+
+ /* lookup and copy values of attributes for ids requested above */
+ for (int attrId : attrReqIds) {
+ /* check if media player provided requested attributes */
+ String value = getAttrValue(mBDAddr, attrId, mediaItem);
+ if (value != null) {
+ attrIdArray.add(attrId);
+ attrValueArray.add(value);
+ }
+ }
+
+ /* copy filtered attr ids and attr values to response parameters */
+ attrIds = new int[attrIdArray.size()];
+ for (int i = 0; i < attrIdArray.size(); i++) {
+ attrIds[i] = attrIdArray.get(i);
+ }
+
+ attrValues = attrValueArray.toArray(new String[attrIdArray.size()]);
+
+ /* create rsp object and send response */
+ ItemAttrRsp_ext rspObj = new ItemAttrRsp_ext(AvrcpConstants_ext.RSP_NO_ERROR, attrIds, attrValues);
+ mMediaInterface.getItemAttrRsp(mBDAddr, AvrcpConstants_ext.RSP_NO_ERROR, rspObj);
+ }
+ }
+
+ /* Constructor */
+ BrowsedMediaPlayer_ext(byte[] address, Context context,
+ AvrcpMediaRspInterface_ext mAvrcpMediaRspInterface) {
+ mContext = context;
+ mMediaInterface = mAvrcpMediaRspInterface;
+ mBDAddr = address;
+ }
+
+ /* initialize mediacontroller in order to communicate with media player. */
+ private void onBrowseConnect(String connectedPackage, MediaBrowser browser) {
+ if (!connectedPackage.equals(mConnectingPackageName)) {
+ Log.w(TAG, "onBrowseConnect: recieved callback for package" + mConnectingPackageName +
+ "we aren't connecting to " + connectedPackage);
+ mMediaInterface.setBrowsedPlayerRsp(
+ mBDAddr, AvrcpConstants_ext.RSP_INTERNAL_ERR, (byte) 0x00, 0, null);
+ return;
+ }
+ mConnectingPackageName = null;
+
+ if (browser == null) {
+ Log.e(TAG, "onBrowseConnect: received a null browser for " + connectedPackage);
+ mMediaInterface.setBrowsedPlayerRsp(mBDAddr, AvrcpConstants_ext.RSP_INTERNAL_ERR,
+ (byte) 0x00, 0, null);
+ return;
+ }
+
+ MediaSession.Token token = null;
+ try {
+ if (!browser.isConnected()) {
+ Log.e(TAG, "setBrowsedPlayer: " + mPackageName + "not connected");
+ } else if ((token = browser.getSessionToken()) == null) {
+ Log.e(TAG, "setBrowsedPlayer: " + mPackageName + "no Session token");
+ } else {
+ /* update to the new MediaBrowser */
+ if (mMediaBrowser != null) {
+ mMediaBrowser.disconnect();
+ }
+ mMediaBrowser = browser;
+ mPackageName = connectedPackage;
+
+ /* get rootfolder uid from media player */
+ if (mMediaId == null) {
+ mMediaId = mMediaBrowser.getRoot();
+ Log.d(TAG, "media browser root = " + mMediaId);
+
+ if (mMediaId == null || mMediaId.length() == 0) {
+ Log.e(TAG, "onBrowseConnect: root value is empty or null");
+ mMediaInterface.setBrowsedPlayerRsp(
+ mBDAddr, AvrcpConstants_ext.RSP_INTERNAL_ERR, (byte) 0x00, 0, null);
+ return;
+ }
+
+ /*
+ * assuming that root folder uid will not change on uids changed
+ */
+ mRootFolderUid = mMediaId;
+ /* store root folder uid to stack */
+ mPathStack.push(mMediaId);
+ String [] ExternalPath = mMediaId.split("/");
+ if (ExternalPath != null) {
+ Log.d(TAG,"external path length: " + ExternalPath.length);
+ if (ExternalPath.length == 1) {
+ mLocalPathCache.push(mMediaId);
+ } else if (ExternalPath.length == 0 && mMediaId.equals("/")) {
+ mPlayerRoot = true;
+ mLocalPathCache.push(mMediaId);
+ } else {
+ //to trim the root in GMP which comes as "com.google.android.music.generic/root"
+ mLocalPathCache.push(ExternalPath[ExternalPath.length - 1]);
+ }
+ }
+ /* get root folder items */
+ Log.e(TAG, "onBrowseConnect: subscribe event for FolderCb");
+ mMediaBrowser.subscribe(mRootFolderUid, mFolderItemsCb);
+ }
+
+ mMediaController = MediaControllerFactory.make(mContext, token);
+ return;
+ }
+ } catch (NullPointerException ex) {
+ Log.e(TAG, "setBrowsedPlayer : Null pointer during init");
+ ex.printStackTrace();
+ }
+
+ mMediaInterface.setBrowsedPlayerRsp(mBDAddr, AvrcpConstants_ext.RSP_INTERNAL_ERR, (byte) 0x00,
+ 0, null);
+ }
+
+ public void setBrowsed(String packageName, String cls) {
+ Log.w(TAG, "!! In setBrowse function !!" + mFolderItems);
+ if ((mPackageName != null && packageName != null
+ && !mPackageName.equals(packageName)) || (mFolderItems == null)) {
+ Log.d(TAG, "setBrowse for packageName = " + packageName);
+ mConnectingPackageName = packageName;
+ mPackageName = packageName;
+ mClassName = cls;
+
+ /* cleanup variables from previous browsed calls */
+ mFolderItems = null;
+ mMediaId = null;
+ mRootFolderUid = null;
+ mPlayerRoot = false;
+ /*
+ * create stack to store the navigation trail (current folder ID). This
+ * will be required while navigating up the folder
+ */
+ mPathStack = new Stack<String>();
+ mLocalPathCache = new Stack<String>();
+ /* Bind to MediaBrowseService of MediaPlayer */
+ MediaConnectionCallback callback = new MediaConnectionCallback(packageName);
+ MediaBrowser tempBrowser = new MediaBrowser(
+ mContext, new ComponentName(packageName, mClassName), callback, null);
+ callback.setBrowser(tempBrowser);
+ tempBrowser.connect();
+ } else if (mFolderItems != null) {
+ mPackageName = packageName;
+ mClassName = cls;
+ int rsp_status = AvrcpConstants_ext.RSP_NO_ERROR;
+ int folder_depth = (mPathStack.size() > 0) ? (mPathStack.size() - 1) : 0;
+ if (!mPathStack.empty()) {
+ Log.d(TAG, "~~current Path = " + mPathStack.peek());
+ if (mPathStack.size() > 1) {
+ String top = mPathStack.peek();
+ mPathStack.pop();
+ String path = mPathStack.peek();
+ mPathStack.push(top);
+ String [] ExternalPath = path.split("/");
+ if (!mPlayerRoot && ExternalPath != null && ExternalPath.length > 1) {
+ Log.d(TAG,"external path length: " + ExternalPath.length);
+ String [] folderPath = new String[ExternalPath.length - 1];
+ for (int i = 0; i < (ExternalPath.length - 1); i++) {
+ folderPath[i] = ExternalPath[i + 1];
+ Log.d(TAG,"folderPath[" + i + "] = " + folderPath[i]);
+ }
+ mMediaInterface.setBrowsedPlayerRsp(mBDAddr, rsp_status,
+ (byte)folder_depth, mFolderItems.size(), folderPath);
+ } else if (!mPlayerRoot && mLocalPathCache.size() > 1 && ExternalPath.length == 1) {
+ String [] folderPath = new String[mLocalPathCache.size() - 1];
+ folderPath = mLocalPathCache.toArray(folderPath);
+ for (int i = 0; i < mLocalPathCache.size() - 1; i++) {
+ Log.d(TAG,"folderPath[" + i + "] = " + folderPath[i]);
+ }
+ mMediaInterface.setBrowsedPlayerRsp(mBDAddr, rsp_status,
+ (byte)folder_depth, mFolderItems.size(), folderPath);
+ } else if (mPlayerRoot && mLocalPathCache.size() > 1) {
+ String [] folderPath = new String[mLocalPathCache.size() - 1];
+ folderPath = mLocalPathCache.toArray(folderPath);
+ for (int i = 0; i < mLocalPathCache.size() - 1; i++) {
+ Log.d(TAG,"folderPath[" + i + "] = " + folderPath[i]);
+ }
+ mMediaInterface.setBrowsedPlayerRsp(mBDAddr, rsp_status,
+ (byte)folder_depth, mFolderItems.size(), folderPath);
+ } else {
+ Log.e(TAG, "sending internal error !!!");
+ rsp_status = AvrcpConstants_ext.RSP_INTERNAL_ERR;
+ mMediaInterface.setBrowsedPlayerRsp(mBDAddr, rsp_status, (byte)0x00, 0, null);
+ }
+ } else if (mPathStack.size() == 1) {
+ Log.d(TAG, "On root send SetBrowse response with root properties");
+ mMediaInterface.setBrowsedPlayerRsp(mBDAddr, rsp_status, (byte)folder_depth,
+ mFolderItems.size(), ROOT_FOLDER);
+ }
+ } else {
+ Log.e(TAG, "Path Stack empty sending internal error !!!");
+ rsp_status = AvrcpConstants_ext.RSP_INTERNAL_ERR;
+ mMediaInterface.setBrowsedPlayerRsp(mBDAddr, rsp_status, (byte)0x00, 0, null);
+ }
+ Log.d(TAG, "send setbrowse rsp status=" + rsp_status + " folder_depth=" + folder_depth);
+ }
+ }
+
+ public void TryReconnectBrowse(String packageName, String cls) {
+ Log.w(TAG, "Try reconnection with Browser service for package = " + packageName);
+ mConnectingPackageName = packageName;
+ mPackageName = packageName;
+ mClassName = cls;
+
+ /* cleanup variables from previous browsed calls */
+ mFolderItems = null;
+ mMediaId = null;
+ mRootFolderUid = null;
+ mPlayerRoot = false;
+
+ if (mPathStack != null)
+ mPathStack = null;
+ mPathStack = new Stack<String>();
+
+ if (mLocalPathCache != null)
+ mLocalPathCache = null;
+ mLocalPathCache = new Stack<String>();
+
+ MediaConnectionCallback callback = new MediaConnectionCallback(packageName);
+ MediaBrowser tempBrowser = new MediaBrowser(
+ mContext, new ComponentName(packageName, cls), callback, null);
+ callback.setBrowser(tempBrowser);
+ tempBrowser.connect();
+ Log.w(TAG, "Reconnected with Browser service");
+ }
+
+ public void setCurrentPackage(String packageName, String cls) {
+ Log.w(TAG, "Set current Browse based on Addr Player as " + packageName);
+ mCurrentBrowsePackage = packageName;
+ mCurrentBrowseClass = cls;
+ }
+
+ /* called when connection to media player is closed */
+ public void cleanup() {
+ if (DEBUG) {
+ Log.d(TAG, "cleanup");
+ }
+
+ if (mConnState != DISCONNECTED) {
+ if (mMediaBrowser != null) mMediaBrowser.disconnect();
+ }
+
+ mMediaHmap = null;
+ mFolderHmap = null;
+ mMediaController = null;
+ mMediaBrowser = null;
+ mPathStack = null;
+ mLocalPathCache = null;
+ mPlayerRoot = false;
+ }
+
+ public boolean isPlayerConnected() {
+ if (mMediaBrowser == null) {
+ if (DEBUG) {
+ Log.d(TAG, "isPlayerConnected: mMediaBrowser = null!");
+ }
+ return false;
+ }
+
+ return mMediaBrowser.isConnected();
+ }
+
+ /* returns number of items in new path as reponse */
+ public void changePath(byte[] folderUid, byte direction) {
+ if (DEBUG) {
+ Log.d(TAG, "changePath.direction = " + direction);
+ }
+ String newPath = "";
+
+ if (!isPlayerConnected()) {
+ Log.w(TAG, "changePath: disconnected from player service, sending internal error");
+ mMediaInterface.changePathRsp(mBDAddr, AvrcpConstants_ext.RSP_INTERNAL_ERR, 0);
+ return;
+ }
+
+ if (mMediaBrowser == null) {
+ Log.e(TAG, "Media browser is null, sending internal error");
+ mMediaInterface.changePathRsp(mBDAddr, AvrcpConstants_ext.RSP_INTERNAL_ERR, 0);
+ return;
+ }
+
+ /* check direction and change the path */
+ if (direction == AvrcpConstants_ext.DIR_DOWN) { /* move down */
+ if ((newPath = byteToStringFolder(folderUid)) == null) {
+ Log.e(TAG, "Could not get media item from folder Uid, sending err response");
+ mMediaInterface.changePathRsp(mBDAddr, AvrcpConstants_ext.RSP_INV_ITEM, 0);
+ } else if (!isBrowsableFolderDn(newPath)) {
+ /* new path is not browsable */
+ Log.e(TAG, "ItemUid received from changePath cmd is not browsable");
+ mMediaInterface.changePathRsp(mBDAddr, AvrcpConstants_ext.RSP_INV_DIRECTORY, 0);
+ } else if (mPathStack.peek().equals(newPath)) {
+ /* new_folder is same as current folder */
+ Log.e(TAG, "new_folder is same as current folder, Invalid direction!");
+ mMediaInterface.changePathRsp(mBDAddr, AvrcpConstants_ext.RSP_INV_DIRN, 0);
+ } else {
+ mMediaBrowser.subscribe(newPath, mFolderItemsCb);
+ String [] ExternalPath = newPath.split("/");
+ if (ExternalPath != null) {
+ Log.d(TAG,"external path length: " + ExternalPath.length);
+ if (ExternalPath.length == 1) {
+ //when external path length is 1 then extract the folder name from index 4
+ String folder_name = parseQueueId(newPath, BROWSED_FOLDER_ID_INDEX);
+ Log.d(TAG,"folder path: " + folder_name);
+ if (folder_name != null) {
+ mLocalPathCache.push(folder_name);
+ } else {
+ mLocalPathCache.push(newPath);
+ }
+ } else {
+ String folderPath = ExternalPath[ExternalPath.length - 1];
+ if (folderPath != null) {
+ Log.d(TAG,"folder path: " + folderPath);
+ mLocalPathCache.push(folderPath);
+ }
+ }
+ }
+ /* assume that call is success and update stack with new folder path */
+ mPathStack.push(newPath);
+ }
+ } else if (direction == AvrcpConstants_ext.DIR_UP) { /* move up */
+ if (!isBrowsableFolderUp()) {
+ /* Already on the root, cannot allow up: PTS: test case TC_TG_MCN_CB_BI_02_C
+ * This is required, otherwise some CT will keep on sending change path up
+ * until they receive error */
+ Log.w(TAG, "Cannot go up from now, already in the root, Invalid direction!");
+ mMediaInterface.changePathRsp(mBDAddr, AvrcpConstants_ext.RSP_INV_DIRN, 0);
+ } else {
+ /* move folder up */
+ mPathStack.pop();
+ mLocalPathCache.pop();
+ newPath = mPathStack.peek();
+ mMediaBrowser.subscribe(newPath, mFolderItemsCb);
+ }
+ } else { /* invalid direction */
+ Log.w(TAG, "changePath : Invalid direction " + direction);
+ mMediaInterface.changePathRsp(mBDAddr, AvrcpConstants_ext.RSP_INV_DIRN, 0);
+ }
+ }
+
+ public void getItemAttr(AvrcpCmd_ext.ItemAttrCmd itemAttr) {
+ String mediaID;
+ if (DEBUG) {
+ Log.d(TAG, "getItemAttr");
+ }
+
+ /* check if uid is valid by doing a lookup in hashmap */
+ mediaID = byteToStringMedia(itemAttr.mUid);
+ if (mediaID == null) {
+ Log.e(TAG, "uid is invalid");
+ mMediaInterface.getItemAttrRsp(mBDAddr, AvrcpConstants_ext.RSP_INV_ITEM, null);
+ return;
+ }
+
+ /* check scope */
+ if (itemAttr.mScope != AvrcpConstants_ext.BTRC_SCOPE_FILE_SYSTEM) {
+ Log.e(TAG, "invalid scope");
+ mMediaInterface.getItemAttrRsp(mBDAddr, AvrcpConstants_ext.RSP_INV_SCOPE, null);
+ return;
+ }
+
+ if (mMediaBrowser == null) {
+ Log.e(TAG, "mMediaBrowser is null");
+ mMediaInterface.getItemAttrRsp(mBDAddr, AvrcpConstants_ext.RSP_INTERNAL_ERR, null);
+ return;
+ }
+
+ /* Subscribe to the parent to list items and retrieve the right one */
+ mMediaBrowser.subscribe(mPathStack.peek(), new ItemAttribSubscriber(itemAttr, mediaID));
+ }
+
+ public void getTotalNumOfItems(byte scope) {
+ if (DEBUG) {
+ Log.d(TAG, "getTotalNumOfItems scope = " + scope);
+ }
+ if (scope != AvrcpConstants_ext.BTRC_SCOPE_FILE_SYSTEM) {
+ Log.e(TAG, "getTotalNumOfItems error" + scope);
+ mMediaInterface.getTotalNumOfItemsRsp(mBDAddr, AvrcpConstants_ext.RSP_INV_SCOPE, 0, 0);
+ return;
+ }
+
+ if (mFolderItems == null) {
+ Log.e(TAG, "mFolderItems is null, sending internal error");
+ /* folderitems were not fetched during change path */
+ mMediaInterface.getTotalNumOfItemsRsp(mBDAddr, AvrcpConstants_ext.RSP_INTERNAL_ERR, 0, 0);
+ return;
+ }
+
+ /* find num items using size of already cached folder items */
+ mMediaInterface.getTotalNumOfItemsRsp(mBDAddr, AvrcpConstants_ext.RSP_NO_ERROR, 0,
+ mFolderItems.size());
+ }
+
+ public void getFolderItemsVFS(AvrcpCmd_ext.FolderItemsCmd reqObj) {
+ if (DEBUG) {
+ Log.d(TAG, "getFolderItemsVFS");
+ }
+ mFolderItemsReqObj = reqObj;
+
+ if ((mCurrentBrowsePackage != null) && (!mCurrentBrowsePackage.equals(mPackageName))) {
+ Log.w(TAG, "Try reconnection with Browser service as addressed pkg is changed = "
+ + mCurrentBrowsePackage + "from " + mPackageName);
+ TryReconnectBrowse(mCurrentBrowsePackage, mCurrentBrowseClass);
+ }
+
+ if (mFolderItems == null) {
+ /* Failed to fetch folder items from media player. Send error to remote device */
+ Log.e(TAG, "Failed to fetch folder items during getFolderItemsVFS");
+ mMediaInterface.folderItemsRsp(mBDAddr, AvrcpConstants_ext.RSP_INTERNAL_ERR, null);
+ return;
+ }
+
+ /* Filter attributes based on the request and send response to remote device */
+ getFolderItemsFilterAttr(mBDAddr, reqObj, mFolderItems,
+ AvrcpConstants_ext.BTRC_SCOPE_FILE_SYSTEM, mFolderItemsReqObj.mStartItem,
+ mFolderItemsReqObj.mEndItem);
+ }
+
+ /* Instructs media player to play particular media item */
+ public void playItem(byte[] uid, byte scope) {
+ String folderUid;
+
+ if (isPlayerConnected()) {
+ /* check if uid is valid */
+ if ((folderUid = byteToStringMedia(uid)) == null) {
+ Log.e(TAG, "uid is invalid!");
+ mMediaInterface.playItemRsp(mBDAddr, AvrcpConstants_ext.RSP_INV_ITEM);
+ return;
+ }
+
+ if (mMediaController != null) {
+ MediaController.TransportControls mediaControllerCntrl =
+ mMediaController.getTransportControls();
+ if (DEBUG) {
+ Log.d(TAG, "Sending playID: " + folderUid);
+ }
+
+ if (scope == AvrcpConstants_ext.BTRC_SCOPE_FILE_SYSTEM) {
+ mediaControllerCntrl.playFromMediaId(folderUid, null);
+ mMediaInterface.playItemRsp(mBDAddr, AvrcpConstants_ext.RSP_NO_ERROR);
+ } else {
+ Log.e(TAG, "playItem received for invalid scope!");
+ mMediaInterface.playItemRsp(mBDAddr, AvrcpConstants_ext.RSP_INV_SCOPE);
+ }
+ } else {
+ Log.e(TAG, "mediaController is null");
+ mMediaInterface.playItemRsp(mBDAddr, AvrcpConstants_ext.RSP_INTERNAL_ERR);
+ }
+ } else {
+ Log.e(TAG, "playItem: Not connected to media player");
+ mMediaInterface.playItemRsp(mBDAddr, AvrcpConstants_ext.RSP_INTERNAL_ERR);
+ }
+ }
+
+ /*
+ * helper method to check if startItem and endItem index is with range of
+ * MediaItem list. (Resultset containing all items in current path)
+ */
+ private List<MediaBrowser.MediaItem> checkIndexOutofBounds(byte[] bdaddr,
+ List<MediaBrowser.MediaItem> children, long startItem, long endItem) {
+ if (endItem >= children.size()) {
+ endItem = children.size() - 1;
+ }
+ if (startItem >= Integer.MAX_VALUE) {
+ startItem = Integer.MAX_VALUE;
+ }
+ try {
+ List<MediaBrowser.MediaItem> childrenSubList =
+ children.subList((int) startItem, (int) endItem + 1);
+ if (childrenSubList.isEmpty()) {
+ Log.i(TAG, "childrenSubList is empty.");
+ throw new IndexOutOfBoundsException();
+ }
+ return childrenSubList;
+ } catch (IndexOutOfBoundsException ex) {
+ Log.w(TAG, "Index out of bounds start item =" + startItem + " end item = " + Math.min(
+ children.size(), endItem + 1));
+ return null;
+ } catch (IllegalArgumentException ex) {
+ Log.i(TAG, "Index out of bounds start item =" + startItem + " > size");
+ return null;
+ }
+ }
+
+
+ /*
+ * helper method to filter required attibutes before sending GetFolderItems response
+ */
+ public void getFolderItemsFilterAttr(byte[] bdaddr, AvrcpCmd_ext.FolderItemsCmd mFolderItemsReqObj,
+ List<MediaBrowser.MediaItem> children, byte scope, long startItem, long endItem) {
+ if (DEBUG) {
+ Log.d(TAG,
+ "getFolderItemsFilterAttr: startItem =" + startItem + ", endItem = " + endItem);
+ }
+
+ List<MediaBrowser.MediaItem> resultItems = new ArrayList<MediaBrowser.MediaItem>();
+
+ if (children == null) {
+ Log.e(TAG, "Error: children are null in getFolderItemsFilterAttr");
+ mMediaInterface.folderItemsRsp(bdaddr, AvrcpConstants_ext.RSP_INV_RANGE, null);
+ return;
+ }
+
+ /* check for index out of bound errors */
+ resultItems = checkIndexOutofBounds(bdaddr, children, startItem, endItem);
+ if (resultItems == null) {
+ Log.w(TAG, "resultItems is null.");
+ mMediaInterface.folderItemsRsp(bdaddr, AvrcpConstants_ext.RSP_INV_RANGE, null);
+ return;
+ }
+ FolderItemsData_ext folderDataNative = new FolderItemsData_ext(resultItems.size());
+
+ /* variables to temperorily add attrs */
+ ArrayList<String> attrArray = new ArrayList<String>();
+ ArrayList<Integer> attrId = new ArrayList<Integer>();
+
+ for (int itemIndex = 0; itemIndex < resultItems.size(); itemIndex++) {
+ /* item type. Needs to be set by media player */
+ MediaBrowser.MediaItem item = resultItems.get(itemIndex);
+ int flags = item.getFlags();
+ if ((flags & MediaBrowser.MediaItem.FLAG_BROWSABLE) != 0) {
+ folderDataNative.mItemTypes[itemIndex] = AvrcpConstants_ext.BTRC_ITEM_FOLDER;
+ } else {
+ folderDataNative.mItemTypes[itemIndex] = AvrcpConstants_ext.BTRC_ITEM_MEDIA;
+ }
+
+ /* set playable */
+ if ((flags & MediaBrowser.MediaItem.FLAG_PLAYABLE) != 0) {
+ folderDataNative.mPlayable[itemIndex] = AvrcpConstants_ext.ITEM_PLAYABLE;
+ } else {
+ folderDataNative.mPlayable[itemIndex] = AvrcpConstants_ext.ITEM_NOT_PLAYABLE;
+ }
+ /* set uid for current item */
+ byte[] uid;
+ if (folderDataNative.mItemTypes[itemIndex] == AvrcpConstants_ext.BTRC_ITEM_MEDIA)
+ uid = stringToByteMedia(item.getDescription().getMediaId(), BROWSED_ITEM_ID_INDEX);
+ else
+ uid = stringToByteFolder(item.getDescription().getMediaId());
+
+ for (int idx = 0; idx < AvrcpConstants_ext.UID_SIZE; idx++) {
+ folderDataNative.mItemUid[itemIndex * AvrcpConstants_ext.UID_SIZE + idx] = uid[idx];
+ }
+
+ /* Set display name for current item */
+ folderDataNative.mDisplayNames[itemIndex] =
+ getAttrValue(bdaddr, AvrcpConstants_ext.ATTRID_TITLE, item);
+
+ int maxAttributesRequested = 0;
+ boolean isAllAttribRequested = false;
+ /* check if remote requested for attributes */
+ if (mFolderItemsReqObj.mNumAttr != AvrcpConstants_ext.NUM_ATTR_NONE) {
+ int attrCnt = 0;
+
+ /* add requested attr ids to a temp array */
+ if (mFolderItemsReqObj.mNumAttr == AvrcpConstants_ext.NUM_ATTR_ALL) {
+ isAllAttribRequested = true;
+ maxAttributesRequested = AvrcpConstants_ext.MAX_NUM_ATTR;
+ } else {
+ /* get only the requested attribute ids from the request */
+ maxAttributesRequested = mFolderItemsReqObj.mNumAttr;
+ }
+
+ /* lookup and copy values of attributes for ids requested above */
+ for (int idx = 0; idx < maxAttributesRequested; idx++) {
+ /* check if media player provided requested attributes */
+ String value = null;
+
+ int attribId =
+ isAllAttribRequested ? (idx + 1) : mFolderItemsReqObj.mAttrIDs[idx];
+ value = getAttrValue(bdaddr, attribId, resultItems.get(itemIndex));
+ if (value != null) {
+ attrArray.add(value);
+ attrId.add(attribId);
+ attrCnt++;
+ }
+ }
+ /* add num attr actually received from media player for a particular item */
+ folderDataNative.mAttributesNum[itemIndex] = attrCnt;
+ }
+ }
+
+ /* copy filtered attr ids and attr values to response parameters */
+ if (attrId.size() > 0) {
+ folderDataNative.mAttrIds = new int[attrId.size()];
+ for (int attrIndex = 0; attrIndex < attrId.size(); attrIndex++) {
+ folderDataNative.mAttrIds[attrIndex] = attrId.get(attrIndex);
+ }
+ folderDataNative.mAttrValues = attrArray.toArray(new String[attrArray.size()]);
+ }
+
+ /* create rsp object and send response to remote device */
+ FolderItemsRsp_ext rspObj =
+ new FolderItemsRsp_ext(AvrcpConstants_ext.RSP_NO_ERROR, Avrcp_ext.sUIDCounter, scope,
+ folderDataNative.mNumItems, folderDataNative.mFolderTypes,
+ folderDataNative.mPlayable, folderDataNative.mItemTypes,
+ folderDataNative.mItemUid, folderDataNative.mDisplayNames,
+ folderDataNative.mAttributesNum, folderDataNative.mAttrIds,
+ folderDataNative.mAttrValues);
+ mMediaInterface.folderItemsRsp(bdaddr, AvrcpConstants_ext.RSP_NO_ERROR, rspObj);
+ }
+
+ public String getAttrValue(byte []bdaddr, int attr, MediaBrowser.MediaItem item) {
+ String attrValue = null;
+ try {
+ MediaDescription desc = item.getDescription();
+ Bundle extras = desc.getExtras();
+ switch (attr) {
+ /* Title is mandatory attribute */
+ case AvrcpConstants_ext.ATTRID_TITLE:
+ attrValue = desc.getTitle().toString();
+ break;
+
+ case AvrcpConstants_ext.ATTRID_ARTIST:
+ attrValue = extras.getString(MediaMetadata.METADATA_KEY_ARTIST);
+ break;
+
+ case AvrcpConstants_ext.ATTRID_ALBUM:
+ attrValue = extras.getString(MediaMetadata.METADATA_KEY_ALBUM);
+ break;
+
+ case AvrcpConstants_ext.ATTRID_TRACK_NUM:
+ attrValue = extras.getString(MediaMetadata.METADATA_KEY_TRACK_NUMBER);
+ break;
+
+ case AvrcpConstants_ext.ATTRID_NUM_TRACKS:
+ attrValue = extras.getString(MediaMetadata.METADATA_KEY_NUM_TRACKS);
+ break;
+
+ case AvrcpConstants_ext.ATTRID_GENRE:
+ attrValue = extras.getString(MediaMetadata.METADATA_KEY_GENRE);
+ break;
+
+ case AvrcpConstants_ext.ATTRID_PLAY_TIME:
+ attrValue = extras.getString(MediaMetadata.METADATA_KEY_DURATION);
+ break;
+
+ case AvrcpConstants_ext.ATTRID_COVER_ART:
+ attrValue = Avrcp_ext.getImgHandleFromTitle(bdaddr,
+ desc.getTitle().toString());
+ break;
+
+ default:
+ Log.e(TAG, "getAttrValue: Unknown attribute ID requested: " + attr);
+ return null;
+ }
+ } catch (NullPointerException ex) {
+ Log.w(TAG, "getAttrValue: attr id not found in result");
+ /* checking if attribute is title, then it is mandatory and cannot send null */
+ if (attr == AvrcpConstants_ext.ATTRID_TITLE) {
+ attrValue = "<Unknown Title>";
+ } else {
+ return null;
+ }
+ }
+ if (DEBUG) {
+ Log.d(TAG, "getAttrValue: attrvalue = " + attrValue + " attr id: " + attr);
+ }
+ return attrValue;
+ }
+
+
+ public String getPackageName() {
+ return mPackageName;
+ }
+
+ /* Helper methods */
+
+ /* check if item is browsable Down*/
+ private boolean isBrowsableFolderDn(String uid) {
+ for (MediaBrowser.MediaItem item : mFolderItems) {
+ if (item.getMediaId().equals(uid) && (
+ (item.getFlags() & MediaBrowser.MediaItem.FLAG_BROWSABLE)
+ == MediaBrowser.MediaItem.FLAG_BROWSABLE)) {
+ return true;
+ }
+ }
+ return false;
+ }
+
+ /* check if browsable Up*/
+ private boolean isBrowsableFolderUp() {
+ if (mPathStack.peek().equals(mRootFolderUid)) {
+ /* Already on the root, cannot go up */
+ return false;
+ }
+ return true;
+ }
+
+ private String parseQueueId(String mediaId, int mId) {
+ if (isNumeric(mediaId)) {
+ Log.d(TAG, "Get queue id: " + mediaId);
+ return mediaId.trim();
+ } else {
+ String[] mediaIdItems = mediaId.split(",");
+ if (mediaIdItems != null && mediaIdItems.length > mId) {
+ Log.d(TAG, "Get queue id: " + mediaIdItems[mId]);
+ return mediaIdItems[mId].trim();
+ }
+ }
+
+ Log.d(TAG, "Unkown queue id");
+ return null;
+ }
+
+ /* convert uid to mediaId for Media item*/
+ private String byteToStringMedia(byte[] byteArray) {
+ int uid = new BigInteger(byteArray).intValue();
+ String mediaId = mMediaHmap.get(uid);
+ return mediaId;
+ }
+
+ /* convert uid to mediaId for Folder item*/
+ private String byteToStringFolder(byte[] byteArray) {
+ int uid = new BigInteger(byteArray).intValue();
+ String mediaId = mFolderHmap.get(uid);
+ return mediaId;
+ }
+
+ /* convert mediaId to uid for Media item*/
+ private byte[] stringToByteMedia(String mediaId, int id) {
+ /* check if this mediaId already exists in hashmap */
+ if (!mMediaHmap.containsValue(mediaId)) { /* add to hashmap */
+ int uid;
+ String queueId = parseQueueId(mediaId, id);
+ if (queueId == null) {
+ uid = mMediaHmap.size() + 1;
+ } else {
+ uid = Integer.valueOf(queueId).intValue();
+ }
+
+ mMediaHmap.put(uid, mediaId);
+ return intToByteArray(uid);
+ } else { /* search key for give mediaId */
+ for (int uid : mMediaHmap.keySet()) {
+ if (mMediaHmap.get(uid).equals(mediaId)) {
+ return intToByteArray(uid);
+ }
+ }
+ }
+ return null;
+ }
+
+ /* convert mediaId to uid for Folder item*/
+ private byte[] stringToByteFolder(String mediaId) {
+ /* check if this mediaId already exists in hashmap */
+ if (!mFolderHmap.containsValue(mediaId)) { /* add to hashmap */
+ // Offset by one as uid 0 is reserved
+ int uid = mFolderHmap.size() + 1;
+ mFolderHmap.put(uid, mediaId);
+ return intToByteArray(uid);
+ } else { /* search key for give mediaId */
+ for (int uid : mFolderHmap.keySet()) {
+ if (mFolderHmap.get(uid).equals(mediaId)) {
+ return intToByteArray(uid);
+ }
+ }
+ }
+ return null;
+ }
+
+ private void refreshFolderItems(List<MediaBrowser.MediaItem> folderItems) {
+ for (int itemIndex = 0; itemIndex < folderItems.size(); itemIndex++) {
+ MediaBrowser.MediaItem item = folderItems.get(itemIndex);
+ int flags = item.getFlags();
+ if ((flags & MediaBrowser.MediaItem.FLAG_BROWSABLE) != 0) {
+ Log.d(TAG, "Folder item, no need refresh hashmap from mediaId to uid");
+ } else {
+ Log.d(TAG, "Media item, refresh haspmap from mediaId to uid");
+ stringToByteMedia(item.getDescription().getMediaId(), BROWSED_ITEM_ID_INDEX);
+ }
+ }
+ }
+
+ /* converts queue item received from getQueue call, to MediaItem used by FilterAttr method */
+ private List<MediaBrowser.MediaItem> queueItem2MediaItem(
+ List<MediaSession.QueueItem> tempItems) {
+
+ List<MediaBrowser.MediaItem> tempMedia = new ArrayList<MediaBrowser.MediaItem>();
+ for (int itemCount = 0; itemCount < tempItems.size(); itemCount++) {
+ MediaDescription.Builder build = new MediaDescription.Builder();
+ build.setMediaId(Long.toString(tempItems.get(itemCount).getQueueId()));
+ build.setTitle(tempItems.get(itemCount).getDescription().getTitle());
+ build.setExtras(tempItems.get(itemCount).getDescription().getExtras());
+ MediaDescription des = build.build();
+ MediaItem item = new MediaItem((des), MediaItem.FLAG_PLAYABLE);
+ tempMedia.add(item);
+ }
+ return tempMedia;
+ }
+
+ /* convert integer to byte array of size 8 bytes */
+ public byte[] intToByteArray(int value) {
+ int index = 0;
+ byte[] encodedValue = new byte[AvrcpConstants_ext.UID_SIZE];
+
+ encodedValue[index++] = (byte) 0x00;
+ encodedValue[index++] = (byte) 0x00;
+ encodedValue[index++] = (byte) 0x00;
+ encodedValue[index++] = (byte) 0x00;
+ encodedValue[index++] = (byte) (value >> 24);
+ encodedValue[index++] = (byte) (value >> 16);
+ encodedValue[index++] = (byte) (value >> 8);
+ encodedValue[index++] = (byte) value;
+
+ return encodedValue;
+ }
+
+ public boolean isNumeric(String mediaId) {
+ String trimStr = mediaId.trim();
+ int length = trimStr.length();
+
+ for(int i = 0; i < length; i++) {
+ char c = trimStr.charAt(i);
+ if (!((c >= '0' && c <= '9'))) {
+ Log.v(TAG, "Non-Numeric media Id");
+ return false;
+ }
+ }
+ Log.v(TAG, "Numeric media Id");
+ return true;
+ }
+}
diff --git a/packages_apps_bluetooth_ext/src/ba/BATService.java b/packages_apps_bluetooth_ext/src/ba/BATService.java
index 1cd459f..9d20265 100644
--- a/packages_apps_bluetooth_ext/src/ba/BATService.java
+++ b/packages_apps_bluetooth_ext/src/ba/BATService.java
@@ -88,7 +88,7 @@
public static final int MESSAGE_BAT_STREAMING_ID_EVT = 104;
private static final String BLUETOOTH_PERM = android.Manifest.permission.BLUETOOTH;
- public static final String mBAAddress = "FA:CE:FA:CE:FA:CE";
+ public static final String mBAAddress = "CE:FA:CE:FA:CE:FA";
public static BluetoothDevice mBADevice;
// we will listen for vol change intent from audio manager.
// this intent is called in all following 3 cases
@@ -452,13 +452,8 @@
// check for transition from PENDING to PAUSED
if((mPrevStackBATState == BA_STACK_STATE_PENDING) &&
(mCurrStackBATState == BA_STACK_STATE_PAUSED)) {
- if (a2dpActiveDevice != null) {
- Log.d(TAG," updating AudioManager: Disconnect for A2dp ");
- mAudioManager.setBluetoothA2dpDeviceConnectionStateSuppressNoisyIntent(
- a2dpActiveDevice, BluetoothProfile.STATE_DISCONNECTED, BluetoothProfile.A2DP,
- true, -1);
- }
- mAudioManager.setBluetoothA2dpDeviceConnectionStateSuppressNoisyIntent(
+ Log.d(TAG," updating AudioManager: Connected for BA ");
+ mAudioManager.handleBluetoothA2dpActiveDeviceChange(
mBADevice, BluetoothProfile.STATE_CONNECTED,BluetoothProfile.A2DP, true, -1);
//BA audio works on the principal of absVol
//Currently mm-audio tracks value of last updated absVol support,
@@ -475,18 +470,14 @@
// inform BA device as disconnected, don't send noisy intent
// as a2dp has to be updated as well. Switching should happen to
// A2DP in this case.
- Log.d(TAG," updating AudioManager: DisConnected for BA ");
- mAudioManager.setBluetoothA2dpDeviceConnectionStateSuppressNoisyIntent(
- mBADevice, BluetoothProfile.STATE_DISCONNECTED,BluetoothProfile.A2DP,
- true, -1);
Log.d(TAG," updating AudioManager: Connected for A2DP ");
- mAudioManager.setBluetoothA2dpDeviceConnectionStateSuppressNoisyIntent(
+ mAudioManager.handleBluetoothA2dpActiveDeviceChange(
a2dpActiveDevice, BluetoothProfile.STATE_CONNECTED,BluetoothProfile.A2DP,
true, -1);
} else {// a2dp active device is null.
// inform BA device as disconnected. we have to send noisy intent
// because BA seems to be last device.
- mAudioManager.setBluetoothA2dpDeviceConnectionStateSuppressNoisyIntent(
+ mAudioManager.handleBluetoothA2dpActiveDeviceChange(
mBADevice, BluetoothProfile.STATE_DISCONNECTED,BluetoothProfile.A2DP,
false, -1);
}
diff --git a/packages_apps_bluetooth_ext/src/btservice/Vendor.java b/packages_apps_bluetooth_ext/src/btservice/Vendor.java
index 54f8979..caa6647 100644
--- a/packages_apps_bluetooth_ext/src/btservice/Vendor.java
+++ b/packages_apps_bluetooth_ext/src/btservice/Vendor.java
@@ -234,7 +234,7 @@
}
public String getA2apOffloadCapability() {
- return socName;
+ return a2dpOffloadCap;
}
public boolean isSplitA2dpEnabled() {
diff --git a/packages_apps_bluetooth_ext/src/hfp/vendorhfservice.java b/packages_apps_bluetooth_ext/src/hfp/vendorhfservice.java
new file mode 100644
index 0000000..e34999a
--- /dev/null
+++ b/packages_apps_bluetooth_ext/src/hfp/vendorhfservice.java
@@ -0,0 +1,89 @@
+/*
+ * Copyright (c) 2019, The Linux Foundation. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above
+ * copyright notice, this list of conditions and the following
+ * disclaimer in the documentation and/or other materials provided
+ * with the distribution.
+ * * Neither the name of The Linux Foundation nor the names of its
+ * contributors may be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED "AS IS" AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS
+ * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
+ * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
+ * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN
+ * IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package com.android.bluetooth.hfp;
+
+import android.util.Log;
+import android.bluetooth.BluetoothAdapter;
+import android.bluetooth.BluetoothDevice;
+import com.android.bluetooth.Utils;
+import android.content.Intent;
+import android.content.Context;
+
+final class vendorhfservice {
+ private static final String TAG = "BluetoothVendorHfService";
+ private HeadsetService mService;
+ private final BluetoothAdapter mAdapter = BluetoothAdapter.getDefaultAdapter();
+
+ static {
+ classInitNative();
+ }
+
+ public vendorhfservice(HeadsetService service) {
+ mService = service;
+ }
+
+ public void init(){
+ initNative();
+ }
+
+ public void cleanup() {
+ cleanupNative();
+ }
+
+ public int enableSwb(boolean enable) {
+ int ret = enableSwbNative(enable);
+ return ret;
+ }
+
+ private BluetoothDevice getDevice(byte[] address) {
+ BluetoothDevice local = mAdapter.getRemoteDevice(Utils.getAddressStringFromByte(address));
+ return local;
+ }
+
+ private void onSWB(int codec, byte[] address) {
+ /*HeadsetStackEvent event =
+ new HeadsetStackEvent(HeadsetStackEvent.EVENT_TYPE_SWB, codec, getDevice(address));
+ sendMessageToService(event);*/
+ }
+
+ private void sendMessageToService(HeadsetStackEvent event) {
+ HeadsetService service = HeadsetService.getHeadsetService();
+ if (service != null) {
+ service.messageFromNative(event);
+ } else {
+ Log.d(TAG,"FATAL: Stack sent event while service is not available: " + event);
+ }
+ }
+
+ private native void initNative();
+ private native static void classInitNative();
+ private native void cleanupNative();
+ private native int enableSwbNative(boolean enable);
+}
diff --git a/packages_apps_bluetooth_ext/src/map/src/BluetoothMapContentEmail.java b/packages_apps_bluetooth_ext/src/map/src/BluetoothMapContentEmail.java
index 40da4eb..655fbf1 100644
--- a/packages_apps_bluetooth_ext/src/map/src/BluetoothMapContentEmail.java
+++ b/packages_apps_bluetooth_ext/src/map/src/BluetoothMapContentEmail.java
@@ -120,11 +120,9 @@
private final BluetoothMapAccountItem mAccount;
/* The MasInstance reference is used to update persistent (over a connection) version counters*/
private final BluetoothMapMasInstance mMasInstance;
- private String mMessageVersion = BluetoothMapUtils.MAP_V10_STR;
private final boolean EMAIL_ATTACHMENT_IMPLEMENTED = false;
private int mRemoteFeatureMask = BluetoothMapUtils.MAP_FEATURE_DEFAULT_BITMASK;
- private int mMsgListingVersion = BluetoothMapUtils.MAP_MESSAGE_LISTING_FORMAT_V10;
private class FilterInfo {
public static final int TYPE_SMS = 0;
diff --git a/packages_apps_bluetooth_ext/src/map/src/BluetoothMapContentObserverEmail.java b/packages_apps_bluetooth_ext/src/map/src/BluetoothMapContentObserverEmail.java
index 005dc42..a99f7a0 100644
--- a/packages_apps_bluetooth_ext/src/map/src/BluetoothMapContentObserverEmail.java
+++ b/packages_apps_bluetooth_ext/src/map/src/BluetoothMapContentObserverEmail.java
@@ -111,7 +111,7 @@
private static final long EVENT_FILTER_CONVERSATION_CHANGED = 1L<<10;
private static final long EVENT_FILTER_PARTICIPANT_PRESENCE_CHANGED = 1L<<11;
private static final long EVENT_FILTER_PARTICIPANT_CHATSTATE_CHANGED= 1L<<12;
- private static final long EVENT_FILTER_MESSAGE_REMOVED = 1L<<13;
+ private static final long EVENT_FILTER_MESSAGE_REMOVED = 1L<<14;
// TODO: If we are requesting a large message from the network, on a slow connection
// 20 seconds might not be enough... But then again 20 seconds is long for other
@@ -141,14 +141,6 @@
private boolean mTransmitEvents = true;
- /* To make the filter update atomic, we declare it volatile.
- * To avoid a penalty when using it, copy the value to a local
- * non-volatile variable when used more than once.
- * Actually we only ever use the lower 4 bytes of this variable,
- * hence we could manage without the volatile keyword, but as
- * we tend to copy ways of doing things, we better do it right:-) */
- private volatile long mEventFilter = 0xFFFFFFFFL;
-
public static final int DELETED_THREAD_ID = -1;
// X-Mms-Message-Type field types. These are from PduHeaders.java
@@ -254,8 +246,8 @@
}
public int getObserverRemoteFeatureMask() {
- if (V) Log.v(TAG, "getObserverRemoteFeatureMask Email: " + mMapEventReportVersion
- + " mMapSupportedFeatures Email: " + mMapSupportedFeatures);
+ if (V) Log.v(TAG, "getObserverRemoteFeatureMask " + mMapEventReportVersion
+ + " mMapSupportedFeatures :" + mMapSupportedFeatures);
return mMapSupportedFeatures;
}
@@ -398,7 +390,7 @@
/* Enable use of the cache for checking the filter */
long eventFilter = mEventFilter;
-
+ if (V) Log.v(TAG," eventFilter " + eventFilter);
/* This should have been a switch on the string, but it is not allowed in Java 1.6 */
/* WARNING: Here we do pointer compare for the string to speed up things, that is.
* HENCE: always use the EVENT_TYPE_"defines" */
@@ -550,7 +542,7 @@
msgList.put(id, msg);
Event evt;
/* Incoming message from the network */
- if (mMapEventReportVersion == BluetoothMapUtils.MAP_EVENT_REPORT_V11) {
+ if (mMapEventReportVersion >= BluetoothMapUtils.MAP_EVENT_REPORT_V11) {
String date = BluetoothMapUtils.getDateTimeString(c.getLong(c
.getColumnIndex(BluetoothMapEmailContract
.ExtEmailMessageColumns.TIMESTAMP)));
@@ -691,7 +683,12 @@
if (!msg.transparent ) {
if(msg.localInitiatedShift == false ) {
// "old_folder" used only for MessageShift event
- Event evt = new Event(EVENT_TYPE_DELETE, msg.id, oldFolder,
+ String eventType = EVENT_TYPE_DELETE;
+ if (mMapEventReportVersion >= BluetoothMapUtils.MAP_EVENT_REPORT_V12) {
+ eventType = EVENT_TYPE_REMOVED;
+ if (V) Log.v(TAG," send EVENT_TYPE_REMOVED");
+ }
+ Event evt = new Event(eventType, msg.id, oldFolder,
null, mAccount.getType());
sendEvent(evt);
} else {
diff --git a/system_bt_ext/bta/Android.bp b/system_bt_ext/bta/Android.bp
index 5d79a17..b87a3ac 100644
--- a/system_bt_ext/bta/Android.bp
+++ b/system_bt_ext/bta/Android.bp
@@ -28,6 +28,7 @@
"ba/bta_ba.cc",
"tws_plus/ag/bta_ag_twsp_dev.cc",
"tws_plus/ag/bta_ag_twsp_sco.cc",
+ "swb/bta_ag_swb.cc"
],
shared_libs: [
"libcutils",
@@ -39,4 +40,7 @@
"libbt-utils",
],
cflags: ["-DBUILDCFG"],
+ sanitize: {
+ never: true,
+ },
}
diff --git a/system_bt_ext/bta/include/bta_ag_swb.h b/system_bt_ext/bta/include/bta_ag_swb.h
new file mode 100644
index 0000000..a65f3c3
--- /dev/null
+++ b/system_bt_ext/bta/include/bta_ag_swb.h
@@ -0,0 +1,89 @@
+/*
+ * Copyright (c) 2019, The Linux Foundation. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above
+ * copyright notice, this list of conditions and the following
+ * disclaimer in the documentation and/or other materials provided
+ * with the distribution.
+ * * Neither the name of The Linux Foundation nor the names of its
+ * contributors may be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED "AS IS" AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS
+ * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
+ * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
+ * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN
+ * IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef _BTA_AG_SWB_H_
+#define _BTA_AG_SWB_H_
+
+#include "bta_ag_int.h"
+#include "device/include/esco_parameters.h"
+
+#define BTA_AG_SCO_SWB_SETTINGS_Q0_MASK 4
+#define BTA_AG_SCO_SWB_SETTINGS_Q1_MASK 8
+#define BTA_AG_SCO_SWB_SETTINGS_Q2_MASK 16
+#define BTA_AG_SCO_SWB_SETTINGS_Q3_MASK 32
+
+/* Events originated from HF side */
+#define BTA_AG_AT_QAC_EVT 253
+#define BTA_AG_AT_QCS_EVT 254
+#define BTA_AG_SWB_EVT 100 /* SWB SCO codec info */
+#define BTA_AG_LOCAL_RES_QAC 0x108
+#define BTA_AG_LOCAL_RES_QCS 0x109
+
+#define LEGACY_CODECS 2
+#define SWB_ESCO_NUM_CODECS 1
+
+void bta_ag_swb_handle_vs_at_events(tBTA_AG_SCB* p_scb, uint16_t cmd, int16_t int_arg, tBTA_AG_VAL val);
+void bta_ag_send_qac(tBTA_AG_SCB* p_scb, tBTA_AG_DATA* p_data);
+void bta_ag_send_qcs(tBTA_AG_SCB* p_scb, tBTA_AG_DATA* p_data);
+tBTA_AG_PEER_CODEC bta_ag_parse_qac(tBTA_AG_SCB* p_scb, char* p_s);
+
+const enh_esco_params_t default_esco_swb_parameters[SWB_ESCO_NUM_CODECS] = {
+ {.transmit_bandwidth = TXRX_64KBITS_RATE,
+ .receive_bandwidth = TXRX_64KBITS_RATE,
+ .transmit_coding_format = {.coding_format = ESCO_CODING_FORMAT_VS,
+ .company_id = 0x000A,
+ .vendor_specific_codec_id = 0x0000},
+ .receive_coding_format = {.coding_format = ESCO_CODING_FORMAT_VS,
+ .company_id = 0x000A,
+ .vendor_specific_codec_id = 0x0000},
+ .transmit_codec_frame_size = 60,
+ .receive_codec_frame_size = 60,
+ .input_bandwidth = INPUT_OUTPUT_128K_RATE,
+ .output_bandwidth = INPUT_OUTPUT_128K_RATE,
+ .input_coding_format = {.coding_format = ESCO_CODING_FORMAT_LINEAR,
+ .company_id = 0x0000,
+ .vendor_specific_codec_id = 0x0000},
+ .output_coding_format = {.coding_format = ESCO_CODING_FORMAT_LINEAR,
+ .company_id = 0x0000,
+ .vendor_specific_codec_id = 0x0000},
+ .input_coded_data_size = 16,
+ .output_coded_data_size = 16,
+ .input_pcm_data_format = ESCO_PCM_DATA_FORMAT_2_COMP,
+ .output_pcm_data_format = ESCO_PCM_DATA_FORMAT_2_COMP,
+ .input_pcm_payload_msb_position = 0,
+ .output_pcm_payload_msb_position = 0,
+ .input_data_path = ESCO_DATA_PATH_PCM,
+ .output_data_path = ESCO_DATA_PATH_PCM,
+ .input_transport_unit_size = 0x00,
+ .output_transport_unit_size = 0x00,
+ .max_latency_ms = 14,
+ .packet_types = 0x0380,
+ .retransmission_effort = ESCO_RETRANSMISSION_QUALITY}};
+
+#endif//_BTA_AG_SWB_H_
diff --git a/system_bt_ext/bta/include/bta_ag_twsp_dev.h b/system_bt_ext/bta/include/bta_ag_twsp_dev.h
index 592038c..c1e2b23 100644
--- a/system_bt_ext/bta/include/bta_ag_twsp_dev.h
+++ b/system_bt_ext/bta/include/bta_ag_twsp_dev.h
@@ -64,10 +64,11 @@
#define TWSPLUS_QDSP_ECHO_CANCELLATION 1
enum {
- TWSPLUS_EB_STATE_OFF,
+ TWSPLUS_EB_STATE_UNKNOWN,
TWSPLUS_EB_STATE_INCASE,
TWSPLUS_EB_STATE_OUT_OF_EAR,
TWSPLUS_EB_STATE_INEAR,
+ TWSPLUS_EB_STATE_OFF
};
enum {
@@ -100,6 +101,7 @@
bool twsp_get_right_eb_addr(RawAddress& eb_addr);
bool twsp_get_left_eb_addr(RawAddress& eb_addr);
uint8_t get_twsp_role(tBTA_AG_SCB *p_scb);
+uint8_t get_twsp_state(tBTA_AG_SCB *p_scb);
tBTA_AG_SCB* twsp_get_best_mic_scb ();
int twsp_get_idx_by_scb(tBTA_AG_SCB* p_scb);
diff --git a/system_bt_ext/bta/swb/bta_ag_swb.cc b/system_bt_ext/bta/swb/bta_ag_swb.cc
new file mode 100644
index 0000000..496f110
--- /dev/null
+++ b/system_bt_ext/bta/swb/bta_ag_swb.cc
@@ -0,0 +1,200 @@
+/*
+ * Copyright (c) 2019, The Linux Foundation. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above
+ * copyright notice, this list of conditions and the following
+ * disclaimer in the documentation and/or other materials provided
+ * with the distribution.
+ * * Neither the name of The Linux Foundation nor the names of its
+ * contributors may be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED "AS IS" AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS
+ * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
+ * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
+ * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN
+ * IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include <unistd.h>
+#include "bta_ag_swb.h"
+#include "internal_include/bt_trace.h"
+#include "bta_ag_int.h"
+#include "utl.h"
+#include "device/include/interop.h"
+#include <hardware/vendor_hf.h>
+
+#if (SWB_ENABLED == TRUE)
+
+#define SWB_CODECS_SUPPORTED "0,4,6,7"
+#define SWB_CODECS_UNSUPPORTD "0xFFFF"
+
+void bta_ag_swb_handle_vs_at_events(tBTA_AG_SCB* p_scb, uint16_t cmd, int16_t int_arg, tBTA_AG_VAL val)
+{
+ APPL_TRACE_DEBUG("%s: p_scb : %x cmd : %d", __func__, p_scb, cmd);
+ switch(cmd) {
+ case BTA_AG_AT_QAC_EVT:
+ if (!get_swb_codec_status()) {
+ bta_ag_send_qac(p_scb, NULL);
+ break;
+ }
+ p_scb->codec_updated = true;
+ if (p_scb->peer_codecs & BTA_AG_SCO_SWB_SETTINGS_Q0_MASK) {
+ p_scb->sco_codec = BTA_AG_SCO_SWB_SETTINGS_Q0;
+ } else if (p_scb->peer_codecs & BTA_AG_CODEC_MSBC) {
+ p_scb->sco_codec = UUID_CODEC_MSBC;
+ }
+ bta_ag_send_qac(p_scb, NULL);
+ APPL_TRACE_DEBUG("Received AT+QAC, updating sco codec to SWB: %d", p_scb->sco_codec);
+ val.num = p_scb->peer_codecs;
+ break;
+ case BTA_AG_AT_QCS_EVT: {
+ tBTA_AG_PEER_CODEC codec_type, codec_sent;
+ alarm_cancel(p_scb->codec_negotiation_timer);
+
+ switch (int_arg) {
+ case BTA_AG_SCO_SWB_SETTINGS_Q0:
+ codec_type = BTA_AG_SCO_SWB_SETTINGS_Q0;
+ break;
+ case BTA_AG_SCO_SWB_SETTINGS_Q1:
+ codec_type = BTA_AG_SCO_SWB_SETTINGS_Q1;
+ break;
+ case BTA_AG_SCO_SWB_SETTINGS_Q2:
+ codec_type = BTA_AG_SCO_SWB_SETTINGS_Q2;
+ break;
+ case BTA_AG_SCO_SWB_SETTINGS_Q3:
+ codec_type = BTA_AG_SCO_SWB_SETTINGS_Q3;
+ break;
+ default:
+ APPL_TRACE_ERROR("Unknown codec_uuid %d", int_arg);
+ p_scb->is_swb_codec = false;
+ codec_type = BTA_AG_CODEC_MSBC;
+ p_scb->codec_fallback = true;
+ p_scb->sco_codec = BTA_AG_CODEC_MSBC;
+ break;
+ }
+
+ if (p_scb->codec_fallback)
+ codec_sent = BTA_AG_CODEC_MSBC;
+ else
+ codec_sent = p_scb->sco_codec;
+
+ if (codec_type == codec_sent)
+ bta_ag_sco_codec_nego(p_scb, true);
+ else
+ bta_ag_sco_codec_nego(p_scb, false);
+
+ /* send final codec info to callback */
+ val.num = codec_sent;
+ break;
+ }
+ }
+}
+
+/*******************************************************************************
+ *
+ * Function bta_ag_send_qcs
+ *
+ * Description Send +%QCS AT command to peer.
+ *
+ * Returns void
+ *
+ ******************************************************************************/
+void bta_ag_send_qcs(tBTA_AG_SCB* p_scb, tBTA_AG_DATA* p_data) {
+ uint16_t codec_uuid;
+ if (p_scb->codec_fallback) {
+ if (p_scb->peer_codecs & BTA_AG_CODEC_MSBC) {
+ codec_uuid = UUID_CODEC_MSBC;
+ } else {
+ codec_uuid = UUID_CODEC_CVSD;
+ }
+ } else {
+ codec_uuid = BTA_AG_SCO_SWB_SETTINGS_Q0;
+ }
+
+ /* send +BCS */
+ APPL_TRACE_DEBUG("send +QCS codec is %d", codec_uuid);
+ bta_ag_send_result(p_scb, BTA_AG_LOCAL_RES_QCS, NULL, codec_uuid);
+}
+
+/*******************************************************************************
+ *
+ * Function bta_ag_send_qac
+ *
+ * Description Send +%QAC AT command to peer.
+ *
+ * Returns void
+ *
+ ******************************************************************************/
+void bta_ag_send_qac(tBTA_AG_SCB* p_scb, tBTA_AG_DATA* p_data) {
+ /* send +BCS */
+ APPL_TRACE_DEBUG("send +QAC codecs suuported");
+ if (!get_swb_codec_status()) {
+ bta_ag_send_result(p_scb, BTA_AG_LOCAL_RES_QAC, SWB_CODECS_UNSUPPORTD, 0);
+ return;
+ } else{
+ bta_ag_send_result(p_scb, BTA_AG_LOCAL_RES_QAC, SWB_CODECS_SUPPORTED, 0);
+ }
+ if (p_scb->sco_codec == BTA_AG_SCO_SWB_SETTINGS_Q0) {
+ p_scb->is_swb_codec = true;
+ }
+}
+
+tBTA_AG_PEER_CODEC bta_ag_parse_qac(tBTA_AG_SCB* p_scb, char* p_s) {
+ tBTA_AG_PEER_CODEC retval = BTA_AG_CODEC_NONE;
+ uint16_t codec_modes;
+ bool cont = false; /* Continue processing */
+ char* p;
+
+ while (p_s) {
+ /* skip to comma delimiter */
+ for (p = p_s; *p != ',' && *p != 0; p++)
+ ;
+
+ /* get integre value */
+ if (*p != 0) {
+ *p = 0;
+ cont = true;
+ } else
+ cont = false;
+
+ codec_modes = utl_str2int(p_s);
+ switch (codec_modes) {
+ case BTA_AG_SCO_SWB_SETTINGS_Q0:
+ retval |= BTA_AG_SCO_SWB_SETTINGS_Q0_MASK;
+ break;
+ case BTA_AG_SCO_SWB_SETTINGS_Q1:
+ retval |= BTA_AG_SCO_SWB_SETTINGS_Q1_MASK;
+ break;
+ case BTA_AG_SCO_SWB_SETTINGS_Q2:
+ retval |= BTA_AG_SCO_SWB_SETTINGS_Q2_MASK;
+ break;
+ case BTA_AG_SCO_SWB_SETTINGS_Q3:
+ retval |= BTA_AG_SCO_SWB_SETTINGS_Q3_MASK;
+ break;
+ default:
+ APPL_TRACE_ERROR("Unknown Codec UUID(%d) received", codec_modes);
+ break;
+ }
+
+ if (cont)
+ p_s = p + 1;
+ else
+ break;
+ }
+
+ return (retval);
+}
+#endif //#if (SWB_ENABLED == TRUE)
+
diff --git a/system_bt_ext/bta/tws_plus/ag/bta_ag_twsp_dev.cc b/system_bt_ext/bta/tws_plus/ag/bta_ag_twsp_dev.cc
index 125e889..11642e3 100644
--- a/system_bt_ext/bta/tws_plus/ag/bta_ag_twsp_dev.cc
+++ b/system_bt_ext/bta/tws_plus/ag/bta_ag_twsp_dev.cc
@@ -63,6 +63,18 @@
return NULL;
}
+bool twsp_is_odd_eb_addr(tBTA_AG_SCB* p_scb) {
+ bool ret;
+ if ((p_scb->peer_addr.address[5] & 0x01) == 0) {
+ ret = false;
+ } else {
+ ret = true;
+ }
+ APPL_TRACE_DEBUG("%s: eb addr: %s, ret: %d", __func__,
+ p_scb->peer_addr.ToString().c_str(), ret);
+ return ret;
+}
+
void reset_twsp_device(int eb_idx) {
if (eb_idx < PRIMARY_EB_IDX || eb_idx > SECONDARY_EB_IDX) {
APPL_TRACE_WARNING("%s: Invalid eb_idx: %d\n", __func__, eb_idx);
@@ -84,7 +96,7 @@
twsp_devices[eb_idx].p_scb = NULL;
twsp_devices[eb_idx].battery_charge = TWSPLUS_MIN_BATTERY_CHARGE;
- twsp_devices[eb_idx].state = TWSPLUS_EB_STATE_OFF;
+ twsp_devices[eb_idx].state = TWSPLUS_EB_STATE_UNKNOWN;
twsp_devices[eb_idx].role = TWSPLUS_EB_ROLE_INVALID;
twsp_devices[eb_idx].mic_path_delay = TWSPLUS_INVALID_MICPATH_DELAY;
twsp_devices[eb_idx].mic_quality = TWSPLUS_MIN_MIC_QUALITY;
@@ -99,14 +111,18 @@
APPL_TRACE_WARNING("%s: idx: %d, p_scb: %x", __func__, i, p_scb);
twsp_devices[i].p_scb = p_scb;
twsp_devices[i].battery_charge = TWSPLUS_MIN_BATTERY_CHARGE;
- twsp_devices[i].state = TWSPLUS_EB_STATE_OFF;
+ twsp_devices[i].state = TWSPLUS_EB_STATE_UNKNOWN;
int other_idx = (i == PRIMARY_EB_IDX) ? SECONDARY_EB_IDX : PRIMARY_EB_IDX;
- if (twsp_devices[other_idx].p_scb != NULL &&
- twsp_devices[other_idx].role == TWSPLUS_EB_ROLE_LEFT) {
- twsp_devices[i].role = TWSPLUS_EB_ROLE_RIGHT;
+ if (twsp_devices[other_idx].p_scb != NULL) {
+ twsp_devices[i].role =
+ (twsp_devices[other_idx].role == TWSPLUS_EB_ROLE_LEFT) ?
+ TWSPLUS_EB_ROLE_RIGHT : TWSPLUS_EB_ROLE_LEFT;
+
} else {
- twsp_devices[i].role = TWSPLUS_EB_ROLE_LEFT;
+ twsp_devices[i].role =
+ (twsp_is_odd_eb_addr(p_scb) == true) ?
+ TWSPLUS_EB_ROLE_LEFT : TWSPLUS_EB_ROLE_RIGHT;
}
APPL_TRACE_WARNING("%s: idx: %d, role: %d", __func__, i, twsp_devices[i].role);
@@ -387,7 +403,7 @@
return false;
}
- if (state < TWSPLUS_EB_STATE_OFF || state > TWSPLUS_EB_STATE_INEAR) {
+ if (state < TWSPLUS_EB_STATE_UNKNOWN || state > TWSPLUS_EB_STATE_INEAR) {
APPL_TRACE_WARNING("%s: Invalid state: %d\n", __func__, state);
return false;
}
@@ -459,6 +475,20 @@
return role;
}
+uint8_t get_twsp_state(tBTA_AG_SCB *p_scb) {
+ APPL_TRACE_DEBUG("%s: p_scb : %d\n", __func__, p_scb);
+ uint8_t state = TWSPLUS_EB_STATE_UNKNOWN;
+ for (int i=0; i<=SECONDARY_EB_IDX; i++) {
+ if (p_scb == twsp_devices[i].p_scb)
+ {
+ state = twsp_devices[i].state;
+ break;
+ }
+ }
+ APPL_TRACE_DEBUG("%s: returns : %d", __func__, state);
+ return state;
+}
+
bool twsp_is_ring_sent(tBTA_AG_SCB *p_scb) {
int sel_idx = -1;
bool ret = false;
diff --git a/system_bt_ext/bta/tws_plus/ag/bta_ag_twsp_sco.cc b/system_bt_ext/bta/tws_plus/ag/bta_ag_twsp_sco.cc
index 5f6af9b..21c4023 100644
--- a/system_bt_ext/bta/tws_plus/ag/bta_ag_twsp_sco.cc
+++ b/system_bt_ext/bta/tws_plus/ag/bta_ag_twsp_sco.cc
@@ -315,7 +315,8 @@
bta_ag_create_sco(p_scb, false);
p_sco->state = BTA_AG_SCO_LISTEN_ST;
other_scb = get_other_twsp_scb((p_scb->peer_addr));
- if (other_scb && twsp_sco_active(other_scb) == false) {
+ if (other_scb && twsp_sco_active(other_scb) == false &&
+ get_twsp_state(other_scb) == TWSPLUS_EB_STATE_INEAR) {
//Atleast try bringing up the other EB eSCO
APPL_TRACE_WARNING("Calling SCO open for other EB");
dispatch_event_primary_peer_device(p_scb, BTA_AG_SCO_OPEN_E);
@@ -702,7 +703,8 @@
uint8_t *p_param = param;
std::string leb, reb;
- APPL_TRACE_DEBUG("%s: left_addr: %x, right_addr: %x, selected_mic: %d", __func__, left_eb_addr, rght_eb_addr, selected_mic);
+ APPL_TRACE_DEBUG("%s: left_addr: %s, right_addr: %s, selected_mic: %d", __func__,
+ left_eb_addr.ToString().c_str(), rght_eb_addr.ToString().c_str(), selected_mic);
*p_param++ = VS_QHCI_TWS_ESCO_SETUP_SUBOPCODE;
diff --git a/system_bt_ext/btconfigstore/Android.bp b/system_bt_ext/btconfigstore/Android.bp
index 2c82531..c1de2b1 100644
--- a/system_bt_ext/btconfigstore/Android.bp
+++ b/system_bt_ext/btconfigstore/Android.bp
@@ -33,4 +33,7 @@
required: [
"bt_configstore.conf",
],
+ sanitize: {
+ never: true,
+ },
}
diff --git a/system_bt_ext/btconfigstore/bt_configstore.cc b/system_bt_ext/btconfigstore/bt_configstore.cc
index d76d05e..4f91bbf 100644
--- a/system_bt_ext/btconfigstore/bt_configstore.cc
+++ b/system_bt_ext/btconfigstore/bt_configstore.cc
@@ -401,7 +401,7 @@
break;
default:
- LOG_INFO(LOG_TAG, "%s:: prop type not handled %d", __func__, vProp.type);
+ LOG_INFO(LOG_TAG, "%s:: prop type not handled %d", __func__, vPropType);
config_free(config);
return status;
}
diff --git a/system_bt_ext/btif/Android.bp b/system_bt_ext/btif/Android.bp
index 76cfb3a..59cfaf2 100644
--- a/system_bt_ext/btif/Android.bp
+++ b/system_bt_ext/btif/Android.bp
@@ -34,6 +34,7 @@
"src/btif_ba.cc",
"src/btif_twsp_hf.cc",
"src/btif_iot_config.cc",
+ "src/btif_vendor_hf.cc",
],
shared_libs: [
"libcutils",
@@ -51,5 +52,7 @@
"-DBUILDCFG",
"-DHAS_NO_BDROID_BUILDCFG",
],
-
+ sanitize: {
+ never: true,
+ },
}
diff --git a/system_bt_ext/btif/include/btif_bat.h b/system_bt_ext/btif/include/btif_bat.h
index 29166dd..3b8d635 100644
--- a/system_bt_ext/btif/include/btif_bat.h
+++ b/system_bt_ext/btif/include/btif_bat.h
@@ -48,6 +48,17 @@
******************************************************************************/
typedef enum {
+ BA_CTRL_ACK_SUCCESS,
+ BA_CTRL_ACK_FAILURE,
+ BA_CTRL_ACK_INCALL_FAILURE, /* Failure when in Call*/
+ BA_CTRL_ACK_UNSUPPORTED,
+ BA_CTRL_ACK_PENDING,
+ BA_CTRL_ACK_DISCONNECT_IN_PROGRESS,
+ BA_CTRL_SKT_DISCONNECTED,
+ BA_CTRL_ACK_UNKNOWN,
+} tBA_CTRL_ACK;
+
+typedef enum {
BTIF_BA_STATE_IDLE_AUDIO_PENDING = 0,
BTIF_BA_STATE_IDLE_AUDIO_STREAMING,
BTIF_BA_STATE_IDLE_AUDIO_NS,
@@ -123,6 +134,14 @@
void ba_send_message(uint8_t event, uint8_t size, char* ptr, bool is_btif_thread);
uint16_t btif_get_ba_latency();
bool btif_ba_is_active();
+uint8_t btif_ba_get_sample_rate();
+uint8_t btif_ba_get_channel_mode();
+uint8_t btif_ba_get_frame_size();
+uint8_t btif_ba_get_complexity();
+uint8_t btif_ba_get_prediction_mode();
+uint8_t btif_ba_get_vbr_flag();
+uint32_t btif_ba_get_bitrate();
+
/*
uint8_t get_curr_vol_level();
uint8_t get_max_vol_level();
diff --git a/system_bt_ext/btif/src/btif_ba.cc b/system_bt_ext/btif/src/btif_ba.cc
index c96fa8b..4ee9219 100644
--- a/system_bt_ext/btif/src/btif_ba.cc
+++ b/system_bt_ext/btif/src/btif_ba.cc
@@ -32,7 +32,7 @@
#include <string.h>
#define LOG_TAG "bt_btif_vendor"
-
+#include "btif_a2dp.h"
#include <cutils/properties.h>
#include "bt_utils.h"
#include "btif_common.h"
@@ -52,17 +52,6 @@
#include "btif_av.h"
#include "btif_config.h"
-typedef enum {
- BA_CTRL_ACK_SUCCESS,
- BA_CTRL_ACK_FAILURE,
- BA_CTRL_ACK_INCALL_FAILURE, /* Failure when in Call*/
- BA_CTRL_ACK_UNSUPPORTED,
- BA_CTRL_ACK_PENDING,
- BA_CTRL_ACK_DISCONNECT_IN_PROGRESS,
- BA_CTRL_SKT_DISCONNECTED,
- BA_CTRL_ACK_UNKNOWN,
-} tBA_CTRL_ACK;
-
extern void btif_av_trigger_suspend();
static ba_transmitter_callbacks_t *ba_transmitter_callback = NULL;
@@ -264,11 +253,13 @@
bool btif_ba_is_active()
{
- LOG_INFO(LOG_TAG,"%s:",__func__);
+ bool ret = false;
if (btif_ba_get_state() > BTIF_BA_STATE_IDLE_AUDIO_NS)
- return true;
+ ret = true;
else
- return false;
+ ret = false;
+ LOG_INFO(LOG_TAG,"%s: %d",__func__, ret);
+ return ret;
}
btif_ba_state_t btif_ba_get_state()
@@ -323,11 +314,11 @@
{
if ( btif_ba_cb.audio_cmd_pending == BTIF_BA_AUDIO_PAUSE_REQ_EVT)
{
- btif_a2dp_audio_on_suspended(result);
+ btif_ba_audio_on_suspended(result);
}
else if ( btif_ba_cb.audio_cmd_pending == BTIF_BA_AUDIO_STOP_REQ_EVT)
{
- btif_a2dp_audio_on_stopped(result);
+ btif_ba_audio_on_stopped(result);
}
else
{
@@ -338,7 +329,7 @@
{
if ( btif_ba_cb.audio_cmd_pending == BTIF_BA_AUDIO_START_REQ_EVT)
{
- btif_a2dp_audio_on_started(result);
+ btif_ba_audio_on_started(result);
}
else
{
@@ -372,6 +363,41 @@
*p_codec_config = bit_rate;p_codec_config ++;
}
+uint8_t btif_ba_get_sample_rate()
+{
+ return btif_ba_cb.sampl_freq;
+}
+
+uint8_t btif_ba_get_channel_mode()
+{
+ return btif_ba_cb.ch_mode;
+}
+
+uint8_t btif_ba_get_frame_size()
+{
+ return btif_ba_cb.frame_size;
+}
+
+uint8_t btif_ba_get_complexity()
+{
+ return btif_ba_cb.complexity;
+}
+
+uint8_t btif_ba_get_prediction_mode()
+{
+ return btif_ba_cb.prediction_mode;
+}
+
+uint8_t btif_ba_get_vbr_flag()
+{
+ return btif_ba_cb.vbr_mode;
+}
+
+uint32_t btif_ba_get_bitrate()
+{
+ return btif_ba_cb.bit_rate;
+}
+
static void memorize_msg(uint8_t event, btif_ba_state_t state)
{
BTIF_TRACE_DEBUG(" %s event = %s, state = %d", __FUNCTION__,
diff --git a/system_bt_ext/btif/src/btif_vendor_hf.cc b/system_bt_ext/btif/src/btif_vendor_hf.cc
new file mode 100644
index 0000000..dc4895e
--- /dev/null
+++ b/system_bt_ext/btif/src/btif_vendor_hf.cc
@@ -0,0 +1,110 @@
+/*
+ * Copyright (c) 2019, The Linux Foundation. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above
+ * copyright notice, this list of conditions and the following
+ * disclaimer in the documentation and/or other materials provided
+ * with the distribution.
+ * * Neither the name of The Linux Foundation nor the names of its
+ * contributors may be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED "AS IS" AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS
+ * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
+ * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
+ * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN
+ * IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ ***********************************************************************/
+
+/************************************************************************************
+ *
+ * Filename: btif_vendor_hf.cc
+ *
+ * Description: Vendor Bluetooth Interface
+ *
+ *
+ ***********************************************************************************/
+
+#define LOG_TAG "bt_btif_vendor_hf"
+
+#include <hardware/vendor_hf.h>
+#include "btif_api.h"
+
+btvendor_hf_callbacks_t *bt_vendor_hf_callbacks = NULL;
+static bool swb_codec_status = false;
+
+/*******************************************************************************
+** VENDOR INTERFACE FUNCTIONS
+*******************************************************************************/
+
+/*******************************************************************************
+**
+** Function init
+**
+** Description initializes the vendor interface for HF
+**
+** Returns bt_status_t
+**
+*******************************************************************************/
+
+static bt_status_t init( btvendor_hf_callbacks_t* callbacks)
+{
+ bt_vendor_hf_callbacks = callbacks;
+ LOG_INFO(LOG_TAG,"init done");
+ return BT_STATUS_SUCCESS;
+}
+
+static void cleanup(void)
+{
+ LOG_INFO(LOG_TAG,"cleanup");
+ if (bt_vendor_hf_callbacks)
+ bt_vendor_hf_callbacks = NULL;
+}
+
+int enable_swb(bool enable) {
+ LOG_INFO(LOG_TAG,"%s: %d", __func__, enable);
+ swb_codec_status = enable;
+ return 0;
+}
+
+static const btvendor_hf_interface_t btvendorhfInterface = {
+ sizeof(btvendorhfInterface),
+ init,
+ cleanup,
+ enable_swb,
+};
+
+void btif_handle_vendor_hf_events(uint16_t event, uint16_t swb_config, RawAddress *bd_addr) {
+ HAL_CBACK(bt_vendor_hf_callbacks, swb_codec_cb, swb_config, bd_addr);
+}
+
+bool get_swb_codec_status() {
+ return swb_codec_status;
+}
+
+/*******************************************************************************
+**
+** Function btif_vendor_hf_get_interface
+**
+** Description Get the vendor callback interface
+**
+** Returns btvendor_hf_interface_t
+**
+*******************************************************************************/
+const btvendor_hf_interface_t *btif_vendor_hf_get_interface()
+{
+ BTIF_TRACE_EVENT("%s", __FUNCTION__);
+ return &btvendorhfInterface;
+}
+
diff --git a/system_bt_ext/conf/interop_database.conf b/system_bt_ext/conf/interop_database.conf
index b80522f..e53da04 100644
--- a/system_bt_ext/conf/interop_database.conf
+++ b/system_bt_ext/conf/interop_database.conf
@@ -8,7 +8,7 @@
#1. Below are the four tags for blacklist
# A. Address_Based C. Manufacturer_based
# B. Name_Based D. Vndr_Prdt_Based
-# E. SSR_Max_Lat_Based
+# E. SSR_Max_Lat_Based F: Version_Based
##
# A. Address_Based : This tag refers to Address based blacklist
# Input Type : Input value should be only 3 to 6 bytes of BD address
@@ -28,16 +28,21 @@
#####
# D. Vndr_Prdt_Based : This tag refers to vendor and product based blacklist
# Input type : Input should be in Hexadecimal value
-# Format Type : 4 bytes hex value( 2bytes of vendor and 2 bytes of product),
+# Format Type : 4 bytes hex value( 2 bytes of vendor and 2 bytes of product),
# Vendor and product hex values should be separated with delimiter(-).
# Examples : 0X00AB-0X00BC = Vndr_Prdt_Based
#####
-# E. SSR_Max_Lat_Based : This tag refers to SSR Max LAtency based blacklist
+# E. SSR_Max_Lat_Based : This tag refers to SSR Max Latency based blacklist
# Input type : Input value should be combination of first 3 bytes of BD address and
# Hexadecimal value of SSR Max Latency
# Format Type : Address should be in XX:XX:XX format followed by 2 bytes hex value
# of max latency Address and Max Latency should be separated with delimiter(-).
# Examples : 00:01:03-0X00AB = SSR_Max_Lat_Based
+#####
+# F. Version_Based : This tag refers to DID Version based blacklist
+# Input type : Input value should be Hexadecimal value
+# Format Type : 2 bytes hex value
+# Examples : 0X00AB = Version_Based
# ******************************* Start of Blacklist Database ********************************
#Disable secure connections
#This is for pre BT 4.1/2 devices that do not handle secure mode very well.
@@ -73,6 +78,7 @@
04:F8:C2 = Address_Based
00:18:91 = Address_Based
00:24:1C = Address_Based
+00:08:8b = Address_Based
# Disable automatic pairing with headsets/car-kits
# Some car kits do not react kindly to a failed pairing attempt and
@@ -114,6 +120,8 @@
00:1E:7C = Address_Based
C8:84:47 = Address_Based
D0:8A:55 = Address_Based
+48:F0:7B = Address_Based
+00:0A:08 = Address_Based
# Some HID pointing devices have proven problematic behaviour if pairing is initiated with
# them, resulting in no response for authentication request and ultimately resulting
@@ -211,10 +219,10 @@
# disable AAC for those headsets so that it switch to SBC
# 1. byte alignment issues with encoder (solo3::20:3c:ae,airpods::4c:32:75)
# 2. remote sending 128 instead of 128k as bitrate (cadillac::28:a1:83, Chevrolet Tahoe,
-# buick_verona::ac:7a:4d, maruti_brezzai:28:a1:83,cadillac cue::e0:75:0a, 30:C3:D9)
+# buick_verano::ac:7a:4d(moved to name based, maruti_brezzai:28:a1:83,
+# cadillac cue::e0:75:0a, 30:C3:D9)
# 3. remote supporting only 44.1Khz (BIG-JAM-BOX::00-21-3c,JVC CK::00:1D:86,BMW CK::9C:DF:03)
[INTEROP_DISABLE_AAC_CODEC]
-AC:7A:4D = Address_Based
28:A1:83 = Address_Based
A0:14:3D = Address_Based
90:03:B7 = Address_Based
@@ -226,6 +234,7 @@
30:C3:D9 = Address_Based
00:E0:4C = Address_Based
abramtek M1 = Name_Based
+Buick Verano = Name_Based
#Enable AAC encoder only for whitelist devices
diff --git a/system_bt_ext/device/Android.bp b/system_bt_ext/device/Android.bp
index 44babef..8a83a90 100644
--- a/system_bt_ext/device/Android.bp
+++ b/system_bt_ext/device/Android.bp
@@ -28,4 +28,7 @@
"libosi_qti",
"libbluetooth-types",
],
+ sanitize: {
+ never: true,
+ },
}
diff --git a/system_bt_ext/device/include/interop_config.h b/system_bt_ext/device/include/interop_config.h
index a2c5e62..35f3e8c 100644
--- a/system_bt_ext/device/include/interop_config.h
+++ b/system_bt_ext/device/include/interop_config.h
@@ -34,6 +34,7 @@
void interop_database_add_vndr_prdt(const interop_feature_t feature, uint16_t vendor_id, uint16_t product_id);
void interop_database_add_addr_max_lat(const interop_feature_t feature, const RawAddress *addr,
size_t length, uint16_t max_lat);
+void interop_database_add_version(const interop_feature_t feature, uint16_t version);
// API's for removing entries from dynamic interop database
bool interop_database_remove_addr(const interop_feature_t feature, const RawAddress *addr);
@@ -42,6 +43,7 @@
bool interop_database_remove_vndr_prdt(const interop_feature_t feature, uint16_t vendor_id, uint16_t product_id);
bool interop_database_remove_addr_max_lat(const interop_feature_t feature,
const RawAddress *addr, size_t length, uint16_t max_lat);
+bool interop_database_remove_version(const interop_feature_t feature, uint16_t version);
// API's to match entries with in dynamic interop database
bool interop_database_match_addr(const interop_feature_t feature, const RawAddress *addr);
@@ -50,4 +52,5 @@
bool interop_database_match_vndr_prdt(const interop_feature_t feature, uint16_t vendor_id, uint16_t product_id);
bool interop_database_match_addr_get_max_lat(const interop_feature_t feature,
const RawAddress *addr, uint16_t *max_lat);
+bool interop_database_match_version(const interop_feature_t feature, uint16_t version);
diff --git a/system_bt_ext/device/src/interop.cc b/system_bt_ext/device/src/interop.cc
index 637fe44..2381669 100644
--- a/system_bt_ext/device/src/interop.cc
+++ b/system_bt_ext/device/src/interop.cc
@@ -69,6 +69,7 @@
#define VALID_VNDR_PRDT_LEN (13)
#define VALID_MNFR_STR_LEN (6)
#define VALID_SSR_LAT_LEN (15)
+#define VALID_VERSION_LEN (6)
#define VENDOR_VALUE_SEPARATOR "-"
#define ADDR_BASED "Address_Based"
@@ -76,6 +77,7 @@
#define MNFR_BASED "Manufacturer_Based"
#define VNDR_PRDT_BASED "Vndr_Prdt_Based"
#define SSR_MAX_LAT_BASED "SSR_Max_Lat_Based"
+#define VERSION_BASED "Version_Based"
struct config_t {
list_t *sections;
@@ -98,12 +100,18 @@
interop_feature_t feature;
} interop_hid_ssr_max_lat_t;
+typedef struct {
+ uint16_t version;
+ interop_feature_t feature;
+} interop_version_t;
+
typedef enum {
INTEROP_BL_TYPE_ADDR = 0,
INTEROP_BL_TYPE_NAME,
INTEROP_BL_TYPE_MANUFACTURE,
INTEROP_BL_TYPE_VNDR_PRDT,
INTEROP_BL_TYPE_SSR_MAX_LAT,
+ INTEROP_BL_TYPE_VERSION,
} interop_bl_type;
@@ -123,6 +131,7 @@
interop_manufacturer_t mnfr_entry;
interop_hid_multitouch_t vnr_pdt_entry;
interop_hid_ssr_max_lat_t ssr_max_lat_entry;
+ interop_version_t version_entry;
} entry_type;
} interop_db_entry_t;
@@ -488,6 +497,18 @@
interop_config_flush();
break;
}
+ case INTEROP_BL_TYPE_VERSION:
+ {
+ interop_feature_t feature =
+ db_entry->entry_type.version_entry.feature;
+ char m_vendor[KEY_MAX_LENGTH] = { '\0' };
+ snprintf(m_vendor, sizeof(m_vendor), "0x%04x",
+ db_entry->entry_type.version_entry.version);
+ interop_config_set_str(interop_feature_string_(feature),
+ m_vendor, VERSION_BASED);
+ interop_config_flush();
+ break;
+ }
}
}
@@ -594,6 +615,20 @@
}
break;
}
+ case INTEROP_BL_TYPE_VERSION:
+ {
+ interop_version_t *src = &entry->entry_type.version_entry;
+ interop_version_t *cur = &db_entry->entry_type.version_entry;
+
+ if ((src->feature == cur->feature) &&
+ (src->version == cur->version)) {
+ if (ret_entry) {
+ *ret_entry = db_entry;
+ }
+ found = true;
+ }
+ break;
+ }
}
if (found) {
@@ -690,6 +725,19 @@
interop_config_flush();
break;
}
+ case INTEROP_BL_TYPE_VERSION:
+ {
+ interop_version_t *src = &entry->entry_type.version_entry;
+
+ interop_feature_t feature = src->feature;
+ char m_version[KEY_MAX_LENGTH] = { '\0' };
+ snprintf(m_version, sizeof(m_version), "0x%04x",
+ src->version);
+ interop_config_remove(interop_feature_string_(feature),
+ m_version);
+ interop_config_flush();
+ break;
+ }
default:
status = false;
}
@@ -923,6 +971,28 @@
entry->entry_type.ssr_max_lat_entry.length = len;
entry->entry_type.ssr_max_lat_entry.max_lat = max_lat;
interop_database_add_(entry, false);
+ } else if ( !strncasecmp( value, VERSION_BASED, strlen(VERSION_BASED))) {
+
+ uint16_t version;
+ char *e;
+
+ if ( strlen(key) != VALID_VERSION_LEN ) {
+ LOG_WARN(LOG_TAG,
+ " ignoring %s due to invalid version in config file", key);
+ return false;
+ }
+
+ version = (uint16_t)strtoul(key, &e, 16);
+ errno = 0;
+ if( *e || errno == EINVAL || errno == ERANGE )
+ return false;
+
+ interop_db_entry_t *entry = (interop_db_entry_t *)osi_calloc(sizeof(interop_db_entry_t));
+ entry->bl_type = INTEROP_BL_TYPE_VERSION;
+ entry->bl_entry_type = entry_type;
+ entry->entry_type.version_entry.feature = (interop_feature_t)feature;
+ entry->entry_type.version_entry.version = version;
+ interop_database_add_(entry, false);
}
LOG_WARN(LOG_TAG, " feature:: %d, key :: %s, value :: %s",
feature, key, value);
@@ -1065,6 +1135,17 @@
interop_database_add_(entry, true);
}
+void interop_database_add_version(const interop_feature_t feature, uint16_t version)
+{
+
+ interop_db_entry_t *entry = (interop_db_entry_t *)osi_calloc(sizeof(interop_db_entry_t));
+ entry->bl_type = INTEROP_BL_TYPE_VERSION;
+ entry->bl_entry_type = INTEROP_ENTRY_TYPE_DYNAMIC;
+ entry->entry_type.version_entry.feature = (interop_feature_t)feature;
+ entry->entry_type.version_entry.version = version;
+ interop_database_add_(entry, true);
+}
+
bool interop_database_match_manufacturer(const interop_feature_t feature,
uint16_t manufacturer)
{
@@ -1178,6 +1259,27 @@
return false;
}
+bool interop_database_match_version(const interop_feature_t feature, uint16_t version)
+{
+
+ interop_db_entry_t entry;
+ interop_db_entry_t *ret_entry = NULL;
+
+ entry.bl_type = INTEROP_BL_TYPE_VERSION;
+
+ entry.entry_type.version_entry.feature = (interop_feature_t)feature;
+ entry.entry_type.version_entry.version = version;
+ if (interop_database_match_(&entry, &ret_entry, (interop_entry_type)(INTEROP_ENTRY_TYPE_STATIC |
+ INTEROP_ENTRY_TYPE_DYNAMIC))) {
+ LOG_WARN(LOG_TAG,
+ "%s() Device with version: 0x%04x is a match for interop workaround %s", __func__, version,
+ interop_feature_string_(feature));
+ return true;
+ }
+
+ return false;
+}
+
bool interop_database_remove_name( const interop_feature_t feature, const char *name)
{
assert(name);
@@ -1291,3 +1393,25 @@
return false;
}
+bool interop_database_remove_version(const interop_feature_t feature, uint16_t version)
+{
+
+ interop_db_entry_t entry;
+
+ entry.bl_type = INTEROP_BL_TYPE_VERSION;
+ entry.bl_entry_type = INTEROP_ENTRY_TYPE_DYNAMIC;
+
+ entry.entry_type.version_entry.feature = (interop_feature_t)feature;
+ entry.entry_type.version_entry.version = version;
+
+ if (interop_database_remove_(&entry)) {
+ LOG_WARN(LOG_TAG,
+ "%s() Device with version: 0x%04x is removed from"
+ "interop workaround %s", __func__, version,
+ interop_feature_string_(feature));
+ return true;
+ }
+ return false;
+}
+
+
diff --git a/system_bt_ext/osi/Android.bp b/system_bt_ext/osi/Android.bp
index 456ab11..438e8fe 100644
--- a/system_bt_ext/osi/Android.bp
+++ b/system_bt_ext/osi/Android.bp
@@ -24,4 +24,7 @@
"libbt-utils",
],
cflags: ["-DBUILDCFG"],
+ sanitize: {
+ never: true,
+ },
}
diff --git a/system_bt_ext/stack/Android.bp b/system_bt_ext/stack/Android.bp
index cca03cb..97b18e6 100644
--- a/system_bt_ext/stack/Android.bp
+++ b/system_bt_ext/stack/Android.bp
@@ -39,4 +39,7 @@
"libcutils",
"liblog",
],
+ sanitize: {
+ never: true,
+ },
}
diff --git a/vhal/include/hardware/vendor_hf.h b/vhal/include/hardware/vendor_hf.h
new file mode 100644
index 0000000..6b07c5c
--- /dev/null
+++ b/vhal/include/hardware/vendor_hf.h
@@ -0,0 +1,73 @@
+/*
+ * Copyright (c) 2019, The Linux Foundation. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above
+ * copyright notice, this list of conditions and the following
+ * disclaimer in the documentation and/or other materials provided
+ * with the distribution.
+ * * Neither the name of The Linux Foundation nor the names of its
+ * contributors may be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED "AS IS" AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS
+ * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
+ * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
+ * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN
+ * IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef ANDROID_INCLUDE_BT_HF_VENDOR_H
+#define ANDROID_INCLUDE_BT_HF_VENDOR_H
+
+#include <hardware/bluetooth.h>
+
+__BEGIN_DECLS
+
+#define BT_PROFILE_VENDOR_HF_ID "vendorhfservice"
+
+bool get_swb_codec_status();
+
+void btif_handle_vendor_hf_events(uint16_t event, uint16_t swb_config, RawAddress *bd_addr);
+
+/* SWB callback events */
+typedef void (* bt_swb_update_callback)(uint16_t swb_codec_config, RawAddress *bd_addr);
+
+/** BT-Vendor hf callback structure. */
+typedef struct {
+ /** set to sizeof(BtVendorhfCallbacks) */
+ size_t size;
+ bt_swb_update_callback swb_codec_cb;
+} btvendor_hf_callbacks_t;
+
+
+/** Represents the standard BT-Vendor hf interface.
+ */
+typedef struct {
+ /** set to sizeof(BtVendorHfInterface) */
+ size_t size;
+
+ /**
+ * Register the BtVendorhf callbacks
+ */
+ bt_status_t (*init)( btvendor_hf_callbacks_t* callbacks );
+
+ /** Closes the interface. */
+ void (*cleanup)( void );
+
+ int (*enable_swb) (bool enable);
+} btvendor_hf_interface_t;
+
+__END_DECLS
+#endif /* ANDROID_INCLUDE_BT_VENDOR_H */
+