Merge tag 'android-7.1.1_r22' into 7.1.1
Android 7.1.1 Release 22 (NMF26X)
diff --git a/Android.mk b/Android.mk
old mode 100644
new mode 100755
index 65f08b6..6bbbc9b
--- a/Android.mk
+++ b/Android.mk
@@ -24,6 +24,7 @@
LOCAL_JNI_SHARED_LIBRARIES := libbluetooth_jni
LOCAL_JAVA_LIBRARIES := javax.obex telephony-common libprotobuf-java-micro services.net
LOCAL_STATIC_JAVA_LIBRARIES := com.android.vcard bluetooth.mapsapi sap-api-java-static android-support-v4 services.net
+LOCAL_STATIC_JAVA_LIBRARIES += com.android.emailcommon
LOCAL_PROTOC_OPTIMIZE_TYPE := micro
LOCAL_REQUIRED_MODULES := bluetooth.default
diff --git a/AndroidManifest.xml b/AndroidManifest.xml
old mode 100644
new mode 100755
index 50cfada..adcff85
--- a/AndroidManifest.xml
+++ b/AndroidManifest.xml
@@ -58,7 +58,6 @@
<uses-permission android:name="android.permission.SEND_SMS" />
<uses-permission android:name="android.permission.READ_SMS" />
<uses-permission android:name="android.permission.WRITE_SMS" />
- <uses-permission android:name="android.permission.READ_CONTACTS" />
<uses-permission android:name="android.permission.MEDIA_CONTENT_CONTROL" />
<uses-permission android:name="android.permission.UPDATE_APP_OPS_STATS" />
<uses-permission android:name="android.permission.VIBRATE" />
@@ -66,6 +65,8 @@
<uses-permission android:name="android.permission.REAL_GET_TASKS" />
<uses-permission android:name="android.permission.MODIFY_AUDIO_ROUTING" />
<uses-permission android:name="android.permission.UPDATE_DEVICE_STATS" />
+ <uses-permission android:name="com.android.email.permission.ACCESS_PROVIDER"/>
+ <uses-permission android:name="com.android.email.permission.READ_ATTACHMENT"/>
<!-- For PBAP Owner Vcard Info -->
<uses-permission android:name="android.permission.READ_PROFILE"/>
@@ -155,8 +156,10 @@
<data android:mimeType="application/zip" />
<data android:mimeType="application/vnd.ms-excel" />
<data android:mimeType="application/msword" />
+ <data android:mimeType="application/vnd.android.package-archive" />
<data android:mimeType="application/vnd.ms-powerpoint" />
<data android:mimeType="application/pdf" />
+ <data android:mimeType="application/epub+zip" />
<data android:mimeType="application/vnd.openxmlformats-officedocument.spreadsheetml.sheet" />
<data android:mimeType="application/vnd.openxmlformats-officedocument.wordprocessingml.document" />
<data android:mimeType="application/vnd.openxmlformats-officedocument.presentationml.presentation" />
@@ -167,6 +170,7 @@
<category android:name="android.intent.category.DEFAULT" />
<data android:mimeType="image/*" />
<data android:mimeType="video/*" />
+ <data android:mimeType="audio/*" />
<data android:mimeType="x-mixmedia/*" />
<data android:mimeType="text/x-vcard" />
</intent-filter>
@@ -209,7 +213,7 @@
android:process="@string/process"
android:label=""
android:excludeFromRecents="true"
- android:configChanges="orientation|keyboardHidden"
+ android:configChanges="orientation|keyboardHidden|screenSize"
android:enabled="@bool/profile_supported_opp">
</activity>
<activity android:name=".pbap.BluetoothPbapActivity"
diff --git a/jni/Android.mk b/jni/Android.mk
index 55aa2cc..581b290 100644
--- a/jni/Android.mk
+++ b/jni/Android.mk
@@ -14,10 +14,18 @@
com_android_bluetooth_hdp.cpp \
com_android_bluetooth_pan.cpp \
com_android_bluetooth_gatt.cpp \
- com_android_bluetooth_sdp.cpp
+ com_android_bluetooth_sdp.cpp \
+ com_android_bluetooth_btservice_vendor.cpp
+ifneq ($(TARGET_SUPPORTS_WEARABLES),true)
LOCAL_C_INCLUDES += \
- $(JNI_H_INCLUDE) \
+ $(JNI_H_INCLUDE) \
+ vendor/qcom/opensource/bluetooth/hal/include
+else
+LOCAL_C_INCLUDES += \
+ $(JNI_H_INCLUDE) \
+ device/qcom/msm8909w/opensource/bluetooth/hal/include
+endif
LOCAL_SHARED_LIBRARIES := \
libandroid_runtime \
diff --git a/jni/com_android_bluetooth.h b/jni/com_android_bluetooth.h
index 4f55c06..a797dd3 100644
--- a/jni/com_android_bluetooth.h
+++ b/jni/com_android_bluetooth.h
@@ -54,6 +54,7 @@
int register_com_android_bluetooth_sdp (JNIEnv* env);
+int register_com_android_bluetooth_btservice_vendor (JNIEnv* env);
}
#endif /* COM_ANDROID_BLUETOOTH_H */
diff --git a/jni/com_android_bluetooth_a2dp.cpp b/jni/com_android_bluetooth_a2dp.cpp
index 2b4cb56..7be8f53 100644
--- a/jni/com_android_bluetooth_a2dp.cpp
+++ b/jni/com_android_bluetooth_a2dp.cpp
@@ -1,4 +1,7 @@
/*
+ * Copyright (C) 2013-2014, The Linux Foundation. All rights reserved.
+ * Not a Contribution.
+ *
* Copyright (C) 2012 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
@@ -24,15 +27,21 @@
#include "android_runtime/AndroidRuntime.h"
#include <string.h>
+#include <pthread.h>
namespace android {
static jmethodID method_onConnectionStateChanged;
static jmethodID method_onAudioStateChanged;
+static jmethodID method_onCheckConnectionPriority;
+static jmethodID method_onMulticastStateChanged;
+static jmethodID method_onReconfigA2dpTriggered;
static const btav_interface_t *sBluetoothA2dpInterface = NULL;
static jobject mCallbacksObj = NULL;
static JNIEnv *sCallbackEnv = NULL;
+static pthread_mutex_t mMutex = PTHREAD_MUTEX_INITIALIZER;
+
static bool checkCallbackThread() {
// Always fetch the latest callbackEnv from AdapterService.
// Caching this could cause this sCallbackEnv to go out-of-sync
@@ -56,6 +65,7 @@
ALOGE("Callback: '%s' is not called on the correct thread", __FUNCTION__); \
return; \
}
+
addr = sCallbackEnv->NewByteArray(sizeof(bt_bdaddr_t));
if (!addr) {
ALOGE("Fail to new jbyteArray bd addr for connection state");
@@ -64,8 +74,16 @@
}
sCallbackEnv->SetByteArrayRegion(addr, 0, sizeof(bt_bdaddr_t), (jbyte*) bd_addr);
- sCallbackEnv->CallVoidMethod(mCallbacksObj, method_onConnectionStateChanged, (jint) state,
- addr);
+
+ pthread_mutex_lock(&mMutex);
+ if (mCallbacksObj != NULL) {
+ sCallbackEnv->CallVoidMethod(mCallbacksObj, method_onConnectionStateChanged,
+ (jint) state, addr);
+ } else {
+ ALOGE("Callbacks Obj is no more valid: '%s", __FUNCTION__);
+ }
+ pthread_mutex_unlock(&mMutex);
+
checkAndClearExceptionFromCallback(sCallbackEnv, __FUNCTION__);
sCallbackEnv->DeleteLocalRef(addr);
}
@@ -87,17 +105,96 @@
}
sCallbackEnv->SetByteArrayRegion(addr, 0, sizeof(bt_bdaddr_t), (jbyte*) bd_addr);
- sCallbackEnv->CallVoidMethod(mCallbacksObj, method_onAudioStateChanged, (jint) state,
- addr);
+
+ pthread_mutex_lock(&mMutex);
+ if (mCallbacksObj != NULL) {
+ sCallbackEnv->CallVoidMethod(mCallbacksObj, method_onAudioStateChanged,
+ (jint) state, addr);
+ } else {
+ ALOGE("Callbacks Obj is no more valid: '%s", __FUNCTION__);
+ }
+ pthread_mutex_unlock(&mMutex);
+
checkAndClearExceptionFromCallback(sCallbackEnv, __FUNCTION__);
sCallbackEnv->DeleteLocalRef(addr);
}
+static void bta2dp_connection_priority_callback(bt_bdaddr_t* bd_addr) {
+ jbyteArray addr;
+
+ ALOGI("%s", __FUNCTION__);
+
+ if (!checkCallbackThread()) { \
+ ALOGE("Callback: '%s' is not called on the correct thread", __FUNCTION__); \
+ return; \
+ }
+ addr = sCallbackEnv->NewByteArray(sizeof(bt_bdaddr_t));
+ if (!addr) {
+ ALOGE("Fail to new jbyteArray bd addr for connection state");
+ checkAndClearExceptionFromCallback(sCallbackEnv, __FUNCTION__);
+ return;
+ }
+
+ sCallbackEnv->SetByteArrayRegion(addr, 0, sizeof(bt_bdaddr_t), (jbyte*) bd_addr);
+
+ pthread_mutex_lock(&mMutex);
+ if (mCallbacksObj != NULL) {
+ sCallbackEnv->CallVoidMethod(mCallbacksObj, method_onCheckConnectionPriority,
+ addr);
+ } else {
+ ALOGE("Callbacks Obj is no more valid: '%s", __FUNCTION__);
+ }
+ pthread_mutex_unlock(&mMutex);
+
+ checkAndClearExceptionFromCallback(sCallbackEnv, __FUNCTION__);
+ sCallbackEnv->DeleteLocalRef(addr);
+}
+
+static void bta2dp_multicast_enabled_callback(int state) {
+
+ ALOGI("%s", __FUNCTION__);
+
+ if (!checkCallbackThread()) { \
+ ALOGE("Callback: '%s' is not called on the correct thread", __FUNCTION__); \
+ return; \
+ }
+
+ pthread_mutex_lock(&mMutex);
+ if (mCallbacksObj != NULL) {
+ sCallbackEnv->CallVoidMethod(mCallbacksObj, method_onMulticastStateChanged,
+ state);
+ } else {
+ ALOGE("Callbacks Obj is no more valid: '%s", __FUNCTION__);
+ }
+ pthread_mutex_unlock(&mMutex);
+
+ checkAndClearExceptionFromCallback(sCallbackEnv, __FUNCTION__);
+}
+
+static void bta2dp_reconfig_a2dp_trigger_callback(int reason, bt_bdaddr_t* bd_addr) {
+ jbyteArray addr;
+ ALOGI("%s",__FUNCTION__);
+
+ addr = sCallbackEnv->NewByteArray(sizeof(bt_bdaddr_t));
+ if (!addr) {
+ ALOGE("Fail to new jbyteArray bd addr for connection state");
+ checkAndClearExceptionFromCallback(sCallbackEnv, __FUNCTION__);
+ return;
+ }
+
+ sCallbackEnv->SetByteArrayRegion(addr, 0, sizeof(bt_bdaddr_t), (jbyte*) bd_addr);
+ sCallbackEnv->CallVoidMethod(mCallbacksObj, method_onReconfigA2dpTriggered, reason, addr);
+ checkAndClearExceptionFromCallback(sCallbackEnv, __FUNCTION__);
+ sCallbackEnv->DeleteLocalRef(addr);
+}
static btav_callbacks_t sBluetoothA2dpCallbacks = {
sizeof(sBluetoothA2dpCallbacks),
bta2dp_connection_state_callback,
bta2dp_audio_state_callback,
- NULL, /* audio_config_cb */
+ NULL,
+ bta2dp_connection_priority_callback,
+ bta2dp_multicast_enabled_callback,
+ bta2dp_reconfig_a2dp_trigger_callback
};
static void classInitNative(JNIEnv* env, jclass clazz) {
@@ -107,13 +204,31 @@
method_onAudioStateChanged =
env->GetMethodID(clazz, "onAudioStateChanged", "(I[B)V");
+ method_onCheckConnectionPriority =
+ env->GetMethodID(clazz, "onCheckConnectionPriority", "([B)V");
+
+ method_onMulticastStateChanged =
+ env->GetMethodID(clazz, "onMulticastStateChanged", "(I)V");
+
+ method_onReconfigA2dpTriggered =
+ env->GetMethodID(clazz, "onReconfigA2dpTriggered", "(I[B)V");
ALOGI("%s: succeeds", __FUNCTION__);
}
-static void initNative(JNIEnv *env, jobject object) {
+static void initNative(JNIEnv *env, jobject object, jint maxA2dpConnections,
+ jint multiCastState, jstring offload_cap) {
const bt_interface_t* btInf;
+ const char *offload_capabilities;
bt_status_t status;
+ // Calling GetStringUTFChars with a null jstring can cause ART to crash
+ if (offload_cap != NULL) {
+ offload_capabilities = env->GetStringUTFChars(offload_cap, NULL);
+ } else {
+ offload_capabilities = NULL;
+ }
+
+
if ( (btInf = getBluetoothInterface()) == NULL) {
ALOGE("Bluetooth module is not loaded");
return;
@@ -125,6 +240,13 @@
sBluetoothA2dpInterface = NULL;
}
+ if ( (sBluetoothA2dpInterface = (btav_interface_t *)
+ btInf->get_profile_interface(BT_PROFILE_ADVANCED_AUDIO_ID)) == NULL) {
+ ALOGE("Failed to get Bluetooth A2DP Interface");
+ return;
+ }
+
+ pthread_mutex_lock(&mMutex);
if (mCallbacksObj != NULL) {
ALOGW("Cleaning up A2DP callback object");
env->DeleteGlobalRef(mCallbacksObj);
@@ -135,16 +257,20 @@
ALOGE("Failed to allocate Global Ref for A2DP Callbacks");
return;
}
+ pthread_mutex_unlock(&mMutex);
- if ( (sBluetoothA2dpInterface = (btav_interface_t *)
- btInf->get_profile_interface(BT_PROFILE_ADVANCED_AUDIO_ID)) == NULL) {
- ALOGE("Failed to get Bluetooth A2DP Interface");
- return;
- }
-
- if ( (status = sBluetoothA2dpInterface->init(&sBluetoothA2dpCallbacks)) != BT_STATUS_SUCCESS) {
+ if ( (status = sBluetoothA2dpInterface->init(&sBluetoothA2dpCallbacks,
+ maxA2dpConnections, multiCastState,
+ offload_capabilities)) != BT_STATUS_SUCCESS) {
ALOGE("Failed to initialize Bluetooth A2DP, status: %d", status);
sBluetoothA2dpInterface = NULL;
+ pthread_mutex_lock(&mMutex);
+ if (mCallbacksObj != NULL) {
+ ALOGW("Clean up A2DP callback object");
+ env->DeleteGlobalRef(mCallbacksObj);
+ mCallbacksObj = NULL;
+ }
+ pthread_mutex_unlock(&mMutex);
return;
}
}
@@ -162,10 +288,12 @@
sBluetoothA2dpInterface = NULL;
}
+ pthread_mutex_lock(&mMutex);
if (mCallbacksObj != NULL) {
env->DeleteGlobalRef(mCallbacksObj);
mCallbacksObj = NULL;
}
+ pthread_mutex_unlock(&mMutex);
}
static jboolean connectA2dpNative(JNIEnv *env, jobject object, jbyteArray address) {
@@ -209,12 +337,32 @@
return (status == BT_STATUS_SUCCESS) ? JNI_TRUE : JNI_FALSE;
}
+static void allowConnectionNative(JNIEnv *env, jobject object, int is_valid, jbyteArray address) {
+
+ jbyte *addr;
+ if (!sBluetoothA2dpInterface) {
+ ALOGE("sBluetoothA2dpInterface is NULL ");
+ return;
+ }
+
+ addr = env->GetByteArrayElements(address, NULL);
+
+ if (!addr) {
+ jniThrowIOException(env, EINVAL);
+ return ;
+ }
+
+ sBluetoothA2dpInterface->allow_connection(is_valid, (bt_bdaddr_t *)addr);
+ env->ReleaseByteArrayElements(address, addr, 0);
+}
+
static JNINativeMethod sMethods[] = {
{"classInitNative", "()V", (void *) classInitNative},
- {"initNative", "()V", (void *) initNative},
+ {"initNative", "(IILjava/lang/String;)V", (void *) initNative},
{"cleanupNative", "()V", (void *) cleanupNative},
{"connectA2dpNative", "([B)Z", (void *) connectA2dpNative},
{"disconnectA2dpNative", "([B)Z", (void *) disconnectA2dpNative},
+ {"allowConnectionNative", "(I[B)V", (void *) allowConnectionNative},
};
int register_com_android_bluetooth_a2dp(JNIEnv* env)
diff --git a/jni/com_android_bluetooth_a2dp_sink.cpp b/jni/com_android_bluetooth_a2dp_sink.cpp
index 82110d3..cc1ee01 100644
--- a/jni/com_android_bluetooth_a2dp_sink.cpp
+++ b/jni/com_android_bluetooth_a2dp_sink.cpp
@@ -1,4 +1,7 @@
/*
+ * Copyright (C) 2013-2014, The Linux Foundation. All rights reserved.
+ * Not a Contribution.
+ *
* Copyright (C) 2012 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
@@ -136,7 +139,8 @@
ALOGI("%s: succeeds", __FUNCTION__);
}
-static void initNative(JNIEnv *env, jobject object) {
+static void initNative(JNIEnv *env, jobject object, jint maxA2dpConnections,
+ jint multiCastState) {
const bt_interface_t* btInf;
bt_status_t status;
@@ -163,7 +167,8 @@
return;
}
- if ( (status = sBluetoothA2dpInterface->init(&sBluetoothA2dpCallbacks)) != BT_STATUS_SUCCESS) {
+ if ( (status = sBluetoothA2dpInterface->init(&sBluetoothA2dpCallbacks,
+ maxA2dpConnections, multiCastState, NULL)) != BT_STATUS_SUCCESS) {
ALOGE("Failed to initialize Bluetooth A2DP Sink, status: %d", status);
sBluetoothA2dpInterface = NULL;
return;
@@ -247,7 +252,7 @@
static JNINativeMethod sMethods[] = {
{"classInitNative", "()V", (void *) classInitNative},
- {"initNative", "()V", (void *) initNative},
+ {"initNative", "(II)V", (void *) initNative},
{"cleanupNative", "()V", (void *) cleanupNative},
{"connectA2dpNative", "([B)Z", (void *) connectA2dpNative},
{"disconnectA2dpNative", "([B)Z", (void *) disconnectA2dpNative},
diff --git a/jni/com_android_bluetooth_avrcp.cpp b/jni/com_android_bluetooth_avrcp.cpp
index ba1d78b..6319de9 100644
--- a/jni/com_android_bluetooth_avrcp.cpp
+++ b/jni/com_android_bluetooth_avrcp.cpp
@@ -1,4 +1,7 @@
/*
+ * Copyright (C) 2013-2015, The Linux Foundation. All rights reserved.
+ * Not a Contribution.
+ *
* Copyright (C) 2012 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
@@ -16,7 +19,7 @@
#define LOG_TAG "BluetoothAvrcpServiceJni"
-#define LOG_NDEBUG 0
+//#define LOG_NDEBUG 0
#include "com_android_bluetooth.h"
#include "hardware/bt_rc.h"
@@ -28,12 +31,27 @@
namespace android {
static jmethodID method_getRcFeatures;
static jmethodID method_getPlayStatus;
+static jmethodID method_onListPlayerAttributeRequest;
static jmethodID method_getElementAttr;
static jmethodID method_registerNotification;
static jmethodID method_volumeChangeCallback;
static jmethodID method_handlePassthroughCmd;
+static jmethodID method_handlePassthroughRsp;
+static jmethodID method_getFolderItems;
+static jmethodID method_setAddressedPlayer;
+static jmethodID method_setBrowsedPlayer;
+static jmethodID method_changePath;
+static jmethodID method_playItem;
+static jmethodID method_getItemAttr;
+static jmethodID method_onListPlayerAttributeValues;
+static jmethodID method_onGetPlayerAttributeValues;
+static jmethodID method_setPlayerAppSetting;
+static jmethodID method_getplayerattribute_text;
+static jmethodID method_getplayervalue_text;
+static jmethodID method_onConnectionStateChanged;
+static jmethodID method_getTotalNumberOfItems;
-static const btrc_interface_t *sBluetoothAvrcpInterface = NULL;
+static const btrc_interface_t *sBluetoothMultiAvrcpInterface = NULL;
static jobject mCallbacksObj = NULL;
static JNIEnv *sCallbackEnv = NULL;
@@ -50,7 +68,7 @@
}
static void btavrcp_remote_features_callback(bt_bdaddr_t* bd_addr, btrc_remote_features_t features) {
- ALOGI("%s", __FUNCTION__);
+ ALOGV("%s", __FUNCTION__);
jbyteArray addr;
if (!checkCallbackThread()) {
@@ -59,14 +77,15 @@
}
addr = sCallbackEnv->NewByteArray(sizeof(bt_bdaddr_t));
if (!addr) {
- ALOGE("Unable to allocate byte array for bd_addr");
+ ALOGE("Fail to new jbyteArray bd addr for remote features");
checkAndClearExceptionFromCallback(sCallbackEnv, __FUNCTION__);
return;
}
if (mCallbacksObj) {
sCallbackEnv->SetByteArrayRegion(addr, 0, sizeof(bt_bdaddr_t), (jbyte*) bd_addr);
- sCallbackEnv->CallVoidMethod(mCallbacksObj, method_getRcFeatures, addr, (jint)features);
+ sCallbackEnv->CallVoidMethod(mCallbacksObj, method_getRcFeatures, addr,
+ (jint)features, addr);
} else {
ALOGE("%s: mCallbacksObj is null", __FUNCTION__);
}
@@ -75,31 +94,252 @@
sCallbackEnv->DeleteLocalRef(addr);
}
-static void btavrcp_get_play_status_callback() {
- ALOGI("%s", __FUNCTION__);
+static void btavrcp_get_play_status_callback(bt_bdaddr_t* bd_addr) {
+ ALOGV("%s", __FUNCTION__);
+ jbyteArray addr;
if (!checkCallbackThread()) {
ALOGE("Callback: '%s' is not called on the correct thread", __FUNCTION__);
return;
}
+ addr = sCallbackEnv->NewByteArray(sizeof(bt_bdaddr_t));
+ if (!addr) {
+ ALOGE("Fail to new jbyteArray bd addr for get play status");
+ checkAndClearExceptionFromCallback(sCallbackEnv, __FUNCTION__);
+ return;
+ }
+ sCallbackEnv->SetByteArrayRegion(addr, 0, sizeof(bt_bdaddr_t), (jbyte*) bd_addr);
if (mCallbacksObj) {
- sCallbackEnv->CallVoidMethod(mCallbacksObj, method_getPlayStatus);
+ sCallbackEnv->CallVoidMethod(mCallbacksObj, method_getPlayStatus, addr);
} else {
ALOGE("%s: mCallbacksObj is null", __FUNCTION__);
}
checkAndClearExceptionFromCallback(sCallbackEnv, __FUNCTION__);
+ sCallbackEnv->DeleteLocalRef(addr);
}
-static void btavrcp_get_element_attr_callback(uint8_t num_attr, btrc_media_attr_t *p_attrs) {
- jintArray attrs;
-
- ALOGI("%s", __FUNCTION__);
+static void btavrcp_get_player_seeting_value_callback(btrc_player_attr_t player_att,
+ bt_bdaddr_t* bd_addr) {
+ ALOGV("%s", __FUNCTION__);
+ jbyteArray addr;
if (!checkCallbackThread()) {
ALOGE("Callback: '%s' is not called on the correct thread", __FUNCTION__);
return;
}
+ addr = sCallbackEnv->NewByteArray(sizeof(bt_bdaddr_t));
+ if (!addr) {
+ ALOGE("Fail to new jbyteArray bd addr for player seeting");
+ checkAndClearExceptionFromCallback(sCallbackEnv, __FUNCTION__);
+ return;
+ }
+ sCallbackEnv->SetByteArrayRegion(addr, 0, sizeof(bt_bdaddr_t), (jbyte*) bd_addr);
+
+ if (mCallbacksObj) {
+ sCallbackEnv->CallVoidMethod(mCallbacksObj ,method_onListPlayerAttributeValues,
+ (jbyte)player_att, addr);
+ } else {
+ ALOGE("%s: mCallbacksObj is null", __FUNCTION__);
+ }
+ checkAndClearExceptionFromCallback(sCallbackEnv, __FUNCTION__);
+ sCallbackEnv->DeleteLocalRef(addr);
+}
+
+static void btavrcp_get_player_attribute_id_callback(bt_bdaddr_t* bd_addr) {
+ ALOGV("%s", __FUNCTION__);
+ jbyteArray addr;
+
+ if (!checkCallbackThread()) {
+ ALOGE("Callback: '%s' is not called on the correct thread", __FUNCTION__);
+ return;
+ }
+ addr = sCallbackEnv->NewByteArray(sizeof(bt_bdaddr_t));
+ if (!addr) {
+ ALOGE("Fail to new jbyteArray bd addr for player attribute");
+ checkAndClearExceptionFromCallback(sCallbackEnv, __FUNCTION__);
+ return;
+ }
+ sCallbackEnv->SetByteArrayRegion(addr, 0, sizeof(bt_bdaddr_t), (jbyte*) bd_addr);
+
+ if (mCallbacksObj) {
+ sCallbackEnv->CallVoidMethod(mCallbacksObj,method_onListPlayerAttributeRequest, addr);
+ } else {
+ ALOGE("%s: mCallbacksObj is null", __FUNCTION__);
+ }
+ checkAndClearExceptionFromCallback(sCallbackEnv, __FUNCTION__);
+ sCallbackEnv->DeleteLocalRef(addr);
+}
+
+static void btavrcp_getcurrent_player_app_setting_values( uint8_t num_attr,
+ btrc_player_attr_t *p_attrs,
+ bt_bdaddr_t* bd_addr) {
+ jintArray attrs;
+ ALOGV("%s", __FUNCTION__);
+ jbyteArray addr;
+
+ if (!checkCallbackThread()) {
+ ALOGE("Callback: '%s' is not called on the correct thread", __FUNCTION__);
+ return;
+ }
+ addr = sCallbackEnv->NewByteArray(sizeof(bt_bdaddr_t));
+ if (!addr) {
+ ALOGE("Fail to new jbyteArray bd addr for player app setting");
+ checkAndClearExceptionFromCallback(sCallbackEnv, __FUNCTION__);
+ return;
+ }
+ sCallbackEnv->SetByteArrayRegion(addr, 0, sizeof(bt_bdaddr_t), (jbyte*) bd_addr);
+ attrs = (jintArray)sCallbackEnv->NewIntArray(num_attr);
+ if (!attrs) {
+ ALOGE("Fail to new jintArray for attrs");
+ checkAndClearExceptionFromCallback(sCallbackEnv, __FUNCTION__);
+ return;
+ }
+ sCallbackEnv->SetIntArrayRegion(attrs, 0, num_attr, (jint *)p_attrs);
+
+ if (mCallbacksObj) {
+ sCallbackEnv->CallVoidMethod(mCallbacksObj,method_onGetPlayerAttributeValues,
+ (jbyte)num_attr,attrs, addr);
+ }
+ else {
+ ALOGE("%s: mCallbacksObj is null", __FUNCTION__);
+ }
+ checkAndClearExceptionFromCallback(sCallbackEnv, __FUNCTION__);
+ sCallbackEnv->DeleteLocalRef(attrs);
+ sCallbackEnv->DeleteLocalRef(addr);
+}
+
+static void btavrcp_set_playerapp_setting_value_callback(btrc_player_settings_t *attr,
+ bt_bdaddr_t* bd_addr)
+{
+ jbyteArray attrs_ids;
+ jbyteArray attrs_value;
+ ALOGV("%s", __FUNCTION__);
+ jbyteArray addr;
+
+ if (!checkCallbackThread()) {
+ ALOGE("Callback: '%s' is not called on the correct thread", __FUNCTION__);
+ return;
+ }
+ addr = sCallbackEnv->NewByteArray(sizeof(bt_bdaddr_t));
+ if (!addr) {
+ ALOGE("Fail to new jbyteArray bd addr for set playerapp");
+ checkAndClearExceptionFromCallback(sCallbackEnv, __FUNCTION__);
+ return;
+ }
+ sCallbackEnv->SetByteArrayRegion(addr, 0, sizeof(bt_bdaddr_t), (jbyte*) bd_addr);
+ attrs_ids = (jbyteArray)sCallbackEnv->NewByteArray(attr->num_attr);
+ if (!attrs_ids) {
+ ALOGE("Fail to new jintArray for attrs");
+ checkAndClearExceptionFromCallback(sCallbackEnv, __FUNCTION__);
+ return;
+ }
+ sCallbackEnv->SetByteArrayRegion(attrs_ids, 0, attr->num_attr, (jbyte *)attr->attr_ids);
+ attrs_value = (jbyteArray)sCallbackEnv->NewByteArray(attr->num_attr);
+ if (!attrs_value) {
+ ALOGE("Fail to new jintArray for attrs");
+ checkAndClearExceptionFromCallback(sCallbackEnv, __FUNCTION__);
+ return;
+ }
+ sCallbackEnv->SetByteArrayRegion(attrs_value, 0, attr->num_attr, (jbyte *)attr->attr_values);
+ if (mCallbacksObj) {
+ sCallbackEnv->CallVoidMethod(mCallbacksObj, method_setPlayerAppSetting,
+ (jbyte)attr->num_attr ,attrs_ids ,attrs_value, addr);
+ } else {
+ ALOGE("%s: mCallbacksObj is null", __FUNCTION__);
+ }
+ sCallbackEnv->DeleteLocalRef(attrs_ids);
+ sCallbackEnv->DeleteLocalRef(attrs_value);
+ sCallbackEnv->DeleteLocalRef(addr);
+}
+
+static void btavrcp_getPlayer_app_attribute_text(uint8_t num , btrc_player_attr_t *att,
+ bt_bdaddr_t* bd_addr)
+{
+ jbyteArray attrs;
+ ALOGV("%s", __FUNCTION__);
+ jbyteArray addr;
+
+ if (!checkCallbackThread()) {
+ ALOGE("Callback: '%s' is not called on the correct thread", __FUNCTION__);
+ return;
+ }
+ addr = sCallbackEnv->NewByteArray(sizeof(bt_bdaddr_t));
+ if (!addr) {
+ ALOGE("Fail to new jbyteArray bd addr for getPlayer app");
+ checkAndClearExceptionFromCallback(sCallbackEnv, __FUNCTION__);
+ return;
+ }
+ sCallbackEnv->SetByteArrayRegion(addr, 0, sizeof(bt_bdaddr_t), (jbyte*) bd_addr);
+ attrs = (jbyteArray)sCallbackEnv->NewByteArray(num);
+ if (!attrs) {
+ ALOGE("Fail to new jintArray for attrs");
+ checkAndClearExceptionFromCallback(sCallbackEnv, __FUNCTION__);
+ return;
+ }
+ sCallbackEnv->SetByteArrayRegion(attrs, 0, num, (jbyte *)att);
+ if (mCallbacksObj) {
+ sCallbackEnv->CallVoidMethod(mCallbacksObj, method_getplayerattribute_text,
+ (jbyte) num ,attrs, addr);
+ } else {
+ ALOGE("%s: mCallbacksObj is null", __FUNCTION__);
+ }
+ sCallbackEnv->DeleteLocalRef(attrs);
+ sCallbackEnv->DeleteLocalRef(addr);
+}
+
+static void btavrcp_getPlayer_app_value_text(uint8_t attr_id , uint8_t num_val , uint8_t *value,
+ bt_bdaddr_t* bd_addr)
+{
+ jbyteArray Attr_Value ;
+ ALOGV("%s", __FUNCTION__);
+ jbyteArray addr;
+
+ if (!checkCallbackThread()) {
+ ALOGE("Callback: '%s' is not called on the correct thread", __FUNCTION__);
+ return;
+ }
+ addr = sCallbackEnv->NewByteArray(sizeof(bt_bdaddr_t));
+ if (!addr) {
+ ALOGE("Fail to new jbyteArray bd addr for getPlayer app");
+ checkAndClearExceptionFromCallback(sCallbackEnv, __FUNCTION__);
+ return;
+ }
+ sCallbackEnv->SetByteArrayRegion(addr, 0, sizeof(bt_bdaddr_t), (jbyte*) bd_addr);
+ Attr_Value = (jbyteArray)sCallbackEnv->NewByteArray(num_val);
+ if (!Attr_Value) {
+ ALOGE("Fail to new jintArray for attrs");
+ checkAndClearExceptionFromCallback(sCallbackEnv, __FUNCTION__);
+ return;
+ }
+ sCallbackEnv->SetByteArrayRegion(Attr_Value, 0, num_val, (jbyte *)value);
+ if (mCallbacksObj) {
+ sCallbackEnv->CallVoidMethod(mCallbacksObj, method_getplayervalue_text,(jbyte) attr_id,
+ (jbyte) num_val , Attr_Value, addr);
+ } else {
+ ALOGE("%s: mCallbacksObj is null", __FUNCTION__);
+ }
+ sCallbackEnv->DeleteLocalRef(Attr_Value);
+}
+
+static void btavrcp_get_element_attr_callback(uint8_t num_attr, btrc_media_attr_t *p_attrs,
+ bt_bdaddr_t* bd_addr) {
+ jintArray attrs;
+ jbyteArray addr;
+
+ ALOGV("%s", __FUNCTION__);
+
+ if (!checkCallbackThread()) {
+ ALOGE("Callback: '%s' is not called on the correct thread", __FUNCTION__);
+ return;
+ }
+ addr = sCallbackEnv->NewByteArray(sizeof(bt_bdaddr_t));
+ if (!addr) {
+ ALOGE("Fail to new jbyteArray bd addr for element attr");
+ checkAndClearExceptionFromCallback(sCallbackEnv, __FUNCTION__);
+ return;
+ }
+ sCallbackEnv->SetByteArrayRegion(addr, 0, sizeof(bt_bdaddr_t), (jbyte*) bd_addr);
attrs = (jintArray)sCallbackEnv->NewIntArray(num_attr);
if (!attrs) {
ALOGE("Fail to new jintArray for attrs");
@@ -108,99 +348,428 @@
}
sCallbackEnv->SetIntArrayRegion(attrs, 0, num_attr, (jint *)p_attrs);
if (mCallbacksObj) {
- sCallbackEnv->CallVoidMethod(mCallbacksObj, method_getElementAttr, (jbyte)num_attr, attrs);
+ sCallbackEnv->CallVoidMethod(mCallbacksObj, method_getElementAttr, (jbyte)num_attr,
+ attrs, addr);
} else {
ALOGE("%s: mCallbacksObj is null", __FUNCTION__);
}
checkAndClearExceptionFromCallback(sCallbackEnv, __FUNCTION__);
sCallbackEnv->DeleteLocalRef(attrs);
+ sCallbackEnv->DeleteLocalRef(addr);
}
-static void btavrcp_register_notification_callback(btrc_event_id_t event_id, uint32_t param) {
+static void btavrcp_register_notification_callback(btrc_event_id_t event_id, uint32_t param,
+ bt_bdaddr_t* bd_addr) {
+ jbyteArray addr;
+ ALOGV("%s", __FUNCTION__);
+
if (!checkCallbackThread()) {
ALOGE("Callback: '%s' is not called on the correct thread", __FUNCTION__);
return;
}
+ addr = sCallbackEnv->NewByteArray(sizeof(bt_bdaddr_t));
+ if (!addr) {
+ ALOGE("Fail to new jbyteArray bd addr for register notification");
+ checkAndClearExceptionFromCallback(sCallbackEnv, __FUNCTION__);
+ return;
+ }
+ sCallbackEnv->SetByteArrayRegion(addr, 0, sizeof(bt_bdaddr_t), (jbyte*) bd_addr);
if (mCallbacksObj) {
sCallbackEnv->CallVoidMethod(mCallbacksObj, method_registerNotification,
- (jint)event_id, (jint)param);
+ (jint)event_id, (jint)param, addr);
} else {
ALOGE("%s: mCallbacksObj is null", __FUNCTION__);
}
checkAndClearExceptionFromCallback(sCallbackEnv, __FUNCTION__);
+ sCallbackEnv->DeleteLocalRef(addr);
}
-static void btavrcp_volume_change_callback(uint8_t volume, uint8_t ctype) {
- ALOGI("%s", __FUNCTION__);
+static void btavrcp_volume_change_callback(uint8_t volume, uint8_t ctype, bt_bdaddr_t* bd_addr) {
+
+ jbyteArray addr;
+ ALOGV("%s", __FUNCTION__);
if (!checkCallbackThread()) {
ALOGE("Callback: '%s' is not called on the correct thread", __FUNCTION__);
return;
}
+ addr = sCallbackEnv->NewByteArray(sizeof(bt_bdaddr_t));
+ if (!addr) {
+ ALOGE("Fail to new jbyteArray bd addr for volume change");
+ checkAndClearExceptionFromCallback(sCallbackEnv, __FUNCTION__);
+ return;
+ }
+ sCallbackEnv->SetByteArrayRegion(addr, 0, sizeof(bt_bdaddr_t), (jbyte*) bd_addr);
if (mCallbacksObj) {
sCallbackEnv->CallVoidMethod(mCallbacksObj, method_volumeChangeCallback, (jint)volume,
- (jint)ctype);
+ (jint)ctype, addr);
} else {
ALOGE("%s: mCallbacksObj is null", __FUNCTION__);
}
checkAndClearExceptionFromCallback(sCallbackEnv, __FUNCTION__);
+ sCallbackEnv->DeleteLocalRef(addr);
}
-static void btavrcp_passthrough_command_callback(int id, int pressed) {
- ALOGI("%s", __FUNCTION__);
+static void btavrcp_get_folder_items_callback(btrc_browse_folderitem_t scope ,
+ btrc_getfolderitem_t *param,
+ bt_bdaddr_t* bd_addr) {
+ jlong start = param->start_item;
+ jlong end = param->end_item;
+ jint size = param->size;
+ jint num_attr = param->attr_count;
+ jintArray attrs;
+ jbyteArray addr;
if (!checkCallbackThread()) {
ALOGE("Callback: '%s' is not called on the correct thread", __FUNCTION__);
return;
}
+ addr = sCallbackEnv->NewByteArray(sizeof(bt_bdaddr_t));
+ if (!addr) {
+ ALOGE("Fail to new jbyteArray bd addr for get folder items");
+ checkAndClearExceptionFromCallback(sCallbackEnv, __FUNCTION__);
+ return;
+ }
+ sCallbackEnv->SetByteArrayRegion(addr, 0, sizeof(bt_bdaddr_t), (jbyte*) bd_addr);
+ if (num_attr == 0xff) {
+ num_attr = 0; // 0xff signifies no attribute required in response
+ } else if (num_attr == 0) {
+ num_attr = 8; // 0x00 signifies all attributes required in response
+ }
+
+ attrs = (jintArray)sCallbackEnv->NewIntArray(num_attr);
+ if (!attrs) {
+ ALOGE("Fail to new jintArray for attrs");
+ checkAndClearExceptionFromCallback(sCallbackEnv, __FUNCTION__);
+ return;
+ }
+ sCallbackEnv->SetIntArrayRegion(attrs, 0, num_attr, (jint *)param->attrs);
+
+ ALOGV("%s", __FUNCTION__);
+ ALOGI("scope: %d", scope);
+ ALOGI("start entry: %d", start);
+ ALOGI("end entry: %d", end);
+ ALOGI("size: %d", size);
+
if (mCallbacksObj) {
- sCallbackEnv->CallVoidMethod(mCallbacksObj, method_handlePassthroughCmd,
- (jint)id, (jint)pressed);
+ sCallbackEnv->CallVoidMethod(mCallbacksObj, method_getFolderItems, (jbyte)scope,
+ start, end, size, num_attr, attrs, addr);
+ } else {
+ ALOGE("%s: mCallbacksObj is null", __FUNCTION__);
+ }
+
+ checkAndClearExceptionFromCallback(sCallbackEnv, __FUNCTION__);
+ sCallbackEnv->DeleteLocalRef(attrs);
+ sCallbackEnv->DeleteLocalRef(addr);
+}
+
+static void btavrcp_passthrough_command_callback(int id, int pressed , bt_bdaddr_t* bd_addr) {
+ jbyteArray addr;
+ ALOGV("%s", __FUNCTION__);
+
+ if (!checkCallbackThread()) {
+ ALOGE("Callback: '%s' is not called on the correct thread", __FUNCTION__);
+ return;
+ }
+ addr = sCallbackEnv->NewByteArray(sizeof(bt_bdaddr_t));
+ if (!addr) {
+ ALOGE("Fail to new jbyteArray bd addr for passthrough command");
+ checkAndClearExceptionFromCallback(sCallbackEnv, __FUNCTION__);
+ return;
+ }
+ sCallbackEnv->SetByteArrayRegion(addr, 0, sizeof(bt_bdaddr_t), (jbyte*) bd_addr);
+ if (mCallbacksObj) {
+ sCallbackEnv->CallVoidMethod(mCallbacksObj, method_handlePassthroughCmd, (jint)id,
+ (jint)pressed, addr);
} else {
ALOGE("%s: mCallbacksObj is null", __FUNCTION__);
}
checkAndClearExceptionFromCallback(sCallbackEnv, __FUNCTION__);
+ sCallbackEnv->DeleteLocalRef(addr);
+}
+
+static void btavrcp_set_addressed_player_callback(uint32_t player_id, bt_bdaddr_t* bd_addr) {
+ jbyteArray addr;
+ ALOGV("%s", __FUNCTION__);
+ ALOGV("player id: %d", player_id);
+
+ if (!checkCallbackThread()) {
+ ALOGE("Callback: '%s' is not called on the correct thread", __FUNCTION__);
+ return;
+ }
+ addr = sCallbackEnv->NewByteArray(sizeof(bt_bdaddr_t));
+ if (!addr) {
+ ALOGE("Fail to new jbyteArray bd addr for set addressed player");
+ checkAndClearExceptionFromCallback(sCallbackEnv, __FUNCTION__);
+ return;
+ }
+ sCallbackEnv->SetByteArrayRegion(addr, 0, sizeof(bt_bdaddr_t), (jbyte*) bd_addr);
+
+ if (mCallbacksObj) {
+ sCallbackEnv->CallVoidMethod(mCallbacksObj, method_setAddressedPlayer, (jint)player_id,
+ addr);
+ } else {
+ ALOGE("%s: mCallbacksObj is null", __FUNCTION__);
+ }
+
+ checkAndClearExceptionFromCallback(sCallbackEnv, __FUNCTION__);
+ sCallbackEnv->DeleteLocalRef(addr);
+}
+
+static void btavrcp_set_browsed_player_callback(uint32_t player_id, bt_bdaddr_t* bd_addr) {
+ jbyteArray addr;
+ ALOGV("%s", __FUNCTION__);
+ ALOGV("player id: %d", player_id);
+
+ if (!checkCallbackThread()) {
+ ALOGE("Callback: '%s' is not called on the correct thread", __FUNCTION__);
+ return;
+ }
+ addr = sCallbackEnv->NewByteArray(sizeof(bt_bdaddr_t));
+ if (!addr) {
+ ALOGE("Fail to new jbyteArray bd addr for set browsed player");
+ checkAndClearExceptionFromCallback(sCallbackEnv, __FUNCTION__);
+ return;
+ }
+ sCallbackEnv->SetByteArrayRegion(addr, 0, sizeof(bt_bdaddr_t), (jbyte*) bd_addr);
+ if (mCallbacksObj) {
+ sCallbackEnv->CallVoidMethod(mCallbacksObj, method_setBrowsedPlayer, (jint)player_id,
+ addr);
+ } else {
+ ALOGE("%s: mCallbacksObj is null", __FUNCTION__);
+ }
+ checkAndClearExceptionFromCallback(sCallbackEnv, __FUNCTION__);
+ sCallbackEnv->DeleteLocalRef(addr);
+}
+
+static void btavrcp_change_path_callback(uint8_t direction, uint64_t uid, bt_bdaddr_t* bd_addr) {
+ jbyteArray addr;
+ ALOGV("%s", __FUNCTION__);
+ ALOGV("direction: %d, uid: %lu", direction, uid);
+
+ if (!checkCallbackThread()) {
+ ALOGE("Callback: '%s' is not called on the correct thread", __FUNCTION__);
+ return;
+ }
+ addr = sCallbackEnv->NewByteArray(sizeof(bt_bdaddr_t));
+ if (!addr) {
+ ALOGE("Fail to new jbyteArray bd addr for change path");
+ checkAndClearExceptionFromCallback(sCallbackEnv, __FUNCTION__);
+ return;
+ }
+ sCallbackEnv->SetByteArrayRegion(addr, 0, sizeof(bt_bdaddr_t), (jbyte*) bd_addr);
+ if (mCallbacksObj) {
+ sCallbackEnv->CallVoidMethod(mCallbacksObj, method_changePath, (jbyte)direction,
+ (jlong)uid, addr);
+ } else {
+ ALOGE("%s: mCallbacksObj is null", __FUNCTION__);
+ }
+ checkAndClearExceptionFromCallback(sCallbackEnv, __FUNCTION__);
+ sCallbackEnv->DeleteLocalRef(addr);
+}
+
+static void btavrcp_play_item_callback(uint8_t scope, uint64_t uid, bt_bdaddr_t* bd_addr) {
+ jbyteArray addr;
+ ALOGV("%s", __FUNCTION__);
+ ALOGV("scope: %d, uid: %lu", scope, uid);
+
+ if (!checkCallbackThread()) {
+ ALOGE("Callback: '%s' is not called on the correct thread", __FUNCTION__);
+ return;
+ }
+ addr = sCallbackEnv->NewByteArray(sizeof(bt_bdaddr_t));
+ if (!addr) {
+ ALOGE("Fail to new jbyteArray bd addr for play item");
+ checkAndClearExceptionFromCallback(sCallbackEnv, __FUNCTION__);
+ return;
+ }
+ sCallbackEnv->SetByteArrayRegion(addr, 0, sizeof(bt_bdaddr_t), (jbyte*) bd_addr);
+ if (mCallbacksObj) {
+ sCallbackEnv->CallVoidMethod(mCallbacksObj, method_playItem, (jbyte)scope, (jlong)uid,
+ addr);
+ } else {
+ ALOGE("%s: mCallbacksObj is null", __FUNCTION__);
+ }
+
+ checkAndClearExceptionFromCallback(sCallbackEnv, __FUNCTION__);
+ sCallbackEnv->DeleteLocalRef(addr);
+}
+
+static void btavrcp_get_item_attr_callback(uint8_t scope, uint64_t uid,
+ uint8_t num_attr, btrc_media_attr_t *p_attrs,
+ uint32_t size, bt_bdaddr_t* bd_addr) {
+ jintArray attrs;
+ jbyteArray addr;
+
+ if (num_attr == 0xff) {
+ num_attr = 0; // 0xff signifies no attribute required in response
+ } else if (num_attr == 0) {
+ num_attr = 8; // 0x00 signifies all attributes required in response
+ }
+
+ ALOGV("%s", __FUNCTION__);
+
+ if (!checkCallbackThread()) {
+ ALOGE("Callback: '%s' is not called on the correct thread", __FUNCTION__);
+ return;
+ }
+ addr = sCallbackEnv->NewByteArray(sizeof(bt_bdaddr_t));
+ if (!addr) {
+ ALOGE("Fail to new jbyteArray bd addr for get item attr");
+ checkAndClearExceptionFromCallback(sCallbackEnv, __FUNCTION__);
+ return;
+ }
+ sCallbackEnv->SetByteArrayRegion(addr, 0, sizeof(bt_bdaddr_t), (jbyte*) bd_addr);
+ attrs = (jintArray)sCallbackEnv->NewIntArray(num_attr);
+ if (!attrs) {
+ ALOGE("Fail to new jintArray for attrs");
+ checkAndClearExceptionFromCallback(sCallbackEnv, __FUNCTION__);
+ return;
+ }
+ sCallbackEnv->SetIntArrayRegion(attrs, 0, num_attr, (jint *)p_attrs);
+ if (mCallbacksObj) {
+ sCallbackEnv->CallVoidMethod(mCallbacksObj, method_getItemAttr, (jbyte)scope, (jlong)uid,
+ (jbyte)num_attr, attrs, (jint)size, addr);
+ } else {
+ ALOGE("%s: mCallbacksObj is null", __FUNCTION__);
+ }
+
+ checkAndClearExceptionFromCallback(sCallbackEnv, __FUNCTION__);
+ sCallbackEnv->DeleteLocalRef(attrs);
+ sCallbackEnv->DeleteLocalRef(addr);
+}
+
+static void btavrcp_connection_state_callback(bool state, bt_bdaddr_t* bd_addr) {
+ jbyteArray addr;
+
+ ALOGI("%s", __FUNCTION__);
+ ALOGI("conn state: %d", state);
+
+ if (!checkCallbackThread()) {
+ ALOGE("Callback: '%s' is not called on the correct thread", __FUNCTION__);
+ return;
+ }
+
+ addr = sCallbackEnv->NewByteArray(sizeof(bt_bdaddr_t));
+ if (!addr) {
+ ALOGE("Fail to new jbyteArray bd addr for connection state");
+ checkAndClearExceptionFromCallback(sCallbackEnv, __FUNCTION__);
+ return;
+ }
+
+ sCallbackEnv->SetByteArrayRegion(addr, 0, sizeof(bt_bdaddr_t), (jbyte*) bd_addr);
+ if (mCallbacksObj) {
+ sCallbackEnv->CallVoidMethod(mCallbacksObj, method_onConnectionStateChanged,
+ (jboolean) state, addr);
+ } else {
+ ALOGE("%s: mCallbacksObj is null", __FUNCTION__);
+ }
+ checkAndClearExceptionFromCallback(sCallbackEnv, __FUNCTION__);
+ sCallbackEnv->DeleteLocalRef(addr);
+}
+
+static void btavrcp_get_total_items_callback(uint8_t scope, bt_bdaddr_t* bd_addr) {
+ jbyteArray addr;
+
+ ALOGI("%s", __FUNCTION__);
+ ALOGI("scope: %d", scope);
+
+ if (!checkCallbackThread()) {
+ ALOGE("Callback: '%s' is not called on the correct thread", __FUNCTION__);
+ return;
+ }
+
+ addr = sCallbackEnv->NewByteArray(sizeof(bt_bdaddr_t));
+ if (!addr) {
+ ALOGE("Fail to new jbyteArray bd addr for connection state");
+ checkAndClearExceptionFromCallback(sCallbackEnv, __FUNCTION__);
+ return;
+ }
+
+ sCallbackEnv->SetByteArrayRegion(addr, 0, sizeof(bt_bdaddr_t), (jbyte*) bd_addr);
+ if (mCallbacksObj) {
+ sCallbackEnv->CallVoidMethod(mCallbacksObj, method_getTotalNumberOfItems,
+ (jbyte)scope, addr);
+ } else {
+ ALOGE("%s: mCallbacksObj is null", __FUNCTION__);
+ }
+ checkAndClearExceptionFromCallback(sCallbackEnv, __FUNCTION__);
+ sCallbackEnv->DeleteLocalRef(addr);
}
static btrc_callbacks_t sBluetoothAvrcpCallbacks = {
sizeof(sBluetoothAvrcpCallbacks),
btavrcp_remote_features_callback,
btavrcp_get_play_status_callback,
- NULL,
- NULL,
- NULL,
- NULL,
- NULL,
- NULL,
+ btavrcp_get_player_attribute_id_callback,
+ btavrcp_get_player_seeting_value_callback,
+ btavrcp_getcurrent_player_app_setting_values,
+ btavrcp_getPlayer_app_attribute_text,
+ btavrcp_getPlayer_app_value_text,
+ btavrcp_set_playerapp_setting_value_callback,
btavrcp_get_element_attr_callback,
btavrcp_register_notification_callback,
btavrcp_volume_change_callback,
btavrcp_passthrough_command_callback,
+ btavrcp_get_folder_items_callback,
+ btavrcp_set_addressed_player_callback,
+ btavrcp_set_browsed_player_callback,
+ btavrcp_change_path_callback,
+ btavrcp_play_item_callback,
+ btavrcp_get_item_attr_callback,
+ btavrcp_connection_state_callback,
+ btavrcp_get_total_items_callback
};
static void classInitNative(JNIEnv* env, jclass clazz) {
method_getRcFeatures =
env->GetMethodID(clazz, "getRcFeatures", "([BI)V");
method_getPlayStatus =
- env->GetMethodID(clazz, "getPlayStatus", "()V");
-
+ env->GetMethodID(clazz, "getPlayStatus", "([B)V");
+ method_onListPlayerAttributeRequest =
+ env->GetMethodID(clazz , "onListPlayerAttributeRequest" , "([B)V");
+ method_onListPlayerAttributeValues =
+ env->GetMethodID(clazz , "onListPlayerAttributeValues" , "(B[B)V");
method_getElementAttr =
- env->GetMethodID(clazz, "getElementAttr", "(B[I)V");
-
+ env->GetMethodID(clazz, "getElementAttr", "(B[I[B)V");
+ method_setPlayerAppSetting =
+ env->GetMethodID(clazz, "setPlayerAppSetting","(B[B[B[B)V");
+ method_getplayerattribute_text =
+ env->GetMethodID(clazz, "getplayerattribute_text" , "(B[B[B)V");
+ method_getplayervalue_text =
+ env->GetMethodID(clazz, "getplayervalue_text" , "(BB[B[B)V");
method_registerNotification =
- env->GetMethodID(clazz, "registerNotification", "(II)V");
-
+ env->GetMethodID(clazz, "registerNotification", "(II[B)V");
+ method_onGetPlayerAttributeValues =
+ env->GetMethodID(clazz, "onGetPlayerAttributeValues", "(B[I[B)V");
method_volumeChangeCallback =
- env->GetMethodID(clazz, "volumeChangeCallback", "(II)V");
-
+ env->GetMethodID(clazz, "volumeChangeCallback", "(II[B)V");
method_handlePassthroughCmd =
- env->GetMethodID(clazz, "handlePassthroughCmd", "(II)V");
-
- ALOGI("%s: succeeds", __FUNCTION__);
+ env->GetMethodID(clazz, "handlePassthroughCmd", "(II[B)V");
+ //setAddressedPlayer: attributes to pass: Player ID
+ method_setAddressedPlayer =
+ env->GetMethodID(clazz, "setAddressedPlayer", "(I[B)V");
+ //getFolderItems: attributes to pass: Scope, Start, End, Attr Cnt
+ method_getFolderItems =
+ env->GetMethodID(clazz, "getFolderItems", "(BJJII[I[B)V");
+ method_setBrowsedPlayer =
+ env->GetMethodID(clazz, "setBrowsedPlayer", "(I[B)V");
+ method_changePath =
+ env->GetMethodID(clazz, "changePath", "(BJ[B)V");
+ method_playItem =
+ env->GetMethodID(clazz, "playItem", "(BJ[B)V");
+ method_getItemAttr =
+ env->GetMethodID(clazz, "getItemAttr", "(BJB[II[B)V");
+ method_onConnectionStateChanged =
+ env->GetMethodID(clazz, "onConnectionStateChanged", "(Z[B)V");
+ method_getTotalNumberOfItems =
+ env->GetMethodID(clazz, "getTotalNumberOfItems", "(B[B)V");
+ ALOGV("%s: succeeds", __FUNCTION__);
}
-static void initNative(JNIEnv *env, jobject object) {
+static void initNative(JNIEnv *env, jobject object,
+ jint maxAvrcpConnections) {
const bt_interface_t* btInf;
bt_status_t status;
@@ -209,10 +778,10 @@
return;
}
- if (sBluetoothAvrcpInterface !=NULL) {
+ if (sBluetoothMultiAvrcpInterface !=NULL) {
ALOGW("Cleaning up Avrcp Interface before initializing...");
- sBluetoothAvrcpInterface->cleanup();
- sBluetoothAvrcpInterface = NULL;
+ sBluetoothMultiAvrcpInterface->cleanup();
+ sBluetoothMultiAvrcpInterface = NULL;
}
if (mCallbacksObj != NULL) {
@@ -221,16 +790,17 @@
mCallbacksObj = NULL;
}
- if ( (sBluetoothAvrcpInterface = (btrc_interface_t *)
+ if ( (sBluetoothMultiAvrcpInterface = (btrc_interface_t *)
btInf->get_profile_interface(BT_PROFILE_AV_RC_ID)) == NULL) {
ALOGE("Failed to get Bluetooth Avrcp Interface");
return;
}
- if ( (status = sBluetoothAvrcpInterface->init(&sBluetoothAvrcpCallbacks)) !=
+ if ((status = sBluetoothMultiAvrcpInterface->init(&sBluetoothAvrcpCallbacks,
+ maxAvrcpConnections)) !=
BT_STATUS_SUCCESS) {
ALOGE("Failed to initialize Bluetooth Avrcp, status: %d", status);
- sBluetoothAvrcpInterface = NULL;
+ sBluetoothMultiAvrcpInterface = NULL;
return;
}
@@ -245,9 +815,9 @@
return;
}
- if (sBluetoothAvrcpInterface !=NULL) {
- sBluetoothAvrcpInterface->cleanup();
- sBluetoothAvrcpInterface = NULL;
+ if (sBluetoothMultiAvrcpInterface !=NULL) {
+ sBluetoothMultiAvrcpInterface->cleanup();
+ sBluetoothMultiAvrcpInterface = NULL;
}
if (mCallbacksObj != NULL) {
@@ -257,31 +827,348 @@
}
static jboolean getPlayStatusRspNative(JNIEnv *env, jobject object, jint playStatus,
- jint songLen, jint songPos) {
+ jint songLen, jint songPos, jbyteArray address) {
bt_status_t status;
+ jbyte *addr;
- ALOGI("%s: sBluetoothAvrcpInterface: %p", __FUNCTION__, sBluetoothAvrcpInterface);
- if (!sBluetoothAvrcpInterface) return JNI_FALSE;
+ ALOGV("%s: sBluetoothMultiAvrcpInterface: %p", __FUNCTION__, sBluetoothMultiAvrcpInterface);
+ if (!sBluetoothMultiAvrcpInterface) return JNI_FALSE;
- if ((status = sBluetoothAvrcpInterface->get_play_status_rsp((btrc_play_status_t)playStatus,
- songLen, songPos)) != BT_STATUS_SUCCESS) {
- ALOGE("Failed get_play_status_rsp, status: %d", status);
+ addr = env->GetByteArrayElements(address, NULL);
+ if (!addr) {
+ jniThrowIOException(env, EINVAL);
+ return JNI_FALSE;
}
+ if ((status = sBluetoothMultiAvrcpInterface->get_play_status_rsp((btrc_play_status_t)playStatus,
+ songLen, songPos,
+ (bt_bdaddr_t *)addr)) != 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 getListPlayerappAttrRspNative(JNIEnv *env ,jobject object , jbyte numAttr,
+ jbyteArray attrIds , jbyteArray address) {
+ bt_status_t status;
+ jbyte *addr;
+ btrc_player_attr_t *pAttrs = NULL;
+ int i;
+ jbyte *attr;
+
+ if (!sBluetoothMultiAvrcpInterface) return JNI_FALSE;
+
+ addr = env->GetByteArrayElements(address, NULL);
+ if (!addr) {
+ jniThrowIOException(env, EINVAL);
+ return JNI_FALSE;
+ }
+
+ if( numAttr > BTRC_MAX_APP_ATTR_SIZE) {
+ ALOGE("get_element_attr_rsp: number of attributes exceed maximum");
+ return JNI_FALSE;
+ }
+ ALOGI("getListPlayerappAttrRspNative");
+ pAttrs = new btrc_player_attr_t[numAttr];
+ if (!pAttrs) {
+ ALOGE("getListPlayerappAttrRspNative: not have enough memeory");
+ return JNI_FALSE;
+ }
+ attr = env->GetByteArrayElements(attrIds, NULL);
+ if( !attr) {
+ delete[] pAttrs;
+ jniThrowIOException(env, EINVAL);
+ return JNI_FALSE ;
+ }
+ for (i = 0; i < numAttr; ++i) {
+ pAttrs[i] = (btrc_player_attr_t)attr[i];
+ }
+ if (i < numAttr) {
+ delete[] pAttrs;
+ env->ReleaseByteArrayElements(attrIds, attr, 0);
+ return JNI_FALSE;
+ }
+ //Call Stack Method
+ if ((status = sBluetoothMultiAvrcpInterface->list_player_app_attr_rsp(numAttr, pAttrs,
+ (bt_bdaddr_t *)addr)) != BT_STATUS_SUCCESS) {
+ ALOGE("Failed list_player_app_attr_rsp, status: %d", status);
+ }
+ delete[] pAttrs;
+ env->ReleaseByteArrayElements(attrIds, attr, 0);
+ env->ReleaseByteArrayElements(address, addr, 0);
+ return (status == BT_STATUS_SUCCESS) ? JNI_TRUE : JNI_FALSE;
+}
+
+
+static jboolean getPlayerAppValueRspNative(JNIEnv *env ,jobject object , jbyte numvalue,
+ jbyteArray value ,jbyteArray address)
+{
+ bt_status_t status;
+ jbyte *addr;
+ uint8_t *pAttrs = NULL;
+ int i;
+ jbyte *attr;
+
+ if (!sBluetoothMultiAvrcpInterface) return JNI_FALSE;
+
+ if( numvalue > BTRC_MAX_APP_ATTR_SIZE) {
+ ALOGE("get_element_attr_rsp: number of attributes exceed maximum");
+ return JNI_FALSE;
+ }
+ pAttrs = new uint8_t[numvalue];
+ /* Klockwork Fix for below
+ * Possible memory leak. Dynamic memory stored in 'pAttrs' allocated
+ * through function 'new[]' at line 887 can be lost at line 897*/
+ if (!pAttrs) {
+ ALOGE("getPlayerAppValueRspNative: not have enough memeory");
+ return JNI_FALSE;
+ }
+ attr = env->GetByteArrayElements(value, NULL);
+
+ addr = env->GetByteArrayElements(address, NULL);
+ if (!addr) {
+ delete[] pAttrs;
+ jniThrowIOException(env, EINVAL);
+ return JNI_FALSE;
+ }
+
+ if (!attr) {
+ delete[] pAttrs;
+ jniThrowIOException(env, EINVAL);
+ return JNI_FALSE;
+ }
+ for (i = 0; i < numvalue; ++i) {
+ pAttrs[i] = (uint8_t)attr[i];
+ }
+ if (i < numvalue) {
+ delete[] pAttrs;
+ env->ReleaseByteArrayElements(value, attr, 0);
+ return JNI_FALSE;
+ }
+ if ((status = sBluetoothMultiAvrcpInterface->list_player_app_value_rsp(numvalue, pAttrs,
+ (bt_bdaddr_t *)addr)) != BT_STATUS_SUCCESS) {
+ ALOGE("Failed list_player_app_value_rsp, status: %d", status);
+ }
+ delete[] pAttrs;
+ env->ReleaseByteArrayElements(value, attr, 0);
+ env->ReleaseByteArrayElements(address, addr, 0);
+ return (status == BT_STATUS_SUCCESS) ? JNI_TRUE : JNI_FALSE;
+}
+
+static jboolean SendCurrentPlayerValueRspNative(JNIEnv *env, jobject object ,
+ jbyte numattr ,jbyteArray value ,jbyteArray address) {
+ btrc_player_settings_t *pAttrs = NULL ;
+ bt_status_t status;
+ jbyte *addr;
+ int i;
+ jbyte *attr;
+
+ if (!sBluetoothMultiAvrcpInterface) return JNI_FALSE;
+
+ addr = env->GetByteArrayElements(address, NULL);
+ if (!addr) {
+ jniThrowIOException(env, EINVAL);
+ return JNI_FALSE;
+ }
+ if( numattr > BTRC_MAX_APP_ATTR_SIZE || numattr == 0) {
+ ALOGE("SendCurrentPlayerValueRspNative: number of attributes exceed maximum");
+ return JNI_FALSE;
+ }
+ pAttrs = new btrc_player_settings_t;
+ if (!pAttrs) {
+ ALOGE("SendCurrentPlayerValueRspNative: not have enough memeory");
+ return JNI_FALSE;
+ }
+ attr = env->GetByteArrayElements(value, NULL);
+ if (!attr) {
+ delete pAttrs;
+ jniThrowIOException(env, EINVAL);
+ return JNI_FALSE;
+ }
+ pAttrs->num_attr = numattr/2 ;
+ for(i =0 ; i < numattr; i+=2)
+ {
+ pAttrs->attr_ids[i/2] = attr[i];
+ pAttrs->attr_values[i/2] = attr[i+1];
+ }
+ if ((status = sBluetoothMultiAvrcpInterface->get_player_app_value_rsp(pAttrs,
+ (bt_bdaddr_t *)addr)) != BT_STATUS_SUCCESS) {
+ ALOGE("Failed get_player_app_value_rsp, status: %d", status);
+ }
+ delete pAttrs;
+ env->ReleaseByteArrayElements(value, attr, 0);
+ env->ReleaseByteArrayElements(address, addr, 0);
+ return (status == BT_STATUS_SUCCESS) ? JNI_TRUE : JNI_FALSE;
+}
+
+
+//JNI Method called to Respond to PDU 0x14
+static jboolean SendSetPlayerAppRspNative(JNIEnv *env, jobject object,
+ jint attr_status, jbyteArray address)
+{
+ bt_status_t status;
+ jbyte *addr;
+ btrc_status_t player_rsp = (btrc_status_t) attr_status;
+
+ if (!sBluetoothMultiAvrcpInterface) return JNI_FALSE;
+
+ addr = env->GetByteArrayElements(address, NULL);
+ if (!addr) {
+ jniThrowIOException(env, EINVAL);
+ return JNI_FALSE;
+ }
+ if ((status = sBluetoothMultiAvrcpInterface->set_player_app_value_rsp(player_rsp,
+ (bt_bdaddr_t *)addr)) != BT_STATUS_SUCCESS) {
+ ALOGE("Failed set_player_app_value_rsp, status: %d", status);
+ }
+ env->ReleaseByteArrayElements(address, addr, 0);
+ return (status == BT_STATUS_SUCCESS) ? JNI_TRUE : JNI_FALSE;
+}
+
+
+//JNI Method Called to Respond to PDU 0x15
+static jboolean sendSettingsTextRspNative(JNIEnv *env, jobject object, jint num_attr,
+ jbyteArray attr,jint length , jobjectArray textArray,
+ jbyteArray address) {
+ btrc_player_setting_text_t *pAttrs = NULL;
+ bt_status_t status;
+ jbyte *addr;
+ int i;
+ jstring text;
+ const char* textStr;
+ jbyte *arr ;
+
+ if (!sBluetoothMultiAvrcpInterface) return JNI_FALSE;
+
+ addr = env->GetByteArrayElements(address, NULL);
+ if (!addr) {
+ jniThrowIOException(env, EINVAL);
+ return JNI_FALSE;
+ }
+ if (num_attr > BTRC_MAX_ELEM_ATTR_SIZE) {
+ ALOGE("get_element_attr_rsp: number of attributes exceed maximum");
+ return JNI_FALSE;
+ }
+ pAttrs = new btrc_player_setting_text_t[num_attr];
+ if (!pAttrs) {
+ ALOGE("sendSettingsTextRspNative: not have enough memeory");
+ return JNI_FALSE;
+ }
+ arr = env->GetByteArrayElements(attr, NULL);
+ if (!arr) {
+ delete[] pAttrs;
+ jniThrowIOException(env, EINVAL);
+ return JNI_FALSE;
+ }
+ for (i = 0; i < num_attr ; ++i) {
+ text = (jstring) env->GetObjectArrayElement(textArray, i);
+ textStr = env->GetStringUTFChars(text, NULL);
+ if (!textStr) {
+ ALOGE("get_element_attr_rsp: GetStringUTFChars return NULL");
+ env->DeleteLocalRef(text);
+ break;
+ }
+ pAttrs[i].id = arr[i];
+ if (strlen(textStr) >= BTRC_MAX_ATTR_STR_LEN) {
+ ALOGE("sendSettingsTextRspNative: string length exceed maximum");
+ }
+ strlcpy((char *)pAttrs[i].text, textStr, BTRC_MAX_ATTR_STR_LEN);
+ //Check out if release need to be done in for loop
+ env->ReleaseStringUTFChars(text, textStr);
+ env->DeleteLocalRef(text);
+ }
+ //Call Stack Methos to Respond PDU 0x16
+ if ((status = sBluetoothMultiAvrcpInterface->get_player_app_attr_text_rsp(num_attr, pAttrs,
+ (bt_bdaddr_t *)addr)) != BT_STATUS_SUCCESS) {
+ ALOGE("Failed get_player_app_attr_text_rsp, status: %d", status);
+ }
+ delete[] pAttrs;
+ env->ReleaseByteArrayElements(attr, arr, 0);
+ env->ReleaseByteArrayElements(address, addr, 0);
+ return (status == BT_STATUS_SUCCESS) ? JNI_TRUE : JNI_FALSE;
+}
+
+//JNI Method Called to respond to PDU 0x16
+static jboolean sendValueTextRspNative(JNIEnv *env, jobject object, jint num_attr,
+ jbyteArray attr, jint length , jobjectArray textArray,
+ jbyteArray address) {
+ btrc_player_setting_text_t *pAttrs = NULL;
+ bt_status_t status;
+ jbyte *addr;
+ int i;
+ jstring text ;
+ const char* textStr;
+ jbyte *arr ;
+
+ //ALOGE("sendValueTextRspNative");
+ if (!sBluetoothMultiAvrcpInterface) return JNI_FALSE;
+
+ addr = env->GetByteArrayElements(address, NULL);
+ if (!addr) {
+ jniThrowIOException(env, EINVAL);
+ return JNI_FALSE;
+ }
+ if (num_attr > BTRC_MAX_ELEM_ATTR_SIZE) {
+ ALOGE("sendValueTextRspNative: number of attributes exceed maximum");
+ return JNI_FALSE;
+ }
+ pAttrs = new btrc_player_setting_text_t[num_attr];
+ if (!pAttrs) {
+ ALOGE("sendValueTextRspNative: not have enough memeory");
+ return JNI_FALSE;
+ }
+ arr = env->GetByteArrayElements(attr, NULL);
+ if (!arr) {
+ delete[] pAttrs;
+ jniThrowIOException(env, EINVAL);
+ return JNI_FALSE;
+ }
+ for (i = 0; i < num_attr ; ++i) {
+ text = (jstring) env->GetObjectArrayElement(textArray, i);
+ textStr = env->GetStringUTFChars(text, NULL);
+ if (!textStr) {
+ ALOGE("sendValueTextRspNative: GetStringUTFChars return NULL");
+ env->DeleteLocalRef(text);
+ break;
+ }
+ pAttrs[i].id = arr[i];
+ if (strlen(textStr) >= BTRC_MAX_ATTR_STR_LEN) {
+ ALOGE("sendValueTextRspNative: string length exceed maximum");
+ }
+ strlcpy((char *)pAttrs[i].text, textStr, BTRC_MAX_ATTR_STR_LEN);
+ env->ReleaseStringUTFChars(text, textStr);
+ env->DeleteLocalRef(text);
+ }
+ //Call Stack Method to Respond to PDU 0x16
+ if ((status = sBluetoothMultiAvrcpInterface->get_player_app_value_text_rsp(num_attr, pAttrs,
+ (bt_bdaddr_t *)addr)) != BT_STATUS_SUCCESS) {
+ ALOGE("Failed get_player_app_value_text_rsp, status: %d", status);
+ }
+ delete[] pAttrs;
+ env->ReleaseByteArrayElements(attr, arr, 0);
+ env->ReleaseByteArrayElements(address, addr, 0);
return (status == BT_STATUS_SUCCESS) ? JNI_TRUE : JNI_FALSE;
}
static jboolean getElementAttrRspNative(JNIEnv *env, jobject object, jbyte numAttr,
- jintArray attrIds, jobjectArray textArray) {
+ jintArray attrIds, jobjectArray textArray,
+ jbyteArray address) {
jint *attr;
bt_status_t status;
+ jbyte *addr;
jstring text;
int i;
btrc_element_attr_val_t *pAttrs = NULL;
const char* textStr;
- if (!sBluetoothAvrcpInterface) return JNI_FALSE;
+ if (!sBluetoothMultiAvrcpInterface) return JNI_FALSE;
+ 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;
@@ -312,11 +1199,8 @@
pAttrs[i].attr_id = attr[i];
if (strlen(textStr) >= BTRC_MAX_ATTR_STR_LEN) {
ALOGE("get_element_attr_rsp: string length exceed maximum");
- strncpy((char *)pAttrs[i].text, textStr, BTRC_MAX_ATTR_STR_LEN-1);
- pAttrs[i].text[BTRC_MAX_ATTR_STR_LEN-1] = 0;
- } else {
- strcpy((char *)pAttrs[i].text, textStr);
}
+ strlcpy((char *)pAttrs[i].text, textStr, BTRC_MAX_ATTR_STR_LEN);
env->ReleaseStringUTFChars(text, textStr);
env->DeleteLocalRef(text);
}
@@ -327,43 +1211,113 @@
return JNI_FALSE;
}
- if ((status = sBluetoothAvrcpInterface->get_element_attr_rsp(numAttr, pAttrs)) !=
- BT_STATUS_SUCCESS) {
+ if ((status = sBluetoothMultiAvrcpInterface->get_element_attr_rsp(numAttr, pAttrs,
+ (bt_bdaddr_t *)addr)) != 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 registerNotificationPlayerAppRspNative(JNIEnv *env, jobject object ,jint type,
+ jbyte numattr ,jbyteArray value ,
+ jbyteArray address) {
+ bt_status_t status;
+ jbyte *addr;
+ int i;
+ jbyte *attr;
+ btrc_register_notification_t *param= NULL;
+
+ if (!sBluetoothMultiAvrcpInterface) return JNI_FALSE;
+
+ addr = env->GetByteArrayElements(address, NULL);
+ if (!addr) {
+ jniThrowIOException(env, EINVAL);
+ return JNI_FALSE;
+ }
+ if( numattr > BTRC_MAX_APP_ATTR_SIZE || numattr == 0) {
+ ALOGE("registerNotificationPlayerAppRspNative: number of attributes exceed maximum");
+ return JNI_FALSE;
+ }
+ param = new btrc_register_notification_t;
+
+ if (!param) {
+ ALOGE("registerNotificationPlayerAppRspNative: not have enough memeory");
+ return JNI_FALSE;
+ }
+ attr = env->GetByteArrayElements(value, NULL);
+ if (!attr) {
+ delete param;
+ jniThrowIOException(env, EINVAL);
+ return JNI_FALSE;
+ }
+ param->player_setting.num_attr = numattr/2;
+ for(i =0 ; i < numattr; i+=2)
+ {
+ param->player_setting.attr_ids[i/2] = attr[i];
+ param->player_setting.attr_values[i/2] = attr[i+1];
+ }
+ //Call Stack Method
+ if ((status =
+ sBluetoothMultiAvrcpInterface->register_notification_rsp(
+ BTRC_EVT_APP_SETTINGS_CHANGED,
+ (btrc_notification_type_t)type,param,
+ (bt_bdaddr_t *)addr)) != BT_STATUS_SUCCESS) {
+ ALOGE("Failed register_notification_rsp, status: %d", status);
+ }
+ delete param;
+ env->ReleaseByteArrayElements(value, 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) {
+ jint type, jint playStatus,
+ jbyteArray address) {
bt_status_t status;
+ jbyte *addr;
btrc_register_notification_t param;
- ALOGI("%s: sBluetoothAvrcpInterface: %p", __FUNCTION__, sBluetoothAvrcpInterface);
- if (!sBluetoothAvrcpInterface) return JNI_FALSE;
+ ALOGV("%s: sBluetoothMultiAvrcpInterface: %p", __FUNCTION__, sBluetoothMultiAvrcpInterface);
+ if (!sBluetoothMultiAvrcpInterface) return JNI_FALSE;
+ addr = env->GetByteArrayElements(address, NULL);
+ if (!addr) {
+ jniThrowIOException(env, EINVAL);
+ return JNI_FALSE;
+ }
param.play_status = (btrc_play_status_t)playStatus;
- if ((status = sBluetoothAvrcpInterface->register_notification_rsp(BTRC_EVT_PLAY_STATUS_CHANGED,
- (btrc_notification_type_t)type, ¶m)) != BT_STATUS_SUCCESS) {
+ if ((status =
+ sBluetoothMultiAvrcpInterface->register_notification_rsp(
+ BTRC_EVT_PLAY_STATUS_CHANGED,
+ (btrc_notification_type_t)type, ¶m, (bt_bdaddr_t *)addr)) !=
+ BT_STATUS_SUCCESS) {
ALOGE("Failed register_notification_rsp play status, status: %d", status);
}
-
+ env->ReleaseByteArrayElements(address, addr, 0);
return (status == BT_STATUS_SUCCESS) ? JNI_TRUE : JNI_FALSE;
}
static jboolean registerNotificationRspTrackChangeNative(JNIEnv *env, jobject object,
- jint type, jbyteArray track) {
+ jint type, jbyteArray track,
+ jbyteArray address) {
bt_status_t status;
+ jbyte *addr;
btrc_register_notification_t param;
jbyte *trk;
int i;
- ALOGI("%s: sBluetoothAvrcpInterface: %p", __FUNCTION__, sBluetoothAvrcpInterface);
- if (!sBluetoothAvrcpInterface) return JNI_FALSE;
+ ALOGV("%s: sBluetoothMultiAvrcpInterface: %p", __FUNCTION__, sBluetoothMultiAvrcpInterface);
+ if (!sBluetoothMultiAvrcpInterface) return JNI_FALSE;
+ addr = env->GetByteArrayElements(address, NULL);
+ if (!addr) {
+ jniThrowIOException(env, EINVAL);
+ return JNI_FALSE;
+ }
trk = env->GetByteArrayElements(track, NULL);
if (!trk) {
jniThrowIOException(env, EINVAL);
@@ -374,61 +1328,803 @@
param.track[i] = trk[i];
}
- if ((status = sBluetoothAvrcpInterface->register_notification_rsp(BTRC_EVT_TRACK_CHANGE,
- (btrc_notification_type_t)type, ¶m)) != BT_STATUS_SUCCESS) {
+ if ((status = sBluetoothMultiAvrcpInterface->register_notification_rsp(BTRC_EVT_TRACK_CHANGE,
+ (btrc_notification_type_t)type, ¶m, (bt_bdaddr_t *)addr)) != BT_STATUS_SUCCESS) {
ALOGE("Failed register_notification_rsp track change, status: %d", status);
}
env->ReleaseByteArrayElements(track, trk, 0);
+ env->ReleaseByteArrayElements(address, addr, 0);
return (status == BT_STATUS_SUCCESS) ? JNI_TRUE : JNI_FALSE;
}
static jboolean registerNotificationRspPlayPosNative(JNIEnv *env, jobject object,
- jint type, jint playPos) {
+ jint type, jint playPos,
+ jbyteArray address) {
bt_status_t status;
+ jbyte *addr;
+
btrc_register_notification_t param;
- if (!sBluetoothAvrcpInterface) return JNI_FALSE;
+ ALOGV("%s: sBluetoothMultiAvrcpInterface: %p", __FUNCTION__, sBluetoothMultiAvrcpInterface);
+ if (!sBluetoothMultiAvrcpInterface) return JNI_FALSE;
+ addr = env->GetByteArrayElements(address, NULL);
+ if (!addr) {
+ jniThrowIOException(env, EINVAL);
+ return JNI_FALSE;
+ }
param.song_pos = (uint32_t)playPos;
- if ((status = sBluetoothAvrcpInterface->register_notification_rsp(BTRC_EVT_PLAY_POS_CHANGED,
- (btrc_notification_type_t)type, ¶m)) != BT_STATUS_SUCCESS) {
+ if ((status = sBluetoothMultiAvrcpInterface->register_notification_rsp(
+ BTRC_EVT_PLAY_POS_CHANGED,
+ (btrc_notification_type_t)type, ¶m, (bt_bdaddr_t *)addr)) !=
+ BT_STATUS_SUCCESS) {
ALOGE("Failed register_notification_rsp play position, status: %d", status);
}
-
+ env->ReleaseByteArrayElements(address, addr, 0);
return (status == BT_STATUS_SUCCESS) ? JNI_TRUE : JNI_FALSE;
}
-static jboolean setVolumeNative(JNIEnv *env, jobject object, jint volume) {
+static jboolean setVolumeNative(JNIEnv *env, jobject object, jint volume,
+ jbyteArray address) {
bt_status_t status;
+ jbyte *addr;
//TODO: delete test code
- ALOGI("%s: jint: %d, uint8_t: %u", __FUNCTION__, volume, (uint8_t) volume);
+ ALOGV("%s: jint: %d, uint8_t: %u", __FUNCTION__, volume, (uint8_t) volume);
- ALOGI("%s: sBluetoothAvrcpInterface: %p", __FUNCTION__, sBluetoothAvrcpInterface);
- if (!sBluetoothAvrcpInterface) return JNI_FALSE;
+ ALOGV("%s: sBluetoothMultiAvrcpInterface: %p", __FUNCTION__, sBluetoothMultiAvrcpInterface);
+ if (!sBluetoothMultiAvrcpInterface) return JNI_FALSE;
- if ((status = sBluetoothAvrcpInterface->set_volume((uint8_t)volume)) != BT_STATUS_SUCCESS) {
+ addr = env->GetByteArrayElements(address, NULL);
+ if (!addr) {
+ jniThrowIOException(env, EINVAL);
+ return JNI_FALSE;
+ }
+ if ((status = sBluetoothMultiAvrcpInterface->set_volume((uint8_t)volume,
+ (bt_bdaddr_t *)addr)) != BT_STATUS_SUCCESS) {
ALOGE("Failed set_volume, status: %d", status);
}
+ env->ReleaseByteArrayElements(address, addr, 0);
+ return (status == BT_STATUS_SUCCESS) ? JNI_TRUE : JNI_FALSE;
+}
+
+static jboolean registerNotificationRspAddressedPlayerChangedNative (JNIEnv *env,
+ jobject object, jint type,
+ jint playerId,
+ jbyteArray address) {
+ bt_status_t status;
+ jbyte *addr;
+ btrc_register_notification_t param;
+
+ ALOGV("%s: sBluetoothMultiAvrcpInterface: %p", __FUNCTION__, sBluetoothMultiAvrcpInterface);
+ ALOGV("playerId: %d", playerId);
+ if (!sBluetoothMultiAvrcpInterface) return JNI_FALSE;
+
+ addr = env->GetByteArrayElements(address, NULL);
+ if (!addr) {
+ jniThrowIOException(env, EINVAL);
+ return JNI_FALSE;
+ }
+ param.player_id = (uint16_t)playerId;
+ if ((status = sBluetoothMultiAvrcpInterface->register_notification_rsp(
+ BTRC_EVT_ADDRESSED_PLAYER_CHANGED, (btrc_notification_type_t)type,
+ ¶m, (bt_bdaddr_t *)addr)) != BT_STATUS_SUCCESS) {
+ ALOGE("Failed registerNotificationRspAddressedPlayerChangedNative, status: %d", status);
+ }
+ env->ReleaseByteArrayElements(address, addr, 0);
+ return (status == BT_STATUS_SUCCESS) ? JNI_TRUE : JNI_FALSE;
+
+}
+
+static jboolean registerNotificationRspAvailablePlayersChangedNative (JNIEnv *env,
+ jobject object, jint type,
+ jbyteArray address) {
+ bt_status_t status;
+ jbyte *addr;
+ btrc_register_notification_t param;
+
+ ALOGV("%s: sBluetoothMultiAvrcpInterface: %p", __FUNCTION__, sBluetoothMultiAvrcpInterface);
+ if (!sBluetoothMultiAvrcpInterface) return JNI_FALSE;
+
+ addr = env->GetByteArrayElements(address, NULL);
+ if (!addr) {
+ jniThrowIOException(env, EINVAL);
+ return JNI_FALSE;
+ }
+ if ((status = sBluetoothMultiAvrcpInterface->register_notification_rsp(
+ BTRC_EVT_AVAILABLE_PLAYERS_CHANGED, (btrc_notification_type_t)type,
+ ¶m, (bt_bdaddr_t *)addr)) != BT_STATUS_SUCCESS) {
+ ALOGE("Failed registerNotificationRspAvailablePlayersChangedNative, status: %d", status);
+ }
+ env->ReleaseByteArrayElements(address, addr, 0);
+ return (status == BT_STATUS_SUCCESS) ? JNI_TRUE : JNI_FALSE;
+}
+
+static jboolean registerNotificationRspNowPlayingContentChangedNative(JNIEnv *env,
+ jobject object, jint type,
+ jbyteArray address) {
+ bt_status_t status;
+ jbyte *addr;
+ btrc_register_notification_t param;
+
+ ALOGV("%s: sBluetoothMultiAvrcpInterface: %p", __FUNCTION__, sBluetoothMultiAvrcpInterface);
+ if (!sBluetoothMultiAvrcpInterface) return JNI_FALSE;
+
+ addr = env->GetByteArrayElements(address, NULL);
+ if (!addr) {
+ jniThrowIOException(env, EINVAL);
+ return JNI_FALSE;
+ }
+ if ((status = sBluetoothMultiAvrcpInterface->register_notification_rsp(
+ BTRC_EVT_NOW_PLAYING_CONTENT_CHANGED, (btrc_notification_type_t)type, ¶m,
+ (bt_bdaddr_t *)addr)) != BT_STATUS_SUCCESS) {
+ ALOGE("Failed registerNotificationRspNowPlayingContentChangedNative, status: %d", status);
+ }
+ env->ReleaseByteArrayElements(address, addr, 0);
+ return (status == BT_STATUS_SUCCESS) ? JNI_TRUE : JNI_FALSE;
+}
+
+static jboolean getFolderItemsRspNative(JNIEnv *env, jobject object, jbyte statusCode,
+ jlong numItems, jintArray itemType, jlongArray uid, jintArray type,
+ jbyteArray playable, jobjectArray displayName, jbyteArray numAtt,
+ jobjectArray attValues, jintArray attIds, jint size, jbyteArray address) {
+ bt_status_t status = BT_STATUS_SUCCESS;
+ jbyte *addr;
+ btrc_folder_list_entries_t param;
+ int32_t *itemTypeElements;
+ int64_t *uidElements;
+ int32_t *typeElements;
+ int8_t *playableElements;
+ jstring *displayNameElements;
+ int8_t *numAttElements;
+ jstring *attValuesElements;
+ int32_t *attIdsElements;
+ jint count = 0;
+ jstring text;
+ const char* textStr;
+ jsize utfStringLength = 0;
+ int num_attr;
+ int total_len = BTRC_BROWSE_PDU_HEADER + BTRC_AVCTP_HEADER + BTRC_BROWSE_PKT_3TO7OCT_LEN;
+
+ ALOGV("%s: sBluetoothMultiAvrcpInterface: %p", __FUNCTION__, sBluetoothMultiAvrcpInterface);
+ if (!sBluetoothMultiAvrcpInterface) return JNI_FALSE;
+
+ addr = env->GetByteArrayElements(address, NULL);
+ if (!addr) {
+ jniThrowIOException(env, EINVAL);
+ return JNI_FALSE;
+ }
+ param.status = statusCode;
+ param.uid_counter = 0;
+
+ if (numItems > 0) {
+ itemTypeElements = env->GetIntArrayElements(itemType, NULL);
+ if (!itemTypeElements) {
+ jniThrowIOException(env, EINVAL);
+ return JNI_FALSE;
+ }
+
+ uidElements = env->GetLongArrayElements(uid, NULL);
+ if (!uidElements) {
+ jniThrowIOException(env, EINVAL);
+ return JNI_FALSE;
+ }
+
+ typeElements = env->GetIntArrayElements(type, NULL);
+ if (!typeElements) {
+ jniThrowIOException(env, EINVAL);
+ return JNI_FALSE;
+ }
+
+ playableElements = env->GetByteArrayElements(playable, NULL);
+ if (!playableElements) {
+ jniThrowIOException(env, EINVAL);
+ return JNI_FALSE;
+ }
+
+ numAttElements = env->GetByteArrayElements(numAtt, NULL);
+ if (!numAttElements) {
+ jniThrowIOException(env, EINVAL);
+ return JNI_FALSE;
+ }
+
+ attIdsElements = env->GetIntArrayElements(attIds, NULL);
+ if (!attIdsElements) {
+ jniThrowIOException(env, EINVAL);
+ return JNI_FALSE;
+ }
+ }
+
+ param.p_item_list = new btrc_folder_list_item_t[numItems];
+
+ for (count = 0; count < numItems; count++) {
+ total_len = total_len + BTRC_ITEM_TYPE_N_LEN_OCT;
+ param.p_item_list[count].item_type = (uint8_t)itemTypeElements[count];
+ ALOGI("getFolderItemsRspNative: item_type: %d", param.p_item_list[count].item_type);
+ if (itemTypeElements[count] == BTRC_TYPE_FOLDER) {
+ param.p_item_list[count].u.folder.uid = uidElements[count];
+ ALOGI("getFolderItemsRspNative: uid: %lu", param.p_item_list[count].u.folder.uid);
+ param.p_item_list[count].u.folder.type = (uint8_t)typeElements[count];
+ ALOGI("getFolderItemsRspNative: type: %d", param.p_item_list[count].u.folder.type);
+ param.p_item_list[count].u.folder.playable = playableElements[count];
+
+ text = (jstring) env->GetObjectArrayElement(displayName, count);
+ if (text == NULL) {
+ ALOGE("getFolderItemsRspNative: App string is NULL, bail out");
+ break;
+ }
+ utfStringLength = env->GetStringUTFLength(text);
+ if (!utfStringLength) {
+ ALOGE("getFolderItemsRspNative: GetStringUTFLength return NULL");
+ env->DeleteLocalRef(text);
+ break;
+ }
+ ALOGI("getFolderItemsRspNative: Disp Elem Length: %d", utfStringLength);
+ total_len = total_len + utfStringLength + BTRC_FOLDER_ITEM_HEADER;
+ if (total_len > size) {
+ ALOGI("total_len = : %d, count = %d", total_len, count);
+ if (count != 0) {
+ env->DeleteLocalRef(text);
+ break;
+ } else {
+ utfStringLength =
+ size - (BTRC_FOLDER_ITEM_HEADER + BTRC_ITEM_TYPE_N_LEN_OCT + 1);
+ ALOGI("modified utfStringLength = : %d", utfStringLength);
+ }
+ }
+
+ textStr = env->GetStringUTFChars(text, NULL);
+ if (!textStr) {
+ ALOGE("getFolderItemsRspNative: GetStringUTFChars return NULL");
+ env->DeleteLocalRef(text);
+ break;
+ }
+ param.p_item_list[count].u.folder.name.charset_id = BTRC_CHARSET_UTF8;
+ param.p_item_list[count].u.folder.name.str_len = utfStringLength;
+ param.p_item_list[count].u.folder.name.p_str = new uint8_t[utfStringLength + 1];
+ strlcpy((char *)param.p_item_list[count].u.folder.name.p_str, textStr,
+ utfStringLength + 1);
+ env->ReleaseStringUTFChars(text, textStr);
+ env->DeleteLocalRef(text);
+ } else if (itemTypeElements[count] == BTRC_TYPE_MEDIA_ELEMENT) {
+ num_attr = 0;
+ param.p_item_list[count].u.media.uid = uidElements[count];
+ ALOGI("getFolderItemsRspNative: uid: %l", param.p_item_list[count].u.media.uid);
+ param.p_item_list[count].u.media.type = (uint8_t)typeElements[count];
+ ALOGI("getFolderItemsRspNative: type: %d", param.p_item_list[count].u.media.type);
+ text = (jstring) env->GetObjectArrayElement(displayName, count);
+ if (text == NULL) {
+ ALOGE("getFolderItemsRspNative: App string is NULL, bail out");
+ break;
+ }
+ utfStringLength = env->GetStringUTFLength(text);
+ if (!utfStringLength) {
+ ALOGE("getFolderItemsRspNative: GetStringUTFLength return NULL");
+ env->DeleteLocalRef(text);
+ break;
+ }
+ ALOGI("getFolderItemsRspNative: Disp Elem Length: %d", utfStringLength);
+
+ total_len = total_len + utfStringLength + BTRC_FOLDER_ITEM_HEADER;
+ textStr = env->GetStringUTFChars(text, NULL);
+ if (!textStr) {
+ ALOGE("getFolderItemsRspNative: GetStringUTFChars return NULL");
+ env->DeleteLocalRef(text);
+ break;
+ }
+ param.p_item_list[count].u.media.name.charset_id = BTRC_CHARSET_UTF8;
+ param.p_item_list[count].u.media.name.str_len = utfStringLength;
+ param.p_item_list[count].u.media.name.p_str = new uint8_t[utfStringLength + 1];
+ strlcpy((char *)param.p_item_list[count].u.media.name.p_str, textStr,
+ utfStringLength + 1);
+ env->ReleaseStringUTFChars(text, textStr);
+ env->DeleteLocalRef(text);
+ ALOGI("getFolderItemsRspNative: numAttr: %d", numAttElements[count]);
+ param.p_item_list[count].u.media.p_attr_list =
+ new btrc_attr_entry_t[numAttElements[count]];
+
+ for (int i = 0; i < numAttElements[count]; i++) {
+ text = (jstring) env->GetObjectArrayElement(attValues, (8 * count) + i);
+ if (text == NULL) {
+ ALOGE("getFolderItemsRspNative: Attribute string is NULL, continue to next");
+ continue;
+ }
+ utfStringLength = env->GetStringUTFLength(text);
+ if (!utfStringLength) {
+ ALOGE("getFolderItemsRspNative: GetStringUTFLength return NULL");
+ env->DeleteLocalRef(text);
+ continue;
+ }
+ total_len = total_len + utfStringLength + BTRC_ITEM_ATTRIBUTE_HEADER;
+ if (total_len > size) {
+ ALOGI("total_len = %d, count = %d", total_len, count);
+ if (count != 0)
+ num_attr = 0;
+ env->DeleteLocalRef(text);
+ break;
+ }
+
+ textStr = env->GetStringUTFChars(text, NULL);
+ if (!textStr) {
+ ALOGE("getFolderItemsRspNative: GetStringUTFChars return NULL");
+ env->DeleteLocalRef(text);
+ continue;
+ }
+ param.p_item_list[count].u.media.p_attr_list[num_attr].attr_id =
+ attIdsElements[(8 * count) + i];
+ ALOGI("getFolderItemsRspNative: Attr id: %d",
+ param.p_item_list[count].u.media.p_attr_list[num_attr].attr_id);
+ param.p_item_list[count].u.media.p_attr_list[num_attr].name.charset_id =
+ BTRC_CHARSET_UTF8;
+ param.p_item_list[count].u.media.p_attr_list[num_attr].name.str_len =
+ utfStringLength;
+ ALOGI("getFolderItemsRspNative: Attr Length: %d",
+ param.p_item_list[count].u.media.p_attr_list[num_attr].name.str_len);
+ param.p_item_list[count].u.media.p_attr_list[num_attr].name.p_str =
+ new uint8_t[utfStringLength + 1];
+ strlcpy((char *)param.p_item_list[count].u.media.p_attr_list[num_attr].
+ name.p_str, textStr, utfStringLength + 1);
+ num_attr++;
+ env->ReleaseStringUTFChars(text, textStr);
+ env->DeleteLocalRef(text);
+ }
+ param.p_item_list[count].u.media.attr_count = num_attr;
+ ALOGI("getFolderItemsRspNative: effective numAttr: %d",
+ param.p_item_list[count].u.media.attr_count);
+ }
+ if (total_len > size) {
+ ALOGI("total_len final =: %d", total_len);
+ break;
+ }
+ }
+
+ if (count != 0)
+ param.item_count = count;
+ else if (param.status == BTRC_STS_NO_ERROR)
+ param.item_count = 1;
+ else
+ param.item_count = 0;
+
+ ALOGE("get_folder_items_rsp: count: %u, effective count: %u", count, param.item_count);
+ if ((status = sBluetoothMultiAvrcpInterface->get_folder_items_rsp(¶m,
+ (bt_bdaddr_t *)addr)) != BT_STATUS_SUCCESS) {
+ ALOGE("Failed get_folder_items_rsp, status: %u", status);
+ }
+
+ if (numItems > 0) {
+ env->ReleaseIntArrayElements(itemType, itemTypeElements, 0);
+ env->ReleaseLongArrayElements(uid, uidElements, 0);
+ env->ReleaseIntArrayElements(type, typeElements, 0);
+ env->ReleaseByteArrayElements(playable, playableElements, 0);
+ env->ReleaseByteArrayElements(numAtt, numAttElements, 0);
+ env->ReleaseIntArrayElements(attIds, attIdsElements, 0);
+ }
return (status == BT_STATUS_SUCCESS) ? JNI_TRUE : JNI_FALSE;
}
+// MediaPlayerItems are populated as byte stream from the apps
+static jboolean getMediaPlayerListRspNative(JNIEnv *env, jobject object, jbyte statusCode,
+ jint uidCounter, jint itemCount, jbyteArray folderItems,
+ jintArray folderItemLengths, jbyteArray address) {
+ bt_status_t status;
+ jbyte *addr;
+ int8_t *folderElements;
+ int32_t *folderElementLengths;
+ int32_t count = 0;
+ int32_t countElementLength = 0;
+ int32_t countTotalBytes = 0;
+ int32_t countTemp = 0;
+ int32_t checkLength = 0;
+ btrc_folder_list_entries_t param;
+
+ ALOGV("%s: sBluetoothMultiAvrcpInterface: %p", __FUNCTION__, sBluetoothMultiAvrcpInterface);
+ if (!sBluetoothMultiAvrcpInterface) return JNI_FALSE;
+
+ addr = env->GetByteArrayElements(address, NULL);
+ if (!addr) {
+ jniThrowIOException(env, EINVAL);
+ return JNI_FALSE;
+ }
+ folderElements = env->GetByteArrayElements(folderItems, NULL);
+ if (!folderElements) {
+ jniThrowIOException(env, EINVAL);
+ return JNI_FALSE;
+ }
+
+ folderElementLengths = env->GetIntArrayElements(folderItemLengths, NULL);
+ if (!folderElementLengths) {
+ jniThrowIOException(env, EINVAL);
+ return JNI_FALSE;
+ }
+
+ param.status = statusCode;
+ param.uid_counter = uidCounter;
+ param.item_count = itemCount;
+ ALOGI("status: %d, item count: %d", param.status, param.item_count);
+ param.p_item_list = new btrc_folder_list_item_t[itemCount];
+ ALOGI("Intermediate List entries:");
+ for (; count < itemCount; count++) {
+ param.p_item_list[count].item_type = folderElements[countTotalBytes]; countTotalBytes++;
+ param.p_item_list[count].u.player.player_id =
+ (uint16_t)(folderElements[countTotalBytes] & 0x00ff); countTotalBytes++;
+ param.p_item_list[count].u.player.player_id +=
+ (uint16_t)((folderElements[countTotalBytes] << 8) & 0xff00); countTotalBytes++;
+ param.p_item_list[count].u.player.major_type =
+ folderElements[countTotalBytes]; countTotalBytes++;
+ param.p_item_list[count].u.player.sub_type =
+ (uint32_t)(folderElements[countTotalBytes] & 0x000000ff); countTotalBytes++;
+ param.p_item_list[count].u.player.sub_type +=
+ (uint32_t)((folderElements[countTotalBytes] << 8) & 0x0000ff00); countTotalBytes++;
+ param.p_item_list[count].u.player.sub_type +=
+ (uint32_t)((folderElements[countTotalBytes] << 16) & 0x00ff0000); countTotalBytes++;
+ param.p_item_list[count].u.player.sub_type +=
+ (uint32_t)((folderElements[countTotalBytes] << 24) & 0xff000000); countTotalBytes++;
+ param.p_item_list[count].u.player.play_status =
+ folderElements[countTotalBytes]; countTotalBytes++;
+ for (countTemp = 0; countTemp < 16; countTemp ++) {
+ param.p_item_list[count].u.player.features[countTemp] =
+ folderElements[countTotalBytes]; countTotalBytes++;
+ }
+ param.p_item_list[count].u.player.name.charset_id =
+ (uint16_t)(folderElements[countTotalBytes] & 0x00ff); countTotalBytes++;
+ param.p_item_list[count].u.player.name.charset_id +=
+ (uint16_t)((folderElements[countTotalBytes] << 8) & 0xff00); countTotalBytes++;
+ param.p_item_list[count].u.player.name.str_len =
+ (uint16_t)(folderElements[countTotalBytes] & 0x00ff); countTotalBytes++;
+ param.p_item_list[count].u.player.name.str_len +=
+ (uint16_t)((folderElements[countTotalBytes] << 8) & 0xff00); countTotalBytes++;
+ param.p_item_list[count].u.player.name.p_str =
+ new uint8_t[param.p_item_list[count].u.player.name.str_len];
+ for (countTemp = 0; countTemp < param.p_item_list[count].u.player.name.str_len;
+ countTemp ++) {
+ param.p_item_list[count].u.player.name.p_str[countTemp] =
+ folderElements[countTotalBytes]; countTotalBytes++;
+ }
+ /*To check if byte feeding went well*/
+ checkLength += folderElementLengths[count];
+ if (checkLength != countTotalBytes) {
+ ALOGE("Error Populating Intermediate Folder Entry");
+ ALOGE("checkLength = %u countTotalBytes = %u", checkLength, countTotalBytes);
+ }
+ ALOGI("entry: %u", count);
+ ALOGI("item type: %u", param.p_item_list[count].item_type);
+ ALOGI("player id: %u", param.p_item_list[count].u.player.player_id);
+ ALOGI("major type: %u", param.p_item_list[count].u.player.major_type);
+ ALOGI("sub type: %u", param.p_item_list[count].u.player.sub_type);
+ ALOGI("play status: %u", param.p_item_list[count].u.player.play_status);
+ ALOGI("features: ");
+ for (countTemp = 0; countTemp < 16; countTemp ++)
+ ALOGI("%u", param.p_item_list[count].u.player.features[countTemp]);
+ ALOGI("charset id: %u", param.p_item_list[count].u.player.name.charset_id);
+ ALOGI("name len: %u", param.p_item_list[count].u.player.name.str_len);
+ ALOGI("name: ");
+ for (countTemp = 0; countTemp < param.p_item_list[count].u.player.name.str_len;
+ countTemp ++) {
+ ALOGI("%u", param.p_item_list[count].u.player.name.p_str[countTemp]);
+ }
+ }
+
+ if ((status = sBluetoothMultiAvrcpInterface->get_folder_items_rsp(¶m,
+ (bt_bdaddr_t *)addr)) != BT_STATUS_SUCCESS) {
+ ALOGE("Failed getMediaPlayerListRspNative, status: %u", status);
+ }
+
+ env->ReleaseByteArrayElements(folderItems, folderElements, 0);
+ env->ReleaseIntArrayElements(folderItemLengths, folderElementLengths, 0);
+ env->ReleaseByteArrayElements(address, addr, 0);
+ return (status == BT_STATUS_SUCCESS) ? JNI_TRUE : JNI_FALSE;
+}
+
+static jboolean setAdressedPlayerRspNative(JNIEnv *env, jobject object, jbyte statusCode,
+ jbyteArray address) {
+ bt_status_t status;
+ jbyte *addr;
+
+ ALOGV("%s: sBluetoothMultiAvrcpInterface: %p", __FUNCTION__, sBluetoothMultiAvrcpInterface);
+ if (!sBluetoothMultiAvrcpInterface) return JNI_FALSE;
+
+ addr = env->GetByteArrayElements(address, NULL);
+ if (!addr) {
+ jniThrowIOException(env, EINVAL);
+ return JNI_FALSE;
+ }
+ if ((status = sBluetoothMultiAvrcpInterface->set_addressed_player_rsp((btrc_status_t)statusCode,
+ (bt_bdaddr_t *)addr)) != BT_STATUS_SUCCESS) {
+ ALOGE("Failed setAdressedPlayerRspNative, status: %d", status);
+ }
+ env->ReleaseByteArrayElements(address, addr, 0);
+ return (status == BT_STATUS_SUCCESS) ? JNI_TRUE : JNI_FALSE;
+}
+
+static jboolean getItemAttrRspNative(JNIEnv *env, jobject object, jbyte numAttr,
+ jintArray attrIds, jobjectArray textArray,
+ jint size, jbyteArray address) {
+ jint *attr;
+ jbyte *addr;
+ bt_status_t status;
+ jstring text;
+ int i;
+ btrc_element_attr_val_t *pAttrs = NULL;
+ const char* textStr;
+ jsize utfStringLength = 0;
+ int total_len = BTRC_BROWSE_PDU_HEADER + BTRC_AVCTP_HEADER +
+ BTRC_BROWSE_PKT_3TO7OCT_LEN + BTRC_ITEM_TYPE_N_LEN_OCT;
+
+ if (!sBluetoothMultiAvrcpInterface) return JNI_FALSE;
+
+ addr = env->GetByteArrayElements(address, NULL);
+ if (!addr) {
+ jniThrowIOException(env, EINVAL);
+ return JNI_FALSE;
+ }
+ if (numAttr > BTRC_MAX_ELEM_ATTR_SIZE) {
+ ALOGE("get_item_attr_rsp: number of attributes exceed maximum");
+ return JNI_FALSE;
+ }
+
+ pAttrs = new btrc_element_attr_val_t[numAttr];
+ if (!pAttrs) {
+ ALOGE("get_item_attr_rsp: not have enough memeory");
+ return JNI_FALSE;
+ }
+
+ attr = env->GetIntArrayElements(attrIds, NULL);
+ if (!attr) {
+ delete[] pAttrs;
+ jniThrowIOException(env, EINVAL);
+ return JNI_FALSE;
+ }
+
+ for (i = 0; i < numAttr; ++i) {
+ text = (jstring) env->GetObjectArrayElement(textArray, i);
+
+ utfStringLength = env->GetStringUTFLength(text);
+ total_len = total_len + utfStringLength + BTRC_ITEM_ATTRIBUTE_HEADER;
+ if (total_len > size) {
+ ALOGI("total_len: %d", total_len);
+ env->DeleteLocalRef(text);
+ break;
+ }
+
+ textStr = env->GetStringUTFChars(text, NULL);
+ if (!textStr) {
+ ALOGE("get_item_attr_rsp: GetStringUTFChars return NULL");
+ env->DeleteLocalRef(text);
+ break;
+ }
+
+ pAttrs[i].attr_id = attr[i];
+ if (utfStringLength >= BTRC_MAX_ATTR_STR_LEN) {
+ ALOGE("get_item_attr_rsp: string length exceed maximum");
+ }
+ strlcpy((char *)pAttrs[i].text, textStr, BTRC_MAX_ATTR_STR_LEN);
+ env->ReleaseStringUTFChars(text, textStr);
+ env->DeleteLocalRef(text);
+ }
+
+ numAttr = i;
+
+ if ((status = sBluetoothMultiAvrcpInterface->get_item_attr_rsp(numAttr, pAttrs,
+ (bt_bdaddr_t *)addr)) != BT_STATUS_SUCCESS) {
+ ALOGE("Failed get_item_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 setBrowsedPlayerRspNative(JNIEnv *env, jobject object,
+ jbyte statusCode, jint uidCounter,
+ jint itemCount, jint folderDepth,
+ jint charId, jobjectArray folderNames,
+ jbyteArray address) {
+ bt_status_t status;
+ jbyte *addr;
+ int32_t count = 0;
+ jstring text;
+ const char* textStr;
+ jsize utfStringLength = 0;
+
+ btrc_set_browsed_player_rsp_t param;
+
+ ALOGV("%s: sBluetoothMultiAvrcpInterface: %p", __FUNCTION__, sBluetoothMultiAvrcpInterface);
+ if (!sBluetoothMultiAvrcpInterface) return JNI_FALSE;
+
+ addr = env->GetByteArrayElements(address, NULL);
+ if (!addr) {
+ jniThrowIOException(env, EINVAL);
+ return JNI_FALSE;
+ }
+ param.status = statusCode;
+ param.uid_counter = uidCounter;
+ param.num_items = itemCount;
+ param.charset_id = charId;
+ param.folder_depth = folderDepth;
+
+ ALOGI("statusCode: %d", statusCode);
+ ALOGI("uidCounter: %d", uidCounter);
+ ALOGI("itemCount: %d", itemCount);
+ ALOGI("charId: %d", charId);
+ ALOGI("folderDepth: %d", folderDepth);
+
+ param.p_folders = new btrc_name_t[folderDepth];
+
+ for (count = 0; count < folderDepth; ++count) {
+ text = (jstring) env->GetObjectArrayElement(folderNames, count);
+
+ utfStringLength = env->GetStringUTFLength(text);
+ if (!utfStringLength) {
+ ALOGE("setBrowsedPlayerRspNative: GetStringUTFLength return NULL");
+ env->DeleteLocalRef(text);
+ break;
+ }
+
+ textStr = env->GetStringUTFChars(text, NULL);
+ if (!textStr) {
+ ALOGE("setBrowsedPlayerRspNative: GetStringUTFChars return NULL");
+ env->DeleteLocalRef(text);
+ break;
+ }
+
+ param.p_folders[count].str_len = utfStringLength;
+ param.p_folders[count].p_str = new uint8_t[utfStringLength + 1];
+ strlcpy((char *)param.p_folders[count].p_str, textStr, utfStringLength + 1);
+ env->ReleaseStringUTFChars(text, textStr);
+ env->DeleteLocalRef(text);
+
+ }
+
+ if ((status = sBluetoothMultiAvrcpInterface->set_browsed_player_rsp(¶m,
+ (bt_bdaddr_t *)addr)) != BT_STATUS_SUCCESS) {
+ ALOGE("Failed setBrowsedPlayerRspNative, status: %u", status);
+ }
+ env->ReleaseByteArrayElements(address, addr, 0);
+ return (status == BT_STATUS_SUCCESS) ? JNI_TRUE : JNI_FALSE;
+}
+
+static jboolean changePathRspNative(JNIEnv *env, jobject object, jint errStatus, jlong itemCount,
+ jbyteArray address) {
+ bt_status_t status;
+ jbyte *addr;
+
+ if (!sBluetoothMultiAvrcpInterface) return JNI_FALSE;
+
+ addr = env->GetByteArrayElements(address, NULL);
+ if (!addr) {
+ jniThrowIOException(env, EINVAL);
+ return JNI_FALSE;
+ }
+ ALOGV("%s: sBluetoothMultiAvrcpInterface: %p", __FUNCTION__, sBluetoothMultiAvrcpInterface);
+ ALOGV("status: %d, itemCount: %l", errStatus, itemCount);
+
+ if ((status = sBluetoothMultiAvrcpInterface->change_path_rsp((uint8_t)errStatus,
+ (uint32_t)itemCount,
+ (bt_bdaddr_t *)addr)) != BT_STATUS_SUCCESS) {
+ ALOGE("Failed sending change path response, status: %d", status);
+ }
+ env->ReleaseByteArrayElements(address, addr, 0);
+ return (status == BT_STATUS_SUCCESS) ? JNI_TRUE : JNI_FALSE;
+}
+
+static jboolean playItemRspNative(JNIEnv *env, jobject object, jint errStatus,
+ jbyteArray address) {
+ bt_status_t status;
+ jbyte *addr;
+
+ if (!sBluetoothMultiAvrcpInterface) return JNI_FALSE;
+
+ addr = env->GetByteArrayElements(address, NULL);
+ if (!addr) {
+ jniThrowIOException(env, EINVAL);
+ return JNI_FALSE;
+ }
+ ALOGV("%s: sBluetoothMultiAvrcpInterface: %p", __FUNCTION__, sBluetoothMultiAvrcpInterface);
+ ALOGV("status: %d", errStatus);
+
+ if ((status = sBluetoothMultiAvrcpInterface->play_item_rsp((uint8_t)errStatus,
+ (bt_bdaddr_t *)addr))!= BT_STATUS_SUCCESS) {
+ ALOGE("Failed sending play item response, status: %d", status);
+ }
+ env->ReleaseByteArrayElements(address, addr, 0);
+ return (status == BT_STATUS_SUCCESS) ? JNI_TRUE : JNI_FALSE;
+}
+
+static jboolean isDeviceActiveInHandOffNative(JNIEnv *env, jobject object, jbyteArray address) {
+ bt_status_t status = BT_STATUS_SUCCESS;
+ jbyte *addr;
+
+ if (!sBluetoothMultiAvrcpInterface) return JNI_FALSE;
+
+ addr = env->GetByteArrayElements(address, NULL);
+ if (!addr) {
+ jniThrowIOException(env, EINVAL);
+ return JNI_FALSE;
+ }
+ ALOGI("%s: sBluetoothMultiAvrcpInterface: %p", __FUNCTION__, sBluetoothMultiAvrcpInterface);
+
+ status = sBluetoothMultiAvrcpInterface->is_device_active_in_handoff((bt_bdaddr_t *)addr);
+
+ ALOGI("isDeviceActiveInHandOffNative: status: %d", status);
+
+ env->ReleaseByteArrayElements(address, addr, 0);
+ return (status == BT_STATUS_SUCCESS) ? JNI_TRUE : JNI_FALSE;
+}
+
+static jboolean getTotalNumberOfItemsRspNative(JNIEnv *env, jobject object,
+ jint errStatus, jlong itemCount, jint uidCounter, jbyteArray address) {
+ bt_status_t status;
+ jbyte *addr;
+ if (!sBluetoothMultiAvrcpInterface) return JNI_FALSE;
+
+ addr = env->GetByteArrayElements(address, NULL);
+ if (!addr) {
+ jniThrowIOException(env, EINVAL);
+ return JNI_FALSE;
+ }
+
+ ALOGI("%s: sBluetoothAvrcpInterface: %p", __FUNCTION__, sBluetoothMultiAvrcpInterface);
+ ALOGI("status: %d", errStatus);
+
+ if ((status = sBluetoothMultiAvrcpInterface->get_total_items_rsp((uint8_t) errStatus,
+ (uint32_t) itemCount, (uint16_t) uidCounter, (bt_bdaddr_t *)addr))
+ != BT_STATUS_SUCCESS) {
+ ALOGE("Failed sending get total items response, 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},
+ {"initNative", "(I)V", (void *) initNative},
{"cleanupNative", "()V", (void *) cleanupNative},
- {"getPlayStatusRspNative", "(III)Z", (void *) getPlayStatusRspNative},
- {"getElementAttrRspNative", "(B[I[Ljava/lang/String;)Z", (void *) getElementAttrRspNative},
- {"registerNotificationRspPlayStatusNative", "(II)Z",
+ {"getPlayStatusRspNative", "(III[B)Z", (void *) getPlayStatusRspNative},
+ {"getElementAttrRspNative", "(B[I[Ljava/lang/String;[B)Z", (void *) getElementAttrRspNative},
+ {"getListPlayerappAttrRspNative", "(B[B[B)Z", (void *) getListPlayerappAttrRspNative},
+ {"getPlayerAppValueRspNative", "(B[B[B)Z", (void *) getPlayerAppValueRspNative},
+ {"registerNotificationRspPlayStatusNative", "(II[B)Z",
(void *) registerNotificationRspPlayStatusNative},
- {"registerNotificationRspTrackChangeNative", "(I[B)Z",
+ {"SendCurrentPlayerValueRspNative", "(B[B[B)Z",
+ (void *) SendCurrentPlayerValueRspNative},
+ {"registerNotificationPlayerAppRspNative", "(IB[B[B)Z",
+ (void *) registerNotificationPlayerAppRspNative},
+ {"registerNotificationRspTrackChangeNative", "(I[B[B)Z",
(void *) registerNotificationRspTrackChangeNative},
- {"registerNotificationRspPlayPosNative", "(II)Z",
+ {"SendSetPlayerAppRspNative", "(I[B)Z",
+ (void *) SendSetPlayerAppRspNative},
+ {"sendSettingsTextRspNative" , "(I[BI[Ljava/lang/String;[B)Z",
+ (void *) sendSettingsTextRspNative},
+ {"sendValueTextRspNative" , "(I[BI[Ljava/lang/String;[B)Z",
+ (void *) sendValueTextRspNative},
+ {"registerNotificationRspPlayPosNative", "(II[B)Z",
(void *) registerNotificationRspPlayPosNative},
- {"setVolumeNative", "(I)Z",
+ {"setVolumeNative", "(I[B)Z",
(void *) setVolumeNative},
+ {"setAdressedPlayerRspNative", "(B[B)Z",
+ (void *) setAdressedPlayerRspNative},
+ {"getMediaPlayerListRspNative", "(BII[B[I[B)Z",
+ (void *) getMediaPlayerListRspNative},
+ {"registerNotificationRspAddressedPlayerChangedNative", "(II[B)Z",
+ (void *) registerNotificationRspAddressedPlayerChangedNative},
+ {"registerNotificationRspAvailablePlayersChangedNative", "(I[B)Z",
+ (void *) registerNotificationRspAvailablePlayersChangedNative},
+ {"registerNotificationRspNowPlayingContentChangedNative", "(I[B)Z",
+ (void *) registerNotificationRspNowPlayingContentChangedNative},
+ {"setBrowsedPlayerRspNative", "(BIIII[Ljava/lang/String;[B)Z",
+ (void *) setBrowsedPlayerRspNative},
+ {"changePathRspNative", "(IJ[B)Z", (void *) changePathRspNative},
+ {"playItemRspNative", "(I[B)Z", (void *) playItemRspNative},
+ {"getItemAttrRspNative", "(B[I[Ljava/lang/String;I[B)Z", (void *) getItemAttrRspNative},
+ {"getFolderItemsRspNative", "(BJ[I[J[I[B[Ljava/lang/String;[B[Ljava/lang/String;[II[B)Z",
+ (void *) getFolderItemsRspNative},
+ {"isDeviceActiveInHandOffNative", "([B)Z", (void *) isDeviceActiveInHandOffNative},
+ {"getTotalNumberOfItemsRspNative", "(IJI[B)Z",
+ (void *) getTotalNumberOfItemsRspNative},
};
int register_com_android_bluetooth_avrcp(JNIEnv* env)
diff --git a/jni/com_android_bluetooth_avrcp_controller.cpp b/jni/com_android_bluetooth_avrcp_controller.cpp
index 2c07a57..faa7942 100644
--- a/jni/com_android_bluetooth_avrcp_controller.cpp
+++ b/jni/com_android_bluetooth_avrcp_controller.cpp
@@ -377,7 +377,7 @@
}
static void btavrcp_play_position_changed_callback(bt_bdaddr_t *bd_addr, uint32_t song_len,
- uint32_t song_pos) {
+ uint32_t song_pos, btrc_play_status_t play_status) {
jbyteArray addr;
ALOGI("%s", __FUNCTION__);
@@ -390,7 +390,7 @@
}
sCallbackEnv->SetByteArrayRegion(addr, 0, sizeof(bt_bdaddr_t), (jbyte*) bd_addr);
sCallbackEnv->CallVoidMethod(mCallbacksObj, method_handleplaypositionchanged, addr,
- (jint)(song_len), (jint)song_pos);
+ (jint)(song_len), (jint)song_pos, (jbyte)play_status);
sCallbackEnv->DeleteLocalRef(addr);
}
@@ -459,7 +459,7 @@
env->GetMethodID(clazz, "onTrackChanged", "([BB[I[Ljava/lang/String;)V");
method_handleplaypositionchanged =
- env->GetMethodID(clazz, "onPlayPositionChanged", "([BII)V");
+ env->GetMethodID(clazz, "onPlayPositionChanged", "([BIIB)V");
method_handleplaystatuschanged =
env->GetMethodID(clazz, "onPlayStatusChanged", "([BB)V");
@@ -595,8 +595,14 @@
pAttrs = new uint8_t[num_attrib];
pAttrsVal = new uint8_t[num_attrib];
+ /* Klockwork Fix for below
+ * Possible memory leak. Dynamic memory stored in 'pAttrsVal' allocated
+ * through function 'new[]' at line 597 can be lost at line 601*/
if ((!pAttrs) ||(!pAttrsVal)) {
- delete[] pAttrs;
+ if (pAttrs)
+ delete[] pAttrs;
+ if (pAttrsVal)
+ delete[] pAttrsVal;
ALOGE("setPlayerApplicationSettingValuesNative: not have enough memeory");
return;
}
diff --git a/jni/com_android_bluetooth_btservice_AdapterService.cpp b/jni/com_android_bluetooth_btservice_AdapterService.cpp
old mode 100755
new mode 100644
index a8d11bb..30724f4
--- a/jni/com_android_bluetooth_btservice_AdapterService.cpp
+++ b/jni/com_android_bluetooth_btservice_AdapterService.cpp
@@ -23,6 +23,8 @@
#include "android_runtime/AndroidRuntime.h"
#include "android_runtime/Log.h"
+#include <hardware/vendor.h>
+
#include <string.h>
#include <pthread.h>
@@ -55,10 +57,11 @@
static const bt_interface_t *sBluetoothInterface = NULL;
static const btsock_interface_t *sBluetoothSocketInterface = NULL;
+static const btvendor_interface_t *sBluetoothVendorInterface = NULL;
static JNIEnv *callbackEnv = NULL;
-static jobject sJniAdapterServiceObj;
-static jobject sJniCallbacksObj;
+static jobject sJniAdapterServiceObj = NULL;
+static jobject sJniCallbacksObj = NULL;
static jfieldID sJniCallbacksField;
@@ -94,9 +97,11 @@
return;
}
ALOGV("%s: Status is: %d", __FUNCTION__, status);
-
- callbackEnv->CallVoidMethod(sJniCallbacksObj, method_stateChangeCallback, (jint)status);
-
+ if(sJniCallbacksObj) {
+ callbackEnv->CallVoidMethod(sJniCallbacksObj, method_stateChangeCallback, (jint)status);
+ } else {
+ ALOGE("JNI ERROR : JNI reference already cleaned : adapter_state_change_callback", __FUNCTION__);
+ }
checkAndClearExceptionFromCallback(callbackEnv, __FUNCTION__);
}
@@ -174,8 +179,10 @@
return;
}
- callbackEnv->CallVoidMethod(sJniCallbacksObj, method_adapterPropertyChangedCallback, types,
- props);
+ if (sJniCallbacksObj) {
+ callbackEnv->CallVoidMethod(sJniCallbacksObj, method_adapterPropertyChangedCallback, types,
+ props);
+ }
checkAndClearExceptionFromCallback(callbackEnv, __FUNCTION__);
callbackEnv->DeleteLocalRef(props);
callbackEnv->DeleteLocalRef(types);
@@ -244,8 +251,10 @@
return;
}
- callbackEnv->CallVoidMethod(sJniCallbacksObj, method_devicePropertyChangedCallback, addr,
- types, props);
+ if (sJniCallbacksObj) {
+ callbackEnv->CallVoidMethod(sJniCallbacksObj, method_devicePropertyChangedCallback, addr,
+ types, props);
+ }
checkAndClearExceptionFromCallback(callbackEnv, __FUNCTION__);
callbackEnv->DeleteLocalRef(props);
callbackEnv->DeleteLocalRef(types);
@@ -286,7 +295,9 @@
remote_device_properties_callback(BT_STATUS_SUCCESS, (bt_bdaddr_t *)properties[addr_index].val,
num_properties, properties);
- callbackEnv->CallVoidMethod(sJniCallbacksObj, method_deviceFoundCallback, addr);
+ if (sJniCallbacksObj) {
+ callbackEnv->CallVoidMethod(sJniCallbacksObj, method_deviceFoundCallback, addr);
+ }
checkAndClearExceptionFromCallback(callbackEnv, __FUNCTION__);
callbackEnv->DeleteLocalRef(addr);
}
@@ -309,8 +320,10 @@
}
callbackEnv->SetByteArrayRegion(addr, 0, sizeof(bt_bdaddr_t), (jbyte *)bd_addr);
- callbackEnv->CallVoidMethod(sJniCallbacksObj, method_bondStateChangeCallback, (jint) status,
- addr, (jint)state);
+ if (sJniCallbacksObj) {
+ callbackEnv->CallVoidMethod(sJniCallbacksObj, method_bondStateChangeCallback, (jint) status,
+ addr, (jint)state);
+ }
checkAndClearExceptionFromCallback(callbackEnv, __FUNCTION__);
callbackEnv->DeleteLocalRef(addr);
}
@@ -334,8 +347,10 @@
}
callbackEnv->SetByteArrayRegion(addr, 0, sizeof(bt_bdaddr_t), (jbyte *)bd_addr);
- callbackEnv->CallVoidMethod(sJniCallbacksObj, method_aclStateChangeCallback, (jint) status,
- addr, (jint)state);
+ if (sJniCallbacksObj) {
+ callbackEnv->CallVoidMethod(sJniCallbacksObj, method_aclStateChangeCallback, (jint) status,
+ addr, (jint)state);
+ }
checkAndClearExceptionFromCallback(callbackEnv, __FUNCTION__);
callbackEnv->DeleteLocalRef(addr);
}
@@ -348,9 +363,10 @@
ALOGV("%s: DiscoveryState:%d ", __FUNCTION__, state);
- callbackEnv->CallVoidMethod(sJniCallbacksObj, method_discoveryStateChangeCallback,
- (jint)state);
-
+ if (sJniCallbacksObj) {
+ callbackEnv->CallVoidMethod(sJniCallbacksObj, method_discoveryStateChangeCallback,
+ (jint)state);
+ }
checkAndClearExceptionFromCallback(callbackEnv, __FUNCTION__);
}
@@ -376,8 +392,10 @@
callbackEnv->SetByteArrayRegion(devname, 0, sizeof(bt_bdname_t), (jbyte*)bdname);
+ if (sJniCallbacksObj) {
callbackEnv->CallVoidMethod(sJniCallbacksObj, method_pinRequestCallback, addr, devname, cod,
min_16_digits);
+ }
checkAndClearExceptionFromCallback(callbackEnv, __FUNCTION__);
callbackEnv->DeleteLocalRef(addr);
@@ -411,8 +429,10 @@
if (devname == NULL) goto Fail;
callbackEnv->SetByteArrayRegion(devname, 0, sizeof(bt_bdname_t), (jbyte*)bdname);
- callbackEnv->CallVoidMethod(sJniCallbacksObj, method_sspRequestCallback, addr, devname, cod,
- (jint) pairing_variant, pass_key);
+ if (sJniCallbacksObj) {
+ callbackEnv->CallVoidMethod(sJniCallbacksObj, method_sspRequestCallback, addr, devname, cod,
+ (jint) pairing_variant, pass_key);
+ }
checkAndClearExceptionFromCallback(callbackEnv, __FUNCTION__);
callbackEnv->DeleteLocalRef(addr);
@@ -477,9 +497,13 @@
callbackEnv->DeleteLocalRef(uidObj);
}
- callbackEnv->CallVoidMethod(sJniAdapterServiceObj, method_energyInfo, p_energy_info->status,
- p_energy_info->ctrl_state, p_energy_info->tx_time, p_energy_info->rx_time,
- p_energy_info->idle_time, p_energy_info->energy_used, array);
+ if (sJniAdapterServiceObj) {
+ callbackEnv->CallVoidMethod(sJniAdapterServiceObj, method_energyInfo, p_energy_info->status,
+ p_energy_info->ctrl_state, p_energy_info->tx_time, p_energy_info->rx_time,
+ p_energy_info->idle_time, p_energy_info->energy_used, array);
+ } else {
+ ALOGE("JNI ERROR : JNI reference already cleaned : energy_info_recv_callback", __FUNCTION__);
+ }
checkAndClearExceptionFromCallback(callbackEnv, __FUNCTION__);
callbackEnv->DeleteLocalRef(array);
@@ -499,7 +523,8 @@
callback_thread_event,
dut_mode_recv_callback,
le_test_mode_recv_callback,
- energy_info_recv_callback
+ energy_info_recv_callback,
+ NULL
};
// The callback to call when the wake alarm fires.
@@ -519,6 +544,7 @@
JNIEnv *env;
JavaVM *vm = AndroidRuntime::getJavaVM();
jint status = vm->GetEnv((void **)&env, JNI_VERSION_1_6);
+ jboolean ret = JNI_FALSE;
if (status != JNI_OK && status != JNI_EDETACHED) {
ALOGE("%s unable to get environment for JNI call", __func__);
@@ -534,9 +560,15 @@
sAlarmCallbackData = data;
jboolean jshould_wake = should_wake ? JNI_TRUE : JNI_FALSE;
- jboolean ret = env->CallBooleanMethod(sJniAdapterServiceObj, method_setWakeAlarm,
+ if (sJniAdapterServiceObj) {
+ ret = env->CallBooleanMethod(sJniAdapterServiceObj, method_setWakeAlarm,
(jlong)delay_millis, jshould_wake);
+ } else {
+ ALOGE("JNI ERROR : JNI reference already cleaned : set_wake_alarm_callout", __FUNCTION__);
+ }
+
if (!ret) {
+ ALOGE("%s setWakeAlarm failed:ret= %d ", __func__, ret);
sAlarmCallback = NULL;
sAlarmCallbackData = NULL;
}
@@ -564,10 +596,14 @@
jint ret = BT_STATUS_SUCCESS;
jstring lock_name_jni = env->NewStringUTF(lock_name);
if (lock_name_jni) {
- bool acquired = env->CallBooleanMethod(sJniAdapterServiceObj,
- method_acquireWakeLock, lock_name_jni);
- if (!acquired) ret = BT_STATUS_WAKELOCK_ERROR;
- env->DeleteLocalRef(lock_name_jni);
+ if (sJniAdapterServiceObj) {
+ bool acquired = env->CallBooleanMethod(sJniAdapterServiceObj,
+ method_acquireWakeLock, lock_name_jni);
+ if (!acquired) ret = BT_STATUS_WAKELOCK_ERROR;
+ env->DeleteLocalRef(lock_name_jni);
+ } else {
+ ALOGE("JNI ERROR : JNI reference already cleaned : acquire_wake_lock_callout", __FUNCTION__);
+ }
} else {
ALOGE("%s unable to allocate string: %s", __func__, lock_name);
ret = BT_STATUS_NOMEM;
@@ -596,10 +632,14 @@
jint ret = BT_STATUS_SUCCESS;
jstring lock_name_jni = env->NewStringUTF(lock_name);
if (lock_name_jni) {
- bool released = env->CallBooleanMethod(sJniAdapterServiceObj,
- method_releaseWakeLock, lock_name_jni);
- if (!released) ret = BT_STATUS_WAKELOCK_ERROR;
- env->DeleteLocalRef(lock_name_jni);
+ if (sJniAdapterServiceObj) {
+ bool released = env->CallBooleanMethod(sJniAdapterServiceObj,
+ method_releaseWakeLock, lock_name_jni);
+ if (!released) ret = BT_STATUS_WAKELOCK_ERROR;
+ env->DeleteLocalRef(lock_name_jni);
+ } else {
+ ALOGE("JNI ERROR : JNI reference already cleaned : release_wake_lock_callout", __FUNCTION__);
+ }
} else {
ALOGE("%s unable to allocate string: %s", __func__, lock_name);
ret = BT_STATUS_NOMEM;
@@ -701,11 +741,15 @@
env->FindClass("android/bluetooth/UidTraffic"));
sJniAdapterServiceObj = env->NewGlobalRef(obj);
- sJniCallbacksObj = env->NewGlobalRef(env->GetObjectField(obj, sJniCallbacksField));
+ if (sJniCallbacksField) {
+ sJniCallbacksObj = env->NewGlobalRef(env->GetObjectField(obj, sJniCallbacksField));
+ } else {
+ ALOGE("Error: sJniCallbacksField is null\n");
+ }
if (sBluetoothInterface) {
int ret = sBluetoothInterface->init(&sBluetoothCallbacks);
- if (ret != BT_STATUS_SUCCESS) {
+ if (ret != BT_STATUS_SUCCESS && ret != BT_STATUS_DONE) {
ALOGE("Error while setting the callbacks: %d\n", ret);
sBluetoothInterface = NULL;
return JNI_FALSE;
@@ -737,10 +781,19 @@
sBluetoothInterface->cleanup();
ALOGI("%s: return from cleanup",__FUNCTION__);
- env->DeleteGlobalRef(sJniCallbacksObj);
- env->DeleteGlobalRef(sJniAdapterServiceObj);
- env->DeleteGlobalRef(android_bluetooth_UidTraffic.clazz);
- android_bluetooth_UidTraffic.clazz = NULL;
+ if (sJniCallbacksObj) {
+ env->DeleteGlobalRef(sJniCallbacksObj);
+ sJniCallbacksObj = NULL;
+ }
+ if (sJniAdapterServiceObj) {
+ env->DeleteGlobalRef(sJniAdapterServiceObj);
+ sJniAdapterServiceObj = NULL;
+ }
+ if (android_bluetooth_UidTraffic.clazz) {
+ env->DeleteGlobalRef(android_bluetooth_UidTraffic.clazz);
+ android_bluetooth_UidTraffic.clazz = NULL;
+ }
+
return JNI_TRUE;
}
@@ -1073,6 +1126,67 @@
return result;
}
+static int getSocketOptNative(JNIEnv *env, jobject obj, jint type, jint channel, jint optionName,
+ jbyteArray optionVal) {
+ ALOGV("%s:",__FUNCTION__);
+
+ jbyte *option_val = NULL;
+ int option_len;
+ bt_status_t status;
+
+ if (!sBluetoothSocketInterface) return -1;
+
+ option_val = env->GetByteArrayElements(optionVal, NULL);
+ if (option_val == NULL) {
+ ALOGE("getSocketOptNative :jniThrowIOException ");
+ jniThrowIOException(env, EINVAL);
+ return -1;
+ }
+
+ if ( (status = sBluetoothSocketInterface->get_sock_opt((btsock_type_t)type, channel,
+ (btsock_option_type_t) optionName, (void *) option_val, &option_len)) !=
+ BT_STATUS_SUCCESS) {
+ ALOGE("get_sock_opt failed: %d", status);
+ goto Fail;
+ }
+ env->ReleaseByteArrayElements(optionVal, option_val, 0);
+
+ return option_len;
+Fail:
+ env->ReleaseByteArrayElements(optionVal, option_val, 0);
+ return -1;
+}
+
+static int setSocketOptNative(JNIEnv *env, jobject obj, jint type, jint channel, jint optionName,
+ jbyteArray optionVal, jint optionLen) {
+ ALOGV("%s:",__FUNCTION__);
+
+ jbyte *option_val = NULL;
+ bt_status_t status;
+
+ if (!sBluetoothSocketInterface) return -1;
+
+ option_val = env->GetByteArrayElements(optionVal, NULL);
+ if (option_val == NULL) {
+ ALOGE("setSocketOptNative:jniThrowIOException ");
+ jniThrowIOException(env, EINVAL);
+ return -1;
+ }
+
+ if ( (status = sBluetoothSocketInterface->set_sock_opt((btsock_type_t)type, channel,
+ (btsock_option_type_t) optionName, (void *) option_val, optionLen)) !=
+ BT_STATUS_SUCCESS) {
+ ALOGE("set_sock_opt failed: %d", status);
+ goto Fail;
+ }
+ env->ReleaseByteArrayElements(optionVal, option_val, 0);
+
+ return 0;
+Fail:
+ env->ReleaseByteArrayElements(optionVal, option_val, 0);
+ return -1;
+}
+
static jboolean getRemoteServicesNative(JNIEnv *env, jobject obj, jbyteArray address) {
ALOGV("%s:",__FUNCTION__);
@@ -1295,7 +1409,10 @@
{"dumpNative", "(Ljava/io/FileDescriptor;[Ljava/lang/String;)V", (void*) dumpNative},
{"factoryResetNative", "()Z", (void*)factoryResetNative},
{"interopDatabaseClearNative", "()V", (void*) interopDatabaseClearNative},
- {"interopDatabaseAddNative", "(I[BI)V", (void*) interopDatabaseAddNative}
+ {"interopDatabaseAddNative", "(I[BI)V", (void*) interopDatabaseAddNative},
+ {"getSocketOptNative", "(III[B)I", (void*) getSocketOptNative},
+ {"setSocketOptNative", "(III[BI)I", (void*) setSocketOptNative}
+
};
int register_com_android_bluetooth_btservice_AdapterService(JNIEnv* env)
@@ -1383,5 +1500,10 @@
return JNI_ERR;
}
+ if ((status = android::register_com_android_bluetooth_btservice_vendor(e)) < 0) {
+ ALOGE("jni vendor registration failure: %d", status);
+ return JNI_ERR;
+ }
+
return JNI_VERSION_1_6;
}
diff --git a/jni/com_android_bluetooth_btservice_vendor.cpp b/jni/com_android_bluetooth_btservice_vendor.cpp
new file mode 100644
index 0000000..5444f1f
--- /dev/null
+++ b/jni/com_android_bluetooth_btservice_vendor.cpp
@@ -0,0 +1,172 @@
+/*
+ * Copyright (C) 2013,2016 The Linux Foundation. All rights reserved
+ * Not a Contribution.
+ * Copyright (C) 2012 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#define LOG_TAG "BluetoothVendorJni"
+
+#include "com_android_bluetooth.h"
+#include <hardware/vendor.h>
+#include "utils/Log.h"
+#include "android_runtime/AndroidRuntime.h"
+
+
+#define CHECK_CALLBACK_ENV \
+ if (!checkCallbackThread()) { \
+ ALOGE("Callback: '%s' is not called on the correct thread", __FUNCTION__);\
+ return; \
+ }
+
+namespace android {
+
+static jmethodID method_onBredrCleanup;
+
+static btvendor_interface_t *sBluetoothVendorInterface = NULL;
+static jobject mCallbacksObj = NULL;
+static JNIEnv *sCallbackEnv = NULL;
+
+static bool checkCallbackThread() {
+ // Always fetch the latest callbackEnv from AdapterService.
+ // Caching this could cause this sCallbackEnv to go out-of-sync
+ // with the AdapterService's ENV if an ASSOCIATE/DISASSOCIATE event
+ // is received
+ //if (sCallbackEnv == NULL) {
+ sCallbackEnv = getCallbackEnv();
+ //}
+ JNIEnv* env = AndroidRuntime::getJNIEnv();
+ if (sCallbackEnv != env || sCallbackEnv == NULL) return false;
+ return true;
+}
+
+
+static void bredr_cleanup_callback(bool status){
+
+ ALOGI("%s", __FUNCTION__);
+
+ CHECK_CALLBACK_ENV
+
+ sCallbackEnv->CallVoidMethod(mCallbacksObj, method_onBredrCleanup, (jboolean)status);
+ checkAndClearExceptionFromCallback(sCallbackEnv, __FUNCTION__);
+}
+
+static btvendor_callbacks_t sBluetoothVendorCallbacks = {
+ sizeof(sBluetoothVendorCallbacks),
+ bredr_cleanup_callback
+};
+
+static void classInitNative(JNIEnv* env, jclass clazz) {
+
+ method_onBredrCleanup = env->GetMethodID(clazz, "onBredrCleanup", "(Z)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 ( (sBluetoothVendorInterface = (btvendor_interface_t *)
+ btInf->get_profile_interface(BT_PROFILE_VENDOR_ID)) == NULL) {
+ ALOGE("Failed to get Bluetooth Vendor Interface");
+ return;
+ }
+
+ if ( (status = sBluetoothVendorInterface->init(&sBluetoothVendorCallbacks))
+ != BT_STATUS_SUCCESS) {
+ ALOGE("Failed to initialize Bluetooth Vendor, status: %d", status);
+ sBluetoothVendorInterface = NULL;
+ return;
+ }
+
+ mCallbacksObj = env->NewGlobalRef(object);
+}
+
+static void cleanupNative(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 (sBluetoothVendorInterface !=NULL) {
+ ALOGW("Cleaning up Bluetooth Vendor Interface...");
+ sBluetoothVendorInterface->cleanup();
+ sBluetoothVendorInterface = NULL;
+ }
+
+ if (mCallbacksObj != NULL) {
+ ALOGW("Cleaning up Bluetooth Vendor callback object");
+ env->DeleteGlobalRef(mCallbacksObj);
+ mCallbacksObj = NULL;
+ }
+
+ env->DeleteGlobalRef(mCallbacksObj);
+}
+
+static bool bredrcleanupNative(JNIEnv *env, jobject obj) {
+
+ ALOGI("%s", __FUNCTION__);
+
+ jboolean result = JNI_FALSE;
+ if (!sBluetoothVendorInterface) return result;
+
+ sBluetoothVendorInterface->bredrcleanup();
+ return JNI_TRUE;
+}
+
+static bool ssrcleanupNative(JNIEnv *env, jobject obj, jboolean cleanup) {
+
+ ALOGI("%s", __FUNCTION__);
+
+ jboolean result = JNI_FALSE;
+ if (!sBluetoothVendorInterface) return result;
+
+ sBluetoothVendorInterface->ssrcleanup();
+ ALOGI("%s: return from cleanup",__FUNCTION__);
+ if (cleanup == JNI_TRUE) {
+ ALOGI("%s: SSR Cleanup - DISABLE Timeout ",__FUNCTION__);
+ }
+ return JNI_TRUE;
+}
+
+static JNINativeMethod sMethods[] = {
+ {"classInitNative", "()V", (void *) classInitNative},
+ {"initNative", "()V", (void *) initNative},
+ {"cleanupNative", "()V", (void *) cleanupNative},
+ {"ssrcleanupNative", "(Z)V", (void*) ssrcleanupNative},
+ {"bredrcleanupNative", "()V", (void*) bredrcleanupNative},
+};
+
+int register_com_android_bluetooth_btservice_vendor(JNIEnv* env)
+{
+ ALOGE("%s:",__FUNCTION__);
+ return jniRegisterNativeMethods(env, "com/android/bluetooth/btservice/Vendor",
+ sMethods, NELEM(sMethods));
+}
+
+} /* namespace android */
diff --git a/jni/com_android_bluetooth_hfp.cpp b/jni/com_android_bluetooth_hfp.cpp
index 3b6c2ce..983ab2f 100644
--- a/jni/com_android_bluetooth_hfp.cpp
+++ b/jni/com_android_bluetooth_hfp.cpp
@@ -850,6 +850,20 @@
}
+static jboolean voipNetworkWifiInfoNative(JNIEnv *env, jobject object,
+ jboolean isVoipStarted, jboolean isNetworkWifi) {
+ bt_status_t status;
+ if (!sBluetoothHfpInterface) return JNI_FALSE;
+
+ if ( (status = sBluetoothHfpInterface->voip_network_type_wifi(isVoipStarted ?
+ BTHF_VOIP_STATE_STARTED : BTHF_VOIP_STATE_STOPPED, isNetworkWifi ?
+ BTHF_VOIP_CALL_NETWORK_TYPE_WIFI : BTHF_VOIP_CALL_NETWORK_TYPE_MOBILE))
+ != BT_STATUS_SUCCESS) {
+ ALOGE("Failed sending VOIP network type, status: %d", status);
+ }
+ return (status == BT_STATUS_SUCCESS) ? JNI_TRUE : JNI_FALSE;
+}
+
static JNINativeMethod sMethods[] = {
{"classInitNative", "()V", (void *) classInitNative},
{"initializeNative", "(I)V", (void *) initializeNative},
@@ -870,6 +884,7 @@
{"clccResponseNative", "(IIIIZLjava/lang/String;I[B)Z", (void *) clccResponseNative},
{"phoneStateChangeNative", "(IIILjava/lang/String;I)Z", (void *) phoneStateChangeNative},
{"configureWBSNative", "([BI)Z", (void *) configureWBSNative},
+ {"voipNetworkWifiInfoNative", "(ZZ)Z", (void *)voipNetworkWifiInfoNative},
};
int register_com_android_bluetooth_hfp(JNIEnv* env)
diff --git a/jni/com_android_bluetooth_hfpclient.cpp b/jni/com_android_bluetooth_hfpclient.cpp
index 333035c..a5945ab 100644
--- a/jni/com_android_bluetooth_hfpclient.cpp
+++ b/jni/com_android_bluetooth_hfpclient.cpp
@@ -56,6 +56,8 @@
static jmethodID method_onInBandRing;
static jmethodID method_onLastVoiceTagNumber;
static jmethodID method_onRingIndication;
+static jmethodID method_onCgmi;
+static jmethodID method_onCgmm;
static bool checkCallbackThread() {
// Always fetch the latest callbackEnv from AdapterService.
@@ -251,6 +253,28 @@
checkAndClearExceptionFromCallback(sCallbackEnv, __FUNCTION__);
}
+static void cgmi_cb (const char *str) {
+ jstring js_manf_id;
+
+ CHECK_CALLBACK_ENV
+
+ js_manf_id = sCallbackEnv->NewStringUTF(str);
+ sCallbackEnv->CallVoidMethod(mCallbacksObj, method_onCgmi, js_manf_id);
+ checkAndClearExceptionFromCallback(sCallbackEnv, __FUNCTION__);
+ sCallbackEnv->DeleteLocalRef(js_manf_id);
+}
+
+static void cgmm_cb (const char *str) {
+ jstring js_manf_model;
+
+ CHECK_CALLBACK_ENV
+
+ js_manf_model = sCallbackEnv->NewStringUTF(str);
+ sCallbackEnv->CallVoidMethod(mCallbacksObj, method_onCgmm, js_manf_model);
+ checkAndClearExceptionFromCallback(sCallbackEnv, __FUNCTION__);
+ sCallbackEnv->DeleteLocalRef(js_manf_model);
+}
+
static bthf_client_callbacks_t sBluetoothHfpClientCallbacks = {
sizeof(sBluetoothHfpClientCallbacks),
connection_state_cb,
@@ -274,6 +298,8 @@
in_band_ring_cb,
last_voice_tag_number_cb,
ring_indication_cb,
+ cgmi_cb,
+ cgmm_cb,
};
static void classInitNative(JNIEnv* env, jclass clazz) {
@@ -299,6 +325,8 @@
method_onLastVoiceTagNumber = env->GetMethodID(clazz, "onLastVoiceTagNumber",
"(Ljava/lang/String;)V");
method_onRingIndication = env->GetMethodID(clazz, "onRingIndication","()V");
+ method_onCgmi = env->GetMethodID(clazz, "onCgmi","(Ljava/lang/String;)V");
+ method_onCgmm = env->GetMethodID(clazz, "onCgmm","(Ljava/lang/String;)V");
ALOGI("%s succeeds", __FUNCTION__);
}
@@ -481,9 +509,16 @@
number = env->GetStringUTFChars(number_str, NULL);
}
- if ( (status = sBluetoothHfpClientInterface->dial(number)) != BT_STATUS_SUCCESS) {
- ALOGE("Failed to dial, status: %d", status);
+ if (number != NULL) {
+ if ( (status = sBluetoothHfpClientInterface->dial(number)) != BT_STATUS_SUCCESS) {
+ ALOGE("Failed to dial, status: %d", status);
+ }
+ } else {
+ if ( (status = sBluetoothHfpClientInterface->dial("")) != BT_STATUS_SUCCESS) {
+ ALOGE("Failed to dial, status: %d", status);
+ }
}
+
if (number != NULL) {
env->ReleaseStringUTFChars(number_str, number);
}
diff --git a/jni/com_android_bluetooth_hid.cpp b/jni/com_android_bluetooth_hid.cpp
index 1630a5d..0565631 100644
--- a/jni/com_android_bluetooth_hid.cpp
+++ b/jni/com_android_bluetooth_hid.cpp
@@ -37,6 +37,7 @@
static jmethodID method_onGetProtocolMode;
static jmethodID method_onGetReport;
static jmethodID method_onHandshake;
+static jmethodID method_onGetIdleTime;
static jmethodID method_onVirtualUnplug;
static const bthh_interface_t *sBluetoothHidInterface = NULL;
@@ -97,6 +98,29 @@
sCallbackEnv->DeleteLocalRef(addr);
}
+static void get_idle_time_callback(bt_bdaddr_t *bd_addr, bthh_status_t hh_status, int idle_time) {
+ jbyteArray addr;
+
+ CHECK_CALLBACK_ENV
+ if (hh_status != BTHH_OK) {
+ ALOGE("BTHH Status is not OK!");
+ checkAndClearExceptionFromCallback(sCallbackEnv, __FUNCTION__);
+ return;
+ }
+
+ addr = sCallbackEnv->NewByteArray(sizeof(bt_bdaddr_t));
+ if (!addr) {
+ ALOGE("Fail to new jbyteArray bd addr for get protocal mode callback");
+ checkAndClearExceptionFromCallback(sCallbackEnv, __FUNCTION__);
+ return;
+ }
+ sCallbackEnv->SetByteArrayRegion(addr, 0, sizeof(bt_bdaddr_t), (jbyte *) bd_addr);
+
+ sCallbackEnv->CallVoidMethod(mCallbacksObj, method_onGetIdleTime, addr, (jint) idle_time);
+ checkAndClearExceptionFromCallback(sCallbackEnv, __FUNCTION__);
+ sCallbackEnv->DeleteLocalRef(addr);
+}
+
static void get_report_callback(bt_bdaddr_t *bd_addr, bthh_status_t hh_status, uint8_t *rpt_data, int rpt_size) {
jbyteArray addr;
jbyteArray data;
@@ -187,7 +211,7 @@
connection_state_callback,
NULL,
get_protocol_mode_callback,
- NULL,
+ get_idle_time_callback,
get_report_callback,
virtual_unplug_callback,
handshake_callback
@@ -198,6 +222,7 @@
static void classInitNative(JNIEnv* env, jclass clazz) {
method_onConnectStateChanged = env->GetMethodID(clazz, "onConnectStateChanged", "([BI)V");
method_onGetProtocolMode = env->GetMethodID(clazz, "onGetProtocolMode", "([BI)V");
+ method_onGetIdleTime = env->GetMethodID(clazz, "onGetIdleTime", "([BI)V");
method_onGetReport = env->GetMethodID(clazz, "onGetReport", "([B[BI)V");
method_onHandshake = env->GetMethodID(clazz, "onHandshake", "([BI)V");
method_onVirtualUnplug = env->GetMethodID(clazz, "onVirtualUnplug", "([BI)V");
@@ -315,7 +340,7 @@
bt_status_t status;
jbyte *addr;
jboolean ret = JNI_TRUE;
- bthh_protocol_mode_t protocolMode;
+ bthh_protocol_mode_t protocolMode = BTHH_UNSUPPORTED_MODE;
if (!sBluetoothHidInterface) return JNI_FALSE;
addr = env->GetByteArrayElements(address, NULL);
@@ -461,7 +486,7 @@
const char *c_report = env->GetStringUTFChars(report, NULL);
if ( (status = sBluetoothHidInterface->send_data((bt_bdaddr_t *) addr, (char*) c_report)) !=
BT_STATUS_SUCCESS) {
- ALOGE("Failed set report, status: %d", status);
+ ALOGE("Failed send data, status: %d", status);
ret = JNI_FALSE;
}
env->ReleaseStringUTFChars(report, c_report);
@@ -471,6 +496,50 @@
}
+static jboolean getIdleTimeNative(JNIEnv *env, jobject object, jbyteArray address) {
+ bt_status_t status;
+ jbyte *addr;
+ jboolean ret = JNI_TRUE;
+ if (!sBluetoothHidInterface) return JNI_FALSE;
+
+ addr = env->GetByteArrayElements(address, NULL);
+ if (!addr) {
+ ALOGE("Bluetooth device address null");
+ return JNI_FALSE;
+ }
+
+ if ( (status = sBluetoothHidInterface->get_idle_time((bt_bdaddr_t *) addr)) !=
+ BT_STATUS_SUCCESS) {
+ ALOGE("Failed get idle time, status: %d", status);
+ ret = JNI_FALSE;
+ }
+ env->ReleaseByteArrayElements(address, addr, 0);
+
+ return ret;
+}
+
+static jboolean setIdleTimeNative(JNIEnv *env, jobject object, jbyteArray address, jbyte idle_time) {
+ bt_status_t status;
+ jbyte *addr;
+ jboolean ret = JNI_TRUE;
+ if (!sBluetoothHidInterface) return JNI_FALSE;
+
+ addr = env->GetByteArrayElements(address, NULL);
+ if (!addr) {
+ ALOGE("Bluetooth device address null");
+ return JNI_FALSE;
+ }
+
+ if ( (status = sBluetoothHidInterface->set_idle_time((bt_bdaddr_t *) addr, idle_time)) !=
+ BT_STATUS_SUCCESS) {
+ ALOGE("Failed set idle time, status: %d", status);
+ ret = JNI_FALSE;
+ }
+ env->ReleaseByteArrayElements(address, addr, 0);
+
+ return ret;
+}
+
static JNINativeMethod sMethods[] = {
{"classInitNative", "()V", (void *) classInitNative},
{"initializeNative", "()V", (void *) initializeNative},
@@ -483,6 +552,8 @@
{"getReportNative", "([BBBI)Z", (void *) getReportNative},
{"setReportNative", "([BBLjava/lang/String;)Z", (void *) setReportNative},
{"sendDataNative", "([BLjava/lang/String;)Z", (void *) sendDataNative},
+ {"getIdleTimeNative", "([B)Z", (void *) getIdleTimeNative},
+ {"setIdleTimeNative", "([BB)Z", (void *) setIdleTimeNative},
};
int register_com_android_bluetooth_hid(JNIEnv* env)
diff --git a/jni/com_android_bluetooth_pan.cpp b/jni/com_android_bluetooth_pan.cpp
index 30056e9..3a2bb8b 100644
--- a/jni/com_android_bluetooth_pan.cpp
+++ b/jni/com_android_bluetooth_pan.cpp
@@ -59,6 +59,10 @@
static void control_state_callback(btpan_control_state_t state, int local_role, bt_status_t error,
const char* ifname) {
debug("state:%d, local_role:%d, ifname:%s", state, local_role, ifname);
+ if (mCallbacksObj == NULL) {
+ error("Callbacks Obj is NULL: '%s", __FUNCTION__);
+ return;
+ }
CHECK_CALLBACK_ENV
jstring js_ifname = sCallbackEnv->NewStringUTF(ifname);
sCallbackEnv->CallVoidMethod(mCallbacksObj, method_onControlStateChanged, (jint)local_role, (jint)state,
@@ -70,6 +74,10 @@
int local_role, int remote_role) {
jbyteArray addr;
debug("state:%d, local_role:%d, remote_role:%d", state, local_role, remote_role);
+ if (mCallbacksObj == NULL) {
+ error("Callbacks Obj is NULL: '%s", __FUNCTION__);
+ return;
+ }
CHECK_CALLBACK_ENV
addr = sCallbackEnv->NewByteArray(sizeof(bt_bdaddr_t));
if (!addr) {
diff --git a/lib/mapapi/com/android/bluetooth/mapapi/BluetoothMapEmailContract.java b/lib/mapapi/com/android/bluetooth/mapapi/BluetoothMapEmailContract.java
new file mode 100755
index 0000000..1dd16d8
--- /dev/null
+++ b/lib/mapapi/com/android/bluetooth/mapapi/BluetoothMapEmailContract.java
@@ -0,0 +1,373 @@
+/*
+ * Copyright (c) 2015, The Linux Foundation. All rights reserved.
+ * Not a Contribution.
+ * Copyright (C) 2013 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.mapapi;
+
+import android.content.ContentResolver;
+import android.net.Uri;
+import com.android.bluetooth.mapapi.BluetoothMapContract.MessageColumns;
+import com.android.bluetooth.mapapi.BluetoothMapContract.EmailMessageColumns;
+import com.android.bluetooth.mapapi.BluetoothMapContract.FolderColumns;
+
+
+/**
+ * This class defines the minimum sets of data needed for a client to
+ * implement to claim support for the Bluetooth Message Access Profile.
+ * Access to three data sets are needed:
+ * <ul>
+ * <li>Message data set containing lists of messages.</li>
+ * <li>Account data set containing info on the existing accounts, and whether to expose
+ * these accounts. The content of the account data set is often sensitive information,
+ * hence care must be taken, not to reveal any personal information nor passwords.
+ * The accounts in this data base will be exposed in the settings menu, where the user
+ * is able to enable and disable the EXPOSE_FLAG, and thereby provide access to an
+ * account from another device, without any password protection the e-mail client
+ * might provide.</li>
+ * <li>Folder data set with the folder structure for the messages. Each message is linked to an
+ * entry in this data set.</li>
+ * <li>Conversation data set with the thread structure of the messages. Each message is linked
+ * to an entry in this data set.</li>
+ * </ul>
+ *
+ * To enable that the Bluetooth Message Access Server can detect the content provider implementing
+ * this interface, the {@code provider} tag for the Bluetooth related content provider must
+ * have an intent-filter like the following in the manifest:
+ * <pre class="prettyprint"><provider android:authorities="[PROVIDER AUTHORITY]"
+ android:exported="true"
+ android:enabled="true"
+ android:permission="android.permission.BLUETOOTH_MAP">
+ * ...
+ * <intent-filter>
+ <action android:name="android.content.action.BLEUETOOT_MAP_PROVIDER" />
+ </intent-filter>
+ * ...
+ * </provider>
+ * [PROVIDER AUTHORITY] shall be the providers authority value which implements this
+ * contract. Only a single authority shall be used. The android.permission.BLUETOOTH_MAP
+ * permission is needed for the provider.
+ */
+public final class BluetoothMapEmailContract {
+ /**
+ * Constructor - should not be used
+ */
+ private BluetoothMapEmailContract(){
+ /* class should not be instantiated */
+ }
+
+ public static final String EMAIL_AUTHORITY = "com.android.email.provider";
+ public static final String ACTION_CHECK_MAIL =
+ "org.codeaurora.email.intent.action.MAIL_SERVICE_WAKEUP";
+ public static final String EXTRA_ACCOUNT = "org.codeaurora.email.intent.extra.ACCOUNT";
+ public static final String ACTION_DELETE_MESSAGE =
+ "org.codeaurora.email.intent.action.MAIL_SERVICE_DELETE_MESSAGE";
+ public static final String ACTION_MOVE_MESSAGE =
+ "org.codeaurora.email.intent.action.MAIL_SERVICE_MOVE_MESSAGE";
+ public static final String ACTION_MESSAGE_READ =
+ "org.codeaurora.email.intent.action.MAIL_SERVICE_MESSAGE_READ";
+ public static final String ACTION_SEND_PENDING_MAIL =
+ "org.codeaurora.email.intent.action.MAIL_SERVICE_SEND_PENDING";
+ public static final String EXTRA_MESSAGE_ID = "org.codeaurora.email.intent.extra.MESSAGE_ID";
+ public static final String EXTRA_MESSAGE_INFO =
+ "org.codeaurora.email.intent.extra.MESSAGE_INFO";
+
+ /**
+ * Build URI representing the given Accounts data-set in a
+ * Bluetooth provider. When queried, the direct URI for the account
+ * with the given accountID is returned.
+ */
+ public static Uri buildEmailAccountUri(String authority) {
+ return new Uri.Builder().scheme(ContentResolver.SCHEME_CONTENT)
+ .authority(authority).appendPath(EMAIL_TABLE_ACCOUNT).build();
+ }
+
+ /**
+ * Build URI representing the given Accounts data-set in a
+ * Bluetooth provider. When queried, the direct URI for the account
+ * with the given accountID is returned.
+ */
+ public static Uri buildEmailAccountUriWithId(String authority, String accountId) {
+ return new Uri.Builder().scheme(ContentResolver.SCHEME_CONTENT)
+ .authority(authority).appendPath(EMAIL_TABLE_ACCOUNT)
+ .appendPath(accountId).build();
+ }
+
+ /**
+ * Build URI representing the entire Message table in a
+ * Bluetooth provider.
+ */
+ public static Uri buildEmailMessageUri(String authority) {
+ return new Uri.Builder().scheme(ContentResolver.SCHEME_CONTENT)
+ .authority(authority)
+ .appendPath(EMAIL_TABLE_MESSAGE)
+ .build();
+ }
+
+ /**
+ * Build URI representing the entire Message table in a
+ * Bluetooth provider.
+ */
+ public static Uri buildEmailMessageUri(String authority, String accountId) {
+ return new Uri.Builder().scheme(ContentResolver.SCHEME_CONTENT)
+ .authority(authority)
+ .appendPath(accountId)
+ .appendPath(EMAIL_TABLE_MESSAGE)
+ .build();
+ }
+
+ /**
+ * Build URI representing the entire email Attachment table in a
+ * Bluetooth provider.
+ */
+ public static Uri buildEmailAttachmentUri(String authority) {
+ return new Uri.Builder().scheme(ContentResolver.SCHEME_CONTENT)
+ .authority(authority)
+ .appendPath(EMAIL_TABLE_ATTACHMENT)
+ .build();
+ }
+ /**
+ * Build URI representing the entire MessageBody table in a
+ * Bluetooth provider.
+ */
+ public static Uri buildEmailMessageBodyUri(String authority) {
+ return new Uri.Builder().scheme(ContentResolver.SCHEME_CONTENT)
+ .authority(authority)
+ .appendPath(EMAIL_TABLE_MSGBODY)
+ .build();
+ }
+ /**
+ * Build URI representing the given Message data-set in a
+ * Bluetooth provider. When queried, the direct URI for the folder
+ * with the given accountID is returned.
+ */
+ public static Uri buildMailboxUri(String authority) {
+ return new Uri.Builder().scheme(ContentResolver.SCHEME_CONTENT)
+ .authority(authority)
+ .appendPath(EMAIL_TABLE_MAILBOX)
+ .build();
+ }
+
+ /**
+ * @hide
+ */
+ public static final String EMAIL_TABLE_ACCOUNT = "account";
+ public static final String EMAIL_TABLE_MESSAGE = "message";
+ public static final String EMAIL_TABLE_ATTACHMENT = "attachment";
+ public static final String EMAIL_TABLE_MSGBODY = "body";
+ public static final String EMAIL_TABLE_MAILBOX = "mailbox";
+
+ /**
+ * Mandatory folders for the Bluetooth message access profile.
+ * The email client shall at least implement the following root folders.
+ * E.g. as a mapping for them such that the naming will match the underlying
+ * matching folder ID's.
+ */
+ public static final String FOLDER_NAME_INBOX = "INBOX";
+ public static final String FOLDER_NAME_SENT = "SENT";
+ public static final String FOLDER_NAME_OUTBOX = "OUTBOX";
+ public static final String FOLDER_NAME_DRAFT = "DRAFT";
+ public static final String FOLDER_NAME_DRAFTS = "DRAFTS";
+ public static final String FOLDER_NAME_DELETED = "DELETED";
+ public static final String FOLDER_NAME_OTHER = "OTHER";
+
+ /**
+ * Folder IDs to be used with Instant Messaging virtual folders
+ */
+ public static final long FOLDER_ID_OTHER = 0;
+ public static final long FOLDER_ID_INBOX = 1;
+ public static final long FOLDER_ID_SENT = 2;
+ public static final long FOLDER_ID_DRAFT = 3;
+ public static final long FOLDER_ID_OUTBOX = 4;
+ public static final long FOLDER_ID_DELETED = 5;
+
+ // Types of mailboxes. From EmailContent.java
+ // inbox
+ public static final int TYPE_INBOX = 0;
+ // draft
+ public static final int TYPE_DRAFT = 3;
+ // outbox
+ public static final int TYPE_OUTBOX = 4;
+ // sent
+ public static final int TYPE_SENT = 5;
+ // deleted
+ public static final int TYPE_DELETED = 6;
+ // Values used in mFlagLoaded
+ public static final int FLAG_LOADED_COMPLETE = 1;
+
+ public interface EmailBodyColumns {
+ // Foreign key to the message corresponding to this body
+ public static final String MESSAGE_KEY = "messageKey";
+ // The html content itself, not returned on query
+ public static final String HTML_CONTENT = "htmlContent";
+ // The html content URI, for ContentResolver#openFileDescriptor()
+ public static final String HTML_CONTENT_URI = "htmlContentUri";
+ // The plain text content itself, not returned on query
+ public static final String TEXT_CONTENT = "textContent";
+ // The text content URI, for ContentResolver#openFileDescriptor()
+ public static final String TEXT_CONTENT_URI = "textContentUri";
+ // Replied-to or forwarded body (in html form)
+ }
+
+ public interface ExtEmailMessageColumns extends EmailMessageColumns {
+ public static final String RECORD_ID = "_id";
+ public static final String DISPLAY_NAME = "displayName";
+ public static final String EMAIL_ADDRESS = "emailAddress";
+ public static final String ACCOUNT_KEY = "accountKey";
+ public static final String IS_DEFAULT = "isDefault";
+ public static final String MAILBOX_KEY = "mailboxKey";
+ // The time (millis) as shown to the user in a message list [INDEX]
+ public static final String TIMESTAMP = "timeStamp";
+ public static final String EMAIL_SERVICE_NAME = "EMAIL Message Access";
+ /**
+ * Message Read flag
+ * <P>Type: INTEGER (boolean) unread = 0, read = 1</P>
+ * read/write
+ */
+ public static final String EMAIL_FLAG_READ = "flagRead";
+ /** The overall size in bytes of the message including any attachments.
+ * This value is informative only and should be the size an email client
+ * would display as size for the message.
+ * <P>Type: INTEGER </P>
+ * read-only
+ */
+ public static final String EMAIL_ATTACHMENT_SIZE = "size";
+ public static final String FLAGS = "flags";
+ public static final String FLAG_LOADED = "flagLoaded";
+ // Boolean, no attachment = 0, attachment = 1
+ public static final String EMAIL_FLAG_ATTACHMENT = "flagAttachment";
+ // Saved draft info (reusing the never-used "clientId" column)
+ public static final String DRAFT_INFO = "clientId";
+ // The message-id in the message's header
+ public static final String MESSAGE_ID = "messageId";
+ // Address lists, packed with Address.pack()
+ public static final String EMAIL_FROM_LIST = "fromList";
+ public static final String EMAIL_TO_LIST = "toList";
+ public static final String EMAIL_CC_LIST = "ccList";
+ public static final String EMAIL_BCC_LIST = "bccList";
+ public static final String EMAIL_REPLY_TO_LIST = "replyToList";
+ public static final String MEETING_INFO = "meetingInfo";
+ public static final String SNIPPET = "snippet";
+ public static final String PROTOCOL_SEARCH_INFO = "protocolSearchInfo";
+ public static final String THREAD_TOPIC = "threadTopic";
+ }
+
+ /**
+ * Message folder structure
+ * MAP enforces use of a folder structure with mandatory folders:
+ * - inbox, outbox, sent, deleted, draft
+ * User defined folders are supported.
+ * The folder table must provide filtering (use of WHERE clauses) of the following entries:
+ * - account_id (linking the folder to an e-mail account)
+ * - parent_id (linking the folders individually)
+ * The folder table must have a folder name for each entry, and the mandatory folders
+ * MUST exist for each account_id. The folders may be empty.
+ * Use the FOLDER_NAME_xxx constants for the mandatory folders. Their names must
+ * not be translated into other languages, as the folder browsing is string based, and
+ * many Bluetooth Message Clients will use these strings to navigate to the folders.
+ */
+ public interface MailBoxColumns extends FolderColumns {
+ public static final String DISPLAY_NAME = "displayName";
+ public static final String SERVER_ID = "serverId";
+ public static final String PARENT_KEY = "parentKey";
+ public static final String PARENT_SERVER_ID = "parentServerId";
+ public static final String ACCOUNT_KEY = "accountKey";
+ public static final String FOLDER_TYPE = "type";
+ }
+
+ public static final String[] BT_EMAIL_ATTACHMENT_PROJECTION = new String[] {
+ ExtEmailMessageColumns.EMAIL_ATTACHMENT_SIZE
+ };
+
+ public static final String[] BT_EMAIL_MESSAGE_PROJECTION = new String[] {
+ ExtEmailMessageColumns.RECORD_ID,
+ ExtEmailMessageColumns.MAILBOX_KEY,
+ ExtEmailMessageColumns.ACCOUNT_KEY,
+ ExtEmailMessageColumns.DISPLAY_NAME,
+ ExtEmailMessageColumns.TIMESTAMP,
+ ExtEmailMessageColumns.EMAIL_FLAG_READ,
+ MessageColumns.SUBJECT,
+ ExtEmailMessageColumns.EMAIL_FLAG_ATTACHMENT,
+ ExtEmailMessageColumns.FLAG_LOADED,
+ ExtEmailMessageColumns.FLAGS,
+ ExtEmailMessageColumns.DRAFT_INFO,
+ ExtEmailMessageColumns.MESSAGE_ID,
+ ExtEmailMessageColumns.EMAIL_FROM_LIST,
+ ExtEmailMessageColumns.EMAIL_TO_LIST,
+ ExtEmailMessageColumns.EMAIL_CC_LIST,
+ ExtEmailMessageColumns.EMAIL_BCC_LIST,
+ ExtEmailMessageColumns.EMAIL_REPLY_TO_LIST,
+ ExtEmailMessageColumns.MEETING_INFO,
+ ExtEmailMessageColumns.SNIPPET,
+ ExtEmailMessageColumns.PROTOCOL_SEARCH_INFO,
+ ExtEmailMessageColumns.THREAD_TOPIC
+ };
+
+ public static final String[] BT_EMAIL_MSG_PROJECTION_SHORT = new String[] {
+ ExtEmailMessageColumns.ACCOUNT_KEY,
+ ExtEmailMessageColumns.RECORD_ID,
+ ExtEmailMessageColumns.MAILBOX_KEY,
+ ExtEmailMessageColumns.EMAIL_FLAG_READ
+ };
+
+ public static final String[] BT_EMAIL_MSG_PROJECTION_SHORT_EXT = new String[] {
+ ExtEmailMessageColumns.ACCOUNT_KEY,
+ ExtEmailMessageColumns.RECORD_ID,
+ ExtEmailMessageColumns.MAILBOX_KEY,
+ ExtEmailMessageColumns.TIMESTAMP,
+ MessageColumns.SUBJECT,
+ ExtEmailMessageColumns.EMAIL_FROM_LIST,
+ ExtEmailMessageColumns.EMAIL_FLAG_READ
+ };
+
+ public static final String[] BT_EMAIL_BODY_CONTENT_PROJECTION = new String[] {
+ MessageColumns._ID,
+ EmailBodyColumns.MESSAGE_KEY,
+ EmailBodyColumns.HTML_CONTENT_URI,
+ EmailBodyColumns.TEXT_CONTENT_URI
+ };
+
+
+ public static final String[] BT_EMAIL_ACCOUNT_ID_PROJECTION = new String[] {
+ ExtEmailMessageColumns.RECORD_ID,
+ ExtEmailMessageColumns.DISPLAY_NAME,
+ ExtEmailMessageColumns.EMAIL_ADDRESS,
+ ExtEmailMessageColumns.IS_DEFAULT
+ };
+
+ /**
+ * A projection of all the columns in the Folder table
+ */
+ public static final String[] BT_FOLDER_PROJECTION = new String[] {
+ FolderColumns._ID,
+ FolderColumns.NAME,
+ FolderColumns.ACCOUNT_ID,
+ FolderColumns.PARENT_FOLDER_ID
+ };
+
+ /**
+ * A projection of all the columns in the Folder table
+ */
+ public static final String[] BT_EMAIL_MAILBOX_PROJECTION = new String[] {
+ FolderColumns._ID,
+ MailBoxColumns.DISPLAY_NAME,
+ MailBoxColumns.ACCOUNT_KEY,
+ MailBoxColumns.SERVER_ID,
+ MailBoxColumns.PARENT_SERVER_ID,
+ MailBoxColumns.FOLDER_TYPE
+ };
+
+}
diff --git a/res/values-af/cm_strings.xml b/res/values-af/cm_strings.xml
new file mode 100644
index 0000000..180874e
--- /dev/null
+++ b/res/values-af/cm_strings.xml
@@ -0,0 +1,20 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--Generated by crowdin.com-->
+<!--
+ Copyright (C) 2015-2016 The CyanogenMod 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.
+-->
+<resources>
+ <string name="upload_fail_waiting">Kan nie lêer stuur nie, probeer weer\u2026</string>
+</resources>
diff --git a/res/values-ar/cm_strings.xml b/res/values-ar/cm_strings.xml
new file mode 100644
index 0000000..6eb66fa
--- /dev/null
+++ b/res/values-ar/cm_strings.xml
@@ -0,0 +1,20 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--Generated by crowdin.com-->
+<!--
+ Copyright (C) 2015-2016 The CyanogenMod 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.
+-->
+<resources>
+ <string name="upload_fail_waiting">تعذر إرسال الملف، جاري إعادة المحاولة\u2026</string>
+</resources>
diff --git a/res/values-as-rIN/cm_strings.xml b/res/values-as-rIN/cm_strings.xml
new file mode 100644
index 0000000..5c1ad61
--- /dev/null
+++ b/res/values-as-rIN/cm_strings.xml
@@ -0,0 +1,20 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--Generated by crowdin.com-->
+<!--
+ Copyright (C) 2015-2016 The CyanogenMod 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.
+-->
+<resources>
+ <string name="upload_fail_waiting">ফাইল প্ৰেৰণ কৰিবলৈ অক্ষম, পুনঃচেষ্টা কৰি আছে\u2026</string>
+</resources>
diff --git a/res/values-as-rIN/strings.xml b/res/values-as-rIN/strings.xml
new file mode 100644
index 0000000..906d653
--- /dev/null
+++ b/res/values-as-rIN/strings.xml
@@ -0,0 +1,99 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--Generated by crowdin.com-->
+<!-- Copyright (C) 2007 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.
+-->
+<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <!--
+ This is the short description of a permission associated with the
+ Bluetooth Share Manager. It is displayed as part of the description of
+ any application that was granted that permission. This specific
+ permission controls access to the Bluetooth Share Manager by
+ applications that initiate Bluetooth share.
+ -->
+ <!--
+ This is the long description of a permission associated with the
+ Android Download Manager. It is displayed as part of the description
+ of any application that was granted that permission. This specific
+ permission controls access to the Download Manager by applications
+ that initiate downloads.
+ -->
+ <!-- string showed on "Share picutre via" dialog -->
+ <!-- string for "unknown device" -->
+ <!-- string for "unknown" phone number" -->
+ <!-- string for "the title of airplane mode error" -->
+ <!-- string for "error message in airplane mode" -->
+ <!-- Activate Bluetooth Confirmation Dialog -->
+ <!--Title -->
+ <!--Line 1 -->
+ <!--Line 2 -->
+ <!-- Label for a cancel button. -->
+ <!-- Label for a confirm button.-->
+ <!-- Bluetooth File Transfer Acceptance Dialog -->
+ <!--Title -->
+ <!--content -->
+ <!-- Label for a cancel button. -->
+ <!-- Label for a confirm button.-->
+ <!-- Label for timeout OK button.-->
+ <!-- content for timeout-->
+ <!-- Bluetooth File Transfer Acceptance Notification item -->
+ <!-- Inbound File Transfer Progress Notification item -->
+ <!-- label for the notification item of receiving file -->
+ <!-- label for the notification item of received file -->
+ <!-- label for the notification item of failed receiving file -->
+ <!-- Outbound File Transfer Progress Notification item -->
+ <!-- label for the notification item of sending file -->
+ <!-- label for the notification item of sent file -->
+ <!-- label for the notification item of sent file -status -->
+ <!-- label for the notification item of failed sending file -->
+ <!-- Bluetooth Download Progress Dialog -->
+ <!--Title -->
+ <!--Line 1 -->
+ <!--Line 2 -->
+ <!--Line 3 -->
+ <!--Line 4 -->
+ <!-- Label for a cancel button. -->
+ <!-- Label for a hide button.-->
+ <!--Line 1 -->
+ <!--Line 2 -->
+ <!--Line 3 -->
+ <!-- Bluetooth failed Download Dialog -->
+ <!--Line 1 -->
+ <!--Line 2 -->
+ <!--Line 3 -->
+ <!-- Label for ok button.-->
+ <!-- Bluetooth Successful Download Dialog -->
+ <!--Line 4 in "Bluetooth Download Progress Dialog" -->
+ <!-- Label for a OK button.-->
+ <!-- Bluetooth Upload Progress Dialog -->
+ <!-- Bluetooth Successful Upload Progress Dialog -->
+ <!--Line 4 -->
+ <!-- Label for a confirm button.-->
+ <!-- Bluetooth Failed Upload File Transfer Dialog -->
+ <!-- Label for a try again button.-->
+ <!-- Label for a cancel button.-->
+ <!-- Bluetooth error dialog -->
+ <!-- Bluetooth Enabling progress dialog -->
+ <!-- Bluetooth Toast Message -->
+ <!-- Bluetooth System Messages [CHAR LIMIT=NONE] -->
+ <!-- Bluetooth System Messages -->
+ <!-- Bluetooth Transfer Failure Reason -->
+ <!-- Shown when USB storage cannot be found. [CHAR LIMIT=NONE] -->
+ <!-- Bluetooth OPP Live Folder -->
+ <!-- Bluetooth OPP Transfer History -->
+ <!-- Do not translate. file name used for sharing. -->
+ <!-- Used to run Bluetooth.apk in another process if needed -->
+ <!-- Do not translate. android:sharedUserId string of this application. -->
+ <!-- Do not translate. android:process of this application. -->
+</resources>
diff --git a/res/values-as-rIN/strings_pbap.xml b/res/values-as-rIN/strings_pbap.xml
new file mode 100644
index 0000000..55f5b83
--- /dev/null
+++ b/res/values-as-rIN/strings_pbap.xml
@@ -0,0 +1,6 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--Generated by crowdin.com-->
+<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <!-- Notification title when a Bluetooth device wants to pair with us -->
+ <!-- Notification message when a Bluetooth device wants to pair with us -->
+</resources>
diff --git a/res/values-ast-rES/cm_strings.xml b/res/values-ast-rES/cm_strings.xml
new file mode 100644
index 0000000..b0f7402
--- /dev/null
+++ b/res/values-ast-rES/cm_strings.xml
@@ -0,0 +1,20 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--Generated by crowdin.com-->
+<!--
+ Copyright (C) 2015-2016 The CyanogenMod 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.
+-->
+<resources>
+ <string name="upload_fail_waiting">Imposible unviar el ficheru, reintentando\u2026</string>
+</resources>
diff --git a/res/values-ast-rES/strings.xml b/res/values-ast-rES/strings.xml
new file mode 100644
index 0000000..45f58f7
--- /dev/null
+++ b/res/values-ast-rES/strings.xml
@@ -0,0 +1,206 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--Generated by crowdin.com-->
+<!-- Copyright (C) 2007 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.
+-->
+<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <!--
+ This is the short description of a permission associated with the
+ Bluetooth Share Manager. It is displayed as part of the description of
+ any application that was granted that permission. This specific
+ permission controls access to the Bluetooth Share Manager by
+ applications that initiate Bluetooth share.
+ -->
+ <string name="permlab_bluetoothShareManager">Accesu al alministrador de descargues</string>
+ <!--
+ This is the long description of a permission associated with the
+ Android Download Manager. It is displayed as part of the description
+ of any application that was granted that permission. This specific
+ permission controls access to the Download Manager by applications
+ that initiate downloads.
+ -->
+ <string name="permdesc_bluetoothShareManager">Permite que l\'aplicación acceda al alministrador BluetoothShare y lu use pa tresferir ficheros.</string>
+ <string name="permlab_bluetoothWhitelist">Accesu de preseos Bluetooth autorizaos</string>
+ <string name="permdesc_bluetoothWhitelist">Permíte-y a l’aplicación autorizar temporalmente un preséu Bluetooth pa poder unviar ficheros a esti preséu ensin la confirmación del usuariu.</string>
+ <!-- string showed on "Share picutre via" dialog -->
+ <string name="bt_share_picker_label">Bluetooth</string>
+ <!-- string for "unknown device" -->
+ <string name="unknown_device">Preséu desconocíu</string>
+ <!-- string for "unknown" phone number" -->
+ <string name="unknownNumber">Desconocíu</string>
+ <!-- string for "the title of airplane mode error" -->
+ <string name="airplane_error_title">Mou avión</string>
+ <!-- string for "error message in airplane mode" -->
+ <string name="airplane_error_msg">Nun pues usar el Bluetooth nel mou avión.</string>
+ <!-- Activate Bluetooth Confirmation Dialog -->
+ <!--Title -->
+ <!--Line 1 -->
+ <string name="bt_enable_line1">Pa utilizar los servicios de Bluetooth, primero tienes d\'activar la función Bluetooth.</string>
+ <!--Line 2 -->
+ <string name="bt_enable_line2">¿Quies activar la función Bluetooth agora?</string>
+ <!-- Label for a cancel button. -->
+ <string name="bt_enable_cancel">Encaboxar</string>
+ <!-- Label for a confirm button.-->
+ <string name="bt_enable_ok">Activar</string>
+ <!-- Bluetooth File Transfer Acceptance Dialog -->
+ <!--Title -->
+ <string name="incoming_file_confirm_title">Tresferencia de ficheros</string>
+ <!--content -->
+ <string name="incoming_file_confirm_content">Aceutar ficheru entrante? </string>
+ <!-- Label for a cancel button. -->
+ <string name="incoming_file_confirm_cancel">Refugar</string>
+ <!-- Label for a confirm button.-->
+ <string name="incoming_file_confirm_ok">Aceutar</string>
+ <!-- Label for timeout OK button.-->
+ <string name="incoming_file_confirm_timeout_ok">Aceutar</string>
+ <!-- content for timeout-->
+ <string name="incoming_file_confirm_timeout_content">Escosó\'l tiempu p\'aceutar el ficheru entrante de \"<xliff:g id="SENDER">%1$s</xliff:g>\".</string>
+ <!-- Bluetooth File Transfer Acceptance Notification item -->
+ <!-- Inbound File Transfer Progress Notification item -->
+ <!-- label for the notification item of receiving file -->
+ <string name="notification_receiving">Bluetooth: recibiendo <xliff:g id="FILE">%1$s</xliff:g></string>
+ <!-- label for the notification item of received file -->
+ <string name="notification_received">Compartir con Bluetooth: <xliff:g id="FILE">%1$s</xliff:g> recibíu</string>
+ <!-- label for the notification item of failed receiving file -->
+ <string name="notification_received_fail">Bluetooth: <xliff:g id="FILE">%1$s</xliff:g> non recibíu</string>
+ <!-- Outbound File Transfer Progress Notification item -->
+ <!-- label for the notification item of sending file -->
+ <string name="notification_sending">Bluetooth: unviando <xliff:g id="FILE">%1$s</xliff:g></string>
+ <!-- label for the notification item of sent file -->
+ <string name="notification_sent">Bluetooth: <xliff:g id="FILE">%1$s</xliff:g> unviáu</string>
+ <!-- label for the notification item of sent file -status -->
+ <string name="notification_sent_complete">100% completáu</string>
+ <!-- label for the notification item of failed sending file -->
+ <string name="notification_sent_fail">Bluetooth: ficheru <xliff:g id="FILE">%1$s</xliff:g> non unviáu</string>
+ <!-- Bluetooth Download Progress Dialog -->
+ <!--Title -->
+ <string name="download_title">Tresferencia de ficheros</string>
+ <!--Line 1 -->
+ <string name="download_line1">De: \"<xliff:g id="SENDER">%1$s</xliff:g>\"</string>
+ <!--Line 2 -->
+ <string name="download_line2">Ficheru: <xliff:g id="FILE">%1$s</xliff:g></string>
+ <!--Line 3 -->
+ <string name="download_line3">Tamañu de ficheru: <xliff:g id="SIZE">%1$s</xliff:g></string>
+ <!--Line 4 -->
+ <string name="download_line5">Recibiendo ficheru…</string>
+ <!-- Label for a cancel button. -->
+ <string name="download_cancel">Detener</string>
+ <!-- Label for a hide button.-->
+ <string name="download_ok">Anubrir</string>
+ <!--Line 1 -->
+ <string name="incoming_line1">De</string>
+ <!--Line 2 -->
+ <string name="incoming_line2">Nome de ficheru</string>
+ <!--Line 3 -->
+ <string name="incoming_line3">Tamañu</string>
+ <!-- Bluetooth failed Download Dialog -->
+ <!--Line 1 -->
+ <string name="download_fail_line1">Ficheru non recibíu</string>
+ <!--Line 2 -->
+ <string name="download_fail_line2">Ficheru: <xliff:g id="FILE">%1$s</xliff:g></string>
+ <!--Line 3 -->
+ <string name="download_fail_line3">Motivu: <xliff:g id="REASON">%1$s</xliff:g></string>
+ <!-- Label for ok button.-->
+ <string name="download_fail_ok">Aceutar</string>
+ <!-- Bluetooth Successful Download Dialog -->
+ <!--Line 4 in "Bluetooth Download Progress Dialog" -->
+ <string name="download_succ_line5">Ficheru recibíu</string>
+ <!-- Label for a OK button.-->
+ <string name="download_succ_ok">Abrir</string>
+ <!-- Bluetooth Upload Progress Dialog -->
+ <string name="upload_line1">Pa: \"<xliff:g id="RECIPIENT">%1$s</xliff:g>\"</string>
+ <string name="upload_line3">Triba de ficheru: <xliff:g id="TYPE">%1$s</xliff:g> (<xliff:g id="SIZE">%2$s</xliff:g>)</string>
+ <string name="upload_line5">Unviando ficheru…</string>
+ <!-- Bluetooth Successful Upload Progress Dialog -->
+ <!--Line 4 -->
+ <string name="upload_succ_line5">Ficheru unviáu</string>
+ <!-- Label for a confirm button.-->
+ <string name="upload_succ_ok">Aceutar</string>
+ <!-- Bluetooth Failed Upload File Transfer Dialog -->
+ <string name="upload_fail_line1">Nun s\'unvió\'l ficheru a \"<xliff:g id="RECIPIENT">%1$s</xliff:g>\".</string>
+ <string name="upload_fail_line1_2">Ficheru: <xliff:g id="FILE">%1$s</xliff:g></string>
+ <!-- Label for a try again button.-->
+ <string name="upload_fail_ok">Volver a intentalo</string>
+ <!-- Label for a cancel button.-->
+ <string name="upload_fail_cancel">Zarrar</string>
+ <!-- Bluetooth error dialog -->
+ <string name="bt_error_btn_ok">Aceutar</string>
+ <string name="unknown_file">Ficheru desconocíu</string>
+ <string name="unknown_file_desc">Nun hai nenguna aplicación que pueda procesar esta triba de ficheru. \n</string>
+ <string name="not_exist_file">Nun hai ficheros.</string>
+ <string name="not_exist_file_desc">El ficheru nun esiste. \n</string>
+ <!-- Bluetooth Enabling progress dialog -->
+ <string name="enabling_progress_title">Por favor, espera...</string>
+ <string name="enabling_progress_content">Activando Bluetooth...</string>
+ <!-- Bluetooth Toast Message -->
+ <string name="bt_toast_1">Va recibise\'l ficheru. Comprueba\'l progresu na barra de notificaciones.</string>
+ <string name="bt_toast_2">Nun pue recibise\'l ficheru.</string>
+ <string name="bt_toast_3">Detúvose la receición del ficheru de \"<xliff:g id="SENDER">%1$s</xliff:g>\"</string>
+ <string name="bt_toast_4">Unviando ficheru a \"<xliff:g id="RECIPIENT">%1$s</xliff:g>\"</string>
+ <string name="bt_toast_5">Unviando <xliff:g id="NUMBER">%1$s</xliff:g> ficheros a "<xliff:g id="RECIPIENT">%2$s</xliff:g>\"</string>
+ <string name="bt_toast_6">Detúvose l\'unviu del ficheru a \"<xliff:g id="RECIPIENT">%1$s</xliff:g>\"</string>
+ <!-- Bluetooth System Messages [CHAR LIMIT=NONE] -->
+ <string name="bt_sm_2_1" product="nosdcard">Nun hai espaciu abondu nel almacenamientu USB pa guardar el ficheru de \"<xliff:g id="SENDER">%1$s</xliff:g>\".</string>
+ <!-- Bluetooth System Messages -->
+ <string name="bt_sm_2_1" product="default">Nun hai espaciu abondu na tarxeta SD pa guardar el ficheru de \"<xliff:g id="SENDER">%1$s</xliff:g>\".</string>
+ <string name="bt_sm_2_2">Espaciu necesariu: <xliff:g id="SIZE">%1$s</xliff:g></string>
+ <string name="ErrorTooManyRequests">Tán procesándose munches solicitúes. Vuelvi a intentalo dempués.</string>
+ <!-- Bluetooth Transfer Failure Reason -->
+ <string name="status_pending">Entá nun s\'anició la tresferencia de ficheros.</string>
+ <string name="status_running">Tresferencia de ficheros en cursu</string>
+ <string name="status_success">La tresferencia de ficheros completóse correcho.</string>
+ <string name="status_not_accept">Conteníu non almitíu</string>
+ <string name="status_forbidden">El preséu de destín nun permite la tresferencia.</string>
+ <string name="status_canceled">Tresferencia encaboxada pol usuariu</string>
+ <string name="status_file_error">Fallu rellacionáu col almacenamientu</string>
+ <!-- Shown when USB storage cannot be found. [CHAR LIMIT=NONE] -->
+ <string name="status_no_sd_card" product="nosdcard">Ensin almacenamientu USB</string>
+ <string name="status_no_sd_card" product="default">Nun se deteuta nenguna tarxeta SD. Inxerta una tarxeta SD y guarda los ficheros tresferíos.</string>
+ <string name="status_connection_error">Conexón incorreuta</string>
+ <string name="status_protocol_error">Nun pue procesase la solicitú correchamente.</string>
+ <string name="status_unknown_error">Fallu desconocíu</string>
+ <!-- Bluetooth OPP Live Folder -->
+ <string name="btopp_live_folder">Recibío per Bluetooth</string>
+ <!-- Bluetooth OPP Transfer History -->
+ <string name="download_success">Receición de <xliff:g id="FILE_SIZE">%1$s</xliff:g> completada</string>
+ <string name="upload_success">Unviu de <xliff:g id="FILE_SIZE">%1$s</xliff:g> completáu</string>
+ <string name="inbound_history_title">Tresferencies entrantes</string>
+ <string name="outbound_history_title">Tresferencies salientes</string>
+ <string name="no_transfers">L\'historial de tresferencies ta baleru.</string>
+ <string name="transfer_clear_dlg_msg">Van desaniciase tolos elementos de la llista.</string>
+ <string name="outbound_noti_title">Bluetooth: ficheros unviaos</string>
+ <string name="inbound_noti_title">Bluetooth: ficheros recibíos</string>
+ <plurals name="noti_caption_unsuccessful">
+ <item quantity="one"><xliff:g id="unsuccessful_number">%1$d</xliff:g> con fallu</item>
+ <item quantity="other"><xliff:g id="unsuccessful_number">%1$d</xliff:g> con fallu</item>
+ </plurals>
+ <plurals name="noti_caption_success">
+ <item quantity="one"><xliff:g id="successful_number">%1$d</xliff:g> correutu, %2$s</item>
+ <item quantity="other"><xliff:g id="successful_number">%1$d</xliff:g> correutos, %2$s</item>
+ </plurals>
+ <string name="transfer_menu_clear_all">Desaniciar llista</string>
+ <string name="transfer_menu_open">Abrir</string>
+ <string name="transfer_menu_clear">Desaniciar de la llista</string>
+ <string name="transfer_clear_dlg_title">Desaniciar</string>
+ <!-- Do not translate. file name used for sharing. -->
+ <!-- Used to run Bluetooth.apk in another process if needed -->
+ <!-- Do not translate. android:sharedUserId string of this application. -->
+ <!-- Do not translate. android:process of this application. -->
+ <string name="bluetooth_map_settings_save">Guardar</string>
+ <string name="bluetooth_map_settings_cancel">Encaboxar</string>
+ <string name="bluetooth_map_settings_count">Slots restantes:</string>
+ <string name="bluetooth_map_settings_app_icon">Iconu d\'aplicación</string>
+ <string name="bluetooth_map_settings_title">Axustes de mensaxes compartíos per Bluetooth</string>
+ <string name="bluetooth_map_settings_no_account_slots_left">Nun pues esbillar la cuenta. Nun queden slots.</string>
+</resources>
diff --git a/res/values-ast-rES/strings_pbap.xml b/res/values-ast-rES/strings_pbap.xml
new file mode 100644
index 0000000..bc1a98e
--- /dev/null
+++ b/res/values-ast-rES/strings_pbap.xml
@@ -0,0 +1,17 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--Generated by crowdin.com-->
+<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="pbap_session_key_dialog_title">Escribi clave de sesión pa %1$s</string>
+ <string name="pbap_session_key_dialog_header">Necesítase la clave de sesión de Bluetooth.</string>
+ <string name="pbap_acceptance_timeout_message">Escosó\'l tiempu p\'aceutar la conexón con %1$s.</string>
+ <string name="pbap_authentication_timeout_message">Escosó\'l tiempu pa inxertar la clave de sesión con %1$s.</string>
+ <string name="auth_notif_ticker">Solicitú d\'autenticación de Obex</string>
+ <!-- Notification title when a Bluetooth device wants to pair with us -->
+ <string name="auth_notif_title">Clave de sesión</string>
+ <!-- Notification message when a Bluetooth device wants to pair with us -->
+ <string name="auth_notif_message">Escribi clave de sesión pa %1$s</string>
+ <string name="defaultname">Kit d\'automóvil</string>
+ <string name="unknownName">Nome desconocíu</string>
+ <string name="localPhoneName">El mio nome</string>
+ <string name="defaultnumber">000000</string>
+</resources>
diff --git a/res/values-ast-rES/strings_sap.xml b/res/values-ast-rES/strings_sap.xml
new file mode 100644
index 0000000..915e96e
--- /dev/null
+++ b/res/values-ast-rES/strings_sap.xml
@@ -0,0 +1,10 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--Generated by crowdin.com-->
+<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="bluetooth_sap_notif_title">Accesu SIM de Bluetooth</string>
+ <string name="bluetooth_sap_notif_ticker">Accesu SIM de Bluetooth</string>
+ <string name="bluetooth_sap_notif_message">¿Solicitar desconexón de veceru?</string>
+ <string name="bluetooth_sap_notif_disconnecting">Esperando pola desconexón del veceru</string>
+ <string name="bluetooth_sap_notif_disconnect_button">Desconeutar</string>
+ <string name="bluetooth_sap_notif_force_disconnect_button">Forciar desconexón</string>
+</resources>
diff --git a/res/values-az-rAZ/cm_strings.xml b/res/values-az-rAZ/cm_strings.xml
new file mode 100644
index 0000000..5f008cd
--- /dev/null
+++ b/res/values-az-rAZ/cm_strings.xml
@@ -0,0 +1,20 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--Generated by crowdin.com-->
+<!--
+ Copyright (C) 2015-2016 The CyanogenMod 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.
+-->
+<resources>
+ <string name="upload_fail_waiting">Fayl göndərilmədi, yenidən yoxlanır\u2026</string>
+</resources>
diff --git a/res/values-be-rBY/cm_strings.xml b/res/values-be-rBY/cm_strings.xml
new file mode 100644
index 0000000..83e0338
--- /dev/null
+++ b/res/values-be-rBY/cm_strings.xml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--Generated by crowdin.com-->
+<!--
+ Copyright (C) 2015-2016 The CyanogenMod 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.
+-->
+<resources>
+ <string name="incoming_file_toast_msg">Уваходны файл з іншае прылады. Пацьвердзі, хочаш атрымаць гэты файл.</string>
+ <string name="upload_fail_waiting">Не атрымалася адправіць файл. Паўторная спроба\u2026</string>
+</resources>
diff --git a/res/values-bg/cm_strings.xml b/res/values-bg/cm_strings.xml
new file mode 100644
index 0000000..6cd02fd
--- /dev/null
+++ b/res/values-bg/cm_strings.xml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--Generated by crowdin.com-->
+<!--
+ Copyright (C) 2015-2016 The CyanogenMod 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.
+-->
+<resources>
+ <string name="incoming_file_toast_msg">Входящ файл от друго устройство. Потвърдете искате ли да получуте този файл.</string>
+ <string name="upload_fail_waiting">Файлът не може да бъде изпратен, опитайте пак\u2026</string>
+</resources>
diff --git a/res/values-bn-rBD/cm_strings.xml b/res/values-bn-rBD/cm_strings.xml
new file mode 100644
index 0000000..f6d358e
--- /dev/null
+++ b/res/values-bn-rBD/cm_strings.xml
@@ -0,0 +1,20 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--Generated by crowdin.com-->
+<!--
+ Copyright (C) 2015-2016 The CyanogenMod 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.
+-->
+<resources>
+ <string name="upload_fail_waiting">ফাইল পাঠাতে অক্ষম, পুনরায় চেষ্টা করা হচ্ছে\u2026</string>
+</resources>
diff --git a/res/values-br-rFR/strings.xml b/res/values-br-rFR/strings.xml
new file mode 100644
index 0000000..906d653
--- /dev/null
+++ b/res/values-br-rFR/strings.xml
@@ -0,0 +1,99 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--Generated by crowdin.com-->
+<!-- Copyright (C) 2007 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.
+-->
+<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <!--
+ This is the short description of a permission associated with the
+ Bluetooth Share Manager. It is displayed as part of the description of
+ any application that was granted that permission. This specific
+ permission controls access to the Bluetooth Share Manager by
+ applications that initiate Bluetooth share.
+ -->
+ <!--
+ This is the long description of a permission associated with the
+ Android Download Manager. It is displayed as part of the description
+ of any application that was granted that permission. This specific
+ permission controls access to the Download Manager by applications
+ that initiate downloads.
+ -->
+ <!-- string showed on "Share picutre via" dialog -->
+ <!-- string for "unknown device" -->
+ <!-- string for "unknown" phone number" -->
+ <!-- string for "the title of airplane mode error" -->
+ <!-- string for "error message in airplane mode" -->
+ <!-- Activate Bluetooth Confirmation Dialog -->
+ <!--Title -->
+ <!--Line 1 -->
+ <!--Line 2 -->
+ <!-- Label for a cancel button. -->
+ <!-- Label for a confirm button.-->
+ <!-- Bluetooth File Transfer Acceptance Dialog -->
+ <!--Title -->
+ <!--content -->
+ <!-- Label for a cancel button. -->
+ <!-- Label for a confirm button.-->
+ <!-- Label for timeout OK button.-->
+ <!-- content for timeout-->
+ <!-- Bluetooth File Transfer Acceptance Notification item -->
+ <!-- Inbound File Transfer Progress Notification item -->
+ <!-- label for the notification item of receiving file -->
+ <!-- label for the notification item of received file -->
+ <!-- label for the notification item of failed receiving file -->
+ <!-- Outbound File Transfer Progress Notification item -->
+ <!-- label for the notification item of sending file -->
+ <!-- label for the notification item of sent file -->
+ <!-- label for the notification item of sent file -status -->
+ <!-- label for the notification item of failed sending file -->
+ <!-- Bluetooth Download Progress Dialog -->
+ <!--Title -->
+ <!--Line 1 -->
+ <!--Line 2 -->
+ <!--Line 3 -->
+ <!--Line 4 -->
+ <!-- Label for a cancel button. -->
+ <!-- Label for a hide button.-->
+ <!--Line 1 -->
+ <!--Line 2 -->
+ <!--Line 3 -->
+ <!-- Bluetooth failed Download Dialog -->
+ <!--Line 1 -->
+ <!--Line 2 -->
+ <!--Line 3 -->
+ <!-- Label for ok button.-->
+ <!-- Bluetooth Successful Download Dialog -->
+ <!--Line 4 in "Bluetooth Download Progress Dialog" -->
+ <!-- Label for a OK button.-->
+ <!-- Bluetooth Upload Progress Dialog -->
+ <!-- Bluetooth Successful Upload Progress Dialog -->
+ <!--Line 4 -->
+ <!-- Label for a confirm button.-->
+ <!-- Bluetooth Failed Upload File Transfer Dialog -->
+ <!-- Label for a try again button.-->
+ <!-- Label for a cancel button.-->
+ <!-- Bluetooth error dialog -->
+ <!-- Bluetooth Enabling progress dialog -->
+ <!-- Bluetooth Toast Message -->
+ <!-- Bluetooth System Messages [CHAR LIMIT=NONE] -->
+ <!-- Bluetooth System Messages -->
+ <!-- Bluetooth Transfer Failure Reason -->
+ <!-- Shown when USB storage cannot be found. [CHAR LIMIT=NONE] -->
+ <!-- Bluetooth OPP Live Folder -->
+ <!-- Bluetooth OPP Transfer History -->
+ <!-- Do not translate. file name used for sharing. -->
+ <!-- Used to run Bluetooth.apk in another process if needed -->
+ <!-- Do not translate. android:sharedUserId string of this application. -->
+ <!-- Do not translate. android:process of this application. -->
+</resources>
diff --git a/res/values-br-rFR/strings_pbap.xml b/res/values-br-rFR/strings_pbap.xml
new file mode 100644
index 0000000..55f5b83
--- /dev/null
+++ b/res/values-br-rFR/strings_pbap.xml
@@ -0,0 +1,6 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--Generated by crowdin.com-->
+<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <!-- Notification title when a Bluetooth device wants to pair with us -->
+ <!-- Notification message when a Bluetooth device wants to pair with us -->
+</resources>
diff --git a/res/values-ca/cm_strings.xml b/res/values-ca/cm_strings.xml
new file mode 100644
index 0000000..3169eb5
--- /dev/null
+++ b/res/values-ca/cm_strings.xml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--Generated by crowdin.com-->
+<!--
+ Copyright (C) 2015-2016 The CyanogenMod 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.
+-->
+<resources>
+ <string name="incoming_file_toast_msg">Fitxer entrant des d\'un altre dispositiu. Confirma que vols rebre aquest fitxer.</string>
+ <string name="upload_fail_waiting">No es pot enviar el fitxer, s\'està reintentant\u2026</string>
+</resources>
diff --git a/res/values-cs/cm_strings.xml b/res/values-cs/cm_strings.xml
new file mode 100644
index 0000000..06482df
--- /dev/null
+++ b/res/values-cs/cm_strings.xml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--Generated by crowdin.com-->
+<!--
+ Copyright (C) 2015-2016 The CyanogenMod 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.
+-->
+<resources>
+ <string name="incoming_file_toast_msg">Příchozí soubor z jiného zařízení. Potvrďte, že chcete přijmout tento soubor.</string>
+ <string name="upload_fail_waiting">Nepodařilo se odeslat soubor, opakování akce\u2026</string>
+</resources>
diff --git a/res/values-csb-rPL/strings.xml b/res/values-csb-rPL/strings.xml
new file mode 100644
index 0000000..906d653
--- /dev/null
+++ b/res/values-csb-rPL/strings.xml
@@ -0,0 +1,99 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--Generated by crowdin.com-->
+<!-- Copyright (C) 2007 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.
+-->
+<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <!--
+ This is the short description of a permission associated with the
+ Bluetooth Share Manager. It is displayed as part of the description of
+ any application that was granted that permission. This specific
+ permission controls access to the Bluetooth Share Manager by
+ applications that initiate Bluetooth share.
+ -->
+ <!--
+ This is the long description of a permission associated with the
+ Android Download Manager. It is displayed as part of the description
+ of any application that was granted that permission. This specific
+ permission controls access to the Download Manager by applications
+ that initiate downloads.
+ -->
+ <!-- string showed on "Share picutre via" dialog -->
+ <!-- string for "unknown device" -->
+ <!-- string for "unknown" phone number" -->
+ <!-- string for "the title of airplane mode error" -->
+ <!-- string for "error message in airplane mode" -->
+ <!-- Activate Bluetooth Confirmation Dialog -->
+ <!--Title -->
+ <!--Line 1 -->
+ <!--Line 2 -->
+ <!-- Label for a cancel button. -->
+ <!-- Label for a confirm button.-->
+ <!-- Bluetooth File Transfer Acceptance Dialog -->
+ <!--Title -->
+ <!--content -->
+ <!-- Label for a cancel button. -->
+ <!-- Label for a confirm button.-->
+ <!-- Label for timeout OK button.-->
+ <!-- content for timeout-->
+ <!-- Bluetooth File Transfer Acceptance Notification item -->
+ <!-- Inbound File Transfer Progress Notification item -->
+ <!-- label for the notification item of receiving file -->
+ <!-- label for the notification item of received file -->
+ <!-- label for the notification item of failed receiving file -->
+ <!-- Outbound File Transfer Progress Notification item -->
+ <!-- label for the notification item of sending file -->
+ <!-- label for the notification item of sent file -->
+ <!-- label for the notification item of sent file -status -->
+ <!-- label for the notification item of failed sending file -->
+ <!-- Bluetooth Download Progress Dialog -->
+ <!--Title -->
+ <!--Line 1 -->
+ <!--Line 2 -->
+ <!--Line 3 -->
+ <!--Line 4 -->
+ <!-- Label for a cancel button. -->
+ <!-- Label for a hide button.-->
+ <!--Line 1 -->
+ <!--Line 2 -->
+ <!--Line 3 -->
+ <!-- Bluetooth failed Download Dialog -->
+ <!--Line 1 -->
+ <!--Line 2 -->
+ <!--Line 3 -->
+ <!-- Label for ok button.-->
+ <!-- Bluetooth Successful Download Dialog -->
+ <!--Line 4 in "Bluetooth Download Progress Dialog" -->
+ <!-- Label for a OK button.-->
+ <!-- Bluetooth Upload Progress Dialog -->
+ <!-- Bluetooth Successful Upload Progress Dialog -->
+ <!--Line 4 -->
+ <!-- Label for a confirm button.-->
+ <!-- Bluetooth Failed Upload File Transfer Dialog -->
+ <!-- Label for a try again button.-->
+ <!-- Label for a cancel button.-->
+ <!-- Bluetooth error dialog -->
+ <!-- Bluetooth Enabling progress dialog -->
+ <!-- Bluetooth Toast Message -->
+ <!-- Bluetooth System Messages [CHAR LIMIT=NONE] -->
+ <!-- Bluetooth System Messages -->
+ <!-- Bluetooth Transfer Failure Reason -->
+ <!-- Shown when USB storage cannot be found. [CHAR LIMIT=NONE] -->
+ <!-- Bluetooth OPP Live Folder -->
+ <!-- Bluetooth OPP Transfer History -->
+ <!-- Do not translate. file name used for sharing. -->
+ <!-- Used to run Bluetooth.apk in another process if needed -->
+ <!-- Do not translate. android:sharedUserId string of this application. -->
+ <!-- Do not translate. android:process of this application. -->
+</resources>
diff --git a/res/values-csb-rPL/strings_pbap.xml b/res/values-csb-rPL/strings_pbap.xml
new file mode 100644
index 0000000..55f5b83
--- /dev/null
+++ b/res/values-csb-rPL/strings_pbap.xml
@@ -0,0 +1,6 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--Generated by crowdin.com-->
+<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <!-- Notification title when a Bluetooth device wants to pair with us -->
+ <!-- Notification message when a Bluetooth device wants to pair with us -->
+</resources>
diff --git a/res/values-cy/strings.xml b/res/values-cy/strings.xml
new file mode 100644
index 0000000..906d653
--- /dev/null
+++ b/res/values-cy/strings.xml
@@ -0,0 +1,99 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--Generated by crowdin.com-->
+<!-- Copyright (C) 2007 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.
+-->
+<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <!--
+ This is the short description of a permission associated with the
+ Bluetooth Share Manager. It is displayed as part of the description of
+ any application that was granted that permission. This specific
+ permission controls access to the Bluetooth Share Manager by
+ applications that initiate Bluetooth share.
+ -->
+ <!--
+ This is the long description of a permission associated with the
+ Android Download Manager. It is displayed as part of the description
+ of any application that was granted that permission. This specific
+ permission controls access to the Download Manager by applications
+ that initiate downloads.
+ -->
+ <!-- string showed on "Share picutre via" dialog -->
+ <!-- string for "unknown device" -->
+ <!-- string for "unknown" phone number" -->
+ <!-- string for "the title of airplane mode error" -->
+ <!-- string for "error message in airplane mode" -->
+ <!-- Activate Bluetooth Confirmation Dialog -->
+ <!--Title -->
+ <!--Line 1 -->
+ <!--Line 2 -->
+ <!-- Label for a cancel button. -->
+ <!-- Label for a confirm button.-->
+ <!-- Bluetooth File Transfer Acceptance Dialog -->
+ <!--Title -->
+ <!--content -->
+ <!-- Label for a cancel button. -->
+ <!-- Label for a confirm button.-->
+ <!-- Label for timeout OK button.-->
+ <!-- content for timeout-->
+ <!-- Bluetooth File Transfer Acceptance Notification item -->
+ <!-- Inbound File Transfer Progress Notification item -->
+ <!-- label for the notification item of receiving file -->
+ <!-- label for the notification item of received file -->
+ <!-- label for the notification item of failed receiving file -->
+ <!-- Outbound File Transfer Progress Notification item -->
+ <!-- label for the notification item of sending file -->
+ <!-- label for the notification item of sent file -->
+ <!-- label for the notification item of sent file -status -->
+ <!-- label for the notification item of failed sending file -->
+ <!-- Bluetooth Download Progress Dialog -->
+ <!--Title -->
+ <!--Line 1 -->
+ <!--Line 2 -->
+ <!--Line 3 -->
+ <!--Line 4 -->
+ <!-- Label for a cancel button. -->
+ <!-- Label for a hide button.-->
+ <!--Line 1 -->
+ <!--Line 2 -->
+ <!--Line 3 -->
+ <!-- Bluetooth failed Download Dialog -->
+ <!--Line 1 -->
+ <!--Line 2 -->
+ <!--Line 3 -->
+ <!-- Label for ok button.-->
+ <!-- Bluetooth Successful Download Dialog -->
+ <!--Line 4 in "Bluetooth Download Progress Dialog" -->
+ <!-- Label for a OK button.-->
+ <!-- Bluetooth Upload Progress Dialog -->
+ <!-- Bluetooth Successful Upload Progress Dialog -->
+ <!--Line 4 -->
+ <!-- Label for a confirm button.-->
+ <!-- Bluetooth Failed Upload File Transfer Dialog -->
+ <!-- Label for a try again button.-->
+ <!-- Label for a cancel button.-->
+ <!-- Bluetooth error dialog -->
+ <!-- Bluetooth Enabling progress dialog -->
+ <!-- Bluetooth Toast Message -->
+ <!-- Bluetooth System Messages [CHAR LIMIT=NONE] -->
+ <!-- Bluetooth System Messages -->
+ <!-- Bluetooth Transfer Failure Reason -->
+ <!-- Shown when USB storage cannot be found. [CHAR LIMIT=NONE] -->
+ <!-- Bluetooth OPP Live Folder -->
+ <!-- Bluetooth OPP Transfer History -->
+ <!-- Do not translate. file name used for sharing. -->
+ <!-- Used to run Bluetooth.apk in another process if needed -->
+ <!-- Do not translate. android:sharedUserId string of this application. -->
+ <!-- Do not translate. android:process of this application. -->
+</resources>
diff --git a/res/values-cy/strings_pbap.xml b/res/values-cy/strings_pbap.xml
new file mode 100644
index 0000000..55f5b83
--- /dev/null
+++ b/res/values-cy/strings_pbap.xml
@@ -0,0 +1,6 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--Generated by crowdin.com-->
+<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <!-- Notification title when a Bluetooth device wants to pair with us -->
+ <!-- Notification message when a Bluetooth device wants to pair with us -->
+</resources>
diff --git a/res/values-da/cm_strings.xml b/res/values-da/cm_strings.xml
new file mode 100644
index 0000000..a0a44b8
--- /dev/null
+++ b/res/values-da/cm_strings.xml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--Generated by crowdin.com-->
+<!--
+ Copyright (C) 2015-2016 The CyanogenMod 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.
+-->
+<resources>
+ <string name="incoming_file_toast_msg">Indgående fil fra en anden enhed. Bekræft, at du vil modtage denne fil.</string>
+ <string name="upload_fail_waiting">Kunne ikke sende filen, prøver igen\u2026</string>
+</resources>
diff --git a/res/values-de/cm_strings.xml b/res/values-de/cm_strings.xml
new file mode 100644
index 0000000..0f70267
--- /dev/null
+++ b/res/values-de/cm_strings.xml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--Generated by crowdin.com-->
+<!--
+ Copyright (C) 2015-2016 The CyanogenMod 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.
+-->
+<resources>
+ <string name="incoming_file_toast_msg">Eingehende Datei von einem anderen Gerät. Bestätigen, wenn Sie diese Datei empfangen möchten.</string>
+ <string name="upload_fail_waiting">Datei kann nicht versendet werden. Erneuter Versuch\u2026</string>
+</resources>
diff --git a/res/values-el/cm_strings.xml b/res/values-el/cm_strings.xml
new file mode 100644
index 0000000..8869c78
--- /dev/null
+++ b/res/values-el/cm_strings.xml
@@ -0,0 +1,20 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--Generated by crowdin.com-->
+<!--
+ Copyright (C) 2015-2016 The CyanogenMod 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.
+-->
+<resources>
+ <string name="upload_fail_waiting">Το αρχείο δεν στάλθηκε, προσπάθεια ξανά\u2026</string>
+</resources>
diff --git a/res/values-en-rAU/cm_strings.xml b/res/values-en-rAU/cm_strings.xml
new file mode 100644
index 0000000..e41bf93
--- /dev/null
+++ b/res/values-en-rAU/cm_strings.xml
@@ -0,0 +1,20 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--Generated by crowdin.com-->
+<!--
+ Copyright (C) 2015-2016 The CyanogenMod 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.
+-->
+<resources>
+ <string name="upload_fail_waiting">Unable to send file, retrying\u2026</string>
+</resources>
diff --git a/res/values-en-rIN/cm_strings.xml b/res/values-en-rIN/cm_strings.xml
new file mode 100644
index 0000000..e41bf93
--- /dev/null
+++ b/res/values-en-rIN/cm_strings.xml
@@ -0,0 +1,20 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--Generated by crowdin.com-->
+<!--
+ Copyright (C) 2015-2016 The CyanogenMod 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.
+-->
+<resources>
+ <string name="upload_fail_waiting">Unable to send file, retrying\u2026</string>
+</resources>
diff --git a/res/values-en-rPT/strings.xml b/res/values-en-rPT/strings.xml
new file mode 100644
index 0000000..906d653
--- /dev/null
+++ b/res/values-en-rPT/strings.xml
@@ -0,0 +1,99 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--Generated by crowdin.com-->
+<!-- Copyright (C) 2007 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.
+-->
+<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <!--
+ This is the short description of a permission associated with the
+ Bluetooth Share Manager. It is displayed as part of the description of
+ any application that was granted that permission. This specific
+ permission controls access to the Bluetooth Share Manager by
+ applications that initiate Bluetooth share.
+ -->
+ <!--
+ This is the long description of a permission associated with the
+ Android Download Manager. It is displayed as part of the description
+ of any application that was granted that permission. This specific
+ permission controls access to the Download Manager by applications
+ that initiate downloads.
+ -->
+ <!-- string showed on "Share picutre via" dialog -->
+ <!-- string for "unknown device" -->
+ <!-- string for "unknown" phone number" -->
+ <!-- string for "the title of airplane mode error" -->
+ <!-- string for "error message in airplane mode" -->
+ <!-- Activate Bluetooth Confirmation Dialog -->
+ <!--Title -->
+ <!--Line 1 -->
+ <!--Line 2 -->
+ <!-- Label for a cancel button. -->
+ <!-- Label for a confirm button.-->
+ <!-- Bluetooth File Transfer Acceptance Dialog -->
+ <!--Title -->
+ <!--content -->
+ <!-- Label for a cancel button. -->
+ <!-- Label for a confirm button.-->
+ <!-- Label for timeout OK button.-->
+ <!-- content for timeout-->
+ <!-- Bluetooth File Transfer Acceptance Notification item -->
+ <!-- Inbound File Transfer Progress Notification item -->
+ <!-- label for the notification item of receiving file -->
+ <!-- label for the notification item of received file -->
+ <!-- label for the notification item of failed receiving file -->
+ <!-- Outbound File Transfer Progress Notification item -->
+ <!-- label for the notification item of sending file -->
+ <!-- label for the notification item of sent file -->
+ <!-- label for the notification item of sent file -status -->
+ <!-- label for the notification item of failed sending file -->
+ <!-- Bluetooth Download Progress Dialog -->
+ <!--Title -->
+ <!--Line 1 -->
+ <!--Line 2 -->
+ <!--Line 3 -->
+ <!--Line 4 -->
+ <!-- Label for a cancel button. -->
+ <!-- Label for a hide button.-->
+ <!--Line 1 -->
+ <!--Line 2 -->
+ <!--Line 3 -->
+ <!-- Bluetooth failed Download Dialog -->
+ <!--Line 1 -->
+ <!--Line 2 -->
+ <!--Line 3 -->
+ <!-- Label for ok button.-->
+ <!-- Bluetooth Successful Download Dialog -->
+ <!--Line 4 in "Bluetooth Download Progress Dialog" -->
+ <!-- Label for a OK button.-->
+ <!-- Bluetooth Upload Progress Dialog -->
+ <!-- Bluetooth Successful Upload Progress Dialog -->
+ <!--Line 4 -->
+ <!-- Label for a confirm button.-->
+ <!-- Bluetooth Failed Upload File Transfer Dialog -->
+ <!-- Label for a try again button.-->
+ <!-- Label for a cancel button.-->
+ <!-- Bluetooth error dialog -->
+ <!-- Bluetooth Enabling progress dialog -->
+ <!-- Bluetooth Toast Message -->
+ <!-- Bluetooth System Messages [CHAR LIMIT=NONE] -->
+ <!-- Bluetooth System Messages -->
+ <!-- Bluetooth Transfer Failure Reason -->
+ <!-- Shown when USB storage cannot be found. [CHAR LIMIT=NONE] -->
+ <!-- Bluetooth OPP Live Folder -->
+ <!-- Bluetooth OPP Transfer History -->
+ <!-- Do not translate. file name used for sharing. -->
+ <!-- Used to run Bluetooth.apk in another process if needed -->
+ <!-- Do not translate. android:sharedUserId string of this application. -->
+ <!-- Do not translate. android:process of this application. -->
+</resources>
diff --git a/res/values-en-rPT/strings_pbap.xml b/res/values-en-rPT/strings_pbap.xml
new file mode 100644
index 0000000..55f5b83
--- /dev/null
+++ b/res/values-en-rPT/strings_pbap.xml
@@ -0,0 +1,6 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--Generated by crowdin.com-->
+<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <!-- Notification title when a Bluetooth device wants to pair with us -->
+ <!-- Notification message when a Bluetooth device wants to pair with us -->
+</resources>
diff --git a/res/values-eo/strings.xml b/res/values-eo/strings.xml
new file mode 100644
index 0000000..ef7e5c8
--- /dev/null
+++ b/res/values-eo/strings.xml
@@ -0,0 +1,111 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--Generated by crowdin.com-->
+<!-- Copyright (C) 2007 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.
+-->
+<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <!--
+ This is the short description of a permission associated with the
+ Bluetooth Share Manager. It is displayed as part of the description of
+ any application that was granted that permission. This specific
+ permission controls access to the Bluetooth Share Manager by
+ applications that initiate Bluetooth share.
+ -->
+ <!--
+ This is the long description of a permission associated with the
+ Android Download Manager. It is displayed as part of the description
+ of any application that was granted that permission. This specific
+ permission controls access to the Download Manager by applications
+ that initiate downloads.
+ -->
+ <!-- string showed on "Share picutre via" dialog -->
+ <!-- string for "unknown device" -->
+ <string name="unknown_device">Nekonata aparato</string>
+ <!-- string for "unknown" phone number" -->
+ <string name="unknownNumber">Nekonata</string>
+ <!-- string for "the title of airplane mode error" -->
+ <string name="airplane_error_title">Aviadila reĝimo</string>
+ <!-- string for "error message in airplane mode" -->
+ <!-- Activate Bluetooth Confirmation Dialog -->
+ <!--Title -->
+ <!--Line 1 -->
+ <!--Line 2 -->
+ <!-- Label for a cancel button. -->
+ <string name="bt_enable_cancel">Nuligi</string>
+ <!-- Label for a confirm button.-->
+ <!-- Bluetooth File Transfer Acceptance Dialog -->
+ <!--Title -->
+ <!--content -->
+ <!-- Label for a cancel button. -->
+ <!-- Label for a confirm button.-->
+ <!-- Label for timeout OK button.-->
+ <!-- content for timeout-->
+ <!-- Bluetooth File Transfer Acceptance Notification item -->
+ <!-- Inbound File Transfer Progress Notification item -->
+ <!-- label for the notification item of receiving file -->
+ <!-- label for the notification item of received file -->
+ <!-- label for the notification item of failed receiving file -->
+ <!-- Outbound File Transfer Progress Notification item -->
+ <!-- label for the notification item of sending file -->
+ <!-- label for the notification item of sent file -->
+ <!-- label for the notification item of sent file -status -->
+ <!-- label for the notification item of failed sending file -->
+ <!-- Bluetooth Download Progress Dialog -->
+ <!--Title -->
+ <!--Line 1 -->
+ <!--Line 2 -->
+ <string name="download_line2">Dosiero: <xliff:g id="file">%1$s</xliff:g></string>
+ <!--Line 3 -->
+ <!--Line 4 -->
+ <!-- Label for a cancel button. -->
+ <!-- Label for a hide button.-->
+ <!--Line 1 -->
+ <!--Line 2 -->
+ <!--Line 3 -->
+ <!-- Bluetooth failed Download Dialog -->
+ <!--Line 1 -->
+ <!--Line 2 -->
+ <!--Line 3 -->
+ <!-- Label for ok button.-->
+ <!-- Bluetooth Successful Download Dialog -->
+ <!--Line 4 in "Bluetooth Download Progress Dialog" -->
+ <!-- Label for a OK button.-->
+ <string name="download_succ_ok">Malfermi</string>
+ <!-- Bluetooth Upload Progress Dialog -->
+ <!-- Bluetooth Successful Upload Progress Dialog -->
+ <!--Line 4 -->
+ <!-- Label for a confirm button.-->
+ <!-- Bluetooth Failed Upload File Transfer Dialog -->
+ <string name="upload_fail_line1_2">Dosiero: <xliff:g id="file">%1$s</xliff:g></string>
+ <!-- Label for a try again button.-->
+ <string name="upload_fail_ok">Reprovi</string>
+ <!-- Label for a cancel button.-->
+ <string name="upload_fail_cancel">Fermi</string>
+ <!-- Bluetooth error dialog -->
+ <!-- Bluetooth Enabling progress dialog -->
+ <!-- Bluetooth Toast Message -->
+ <!-- Bluetooth System Messages [CHAR LIMIT=NONE] -->
+ <!-- Bluetooth System Messages -->
+ <!-- Bluetooth Transfer Failure Reason -->
+ <!-- Shown when USB storage cannot be found. [CHAR LIMIT=NONE] -->
+ <string name="status_unknown_error">Nekonata eraro.</string>
+ <!-- Bluetooth OPP Live Folder -->
+ <!-- Bluetooth OPP Transfer History -->
+ <string name="transfer_menu_open">Malfermi</string>
+ <!-- Do not translate. file name used for sharing. -->
+ <!-- Used to run Bluetooth.apk in another process if needed -->
+ <!-- Do not translate. android:sharedUserId string of this application. -->
+ <!-- Do not translate. android:process of this application. -->
+ <string name="bluetooth_map_settings_cancel">Nuligi</string>
+</resources>
diff --git a/res/values-eo/strings_pbap.xml b/res/values-eo/strings_pbap.xml
new file mode 100644
index 0000000..55f5b83
--- /dev/null
+++ b/res/values-eo/strings_pbap.xml
@@ -0,0 +1,6 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--Generated by crowdin.com-->
+<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <!-- Notification title when a Bluetooth device wants to pair with us -->
+ <!-- Notification message when a Bluetooth device wants to pair with us -->
+</resources>
diff --git a/res/values-es-rCO/strings.xml b/res/values-es-rCO/strings.xml
new file mode 100644
index 0000000..906d653
--- /dev/null
+++ b/res/values-es-rCO/strings.xml
@@ -0,0 +1,99 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--Generated by crowdin.com-->
+<!-- Copyright (C) 2007 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.
+-->
+<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <!--
+ This is the short description of a permission associated with the
+ Bluetooth Share Manager. It is displayed as part of the description of
+ any application that was granted that permission. This specific
+ permission controls access to the Bluetooth Share Manager by
+ applications that initiate Bluetooth share.
+ -->
+ <!--
+ This is the long description of a permission associated with the
+ Android Download Manager. It is displayed as part of the description
+ of any application that was granted that permission. This specific
+ permission controls access to the Download Manager by applications
+ that initiate downloads.
+ -->
+ <!-- string showed on "Share picutre via" dialog -->
+ <!-- string for "unknown device" -->
+ <!-- string for "unknown" phone number" -->
+ <!-- string for "the title of airplane mode error" -->
+ <!-- string for "error message in airplane mode" -->
+ <!-- Activate Bluetooth Confirmation Dialog -->
+ <!--Title -->
+ <!--Line 1 -->
+ <!--Line 2 -->
+ <!-- Label for a cancel button. -->
+ <!-- Label for a confirm button.-->
+ <!-- Bluetooth File Transfer Acceptance Dialog -->
+ <!--Title -->
+ <!--content -->
+ <!-- Label for a cancel button. -->
+ <!-- Label for a confirm button.-->
+ <!-- Label for timeout OK button.-->
+ <!-- content for timeout-->
+ <!-- Bluetooth File Transfer Acceptance Notification item -->
+ <!-- Inbound File Transfer Progress Notification item -->
+ <!-- label for the notification item of receiving file -->
+ <!-- label for the notification item of received file -->
+ <!-- label for the notification item of failed receiving file -->
+ <!-- Outbound File Transfer Progress Notification item -->
+ <!-- label for the notification item of sending file -->
+ <!-- label for the notification item of sent file -->
+ <!-- label for the notification item of sent file -status -->
+ <!-- label for the notification item of failed sending file -->
+ <!-- Bluetooth Download Progress Dialog -->
+ <!--Title -->
+ <!--Line 1 -->
+ <!--Line 2 -->
+ <!--Line 3 -->
+ <!--Line 4 -->
+ <!-- Label for a cancel button. -->
+ <!-- Label for a hide button.-->
+ <!--Line 1 -->
+ <!--Line 2 -->
+ <!--Line 3 -->
+ <!-- Bluetooth failed Download Dialog -->
+ <!--Line 1 -->
+ <!--Line 2 -->
+ <!--Line 3 -->
+ <!-- Label for ok button.-->
+ <!-- Bluetooth Successful Download Dialog -->
+ <!--Line 4 in "Bluetooth Download Progress Dialog" -->
+ <!-- Label for a OK button.-->
+ <!-- Bluetooth Upload Progress Dialog -->
+ <!-- Bluetooth Successful Upload Progress Dialog -->
+ <!--Line 4 -->
+ <!-- Label for a confirm button.-->
+ <!-- Bluetooth Failed Upload File Transfer Dialog -->
+ <!-- Label for a try again button.-->
+ <!-- Label for a cancel button.-->
+ <!-- Bluetooth error dialog -->
+ <!-- Bluetooth Enabling progress dialog -->
+ <!-- Bluetooth Toast Message -->
+ <!-- Bluetooth System Messages [CHAR LIMIT=NONE] -->
+ <!-- Bluetooth System Messages -->
+ <!-- Bluetooth Transfer Failure Reason -->
+ <!-- Shown when USB storage cannot be found. [CHAR LIMIT=NONE] -->
+ <!-- Bluetooth OPP Live Folder -->
+ <!-- Bluetooth OPP Transfer History -->
+ <!-- Do not translate. file name used for sharing. -->
+ <!-- Used to run Bluetooth.apk in another process if needed -->
+ <!-- Do not translate. android:sharedUserId string of this application. -->
+ <!-- Do not translate. android:process of this application. -->
+</resources>
diff --git a/res/values-es-rCO/strings_pbap.xml b/res/values-es-rCO/strings_pbap.xml
new file mode 100644
index 0000000..55f5b83
--- /dev/null
+++ b/res/values-es-rCO/strings_pbap.xml
@@ -0,0 +1,6 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--Generated by crowdin.com-->
+<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <!-- Notification title when a Bluetooth device wants to pair with us -->
+ <!-- Notification message when a Bluetooth device wants to pair with us -->
+</resources>
diff --git a/res/values-es-rMX/strings.xml b/res/values-es-rMX/strings.xml
new file mode 100644
index 0000000..906d653
--- /dev/null
+++ b/res/values-es-rMX/strings.xml
@@ -0,0 +1,99 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--Generated by crowdin.com-->
+<!-- Copyright (C) 2007 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.
+-->
+<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <!--
+ This is the short description of a permission associated with the
+ Bluetooth Share Manager. It is displayed as part of the description of
+ any application that was granted that permission. This specific
+ permission controls access to the Bluetooth Share Manager by
+ applications that initiate Bluetooth share.
+ -->
+ <!--
+ This is the long description of a permission associated with the
+ Android Download Manager. It is displayed as part of the description
+ of any application that was granted that permission. This specific
+ permission controls access to the Download Manager by applications
+ that initiate downloads.
+ -->
+ <!-- string showed on "Share picutre via" dialog -->
+ <!-- string for "unknown device" -->
+ <!-- string for "unknown" phone number" -->
+ <!-- string for "the title of airplane mode error" -->
+ <!-- string for "error message in airplane mode" -->
+ <!-- Activate Bluetooth Confirmation Dialog -->
+ <!--Title -->
+ <!--Line 1 -->
+ <!--Line 2 -->
+ <!-- Label for a cancel button. -->
+ <!-- Label for a confirm button.-->
+ <!-- Bluetooth File Transfer Acceptance Dialog -->
+ <!--Title -->
+ <!--content -->
+ <!-- Label for a cancel button. -->
+ <!-- Label for a confirm button.-->
+ <!-- Label for timeout OK button.-->
+ <!-- content for timeout-->
+ <!-- Bluetooth File Transfer Acceptance Notification item -->
+ <!-- Inbound File Transfer Progress Notification item -->
+ <!-- label for the notification item of receiving file -->
+ <!-- label for the notification item of received file -->
+ <!-- label for the notification item of failed receiving file -->
+ <!-- Outbound File Transfer Progress Notification item -->
+ <!-- label for the notification item of sending file -->
+ <!-- label for the notification item of sent file -->
+ <!-- label for the notification item of sent file -status -->
+ <!-- label for the notification item of failed sending file -->
+ <!-- Bluetooth Download Progress Dialog -->
+ <!--Title -->
+ <!--Line 1 -->
+ <!--Line 2 -->
+ <!--Line 3 -->
+ <!--Line 4 -->
+ <!-- Label for a cancel button. -->
+ <!-- Label for a hide button.-->
+ <!--Line 1 -->
+ <!--Line 2 -->
+ <!--Line 3 -->
+ <!-- Bluetooth failed Download Dialog -->
+ <!--Line 1 -->
+ <!--Line 2 -->
+ <!--Line 3 -->
+ <!-- Label for ok button.-->
+ <!-- Bluetooth Successful Download Dialog -->
+ <!--Line 4 in "Bluetooth Download Progress Dialog" -->
+ <!-- Label for a OK button.-->
+ <!-- Bluetooth Upload Progress Dialog -->
+ <!-- Bluetooth Successful Upload Progress Dialog -->
+ <!--Line 4 -->
+ <!-- Label for a confirm button.-->
+ <!-- Bluetooth Failed Upload File Transfer Dialog -->
+ <!-- Label for a try again button.-->
+ <!-- Label for a cancel button.-->
+ <!-- Bluetooth error dialog -->
+ <!-- Bluetooth Enabling progress dialog -->
+ <!-- Bluetooth Toast Message -->
+ <!-- Bluetooth System Messages [CHAR LIMIT=NONE] -->
+ <!-- Bluetooth System Messages -->
+ <!-- Bluetooth Transfer Failure Reason -->
+ <!-- Shown when USB storage cannot be found. [CHAR LIMIT=NONE] -->
+ <!-- Bluetooth OPP Live Folder -->
+ <!-- Bluetooth OPP Transfer History -->
+ <!-- Do not translate. file name used for sharing. -->
+ <!-- Used to run Bluetooth.apk in another process if needed -->
+ <!-- Do not translate. android:sharedUserId string of this application. -->
+ <!-- Do not translate. android:process of this application. -->
+</resources>
diff --git a/res/values-es-rMX/strings_pbap.xml b/res/values-es-rMX/strings_pbap.xml
new file mode 100644
index 0000000..55f5b83
--- /dev/null
+++ b/res/values-es-rMX/strings_pbap.xml
@@ -0,0 +1,6 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--Generated by crowdin.com-->
+<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <!-- Notification title when a Bluetooth device wants to pair with us -->
+ <!-- Notification message when a Bluetooth device wants to pair with us -->
+</resources>
diff --git a/res/values-es-rUS/cm_strings.xml b/res/values-es-rUS/cm_strings.xml
new file mode 100644
index 0000000..a9d0fbc
--- /dev/null
+++ b/res/values-es-rUS/cm_strings.xml
@@ -0,0 +1,20 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--Generated by crowdin.com-->
+<!--
+ Copyright (C) 2015-2016 The CyanogenMod 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.
+-->
+<resources>
+ <string name="upload_fail_waiting">No se pudo enviar el archivo, reintentando\u2026</string>
+</resources>
diff --git a/res/values-es/cm_strings.xml b/res/values-es/cm_strings.xml
new file mode 100644
index 0000000..3e8d83f
--- /dev/null
+++ b/res/values-es/cm_strings.xml
@@ -0,0 +1,20 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--Generated by crowdin.com-->
+<!--
+ Copyright (C) 2015-2016 The CyanogenMod 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.
+-->
+<resources>
+ <string name="upload_fail_waiting">No se puede enviar el archivo, reintentando\u2026</string>
+</resources>
diff --git a/res/values-et-rEE/cm_strings.xml b/res/values-et-rEE/cm_strings.xml
new file mode 100644
index 0000000..7ba2578
--- /dev/null
+++ b/res/values-et-rEE/cm_strings.xml
@@ -0,0 +1,20 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--Generated by crowdin.com-->
+<!--
+ Copyright (C) 2015-2016 The CyanogenMod 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.
+-->
+<resources>
+ <string name="upload_fail_waiting">Ei saanud saata faili, proovin uuesti\u2026</string>
+</resources>
diff --git a/res/values-eu-rES/cm_strings.xml b/res/values-eu-rES/cm_strings.xml
new file mode 100644
index 0000000..d745f7b
--- /dev/null
+++ b/res/values-eu-rES/cm_strings.xml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--Generated by crowdin.com-->
+<!--
+ Copyright (C) 2015-2016 The CyanogenMod 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.
+-->
+<resources>
+ <string name="incoming_file_toast_msg">Fitxategi bat dator beste gailu batetik. Berretsi fitxategi hau jasu nahi duzula.</string>
+ <string name="upload_fail_waiting">Ezin izan da fitxategia bidali, berriro saiatzen\u2026</string>
+</resources>
diff --git a/res/values-fa/cm_strings.xml b/res/values-fa/cm_strings.xml
new file mode 100644
index 0000000..b6ff0d5
--- /dev/null
+++ b/res/values-fa/cm_strings.xml
@@ -0,0 +1,20 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--Generated by crowdin.com-->
+<!--
+ Copyright (C) 2015-2016 The CyanogenMod 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.
+-->
+<resources>
+ <string name="upload_fail_waiting">فایل فرستاده نشد، تلاش مجدد\u2026</string>
+</resources>
diff --git a/res/values-fi/cm_strings.xml b/res/values-fi/cm_strings.xml
new file mode 100644
index 0000000..bb21687
--- /dev/null
+++ b/res/values-fi/cm_strings.xml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--Generated by crowdin.com-->
+<!--
+ Copyright (C) 2015-2016 The CyanogenMod 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.
+-->
+<resources>
+ <string name="incoming_file_toast_msg">Toisesta laitteesta saapuva tiedosto. Vahvista tiedoston vastaanotto.</string>
+ <string name="upload_fail_waiting">Tiedoston lähetys epäonnistui, yritetään uudelleen\u2026</string>
+</resources>
diff --git a/res/values-fr/cm_strings.xml b/res/values-fr/cm_strings.xml
new file mode 100644
index 0000000..5573526
--- /dev/null
+++ b/res/values-fr/cm_strings.xml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--Generated by crowdin.com-->
+<!--
+ Copyright (C) 2015-2016 The CyanogenMod 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.
+-->
+<resources>
+ <string name="incoming_file_toast_msg">Fichier en provenance d\'un autre appareil. Confirmez que vous voulez recevoir ce fichier.</string>
+ <string name="upload_fail_waiting">Impossible d\'envoyer le fichier, nouvelle tentative\u2026</string>
+</resources>
diff --git a/res/values-frp-rIT/strings.xml b/res/values-frp-rIT/strings.xml
new file mode 100644
index 0000000..906d653
--- /dev/null
+++ b/res/values-frp-rIT/strings.xml
@@ -0,0 +1,99 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--Generated by crowdin.com-->
+<!-- Copyright (C) 2007 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.
+-->
+<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <!--
+ This is the short description of a permission associated with the
+ Bluetooth Share Manager. It is displayed as part of the description of
+ any application that was granted that permission. This specific
+ permission controls access to the Bluetooth Share Manager by
+ applications that initiate Bluetooth share.
+ -->
+ <!--
+ This is the long description of a permission associated with the
+ Android Download Manager. It is displayed as part of the description
+ of any application that was granted that permission. This specific
+ permission controls access to the Download Manager by applications
+ that initiate downloads.
+ -->
+ <!-- string showed on "Share picutre via" dialog -->
+ <!-- string for "unknown device" -->
+ <!-- string for "unknown" phone number" -->
+ <!-- string for "the title of airplane mode error" -->
+ <!-- string for "error message in airplane mode" -->
+ <!-- Activate Bluetooth Confirmation Dialog -->
+ <!--Title -->
+ <!--Line 1 -->
+ <!--Line 2 -->
+ <!-- Label for a cancel button. -->
+ <!-- Label for a confirm button.-->
+ <!-- Bluetooth File Transfer Acceptance Dialog -->
+ <!--Title -->
+ <!--content -->
+ <!-- Label for a cancel button. -->
+ <!-- Label for a confirm button.-->
+ <!-- Label for timeout OK button.-->
+ <!-- content for timeout-->
+ <!-- Bluetooth File Transfer Acceptance Notification item -->
+ <!-- Inbound File Transfer Progress Notification item -->
+ <!-- label for the notification item of receiving file -->
+ <!-- label for the notification item of received file -->
+ <!-- label for the notification item of failed receiving file -->
+ <!-- Outbound File Transfer Progress Notification item -->
+ <!-- label for the notification item of sending file -->
+ <!-- label for the notification item of sent file -->
+ <!-- label for the notification item of sent file -status -->
+ <!-- label for the notification item of failed sending file -->
+ <!-- Bluetooth Download Progress Dialog -->
+ <!--Title -->
+ <!--Line 1 -->
+ <!--Line 2 -->
+ <!--Line 3 -->
+ <!--Line 4 -->
+ <!-- Label for a cancel button. -->
+ <!-- Label for a hide button.-->
+ <!--Line 1 -->
+ <!--Line 2 -->
+ <!--Line 3 -->
+ <!-- Bluetooth failed Download Dialog -->
+ <!--Line 1 -->
+ <!--Line 2 -->
+ <!--Line 3 -->
+ <!-- Label for ok button.-->
+ <!-- Bluetooth Successful Download Dialog -->
+ <!--Line 4 in "Bluetooth Download Progress Dialog" -->
+ <!-- Label for a OK button.-->
+ <!-- Bluetooth Upload Progress Dialog -->
+ <!-- Bluetooth Successful Upload Progress Dialog -->
+ <!--Line 4 -->
+ <!-- Label for a confirm button.-->
+ <!-- Bluetooth Failed Upload File Transfer Dialog -->
+ <!-- Label for a try again button.-->
+ <!-- Label for a cancel button.-->
+ <!-- Bluetooth error dialog -->
+ <!-- Bluetooth Enabling progress dialog -->
+ <!-- Bluetooth Toast Message -->
+ <!-- Bluetooth System Messages [CHAR LIMIT=NONE] -->
+ <!-- Bluetooth System Messages -->
+ <!-- Bluetooth Transfer Failure Reason -->
+ <!-- Shown when USB storage cannot be found. [CHAR LIMIT=NONE] -->
+ <!-- Bluetooth OPP Live Folder -->
+ <!-- Bluetooth OPP Transfer History -->
+ <!-- Do not translate. file name used for sharing. -->
+ <!-- Used to run Bluetooth.apk in another process if needed -->
+ <!-- Do not translate. android:sharedUserId string of this application. -->
+ <!-- Do not translate. android:process of this application. -->
+</resources>
diff --git a/res/values-frp-rIT/strings_pbap.xml b/res/values-frp-rIT/strings_pbap.xml
new file mode 100644
index 0000000..55f5b83
--- /dev/null
+++ b/res/values-frp-rIT/strings_pbap.xml
@@ -0,0 +1,6 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--Generated by crowdin.com-->
+<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <!-- Notification title when a Bluetooth device wants to pair with us -->
+ <!-- Notification message when a Bluetooth device wants to pair with us -->
+</resources>
diff --git a/res/values-fy-rNL/strings.xml b/res/values-fy-rNL/strings.xml
new file mode 100644
index 0000000..906d653
--- /dev/null
+++ b/res/values-fy-rNL/strings.xml
@@ -0,0 +1,99 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--Generated by crowdin.com-->
+<!-- Copyright (C) 2007 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.
+-->
+<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <!--
+ This is the short description of a permission associated with the
+ Bluetooth Share Manager. It is displayed as part of the description of
+ any application that was granted that permission. This specific
+ permission controls access to the Bluetooth Share Manager by
+ applications that initiate Bluetooth share.
+ -->
+ <!--
+ This is the long description of a permission associated with the
+ Android Download Manager. It is displayed as part of the description
+ of any application that was granted that permission. This specific
+ permission controls access to the Download Manager by applications
+ that initiate downloads.
+ -->
+ <!-- string showed on "Share picutre via" dialog -->
+ <!-- string for "unknown device" -->
+ <!-- string for "unknown" phone number" -->
+ <!-- string for "the title of airplane mode error" -->
+ <!-- string for "error message in airplane mode" -->
+ <!-- Activate Bluetooth Confirmation Dialog -->
+ <!--Title -->
+ <!--Line 1 -->
+ <!--Line 2 -->
+ <!-- Label for a cancel button. -->
+ <!-- Label for a confirm button.-->
+ <!-- Bluetooth File Transfer Acceptance Dialog -->
+ <!--Title -->
+ <!--content -->
+ <!-- Label for a cancel button. -->
+ <!-- Label for a confirm button.-->
+ <!-- Label for timeout OK button.-->
+ <!-- content for timeout-->
+ <!-- Bluetooth File Transfer Acceptance Notification item -->
+ <!-- Inbound File Transfer Progress Notification item -->
+ <!-- label for the notification item of receiving file -->
+ <!-- label for the notification item of received file -->
+ <!-- label for the notification item of failed receiving file -->
+ <!-- Outbound File Transfer Progress Notification item -->
+ <!-- label for the notification item of sending file -->
+ <!-- label for the notification item of sent file -->
+ <!-- label for the notification item of sent file -status -->
+ <!-- label for the notification item of failed sending file -->
+ <!-- Bluetooth Download Progress Dialog -->
+ <!--Title -->
+ <!--Line 1 -->
+ <!--Line 2 -->
+ <!--Line 3 -->
+ <!--Line 4 -->
+ <!-- Label for a cancel button. -->
+ <!-- Label for a hide button.-->
+ <!--Line 1 -->
+ <!--Line 2 -->
+ <!--Line 3 -->
+ <!-- Bluetooth failed Download Dialog -->
+ <!--Line 1 -->
+ <!--Line 2 -->
+ <!--Line 3 -->
+ <!-- Label for ok button.-->
+ <!-- Bluetooth Successful Download Dialog -->
+ <!--Line 4 in "Bluetooth Download Progress Dialog" -->
+ <!-- Label for a OK button.-->
+ <!-- Bluetooth Upload Progress Dialog -->
+ <!-- Bluetooth Successful Upload Progress Dialog -->
+ <!--Line 4 -->
+ <!-- Label for a confirm button.-->
+ <!-- Bluetooth Failed Upload File Transfer Dialog -->
+ <!-- Label for a try again button.-->
+ <!-- Label for a cancel button.-->
+ <!-- Bluetooth error dialog -->
+ <!-- Bluetooth Enabling progress dialog -->
+ <!-- Bluetooth Toast Message -->
+ <!-- Bluetooth System Messages [CHAR LIMIT=NONE] -->
+ <!-- Bluetooth System Messages -->
+ <!-- Bluetooth Transfer Failure Reason -->
+ <!-- Shown when USB storage cannot be found. [CHAR LIMIT=NONE] -->
+ <!-- Bluetooth OPP Live Folder -->
+ <!-- Bluetooth OPP Transfer History -->
+ <!-- Do not translate. file name used for sharing. -->
+ <!-- Used to run Bluetooth.apk in another process if needed -->
+ <!-- Do not translate. android:sharedUserId string of this application. -->
+ <!-- Do not translate. android:process of this application. -->
+</resources>
diff --git a/res/values-fy-rNL/strings_pbap.xml b/res/values-fy-rNL/strings_pbap.xml
new file mode 100644
index 0000000..55f5b83
--- /dev/null
+++ b/res/values-fy-rNL/strings_pbap.xml
@@ -0,0 +1,6 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--Generated by crowdin.com-->
+<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <!-- Notification title when a Bluetooth device wants to pair with us -->
+ <!-- Notification message when a Bluetooth device wants to pair with us -->
+</resources>
diff --git a/res/values-ga-rIE/strings.xml b/res/values-ga-rIE/strings.xml
new file mode 100644
index 0000000..906d653
--- /dev/null
+++ b/res/values-ga-rIE/strings.xml
@@ -0,0 +1,99 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--Generated by crowdin.com-->
+<!-- Copyright (C) 2007 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.
+-->
+<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <!--
+ This is the short description of a permission associated with the
+ Bluetooth Share Manager. It is displayed as part of the description of
+ any application that was granted that permission. This specific
+ permission controls access to the Bluetooth Share Manager by
+ applications that initiate Bluetooth share.
+ -->
+ <!--
+ This is the long description of a permission associated with the
+ Android Download Manager. It is displayed as part of the description
+ of any application that was granted that permission. This specific
+ permission controls access to the Download Manager by applications
+ that initiate downloads.
+ -->
+ <!-- string showed on "Share picutre via" dialog -->
+ <!-- string for "unknown device" -->
+ <!-- string for "unknown" phone number" -->
+ <!-- string for "the title of airplane mode error" -->
+ <!-- string for "error message in airplane mode" -->
+ <!-- Activate Bluetooth Confirmation Dialog -->
+ <!--Title -->
+ <!--Line 1 -->
+ <!--Line 2 -->
+ <!-- Label for a cancel button. -->
+ <!-- Label for a confirm button.-->
+ <!-- Bluetooth File Transfer Acceptance Dialog -->
+ <!--Title -->
+ <!--content -->
+ <!-- Label for a cancel button. -->
+ <!-- Label for a confirm button.-->
+ <!-- Label for timeout OK button.-->
+ <!-- content for timeout-->
+ <!-- Bluetooth File Transfer Acceptance Notification item -->
+ <!-- Inbound File Transfer Progress Notification item -->
+ <!-- label for the notification item of receiving file -->
+ <!-- label for the notification item of received file -->
+ <!-- label for the notification item of failed receiving file -->
+ <!-- Outbound File Transfer Progress Notification item -->
+ <!-- label for the notification item of sending file -->
+ <!-- label for the notification item of sent file -->
+ <!-- label for the notification item of sent file -status -->
+ <!-- label for the notification item of failed sending file -->
+ <!-- Bluetooth Download Progress Dialog -->
+ <!--Title -->
+ <!--Line 1 -->
+ <!--Line 2 -->
+ <!--Line 3 -->
+ <!--Line 4 -->
+ <!-- Label for a cancel button. -->
+ <!-- Label for a hide button.-->
+ <!--Line 1 -->
+ <!--Line 2 -->
+ <!--Line 3 -->
+ <!-- Bluetooth failed Download Dialog -->
+ <!--Line 1 -->
+ <!--Line 2 -->
+ <!--Line 3 -->
+ <!-- Label for ok button.-->
+ <!-- Bluetooth Successful Download Dialog -->
+ <!--Line 4 in "Bluetooth Download Progress Dialog" -->
+ <!-- Label for a OK button.-->
+ <!-- Bluetooth Upload Progress Dialog -->
+ <!-- Bluetooth Successful Upload Progress Dialog -->
+ <!--Line 4 -->
+ <!-- Label for a confirm button.-->
+ <!-- Bluetooth Failed Upload File Transfer Dialog -->
+ <!-- Label for a try again button.-->
+ <!-- Label for a cancel button.-->
+ <!-- Bluetooth error dialog -->
+ <!-- Bluetooth Enabling progress dialog -->
+ <!-- Bluetooth Toast Message -->
+ <!-- Bluetooth System Messages [CHAR LIMIT=NONE] -->
+ <!-- Bluetooth System Messages -->
+ <!-- Bluetooth Transfer Failure Reason -->
+ <!-- Shown when USB storage cannot be found. [CHAR LIMIT=NONE] -->
+ <!-- Bluetooth OPP Live Folder -->
+ <!-- Bluetooth OPP Transfer History -->
+ <!-- Do not translate. file name used for sharing. -->
+ <!-- Used to run Bluetooth.apk in another process if needed -->
+ <!-- Do not translate. android:sharedUserId string of this application. -->
+ <!-- Do not translate. android:process of this application. -->
+</resources>
diff --git a/res/values-ga-rIE/strings_pbap.xml b/res/values-ga-rIE/strings_pbap.xml
new file mode 100644
index 0000000..55f5b83
--- /dev/null
+++ b/res/values-ga-rIE/strings_pbap.xml
@@ -0,0 +1,6 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--Generated by crowdin.com-->
+<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <!-- Notification title when a Bluetooth device wants to pair with us -->
+ <!-- Notification message when a Bluetooth device wants to pair with us -->
+</resources>
diff --git a/res/values-gd-rGB/strings.xml b/res/values-gd-rGB/strings.xml
new file mode 100644
index 0000000..906d653
--- /dev/null
+++ b/res/values-gd-rGB/strings.xml
@@ -0,0 +1,99 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--Generated by crowdin.com-->
+<!-- Copyright (C) 2007 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.
+-->
+<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <!--
+ This is the short description of a permission associated with the
+ Bluetooth Share Manager. It is displayed as part of the description of
+ any application that was granted that permission. This specific
+ permission controls access to the Bluetooth Share Manager by
+ applications that initiate Bluetooth share.
+ -->
+ <!--
+ This is the long description of a permission associated with the
+ Android Download Manager. It is displayed as part of the description
+ of any application that was granted that permission. This specific
+ permission controls access to the Download Manager by applications
+ that initiate downloads.
+ -->
+ <!-- string showed on "Share picutre via" dialog -->
+ <!-- string for "unknown device" -->
+ <!-- string for "unknown" phone number" -->
+ <!-- string for "the title of airplane mode error" -->
+ <!-- string for "error message in airplane mode" -->
+ <!-- Activate Bluetooth Confirmation Dialog -->
+ <!--Title -->
+ <!--Line 1 -->
+ <!--Line 2 -->
+ <!-- Label for a cancel button. -->
+ <!-- Label for a confirm button.-->
+ <!-- Bluetooth File Transfer Acceptance Dialog -->
+ <!--Title -->
+ <!--content -->
+ <!-- Label for a cancel button. -->
+ <!-- Label for a confirm button.-->
+ <!-- Label for timeout OK button.-->
+ <!-- content for timeout-->
+ <!-- Bluetooth File Transfer Acceptance Notification item -->
+ <!-- Inbound File Transfer Progress Notification item -->
+ <!-- label for the notification item of receiving file -->
+ <!-- label for the notification item of received file -->
+ <!-- label for the notification item of failed receiving file -->
+ <!-- Outbound File Transfer Progress Notification item -->
+ <!-- label for the notification item of sending file -->
+ <!-- label for the notification item of sent file -->
+ <!-- label for the notification item of sent file -status -->
+ <!-- label for the notification item of failed sending file -->
+ <!-- Bluetooth Download Progress Dialog -->
+ <!--Title -->
+ <!--Line 1 -->
+ <!--Line 2 -->
+ <!--Line 3 -->
+ <!--Line 4 -->
+ <!-- Label for a cancel button. -->
+ <!-- Label for a hide button.-->
+ <!--Line 1 -->
+ <!--Line 2 -->
+ <!--Line 3 -->
+ <!-- Bluetooth failed Download Dialog -->
+ <!--Line 1 -->
+ <!--Line 2 -->
+ <!--Line 3 -->
+ <!-- Label for ok button.-->
+ <!-- Bluetooth Successful Download Dialog -->
+ <!--Line 4 in "Bluetooth Download Progress Dialog" -->
+ <!-- Label for a OK button.-->
+ <!-- Bluetooth Upload Progress Dialog -->
+ <!-- Bluetooth Successful Upload Progress Dialog -->
+ <!--Line 4 -->
+ <!-- Label for a confirm button.-->
+ <!-- Bluetooth Failed Upload File Transfer Dialog -->
+ <!-- Label for a try again button.-->
+ <!-- Label for a cancel button.-->
+ <!-- Bluetooth error dialog -->
+ <!-- Bluetooth Enabling progress dialog -->
+ <!-- Bluetooth Toast Message -->
+ <!-- Bluetooth System Messages [CHAR LIMIT=NONE] -->
+ <!-- Bluetooth System Messages -->
+ <!-- Bluetooth Transfer Failure Reason -->
+ <!-- Shown when USB storage cannot be found. [CHAR LIMIT=NONE] -->
+ <!-- Bluetooth OPP Live Folder -->
+ <!-- Bluetooth OPP Transfer History -->
+ <!-- Do not translate. file name used for sharing. -->
+ <!-- Used to run Bluetooth.apk in another process if needed -->
+ <!-- Do not translate. android:sharedUserId string of this application. -->
+ <!-- Do not translate. android:process of this application. -->
+</resources>
diff --git a/res/values-gd-rGB/strings_pbap.xml b/res/values-gd-rGB/strings_pbap.xml
new file mode 100644
index 0000000..55f5b83
--- /dev/null
+++ b/res/values-gd-rGB/strings_pbap.xml
@@ -0,0 +1,6 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--Generated by crowdin.com-->
+<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <!-- Notification title when a Bluetooth device wants to pair with us -->
+ <!-- Notification message when a Bluetooth device wants to pair with us -->
+</resources>
diff --git a/res/values-gl-rES/cm_strings.xml b/res/values-gl-rES/cm_strings.xml
new file mode 100644
index 0000000..005f57b
--- /dev/null
+++ b/res/values-gl-rES/cm_strings.xml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--Generated by crowdin.com-->
+<!--
+ Copyright (C) 2015-2016 The CyanogenMod 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.
+-->
+<resources>
+ <string name="incoming_file_toast_msg">Recibindo arquivo doutro dispositivo. Confirme se vostede desexa recibir este arquivo.</string>
+ <string name="upload_fail_waiting">Non se puido enviar o ficheiro, vólvese tentar\u2026</string>
+</resources>
diff --git a/res/values-gu-rIN/cm_strings.xml b/res/values-gu-rIN/cm_strings.xml
new file mode 100644
index 0000000..e24cf74
--- /dev/null
+++ b/res/values-gu-rIN/cm_strings.xml
@@ -0,0 +1,20 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--Generated by crowdin.com-->
+<!--
+ Copyright (C) 2015-2016 The CyanogenMod 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.
+-->
+<resources>
+ <string name="upload_fail_waiting">ફાઇલ મોકલવામાં અક્ષમ, પુનઃપ્રયાસ થાય છે\u2026</string>
+</resources>
diff --git a/res/values-hi/cm_strings.xml b/res/values-hi/cm_strings.xml
new file mode 100644
index 0000000..f1fea5e
--- /dev/null
+++ b/res/values-hi/cm_strings.xml
@@ -0,0 +1,20 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--Generated by crowdin.com-->
+<!--
+ Copyright (C) 2015-2016 The CyanogenMod 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.
+-->
+<resources>
+ <string name="upload_fail_waiting">फ़ाइल नहीं भेज सके, फिर से कोशिश कर रहे हैं\u2026</string>
+</resources>
diff --git a/res/values-hr/cm_strings.xml b/res/values-hr/cm_strings.xml
new file mode 100644
index 0000000..f9fcdf5
--- /dev/null
+++ b/res/values-hr/cm_strings.xml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--Generated by crowdin.com-->
+<!--
+ Copyright (C) 2015-2016 The CyanogenMod 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.
+-->
+<resources>
+ <string name="incoming_file_toast_msg">Dolazna datoteka s drugog uređaja. Potvrdite ako želite primiti ovu datoteku.</string>
+ <string name="upload_fail_waiting">Nije moguće poslati datoteku, ponovno pokušavanje\u2026</string>
+</resources>
diff --git a/res/values-hu/cm_strings.xml b/res/values-hu/cm_strings.xml
new file mode 100644
index 0000000..03ba2ed
--- /dev/null
+++ b/res/values-hu/cm_strings.xml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--Generated by crowdin.com-->
+<!--
+ Copyright (C) 2015-2016 The CyanogenMod 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.
+-->
+<resources>
+ <string name="incoming_file_toast_msg">Bejövő fájl egy másik készülékről. Erősítse meg, hogy szeretné-e fogadni.</string>
+ <string name="upload_fail_waiting">Fájl küldése sikertelen, újraküldés\u2026</string>
+</resources>
diff --git a/res/values-in/cm_strings.xml b/res/values-in/cm_strings.xml
new file mode 100644
index 0000000..1e3de9d
--- /dev/null
+++ b/res/values-in/cm_strings.xml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--Generated by crowdin.com-->
+<!--
+ Copyright (C) 2015-2016 The CyanogenMod 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.
+-->
+<resources>
+ <string name="incoming_file_toast_msg">Berkas masuk dari perangkat lain. Konfirmasi anda ingin menerima berkas ini.</string>
+ <string name="upload_fail_waiting">Tidak dapat mengirim berkas, mencoba kembali\u2026</string>
+</resources>
diff --git a/res/values-it/cm_strings.xml b/res/values-it/cm_strings.xml
new file mode 100644
index 0000000..1294353
--- /dev/null
+++ b/res/values-it/cm_strings.xml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--Generated by crowdin.com-->
+<!--
+ Copyright (C) 2015-2016 The CyanogenMod 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.
+-->
+<resources>
+ <string name="incoming_file_toast_msg">File in arrivo da un altro dispositivo. Conferma che vuoi ricevere questo file.</string>
+ <string name="upload_fail_waiting">Impossibile inviare file, riprovo\u2026</string>
+</resources>
diff --git a/res/values-iw/cm_strings.xml b/res/values-iw/cm_strings.xml
new file mode 100644
index 0000000..30e8de4
--- /dev/null
+++ b/res/values-iw/cm_strings.xml
@@ -0,0 +1,20 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--Generated by crowdin.com-->
+<!--
+ Copyright (C) 2015-2016 The CyanogenMod 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.
+-->
+<resources>
+ <string name="upload_fail_waiting">שליחת הקובץ נכשלה, מנסה שנית\u2026</string>
+</resources>
diff --git a/res/values-ja/cm_strings.xml b/res/values-ja/cm_strings.xml
new file mode 100644
index 0000000..d655629
--- /dev/null
+++ b/res/values-ja/cm_strings.xml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--Generated by crowdin.com-->
+<!--
+ Copyright (C) 2015-2016 The CyanogenMod 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.
+-->
+<resources>
+ <string name="incoming_file_toast_msg">他の端末からファイルが送信されています。このファイルを受け取るかどうか確認してください。</string>
+ <string name="upload_fail_waiting">ファイルを送信できませんでした、再試行しています\u2026</string>
+</resources>
diff --git a/res/values-kn-rIN/cm_strings.xml b/res/values-kn-rIN/cm_strings.xml
new file mode 100644
index 0000000..754e4ce
--- /dev/null
+++ b/res/values-kn-rIN/cm_strings.xml
@@ -0,0 +1,20 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--Generated by crowdin.com-->
+<!--
+ Copyright (C) 2015-2016 The CyanogenMod 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.
+-->
+<resources>
+ <string name="upload_fail_waiting">ಕಡತ ಕಳುಹಿಸಲು ಸಾಧವಾಗುತ್ತಿಲ್ಲ, ಮರುಪ್ರಯತ್ನಿಸುತ್ತಿದೆ\u2026</string>
+</resources>
diff --git a/res/values-ko/cm_strings.xml b/res/values-ko/cm_strings.xml
new file mode 100644
index 0000000..6f704e2
--- /dev/null
+++ b/res/values-ko/cm_strings.xml
@@ -0,0 +1,20 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--Generated by crowdin.com-->
+<!--
+ Copyright (C) 2015-2016 The CyanogenMod 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.
+-->
+<resources>
+ <string name="upload_fail_waiting">파일 전송 실패, 재시도 중\u2026</string>
+</resources>
diff --git a/res/values-ku/cm_strings.xml b/res/values-ku/cm_strings.xml
new file mode 100644
index 0000000..2852fe0
--- /dev/null
+++ b/res/values-ku/cm_strings.xml
@@ -0,0 +1,20 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--Generated by crowdin.com-->
+<!--
+ Copyright (C) 2015-2016 The CyanogenMod 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.
+-->
+<resources>
+ <string name="upload_fail_waiting">ناتوانرێت فایل بنێردرێت، دوبارەهەوڵبەرەوە\u2026</string>
+</resources>
diff --git a/res/values-ku/strings.xml b/res/values-ku/strings.xml
new file mode 100644
index 0000000..07e565b
--- /dev/null
+++ b/res/values-ku/strings.xml
@@ -0,0 +1,194 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--Generated by crowdin.com-->
+<!-- Copyright (C) 2007 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.
+-->
+<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <!--
+ This is the short description of a permission associated with the
+ Bluetooth Share Manager. It is displayed as part of the description of
+ any application that was granted that permission. This specific
+ permission controls access to the Bluetooth Share Manager by
+ applications that initiate Bluetooth share.
+ -->
+ <string name="permlab_bluetoothShareManager">ڕێگەدانی بەڕێوەبەرایەتی دابەزین.</string>
+ <!--
+ This is the long description of a permission associated with the
+ Android Download Manager. It is displayed as part of the description
+ of any application that was granted that permission. This specific
+ permission controls access to the Download Manager by applications
+ that initiate downloads.
+ -->
+ <string name="permdesc_bluetoothShareManager">ڕێگەدان بە بەرنامەکە بۆ ڕێگە بەشداری
+بەڕێوەبەری بلوتوس و بەکارهێنانی ناردنی پەڕگە.</string>
+ <string name="permlab_bluetoothWhitelist">ڕێگەکانی ئامێری بلوتوس.</string>
+ <string name="permdesc_bluetoothWhitelist">بۆ بەرنامە بە شێوەيەكى كاتى ڕێ دەدات بە وهيتێليست
+
+ئامرازێكى بلوێتوث، ڕێدان بەى ئەوە ئامراز تا بۆ ئەمە ئامرازى فايل بەبێ بەكاربەر بنێرێت
+
+دووپات كردن.</string>
+ <!-- string showed on "Share picutre via" dialog -->
+ <string name="bt_share_picker_label">بلوتوس</string>
+ <!-- string for "unknown device" -->
+ <string name="unknown_device">ئامێری نەناسراو</string>
+ <!-- string for "unknown" phone number" -->
+ <string name="unknownNumber">نەناسراو</string>
+ <!-- string for "the title of airplane mode error" -->
+ <string name="airplane_error_title">شێوازی فڕۆکە</string>
+ <!-- string for "error message in airplane mode" -->
+ <string name="airplane_error_msg">ناتوانی بلوتوس بەکاربهێنی لە باری فڕۆکە.</string>
+ <!-- Activate Bluetooth Confirmation Dialog -->
+ <!--Title -->
+ <!--Line 1 -->
+ <string name="bt_enable_line1">بۆ بەکارهێنانی خزمەتگوزاری بلوتوس ئەبێت بلوتوسەکە بکەیتەوە.</string>
+ <!--Line 2 -->
+ <string name="bt_enable_line2">ئێستا چالاککردنی بلوتوس?</string>
+ <!-- Label for a cancel button. -->
+ <string name="bt_enable_cancel">هەڵوەشاندنەوە</string>
+ <!-- Label for a confirm button.-->
+ <string name="bt_enable_ok">کردنە کار</string>
+ <!-- Bluetooth File Transfer Acceptance Dialog -->
+ <!--Title -->
+ <string name="incoming_file_confirm_title">گواستنهوهی پهڕگه</string>
+ <!--content -->
+ <!-- Label for a cancel button. -->
+ <string name="incoming_file_confirm_cancel">ڕەتکردنەوە</string>
+ <!-- Label for a confirm button.-->
+ <string name="incoming_file_confirm_ok">ڕازیبوون</string>
+ <!-- Label for timeout OK button.-->
+ <string name="incoming_file_confirm_timeout_ok">باشه</string>
+ <!-- content for timeout-->
+ <string name="incoming_file_confirm_timeout_content">کاتەکە بەسەر چووکاتێک پەڕگەکەت قبولکردن هات \u0022<xliff:g id="sender">%1$s</xliff:g>\u0022</string>
+ <!-- Bluetooth File Transfer Acceptance Notification item -->
+ <!-- Inbound File Transfer Progress Notification item -->
+ <!-- label for the notification item of receiving file -->
+ <string name="notification_receiving">بەشداریکردنی بلوتوس: وەرگرتن<xliff:g id="file">%1$s</xliff:g></string>
+ <!-- label for the notification item of received file -->
+ <string name="notification_received">بەشداریکردنی بلوتوس: وەرگرتن<xliff:g id="file">%1$s</xliff:g></string>
+ <!-- label for the notification item of failed receiving file -->
+ <string name="notification_received_fail">بەشداریکردنی بلوتوس: File <xliff:g id="file">%1$s</xliff:g> نەگەیشت</string>
+ <!-- Outbound File Transfer Progress Notification item -->
+ <!-- label for the notification item of sending file -->
+ <string name="notification_sending">بەشداریکردنی بلوتوس: وەرگرتن<xliff:g id="file">%1$s</xliff:g></string>
+ <!-- label for the notification item of sent file -->
+ <string name="notification_sent">بەشداریکردنی بلوتوس: گەیشت<xliff:g id="file">%1$s</xliff:g></string>
+ <!-- label for the notification item of sent file -status -->
+ <string name="notification_sent_complete">100% تەواوبوو</string>
+ <!-- label for the notification item of failed sending file -->
+ <string name="notification_sent_fail">بەشداری بلوتوس: پەڕگە<xliff:g id="file">%1$s</xliff:g> نەگەیشت</string>
+ <!-- Bluetooth Download Progress Dialog -->
+ <!--Title -->
+ <string name="download_title">گواستنهوهی پەڕگە</string>
+ <!--Line 1 -->
+ <string name="download_line1">لە: \u0022<xliff:g id="sender">%1$s</xliff:g>\u0022</string>
+ <!--Line 2 -->
+ <string name="download_line2">پەڕگە: <xliff:g id="file">%1$s</xliff:g></string>
+ <!--Line 3 -->
+ <string name="download_line3">قەبارەی فایلەکە: <xliff:g id="size">%1$s</xliff:g></string>
+ <!--Line 4 -->
+ <string name="download_line5">وەرگرتنی پەڕگە\u2026</string>
+ <!-- Label for a cancel button. -->
+ <string name="download_cancel">وهستاندن</string>
+ <!-- Label for a hide button.-->
+ <string name="download_ok">شاردنەوە</string>
+ <!--Line 1 -->
+ <!--Line 2 -->
+ <!--Line 3 -->
+ <!-- Bluetooth failed Download Dialog -->
+ <!--Line 1 -->
+ <string name="download_fail_line1">فایلەکە نەگەیشت</string>
+ <!--Line 2 -->
+ <string name="download_fail_line2">پەڕگە: <xliff:g id="file">%1$s</xliff:g></string>
+ <!--Line 3 -->
+ <string name="download_fail_line3">هۆکار: <xliff:g id="reason">%1$s</xliff:g></string>
+ <!-- Label for ok button.-->
+ <string name="download_fail_ok">باشه</string>
+ <!-- Bluetooth Successful Download Dialog -->
+ <!--Line 4 in "Bluetooth Download Progress Dialog" -->
+ <string name="download_succ_line5">فایلەکە گەیشت</string>
+ <!-- Label for a OK button.-->
+ <string name="download_succ_ok">کردنهوه</string>
+ <!-- Bluetooth Upload Progress Dialog -->
+ <string name="upload_line1">بۆ: \u0022<xliff:g id="recipient">%1$s</xliff:g>\u0022</string>
+ <string name="upload_line3">جۆری پەڕگە: <xliff:g id="type">%1$s</xliff:g> (<xliff:g id="size">%2$s</xliff:g>)</string>
+ <string name="upload_line5">ناردنی پەڕگە\u2026</string>
+ <!-- Bluetooth Successful Upload Progress Dialog -->
+ <!--Line 4 -->
+ <string name="upload_succ_line5">فایلەکە نێردرا</string>
+ <!-- Label for a confirm button.-->
+ <string name="upload_succ_ok">باشه</string>
+ <!-- Bluetooth Failed Upload File Transfer Dialog -->
+ <string name="upload_fail_line1">فایلەکە نەنێردرا بۆ \u0022<xliff:g id="recipient">%1$s</xliff:g>\u0022.</string>
+ <string name="upload_fail_line1_2">پەڕگە: <xliff:g id="file">%1$s</xliff:g></string>
+ <!-- Label for a try again button.-->
+ <string name="upload_fail_ok">دوبارە هەوڵبدەرەوە</string>
+ <!-- Label for a cancel button.-->
+ <string name="upload_fail_cancel">داخستن</string>
+ <!-- Bluetooth error dialog -->
+ <string name="bt_error_btn_ok">باشه</string>
+ <string name="unknown_file">فایلی نەناسراو</string>
+ <string name="unknown_file_desc">بەرنامەیەک نیە بۆ هەڵگرتنی ئەم جۆرە فایلە. \n</string>
+ <string name="not_exist_file">پەڕگە نیە</string>
+ <string name="not_exist_file_desc">فایلەکە بونی نیە. \n</string>
+ <!-- Bluetooth Enabling progress dialog -->
+ <string name="enabling_progress_title">تکایە چاوەڕێبکە\u2026</string>
+ <string name="enabling_progress_content">چالاککردنی بلوتوس\u2026</string>
+ <!-- Bluetooth Toast Message -->
+ <string name="bt_toast_1">فەیلەکە ئەگەیەت. پشکنینی تێبینی.</string>
+ <string name="bt_toast_2">فایلەکە ناتوانرێت بنێردرێت.</string>
+ <string name="bt_toast_3">گەیشتنی فایلەکە وەستا لە \u0022<xliff:g id="sender">%1$s</xliff:g>\u0022</string>
+ <string name="bt_toast_4">ناردنی بەرنامەکە بۆ \u0022<xliff:g id="recipient">%1$s</xliff:g>\u0022</string>
+ <string name="bt_toast_5">ناردن <xliff:g id="number">%1$s</xliff:g> فایل بۆ \u0022<xliff:g id="recipient">%2$s</xliff:g>\u0022</string>
+ <string name="bt_toast_6">وەستاندنی فایل ناردن بۆ \u0022<xliff:g id="recipient">%1$s</xliff:g>\u0022</string>
+ <!-- Bluetooth System Messages [CHAR LIMIT=NONE] -->
+ <string name="bt_sm_2_1" product="nosdcard">بیرگەی تەواو لەبەردەست نیە بۆ پاشەکەوتکردنی فایلەکە لە \u0022<xliff:g id="sender">%1$s</xliff:g>\u0022</string>
+ <!-- Bluetooth System Messages -->
+ <string name="bt_sm_2_1" product="default">بیرگەی تەواو لەبەردەست نیە بۆ پاشەکەوتکردنی فایلەکە لە \u0022<xliff:g id="sender">%1$s</xliff:g>\u0022</string>
+ <string name="bt_sm_2_2">بۆشایی پێوستە: <xliff:g id="size">%1$s</xliff:g></string>
+ <string name="ErrorTooManyRequests">زۆر داواکاری لە ڕێکخەریدان. دواتر هەوڵبدەوە.</string>
+ <!-- Bluetooth Transfer Failure Reason -->
+ <string name="status_pending">گواستنەوەی فایلەکە دەستی پێ نەکردوە هێشتا.</string>
+ <string name="status_running">ڕۆیشتنی پەڕگە ناردنەکە.</string>
+ <string name="status_success">گواستنەوەی فایلەکە بەسەرکەوتوی تەواوبوو.</string>
+ <string name="status_not_accept">ناوەڕۆک پاڵپشتی ناکات.</string>
+ <string name="status_forbidden">گواستنەوەکە قەدەغەکراوە لەلایەن ئامێری نیشانەوە.</string>
+ <string name="status_canceled">گواستنەوەکە پوچەڵکرایەوە لەلایەن بەکارهێنەرەوە.</string>
+ <string name="status_file_error">کێشەی بیرگە.</string>
+ <!-- Shown when USB storage cannot be found. [CHAR LIMIT=NONE] -->
+ <string name="status_no_sd_card" product="nosdcard">بیرگەی ناوەکی نیە.</string>
+ <string name="status_no_sd_card" product="default">بیرگەی دەرەکی نیە. بیرگەیەکی تێبکە بۆ گواستنەوەی فایلەکان.</string>
+ <string name="status_connection_error">بەستنەوەکە سەرکەوتونەبو.</string>
+ <string name="status_protocol_error">داواکردنەکە ناتوانرێت بەتەواوی بکرێت.</string>
+ <string name="status_unknown_error">ههڵهی نهزانراو.</string>
+ <!-- Bluetooth OPP Live Folder -->
+ <string name="btopp_live_folder">وهرگیراوهکان</string>
+ <!-- Bluetooth OPP Transfer History -->
+ <string name="download_success"> <xliff:g id="file_size">%1$s</xliff:g> گەیشتنەکە تەواوبو.</string>
+ <string name="upload_success"> <xliff:g id="file_size">%1$s</xliff:g> گەیشتنەکە تەواوبو.</string>
+ <string name="inbound_history_title">نەبەستنەوەی گواستنەوە</string>
+ <string name="outbound_history_title">گواستنەوەی بەرەودەرەوە</string>
+ <string name="no_transfers">تۆماری وهرگیراوهکان بهتاڵه.</string>
+ <string name="transfer_clear_dlg_msg">هەموو ئامرازەکان پاک ئەکرێنەوە لە خشتەکە.</string>
+ <string name="outbound_noti_title">بەشداری بلوتوس: فایلەکە نێردرا</string>
+ <string name="inbound_noti_title">بەشداری بلوتوس: فایلەکە گەیشت</string>
+ <string name="transfer_menu_clear_all">پاکردنەوەی خشتەکە</string>
+ <string name="transfer_menu_open">کردنهوه</string>
+ <string name="transfer_menu_clear">پاکردنەوە لە خشتەکە</string>
+ <string name="transfer_clear_dlg_title">پاککردنهوه</string>
+ <!-- Do not translate. file name used for sharing. -->
+ <!-- Used to run Bluetooth.apk in another process if needed -->
+ <!-- Do not translate. android:sharedUserId string of this application. -->
+ <!-- Do not translate. android:process of this application. -->
+ <string name="bluetooth_map_settings_cancel">هەڵوەشاندنەوە</string>
+</resources>
diff --git a/res/values-ku/strings_pbap.xml b/res/values-ku/strings_pbap.xml
new file mode 100644
index 0000000..a0f50fd
--- /dev/null
+++ b/res/values-ku/strings_pbap.xml
@@ -0,0 +1,17 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--Generated by crowdin.com-->
+<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="pbap_session_key_dialog_title">کلیلی تر بنوسە بۆ %1$s</string>
+ <string name="pbap_session_key_dialog_header">کلیلێکی تر پێویستە بۆ بلوتوس</string>
+ <string name="pbap_acceptance_timeout_message">کات تهواو بوو بۆ پهیوهندی کردن لهگهڵ %1$s</string>
+ <string name="pbap_authentication_timeout_message">کات تهواو بوو بۆ پهیوهندی کردن لهگهڵ %1$s</string>
+ <string name="auth_notif_ticker">داواکاری ناساندنی ئۆبیکس</string>
+ <!-- Notification title when a Bluetooth device wants to pair with us -->
+ <string name="auth_notif_title">کلیلی تر</string>
+ <!-- Notification message when a Bluetooth device wants to pair with us -->
+ <string name="auth_notif_message">کلیلی تر بنووسه بۆ %1$s</string>
+ <string name="defaultname">کارکیت</string>
+ <string name="unknownName">ناوی نهزانراو</string>
+ <string name="localPhoneName">ناوی من</string>
+ <string name="defaultnumber">000000</string>
+</resources>
diff --git a/res/values-lb/cm_strings.xml b/res/values-lb/cm_strings.xml
new file mode 100644
index 0000000..b8fcc81
--- /dev/null
+++ b/res/values-lb/cm_strings.xml
@@ -0,0 +1,20 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--Generated by crowdin.com-->
+<!--
+ Copyright (C) 2015-2016 The CyanogenMod 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.
+-->
+<resources>
+ <string name="upload_fail_waiting">Konnt de Fichier net schécken, gëtt nees probéiert\u2026</string>
+</resources>
diff --git a/res/values-lb/strings.xml b/res/values-lb/strings.xml
new file mode 100644
index 0000000..9530539
--- /dev/null
+++ b/res/values-lb/strings.xml
@@ -0,0 +1,203 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--Generated by crowdin.com-->
+<!-- Copyright (C) 2007 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.
+-->
+<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <!--
+ This is the short description of a permission associated with the
+ Bluetooth Share Manager. It is displayed as part of the description of
+ any application that was granted that permission. This specific
+ permission controls access to the Bluetooth Share Manager by
+ applications that initiate Bluetooth share.
+ -->
+ <string name="permlab_bluetoothShareManager">Zougrëff op den Download-Manager</string>
+ <!--
+ This is the long description of a permission associated with the
+ Android Download Manager. It is displayed as part of the description
+ of any application that was granted that permission. This specific
+ permission controls access to the Download Manager by applications
+ that initiate downloads.
+ -->
+ <string name="permdesc_bluetoothShareManager">Erlaabt der App, op de BluetoothShare-Manager zouzegräifen an e fir Fichiersiwwerdroungen ze benotzen.</string>
+ <string name="permlab_bluetoothWhitelist">Bluetooth-Apparat op d\'Zougrëffslëscht setzen</string>
+ <string name="permdesc_bluetoothWhitelist">Erlaabt der App, temporär e Bluetooth-Apparat op d\'wäiss Lëscht ze setzen, sou datt vun deem Apparat e Fichier kann empfaange ginn ouni datt de Benotzer d\'Iwwerdroung bestätege muss.</string>
+ <!-- string showed on "Share picutre via" dialog -->
+ <string name="bt_share_picker_label">Bluetooth</string>
+ <!-- string for "unknown device" -->
+ <string name="unknown_device">Onbekannten Apparat</string>
+ <!-- string for "unknown" phone number" -->
+ <string name="unknownNumber">Onbekannt</string>
+ <!-- string for "the title of airplane mode error" -->
+ <string name="airplane_error_title">Fligermodus</string>
+ <!-- string for "error message in airplane mode" -->
+ <string name="airplane_error_msg">Du kanns Bluetooth net am Fligermodus benotzen.</string>
+ <!-- Activate Bluetooth Confirmation Dialog -->
+ <!--Title -->
+ <!--Line 1 -->
+ <string name="bt_enable_line1">Fir Bluetooth-Servicer ze benotze muss du fir d\'éischt Bluetooth uschalten.</string>
+ <!--Line 2 -->
+ <string name="bt_enable_line2">Bluetooth elo uschalten?</string>
+ <!-- Label for a cancel button. -->
+ <string name="bt_enable_cancel">Ofbriechen</string>
+ <!-- Label for a confirm button.-->
+ <string name="bt_enable_ok">Uschalten</string>
+ <!-- Bluetooth File Transfer Acceptance Dialog -->
+ <!--Title -->
+ <string name="incoming_file_confirm_title">Fichiersiwwerdroung</string>
+ <!--content -->
+ <string name="incoming_file_confirm_content">Erakommende Fichier acceptéieren?</string>
+ <!-- Label for a cancel button. -->
+ <string name="incoming_file_confirm_cancel">Refuséieren</string>
+ <!-- Label for a confirm button.-->
+ <string name="incoming_file_confirm_ok">Acceptéieren</string>
+ <!-- Label for timeout OK button.-->
+ <string name="incoming_file_confirm_timeout_ok">OK</string>
+ <!-- content for timeout-->
+ <string name="incoming_file_confirm_timeout_content">D\'Zäit ass beim Empfänke vun engem Fichier vum \u0022<xliff:g id="sender">%1$s</xliff:g>\u0022 ofgelaf</string>
+ <!-- Bluetooth File Transfer Acceptance Notification item -->
+ <!-- Inbound File Transfer Progress Notification item -->
+ <!-- label for the notification item of receiving file -->
+ <string name="notification_receiving">Bluetooth-Fräigab: <xliff:g id="file">%1$s</xliff:g> gëtt empfaangen</string>
+ <!-- label for the notification item of received file -->
+ <string name="notification_received">Bluetooth-Fräigab: <xliff:g id="file">%1$s</xliff:g> empfaangen</string>
+ <!-- label for the notification item of failed receiving file -->
+ <string name="notification_received_fail">Bluetooth-Fräigab: Fichier <xliff:g id="file">%1$s</xliff:g> net empfaangen</string>
+ <!-- Outbound File Transfer Progress Notification item -->
+ <!-- label for the notification item of sending file -->
+ <string name="notification_sending">Bluetooth-Fräigab: <xliff:g id="file">%1$s</xliff:g> gëtt geschéckt</string>
+ <!-- label for the notification item of sent file -->
+ <string name="notification_sent">Bluetooth-Fräigab: <xliff:g id="file">%1$s</xliff:g> geschéckt</string>
+ <!-- label for the notification item of sent file -status -->
+ <string name="notification_sent_complete">100 % ofgeschloss</string>
+ <!-- label for the notification item of failed sending file -->
+ <string name="notification_sent_fail">Bluetooth-Fräigab: Fichier <xliff:g id="file">%1$s</xliff:g> net geschéckt</string>
+ <!-- Bluetooth Download Progress Dialog -->
+ <!--Title -->
+ <string name="download_title">Fichiersiwwerdroung</string>
+ <!--Line 1 -->
+ <string name="download_line1">Vum: \u0022<xliff:g id="sender">%1$s</xliff:g>\u0022</string>
+ <!--Line 2 -->
+ <string name="download_line2">Fichier: <xliff:g id="file">%1$s</xliff:g></string>
+ <!--Line 3 -->
+ <string name="download_line3">Fichiersgréisst: <xliff:g id="size">%1$s</xliff:g></string>
+ <!--Line 4 -->
+ <string name="download_line5">Fichier gëtt empfaangen\u2026</string>
+ <!-- Label for a cancel button. -->
+ <string name="download_cancel">Stoppen</string>
+ <!-- Label for a hide button.-->
+ <string name="download_ok">Verstoppen</string>
+ <!--Line 1 -->
+ <string name="incoming_line1">Vum</string>
+ <!--Line 2 -->
+ <string name="incoming_line2">Fichiersnumm</string>
+ <!--Line 3 -->
+ <string name="incoming_line3">Gréisst</string>
+ <!-- Bluetooth failed Download Dialog -->
+ <!--Line 1 -->
+ <string name="download_fail_line1">Fichier net empfaangen</string>
+ <!--Line 2 -->
+ <string name="download_fail_line2">Fichier: <xliff:g id="file">%1$s</xliff:g></string>
+ <!--Line 3 -->
+ <string name="download_fail_line3">Grond: <xliff:g id="reason">%1$s</xliff:g></string>
+ <!-- Label for ok button.-->
+ <string name="download_fail_ok">OK</string>
+ <!-- Bluetooth Successful Download Dialog -->
+ <!--Line 4 in "Bluetooth Download Progress Dialog" -->
+ <string name="download_succ_line5">Fichier empfaangen</string>
+ <!-- Label for a OK button.-->
+ <string name="download_succ_ok">Opmaachen</string>
+ <!-- Bluetooth Upload Progress Dialog -->
+ <string name="upload_line1">Un: \u0022<xliff:g id="recipient">%1$s</xliff:g>\u0022</string>
+ <string name="upload_line3">Fichierstyp: <xliff:g id="type">%1$s</xliff:g> (<xliff:g id="size">%2$s</xliff:g>)</string>
+ <string name="upload_line5">Fichier gëtt geschéckt\u2026</string>
+ <!-- Bluetooth Successful Upload Progress Dialog -->
+ <!--Line 4 -->
+ <string name="upload_succ_line5">Fichier geschéckt</string>
+ <!-- Label for a confirm button.-->
+ <string name="upload_succ_ok">OK</string>
+ <!-- Bluetooth Failed Upload File Transfer Dialog -->
+ <string name="upload_fail_line1">De Fichier gouf net un den/d\' \u0022<xliff:g id="recipient">%1$s</xliff:g>\u0022 geschéckt.</string>
+ <string name="upload_fail_line1_2">Fichier: <xliff:g id="file">%1$s</xliff:g></string>
+ <!-- Label for a try again button.-->
+ <string name="upload_fail_ok">Probéier nees</string>
+ <!-- Label for a cancel button.-->
+ <string name="upload_fail_cancel">Zoumaachen</string>
+ <!-- Bluetooth error dialog -->
+ <string name="bt_error_btn_ok">OK</string>
+ <string name="unknown_file">Onbekannte Fichier</string>
+ <string name="unknown_file_desc">Et gëtt keng App déi mat dësem Typ vu Fichier kann ëmgoen.\n</string>
+ <string name="not_exist_file">Kee Fichier</string>
+ <string name="not_exist_file_desc">De Fichier existéiert net. \n</string>
+ <!-- Bluetooth Enabling progress dialog -->
+ <string name="enabling_progress_title">Waart w.e.g.\u2026</string>
+ <string name="enabling_progress_content">Bluetooth gëtt ugemaach\u2026</string>
+ <!-- Bluetooth Toast Message -->
+ <string name="bt_toast_1">De Fichier gëtt empfaangen. De Fortschrëtt gëtt am Notifikatiouns-Panneau ugewisen.</string>
+ <string name="bt_toast_2">De Fichier kann net empfaange ginn.</string>
+ <string name="bt_toast_3">D\'Empfänke vum Fichier vum \u0022<xliff:g id="sender">%1$s</xliff:g>\u0022 gouf gestoppt</string>
+ <string name="bt_toast_4">Fichier gëtt un den Apparat \u0022<xliff:g id="recipient">%1$s</xliff:g>\u0022 geschéckt</string>
+ <string name="bt_toast_5"><xliff:g id="number">%1$s</xliff:g> Fichiere ginn un den Apparat \u0022<xliff:g id="recipient">%2$s</xliff:g>\u0022 geschéckt</string>
+ <string name="bt_toast_6">D\'Schécke vum Fichier un den Apparat \u0022<xliff:g id="sender">%1$s</xliff:g>\u0022 gouf gestoppt</string>
+ <!-- Bluetooth System Messages [CHAR LIMIT=NONE] -->
+ <string name="bt_sm_2_1" product="nosdcard">Et ass net genuch Plaz um USB-Späicher fir de Fichier vum \u0022<xliff:g id="sender">%1$s</xliff:g>\u0022 ze späicheren</string>
+ <!-- Bluetooth System Messages -->
+ <string name="bt_sm_2_1" product="default">Et ass net genuch Späicherplaz op der SD-Kaart fir de Fichier vum \u0022<xliff:g id="sender">%1$s</xliff:g>\u0022 ze späicheren</string>
+ <string name="bt_sm_2_2">Néideg Plaz: <xliff:g id="size">%1$s</xliff:g></string>
+ <string name="ErrorTooManyRequests">Et ginn ze vill Ufroe beaarbecht. Probéier méi spéit nees.</string>
+ <!-- Bluetooth Transfer Failure Reason -->
+ <string name="status_pending">Iwwerdroung vu Fichieren nach net gestart.</string>
+ <string name="status_running">Iwwerdroung vu Fichiere leeft.</string>
+ <string name="status_success">Iwwerdroung vu Fichieren erfollegräich ofgeschloss.</string>
+ <string name="status_not_accept">Inhalt net ënnerstëtzt.</string>
+ <string name="status_forbidden">Iwwerdroung duerch den Zilapparat verbueden.</string>
+ <string name="status_canceled">Iwwerdroung duerch de Benotzer ofgebrach.</string>
+ <string name="status_file_error">Späicherproblem.</string>
+ <!-- Shown when USB storage cannot be found. [CHAR LIMIT=NONE] -->
+ <string name="status_no_sd_card" product="nosdcard">Keen USB-Späicher.</string>
+ <string name="status_no_sd_card" product="default">Keng SD-Kaart. Schléiss eng SD-Kaart un, fir déi transferéiert Fichieren ze späicheren.</string>
+ <string name="status_connection_error">Connectioun feelgeschloen.</string>
+ <string name="status_protocol_error">D\'Ufro kann net korrekt behandelt ginn.</string>
+ <string name="status_unknown_error">Onbekannte Feeler.</string>
+ <!-- Bluetooth OPP Live Folder -->
+ <string name="btopp_live_folder">Bluetooth empfaangen</string>
+ <!-- Bluetooth OPP Transfer History -->
+ <string name="download_success"> <xliff:g id="file_size">%1$s</xliff:g> vollstänneg empfaangen.</string>
+ <string name="upload_success"> <xliff:g id="file_size">%1$s</xliff:g> vollstänneg geschéckt.</string>
+ <string name="inbound_history_title">Erakommend Iwwerdroungen</string>
+ <string name="outbound_history_title">Erausgoend Iwwerdroungen</string>
+ <string name="no_transfers">Den Iwwerdroungshistorique ass eidel.</string>
+ <string name="transfer_clear_dlg_msg">All d\'Elementer ginn aus der Lëscht erausgeholl.</string>
+ <string name="outbound_noti_title">Bluetooth-Fräigab: Geschéckt Fichieren</string>
+ <string name="inbound_noti_title">Bluetooth-Fräigab: Emfaange Fichieren</string>
+ <plurals name="noti_caption_unsuccessful">
+ <item quantity="one"><xliff:g id="unsuccessful_number">%1$d</xliff:g> net erfollegräich.</item>
+ <item quantity="other"><xliff:g id="unsuccessful_number">%1$d</xliff:g> net erfollegräich.</item>
+ </plurals>
+ <plurals name="noti_caption_success">
+ <item quantity="one"><xliff:g id="successful_number">%1$d</xliff:g> erfollegräich, %2$s</item>
+ <item quantity="other"><xliff:g id="successful_number">%1$d</xliff:g> erfollegräich, %2$s</item>
+ </plurals>
+ <string name="transfer_menu_clear_all">Lëscht eidel maachen</string>
+ <string name="transfer_menu_open">Opmaachen</string>
+ <string name="transfer_menu_clear">Aus der Lëscht ewechhuelen</string>
+ <string name="transfer_clear_dlg_title">Eidel maachen</string>
+ <!-- Do not translate. file name used for sharing. -->
+ <!-- Used to run Bluetooth.apk in another process if needed -->
+ <!-- Do not translate. android:sharedUserId string of this application. -->
+ <!-- Do not translate. android:process of this application. -->
+ <string name="bluetooth_map_settings_save">Späicheren</string>
+ <string name="bluetooth_map_settings_cancel">Ofbriechen</string>
+ <string name="bluetooth_map_settings_app_icon">App-Symbol</string>
+</resources>
diff --git a/res/values-lb/strings_pbap.xml b/res/values-lb/strings_pbap.xml
new file mode 100644
index 0000000..46ecc28
--- /dev/null
+++ b/res/values-lb/strings_pbap.xml
@@ -0,0 +1,17 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--Generated by crowdin.com-->
+<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="pbap_session_key_dialog_title">Sëtzungsschlëssel fir %1$s aginn</string>
+ <string name="pbap_session_key_dialog_header">Bluetooth-Sëtzungsschlëssel néideg</string>
+ <string name="pbap_acceptance_timeout_message">Beim Acceptéiere vun der Connectioun mat %1$s ass d\'Zäit ofgelaf</string>
+ <string name="pbap_authentication_timeout_message">Beim Agi vum Sëtzungsschlëssel mat %1$s ass d\'Zäit ofgelaf</string>
+ <string name="auth_notif_ticker">Obex-Authentifizéierungs-Ufro</string>
+ <!-- Notification title when a Bluetooth device wants to pair with us -->
+ <string name="auth_notif_title">Sëtzungsschlëssel</string>
+ <!-- Notification message when a Bluetooth device wants to pair with us -->
+ <string name="auth_notif_message">Sëtzungsschlëssel fir %1$s aginn</string>
+ <string name="defaultname">Fräispriechanlag</string>
+ <string name="unknownName">Onbekannten Numm</string>
+ <string name="localPhoneName">Mäin Numm</string>
+ <string name="defaultnumber">000000</string>
+</resources>
diff --git a/res/values-lt/cm_strings.xml b/res/values-lt/cm_strings.xml
new file mode 100644
index 0000000..6881aa5
--- /dev/null
+++ b/res/values-lt/cm_strings.xml
@@ -0,0 +1,20 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--Generated by crowdin.com-->
+<!--
+ Copyright (C) 2015-2016 The CyanogenMod 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.
+-->
+<resources>
+ <string name="upload_fail_waiting">Nepavyko išsiųsti failo, bandoma dar kartą\u2026</string>
+</resources>
diff --git a/res/values-lv/cm_strings.xml b/res/values-lv/cm_strings.xml
new file mode 100644
index 0000000..ee7147b
--- /dev/null
+++ b/res/values-lv/cm_strings.xml
@@ -0,0 +1,20 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--Generated by crowdin.com-->
+<!--
+ Copyright (C) 2015-2016 The CyanogenMod 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.
+-->
+<resources>
+ <string name="upload_fail_waiting">Neizdevās nosūtīt failu, atkārtoju\u2026</string>
+</resources>
diff --git a/res/values-ml-rIN/cm_strings.xml b/res/values-ml-rIN/cm_strings.xml
new file mode 100644
index 0000000..a34e1d7
--- /dev/null
+++ b/res/values-ml-rIN/cm_strings.xml
@@ -0,0 +1,20 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--Generated by crowdin.com-->
+<!--
+ Copyright (C) 2015-2016 The CyanogenMod 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.
+-->
+<resources>
+ <string name="upload_fail_waiting">ഫയൽ അയക്കാൻ കഴിയുന്നില്ല, വീണ്ടും ശ്രമിക്കുന്നു\u2026</string>
+</resources>
diff --git a/res/values-mr-rIN/cm_strings.xml b/res/values-mr-rIN/cm_strings.xml
new file mode 100644
index 0000000..2dfdd29
--- /dev/null
+++ b/res/values-mr-rIN/cm_strings.xml
@@ -0,0 +1,20 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--Generated by crowdin.com-->
+<!--
+ Copyright (C) 2015-2016 The CyanogenMod 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.
+-->
+<resources>
+ <string name="upload_fail_waiting">फाइल पाठवण्यात अक्षम, पुन्हा प्रयत्न करत आहे\u2026</string>
+</resources>
diff --git a/res/values-nb/cm_strings.xml b/res/values-nb/cm_strings.xml
new file mode 100644
index 0000000..db24eaa
--- /dev/null
+++ b/res/values-nb/cm_strings.xml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--Generated by crowdin.com-->
+<!--
+ Copyright (C) 2015-2016 The CyanogenMod 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.
+-->
+<resources>
+ <string name="incoming_file_toast_msg">Innkommende fil fra en annen enhet. Bekreft at du vil motta denne filen.</string>
+ <string name="upload_fail_waiting">Kan ikke sende filen, prøver på nytt\u2026</string>
+</resources>
diff --git a/res/values-nl/cm_strings.xml b/res/values-nl/cm_strings.xml
new file mode 100644
index 0000000..321a741
--- /dev/null
+++ b/res/values-nl/cm_strings.xml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--Generated by crowdin.com-->
+<!--
+ Copyright (C) 2015-2016 The CyanogenMod 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.
+-->
+<resources>
+ <string name="incoming_file_toast_msg">Inkomend bestand vanaf een ander apparaat. Bevestig dat u dit bestand wilt ontvangen.</string>
+ <string name="upload_fail_waiting">Kan bestand niet verzenden, opnieuw proberen\u2026</string>
+</resources>
diff --git a/res/values-oc-rFR/strings.xml b/res/values-oc-rFR/strings.xml
new file mode 100644
index 0000000..906d653
--- /dev/null
+++ b/res/values-oc-rFR/strings.xml
@@ -0,0 +1,99 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--Generated by crowdin.com-->
+<!-- Copyright (C) 2007 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.
+-->
+<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <!--
+ This is the short description of a permission associated with the
+ Bluetooth Share Manager. It is displayed as part of the description of
+ any application that was granted that permission. This specific
+ permission controls access to the Bluetooth Share Manager by
+ applications that initiate Bluetooth share.
+ -->
+ <!--
+ This is the long description of a permission associated with the
+ Android Download Manager. It is displayed as part of the description
+ of any application that was granted that permission. This specific
+ permission controls access to the Download Manager by applications
+ that initiate downloads.
+ -->
+ <!-- string showed on "Share picutre via" dialog -->
+ <!-- string for "unknown device" -->
+ <!-- string for "unknown" phone number" -->
+ <!-- string for "the title of airplane mode error" -->
+ <!-- string for "error message in airplane mode" -->
+ <!-- Activate Bluetooth Confirmation Dialog -->
+ <!--Title -->
+ <!--Line 1 -->
+ <!--Line 2 -->
+ <!-- Label for a cancel button. -->
+ <!-- Label for a confirm button.-->
+ <!-- Bluetooth File Transfer Acceptance Dialog -->
+ <!--Title -->
+ <!--content -->
+ <!-- Label for a cancel button. -->
+ <!-- Label for a confirm button.-->
+ <!-- Label for timeout OK button.-->
+ <!-- content for timeout-->
+ <!-- Bluetooth File Transfer Acceptance Notification item -->
+ <!-- Inbound File Transfer Progress Notification item -->
+ <!-- label for the notification item of receiving file -->
+ <!-- label for the notification item of received file -->
+ <!-- label for the notification item of failed receiving file -->
+ <!-- Outbound File Transfer Progress Notification item -->
+ <!-- label for the notification item of sending file -->
+ <!-- label for the notification item of sent file -->
+ <!-- label for the notification item of sent file -status -->
+ <!-- label for the notification item of failed sending file -->
+ <!-- Bluetooth Download Progress Dialog -->
+ <!--Title -->
+ <!--Line 1 -->
+ <!--Line 2 -->
+ <!--Line 3 -->
+ <!--Line 4 -->
+ <!-- Label for a cancel button. -->
+ <!-- Label for a hide button.-->
+ <!--Line 1 -->
+ <!--Line 2 -->
+ <!--Line 3 -->
+ <!-- Bluetooth failed Download Dialog -->
+ <!--Line 1 -->
+ <!--Line 2 -->
+ <!--Line 3 -->
+ <!-- Label for ok button.-->
+ <!-- Bluetooth Successful Download Dialog -->
+ <!--Line 4 in "Bluetooth Download Progress Dialog" -->
+ <!-- Label for a OK button.-->
+ <!-- Bluetooth Upload Progress Dialog -->
+ <!-- Bluetooth Successful Upload Progress Dialog -->
+ <!--Line 4 -->
+ <!-- Label for a confirm button.-->
+ <!-- Bluetooth Failed Upload File Transfer Dialog -->
+ <!-- Label for a try again button.-->
+ <!-- Label for a cancel button.-->
+ <!-- Bluetooth error dialog -->
+ <!-- Bluetooth Enabling progress dialog -->
+ <!-- Bluetooth Toast Message -->
+ <!-- Bluetooth System Messages [CHAR LIMIT=NONE] -->
+ <!-- Bluetooth System Messages -->
+ <!-- Bluetooth Transfer Failure Reason -->
+ <!-- Shown when USB storage cannot be found. [CHAR LIMIT=NONE] -->
+ <!-- Bluetooth OPP Live Folder -->
+ <!-- Bluetooth OPP Transfer History -->
+ <!-- Do not translate. file name used for sharing. -->
+ <!-- Used to run Bluetooth.apk in another process if needed -->
+ <!-- Do not translate. android:sharedUserId string of this application. -->
+ <!-- Do not translate. android:process of this application. -->
+</resources>
diff --git a/res/values-oc-rFR/strings_pbap.xml b/res/values-oc-rFR/strings_pbap.xml
new file mode 100644
index 0000000..55f5b83
--- /dev/null
+++ b/res/values-oc-rFR/strings_pbap.xml
@@ -0,0 +1,6 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--Generated by crowdin.com-->
+<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <!-- Notification title when a Bluetooth device wants to pair with us -->
+ <!-- Notification message when a Bluetooth device wants to pair with us -->
+</resources>
diff --git a/res/values-or-rIN/cm_strings.xml b/res/values-or-rIN/cm_strings.xml
new file mode 100644
index 0000000..d093caa
--- /dev/null
+++ b/res/values-or-rIN/cm_strings.xml
@@ -0,0 +1,20 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--Generated by crowdin.com-->
+<!--
+ Copyright (C) 2015-2016 The CyanogenMod 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.
+-->
+<resources>
+ <string name="upload_fail_waiting">ଫାଇଲ୍ ପଠାଇବା ପାଇଁ ଅଷମ ହେଉଛୁ, ପୁନର୍ବାର ଚେଷ୍ଟା କରୁଛି\u2026</string>
+</resources>
diff --git a/res/values-or-rIN/strings.xml b/res/values-or-rIN/strings.xml
new file mode 100644
index 0000000..906d653
--- /dev/null
+++ b/res/values-or-rIN/strings.xml
@@ -0,0 +1,99 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--Generated by crowdin.com-->
+<!-- Copyright (C) 2007 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.
+-->
+<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <!--
+ This is the short description of a permission associated with the
+ Bluetooth Share Manager. It is displayed as part of the description of
+ any application that was granted that permission. This specific
+ permission controls access to the Bluetooth Share Manager by
+ applications that initiate Bluetooth share.
+ -->
+ <!--
+ This is the long description of a permission associated with the
+ Android Download Manager. It is displayed as part of the description
+ of any application that was granted that permission. This specific
+ permission controls access to the Download Manager by applications
+ that initiate downloads.
+ -->
+ <!-- string showed on "Share picutre via" dialog -->
+ <!-- string for "unknown device" -->
+ <!-- string for "unknown" phone number" -->
+ <!-- string for "the title of airplane mode error" -->
+ <!-- string for "error message in airplane mode" -->
+ <!-- Activate Bluetooth Confirmation Dialog -->
+ <!--Title -->
+ <!--Line 1 -->
+ <!--Line 2 -->
+ <!-- Label for a cancel button. -->
+ <!-- Label for a confirm button.-->
+ <!-- Bluetooth File Transfer Acceptance Dialog -->
+ <!--Title -->
+ <!--content -->
+ <!-- Label for a cancel button. -->
+ <!-- Label for a confirm button.-->
+ <!-- Label for timeout OK button.-->
+ <!-- content for timeout-->
+ <!-- Bluetooth File Transfer Acceptance Notification item -->
+ <!-- Inbound File Transfer Progress Notification item -->
+ <!-- label for the notification item of receiving file -->
+ <!-- label for the notification item of received file -->
+ <!-- label for the notification item of failed receiving file -->
+ <!-- Outbound File Transfer Progress Notification item -->
+ <!-- label for the notification item of sending file -->
+ <!-- label for the notification item of sent file -->
+ <!-- label for the notification item of sent file -status -->
+ <!-- label for the notification item of failed sending file -->
+ <!-- Bluetooth Download Progress Dialog -->
+ <!--Title -->
+ <!--Line 1 -->
+ <!--Line 2 -->
+ <!--Line 3 -->
+ <!--Line 4 -->
+ <!-- Label for a cancel button. -->
+ <!-- Label for a hide button.-->
+ <!--Line 1 -->
+ <!--Line 2 -->
+ <!--Line 3 -->
+ <!-- Bluetooth failed Download Dialog -->
+ <!--Line 1 -->
+ <!--Line 2 -->
+ <!--Line 3 -->
+ <!-- Label for ok button.-->
+ <!-- Bluetooth Successful Download Dialog -->
+ <!--Line 4 in "Bluetooth Download Progress Dialog" -->
+ <!-- Label for a OK button.-->
+ <!-- Bluetooth Upload Progress Dialog -->
+ <!-- Bluetooth Successful Upload Progress Dialog -->
+ <!--Line 4 -->
+ <!-- Label for a confirm button.-->
+ <!-- Bluetooth Failed Upload File Transfer Dialog -->
+ <!-- Label for a try again button.-->
+ <!-- Label for a cancel button.-->
+ <!-- Bluetooth error dialog -->
+ <!-- Bluetooth Enabling progress dialog -->
+ <!-- Bluetooth Toast Message -->
+ <!-- Bluetooth System Messages [CHAR LIMIT=NONE] -->
+ <!-- Bluetooth System Messages -->
+ <!-- Bluetooth Transfer Failure Reason -->
+ <!-- Shown when USB storage cannot be found. [CHAR LIMIT=NONE] -->
+ <!-- Bluetooth OPP Live Folder -->
+ <!-- Bluetooth OPP Transfer History -->
+ <!-- Do not translate. file name used for sharing. -->
+ <!-- Used to run Bluetooth.apk in another process if needed -->
+ <!-- Do not translate. android:sharedUserId string of this application. -->
+ <!-- Do not translate. android:process of this application. -->
+</resources>
diff --git a/res/values-or-rIN/strings_pbap.xml b/res/values-or-rIN/strings_pbap.xml
new file mode 100644
index 0000000..55f5b83
--- /dev/null
+++ b/res/values-or-rIN/strings_pbap.xml
@@ -0,0 +1,6 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--Generated by crowdin.com-->
+<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <!-- Notification title when a Bluetooth device wants to pair with us -->
+ <!-- Notification message when a Bluetooth device wants to pair with us -->
+</resources>
diff --git a/res/values-pl/cm_strings.xml b/res/values-pl/cm_strings.xml
new file mode 100644
index 0000000..15276e7
--- /dev/null
+++ b/res/values-pl/cm_strings.xml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--Generated by crowdin.com-->
+<!--
+ Copyright (C) 2015-2016 The CyanogenMod 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.
+-->
+<resources>
+ <string name="incoming_file_toast_msg">Przychodzący plik z innego urządzenia. Potwierdź, że chcesz otrzymać ten plik.</string>
+ <string name="upload_fail_waiting">Nie można wysłać pliku, ponawianie\u2026</string>
+</resources>
diff --git a/res/values-pt-rBR/cm_strings.xml b/res/values-pt-rBR/cm_strings.xml
new file mode 100644
index 0000000..d67b8b9
--- /dev/null
+++ b/res/values-pt-rBR/cm_strings.xml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--Generated by crowdin.com-->
+<!--
+ Copyright (C) 2015-2016 The CyanogenMod 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.
+-->
+<resources>
+ <string name="incoming_file_toast_msg">Recebendo arquivo de outro dispositivo. Confirme que você deseja receber este arquivo.</string>
+ <string name="upload_fail_waiting">Impossível enviar arquivo, tentando novamente\u2026</string>
+</resources>
diff --git a/res/values-pt-rPT/cm_strings.xml b/res/values-pt-rPT/cm_strings.xml
new file mode 100644
index 0000000..f321623
--- /dev/null
+++ b/res/values-pt-rPT/cm_strings.xml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--Generated by crowdin.com-->
+<!--
+ Copyright (C) 2015-2016 The CyanogenMod 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.
+-->
+<resources>
+ <string name="incoming_file_toast_msg">A receber ficheiro de outro dispositivo. Confirme que quer receber este ficheiro.</string>
+ <string name="upload_fail_waiting">Não foi possível enviar o ficheiro. A tentar novamente\u2026</string>
+</resources>
diff --git a/res/values-ro/cm_strings.xml b/res/values-ro/cm_strings.xml
new file mode 100644
index 0000000..b30416b
--- /dev/null
+++ b/res/values-ro/cm_strings.xml
@@ -0,0 +1,20 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--Generated by crowdin.com-->
+<!--
+ Copyright (C) 2015-2016 The CyanogenMod 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.
+-->
+<resources>
+ <string name="upload_fail_waiting">Imposibil de transmis fișierul, se reîncearcă\u2026</string>
+</resources>
diff --git a/res/values-ru/cm_strings.xml b/res/values-ru/cm_strings.xml
new file mode 100644
index 0000000..4948da0
--- /dev/null
+++ b/res/values-ru/cm_strings.xml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--Generated by crowdin.com-->
+<!--
+ Copyright (C) 2015-2016 The CyanogenMod 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.
+-->
+<resources>
+ <string name="incoming_file_toast_msg">Входящий файл с другого устройства. Подтвердите, что вы хотите получить этот файл.</string>
+ <string name="upload_fail_waiting">Не удалось отправить файл. Повторная попытка\u2026</string>
+</resources>
diff --git a/res/values-sk/cm_strings.xml b/res/values-sk/cm_strings.xml
new file mode 100644
index 0000000..88d96f3
--- /dev/null
+++ b/res/values-sk/cm_strings.xml
@@ -0,0 +1,20 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--Generated by crowdin.com-->
+<!--
+ Copyright (C) 2015-2016 The CyanogenMod 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.
+-->
+<resources>
+ <string name="upload_fail_waiting">Nepodarilo sa poslať súbor, opakovanie\u2026</string>
+</resources>
diff --git a/res/values-sl/cm_strings.xml b/res/values-sl/cm_strings.xml
new file mode 100644
index 0000000..ba5cb7b
--- /dev/null
+++ b/res/values-sl/cm_strings.xml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--Generated by crowdin.com-->
+<!--
+ Copyright (C) 2015-2016 The CyanogenMod 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.
+-->
+<resources>
+ <string name="incoming_file_toast_msg">Prihaja datoteka iz druge naprave. Potrdite, če jo želite prejeti.</string>
+ <string name="upload_fail_waiting">Datoteke ni mogoče poslati, ponovno poskušanje \u2026</string>
+</resources>
diff --git a/res/values-sr/cm_strings.xml b/res/values-sr/cm_strings.xml
new file mode 100644
index 0000000..0ae5bba
--- /dev/null
+++ b/res/values-sr/cm_strings.xml
@@ -0,0 +1,20 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--Generated by crowdin.com-->
+<!--
+ Copyright (C) 2015-2016 The CyanogenMod 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.
+-->
+<resources>
+ <string name="upload_fail_waiting">Није могуће послати датотеку, понављам\u2026</string>
+</resources>
diff --git a/res/values-sv/cm_strings.xml b/res/values-sv/cm_strings.xml
new file mode 100644
index 0000000..0164301
--- /dev/null
+++ b/res/values-sv/cm_strings.xml
@@ -0,0 +1,20 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--Generated by crowdin.com-->
+<!--
+ Copyright (C) 2015-2016 The CyanogenMod 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.
+-->
+<resources>
+ <string name="upload_fail_waiting">Kunde inte skicka filen, försöker igen\u2026</string>
+</resources>
diff --git a/res/values-ta-rIN/cm_strings.xml b/res/values-ta-rIN/cm_strings.xml
new file mode 100644
index 0000000..65bc2ef
--- /dev/null
+++ b/res/values-ta-rIN/cm_strings.xml
@@ -0,0 +1,20 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--Generated by crowdin.com-->
+<!--
+ Copyright (C) 2015-2016 The CyanogenMod 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.
+-->
+<resources>
+ <string name="upload_fail_waiting">கோப்பை அனுப்ப முடியவில்லை, மறுமுயற்சிசெய்கிறது\u2026</string>
+</resources>
diff --git a/res/values-te-rIN/cm_strings.xml b/res/values-te-rIN/cm_strings.xml
new file mode 100644
index 0000000..0f20452
--- /dev/null
+++ b/res/values-te-rIN/cm_strings.xml
@@ -0,0 +1,20 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--Generated by crowdin.com-->
+<!--
+ Copyright (C) 2015-2016 The CyanogenMod 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.
+-->
+<resources>
+ <string name="upload_fail_waiting">ఫైలును పంపడం కుదరలేదు, తిరిగి ప్రయత్నించబడుతోంది\u2026</string>
+</resources>
diff --git a/res/values-th/cm_strings.xml b/res/values-th/cm_strings.xml
new file mode 100644
index 0000000..02cb44a
--- /dev/null
+++ b/res/values-th/cm_strings.xml
@@ -0,0 +1,20 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--Generated by crowdin.com-->
+<!--
+ Copyright (C) 2015-2016 The CyanogenMod 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.
+-->
+<resources>
+ <string name="upload_fail_waiting">ไม่สามารถส่งไฟล์ได้ กำลังลองอีกครั้ง\u2026</string>
+</resources>
diff --git a/res/values-tr/cm_strings.xml b/res/values-tr/cm_strings.xml
new file mode 100644
index 0000000..e6f18af
--- /dev/null
+++ b/res/values-tr/cm_strings.xml
@@ -0,0 +1,20 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--Generated by crowdin.com-->
+<!--
+ Copyright (C) 2015-2016 The CyanogenMod 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.
+-->
+<resources>
+ <string name="upload_fail_waiting">Dosya gönderilemedi, yeniden deneniyor\u2026</string>
+</resources>
diff --git a/res/values-ug/cm_strings.xml b/res/values-ug/cm_strings.xml
new file mode 100644
index 0000000..9d1b2ce
--- /dev/null
+++ b/res/values-ug/cm_strings.xml
@@ -0,0 +1,20 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--Generated by crowdin.com-->
+<!--
+ Copyright (C) 2015-2016 The CyanogenMod 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.
+-->
+<resources>
+ <string name="upload_fail_waiting">ھۆججەتنى يوللىيالمىدى، قايتا سىناۋاتىدۇ\u2026</string>
+</resources>
diff --git a/res/values-ug/strings.xml b/res/values-ug/strings.xml
new file mode 100644
index 0000000..63f8e5e
--- /dev/null
+++ b/res/values-ug/strings.xml
@@ -0,0 +1,189 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--Generated by crowdin.com-->
+<!-- Copyright (C) 2007 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.
+-->
+<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <!--
+ This is the short description of a permission associated with the
+ Bluetooth Share Manager. It is displayed as part of the description of
+ any application that was granted that permission. This specific
+ permission controls access to the Bluetooth Share Manager by
+ applications that initiate Bluetooth share.
+ -->
+ <string name="permlab_bluetoothShareManager">چۈشۈرۈش باشقۇرغۇچنى زىيارەت.</string>
+ <!--
+ This is the long description of a permission associated with the
+ Android Download Manager. It is displayed as part of the description
+ of any application that was granted that permission. This specific
+ permission controls access to the Download Manager by applications
+ that initiate downloads.
+ -->
+ <string name="permdesc_bluetoothShareManager">ئەپنىڭ كۆكچىش ھەمبەھىر باشقۇرغۇچنى زىيارەت قىلىشقا يول قويىدۇ ھەمدە شۇ باشقۇرغۇچنى ئىشلىتىپ ھۆججەت يوللايدۇ.</string>
+ <string name="permlab_bluetoothWhitelist">ئاق تىزىمدىكى كۆكچىش ئۈسكۈنە زىيارىتى.</string>
+ <string name="permdesc_bluetoothWhitelist">كۆكچىش ئۈسكۈنىسىنى زىيارەت ھوقۇقى ئاق تىزىملىكىگە كىرگۈزۈشكە يول قويىدۇ، شۇ ئارقىلىق ئىشلەتكۈچىنىڭ جەزملىشى بولمىغان ئەھۋالدا ھۆججەتنى بۇ ئۈسكۈنىگە يوللايدۇ.</string>
+ <!-- string showed on "Share picutre via" dialog -->
+ <string name="bt_share_picker_label">كۆكچىش</string>
+ <!-- string for "unknown device" -->
+ <string name="unknown_device">يوچۇن ئۈسكۈنە</string>
+ <!-- string for "unknown" phone number" -->
+ <string name="unknownNumber">يوچۇن</string>
+ <!-- string for "the title of airplane mode error" -->
+ <string name="airplane_error_title">ئايروپىلان ھالىتى</string>
+ <!-- string for "error message in airplane mode" -->
+ <string name="airplane_error_msg">ئايروپىلان ھالىتىدە كۆكچىش ئىشلىتەلمەيسىز.</string>
+ <!-- Activate Bluetooth Confirmation Dialog -->
+ <!--Title -->
+ <!--Line 1 -->
+ <string name="bt_enable_line1">كۆكچىش مۇلازىمىتى ئىشلىتىشتە ئالدى بىلەن كۆكچىشنى ئېچىشىڭىز لازىم.</string>
+ <!--Line 2 -->
+ <string name="bt_enable_line2">ھازىر كۆكچىشنى ئاچامدۇ؟</string>
+ <!-- Label for a cancel button. -->
+ <string name="bt_enable_cancel">ۋاز كەچ</string>
+ <!-- Label for a confirm button.-->
+ <string name="bt_enable_ok">ئاچ</string>
+ <!-- Bluetooth File Transfer Acceptance Dialog -->
+ <!--Title -->
+ <string name="incoming_file_confirm_title">ھۆججەت يوللاش</string>
+ <!--content -->
+ <!-- Label for a cancel button. -->
+ <string name="incoming_file_confirm_cancel">قوشۇلما</string>
+ <!-- Label for a confirm button.-->
+ <string name="incoming_file_confirm_ok">قوشۇل</string>
+ <!-- Label for timeout OK button.-->
+ <string name="incoming_file_confirm_timeout_ok">جەزملە</string>
+ <!-- content for timeout-->
+ <string name="incoming_file_confirm_timeout_content">\"<xliff:g id="SENDER">%1$s</xliff:g>\" دىن كەلگەن ھۆججەتنى قوبۇل قىلىۋاتقاندا ۋاقىت ھالقىدى</string>
+ <!-- Bluetooth File Transfer Acceptance Notification item -->
+ <!-- Inbound File Transfer Progress Notification item -->
+ <!-- label for the notification item of receiving file -->
+ <string name="notification_receiving">كۆكچىش ھەمبەھىر: <xliff:g id="FILE">%1$s</xliff:g> قوبۇللاۋاتىدۇ</string>
+ <!-- label for the notification item of received file -->
+ <string name="notification_received">كۆكچىش ھەمبەھىر: <xliff:g id="FILE">%1$s</xliff:g> قوبۇللىدى</string>
+ <!-- label for the notification item of failed receiving file -->
+ <string name="notification_received_fail">كۆكچىش ھەمبەھىر: <xliff:g id="FILE">%1$s</xliff:g> ھۆججەتنى قوبۇللىمىدى</string>
+ <!-- Outbound File Transfer Progress Notification item -->
+ <!-- label for the notification item of sending file -->
+ <string name="notification_sending">كۆكچىش ھەمبەھىر: <xliff:g id="FILE">%1$s</xliff:g> يوللاۋاتىدۇ</string>
+ <!-- label for the notification item of sent file -->
+ <string name="notification_sent">كۆكچىش ھەمبەھىر: <xliff:g id="FILE">%1$s</xliff:g> يوللاندى</string>
+ <!-- label for the notification item of sent file -status -->
+ <string name="notification_sent_complete">100% تامام</string>
+ <!-- label for the notification item of failed sending file -->
+ <string name="notification_sent_fail">كۆكچىش ھەمبەھىر: <xliff:g id="FILE">%1$s</xliff:g> ھۆججەت يوللانمىدى</string>
+ <!-- Bluetooth Download Progress Dialog -->
+ <!--Title -->
+ <string name="download_title">ھۆججەت يوللاش</string>
+ <!--Line 1 -->
+ <string name="download_line1">ئورنى: \"<xliff:g id="SENDER">%1$s</xliff:g></string>
+ <!--Line 2 -->
+ <string name="download_line2">ھۆججەت: <xliff:g id="FILE">%1$s</xliff:g></string>
+ <!--Line 3 -->
+ <string name="download_line3">ھۆججەت چوڭلۇقى: <xliff:g id="SIZE">%1$s</xliff:g></string>
+ <!--Line 4 -->
+ <string name="download_line5">ھۆججەت قوبۇللاۋاتىدۇ…</string>
+ <!-- Label for a cancel button. -->
+ <string name="download_cancel">توختا</string>
+ <!-- Label for a hide button.-->
+ <string name="download_ok">يوشۇر</string>
+ <!--Line 1 -->
+ <!--Line 2 -->
+ <!--Line 3 -->
+ <!-- Bluetooth failed Download Dialog -->
+ <!--Line 1 -->
+ <string name="download_fail_line1">ھۆججەت قوبۇللانمىدى</string>
+ <!--Line 2 -->
+ <string name="download_fail_line2">ھۆججەت: <xliff:g id="FILE">%1$s</xliff:g></string>
+ <!--Line 3 -->
+ <string name="download_fail_line3">سەۋەب: <xliff:g id="REASON">%1$s</xliff:g></string>
+ <!-- Label for ok button.-->
+ <string name="download_fail_ok">جەزملە</string>
+ <!-- Bluetooth Successful Download Dialog -->
+ <!--Line 4 in "Bluetooth Download Progress Dialog" -->
+ <string name="download_succ_line5">ھۆججەت قوبۇللاندى</string>
+ <!-- Label for a OK button.-->
+ <string name="download_succ_ok">ئاچ</string>
+ <!-- Bluetooth Upload Progress Dialog -->
+ <string name="upload_line1">ئورنى: \"<xliff:g id="RECIPIENT">%1$s</xliff:g>\"</string>
+ <string name="upload_line3">ھۆججەت تىپى: <xliff:g id="TYPE">%1$s</xliff:g> (<xliff:g id="SIZE">%2$s</xliff:g>)</string>
+ <string name="upload_line5">ھۆججەت يوللاۋاتىدۇ…</string>
+ <!-- Bluetooth Successful Upload Progress Dialog -->
+ <!--Line 4 -->
+ <string name="upload_succ_line5">ھۆججەت يوللاندى</string>
+ <!-- Label for a confirm button.-->
+ <string name="upload_succ_ok">جەزملە</string>
+ <!-- Bluetooth Failed Upload File Transfer Dialog -->
+ <string name="upload_fail_line1">بۇ ھۆججەتنى \"<xliff:g id="RECIPIENT">%1$s</xliff:g>\" غا يوللىيالمىدى.</string>
+ <string name="upload_fail_line1_2">ھۆججەت: <xliff:g id="FILE">%1$s</xliff:g></string>
+ <!-- Label for a try again button.-->
+ <string name="upload_fail_ok">قايتا سىنا</string>
+ <!-- Label for a cancel button.-->
+ <string name="upload_fail_cancel">ياپ</string>
+ <!-- Bluetooth error dialog -->
+ <string name="bt_error_btn_ok">جەزملە</string>
+ <string name="unknown_file">يوچۇن ھۆججەت</string>
+ <string name="unknown_file_desc">بۇ تىپتىكى ھۆججەتنى بىر تەرەپ قىلىدىغان ئەپ تېپىلمىدى. \n</string>
+ <string name="not_exist_file"> ھۆججەت يوق</string>
+ <string name="not_exist_file_desc">ھۆججەت مەۋجۇت ئەمەس. \n</string>
+ <!-- Bluetooth Enabling progress dialog -->
+ <string name="enabling_progress_title">سەل كۈتۈڭ…</string>
+ <string name="enabling_progress_content">كۆكچىشنى ئېچىۋاتىدۇ…</string>
+ <!-- Bluetooth Toast Message -->
+ <string name="bt_toast_1">ھۆججەت قوبۇل قىلماقچى. ئۇقتۇرۇش تاختىسىدىن جەريانىنى تەكشۈرۈڭ.</string>
+ <string name="bt_toast_2">بۇ ھۆججەتنى قوبۇل قىلالمايدۇ.</string>
+ <string name="bt_toast_3">\"<xliff:g id="SENDER">%1$s</xliff:g>\" دىن ھۆججەت قوبۇللاشنى توختاتتى</string>
+ <string name="bt_toast_4">\"<xliff:g id="RECIPIENT">%1$s</xliff:g>\" غا ھۆججەت يوللاۋاتىدۇ</string>
+ <string name="bt_toast_5"><xliff:g id="NUMBER">%1$s</xliff:g> ھۆججەتنى "<xliff:g id="RECIPIENT">%2$s</xliff:g>\" غا يوللاۋاتىدۇ</string>
+ <string name="bt_toast_6">\"<xliff:g id="RECIPIENT">%1$s</xliff:g>\" غا ھۆججەت يوللاش توختىدى</string>
+ <!-- Bluetooth System Messages [CHAR LIMIT=NONE] -->
+ <string name="bt_sm_2_1" product="nosdcard">بۇ USB ساقلىغۇچتا \"<xliff:g id="SENDER">%1$s</xliff:g>\" دىن كەلگەن ھۆججەتنى ساقلاشقا يېتەرلىك بوشلۇق يوق</string>
+ <!-- Bluetooth System Messages -->
+ <string name="bt_sm_2_1" product="default">SD كارتادا \"<xliff:g id="SENDER">%1$s</xliff:g>\" دىن كەلگەن ھۆججەتنى ساقلاشقا يېتەرلىك بوشلۇق يوق</string>
+ <string name="bt_sm_2_2">كېتەرلىك بوشلۇق: <xliff:g id="SIZE">%1$s</xliff:g></string>
+ <string name="ErrorTooManyRequests">بىر تەرەپ قىلىۋاتقان ئىلتىماس بەك كۆپ. سەل تۇرۇپ قايتا سىناڭ.</string>
+ <!-- Bluetooth Transfer Failure Reason -->
+ <string name="status_pending">ھۆججەت يوللاش باشلانمىدى</string>
+ <string name="status_running">ھۆججەت يوللىنىۋاتىدۇ.</string>
+ <string name="status_success">ھۆججەت يوللاش مۇۋەپپەقىيەتلىك تاماملاندى.</string>
+ <string name="status_not_accept">مەزمۇننى قوللىمايدۇ.</string>
+ <string name="status_forbidden">نىشان ئۈسكۈنە يوللاشنى چەكلەيدۇ.</string>
+ <string name="status_canceled">ئىشلەتكۈچى يوللاشتىن ۋاز كەچتى.</string>
+ <string name="status_file_error">ساقلاش مەسىلىسى</string>
+ <!-- Shown when USB storage cannot be found. [CHAR LIMIT=NONE] -->
+ <string name="status_no_sd_card" product="nosdcard">USB ساقلىغۇچ يوق.</string>
+ <string name="status_no_sd_card" product="default">SD كارتا يوق. يوللىغان ھۆججەتنى ساقلايدىغان SD كارتىنى قىستۇرۇڭ.</string>
+ <string name="status_connection_error">مۇۋەپپەقىيەتلىك باغلىنالمىدى.</string>
+ <string name="status_protocol_error">ئىلتىماسنى توغرا بىر تەرەپ قىلالمايدۇ.</string>
+ <string name="status_unknown_error">يوچۇن خاتالىق.</string>
+ <!-- Bluetooth OPP Live Folder -->
+ <string name="btopp_live_folder">كۆكچىش قوبۇللىدى</string>
+ <!-- Bluetooth OPP Transfer History -->
+ <string name="download_success"><xliff:g id="FILE_SIZE">%1$s</xliff:g> قوبۇللاش تاماملاندى.</string>
+ <string name="upload_success"><xliff:g id="FILE_SIZE">%1$s</xliff:g> يوللاش تاماملاندى.</string>
+ <string name="inbound_history_title">يوللاشنىڭ كىرگەن ئىزلىرى</string>
+ <string name="outbound_history_title">يوللاشنىڭ چىققان ئىزلىرى</string>
+ <string name="no_transfers">توركۆرگۈ تارىخ خاتىرىسى بوش.</string>
+ <string name="transfer_clear_dlg_msg">ھەممە تۈرلەر تىزىملىكتىن ئۆچۈرۈلىدۇ.</string>
+ <string name="outbound_noti_title">كۆكچىش ھەمبەھىر: يوللانغان ھۆججەتلەر</string>
+ <string name="inbound_noti_title">كۆكچىش ھەمبەھىر: قوبۇللىغان ھۆججەتلەر</string>
+ <string name="transfer_menu_clear_all">تىزىملىكنى تازىلا</string>
+ <string name="transfer_menu_open">ئاچ</string>
+ <string name="transfer_menu_clear">تىزىملىكتىن تازىلا</string>
+ <string name="transfer_clear_dlg_title">تازىلا</string>
+ <!-- Do not translate. file name used for sharing. -->
+ <!-- Used to run Bluetooth.apk in another process if needed -->
+ <!-- Do not translate. android:sharedUserId string of this application. -->
+ <!-- Do not translate. android:process of this application. -->
+ <string name="bluetooth_map_settings_cancel">ۋاز كەچ</string>
+</resources>
diff --git a/res/values-ug/strings_pbap.xml b/res/values-ug/strings_pbap.xml
new file mode 100644
index 0000000..f211002
--- /dev/null
+++ b/res/values-ug/strings_pbap.xml
@@ -0,0 +1,17 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--Generated by crowdin.com-->
+<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="pbap_session_key_dialog_title">%1$s سۆزلىشىش ئاچقۇچىنى كىرگۈزۈڭ</string>
+ <string name="pbap_session_key_dialog_header">كۆكچىش سۆزلىشىش ئاچقۇچى زۆرۈر</string>
+ <string name="pbap_acceptance_timeout_message">%1$s نىڭ بىلەن بولغان باغلىنىشقا قوشۇلۇش ۋاقىت ھالقىدى</string>
+ <string name="pbap_authentication_timeout_message">%1$s سۆزلىشىش ئاچقۇچىنى كىرگۈزۈشتە ۋاقىت ھالقىدى</string>
+ <string name="auth_notif_ticker">Obex سالاھىيەت دەلىللەش ئىلتىماسى</string>
+ <!-- Notification title when a Bluetooth device wants to pair with us -->
+ <string name="auth_notif_title">سۆزلىشىش ئاچقۇچى</string>
+ <!-- Notification message when a Bluetooth device wants to pair with us -->
+ <string name="auth_notif_message">%1$s سۆزلىشىش ئاچقۇچىنى كىرگۈزۈڭ</string>
+ <string name="defaultname">ماشىنا يۈرۈشلۈكى</string>
+ <string name="unknownName">يوچۇن ئات</string>
+ <string name="localPhoneName">ئاتىم</string>
+ <string name="defaultnumber">000000</string>
+</resources>
diff --git a/res/values-uk/cm_strings.xml b/res/values-uk/cm_strings.xml
new file mode 100644
index 0000000..9c4f5d6
--- /dev/null
+++ b/res/values-uk/cm_strings.xml
@@ -0,0 +1,20 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--Generated by crowdin.com-->
+<!--
+ Copyright (C) 2015-2016 The CyanogenMod 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.
+-->
+<resources>
+ <string name="upload_fail_waiting">Не вдається надіслати файл, повторна спроба\u2026</string>
+</resources>
diff --git a/res/values-vi/cm_strings.xml b/res/values-vi/cm_strings.xml
new file mode 100644
index 0000000..08ac447
--- /dev/null
+++ b/res/values-vi/cm_strings.xml
@@ -0,0 +1,20 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--Generated by crowdin.com-->
+<!--
+ Copyright (C) 2015-2016 The CyanogenMod 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.
+-->
+<resources>
+ <string name="upload_fail_waiting">Không thể gửi tập tin, đang thử lại\u2026</string>
+</resources>
diff --git a/res/values-zh-rCN/cm_strings.xml b/res/values-zh-rCN/cm_strings.xml
new file mode 100644
index 0000000..e0aa712
--- /dev/null
+++ b/res/values-zh-rCN/cm_strings.xml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--Generated by crowdin.com-->
+<!--
+ Copyright (C) 2015-2016 The CyanogenMod 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.
+-->
+<resources>
+ <string name="incoming_file_toast_msg">另一台设备尝试传入文件。请确认您是否要接受此文件。</string>
+ <string name="upload_fail_waiting">无法发送文件,正在重试\u2026</string>
+</resources>
diff --git a/res/values-zh-rTW/cm_strings.xml b/res/values-zh-rTW/cm_strings.xml
new file mode 100644
index 0000000..25dcea0
--- /dev/null
+++ b/res/values-zh-rTW/cm_strings.xml
@@ -0,0 +1,20 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--Generated by crowdin.com-->
+<!--
+ Copyright (C) 2015-2016 The CyanogenMod 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.
+-->
+<resources>
+ <string name="upload_fail_waiting">無法傳送檔案,正在重試\u2026</string>
+</resources>
diff --git a/res/values/cm_strings.xml b/res/values/cm_strings.xml
new file mode 100644
index 0000000..daeb40f
--- /dev/null
+++ b/res/values/cm_strings.xml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ Copyright (C) 2015-2016 The CyanogenMod 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.
+-->
+<resources>
+ <string name="incoming_file_toast_msg">Incoming file from another device. Confirm you want to receive this file.</string>
+
+ <string name="upload_fail_waiting">Unable to send file, retrying\u2026</string>
+</resources>
diff --git a/res/values/config.xml b/res/values/config.xml
index 91f0c0f..90e513d 100644
--- a/res/values/config.xml
+++ b/res/values/config.xml
@@ -66,4 +66,8 @@
<!-- For enabling the hfp client connection service -->
<bool name="hfp_client_connection_service_enabled">false</bool>
+
+ <!-- For disabling non AOSP bluetooth features -->
+ <bool name="disable_non_aosp_bt_features">false</bool>
+
</resources>
diff --git a/src/com/android/bluetooth/ObexServerSockets.java b/src/com/android/bluetooth/ObexServerSockets.java
old mode 100644
new mode 100755
index f5b7363..b80d44c
--- a/src/com/android/bluetooth/ObexServerSockets.java
+++ b/src/com/android/bluetooth/ObexServerSockets.java
@@ -87,6 +87,20 @@
/**
* Creates an RFCOMM {@link BluetoothServerSocket} and a L2CAP {@link BluetoothServerSocket}
+ * @param validator a reference to the {@link IObexConnectionHandler} object to call
+ * to validate an incoming connection.
+ * @param rfcommChannel fixed rfcomm channel number to listen on
+ * @param l2capPsm fixed l2cap psm to listen on
+ * @return a reference to a {@link ObexServerSockets} object instance.
+ * @throws IOException if it occurs while creating the {@link BluetoothServerSocket}s.
+ */
+ public static ObexServerSockets createWithFixedChannels(IObexConnectionHandler validator,
+ int rfcommChannel, int l2capPsm) {
+ return create(validator, rfcommChannel, l2capPsm);
+ }
+
+ /**
+ * Creates an RFCOMM {@link BluetoothServerSocket} and a L2CAP {@link BluetoothServerSocket}
* with specific l2cap and RFCOMM channel numbers. It is the responsibility of the caller to
* ensure the numbers are free and can be used, e.g. by calling {@link #getL2capPsm()} and
* {@link #getRfcommChannel()} in {@link ObexServerSockets}.
@@ -114,10 +128,10 @@
for (int i = 0; i < CREATE_RETRY_TIME; i++) {
initSocketOK = true;
try {
- if(rfcommSocket == null) {
+ if(rfcommSocket == null && rfcommChannel != -1) {
rfcommSocket = bt.listenUsingRfcommOn(rfcommChannel);
}
- if(l2capSocket == null) {
+ if(l2capSocket == null && l2capPsm != -1) {
l2capSocket = bt.listenUsingL2capOn(l2capPsm);
}
} catch (IOException e) {
@@ -181,11 +195,15 @@
if(D) Log.d(TAG,"startAccept()");
prepareForNewConnect();
- mRfcommThread = new SocketAcceptThread(mRfcommSocket);
- mRfcommThread.start();
+ if (mRfcommSocket != null) {
+ mRfcommThread = new SocketAcceptThread(mRfcommSocket);
+ mRfcommThread.start();
+ }
- mL2capThread = new SocketAcceptThread(mL2capSocket);
- mL2capThread.start();
+ if (mL2capSocket != null) {
+ mL2capThread = new SocketAcceptThread(mL2capSocket);
+ mL2capThread.start();
+ }
}
/**
@@ -221,9 +239,15 @@
* Signal to the {@link IObexConnectionHandler} that an error have occurred.
*/
synchronized private void onAcceptFailed() {
- Log.w(TAG,"onAcceptFailed() calling shutdown...");
- mConHandler.onAcceptFailed();
+ //Donot block for Accept thread cleanup.
+ //Fix Handler Thread block during BT Turn OFF.
shutdown(false);
+ BluetoothAdapter mAdapter = BluetoothAdapter.getDefaultAdapter();
+ if((mAdapter != null) && (mAdapter.getState() == BluetoothAdapter.STATE_ON)) {
+ Log.d(TAG,"onAcceptFailed() calling shutdown...");
+ mConHandler.onAcceptFailed();
+ }
+
}
/**
diff --git a/src/com/android/bluetooth/OolConnManager.java b/src/com/android/bluetooth/OolConnManager.java
new file mode 100644
index 0000000..6f520fb
--- /dev/null
+++ b/src/com/android/bluetooth/OolConnManager.java
@@ -0,0 +1,97 @@
+/*
+ * Copyright (c) 2015, 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;
+import java.util.UUID;
+import android.bluetooth.BluetoothDevice;
+import android.bluetooth.BluetoothSocket;
+import android.util.Log;
+import java.io.IOException;
+import android.bluetooth.SdpOppOpsRecord;
+public class OolConnManager {
+
+ private static final String TAG="OolConnManager";
+ static int channel = 0;
+ static boolean sdpDone = false;
+ static String mAddress;
+
+ public static BluetoothSocket CreateL2capConnection(BluetoothDevice remBtDev,UUID uuid ) {
+
+ Log.d(TAG,"createL2cConnection "+remBtDev.getAddress());
+ try {
+ return remBtDev.createL2capSocket(channel);
+ } catch (IOException e) {
+ Log.e(TAG, "BtSocket Connect error " + e.getMessage());
+ }
+ return null;
+ }
+
+ public static void setSdpInitiatedAddress(BluetoothDevice remBtDev) {
+
+ if (remBtDev != null)
+ mAddress = remBtDev.getAddress();
+ else
+ mAddress = null;
+ Log.d(TAG,"setSdpInitiatedAddress "+ mAddress);
+
+ }
+
+ public static int getL2cPSM(BluetoothDevice remBtDev) {
+
+ int waitCount = 0;
+ int channelNo = -1;
+ while(!sdpDone && waitCount < 100) {
+
+ try {
+ Thread.sleep(100);
+ } catch (InterruptedException e) {
+ Log.e(TAG, "Interrupted", e);
+ }
+ waitCount++;
+ }
+ waitCount = 0;
+ sdpDone = false;
+
+ Log.d(TAG,"returning l2c channel as "+channel);
+ channelNo = channel;
+ channel = -1;
+ return channelNo;
+ }
+
+ public static void saveOppSdpRecord(SdpOppOpsRecord sdpRec, BluetoothDevice btDevice) {
+
+ Log.v(TAG,"saveOppSdpRecord"+ btDevice.getAddress());
+ if ((mAddress != null) && mAddress.equalsIgnoreCase(btDevice.getAddress())) {
+ channel = sdpRec != null ? sdpRec.getL2capPsm() : -1;
+ sdpDone = true;
+ Log.d(TAG,"saveOppSdpRecord channel "+ channel);
+ }
+ }
+
+}
diff --git a/src/com/android/bluetooth/a2dp/A2dpService.java b/src/com/android/bluetooth/a2dp/A2dpService.java
old mode 100755
new mode 100644
index 41c31d1..ea29f47
--- a/src/com/android/bluetooth/a2dp/A2dpService.java
+++ b/src/com/android/bluetooth/a2dp/A2dpService.java
@@ -1,4 +1,7 @@
/*
+ * Copyright (C) 2013-2014, The Linux Foundation. All rights reserved.
+ * Not a Contribution.
+ *
* Copyright (C) 2012 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
@@ -22,8 +25,10 @@
import android.bluetooth.IBluetoothA2dp;
import android.content.Context;
import android.content.Intent;
+import android.media.AudioManager;
import android.os.ParcelUuid;
import android.provider.Settings;
+import android.os.SystemProperties;
import android.util.Log;
import com.android.bluetooth.avrcp.Avrcp;
import com.android.bluetooth.btservice.ProfileService;
@@ -38,11 +43,12 @@
* @hide
*/
public class A2dpService extends ProfileService {
- private static final boolean DBG = false;
+ private static final boolean DBG = true;
private static final String TAG="A2dpService";
private A2dpStateMachine mStateMachine;
private Avrcp mAvrcp;
+ private AudioManager mAudioManager;
private static A2dpService sAd2dpService;
static final ParcelUuid[] A2DP_SOURCE_UUID = {
BluetoothUuid.AudioSource
@@ -61,23 +67,57 @@
}
protected boolean start() {
- mAvrcp = Avrcp.make(this);
- mStateMachine = A2dpStateMachine.make(this, this);
+ int maxConnections = 1;
+ int multiCastState = 0;
+ int maxA2dpConnection =
+ SystemProperties.getInt("persist.bt.max.a2dp.connections", 1);
+ int a2dpMultiCastState =
+ SystemProperties.getInt("persist.bt.enable.multicast", 0);
+ if (DBG) Log.d(TAG, "START of A2dpService");
+ String offload_cap =
+ SystemProperties.get("persist.bt.a2dp_offload_cap");
+ if (offload_cap.isEmpty() || "false".equals(offload_cap)) {
+ Log.i(TAG,"offload cap not set");
+ offload_cap = null;
+ }
+ if (offload_cap != null && a2dpMultiCastState == 1) {
+ Log.i(TAG,"Split a2dp mode is enabled, disabling multicast");
+ a2dpMultiCastState = 0;
+ }
+ if (a2dpMultiCastState == 1)
+ multiCastState = a2dpMultiCastState;
+ if (maxA2dpConnection == 2)
+ maxConnections = maxA2dpConnection;
+ // enable soft hands-off also when multicast is enabled.
+ if (multiCastState == 1 && maxConnections != 2) {
+ Log.i(TAG,"Enable soft handsoff as multicast is enabled");
+ maxConnections = 2;
+ }
+ log( "maxA2dpConnections = " + maxConnections);
+ log( "multiCastState = " + multiCastState);
+ mStateMachine = A2dpStateMachine.make(this, this,
+ maxConnections, multiCastState, offload_cap);
setA2dpService(this);
+ mAudioManager = (AudioManager) getSystemService(Context.AUDIO_SERVICE);
+ mAvrcp = Avrcp.make(this, this, maxConnections);
+ if (DBG) Log.d(TAG, "Exit START of A2dpService");
return true;
}
protected boolean stop() {
+ if (DBG) Log.d(TAG, "STOP of A2dpService");
if (mStateMachine != null) {
mStateMachine.doQuit();
}
if (mAvrcp != null) {
mAvrcp.doQuit();
}
+ if (DBG) Log.d(TAG, "Exit STOP of A2dpService");
return true;
}
protected boolean cleanup() {
+ if (DBG) Log.d(TAG, "Enter cleanup");
if (mStateMachine!= null) {
mStateMachine.cleanup();
}
@@ -86,6 +126,7 @@
mAvrcp = null;
}
clearA2dpService();
+ if (DBG) Log.d(TAG, "Exit cleanup");
return true;
}
@@ -126,6 +167,7 @@
}
public boolean connect(BluetoothDevice device) {
+ if (DBG) Log.d(TAG, "Enter connect");
enforceCallingOrSelfPermission(BLUETOOTH_ADMIN_PERM,
"Need BLUETOOTH ADMIN permission");
@@ -146,10 +188,12 @@
}
mStateMachine.sendMessage(A2dpStateMachine.CONNECT, device);
+ if (DBG) Log.d(TAG, "Exit connect");
return true;
}
boolean disconnect(BluetoothDevice device) {
+ if (DBG) Log.d(TAG, "Enter Disconnect");
enforceCallingOrSelfPermission(BLUETOOTH_ADMIN_PERM,
"Need BLUETOOTH ADMIN permission");
int connectionState = mStateMachine.getConnectionState(device);
@@ -159,6 +203,7 @@
}
mStateMachine.sendMessage(A2dpStateMachine.DISCONNECT, device);
+ if (DBG) Log.d(TAG, "Exit disconnect");
return true;
}
@@ -178,21 +223,25 @@
}
public boolean setPriority(BluetoothDevice device, int priority) {
+ if (DBG) Log.d(TAG, "Enter setPriority");
enforceCallingOrSelfPermission(BLUETOOTH_ADMIN_PERM,
"Need BLUETOOTH_ADMIN permission");
Settings.Global.putInt(getContentResolver(),
Settings.Global.getBluetoothA2dpSinkPriorityKey(device.getAddress()),
priority);
if (DBG) Log.d(TAG,"Saved priority " + device + " = " + priority);
+ if (DBG) Log.d(TAG, "Exit setPriority");
return true;
}
public int getPriority(BluetoothDevice device) {
+ if (DBG) Log.d(TAG, "Enter getPriority");
enforceCallingOrSelfPermission(BLUETOOTH_ADMIN_PERM,
"Need BLUETOOTH_ADMIN permission");
int priority = Settings.Global.getInt(getContentResolver(),
Settings.Global.getBluetoothA2dpSinkPriorityKey(device.getAddress()),
BluetoothProfile.PRIORITY_UNDEFINED);
+ if (DBG) Log.d(TAG, "Exit getPriority");
return priority;
}
@@ -209,8 +258,43 @@
mAvrcp.setAbsoluteVolume(volume);
}
- public void setAvrcpAudioState(int state) {
- mAvrcp.setA2dpAudioState(state);
+ public void setAvrcpAudioState(int state, BluetoothDevice device) {
+ mAvrcp.setA2dpAudioState(state, device);
+ }
+
+ public List<BluetoothDevice> getA2dpPlayingDevice() {
+ return mStateMachine.getPlayingDevice();
+ }
+
+ public boolean isMulticastEnabled() {
+ return mStateMachine.isMulticastEnabled();
+ }
+
+ public boolean isMulticastFeatureEnabled() {
+ return mStateMachine.isMulticastFeatureEnabled();
+ }
+
+ // return status of multicast,needed for blocking outgoing connections
+ public boolean isMulticastOngoing(BluetoothDevice device) {
+
+ Log.i(TAG,"audio isMusicActive is " + mAudioManager.isMusicActive());
+ // we should never land is case where playing device size is bigger
+ // than 2 still have safe check.
+ if (device == null) {
+ if ((getA2dpPlayingDevice().size() >= 2) &&
+ (mAudioManager.isMusicActive())) {
+ return true;
+ } else {
+ return false;
+ }
+ }
+ if ((getA2dpPlayingDevice().size() >= 2) &&
+ mAudioManager.isMusicActive() &&
+ !(getA2dpPlayingDevice().contains(device))) {
+ return true;
+ } else {
+ return false;
+ }
}
public void resetAvrcpBlacklist(BluetoothDevice device) {
@@ -255,6 +339,11 @@
public boolean connect(BluetoothDevice device) {
A2dpService service = getService();
if (service == null) return false;
+ //do not allow new connections with active multicast
+ if (service.isMulticastOngoing(device)) {
+ Log.i(TAG,"A2dp Multicast is Ongoing, ignore Connection Request");
+ return false;
+ }
return service.connect(device);
}
diff --git a/src/com/android/bluetooth/a2dp/A2dpStateMachine.java b/src/com/android/bluetooth/a2dp/A2dpStateMachine.java
old mode 100755
new mode 100644
index 7ad4a59..8016b34
--- a/src/com/android/bluetooth/a2dp/A2dpStateMachine.java
+++ b/src/com/android/bluetooth/a2dp/A2dpStateMachine.java
@@ -1,4 +1,7 @@
/*
+ * Copyright (C) 2013-2014, The Linux Foundation. All rights reserved.
+ * Not a Contribution.
+ *
* Copyright (C) 2012 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
@@ -25,6 +28,11 @@
* CONNECTED | | CONNECT
* V |
* (Connected)
+ * | ^
+ * CONNECTING/DISCONNECTING | | CONNECTED/DISCONNECTED
+ * V |
+ * (MultiConnectionpending)
+
*/
package com.android.bluetooth.a2dp;
@@ -58,16 +66,35 @@
import java.util.Set;
final class A2dpStateMachine extends StateMachine {
- private static final boolean DBG = false;
+ private static final boolean DBG = true;
+ private static final String TAG="A2dpStateMachine";
static final int CONNECT = 1;
static final int DISCONNECT = 2;
private static final int STACK_EVENT = 101;
private static final int CONNECT_TIMEOUT = 201;
+ /* Allow time for possible LMP response timeout + Page timeout */
+ private static final int CONNECT_TIMEOUT_SEC = 38000;
+
+
+ // Max number of A2dp connections at any time
+ private int maxA2dpConnections = 1;
+
+ private static final int IS_INVALID_DEVICE = 0;
+ private static final int IS_VALID_DEVICE = 1;
+
+ // enable disable multicast
+ private static final int DISABLE_MULTICAST = 0;
+ private static final int ENABLE_MULTICAST = 1;
+ private static boolean isMultiCastEnabled = false;
+ private static boolean isScanDisabled = false;
+ private static boolean isMultiCastFeatureEnabled = false;
private Disconnected mDisconnected;
private Pending mPending;
private Connected mConnected;
+ // Multi A2dp: add new class object
+ private MultiConnectionPending mMultiConnectionPending;
private A2dpService mService;
private Context mContext;
@@ -85,6 +112,9 @@
// and mTargetDevice are null
// when either mCurrentDevice or mTargetDevice is not null,
// mIncomingDevice is null
+ // mMultiDisconnectDevice is the device for which disconnect is initiated
+ // in connected state.It is cleared when disconnected event is recieved
+ // from stack.
// Stable states
// No connection, Disconnected state
// both mCurrentDevice and mTargetDevice are null
@@ -100,31 +130,51 @@
// mCurrentDevice is not null, mTargetDevice is null
// Incoming connections Pending
// Both mCurrentDevice and mTargetDevice are null
+ // MultiConnectionPending to hanle connection/disconnection for new device
+ // when already connected to one device
+
private BluetoothDevice mCurrentDevice = null;
private BluetoothDevice mTargetDevice = null;
private BluetoothDevice mIncomingDevice = null;
- private BluetoothDevice mPlayingA2dpDevice = null;
-
+ private BluetoothDevice mMultiDisconnectDevice = null;
+ // Multi A2dp: Connected devices list holds all currently connected headsets
+ private ArrayList<BluetoothDevice> mConnectedDevicesList =
+ new ArrayList<BluetoothDevice>();
+ private ArrayList<BluetoothDevice> mPlayingA2dpDevice =
+ new ArrayList<BluetoothDevice>();
static {
classInitNative();
}
- private A2dpStateMachine(A2dpService svc, Context context) {
+ private A2dpStateMachine(A2dpService svc, Context context, int
+ maxConnections, int multiCastState, String offload_cap) {
super("A2dpStateMachine");
mService = svc;
mContext = context;
mAdapter = BluetoothAdapter.getDefaultAdapter();
+ maxA2dpConnections = maxConnections;
+ // By default isMultiCastEnabled is set to false, value changes based on stack update
+ isMultiCastEnabled = false;
+ if (multiCastState == 1) {
+ isMultiCastFeatureEnabled = true;
+ } else {
+ isMultiCastFeatureEnabled = false;
+ }
- initNative();
+ initNative(maxA2dpConnections, multiCastState, offload_cap);
mDisconnected = new Disconnected();
mPending = new Pending();
mConnected = new Connected();
+ // Multi A2dp: initialise new class variable
+ mMultiConnectionPending = new MultiConnectionPending();
addState(mDisconnected);
addState(mPending);
addState(mConnected);
+ // Multi A2dp: add State
+ addState(mMultiConnectionPending);
setInitialState(mDisconnected);
@@ -136,38 +186,70 @@
mAudioManager = (AudioManager) context.getSystemService(Context.AUDIO_SERVICE);
}
- static A2dpStateMachine make(A2dpService svc, Context context) {
+ static A2dpStateMachine make(A2dpService svc, Context context,
+ int maxConnections, int multiCastState, String offload_cap) {
Log.d("A2dpStateMachine", "make");
- A2dpStateMachine a2dpSm = new A2dpStateMachine(svc, context);
+ A2dpStateMachine a2dpSm = new A2dpStateMachine(svc, context,
+ maxConnections, multiCastState, offload_cap);
a2dpSm.start();
return a2dpSm;
}
public void doQuit() {
+ log("Enter doQuit()");
if ((mTargetDevice != null) &&
(getConnectionState(mTargetDevice) == BluetoothProfile.STATE_CONNECTING)) {
log("doQuit()- Move A2DP State to DISCONNECTED");
broadcastConnectionState(mTargetDevice, BluetoothProfile.STATE_DISCONNECTED,
BluetoothProfile.STATE_CONNECTING);
}
+
+ if ((mIncomingDevice!= null) &&
+ (getConnectionState(mIncomingDevice) == BluetoothProfile.STATE_CONNECTING)) {
+ log("doQuit()- Move A2DP State to DISCONNECTED");
+ broadcastConnectionState(mIncomingDevice, BluetoothProfile.STATE_DISCONNECTED,
+ BluetoothProfile.STATE_CONNECTING);
+ }
+
quitNow();
+ log("Exit doQuit()");
}
public void cleanup() {
+ log("Enter cleanup()");
+ int deviceSize = mConnectedDevicesList.size();
+ log("cleanup: mConnectedDevicesList size is " + deviceSize);
cleanupNative();
+ for (int i = 0; i < deviceSize; i++) {
+ mCurrentDevice = mConnectedDevicesList.get(i);
+ broadcastConnectionState(mCurrentDevice, BluetoothProfile.STATE_DISCONNECTED,
+ BluetoothProfile.STATE_CONNECTED);
+ }
+ log("Exit cleanup()");
}
private class Disconnected extends State {
@Override
public void enter() {
log("Enter Disconnected: " + getCurrentMessage().what);
+ log("mConnectedDevicesList size: " + mConnectedDevicesList.size());
+ // Remove Timeout msg when moved to stable state
+ removeMessages(CONNECT_TIMEOUT);
+ mCurrentDevice = null;
}
@Override
public boolean processMessage(Message message) {
log("Disconnected process message: " + message.what);
- if (mCurrentDevice != null || mTargetDevice != null || mIncomingDevice != null) {
- loge("ERROR: current, target, or mIncomingDevice not null in Disconnected");
+ log("mConnectedDevicesList size: " + mConnectedDevicesList.size());
+ if (mCurrentDevice != null || mTargetDevice != null || mIncomingDevice != null
+ || mConnectedDevicesList.size() != 0) {
+ loge("ERROR: mConnectedDevicesList is not empty," +
+ "current, target, or mIncomingDevice not null in Disconnected");
+ loge("mCurrentDevice is " + mCurrentDevice);
+ loge("mTargetDevice is " + mTargetDevice);
+ loge("mIncomingDevice is " + mIncomingDevice);
+ loge("mConnectedDevicesList.size() is " + mConnectedDevicesList.size());
return NOT_HANDLED;
}
@@ -190,7 +272,9 @@
}
// TODO(BT) remove CONNECT_TIMEOUT when the stack
// sends back events consistently
- sendMessageDelayed(CONNECT_TIMEOUT, 30000);
+ Message m = obtainMessage(CONNECT_TIMEOUT);
+ m.obj = device;
+ sendMessageDelayed(m, CONNECT_TIMEOUT_SEC);
break;
case DISCONNECT:
// ignore
@@ -209,6 +293,7 @@
default:
return NOT_HANDLED;
}
+ log("Exit Disconnected processMessage() ");
return retValue;
}
@@ -219,12 +304,14 @@
// in Disconnected state
private void processConnectionEvent(int state, BluetoothDevice device) {
+ log("processConnectionEvent state = " + state +
+ ", device = " + device);
switch (state) {
case CONNECTION_STATE_DISCONNECTED:
- logw("Ignore HF DISCONNECTED event, device: " + device);
+ logw("Ignore A2DP DISCONNECTED event, device: " + device);
break;
case CONNECTION_STATE_CONNECTING:
- if (okToConnect(device)){
+ if (okToConnect(device)) {
logi("Incoming A2DP accepted");
broadcastConnectionState(device, BluetoothProfile.STATE_CONNECTING,
BluetoothProfile.STATE_DISCONNECTED);
@@ -240,20 +327,25 @@
AdapterService adapterService = AdapterService.getAdapterService();
if (adapterService != null) {
adapterService.connectOtherProfile(device,
- AdapterService.PROFILE_CONN_REJECTED);
+ AdapterService.PROFILE_CONN_REJECTED);
}
}
break;
case CONNECTION_STATE_CONNECTED:
logw("A2DP Connected from Disconnected state");
- if (okToConnect(device)){
+ if (okToConnect(device)) {
logi("Incoming A2DP accepted");
- broadcastConnectionState(device, BluetoothProfile.STATE_CONNECTED,
- BluetoothProfile.STATE_DISCONNECTED);
synchronized (A2dpStateMachine.this) {
+ if (!mConnectedDevicesList.contains(device)) {
+ mConnectedDevicesList.add(device);
+ log( "device " + device.getAddress() +
+ " is adding in Disconnected state");
+ }
mCurrentDevice = device;
transitionTo(mConnected);
}
+ broadcastConnectionState(device, BluetoothProfile.STATE_CONNECTED,
+ BluetoothProfile.STATE_DISCONNECTED);
} else {
//reject the connection and stay in Disconnected state itself
logi("Incoming A2DP rejected");
@@ -273,6 +365,7 @@
loge("Incorrect state: " + state);
break;
}
+ log("Exit Disconnected processConnectionEvent() ");
}
}
@@ -284,7 +377,8 @@
@Override
public boolean processMessage(Message message) {
- log("Pending process message: " + message.what);
+ log("Pending process message: " + message.what + ", size: "
+ + mConnectedDevicesList.size());
boolean retValue = HANDLED;
switch(message.what) {
@@ -292,15 +386,27 @@
deferMessage(message);
break;
case CONNECT_TIMEOUT:
+ // This is always for Outgoing connection
+ BluetoothDevice device = (BluetoothDevice) message.obj;
+ // getByteAddress has no null check
+ Log.v(TAG,"device for timeout is " + device);
+ if (device != null && (mTargetDevice == null ||
+ !mTargetDevice.equals(device))) {
+ log("Timeout for unknown device " + device);
+ onConnectionStateChanged(CONNECTION_STATE_DISCONNECTED,
+ getByteAddress(device));
+ break;
+ }
+ disconnectA2dpNative(getByteAddress(mTargetDevice));
onConnectionStateChanged(CONNECTION_STATE_DISCONNECTED,
getByteAddress(mTargetDevice));
break;
case DISCONNECT:
- BluetoothDevice device = (BluetoothDevice) message.obj;
+ BluetoothDevice dev = (BluetoothDevice) message.obj;
if (mCurrentDevice != null && mTargetDevice != null &&
- mTargetDevice.equals(device) ) {
+ mTargetDevice.equals(dev) ) {
// cancel connection to the mTargetDevice
- broadcastConnectionState(device, BluetoothProfile.STATE_DISCONNECTED,
+ broadcastConnectionState(dev, BluetoothProfile.STATE_DISCONNECTED,
BluetoothProfile.STATE_CONNECTING);
synchronized (A2dpStateMachine.this) {
mTargetDevice = null;
@@ -313,7 +419,6 @@
StackEvent event = (StackEvent) message.obj;
switch (event.type) {
case EVENT_TYPE_CONNECTION_STATE_CHANGED:
- removeMessages(CONNECT_TIMEOUT);
processConnectionEvent(event.valueInt, event.device);
break;
default:
@@ -324,26 +429,57 @@
default:
return NOT_HANDLED;
}
+ log("Exit Pending processMessage() ");
return retValue;
}
// in Pending state
private void processConnectionEvent(int state, BluetoothDevice device) {
+ log("processConnectionEvent state = " + state +
+ ", device = " + device);
switch (state) {
case CONNECTION_STATE_DISCONNECTED:
- if ((mCurrentDevice != null) && mCurrentDevice.equals(device)) {
- broadcastConnectionState(mCurrentDevice,
- BluetoothProfile.STATE_DISCONNECTED,
- BluetoothProfile.STATE_DISCONNECTING);
+ // remove this device from playing device list
+ if (mPlayingA2dpDevice.size() != 0 &&
+ mPlayingA2dpDevice.contains(device)) {
+ log ("Playing A2dp Device is disconnected, setting it to null");
+ broadcastAudioState(device,
+ BluetoothA2dp.STATE_NOT_PLAYING,
+ BluetoothA2dp.STATE_PLAYING);
+ mPlayingA2dpDevice.remove(device);
+ }
+ // Reset scan mode if it set due to multicast
+ Log.i(TAG,"getScanMode " + mAdapter.getScanMode() +
+ " isScanDisabled: " + isScanDisabled);
+ if (mPlayingA2dpDevice.size() <= 1 &&
+ (mAdapter.getScanMode() ==
+ BluetoothAdapter.SCAN_MODE_NONE) &&
+ isScanDisabled) {
+ isScanDisabled = false;
+ AdapterService adapterService =
+ AdapterService.getAdapterService();
+ if (adapterService != null) {
+ adapterService.restoreScanMode();
+ }
+ }
+ if (mConnectedDevicesList.contains(device)) {
+ synchronized (A2dpStateMachine.this) {
+ mConnectedDevicesList.remove(device);
+ log( "device " + device.getAddress() +
+ " is removed in Pending state");
+ }
+ broadcastConnectionState(device,
+ BluetoothProfile.STATE_DISCONNECTED,
+ BluetoothProfile.STATE_DISCONNECTING);
synchronized (A2dpStateMachine.this) {
mCurrentDevice = null;
}
-
+ log("disconnected for target in pending state " + mTargetDevice);
if (mTargetDevice != null) {
if (!connectA2dpNative(getByteAddress(mTargetDevice))) {
broadcastConnectionState(mTargetDevice,
- BluetoothProfile.STATE_DISCONNECTED,
- BluetoothProfile.STATE_CONNECTING);
+ BluetoothProfile.STATE_DISCONNECTED,
+ BluetoothProfile.STATE_CONNECTING);
synchronized (A2dpStateMachine.this) {
mTargetDevice = null;
transitionTo(mDisconnected);
@@ -352,13 +488,18 @@
} else {
synchronized (A2dpStateMachine.this) {
mIncomingDevice = null;
- transitionTo(mDisconnected);
+ if (mConnectedDevicesList.size() == 0) {
+ transitionTo(mDisconnected);
+ } else {
+ processMultiA2dpDisconnected(device);
+ }
}
}
} else if (mTargetDevice != null && mTargetDevice.equals(device)) {
// outgoing connection failed
- broadcastConnectionState(mTargetDevice, BluetoothProfile.STATE_DISCONNECTED,
- BluetoothProfile.STATE_CONNECTING);
+ broadcastConnectionState(mTargetDevice,
+ BluetoothProfile.STATE_DISCONNECTED,
+ BluetoothProfile.STATE_CONNECTING);
// check if there is some incoming connection request
if (mIncomingDevice != null) {
logi("disconnect for outgoing in pending state");
@@ -369,72 +510,121 @@
}
synchronized (A2dpStateMachine.this) {
mTargetDevice = null;
- transitionTo(mDisconnected);
+ if (mConnectedDevicesList.size() == 0) {
+ transitionTo(mDisconnected);
+ } else {
+ transitionTo(mConnected);
+ }
}
} else if (mIncomingDevice != null && mIncomingDevice.equals(device)) {
broadcastConnectionState(mIncomingDevice,
- BluetoothProfile.STATE_DISCONNECTED,
- BluetoothProfile.STATE_CONNECTING);
+ BluetoothProfile.STATE_DISCONNECTED,
+ BluetoothProfile.STATE_CONNECTING);
+ // check if there is some outgoing connection request
+ if (mTargetDevice != null) {
+ logi("disconnect for incoming in pending state");
+ synchronized (A2dpStateMachine.this) {
+ mIncomingDevice = null;
+ }
+ break;
+ }
synchronized (A2dpStateMachine.this) {
mIncomingDevice = null;
- transitionTo(mDisconnected);
+ if (mConnectedDevicesList.size() == 0) {
+ transitionTo(mDisconnected);
+ } else {
+ transitionTo(mConnected);
+ }
}
} else {
loge("Unknown device Disconnected: " + device);
}
break;
case CONNECTION_STATE_CONNECTED:
- if ((mCurrentDevice != null) && mCurrentDevice.equals(device)) {
+ if (mConnectedDevicesList.contains(device)) {
// disconnection failed
- broadcastConnectionState(mCurrentDevice, BluetoothProfile.STATE_CONNECTED,
- BluetoothProfile.STATE_DISCONNECTING);
+ broadcastConnectionState(device, BluetoothProfile.STATE_CONNECTED,
+ BluetoothProfile.STATE_DISCONNECTING);
if (mTargetDevice != null) {
broadcastConnectionState(mTargetDevice, BluetoothProfile.STATE_DISCONNECTED,
- BluetoothProfile.STATE_CONNECTING);
+ BluetoothProfile.STATE_CONNECTING);
}
synchronized (A2dpStateMachine.this) {
mTargetDevice = null;
transitionTo(mConnected);
}
} else if (mTargetDevice != null && mTargetDevice.equals(device)) {
- broadcastConnectionState(mTargetDevice, BluetoothProfile.STATE_CONNECTED,
- BluetoothProfile.STATE_CONNECTING);
synchronized (A2dpStateMachine.this) {
mCurrentDevice = mTargetDevice;
+ mConnectedDevicesList.add(mTargetDevice);
mTargetDevice = null;
- transitionTo(mConnected);
+ log( "device " + device.getAddress() +
+ " is added in Pending state");
+ if (mIncomingDevice == null ||
+ (mIncomingDevice != null && !okToConnect(mIncomingDevice)))
+ transitionTo(mConnected);
}
+ broadcastConnectionState(device, BluetoothProfile.STATE_CONNECTED,
+ BluetoothProfile.STATE_CONNECTING);
} else if (mIncomingDevice != null && mIncomingDevice.equals(device)) {
- broadcastConnectionState(mIncomingDevice, BluetoothProfile.STATE_CONNECTED,
- BluetoothProfile.STATE_CONNECTING);
// check for a2dp connection allowed for this device in race condition
if (okToConnect(mIncomingDevice)) {
logi("Ready to connect incoming Connection from pending state");
synchronized (A2dpStateMachine.this) {
mCurrentDevice = mIncomingDevice;
+ mConnectedDevicesList.add(mIncomingDevice);
mIncomingDevice = null;
- transitionTo(mConnected);
+ if (mTargetDevice == null)
+ transitionTo(mConnected);
+ log( "device " + device.getAddress() +
+ " is added in Pending state");
}
+ broadcastConnectionState(device, BluetoothProfile.STATE_CONNECTED,
+ BluetoothProfile.STATE_CONNECTING);
} else {
// A2dp connection unchecked for this device
loge("Incoming A2DP rejected from pending state");
disconnectA2dpNative(getByteAddress(device));
+ // the other profile connection should be initiated
+ AdapterService adapterService = AdapterService.getAdapterService();
+ if (adapterService != null) {
+ adapterService.connectOtherProfile(device,
+ AdapterService.PROFILE_CONN_REJECTED);
+ }
}
} else {
loge("Unknown device Connected: " + device);
// something is wrong here, but sync our state with stack
- broadcastConnectionState(device, BluetoothProfile.STATE_CONNECTED,
- BluetoothProfile.STATE_DISCONNECTED);
- synchronized (A2dpStateMachine.this) {
- mCurrentDevice = device;
- mTargetDevice = null;
- mIncomingDevice = null;
- transitionTo(mConnected);
+ if (okToConnect(device)) {
+ synchronized (A2dpStateMachine.this) {
+ mConnectedDevicesList.add(device);
+ if (mTargetDevice != null) {
+ log("Waiting for Connected event for mTargetDevice");
+ } else if (mIncomingDevice != null) {
+ log("Waiting for Connected event for mIncomingDevice");
+ }
+ log( "device " + device.getAddress() +
+ " is added in Pending state");
+ }
+ broadcastConnectionState(device, BluetoothProfile.STATE_CONNECTED,
+ BluetoothProfile.STATE_DISCONNECTED);
+ } else {
+ //reject the connection and stay in Pending state itself
+ Log.i(TAG,"Incoming A2dp rejected. priority=" +
+ mService.getPriority(device) + " bondState=" +
+ device.getBondState());
+ disconnectA2dpNative(getByteAddress(device));
+ // the other profile connection should be initiated
+ AdapterService adapterService = AdapterService.getAdapterService();
+ if (adapterService != null) {
+ adapterService.connectOtherProfile(device,
+ AdapterService.PROFILE_CONN_REJECTED);
+ }
}
}
break;
case CONNECTION_STATE_CONNECTING:
- if ((mCurrentDevice != null) && mCurrentDevice.equals(device)) {
+ if (mConnectedDevicesList.contains(device)) {
log("current device tries to connect back");
// TODO(BT) ignore or reject
} else if (mTargetDevice != null && mTargetDevice.equals(device)) {
@@ -443,15 +633,31 @@
// we already broadcasted the intent, doing nothing here
log("Stack and target device are connecting");
}
- else if (mIncomingDevice != null && mIncomingDevice.equals(device)) {
- loge("Another connecting event on the incoming device");
+ else if (mIncomingDevice != null) {
+ if (mIncomingDevice.equals(device)) {
+ loge("Connecting for same device");
+ } else {
+ log("Processing incoming " + mIncomingDevice +
+ " Rejecting incoming " + device);
+ disconnectA2dpNative(getByteAddress(device));
+ }
} else {
// We get an incoming connecting request while Pending
// TODO(BT) is stack handing this case? let's ignore it for now
log("Incoming connection while pending, accept it");
- broadcastConnectionState(device, BluetoothProfile.STATE_CONNECTING,
- BluetoothProfile.STATE_DISCONNECTED);
- mIncomingDevice = device;
+ if (okToConnect(device)) {
+ broadcastConnectionState(device, BluetoothProfile.STATE_CONNECTING,
+ BluetoothProfile.STATE_DISCONNECTED);
+ mIncomingDevice = device;
+ } else {
+ disconnectA2dpNative(getByteAddress(device));
+ // the other profile connection should be initiated
+ AdapterService adapterService = AdapterService.getAdapterService();
+ if (adapterService != null) {
+ adapterService.connectOtherProfile(device,
+ AdapterService.PROFILE_CONN_REJECTED);
+ }
+ }
}
break;
case CONNECTION_STATE_DISCONNECTING:
@@ -472,8 +678,26 @@
loge("Incorrect state: " + state);
break;
}
+ log("Exit Pending processConnectionEvent() ");
}
+ private void processMultiA2dpDisconnected(BluetoothDevice device) {
+ log("Pending state: processMultiA2dpDisconnected");
+ /* Assign the current activedevice again if the disconnected
+ device equals to the current active device*/
+ if (mCurrentDevice != null && mCurrentDevice.equals(device)) {
+ int deviceSize = mConnectedDevicesList.size();
+ mCurrentDevice = mConnectedDevicesList.get(deviceSize-1);
+ }
+ transitionTo(mConnected);
+ log("processMultiA2dpDisconnected , the latest mCurrentDevice is:"
+ + mCurrentDevice);
+ log("Pending state: processMultiA2dpDisconnected ," +
+ "fake broadcasting for new mCurrentDevice");
+ broadcastConnectionState(mCurrentDevice, BluetoothProfile.STATE_CONNECTED,
+ BluetoothProfile.STATE_DISCONNECTED);
+ log("Exit Pending processMultiA2dpDisconnected() ");
+ }
}
private class Connected extends State {
@@ -483,9 +707,12 @@
// state. This is to prevent auto connect attempts from disconnecting
// devices that previously successfully connected.
// TODO: This needs to check for multiple A2DP connections, once supported...
- removeDeferredMessages(CONNECT);
-
- log("Enter Connected: " + getCurrentMessage().what);
+ log("Enter Connected: " + getCurrentMessage().what +
+ ", size: " + mConnectedDevicesList.size());
+ // remove timeout for connected device only.
+ if (getDeviceForMessage(CONNECT_TIMEOUT) == null) {
+ removeMessages(CONNECT_TIMEOUT);
+ }
// Upon connected, the audio starts out as stopped
broadcastAudioState(mCurrentDevice, BluetoothA2dp.STATE_NOT_PLAYING,
BluetoothA2dp.STATE_PLAYING);
@@ -493,7 +720,8 @@
@Override
public boolean processMessage(Message message) {
- log("Connected process message: " + message.what);
+ log("Connected process message: " + message.what +
+ ", size: " + mConnectedDevicesList.size());
if (mCurrentDevice == null) {
loge("ERROR: mCurrentDevice is null in Connected");
return NOT_HANDLED;
@@ -504,38 +732,83 @@
case CONNECT:
{
BluetoothDevice device = (BluetoothDevice) message.obj;
- if (mCurrentDevice.equals(device)) {
+ if (device == null) {
+ Log.e(TAG,"device is NULL");
break;
}
-
- broadcastConnectionState(device, BluetoothProfile.STATE_CONNECTING,
- BluetoothProfile.STATE_DISCONNECTED);
- if (!disconnectA2dpNative(getByteAddress(mCurrentDevice))) {
- broadcastConnectionState(device, BluetoothProfile.STATE_DISCONNECTED,
- BluetoothProfile.STATE_CONNECTING);
+ if (mConnectedDevicesList.contains(device)) {
+ Log.e(TAG, "ERROR: Connect received for already connected device, Ignore");
break;
}
-
- synchronized (A2dpStateMachine.this) {
- mTargetDevice = device;
- transitionTo(mPending);
+ if (mConnectedDevicesList.size() >= maxA2dpConnections) {
+ BluetoothDevice disconnectConnectedDevice = null;
+ log( "Reach to max size, disconnect one of them first");
+ disconnectConnectedDevice = mConnectedDevicesList.get(0);
+ broadcastConnectionState(device, BluetoothProfile.STATE_CONNECTING,
+ BluetoothProfile.STATE_DISCONNECTED);
+ if (!disconnectA2dpNative(getByteAddress(disconnectConnectedDevice))) {
+ broadcastConnectionState(device, BluetoothProfile.STATE_DISCONNECTED,
+ BluetoothProfile.STATE_CONNECTING);
+ break;
+ } else {
+ broadcastConnectionState(disconnectConnectedDevice,
+ BluetoothProfile.STATE_DISCONNECTING,
+ BluetoothProfile.STATE_CONNECTED);
+ }
+ synchronized (A2dpStateMachine.this) {
+ mTargetDevice = device;
+ if (maxA2dpConnections == 1) {
+ transitionTo(mPending);
+ } else {
+ mMultiDisconnectDevice = disconnectConnectedDevice;
+ transitionTo(mMultiConnectionPending);
+ }
+ disconnectConnectedDevice = null;
+ }
+ } else if (mConnectedDevicesList.size() < maxA2dpConnections) {
+ broadcastConnectionState(device, BluetoothProfile.STATE_CONNECTING,
+ BluetoothProfile.STATE_DISCONNECTED);
+ if (!connectA2dpNative(getByteAddress(device))) {
+ broadcastConnectionState(device, BluetoothProfile.STATE_DISCONNECTED,
+ BluetoothProfile.STATE_CONNECTING);
+ break;
+ }
+ synchronized (A2dpStateMachine.this) {
+ mTargetDevice = device;
+ // Transtion to MultiConnectionPending state for
+ // Multi A2dp connection
+ transitionTo(mMultiConnectionPending);
+ }
}
+ Message m = obtainMessage(CONNECT_TIMEOUT);
+ m.obj = device;
+ sendMessageDelayed(m, CONNECT_TIMEOUT_SEC);
+
}
break;
case DISCONNECT:
{
BluetoothDevice device = (BluetoothDevice) message.obj;
- if (!mCurrentDevice.equals(device)) {
+ if (!mConnectedDevicesList.contains(device)) {
+ log("device not connected " + device);
break;
}
+ /* do not remove playing device here, wait for
+ * disconnected event from stack to remove from palying
+ * device.*/
broadcastConnectionState(device, BluetoothProfile.STATE_DISCONNECTING,
- BluetoothProfile.STATE_CONNECTED);
+ BluetoothProfile.STATE_CONNECTED);
if (!disconnectA2dpNative(getByteAddress(device))) {
broadcastConnectionState(device, BluetoothProfile.STATE_CONNECTED,
- BluetoothProfile.STATE_DISCONNECTED);
+ BluetoothProfile.STATE_DISCONNECTING);
break;
}
- transitionTo(mPending);
+ if (mConnectedDevicesList.size() > 1) {
+ mMultiDisconnectDevice = device;
+ transitionTo(mMultiConnectionPending);
+ } else {
+ transitionTo(mPending);
+ }
}
break;
case STACK_EVENT:
@@ -547,6 +820,367 @@
case EVENT_TYPE_AUDIO_STATE_CHANGED:
processAudioStateEvent(event.valueInt, event.device);
break;
+ case EVENT_TYPE_RECONFIGURE_A2DP:
+ processReconfigA2dp(event.valueInt, event.device);
+ break;
+ default:
+ loge("Unexpected stack event: " + event.type);
+ break;
+ }
+ break;
+ case CONNECT_TIMEOUT:
+ BluetoothDevice timedOutDevice = getDeviceForMessage(CONNECT_TIMEOUT);
+ if ((mTargetDevice == null) || (timedOutDevice == null)) {
+ loge("CONNECT_TIMEOUT received : targetDevice : " +
+ mTargetDevice + " : timedout device : " + timedOutDevice);
+ } else if(mTargetDevice.equals(timedOutDevice)) {
+ loge("CONNECT_TIMEOUT received : connected devices : " +
+ mConnectedDevicesList.size() +
+ " : timedout device : " + timedOutDevice);
+ broadcastConnectionState(mTargetDevice,
+ BluetoothProfile.STATE_DISCONNECTED,
+ BluetoothProfile.STATE_CONNECTING);
+ mTargetDevice = null;
+ } else {
+ /* Should not hit this
+ */
+ loge("CONNECT_TIMEOUT received : connected devices : " +
+ mConnectedDevicesList.size() +
+ " : targetDevice : " + mTargetDevice +
+ " : timedout device : " + timedOutDevice);
+ }
+ break;
+ default:
+ return NOT_HANDLED;
+ }
+ log("Exit Connected processMessage() ");
+ return retValue;
+ }
+
+ // in Connected state
+ private void processConnectionEvent(int state, BluetoothDevice device) {
+ log( "processConnectionEvent state = " + state + ", device = "
+ + device);
+ switch (state) {
+ case CONNECTION_STATE_DISCONNECTED:
+ if (mConnectedDevicesList.contains(device)) {
+ // if device is playing then remove it from playing
+ // device list.
+ if (mPlayingA2dpDevice.size() != 0 &&
+ mPlayingA2dpDevice.contains(device)) {
+ log ("Playing A2dp Device is disconnected, setting it to null");
+ broadcastAudioState(device,
+ BluetoothA2dp.STATE_NOT_PLAYING,
+ BluetoothA2dp.STATE_PLAYING);
+ mPlayingA2dpDevice.remove(device);
+ }
+ // Reset scan mode if it set due to multicast
+ Log.i(TAG,"getScanMode: " + mAdapter.getScanMode() +
+ " isScanDisabled: " + isScanDisabled);
+ if (mPlayingA2dpDevice.size() <= 1 &&
+ (mAdapter.getScanMode() ==
+ BluetoothAdapter.SCAN_MODE_NONE) &&
+ isScanDisabled) {
+ isScanDisabled = false;
+ AdapterService adapterService =
+ AdapterService.getAdapterService();
+ if (adapterService != null) {
+ adapterService.restoreScanMode();
+ }
+ }
+ broadcastConnectionState(device, BluetoothProfile.STATE_DISCONNECTED,
+ BluetoothProfile.STATE_CONNECTED);
+ synchronized (A2dpStateMachine.this) {
+ mConnectedDevicesList.remove(device);
+ log( "device " + device.getAddress() +
+ " is removed in Connected state");
+ if (mConnectedDevicesList.size() == 0) {
+ mCurrentDevice = null;
+ // cleanup mMultiDisconnectDevice, if incoming
+ // disconnect is processed first.
+ if (mMultiDisconnectDevice != null)
+ mMultiDisconnectDevice = null;
+ transitionTo(mDisconnected);
+ } else {
+ processMultiA2dpDisconnected(device);
+ }
+ }
+ } else if (mTargetDevice != null && mTargetDevice.equals(device)) {
+ broadcastConnectionState(device, BluetoothProfile.STATE_DISCONNECTED,
+ BluetoothProfile.STATE_CONNECTING);
+ synchronized (A2dpStateMachine.this) {
+ mTargetDevice = null;
+ }
+ logi("Disconnected from mTargetDevice in connected state device: " +
+ device);
+ } else {
+ loge("Disconnected from unknown device: " + device);
+ }
+ break;
+ case CONNECTION_STATE_CONNECTING:
+ // 2nd incoming connection
+ log("Incoming connection in Connected State for device " +
+ device);
+ if (mConnectedDevicesList.contains(device)) {
+ log("device is already connected ");
+ mIncomingDevice = null;
+ mTargetDevice = null;
+ break;
+ }
+ // this should be never be case, as we will move to MPC
+ if (mTargetDevice != null) {
+ log("Outgoing initiated before incoming");
+ disconnectA2dpNative(getByteAddress(device));
+ // the other profile connection should be initiated
+ AdapterService adapterService = AdapterService.getAdapterService();
+ if (adapterService != null) {
+ adapterService.connectOtherProfile(device,
+ AdapterService.PROFILE_CONN_REJECTED);
+ }
+ break;
+ }
+ if (okToConnect(device) &&
+ (mConnectedDevicesList.size() < maxA2dpConnections)) {
+ broadcastConnectionState(device, BluetoothProfile.STATE_CONNECTING,
+ BluetoothProfile.STATE_DISCONNECTED);
+ mIncomingDevice = device;
+ transitionTo(mMultiConnectionPending);
+ Message m = obtainMessage(CONNECT_TIMEOUT);
+ m.obj = device;
+ sendMessageDelayed(m, CONNECT_TIMEOUT_SEC);
+ } else {
+ disconnectA2dpNative(getByteAddress(device));
+ // the other profile connection should be initiated
+ AdapterService adapterService = AdapterService.getAdapterService();
+ if (adapterService != null) {
+ adapterService.connectOtherProfile(device,
+ AdapterService.PROFILE_CONN_REJECTED);
+ }
+ }
+
+ break;
+ case CONNECTION_STATE_CONNECTED:
+ // 2nd incoming connection
+ log("Connected event for device " + device);
+ if (mConnectedDevicesList.contains(device)) {
+ log("device already connected ");
+ mIncomingDevice = null;
+ mTargetDevice = null;
+ break;
+ }
+ if (mMultiDisconnectDevice != null) {
+ log("disconnection failed for device");
+ mMultiDisconnectDevice = null;
+ broadcastConnectionState(device, BluetoothProfile.STATE_CONNECTED,
+ BluetoothProfile.STATE_DISCONNECTING);
+ break;
+ }
+ if (mTargetDevice != null && mTargetDevice.equals(device) &&
+ (mConnectedDevicesList.size() < maxA2dpConnections)) {
+ synchronized (A2dpStateMachine.this) {
+ mCurrentDevice = mTargetDevice;
+ mConnectedDevicesList.add(mTargetDevice);
+ mTargetDevice = null;
+ log( "device " + device.getAddress() +
+ " is added in Connected state");
+ }
+ broadcastConnectionState(device, BluetoothProfile.STATE_CONNECTED,
+ BluetoothProfile.STATE_CONNECTING);
+ break;
+ }
+ Log.i(TAG,"okToConnect in connected state " + okToConnect(device));
+ if (okToConnect(device) &&
+ (mConnectedDevicesList.size() < maxA2dpConnections)) {
+ synchronized (A2dpStateMachine.this) {
+ mCurrentDevice = device;
+ mConnectedDevicesList.add(device);
+ mIncomingDevice= null;
+ log( "device " + device.getAddress() +
+ " is added in Connected state");
+ }
+ broadcastConnectionState(device, BluetoothProfile.STATE_CONNECTED,
+ BluetoothProfile.STATE_DISCONNECTED);
+ } else {
+ disconnectA2dpNative(getByteAddress(device));
+ // the other profile connection should be initiated
+ AdapterService adapterService = AdapterService.getAdapterService();
+ if (adapterService != null) {
+ adapterService.connectOtherProfile(device,
+ AdapterService.PROFILE_CONN_REJECTED);
+ }
+ }
+ break;
+ // this case would never happen
+ case CONNECTION_STATE_DISCONNECTING:
+ loge("Disconnecting in Connected State ignore it");
+ break;
+
+ default:
+ loge("Connection State Device: " + device + " bad state: " + state);
+ break;
+ }
+ log("Exit Connected processConnectionEvent() ");
+ }
+ private void processMultiA2dpDisconnected(BluetoothDevice device) {
+ log("Connect state: processMultiA2dpDisconnected");
+ /* Assign the current activedevice again if the disconnected
+ device equals to the current active device */
+ if (mCurrentDevice != null && mCurrentDevice.equals(device)) {
+ int deviceSize = mConnectedDevicesList.size();
+ mCurrentDevice = mConnectedDevicesList.get(deviceSize-1);
+ }
+ log("processMultiA2dpDisconnected, the latest mCurrentDevice is:" +
+ mCurrentDevice);
+ log("Connect state: processMultiA2dpDisconnected ," +
+ "fake broadcasting for mCurrentDevice");
+ broadcastConnectionState(mCurrentDevice, BluetoothProfile.STATE_CONNECTED,
+ BluetoothProfile.STATE_DISCONNECTED);
+ log("Exit Connected processMultiA2dpDisconnected() ");
+ }
+
+ private void processAudioStateEvent(int state, BluetoothDevice device) {
+ log("Enter Connected processAudioStateEvent() ");
+ if (!mConnectedDevicesList.contains(device)) {
+ loge("Audio State Device:" + device + "is not in mConnectedDevicesList" +
+ mCurrentDevice);
+ return;
+ }
+ log("connectedState: processAudioStateEvent state: " + state + " device "
+ + device);
+ log("mPlayingA2dpDevice size is " + mPlayingA2dpDevice.size());
+ log("mConnectedDevicesList size is " +
+ mConnectedDevicesList.contains(device));
+ switch (state) {
+ case AUDIO_STATE_STARTED:
+ synchronized (A2dpStateMachine.this) {
+ if (mConnectedDevicesList.contains(device) &&
+ !(mPlayingA2dpDevice.size() != 0 &&
+ mPlayingA2dpDevice.contains(device))) {
+ /* set scan mode before adding device to mPlayingA2dpDevice
+ * so that scan mode is set to last set mode after multicast
+ * is stopped. */
+ if (mPlayingA2dpDevice.size() == 1) {
+ Log.i(TAG,"setScanMode:SCAN_MODE_NONE");
+ isScanDisabled = true;
+ mAdapter.setScanMode(BluetoothAdapter.SCAN_MODE_NONE);
+ }
+ mPlayingA2dpDevice.add(device);
+ mService.setAvrcpAudioState(BluetoothA2dp.STATE_PLAYING, device);
+ broadcastAudioState(device, BluetoothA2dp.STATE_PLAYING,
+ BluetoothA2dp.STATE_NOT_PLAYING);
+ }
+ /* cancel any discovery if in progress and scan mode to
+ * none when multicast is active.Set flag to reset
+ * scan mode if changed due to multicast.*/
+ if (mPlayingA2dpDevice.size() == 2) {
+ if (mAdapter.isDiscovering()) {
+ mAdapter.cancelDiscovery();
+ }
+ }
+ }
+ break;
+ case AUDIO_STATE_STOPPED:
+ case AUDIO_STATE_REMOTE_SUSPEND:
+ synchronized (A2dpStateMachine.this) {
+ if (mPlayingA2dpDevice.size() != 0 &&
+ mPlayingA2dpDevice.contains(device)) {
+ mPlayingA2dpDevice.remove(device);
+ mService.setAvrcpAudioState(BluetoothA2dp.STATE_NOT_PLAYING, device);
+ broadcastAudioState(device, BluetoothA2dp.STATE_NOT_PLAYING,
+ BluetoothA2dp.STATE_PLAYING);
+ }
+ // Reset scan mode if it set due to multicast
+ Log.i(TAG,"getScanMode: " + mAdapter.getScanMode() +
+ " isScanDisabled: " + isScanDisabled);
+ if (mPlayingA2dpDevice.size() <= 1 &&
+ (mAdapter.getScanMode() == BluetoothAdapter.SCAN_MODE_NONE) &&
+ isScanDisabled) {
+ isScanDisabled = false;
+ AdapterService adapterService = AdapterService.getAdapterService();
+ if (adapterService != null) {
+ adapterService.restoreScanMode();
+ }
+ }
+ }
+ break;
+ default:
+ loge("Audio State Device: " + device + " bad state: " + state);
+ break;
+ }
+ log("Exit Connected processAudioStateEvent() ");
+ }
+ private void processReconfigA2dp(int state, BluetoothDevice device){
+ log("processReconfigA2dp state" + state);
+ switch (state) {
+ case SOFT_HANDOFF:
+ broadcastReconfigureA2dp(device);
+ break;
+ default:
+ loge("Unknown reconfigure state");
+ break;
+ }
+ }
+ }
+ /* Add MultiConnectionPending state when atleast 1 HS is connected
+ and disconnect/connect is initiated for new HS */
+ private class MultiConnectionPending extends State {
+ @Override
+ public void enter() {
+ log("Enter MultiConnectionPending: " + getCurrentMessage().what +
+ ", size: " + mConnectedDevicesList.size());
+ }
+
+ public boolean processMessage(Message message) {
+ log( "MultiConnectionPending process message: " + message.what +
+ ", size: " + mConnectedDevicesList.size());
+ boolean retValue = HANDLED;
+ switch(message.what) {
+ case CONNECT:
+ deferMessage(message);
+ break;
+ case DISCONNECT:
+ BluetoothDevice device = (BluetoothDevice) message.obj;
+ if (mConnectedDevicesList.contains(device) &&
+ mTargetDevice != null && mTargetDevice.equals(device)) {
+ // cancel connection to the mTargetDevice
+ broadcastConnectionState(device,
+ BluetoothProfile.STATE_DISCONNECTED,
+ BluetoothProfile.STATE_CONNECTING);
+ synchronized (A2dpStateMachine.this) {
+ mTargetDevice = null;
+ }
+ } else {
+ deferMessage(message);
+ }
+ break;
+ case CONNECT_TIMEOUT:
+ // This is always for Outgoing connection
+ BluetoothDevice dev = (BluetoothDevice)message.obj;
+ // getByteAddress has no null check
+ log("Timeout for device in MCP " + dev);
+ if ((dev != null) && (mTargetDevice == null ||
+ !mTargetDevice.equals(dev))) {
+ log("Timeout for incoming device " + dev);
+ onConnectionStateChanged(CONNECTION_STATE_DISCONNECTED,
+ getByteAddress(dev));
+ break;
+ }
+ disconnectA2dpNative(getByteAddress(mTargetDevice));
+ onConnectionStateChanged(CONNECTION_STATE_DISCONNECTED,
+ getByteAddress(mTargetDevice));
+ break;
+ case STACK_EVENT:
+ StackEvent event = (StackEvent) message.obj;
+ switch (event.type) {
+ case EVENT_TYPE_CONNECTION_STATE_CHANGED:
+ processConnectionEvent(event.valueInt, event.device);
+ break;
+ case EVENT_TYPE_AUDIO_STATE_CHANGED:
+ processAudioStateEvent(event.valueInt, event.device);
+ break;
+ case EVENT_TYPE_RECONFIGURE_A2DP:
+ processReconfigA2dp(event.valueInt, event.device);
+ break;
default:
loge("Unexpected stack event: " + event.type);
break;
@@ -555,79 +1189,381 @@
default:
return NOT_HANDLED;
}
+ log("Exit MultiConnectionPending processAudioStateEvent() ");
return retValue;
}
- // in Connected state
+ @Override
+ public void exit() {
+ log("Exit MultiConnectionPending: " + getCurrentMessage().what);
+ }
+
+ // in MultiConnectionPending state
private void processConnectionEvent(int state, BluetoothDevice device) {
+ log("processConnectionEvent state = " + state +
+ ", device = " + device);
switch (state) {
case CONNECTION_STATE_DISCONNECTED:
- if (mCurrentDevice.equals(device)) {
- broadcastConnectionState(mCurrentDevice, BluetoothProfile.STATE_DISCONNECTED,
- BluetoothProfile.STATE_CONNECTED);
- synchronized (A2dpStateMachine.this) {
- mCurrentDevice = null;
- transitionTo(mDisconnected);
+ if (mConnectedDevicesList.contains(device)) {
+ if (mPlayingA2dpDevice.size() != 0 &&
+ mPlayingA2dpDevice.contains(device)) {
+ log ("mPlayingA2dpDevice is disconnected, setting it to null");
+ broadcastAudioState(device, BluetoothA2dp.STATE_NOT_PLAYING,
+ BluetoothA2dp.STATE_PLAYING);
+ mPlayingA2dpDevice.remove(device);
+ }
+ // Reset scan mode if it set due to multicast
+ Log.i(TAG,"getScanMode: " + mAdapter.getScanMode() +
+ " isScanDisabled: " + isScanDisabled);
+ if (mPlayingA2dpDevice.size() <= 1 &&
+ (mAdapter.getScanMode() ==
+ BluetoothAdapter.SCAN_MODE_NONE) &&
+ isScanDisabled) {
+ isScanDisabled = false;
+ AdapterService adapterService =
+ AdapterService.getAdapterService();
+ if (adapterService != null) {
+ adapterService.restoreScanMode();
+ }
+ }
+ if (mMultiDisconnectDevice != null &&
+ mMultiDisconnectDevice.equals(device)) {
+ mMultiDisconnectDevice = null;
+ synchronized (A2dpStateMachine.this) {
+ mConnectedDevicesList.remove(device);
+ log( "device " + device.getAddress() +
+ " is removed in MultiConnectionPending state");
+ }
+ broadcastConnectionState(device,
+ BluetoothProfile.STATE_DISCONNECTED,
+ BluetoothProfile.STATE_DISCONNECTING);
+ if (mTargetDevice != null) {
+ if (!connectA2dpNative(getByteAddress(mTargetDevice))) {
+ broadcastConnectionState(mTargetDevice,
+ BluetoothProfile.STATE_DISCONNECTED,
+ BluetoothProfile.STATE_CONNECTING);
+ synchronized (A2dpStateMachine.this) {
+ mTargetDevice = null;
+ if (mConnectedDevicesList.size() == 0) {
+ // Should be not in this state since it has at least
+ // one HF connected in MultiHFPending state
+ log( "Should be not in this state, error handling");
+ transitionTo(mDisconnected);
+
+ } else {
+ processMultiA2dpDisconnected(device);
+ }
+ }
+ }
+ } else {
+ synchronized (A2dpStateMachine.this) {
+ mIncomingDevice = null;
+ if (mConnectedDevicesList.size() == 0) {
+ transitionTo(mDisconnected);
+ } else {
+ processMultiA2dpDisconnected(device);
+ }
+ }
+ }
+ } else {
+ /* HS disconnected, when other HS is connected */
+ synchronized (A2dpStateMachine.this) {
+ mConnectedDevicesList.remove(device);
+
+ log( "device " + device.getAddress() +
+ " is removed in MultiConnectionPending state");
+ }
+ broadcastConnectionState(device,
+ BluetoothProfile.STATE_DISCONNECTED,
+ BluetoothProfile.STATE_CONNECTED);
}
} else if (mTargetDevice != null && mTargetDevice.equals(device)) {
- broadcastConnectionState(device, BluetoothProfile.STATE_DISCONNECTED,
- BluetoothProfile.STATE_CONNECTING);
+ broadcastConnectionState(mTargetDevice,
+ BluetoothProfile.STATE_DISCONNECTED,
+ BluetoothProfile.STATE_CONNECTING);
synchronized (A2dpStateMachine.this) {
mTargetDevice = null;
+ if (mConnectedDevicesList.size() == 0) {
+ transitionTo(mDisconnected);
+ } else {
+ transitionTo(mConnected);
+ }
}
- logi("Disconnected from mTargetDevice in connected state device: " + device);
+ } else if (mIncomingDevice!= null && mIncomingDevice.equals(device)) {
+ // incoming connection failure
+ broadcastConnectionState(mIncomingDevice,
+ BluetoothProfile.STATE_DISCONNECTED,
+ BluetoothProfile.STATE_CONNECTING);
+ synchronized (A2dpStateMachine.this) {
+ mIncomingDevice = null;
+ if (mConnectedDevicesList.size() == 0) {
+ transitionTo(mDisconnected);
+ } else {
+ transitionTo(mConnected);
+ }
+ }
} else {
- loge("Disconnected from unknown device: " + device);
+ // mTargetDevice & mIncomingDevice is made null when
+ // 3rd device is connected, hence move to connected
+ // state if mConnectedDevicesList size is 2 and other
+ // device are null
+ if (mTargetDevice == null && mIncomingDevice == null &&
+ (mConnectedDevicesList.size() ==
+ maxA2dpConnections)) {
+ transitionTo(mConnected);
+ }
+ Log.e(TAG, "Unknown device Disconnected: " + device);
+ }
+
+ break;
+ case CONNECTION_STATE_CONNECTING:
+ if (mTargetDevice != null && mTargetDevice.equals(device)) {
+ log("Outgoing Connection initiated, Ignore it");
+ } else if (mIncomingDevice!= null &&
+ mIncomingDevice.equals(device)) {
+ log("Incoming connection from same device, Ignore it");
+ } else if (mConnectedDevicesList.contains(device)) {
+ log("current device tries to connect back");
+ } else {
+ log("Connection event from new device " + device);
+ if (okToConnect(device) &&
+ (mConnectedDevicesList.size() < maxA2dpConnections)) {
+ broadcastConnectionState(device, BluetoothProfile.STATE_CONNECTING,
+ BluetoothProfile.STATE_DISCONNECTED);
+ mIncomingDevice = device;
+ } else {
+ disconnectA2dpNative(getByteAddress(device));
+ // the other profile connection should be initiated
+ AdapterService adapterService = AdapterService.getAdapterService();
+ if (adapterService != null) {
+ adapterService.connectOtherProfile(device,
+ AdapterService.PROFILE_CONN_REJECTED);
+ }
+ }
}
break;
- default:
- loge("Connection State Device: " + device + " bad state: " + state);
- break;
+ case CONNECTION_STATE_CONNECTED:
+ log("Connected event for device " + device);
+ if (mConnectedDevicesList.size() == maxA2dpConnections) {
+
+ // Unkown device connected, check for target and
+ // incoming devices, make them null and broadcast
+ // disconnection for them
+ if (mTargetDevice != null && mTargetDevice.equals(device)) {
+ log("mTargetDevice, connected list is full");
+ broadcastConnectionState(device, BluetoothProfile.STATE_DISCONNECTED,
+ BluetoothProfile.STATE_CONNECTING);
+ synchronized (A2dpStateMachine.this) {
+ mTargetDevice = null;
+ }
+ disconnectA2dpNative(getByteAddress(device));
+ onConnectionStateChanged(CONNECTION_STATE_DISCONNECTED,
+ getByteAddress(device));
+ }
+ if (mIncomingDevice != null && mIncomingDevice.equals(device)) {
+ log("mIncomingDevice connected list is full");
+ broadcastConnectionState(device, BluetoothProfile.STATE_DISCONNECTED,
+ BluetoothProfile.STATE_CONNECTING);
+ synchronized (A2dpStateMachine.this) {
+ mIncomingDevice = null;
+ }
+
+ disconnectA2dpNative(getByteAddress(device));
+ onConnectionStateChanged(CONNECTION_STATE_DISCONNECTED,
+ getByteAddress(device));
+ }
+ }
+ if (mConnectedDevicesList.contains(device)) {
+ log("Disconnection failed event for device " + device);
+ broadcastConnectionState(device, BluetoothProfile.STATE_CONNECTED,
+ BluetoothProfile.STATE_DISCONNECTING);
+ if (mTargetDevice != null) {
+ broadcastConnectionState(mTargetDevice,
+ BluetoothProfile.STATE_DISCONNECTED,
+ BluetoothProfile.STATE_CONNECTING);
+ }
+ synchronized (A2dpStateMachine.this) {
+ mTargetDevice = null;
+ transitionTo(mConnected);
+ }
+ } else if (mTargetDevice != null && mTargetDevice.equals(device)) {
+ synchronized (A2dpStateMachine.this) {
+ mCurrentDevice = device;
+ mConnectedDevicesList.add(device);
+ log( "device " + device.getAddress() +
+ " is added in MultiConnectionPending state");
+ mTargetDevice = null;
+ transitionTo(mConnected);
+ }
+ broadcastConnectionState(device, BluetoothProfile.STATE_CONNECTED,
+ BluetoothProfile.STATE_CONNECTING);
+ } else if (mIncomingDevice!= null && mIncomingDevice.equals(device)) {
+ synchronized (A2dpStateMachine.this) {
+ mCurrentDevice = device;
+ mConnectedDevicesList.add(device);
+ log( "device " + device.getAddress() +
+ " is added in MultiConnectionPending state");
+ mIncomingDevice = null;
+ transitionTo(mConnected);
+ }
+ broadcastConnectionState(device, BluetoothProfile.STATE_CONNECTED,
+ BluetoothProfile.STATE_CONNECTING);
+
+ } else {
+ log("Unknown Device connected");
+ if (okToConnect(device) &&
+ (mConnectedDevicesList.size() < maxA2dpConnections)) {
+ mCurrentDevice = device;
+ mConnectedDevicesList.add(device);
+ log( "device " + device.getAddress() +
+ " is added in MultiConnectionPending state");
+ broadcastConnectionState(device, BluetoothProfile.STATE_CONNECTED,
+ BluetoothProfile.STATE_DISCONNECTED);
+
+ } else {
+ disconnectA2dpNative(getByteAddress(device));
+ }
+
+ }
+ break;
+ case CONNECTION_STATE_DISCONNECTING:
+ if (mConnectedDevicesList.contains(device)) {
+ // we already broadcasted the intent, doing nothing here
+ log("stack is disconnecting mCurrentDevice");
+ } else if (mTargetDevice != null && mTargetDevice.equals(device)) {
+ loge("TargetDevice is getting disconnected");
+ } else if (mIncomingDevice != null && mIncomingDevice.equals(device)) {
+ loge("mIncomingDevice is getting disconnected");
+ } else {
+ loge("Disconnecting unknow device: " + device);
+ }
+ break;
+ default:
+ loge("Incorrect state: " + state);
+ break;
+
}
+ log("Exit MultiConnectionPending processConnectionEvent() ");
+
}
+
private void processAudioStateEvent(int state, BluetoothDevice device) {
- if (!mCurrentDevice.equals(device)) {
- loge("Audio State Device:" + device + "is different from ConnectedDevice:" +
- mCurrentDevice);
+ log("Enter MultiConnectionPending processAudioStateEvent() ");
+ if (!mConnectedDevicesList.contains(device)) {
+ loge("Audio State Device:" + device + "is not in mConnectedDevicesList" +
+ mCurrentDevice);
return;
}
+ log("MultiPendingState: processAudioStateEvent state: " + state + " device "
+ + device.getName());
+ log("mPlayingA2dpDevice size is " + mPlayingA2dpDevice.size());
+ log("mConnectedDevicesList size is " + mConnectedDevicesList.size());
switch (state) {
case AUDIO_STATE_STARTED:
- if (mPlayingA2dpDevice == null) {
- mPlayingA2dpDevice = device;
- mService.setAvrcpAudioState(BluetoothA2dp.STATE_PLAYING);
- broadcastAudioState(device, BluetoothA2dp.STATE_PLAYING,
- BluetoothA2dp.STATE_NOT_PLAYING);
+ synchronized (A2dpStateMachine.this) {
+ if (mConnectedDevicesList.contains(device) &&
+ !(mPlayingA2dpDevice.size()!= 0 &&
+ mPlayingA2dpDevice.contains(device))) {
+ /* set scan mode before adding device to mPlayingA2dpDevice
+ * so that scan mode is set to last set mode after multicast
+ * is stopped. */
+ if (mPlayingA2dpDevice.size() == 1) {
+ Log.i(TAG,"setScanMode:SCAN_MODE_NONE");
+ isScanDisabled = true;
+ mAdapter.setScanMode(BluetoothAdapter.SCAN_MODE_NONE);
+ }
+ mPlayingA2dpDevice.add(device);
+ mService.setAvrcpAudioState(BluetoothA2dp.STATE_PLAYING, device);
+ broadcastAudioState(device, BluetoothA2dp.STATE_PLAYING,
+ BluetoothA2dp.STATE_NOT_PLAYING);
+ }
+ /* cancel any discovery if in progress and scan mode to
+ * none when multicast is active.Set flag to reset
+ * scan mode if changed due to multicast.*/
+ if (mPlayingA2dpDevice.size() == 2) {
+ if (mAdapter.isDiscovering()) {
+ mAdapter.cancelDiscovery();
+ }
+ }
}
break;
case AUDIO_STATE_REMOTE_SUSPEND:
case AUDIO_STATE_STOPPED:
- if (mPlayingA2dpDevice != null) {
- mPlayingA2dpDevice = null;
- mService.setAvrcpAudioState(BluetoothA2dp.STATE_NOT_PLAYING);
- broadcastAudioState(device, BluetoothA2dp.STATE_NOT_PLAYING,
- BluetoothA2dp.STATE_PLAYING);
+ synchronized (A2dpStateMachine.this) {
+ if (mPlayingA2dpDevice.size() != 0 &&
+ mPlayingA2dpDevice.contains(device)) {
+ mPlayingA2dpDevice.remove(device);
+ mService.setAvrcpAudioState(BluetoothA2dp.STATE_NOT_PLAYING, device);
+ broadcastAudioState(device, BluetoothA2dp.STATE_NOT_PLAYING,
+ BluetoothA2dp.STATE_PLAYING);
+ }
+ // Reset scan mode if it set due to multicast
+ Log.i(TAG,"getScanMode: " + mAdapter.getScanMode() +
+ " isScanDisabled: " + isScanDisabled);
+ if (mPlayingA2dpDevice.size() <= 1 &&
+ (mAdapter.getScanMode() == BluetoothAdapter.SCAN_MODE_NONE) &&
+ isScanDisabled) {
+ isScanDisabled = false;
+ AdapterService adapterService = AdapterService.getAdapterService();
+ if (adapterService != null) {
+ adapterService.restoreScanMode();
+ }
+ }
}
break;
default:
loge("Audio State Device: " + device + " bad state: " + state);
break;
}
+ log("Exit MultiConnectionPending processAudioStateEvent() ");
+ }
+
+ private void processReconfigA2dp(int state, BluetoothDevice device){
+ log("processReconfigA2dp state" + state);
+ switch (state) {
+ case SOFT_HANDOFF:
+ broadcastReconfigureA2dp(device);
+ break;
+ default:
+ loge("Unknown reconfigure state");
+ break;
+ }
+ }
+
+ private void processMultiA2dpDisconnected(BluetoothDevice device) {
+ log("Enter MultiConnectionPending processMultiA2dpDisconnected() ");
+ log("processMultiA2dpDisconnected state: processMultiA2dpDisconnected");
+
+ if (mCurrentDevice != null && mCurrentDevice.equals(device)) {
+ int deviceSize = mConnectedDevicesList.size();
+ mCurrentDevice = mConnectedDevicesList.get(deviceSize-1);
+ }
+ transitionTo(mConnected);
+ log("processMultiA2dpDisconnected , the latest mCurrentDevice is:"
+ + mCurrentDevice);
+ log("MultiA2dpPending state: processMultiA2dpDisconnected ," +
+ "fake broadcasting for mCurrentDevice");
+ broadcastConnectionState(mCurrentDevice, BluetoothProfile.STATE_CONNECTED,
+ BluetoothProfile.STATE_DISCONNECTED);
+ log("Exit MultiConnectionPending processMultiA2dpDisconnected() ");
}
}
int getConnectionState(BluetoothDevice device) {
+ log("Enter getConnectionState() ");
if (getCurrentState() == mDisconnected) {
+ log( "currentState is Disconnected");
return BluetoothProfile.STATE_DISCONNECTED;
}
synchronized (this) {
IState currentState = getCurrentState();
+ log( "currentState = " + currentState);
if (currentState == mPending) {
if ((mTargetDevice != null) && mTargetDevice.equals(device)) {
return BluetoothProfile.STATE_CONNECTING;
}
- if ((mCurrentDevice != null) && mCurrentDevice.equals(device)) {
+ if (mConnectedDevicesList.contains(device)) {
return BluetoothProfile.STATE_DISCONNECTING;
}
if ((mIncomingDevice != null) && mIncomingDevice.equals(device)) {
@@ -636,8 +1572,26 @@
return BluetoothProfile.STATE_DISCONNECTED;
}
+ if (currentState == mMultiConnectionPending) {
+ if ((mTargetDevice != null) && mTargetDevice.equals(device)) {
+ return BluetoothProfile.STATE_CONNECTING;
+ }
+ if ((mIncomingDevice != null) && mIncomingDevice.equals(device)) {
+ return BluetoothProfile.STATE_CONNECTING; // incoming connection
+ }
+ if (mConnectedDevicesList.contains(device)) {
+ if ((mMultiDisconnectDevice != null) &&
+ (!mMultiDisconnectDevice.equals(device))) {
+ // The device is still connected
+ return BluetoothProfile.STATE_CONNECTED;
+ }
+ return BluetoothProfile.STATE_DISCONNECTING;
+ }
+ return BluetoothProfile.STATE_DISCONNECTED;
+ }
+
if (currentState == mConnected) {
- if (mCurrentDevice.equals(device)) {
+ if (mConnectedDevicesList.contains(device)) {
return BluetoothProfile.STATE_CONNECTED;
}
return BluetoothProfile.STATE_DISCONNECTED;
@@ -649,25 +1603,43 @@
}
List<BluetoothDevice> getConnectedDevices() {
+ log("Enter getConnectedDevices() ");
List<BluetoothDevice> devices = new ArrayList<BluetoothDevice>();
+ Log.i(TAG,"mConnectedDevicesList size is " +
+ mConnectedDevicesList.size());
synchronized(this) {
- if (getCurrentState() == mConnected) {
- devices.add(mCurrentDevice);
+ /* If connected and mCurrentDevice is not null*/
+ for (int i = 0; i < mConnectedDevicesList.size(); i++) {
+ devices.add(mConnectedDevicesList.get(i));
}
}
+ log("Exit getConnectedDevices() ");
return devices;
}
boolean isPlaying(BluetoothDevice device) {
synchronized(this) {
- if (device.equals(mPlayingA2dpDevice)) {
+ if (mPlayingA2dpDevice.contains(device)) {
return true;
}
}
return false;
}
+ public List<BluetoothDevice> getPlayingDevice() {
+ return mPlayingA2dpDevice;
+ }
+
+ public boolean isMulticastEnabled() {
+ return isMultiCastEnabled;
+ }
+
+ public boolean isMulticastFeatureEnabled() {
+ return isMultiCastFeatureEnabled;
+ }
+
boolean okToConnect(BluetoothDevice device) {
+ log("Enter okToConnect() ");
AdapterService adapterService = AdapterService.getAdapterService();
int priority = mService.getPriority(device);
boolean ret = false;
@@ -685,10 +1657,12 @@
(device.getBondState() != BluetoothDevice.BOND_NONE))){
ret= true;
}
+ log("Exit okToConnect() ");
return ret;
}
synchronized List<BluetoothDevice> getDevicesMatchingConnectionStates(int[] states) {
+ log("Enter getDevicesMatchingConnectionStates() ");
List<BluetoothDevice> deviceList = new ArrayList<BluetoothDevice>();
Set<BluetoothDevice> bondedDevices = mAdapter.getBondedDevices();
int connectionState;
@@ -705,16 +1679,45 @@
}
}
}
+ log("Exit getDevicesMatchingConnectionStates() ");
return deviceList;
}
+ private BluetoothDevice getDeviceForMessage(int what) {
+ log("Enter getDeviceForMessage() ");
+ if (what == CONNECT_TIMEOUT) {
+ log("getDeviceForMessage: returning mTargetDevice for what=" + what);
+ return mTargetDevice;
+ }
+ if (mConnectedDevicesList.size() == 0) {
+ log("getDeviceForMessage: No connected device. what=" + what);
+ return null;
+ }
+ for (BluetoothDevice device : mConnectedDevicesList){
+ if (getHandler().hasMessages(what, device)) {
+ log("getDeviceForMessage: returning " + device + "for what " +
+ what);
+ return device;
+ }
+ }
+ log("getDeviceForMessage: No matching device for " + what + ". Returning null");
+ log("Exit getDeviceForMessage() ");
+ return null;
+ }
+
// This method does not check for error conditon (newState == prevState)
private void broadcastConnectionState(BluetoothDevice device, int newState, int prevState) {
+ log("Enter broadcastConnectionState() ");
int delay = mAudioManager.setBluetoothA2dpDeviceConnectionState(device, newState,
BluetoothProfile.A2DP);
+ Log.i(TAG,"connectoin state change " + device + " state " + newState);
+ if (newState == BluetoothProfile.STATE_DISCONNECTING ||
+ newState == BluetoothProfile.STATE_CONNECTING) {
+ delay = 0;
+ }
mWakeLock.acquire();
mIntentBroadcastHandler.sendMessageDelayed(mIntentBroadcastHandler.obtainMessage(
MSG_CONNECTION_STATE_CHANGED,
@@ -722,9 +1725,11 @@
newState,
device),
delay);
+ log("Exit broadcastConnectionState() ");
}
private void broadcastAudioState(BluetoothDevice device, int state, int prevState) {
+ log("Enter broadcastAudioState() ");
Intent intent = new Intent(BluetoothA2dp.ACTION_PLAYING_STATE_CHANGED);
intent.putExtra(BluetoothDevice.EXTRA_DEVICE, device);
intent.putExtra(BluetoothProfile.EXTRA_PREVIOUS_STATE, prevState);
@@ -733,6 +1738,12 @@
mContext.sendBroadcast(intent, A2dpService.BLUETOOTH_PERM);
log("A2DP Playing state : device: " + device + " State:" + prevState + "->" + state);
+ log("Exit broadcastAudioState() ");
+ }
+
+ private void broadcastReconfigureA2dp(BluetoothDevice device) {
+ log("broadcastReconfigureA2dp");
+ mAudioManager.setParameters("reconfigA2dp=true");
}
private byte[] getByteAddress(BluetoothDevice device) {
@@ -740,18 +1751,58 @@
}
private void onConnectionStateChanged(int state, byte[] address) {
+ log("Enter onConnectionStateChanged() ");
StackEvent event = new StackEvent(EVENT_TYPE_CONNECTION_STATE_CHANGED);
event.valueInt = state;
event.device = getDevice(address);
sendMessage(STACK_EVENT, event);
+ log("Exit onConnectionStateChanged() ");
}
private void onAudioStateChanged(int state, byte[] address) {
+ log("Enter onAudioStateChanged() ");
StackEvent event = new StackEvent(EVENT_TYPE_AUDIO_STATE_CHANGED);
event.valueInt = state;
event.device = getDevice(address);
sendMessage(STACK_EVENT, event);
+ log("Exit onAudioStateChanged() ");
}
+
+ private void onCheckConnectionPriority(byte[] address) {
+ log("Enter onCheckConnectionPriority() ");
+ BluetoothDevice device = getDevice(address);
+ logw(" device " + device + " okToConnect " + okToConnect(device));
+ if (okToConnect(device)) {
+ // if connection is allowed then go ahead and connect
+ allowConnectionNative(IS_VALID_DEVICE, getByteAddress(device));
+ } else {
+ // if connection is not allowed DO NOT CONNECT
+ allowConnectionNative(IS_INVALID_DEVICE, getByteAddress(device));
+ }
+ log("Exit onCheckConnectionPriority() ");
+ }
+
+ private void onMulticastStateChanged(int state) {
+ log("Enter onMulticastStateChanged() ");
+ if (state == ENABLE_MULTICAST) {
+ Log.i(TAG,"A2dp Multicast is Enabled");
+ isMultiCastEnabled = true;
+ } else {
+ Log.i(TAG,"A2dp Multicast is Disabled");
+ isMultiCastEnabled = false;
+ }
+ log("Exit onMulticastStateChanged() ");
+ }
+
+ private void onReconfigA2dpTriggered(int reason, byte[] address) {
+ BluetoothDevice device = getDevice(address);
+ Log.i(TAG,"onSoftHandoffTriggered to device " + device);
+ StackEvent event = new StackEvent(EVENT_TYPE_RECONFIGURE_A2DP);
+ event.valueInt = reason;//SOFT_HANDOFF;
+ event.device = device;
+ sendMessage(STACK_EVENT,event);
+ }
+
private BluetoothDevice getDevice(byte[] address) {
return mAdapter.getRemoteDevice(Utils.getAddressStringFromByte(address));
}
@@ -769,6 +1820,7 @@
private class IntentBroadcastHandler extends Handler {
private void onConnectionStateChanged(BluetoothDevice device, int prevState, int state) {
+ log("Enter onConnectionStateChanged() ");
Intent intent = new Intent(BluetoothA2dp.ACTION_CONNECTION_STATE_CHANGED);
intent.putExtra(BluetoothProfile.EXTRA_PREVIOUS_STATE, prevState);
intent.putExtra(BluetoothProfile.EXTRA_STATE, state);
@@ -777,16 +1829,19 @@
mContext.sendBroadcast(intent, ProfileService.BLUETOOTH_PERM);
log("Connection state " + device + ": " + prevState + "->" + state);
mService.notifyProfileConnectionStateChanged(device, BluetoothProfile.A2DP, state, prevState);
+ log("Exit onConnectionStateChanged() ");
}
@Override
public void handleMessage(Message msg) {
+ log("Enter handleMessage() ");
switch (msg.what) {
case MSG_CONNECTION_STATE_CHANGED:
onConnectionStateChanged((BluetoothDevice) msg.obj, msg.arg1, msg.arg2);
mWakeLock.release();
break;
}
+ log("Exit handleMessage() ");
}
}
@@ -802,6 +1857,10 @@
final private static int EVENT_TYPE_NONE = 0;
final private static int EVENT_TYPE_CONNECTION_STATE_CHANGED = 1;
final private static int EVENT_TYPE_AUDIO_STATE_CHANGED = 2;
+ final private static int EVENT_TYPE_RECONFIGURE_A2DP = 3;
+
+ // Reason to Reconfig A2dp
+ final private static int SOFT_HANDOFF = 1;
// Do not modify without updating the HAL bt_av.h files.
@@ -817,8 +1876,10 @@
final static int AUDIO_STATE_STARTED = 2;
private native static void classInitNative();
- private native void initNative();
+ private native void initNative(int maxA2dpConnectionsAllowed,
+ int multiCastState, String offload_cap);
private native void cleanupNative();
private native boolean connectA2dpNative(byte[] address);
private native boolean disconnectA2dpNative(byte[] address);
+ private native void allowConnectionNative(int isValid, byte[] address);
}
diff --git a/src/com/android/bluetooth/a2dpsink/A2dpSinkService.java b/src/com/android/bluetooth/a2dpsink/A2dpSinkService.java
index 82d6d34..4481864 100644
--- a/src/com/android/bluetooth/a2dpsink/A2dpSinkService.java
+++ b/src/com/android/bluetooth/a2dpsink/A2dpSinkService.java
@@ -59,6 +59,7 @@
startService(startIntent);
mStateMachine = A2dpSinkStateMachine.make(this, this);
setA2dpSinkService(this);
+ if (DBG) Log.d(TAG, "Exit start");
return true;
}
@@ -69,14 +70,17 @@
mStateMachine.doQuit();
Intent stopIntent = new Intent(this, A2dpMediaBrowserService.class);
stopService(stopIntent);
+ if (DBG) Log.d(TAG, "Exit stop");
return true;
}
protected boolean cleanup() {
+ if (DBG) Log.d(TAG, "Enter cleanup");
if (mStateMachine!= null) {
mStateMachine.cleanup();
}
clearA2dpSinkService();
+ if (DBG) Log.d(TAG, "Exit cleanup");
return true;
}
@@ -117,6 +121,7 @@
}
public boolean connect(BluetoothDevice device) {
+ if (DBG) Log.d(TAG, "Enter connect");
enforceCallingOrSelfPermission(BLUETOOTH_ADMIN_PERM,
"Need BLUETOOTH ADMIN permission");
@@ -127,10 +132,12 @@
}
mStateMachine.sendMessage(A2dpSinkStateMachine.CONNECT, device);
+ if (DBG) Log.d(TAG, "Exit connect");
return true;
}
boolean disconnect(BluetoothDevice device) {
+ if (DBG) Log.d(TAG, "Enter disconnect");
enforceCallingOrSelfPermission(BLUETOOTH_ADMIN_PERM,
"Need BLUETOOTH ADMIN permission");
int connectionState = mStateMachine.getConnectionState(device);
@@ -140,6 +147,7 @@
}
mStateMachine.sendMessage(A2dpSinkStateMachine.DISCONNECT, device);
+ if (DBG) Log.d(TAG, "Exit disconnect");
return true;
}
@@ -148,7 +156,7 @@
return mStateMachine.getConnectedDevices();
}
- List<BluetoothDevice> getDevicesMatchingConnectionStates(int[] states) {
+ public List<BluetoothDevice> getDevicesMatchingConnectionStates(int[] states) {
enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission");
return mStateMachine.getDevicesMatchingConnectionStates(states);
}
@@ -159,6 +167,7 @@
}
public boolean setPriority(BluetoothDevice device, int priority) {
+ if (DBG) Log.d(TAG, "Enter setPriority");
enforceCallingOrSelfPermission(BLUETOOTH_ADMIN_PERM,
"Need BLUETOOTH_ADMIN permission");
Settings.Global.putInt(getContentResolver(),
@@ -167,15 +176,18 @@
if (DBG) {
Log.d(TAG,"Saved priority " + device + " = " + priority);
}
+ if (DBG) Log.d(TAG, "Exit setPriority");
return true;
}
public int getPriority(BluetoothDevice device) {
+ if (DBG) Log.d(TAG, "Enter getPriority");
enforceCallingOrSelfPermission(BLUETOOTH_ADMIN_PERM,
"Need BLUETOOTH_ADMIN permission");
int priority = Settings.Global.getInt(getContentResolver(),
Settings.Global.getBluetoothA2dpSrcPriorityKey(device.getAddress()),
BluetoothProfile.PRIORITY_UNDEFINED);
+ if (DBG) Log.d(TAG, "Exit getPriority");
return priority;
}
@@ -188,6 +200,7 @@
* component will take the focus away but also notify the stack to throw away incoming data.
*/
public void informAvrcpPassThroughCmd(BluetoothDevice device, int keyCode, int keyState) {
+ if (DBG) Log.d(TAG, "Enter informAvrcpPassThroughCmd");
if (mStateMachine != null) {
if (keyCode == BluetoothAvrcpController.PASS_THRU_CMD_ID_PLAY &&
keyState == BluetoothAvrcpController.KEY_STATE_RELEASED) {
@@ -198,6 +211,7 @@
mStateMachine.sendMessage(A2dpSinkStateMachine.EVENT_AVRCP_CT_PAUSE);
}
}
+ if (DBG) Log.d(TAG, "Exit informAvrcpPassThroughCmd");
}
/**
@@ -208,6 +222,7 @@
* stopping playback.
*/
public void informTGStatePlaying(BluetoothDevice device, boolean isPlaying) {
+ if (DBG) Log.d(TAG, "Enter informTGStatePlaying");
if (mStateMachine != null) {
if (!isPlaying) {
mStateMachine.sendMessage(A2dpSinkStateMachine.EVENT_AVRCP_TG_PAUSE);
@@ -215,6 +230,7 @@
mStateMachine.sendMessage(A2dpSinkStateMachine.EVENT_AVRCP_TG_PLAY);
}
}
+ if (DBG) Log.d(TAG, "Exit informTGStatePlaying");
}
synchronized boolean isA2dpPlaying(BluetoothDevice device) {
diff --git a/src/com/android/bluetooth/a2dpsink/A2dpSinkStateMachine.java b/src/com/android/bluetooth/a2dpsink/A2dpSinkStateMachine.java
index 07acd6f..a08388f 100644
--- a/src/com/android/bluetooth/a2dpsink/A2dpSinkStateMachine.java
+++ b/src/com/android/bluetooth/a2dpsink/A2dpSinkStateMachine.java
@@ -1,4 +1,7 @@
/*
+ * Copyright (C) 2013-2014, The Linux Foundation. All rights reserved.
+ * Not a Contribution.
+ *
* Copyright (C) 2014 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
@@ -96,8 +99,11 @@
private IntentBroadcastHandler mIntentBroadcastHandler;
private static final int MSG_CONNECTION_STATE_CHANGED = 0;
+ private static A2dpSinkStreamingStateMachine mStreaming;
private final Object mLockForPatch = new Object();
+ private static final int maxA2dpSinkConnections = 1;
+ private static final int multiCastState = 0;
// mCurrentDevice is the device connected before the state changes
// mTargetDevice is the device to be connected
@@ -139,7 +145,8 @@
mContext = context;
mAdapter = BluetoothAdapter.getDefaultAdapter();
- initNative();
+ // for sink soft handsoff and multicast are disabled
+ initNative(maxA2dpSinkConnections, multiCastState);
mDisconnected = new Disconnected();
mPending = new Pending();
@@ -160,10 +167,12 @@
Log.d("A2dpSinkStateMachine", "make");
A2dpSinkStateMachine a2dpSm = new A2dpSinkStateMachine(svc, context);
a2dpSm.start();
+ mStreaming = A2dpSinkStreamingStateMachine.make(a2dpSm, context);
return a2dpSm;
}
public void doQuit() {
+ mStreaming.doQuit();
quitNow();
}
@@ -244,6 +253,7 @@
// in Disconnected state
private void processConnectionEvent(int state, BluetoothDevice device) {
+ log("Disconnected: processConnectionEvent" + state);
switch (state) {
case CONNECTION_STATE_DISCONNECTED:
logw("Ignore HF DISCONNECTED event, device: " + device);
@@ -264,8 +274,8 @@
// the other profile connection should be initiated
AdapterService adapterService = AdapterService.getAdapterService();
if (adapterService != null) {
- adapterService.connectOtherProfile(device,
- AdapterService.PROFILE_CONN_REJECTED);
+ adapterService.connectOtherClientProfile(device,
+ AdapterService.PROFILE_CONN_REJECTED);
}
}
break;
@@ -286,8 +296,8 @@
// the other profile connection should be initiated
AdapterService adapterService = AdapterService.getAdapterService();
if (adapterService != null) {
- adapterService.connectOtherProfile(device,
- AdapterService.PROFILE_CONN_REJECTED);
+ adapterService.connectOtherClientProfile(device,
+ AdapterService.PROFILE_CONN_REJECTED);
}
}
break;
@@ -388,7 +398,7 @@
}
} else if (mTargetDevice != null && mTargetDevice.equals(device)) {
// outgoing connection failed
- broadcastConnectionState(mTargetDevice, BluetoothProfile.STATE_CONNECTED,
+ broadcastConnectionState(mTargetDevice, BluetoothProfile.STATE_DISCONNECTED,
BluetoothProfile.STATE_CONNECTING);
synchronized (A2dpSinkStateMachine.this) {
mTargetDevice = null;
@@ -492,14 +502,12 @@
}
private class Connected extends State {
- private A2dpSinkStreamingStateMachine mStreaming;
@Override
public void enter() {
log("Enter Connected: " + getCurrentMessage().what);
// Upon connected, the audio starts out as stopped
broadcastAudioState(mCurrentDevice, BluetoothA2dpSink.STATE_NOT_PLAYING,
BluetoothA2dpSink.STATE_PLAYING);
- mStreaming = A2dpSinkStreamingStateMachine.make(A2dpSinkStateMachine.this, mContext);
}
@Override
@@ -571,17 +579,10 @@
}
break;
- case EVENT_AVRCP_CT_PLAY:
case EVENT_AVRCP_TG_PLAY:
mStreaming.sendMessage(A2dpSinkStreamingStateMachine.ACT_PLAY);
break;
- case EVENT_AVRCP_CT_PAUSE:
- case EVENT_AVRCP_TG_PAUSE:
- mStreaming.sendMessage(A2dpSinkStreamingStateMachine.ACT_PAUSE);
- break;
-
-
default:
return NOT_HANDLED;
}
@@ -590,6 +591,7 @@
// in Connected state
private void processConnectionEvent(int state, BluetoothDevice device) {
+ log("Connected: processConnectionEvent in connected state " + state);
switch (state) {
case CONNECTION_STATE_DISCONNECTED:
mAudioConfigs.remove(device);
@@ -616,6 +618,7 @@
}
private void processAudioStateEvent(int state, BluetoothDevice device) {
+ log("processAudioStateEvent state " + state);
if (!mCurrentDevice.equals(device)) {
loge("Audio State Device:" + device + "is different from ConnectedDevice:" +
mCurrentDevice);
@@ -624,11 +627,19 @@
log(" processAudioStateEvent in state " + state);
switch (state) {
case AUDIO_STATE_STARTED:
+ if (mPlayingDevice == null) {
+ mPlayingDevice = device;
+ }
mStreaming.sendMessage(A2dpSinkStreamingStateMachine.SRC_STR_START);
+ broadcastAudioState(device, BluetoothA2dpSink.STATE_PLAYING,
+ BluetoothA2dpSink.STATE_NOT_PLAYING);
break;
case AUDIO_STATE_REMOTE_SUSPEND:
case AUDIO_STATE_STOPPED:
+ mPlayingDevice = null;
mStreaming.sendMessage(A2dpSinkStreamingStateMachine.SRC_STR_STOP);
+ broadcastAudioState(device, BluetoothA2dpSink.STATE_NOT_PLAYING,
+ BluetoothA2dpSink.STATE_PLAYING);
break;
default:
loge("Audio State Device: " + device + " bad state: " + state);
@@ -644,6 +655,7 @@
}
int getConnectionState(BluetoothDevice device) {
+ log("Enter getConnectionState");
if (getCurrentState() == mDisconnected) {
return BluetoothProfile.STATE_DISCONNECTED;
}
@@ -700,13 +712,25 @@
boolean okToConnect(BluetoothDevice device) {
AdapterService adapterService = AdapterService.getAdapterService();
- boolean ret = true;
+ int priority = mService.getPriority(device);
+ boolean ret = false;
+
+ log("Enter okToConnect");
//check if this is an incoming connection in Quiet mode.
if((adapterService == null) ||
((adapterService.isQuietModeEnabled() == true) &&
(mTargetDevice == null))){
ret = false;
}
+ // check priority and accept or reject the connection. if priority is undefined
+ // it is likely that our SDP has not completed and peer is initiating the
+ // connection. Allow this connection, provided the device is bonded
+ else if((BluetoothProfile.PRIORITY_OFF < priority) ||
+ ((BluetoothProfile.PRIORITY_UNDEFINED == priority) &&
+ (device.getBondState() != BluetoothDevice.BOND_NONE))){
+ ret= true;
+ }
+ log("Exit okToConnect");
return ret;
}
@@ -870,7 +894,8 @@
final static int AUDIO_STATE_STARTED = 2;
private native static void classInitNative();
- private native void initNative();
+ private native void initNative(int maxA2dpConnectionsAllowed,
+ int multiCastState);
private native void cleanupNative();
private native boolean connectA2dpNative(byte[] address);
private native boolean disconnectA2dpNative(byte[] address);
diff --git a/src/com/android/bluetooth/a2dpsink/A2dpSinkStreamingStateMachine.java b/src/com/android/bluetooth/a2dpsink/A2dpSinkStreamingStateMachine.java
index fb62c95..58c6ae4 100644
--- a/src/com/android/bluetooth/a2dpsink/A2dpSinkStreamingStateMachine.java
+++ b/src/com/android/bluetooth/a2dpsink/A2dpSinkStreamingStateMachine.java
@@ -88,10 +88,10 @@
private static final int DEFAULT_DUCK_PERCENT = 25;
// Streaming states (see the description above).
- private SRC_F_ACT_F mSrcFActF;
- private SRC_F_ACT_T mSrcFActT;
- private SRC_T_ACT_F mSrcTActF;
- private SRC_T_ACT_T mSrcTActT;
+ private Idle mIdle;
+ private Focus_Granted mFGranted;
+ private Focus_Transient mFTransient;
+ private Focus_Loss mFLoss;
// Transitions.
public static final int SRC_STR_START = 0;
@@ -134,17 +134,17 @@
mContext = context;
mAudioManager = (AudioManager) context.getSystemService(Context.AUDIO_SERVICE);
- mSrcFActF = new SRC_F_ACT_F();
- mSrcFActT = new SRC_F_ACT_T();
- mSrcTActF = new SRC_T_ACT_F();
- mSrcTActT = new SRC_T_ACT_T();
+ mIdle = new Idle();
+ mFTransient = new Focus_Transient();
+ mFLoss = new Focus_Loss();
+ mFGranted = new Focus_Granted();
// States are independent of each other. We simply use transitionTo.
- addState(mSrcFActF);
- addState(mSrcFActT);
- addState(mSrcTActF);
- addState(mSrcTActT);
- setInitialState(mSrcFActF);
+ addState(mIdle);
+ addState(mFTransient);
+ addState(mFLoss);
+ addState(mFGranted);
+ setInitialState(mIdle);
}
@@ -159,13 +159,36 @@
return a2dpStrStateMachine;
}
+ public void doQuit() {
+ if ((getCurrentState() == mFTransient) ||
+ (getCurrentState() == mFGranted)) {
+ abandonAudioFocus();
+ }
+ quitNow();
+ }
+
/**
* Utility functions that can be used by all states.
*/
- private boolean requestAudioFocus() {
- return (mAudioManager.requestAudioFocus(
- mAudioFocusListener, AudioManager.STREAM_MUSIC, AudioManager.AUDIOFOCUS_GAIN) ==
- AudioManager.AUDIOFOCUS_REQUEST_GRANTED);
+
+ private int abandonAudioFocus() {
+ Log.d(TAG, "abandonAudioFocus");
+ mCurrentAudioFocus = AudioManager.AUDIOFOCUS_LOSS;
+ if (mAudioManager != null)
+ return mAudioManager.abandonAudioFocus(mAudioFocusListener);
+ else
+ return AudioManager.AUDIOFOCUS_REQUEST_GRANTED;
+ }
+
+ private int requestAudioFocus() {
+ Log.d(TAG, "requestAudioFocus");
+ if (mCurrentAudioFocus == AudioManager.AUDIOFOCUS_GAIN)
+ return AudioManager.AUDIOFOCUS_REQUEST_GRANTED;
+ int ret = mAudioManager.requestAudioFocus(
+ mAudioFocusListener, AudioManager.STREAM_MUSIC, AudioManager.AUDIOFOCUS_GAIN);
+ if (ret == AudioManager.AUDIOFOCUS_REQUEST_GRANTED)
+ mCurrentAudioFocus = AudioManager.AUDIOFOCUS_GAIN;
+ return ret;
}
private void startAvrcpUpdates() {
@@ -257,13 +280,15 @@
mA2dpSinkSm.informAudioTrackGainNative(gain);
}
- private class SRC_F_ACT_F extends State {
- private static final String STATE_TAG = A2dpSinkStreamingStateMachine.TAG + ".SRC_F_ACT_F";
+ private class Idle extends State {
+ private static final String STATE_TAG = A2dpSinkStreamingStateMachine.TAG + ".Idle";
@Override
public void enter() {
if (DBG) {
Log.d(STATE_TAG, "Enter: " + getCurrentMessage().what);
}
+ stopFluorideStreaming();
+ stopAvrcpUpdates();
}
@Override
@@ -273,20 +298,12 @@
}
switch (message.what) {
case SRC_STR_START:
- // Opt out of all sounds without AVRCP play. We simply throw away.
- transitionTo(mSrcTActF);
- break;
-
- case ACT_PLAY:
- // Wait in next state for actual playback. We defer the message so that the next
- // state (SRC_F_ACT_T) can execute the retry logic.
- deferMessage(message);
- transitionTo(mSrcFActT);
+ if (requestAudioFocus() == AudioManager.AUDIOFOCUS_REQUEST_GRANTED);
+ transitionTo(mFGranted);
break;
case DISCONNECT:
- mAudioManager.abandonAudioFocus(mAudioFocusListener);
- mCurrentAudioFocus = AudioManager.AUDIOFOCUS_LOSS;
+ abandonAudioFocus();
break;
case AUDIO_FOCUS_CHANGE:
@@ -309,91 +326,22 @@
break;
default:
- Log.e(TAG, "Don't know how to handle " + message.what);
+ Log.e(STATE_TAG, "Don't know how to handle " + message.what);
+ return NOT_HANDLED;
}
return HANDLED;
}
}
- private class SRC_F_ACT_T extends State {
- private static final String STATE_TAG = A2dpSinkStreamingStateMachine.TAG + ".SRC_F_ACT_T";
- private boolean mPlay = false;
-
+ private class Focus_Granted extends State {
+ private static final String STATE_TAG = A2dpSinkStreamingStateMachine.TAG + ".F_Granted";
@Override
public void enter() {
if (DBG) {
Log.d(STATE_TAG, "Enter: " + getCurrentMessage().what);
}
- }
-
- @Override
- public boolean processMessage(Message message) {
- if (DBG) {
- Log.d(STATE_TAG, " process message: " + message.what);
- }
- switch (message.what) {
- case SRC_STR_START:
- deferMessage(message);
- transitionTo(mSrcTActT);
- break;
-
- case ACT_PAUSE:
- transitionTo(mSrcFActF);
- break;
-
- case ACT_PLAY:
- // Retry if the remote has not yet started playing music. This is seen in some
- // devices where after the phone call it requires multiple play commands to
- // start music.
- break;
-
- case ACT_PLAY_RETRY:
- if (message.arg1 > 0) {
- Log.d(STATE_TAG, "Retry " + message.arg1);
- sendAvrcpPlay();
- sendMessageDelayed(ACT_PLAY_RETRY, message.arg1 - 1, ACT_PLAY_RETRY_DELAY);
- }
- break;
-
-
- case DISCONNECT:
- deferMessage(message);
- transitionTo(mSrcFActF);
- mPlay = false;
- break;
-
- case AUDIO_FOCUS_CHANGE:
- int newAudioFocus = message.arg1;
- if (DBG) {
- Log.d(STATE_TAG,
- "prev focus " + mCurrentAudioFocus + " new focus " + newAudioFocus);
- }
- if (newAudioFocus == AudioManager.AUDIOFOCUS_GAIN) {
- sendAvrcpPlay();
- mCurrentAudioFocus = newAudioFocus;
- } else if (newAudioFocus == AudioManager.AUDIOFOCUS_LOSS_TRANSIENT) {
- sendAvrcpPause();
- mCurrentAudioFocus = newAudioFocus;
- } else if (newAudioFocus == AudioManager.AUDIOFOCUS_LOSS) {
- mAudioManager.abandonAudioFocus(mAudioFocusListener);
- mCurrentAudioFocus = AudioManager.AUDIOFOCUS_LOSS;
- }
- break;
-
- default:
- Log.e(TAG, "Don't know how to handle " + message.what);
- }
- return HANDLED;
- }
- }
-
- private class SRC_T_ACT_F extends State {
- private static final String STATE_TAG = A2dpSinkStreamingStateMachine.TAG + ".SRC_T_ACT_F";
- @Override
- public void enter() {
- if (DBG) {
- Log.d(STATE_TAG, "Enter: " + getCurrentMessage().what);
- }
+ startAvrcpUpdates();
+ startFluorideStreaming();
}
@Override
@@ -403,52 +351,96 @@
}
switch (message.what) {
case SRC_STR_STOP:
- transitionTo(mSrcFActF);
- break;
-
- case ACT_PLAY:
- deferMessage(message);
- transitionTo(mSrcTActT);
- break;
-
case DISCONNECT:
- deferMessage(message);
- transitionTo(mSrcFActF);
+ abandonAudioFocus();
+ transitionTo(mIdle);
break;
case AUDIO_FOCUS_CHANGE:
- // If we regain focus from TRANSIENT that means that the remote was playing all
- // this while although we must have sent a PAUSE (see focus loss in SRC_T_ACT_T
- // state). In any case, we should resume music here if that is the case.
int newAudioFocus = message.arg1;
if (DBG) {
Log.d(STATE_TAG,
- "prev focus " + mCurrentAudioFocus + " new focus " + newAudioFocus);
+ "prev focus " + mCurrentAudioFocus + " new focus " + newAudioFocus);
}
- if (newAudioFocus == AudioManager.AUDIOFOCUS_LOSS ||
- newAudioFocus == AudioManager.AUDIOFOCUS_LOSS_TRANSIENT) {
+ if (newAudioFocus == AudioManager.AUDIOFOCUS_LOSS_TRANSIENT) {
sendAvrcpPause();
- } else if (newAudioFocus == AudioManager.AUDIOFOCUS_GAIN) {
- sendAvrcpPlay();
+ transitionTo(mFTransient);
+ }
+ if (newAudioFocus == AudioManager.AUDIOFOCUS_LOSS) {
+ sendAvrcpPause();
+ transitionTo(mFLoss);
}
mCurrentAudioFocus = newAudioFocus;
break;
default:
- Log.e(TAG, "Don't know how to handle " + message.what);
+ Log.e(STATE_TAG, "Don't know how to handle " + message.what);
+ return NOT_HANDLED;
}
return HANDLED;
}
}
- private class SRC_T_ACT_T extends State {
- private static final String STATE_TAG = A2dpSinkStreamingStateMachine.TAG + ".SRC_T_ACT_T";
+ private class Focus_Transient extends State {
+ private static final String STATE_TAG = A2dpSinkStreamingStateMachine.TAG + ".F_Transient";
+ @Override
+ public void enter() {
+ if (DBG) {
+ Log.d(STATE_TAG, "Enter: " + getCurrentMessage().what);
+ }
+ stopFluorideStreaming();
+ stopAvrcpUpdates();
+ }
+
+ @Override
+ public boolean processMessage(Message message) {
+ if (DBG) {
+ Log.d(STATE_TAG, " process message: " + message.what);
+ }
+ switch (message.what) {
+ case SRC_STR_STOP:
+ transitionTo(mIdle);
+ break;
+
+ case DISCONNECT:
+ abandonAudioFocus();
+ transitionTo(mIdle);
+ break;
+
+ case AUDIO_FOCUS_CHANGE:
+ int newAudioFocus = message.arg1;
+ if (DBG) {
+ Log.d(STATE_TAG,
+ "prev focus " + mCurrentAudioFocus + " new focus " + newAudioFocus);
+ }
+ if (newAudioFocus == AudioManager.AUDIOFOCUS_GAIN)
+ sendAvrcpPlay();
+ transitionTo(mFGranted);
+ if (newAudioFocus == AudioManager.AUDIOFOCUS_LOSS) {
+ abandonAudioFocus();
+ transitionTo(mIdle);
+ }
+ mCurrentAudioFocus = newAudioFocus;
+ break;
+
+ default:
+ Log.e(STATE_TAG, "Don't know how to handle " + message.what);
+ return NOT_HANDLED;
+ }
+ return HANDLED;
+ }
+ }
+
+ private class Focus_Loss extends State {
+ private static final String STATE_TAG = A2dpSinkStreamingStateMachine.TAG + ".F_Loss";
private boolean mWaitForJitter = false;
@Override
public void enter() {
if (DBG) {
Log.d(STATE_TAG, "Enter: " + getCurrentMessage().what);
}
+ stopFluorideStreaming();
+ stopAvrcpUpdates();
}
@Override
@@ -457,102 +449,20 @@
Log.d(STATE_TAG, " process message: " + message.what);
}
switch (message.what) {
- case ACT_PAUSE:
- // Stop avrcp updates.
- stopAvrcpUpdates();
- stopFluorideStreaming();
- transitionTo(mSrcTActF);
- if (mCurrentAudioFocus == AudioManager.AUDIOFOCUS_GAIN) {
- // If we have focus gain and we still get pause that means that we must have
- // gotten a PAUSE by user explicitly pressing PAUSE on Car or Phone. Hence
- // we release focus.
- mAudioManager.abandonAudioFocus(mAudioFocusListener);
- mCurrentAudioFocus = AudioManager.AUDIOFOCUS_LOSS;
- }
- break;
-
case SRC_STR_STOP:
- stopAvrcpUpdates();
- stopFluorideStreaming();
- transitionTo(mSrcFActT);
- // This could be variety of reasons including that the remote is going to
- // (eventually send us pause) or the device is going to go into a call state
- // etc. Also it may simply be stutter of music. Instead of sending pause
- // prematurely we wait for either a Pause from remote or AudioFocus change owing
- // an ongoing call.
- break;
-
- case SRC_STR_START:
- case ACT_PLAY:
- Log.d(STATE_TAG, "Current Audio Focus " + mCurrentAudioFocus);
- boolean startStream = true;
- if (mCurrentAudioFocus == AudioManager.AUDIOFOCUS_LOSS) {
- if (!requestAudioFocus()) {
- Log.e(STATE_TAG, "Cannot get focus, hence not starting streaming.");
- startStream = false;
- } else {
- mCurrentAudioFocus = AudioManager.AUDIOFOCUS_GAIN;
- }
- }
- if (startStream) {
- startAvrcpUpdates();
- startFluorideStreaming();
- }
- // If we did not get focus, it may mean that the device in a call state and
- // hence we should wait for an audio focus event.
- break;
-
- // On Audio Focus events we stay in the same state but this can potentially change
- // if we playback.
- case AUDIO_FOCUS_CHANGE:
- int newAudioFocus = (int) message.arg1;
- if (DBG) {
- Log.d(STATE_TAG,
- "prev focus " + mCurrentAudioFocus + " new focus " + newAudioFocus);
- }
-
- if (newAudioFocus == AudioManager.AUDIOFOCUS_GAIN) {
- // We have gained focus so play with 1.0 gain.
- sendAvrcpPlay();
- startAvrcpUpdates();
- startFluorideStreaming();
- setFluorideAudioTrackGain(1.0f);
- } else if (newAudioFocus == AudioManager.AUDIOFOCUS_LOSS_TRANSIENT_CAN_DUCK) {
- // Make the volume duck.
- int duckPercent =
- mContext.getResources().getInteger(R.integer.a2dp_sink_duck_percent);
- if (duckPercent < 0 || duckPercent > 100) {
- Log.e(STATE_TAG, "Invalid duck percent using default.");
- duckPercent = DEFAULT_DUCK_PERCENT;
- }
- float duckRatio = (float) ((duckPercent * 1.0f) / 100);
- Log.d(STATE_TAG,
- "Setting reduce gain on transient loss gain=" + duckRatio);
- setFluorideAudioTrackGain(duckRatio);
- } else {
- // We either are in transient loss or we are in permanent loss,
- // either ways we should stop streaming.
- sendAvrcpPause();
- stopAvrcpUpdates();
- stopFluorideStreaming();
-
- // If it is permanent focus loss then we should abandon focus here and wait
- // for user to explicitly play again.
- if (newAudioFocus == AudioManager.AUDIOFOCUS_LOSS) {
- mAudioManager.abandonAudioFocus(mAudioFocusListener);
- mCurrentAudioFocus = AudioManager.AUDIOFOCUS_LOSS;
- }
- }
- mCurrentAudioFocus = newAudioFocus;
- break;
-
case DISCONNECT:
- deferMessage(message);
- transitionTo(mSrcFActF);
+ abandonAudioFocus();
+ transitionTo(mIdle);
+ break;
+
+ case ACT_PLAY:
+ if (requestAudioFocus() == AudioManager.AUDIOFOCUS_REQUEST_GRANTED)
+ transitionTo(mFGranted);
break;
default:
- Log.e(TAG, "Don't know how to handle " + message.what);
+ Log.e(STATE_TAG, "Don't know how to handle " + message.what);
+ return NOT_HANDLED;
}
return HANDLED;
}
diff --git a/src/com/android/bluetooth/avrcp/Avrcp.java b/src/com/android/bluetooth/avrcp/Avrcp.java
old mode 100755
new mode 100644
index 57c98a8..82ecf73
--- a/src/com/android/bluetooth/avrcp/Avrcp.java
+++ b/src/com/android/bluetooth/avrcp/Avrcp.java
@@ -1,4 +1,7 @@
/*
+ * Copyright (C) 2013-2015, The Linux Foundation. All rights reserved.
+ * Not a Contribution.
+ *
* Copyright (C) 2012 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
@@ -19,12 +22,16 @@
import java.util.Timer;
import java.util.TimerTask;
+import android.app.PendingIntent;
+import android.bluetooth.BluetoothAdapter;
import android.bluetooth.BluetoothA2dp;
import android.bluetooth.BluetoothAvrcp;
+import android.bluetooth.BluetoothDevice;
import android.content.Context;
import android.content.Intent;
import android.content.res.Resources;
import android.content.SharedPreferences;
+import android.content.IntentFilter;
import android.graphics.Bitmap;
import android.media.AudioManager;
import android.media.MediaDescription;
@@ -47,6 +54,8 @@
import android.view.KeyEvent;
import com.android.bluetooth.R;
+import android.content.BroadcastReceiver;
+import com.android.bluetooth.a2dp.A2dpService;
import com.android.bluetooth.btservice.AdapterService;
import com.android.bluetooth.btservice.ProfileService;
import com.android.bluetooth.Utils;
@@ -59,6 +68,20 @@
import java.util.HashMap;
import java.util.List;
import java.util.Set;
+import java.util.Iterator;
+
+import android.provider.MediaStore;
+import android.content.ContentResolver;
+import android.database.Cursor;
+import android.database.sqlite.SQLiteException;
+import android.net.Uri;
+
+import android.app.Notification;
+import android.app.NotificationManager;
+
+import com.android.bluetooth.avrcp.AvrcpControllerService;
+import android.os.SystemProperties;
+
/**
* support Bluetooth AVRCP profile.
* support metadata, play status and event notification
@@ -70,6 +93,7 @@
private Context mContext;
private final AudioManager mAudioManager;
+ private A2dpService mA2dpService;
private AvrcpMessageHandler mHandler;
private MediaSessionManager mMediaSessionManager;
private MediaSessionChangeListener mSessionChangeListener;
@@ -77,43 +101,34 @@
private MediaControllerListener mMediaControllerCb;
private MediaAttributes mMediaAttributes;
private int mTransportControlFlags;
- private PlaybackState mCurrentPlayState;
+ private PlaybackState mCurrentPlayerState;
private long mLastStateUpdate;
private int mPlayStatusChangedNT;
private int mTrackChangedNT;
private int mPlayPosChangedNT;
- private long mTrackNumber;
private long mSongLengthMs;
private long mPlaybackIntervalMs;
- private long mLastReportedPosition;
- private long mNextPosMs;
- private long mPrevPosMs;
private long mSkipStartTime;
- private int mFeatures;
- private int mRemoteVolume;
- private int mLastRemoteVolume;
- private int mInitialRemoteVolume;
+
+ Resources mResources;
- /* Local volume in audio index 0-15 */
- private int mLocalVolume;
- private int mLastLocalVolume;
- private int mAbsVolThreshold;
-
- private String mAddress;
- private HashMap<Integer, Integer> mVolumeMapping;
-
- private int mLastDirection;
- private final int mVolumeStep;
+ private int mVolumeStep;
private final int mAudioStreamMax;
- private boolean mVolCmdAdjustInProgress;
- private boolean mVolCmdSetInProgress;
- private int mAbsVolRetryTimes;
+ private static final String BLUETOOTH_ADMIN_PERM = android.Manifest.permission.BLUETOOTH_ADMIN;
+ private static final String BLUETOOTH_PERM = android.Manifest.permission.BLUETOOTH;
private int mSkipAmount;
+ private final BluetoothAdapter mAdapter;
+ private static Uri mMediaUriStatic;
+ private static long currentTrackPos;
+ private static boolean updatePlayTime;
+ private static boolean updateValues;
+ private int mAddressedPlayerId;
/* BTRC features */
public static final int BTRC_FEAT_METADATA = 0x01;
public static final int BTRC_FEAT_ABSOLUTE_VOLUME = 0x02;
public static final int BTRC_FEAT_BROWSE = 0x04;
+ public static final int BTRC_FEAT_AVRC_UI_UPDATE = 0x08;
/* AVRC response codes, from avrc_defs */
private static final int AVRC_RSP_NOT_IMPL = 8;
@@ -137,6 +152,30 @@
private static final int MESSAGE_REWIND = 11;
private static final int MESSAGE_CHANGE_PLAY_POS = 12;
private static final int MESSAGE_SET_A2DP_AUDIO_STATE = 13;
+ private static final int MESSAGE_SET_ADDR_PLAYER_REQ_TIMEOUT = 14;
+
+ private static final int AVRCP_BR_RSP_TIMEOUT = 2000;
+ private static final int MESSAGE_SEND_PASS_THROUGH_CMD = 2001;
+ private static final int MESSAGE_SET_ADDR_PLAYER = 2002;
+ private static final int MESSAGE_GET_FOLDER_ITEMS = 2003;
+ private static final int MESSAGE_SET_BROWSED_PLAYER = 2004;
+ private static final int MESSAGE_CHANGE_PATH = 2005;
+ private static final int MESSAGE_PLAY_ITEM = 2006;
+ private static final int MESSAGE_GET_ITEM_ATTRS = 2007;
+ private static final int MESSAGE_GET_TOTAL_NUMBER_OF_ITEMS = 2008;
+
+ private CachedRequest mCachedRequest = null;
+
+ private static final int MSG_UPDATE_AVAILABLE_PLAYERS = 201;
+ private static final int MSG_UPDATE_ADDRESSED_PLAYER = 202;
+ private static final int MSG_UPDATE_RCC_CHANGE = 203;
+ private static final int MSG_UPDATE_BROWSED_PLAYER_FOLDER = 204;
+ private static final int MSG_UPDATE_NOW_PLAYING_CONTENT_CHANGED = 205;
+ private static final int MSG_PLAY_ITEM_RESPONSE = 206;
+ private static final int MSG_NOW_PLAYING_ENTRIES_RECEIVED = 207;
+
+ private MediaPlayerInfo mediaPlayerInfo1;
+ private MediaPlayerInfo mediaPlayerInfo2;
private static final int BUTTON_TIMEOUT_TIME = 2000;
private static final int BASE_SKIP_AMOUNT = 2000;
@@ -149,86 +188,610 @@
private static final int MAX_ERROR_RETRY_TIMES = 6;
private static final int AVRCP_MAX_VOL = 127;
private static final int AVRCP_BASE_VOLUME_STEP = 1;
+ private final static int MESSAGE_PLAYERSETTINGS_TIMEOUT = 602;
+
+ private static final int AVRCP_CONNECTED = 1;
+ public static final int KEY_STATE_PRESSED = 0;
+ public static final int KEY_STATE_RELEASED = 1;
+ public static final int AVRC_ID_VOL_UP = 0x41;
+ public static final int AVRC_ID_VOL_DOWN = 0x42;
+ private boolean pts_test = false;
+
+ private final static int TYPE_MEDIA_PLAYER_ITEM = 0x01;
+ private final static int TYPE_FOLDER_ITEM = 0x02;
+ private final static int TYPE_MEDIA_ELEMENT_ITEM = 0x03;
+
+ private final static int FOLDER_UP = 0x00;
+ private final static int FOLDER_DOWN = 0x01;
+
+ private static final String PATH_INVALID = "invalid";
+ private static final String PATH_ROOT = "root";
+ private static final String PATH_TITLES = "titles";
+ private static final String PATH_ALBUMS = "albums";
+ private static final String PATH_ARTISTS = "artists";
+ private static final String PATH_PLAYLISTS = "playlists";
+
+ private final static long UID_TITLES = 0x01;
+ private final static long UID_ALBUM = 0x02;
+ private final static long UID_ARTIST = 0x03;
+ private final static long UID_PLAYLIST = 0x04;
+ private final static int NUM_ROOT_ELEMENTS = 0x04;
+
+ private static final int INTERNAL_ERROR = 0x03;
+ private static final int OPERATION_SUCCESSFUL = 0x04;
+ private static final int INVALID_DIRECTION = 0x07;
+ private static final int NOT_A_DIRECTORY = 0x08;
+ private static final int DOES_NOT_EXIST = 0x09;
+ private static final int INVALID_SCOPE = 0x0a;
+ private static final int RANGE_OUT_OF_BOUNDS = 0x0b;
+ private static final int UID_A_DIRECTORY = 0x0c;
+ private static final int MEDIA_IN_USE = 0x0d;
+ private static final int INVALID_PLAYER_ID = 0x11;
+ private static final int PLAYER_NOT_BROWSABLE = 0x12;
+ private static final int PLAYER_NOT_ADDRESSED = 0x13;
+
+ private static final int FOLDER_TYPE_MIXED = 0x00;
+ private static final int FOLDER_TYPE_TITLES = 0x01;
+ private static final int FOLDER_TYPE_ALBUMS = 0x02;
+ private static final int FOLDER_TYPE_ARTISTS = 0x03;
+ private static final int FOLDER_TYPE_GENRES = 0x04;
+ private static final int FOLDER_TYPE_PLAYLISTS = 0x05;
+
+ private static final int MEDIA_TYPE_AUDIO = 0X00;
+ private static final int MEDIA_TYPE_VIDEO = 0X01;
+
+ private static final int MAX_BROWSE_ITEM_TO_SEND = 10;
+ private static final int MAX_ATTRIB_COUNT = 0x08;
+
+ private final static int ALBUMS_ITEM_INDEX = 0;
+ private final static int ARTISTS_ITEM_INDEX = 1;
+ private final static int PLAYLISTS_ITEM_INDEX = 2;
+ private final static int TITLES_ITEM_INDEX = 3;
+
+ //Intents for PlayerApplication Settings
+ private static final String PLAYERSETTINGS_REQUEST =
+ "org.codeaurora.music.playersettingsrequest";
+ private static final String PLAYERSETTINGS_RESPONSE =
+ "org.codeaurora.music.playersettingsresponse";
+ // Max number of Avrcp connections at any time
+ private int maxAvrcpConnections = 1;
+ BluetoothDevice mBrowserDevice = null;
+ private static final int INVALID_DEVICE_INDEX = 0xFF;
+ // codes for reset of of notifications
+ private static final int PLAY_POSITION_CHANGE_NOTIFICATION = 101;
+ private static final int PLAY_STATUS_CHANGE_NOTIFICATION = 102;
+ private static final int TRACK_CHANGE_NOTIFICATION = 103;
+ private static final int NOW_PALYING_CONTENT_CHANGED_NOTIFICATION = 104;
+ private static final int PLAYER_STATUS_CHANGED_NOTIFICATION = 105;
+
+ private static final int INVALID_ADDRESSED_PLAYER_ID = -1;
+ // Device dependent registered Notification & Variables
+ private class DeviceDependentFeature {
+ private BluetoothDevice mCurrentDevice;
+ private PlaybackState mCurrentPlayState;
+ private int mPlayStatusChangedNT;
+ private int mPlayerStatusChangeNT;
+ private int mTrackChangedNT;
+ private long mNextPosMs;
+ private long mPrevPosMs;
+ private long mPlaybackIntervalMs;
+ private long mLastReportedPosition;
+ private int mPlayPosChangedNT;
+ private int mFeatures;
+ private int mAbsoluteVolume;
+ private int mLastSetVolume;
+ private int mLastDirection;
+ private boolean mVolCmdSetInProgress;
+ private boolean mVolCmdAdjustInProgress;
+ private int mAbsVolRetryTimes;
+ private int keyPressState;
+ private int mAddressedPlayerChangedNT;
+ private int mAvailablePlayersChangedNT;
+ private int mNowPlayingContentChangedNT;
+ private String mRequestedAddressedPlayerPackageName;
+ private String mCurrentPath;
+ private String mCurrentPathUid;
+ private Uri mMediaUri;
+ private boolean isMusicAppResponsePending;
+ private boolean isBrowsingSupported;
+ private boolean isAbsoluteVolumeSupportingDevice;
+ private boolean isActiveDevice;
+
+ private int mRemoteVolume;
+ private int mLastRemoteVolume;
+ private int mInitialRemoteVolume;
+
+ /* Local volume in audio index 0-15 */
+ private int mLocalVolume;
+ private int mLastLocalVolume;
+ private int mAbsVolThreshold;
+ private HashMap<Integer, Integer> mVolumeMapping;
+
+ public DeviceDependentFeature() {
+ mCurrentDevice = null;
+ mCurrentPlayState = new PlaybackState.Builder().setState(PlaybackState.STATE_NONE, -1L, 0.0f).build();;
+ mPlayStatusChangedNT = NOTIFICATION_TYPE_CHANGED;
+ mPlayerStatusChangeNT = NOTIFICATION_TYPE_CHANGED;
+ mTrackChangedNT = NOTIFICATION_TYPE_CHANGED;
+ mNextPosMs = -1;
+ mPrevPosMs = -1;
+ mPlaybackIntervalMs = 0L;
+ mLastReportedPosition = -1;
+ mPlayPosChangedNT = NOTIFICATION_TYPE_CHANGED;
+ mFeatures = 0;
+ mAbsoluteVolume = -1;
+ mLastSetVolume = -1;
+ mLastDirection = 0;
+ mVolCmdAdjustInProgress = false;
+ mVolCmdSetInProgress = false;
+ mAbsVolRetryTimes = 0;
+ keyPressState = KEY_STATE_RELEASE; //Key release state
+ mAddressedPlayerChangedNT = NOTIFICATION_TYPE_CHANGED;
+ mAvailablePlayersChangedNT = NOTIFICATION_TYPE_CHANGED;
+ mNowPlayingContentChangedNT = NOTIFICATION_TYPE_CHANGED;
+ mRequestedAddressedPlayerPackageName = null;
+ mCurrentPath = PATH_INVALID;
+ mCurrentPathUid = null;
+ mMediaUri = Uri.EMPTY;
+ isMusicAppResponsePending = false;
+ isBrowsingSupported = false;
+ isAbsoluteVolumeSupportingDevice = false;
+ isActiveDevice = false;
+ mRemoteVolume = -1;
+ mInitialRemoteVolume = -1;
+ mLastRemoteVolume = -1;
+ mLocalVolume = -1;
+ mLastLocalVolume = -1;
+ mAbsVolThreshold = 0;
+ mVolumeMapping = new HashMap<Integer, Integer>();
+ if (mResources != null) {
+ mAbsVolThreshold = mResources.getInteger(R.integer.a2dp_absolute_volume_initial_threshold);
+ }
+ }
+ };
+
+ private class PlayerSettings {
+ public byte attr;
+ public byte [] attrIds;
+ public String path;
+ };
+
+ private PlayerSettings mPlayerSettings = new PlayerSettings();
+ private class localPlayerSettings {
+ public byte eq_value = 0x01;
+ public byte repeat_value = 0x01;
+ public byte shuffle_value = 0x01;
+ public byte scan_value = 0x01;
+ };
+ private localPlayerSettings settingValues = new localPlayerSettings();
+ private static final String COMMAND = "command";
+ private static final String CMDGET = "get";
+ private static final String CMDSET = "set";
+ private static final String EXTRA_GET_COMMAND = "commandExtra";
+ private static final String EXTRA_GET_RESPONSE = "Response";
+
+ private static final int GET_ATTRIBUTE_IDS = 0;
+ private static final int GET_VALUE_IDS = 1;
+ private static final int GET_ATTRIBUTE_TEXT = 2;
+ private static final int GET_VALUE_TEXT = 3;
+ private static final int GET_ATTRIBUTE_VALUES = 4;
+ private static final int NOTIFY_ATTRIBUTE_VALUES = 5;
+ private static final int SET_ATTRIBUTE_VALUES = 6;
+ private static final int GET_INVALID = 0xff;
+
+ private static final String EXTRA_ATTRIBUTE_ID = "Attribute";
+ private static final String EXTRA_VALUE_STRING_ARRAY = "ValueStrings";
+ private static final String EXTRA_ATTRIB_VALUE_PAIRS = "AttribValuePairs";
+ private static final String EXTRA_ATTRIBUTE_STRING_ARRAY = "AttributeStrings";
+ private static final String EXTRA_VALUE_ID_ARRAY = "Values";
+ private static final String EXTRA_ATTIBUTE_ID_ARRAY = "Attributes";
+
+ public static final int VALUE_SHUFFLEMODE_OFF = 1;
+ public static final int VALUE_SHUFFLEMODE_ALL = 2;
+ public static final int VALUE_REPEATMODE_OFF = 1;
+ public static final int VALUE_REPEATMODE_SINGLE = 2;
+ public static final int VALUE_REPEATMODE_ALL = 3;
+ public static final int VALUE_INVALID = 0;
+ public static final int ATTRIBUTE_NOTSUPPORTED = -1;
+
+ public static final int ATTRIBUTE_EQUALIZER = 1;
+ public static final int ATTRIBUTE_REPEATMODE = 2;
+ public static final int ATTRIBUTE_SHUFFLEMODE = 3;
+ public static final int ATTRIBUTE_SCANMODE = 4;
+ public static final int NUMPLAYER_ATTRIBUTE = 2;
+
+ private static AvrcpBipRsp mAvrcpBipRsp;
+
+ private byte [] def_attrib = new byte [] {ATTRIBUTE_REPEATMODE, ATTRIBUTE_SHUFFLEMODE};
+ private byte [] value_repmode = new byte [] { VALUE_REPEATMODE_OFF,
+ VALUE_REPEATMODE_SINGLE,
+ VALUE_REPEATMODE_ALL };
+
+ private byte [] value_shufmode = new byte [] { VALUE_SHUFFLEMODE_OFF,
+ VALUE_SHUFFLEMODE_ALL };
+ private byte [] value_default = new byte [] {0};
+ private final String UPDATE_ATTRIBUTES = "UpdateSupportedAttributes";
+ private final String UPDATE_VALUES = "UpdateSupportedValues";
+ private final String UPDATE_ATTRIB_VALUE = "UpdateCurrentValues";
+ private final String UPDATE_ATTRIB_TEXT = "UpdateAttributesText";
+ private final String UPDATE_VALUE_TEXT = "UpdateValuesText";
+ private ArrayList <Integer> mPendingCmds;
+ private ArrayList <Integer> mPendingSetAttributes;
+ DeviceDependentFeature[] deviceFeatures;
static {
classInitNative();
}
- private Avrcp(Context context) {
+ private Avrcp(Context context, A2dpService svc, int maxConnections ) {
+ if (DEBUG)
+ Log.v(TAG, "Avrcp");
+ mAdapter = BluetoothAdapter.getDefaultAdapter();
mMediaAttributes = new MediaAttributes(null);
- mCurrentPlayState = new PlaybackState.Builder().setState(PlaybackState.STATE_NONE, -1L, 0.0f).build();
- mPlayStatusChangedNT = NOTIFICATION_TYPE_CHANGED;
- mTrackChangedNT = NOTIFICATION_TYPE_CHANGED;
- mTrackNumber = -1L;
+ mCurrentPlayerState = new PlaybackState.Builder().setState(PlaybackState.STATE_NONE, -1L, 0.0f).build();
mLastStateUpdate = -1L;
mSongLengthMs = 0L;
- mPlaybackIntervalMs = 0L;
- mPlayPosChangedNT = NOTIFICATION_TYPE_CHANGED;
- mLastReportedPosition = -1;
- mNextPosMs = -1;
- mPrevPosMs = -1;
- mFeatures = 0;
- mRemoteVolume = -1;
- mInitialRemoteVolume = -1;
- mLastRemoteVolume = -1;
- mLastDirection = 0;
- mVolCmdAdjustInProgress = false;
- mVolCmdSetInProgress = false;
- mAbsVolRetryTimes = 0;
- mLocalVolume = -1;
- mLastLocalVolume = -1;
- mAbsVolThreshold = 0;
- mVolumeMapping = new HashMap<Integer, Integer>();
-
+ mA2dpService = svc;
+ maxAvrcpConnections = maxConnections;
+ deviceFeatures = new DeviceDependentFeature[maxAvrcpConnections];
+ mAddressedPlayerId = INVALID_ADDRESSED_PLAYER_ID;
+ for(int i = 0; i < maxAvrcpConnections; i++) {
+ deviceFeatures[i] = new DeviceDependentFeature();
+ }
mContext = context;
- initNative();
+ initNative(maxConnections);
mMediaSessionManager = (MediaSessionManager) context.getSystemService(Context.MEDIA_SESSION_SERVICE);
mAudioManager = (AudioManager) context.getSystemService(Context.AUDIO_SERVICE);
+ mResources = context.getResources();
mAudioStreamMax = mAudioManager.getStreamMaxVolume(AudioManager.STREAM_MUSIC);
mVolumeStep = Math.max(AVRCP_BASE_VOLUME_STEP, AVRCP_MAX_VOL/mAudioStreamMax);
- Resources resources = context.getResources();
- if (resources != null) {
- mAbsVolThreshold = resources.getInteger(R.integer.a2dp_absolute_volume_initial_threshold);
- }
+ mAvrcpBipRsp = new AvrcpBipRsp(mContext);
+ pts_test = SystemProperties.getBoolean("bt.avrcpct-passthrough.pts", false);
}
private void start() {
+ if (DEBUG)
+ Log.v(TAG, "start");
HandlerThread thread = new HandlerThread("BluetoothAvrcpHandler");
thread.start();
Looper looper = thread.getLooper();
mHandler = new AvrcpMessageHandler(looper);
-
+ registerMediaPlayers();
mSessionChangeListener = new MediaSessionChangeListener();
mMediaSessionManager.addOnActiveSessionsChangedListener(mSessionChangeListener, null, mHandler);
List<MediaController> sessions = mMediaSessionManager.getActiveSessions(null);
mMediaControllerCb = new MediaControllerListener();
if (sessions.size() > 0) {
+ final Iterator<MediaController> mcIterator = sessions.iterator();
+ while (mcIterator.hasNext()) {
+ final MediaController player = mcIterator.next();
+ if (mHandler != null) {
+ Log.v(TAG, "Availability change for player: " + player.getPackageName());
+ mHandler.obtainMessage(MSG_UPDATE_RCC_CHANGE, 0, 1,
+ player.getPackageName()).sendToTarget();
+ }
+ }
updateCurrentMediaController(sessions.get(0));
}
+ mPendingCmds = new ArrayList<Integer>();
+ mPendingSetAttributes = new ArrayList<Integer>();
+ // clear path for all devices
+ for (int i = 0; i < maxAvrcpConnections; i++) {
+ deviceFeatures[i].mCurrentPath = PATH_INVALID;
+ deviceFeatures[i].mCurrentPathUid = null;
+ deviceFeatures[i].mMediaUri = Uri.EMPTY;
+ }
+ IntentFilter intentFilter = new IntentFilter();
+ intentFilter.addAction(AudioManager.RCC_CHANGED_ACTION);
+ intentFilter.addAction(PLAYERSETTINGS_RESPONSE);
+ try {
+ mContext.registerReceiver(mIntentReceiver, intentFilter);
+ }catch (Exception e) {
+ Log.e(TAG,"Unable to register Avrcp receiver", e);
+ }
+ mAvrcpBipRsp.start();
}
- public static Avrcp make(Context context) {
- if (DEBUG) Log.v(TAG, "make");
- Avrcp ar = new Avrcp(context);
+ //Listen to intents from MediaPlayer and Audio Manager and update data structures
+ private BroadcastReceiver mIntentReceiver = new BroadcastReceiver() {
+ @Override
+ public void onReceive(Context context, Intent intent) {
+ Log.d(TAG, "Enter onReceive()" +intent);
+ String action = intent.getAction();
+ if (action.equals(AudioManager.RCC_CHANGED_ACTION)) {
+ Log.v(TAG, "received RCC_CHANGED_ACTION");
+ int isRCCFocussed = 0;
+ int isRCCAvailable = 0;
+ String callingPackageName =
+ intent.getStringExtra(AudioManager.EXTRA_CALLING_PACKAGE_NAME);
+ boolean isFocussed =
+ intent.getBooleanExtra(AudioManager.EXTRA_FOCUS_CHANGED_VALUE,
+ false);
+ boolean isAvailable =
+ intent.getBooleanExtra(AudioManager.EXTRA_AVAILABLITY_CHANGED_VALUE,
+ false);
+ if (isFocussed)
+ isRCCFocussed = 1;
+ if (isAvailable)
+ isRCCAvailable = 1;
+ Log.v(TAG, "focus: " + isFocussed + " , availability: " + isAvailable);
+ if (mHandler != null) {
+ mHandler.obtainMessage(MSG_UPDATE_RCC_CHANGE, isRCCFocussed,
+ isRCCAvailable, callingPackageName).sendToTarget();
+ }
+ } else if (action.equals(PLAYERSETTINGS_RESPONSE)) {
+ int getResponse = intent.getIntExtra(EXTRA_GET_RESPONSE,
+ GET_INVALID);
+ byte [] data;
+ String [] text;
+ boolean isSetAttrValRsp = false;
+ BluetoothDevice device = null;
+
+ synchronized (mPendingCmds) {
+ Integer val = new Integer(getResponse);
+ if (mPendingCmds.contains(val)) {
+ if (getResponse == SET_ATTRIBUTE_VALUES) {
+ isSetAttrValRsp = true;
+ if (DEBUG) Log.v(TAG,"Response received for SET_ATTRIBUTE_VALUES");
+ }
+ mHandler.removeMessages(MESSAGE_PLAYERSETTINGS_TIMEOUT);
+ mPendingCmds.remove(val);
+ }
+ }
+ for (int i = 0; i < maxAvrcpConnections; i++) {
+ if (deviceFeatures[i].isMusicAppResponsePending ==
+ true) {
+ device = deviceFeatures[i].mCurrentDevice;
+ deviceFeatures[i].isMusicAppResponsePending = false;
+ break;
+ }
+ }
+
+ if (DEBUG)
+ Log.v(TAG,"getResponse" + getResponse);
+ switch (getResponse) {
+ case GET_ATTRIBUTE_IDS:
+ if (device == null) {
+ Log.e(TAG,"ERROR!!! device is null");
+ return;
+ }
+ data = intent.getByteArrayExtra(EXTRA_ATTIBUTE_ID_ARRAY);
+ byte numAttr = (byte) data.length;
+ if (DEBUG)
+ Log.v(TAG,"GET_ATTRIBUTE_IDS");
+ getListPlayerappAttrRspNative(numAttr ,
+ data ,getByteAddress(device));
+
+ break;
+ case GET_VALUE_IDS:
+ if (device == null) {
+ Log.e(TAG,"ERROR!!! device is null");
+ return;
+ }
+ data = intent.getByteArrayExtra(EXTRA_VALUE_ID_ARRAY);
+ numAttr = (byte) data.length;
+ if (DEBUG)
+ Log.v(TAG,"GET_VALUE_IDS" + numAttr);
+ getPlayerAppValueRspNative(numAttr, data,
+ getByteAddress(device));
+ break;
+ case GET_ATTRIBUTE_VALUES:
+ if (device == null) {
+ Log.e(TAG,"ERROR!!! device is null");
+ return;
+ }
+ data = intent.getByteArrayExtra(EXTRA_ATTRIB_VALUE_PAIRS);
+ updateLocalPlayerSettings(data);
+ numAttr = (byte) data.length;
+ if (DEBUG)
+ Log.v(TAG,"GET_ATTRIBUTE_VALUES" + numAttr);
+ SendCurrentPlayerValueRspNative(numAttr ,
+ data, getByteAddress(device));
+ break;
+ case SET_ATTRIBUTE_VALUES:
+ data = intent.getByteArrayExtra(EXTRA_ATTRIB_VALUE_PAIRS);
+ updateLocalPlayerSettings(data);
+ if (isSetAttrValRsp) {
+ isSetAttrValRsp = false;
+ for (int i = 0; i < maxAvrcpConnections; i++) {
+ if (deviceFeatures[i].mCurrentDevice != null) {
+ Log.v(TAG,"Respond to SET_ATTRIBUTE_VALUES request");
+ if (checkPlayerAttributeResponse(data)) {
+ SendSetPlayerAppRspNative(OPERATION_SUCCESSFUL,
+ getByteAddress(deviceFeatures[i].mCurrentDevice));
+ } else {
+ SendSetPlayerAppRspNative(INTERNAL_ERROR,
+ getByteAddress(deviceFeatures[i].mCurrentDevice));
+ }
+ }
+ }
+ mPendingSetAttributes.clear();
+ }
+ for (int i = 0; i < maxAvrcpConnections; i++) {
+ if (deviceFeatures[i].mPlayerStatusChangeNT ==
+ NOTIFICATION_TYPE_INTERIM) {
+ Log.v(TAG,"device has registered for"+
+ "mPlayerStatusChangeNT");
+ deviceFeatures[i].mPlayerStatusChangeNT =
+ NOTIFICATION_TYPE_CHANGED;
+ sendPlayerAppChangedRsp(deviceFeatures[i].mPlayerStatusChangeNT,
+ deviceFeatures[i].mCurrentDevice);
+ } else {
+ Log.v(TAG,"Drop Set Attr Val update from media player");
+ }
+ }
+ break;
+ case GET_ATTRIBUTE_TEXT:
+ text = intent.getStringArrayExtra(EXTRA_ATTRIBUTE_STRING_ARRAY);
+ if (device == null) {
+ Log.e(TAG,"ERROR!!! device is null");
+ return;
+ }
+ sendSettingsTextRspNative(mPlayerSettings.attrIds.length ,
+ mPlayerSettings.attrIds ,text.length,
+ text, getByteAddress(device));
+ if (DEBUG)
+ Log.v(TAG,"mPlayerSettings.attrIds"
+ + mPlayerSettings.attrIds.length);
+ break;
+ case GET_VALUE_TEXT:
+ text = intent.getStringArrayExtra(EXTRA_VALUE_STRING_ARRAY);
+ if (device == null) {
+ Log.e(TAG,"ERROR!!! device is null");
+ return;
+ }
+ sendValueTextRspNative(mPlayerSettings.attrIds.length ,
+ mPlayerSettings.attrIds,
+ text.length, text,
+ getByteAddress(device));
+ break;
+ }
+ }
+
+ }
+ };
+
+ /* This method is used for create entries of existing media players on RCD start
+ * Later when media players become avaialable corresponding entries
+ * are marked accordingly and similarly when media players changes focus
+ * the corresponding fields are modified */
+ private void registerMediaPlayers () {
+ if (DEBUG)
+ Log.v(TAG, "registerMediaPlayers");
+ int[] featureMasks = {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00};
+ int[] featureMasks2 = {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00};
+ byte[] playerName1 = {0x4d, 0x75, 0x73, 0x69, 0x63}/*Music*/;
+ byte[] playerName2 = {0x4d, 0x75, 0x73, 0x69, 0x63, 0x32}/*Music2*/;
+
+ featureMasks[FEATURE_MASK_PLAY_OFFSET] =
+ featureMasks[FEATURE_MASK_PLAY_OFFSET] | FEATURE_MASK_PLAY_MASK;
+ featureMasks[FEATURE_MASK_PAUSE_OFFSET] =
+ featureMasks[FEATURE_MASK_PAUSE_OFFSET] | FEATURE_MASK_PAUSE_MASK;
+ featureMasks[FEATURE_MASK_STOP_OFFSET] =
+ featureMasks[FEATURE_MASK_STOP_OFFSET] | FEATURE_MASK_STOP_MASK;
+ featureMasks[FEATURE_MASK_PAGE_UP_OFFSET] =
+ featureMasks[FEATURE_MASK_PAGE_UP_OFFSET] | FEATURE_MASK_PAGE_UP_MASK;
+ featureMasks[FEATURE_MASK_PAGE_DOWN_OFFSET] =
+ featureMasks[FEATURE_MASK_PAGE_DOWN_OFFSET] | FEATURE_MASK_PAGE_DOWN_MASK;
+ featureMasks[FEATURE_MASK_REWIND_OFFSET] =
+ featureMasks[FEATURE_MASK_REWIND_OFFSET] | FEATURE_MASK_REWIND_MASK;
+ featureMasks[FEATURE_MASK_FAST_FWD_OFFSET] =
+ featureMasks[FEATURE_MASK_FAST_FWD_OFFSET] | FEATURE_MASK_FAST_FWD_MASK;
+ featureMasks[FEATURE_MASK_VENDOR_OFFSET] =
+ featureMasks[FEATURE_MASK_VENDOR_OFFSET] | FEATURE_MASK_VENDOR_MASK;
+ featureMasks[FEATURE_MASK_ADV_CTRL_OFFSET] =
+ featureMasks[FEATURE_MASK_ADV_CTRL_OFFSET] | FEATURE_MASK_ADV_CTRL_MASK;
+ featureMasks[FEATURE_MASK_BROWSE_OFFSET] =
+ featureMasks[FEATURE_MASK_BROWSE_OFFSET] | FEATURE_MASK_BROWSE_MASK;
+ featureMasks[FEATURE_MASK_NOW_PLAY_OFFSET] =
+ featureMasks[FEATURE_MASK_NOW_PLAY_OFFSET] | FEATURE_MASK_NOW_PLAY_MASK;
+ featureMasks[FEATURE_MASK_BR_WH_ADDR_OFFSET] =
+ featureMasks[FEATURE_MASK_BR_WH_ADDR_OFFSET] | FEATURE_MASK_BR_WH_ADDR_MASK;
+ featureMasks[FEATURE_MASK_NUM_ITEMS_OFFSET] =
+ featureMasks[FEATURE_MASK_NUM_ITEMS_OFFSET] | FEATURE_MASK_NUM_ITEMS_MASK;
+ featureMasks[FEATURE_MASK_COVER_ART_OFFSET] =
+ featureMasks[FEATURE_MASK_COVER_ART_OFFSET] | FEATURE_MASK_COVER_ART_MASK;
+
+/*Player2 does not support browsing and now playing,
+ hence updated the masks properly*/
+ featureMasks2[FEATURE_MASK_PLAY_OFFSET] =
+ featureMasks2[FEATURE_MASK_PLAY_OFFSET] | FEATURE_MASK_PLAY_MASK;
+ featureMasks2[FEATURE_MASK_PAUSE_OFFSET] =
+ featureMasks2[FEATURE_MASK_PAUSE_OFFSET] | FEATURE_MASK_PAUSE_MASK;
+ featureMasks2[FEATURE_MASK_STOP_OFFSET] =
+ featureMasks2[FEATURE_MASK_STOP_OFFSET] | FEATURE_MASK_STOP_MASK;
+ featureMasks2[FEATURE_MASK_PAGE_UP_OFFSET] =
+ featureMasks2[FEATURE_MASK_PAGE_UP_OFFSET] | FEATURE_MASK_PAGE_UP_MASK;
+ featureMasks2[FEATURE_MASK_PAGE_DOWN_OFFSET] =
+ featureMasks2[FEATURE_MASK_PAGE_DOWN_OFFSET] | FEATURE_MASK_PAGE_DOWN_MASK;
+ featureMasks2[FEATURE_MASK_REWIND_OFFSET] =
+ featureMasks2[FEATURE_MASK_REWIND_OFFSET] | FEATURE_MASK_REWIND_MASK;
+ featureMasks2[FEATURE_MASK_FAST_FWD_OFFSET] =
+ featureMasks2[FEATURE_MASK_FAST_FWD_OFFSET] | FEATURE_MASK_FAST_FWD_MASK;
+ mediaPlayerInfo1 = new MediaPlayerInfo ((short)0x0001,
+ MAJOR_TYPE_AUDIO,
+ SUB_TYPE_NONE,
+ (byte)PlaybackState.STATE_PAUSED,
+ CHAR_SET_UTF8,
+ (short)0x05,
+ playerName1,
+ "com.android.music",
+ true,
+ featureMasks);
+ mediaPlayerInfo2 = new MediaPlayerInfo ((short)0x0000,
+ MAJOR_TYPE_AUDIO,
+ SUB_TYPE_NONE,
+ (byte)PlaybackState.STATE_PAUSED,
+ CHAR_SET_UTF8,
+ (short)0x06,
+ playerName2,
+ "com.google.android.music",
+ true,
+ featureMasks2);
+ mMediaPlayers.add(mediaPlayerInfo1);
+ mMediaPlayers.add(mediaPlayerInfo2);
+ Log.d(TAG, "Exit registerMediaPlayers()");
+ }
+
+ public static Avrcp make(Context context, A2dpService svc,
+ int maxConnections) {
+ if (DEBUG)
+ Log.v(TAG, "make");
+ Avrcp ar = new Avrcp(context, svc, maxConnections);
ar.start();
return ar;
}
public void doQuit() {
+ if (DEBUG)
+ Log.v(TAG, "doQuit");
mHandler.removeCallbacksAndMessages(null);
Looper looper = mHandler.getLooper();
if (looper != null) {
looper.quit();
}
mMediaSessionManager.removeOnActiveSessionsChangedListener(mSessionChangeListener);
+ clearDeviceDependentFeature();
+ for (int i = 0; i < maxAvrcpConnections; i++) {
+ cleanupDeviceFeaturesIndex(i);
+ }
+ mAvrcpBipRsp.stop();
+ try {
+ mContext.unregisterReceiver(mIntentReceiver);
+ } catch (Exception e) {
+ Log.e(TAG,"Unable to unregister Avrcp receiver", e);
+ }
+ mMediaPlayers.clear();
+ if (mHandler.hasMessages(MESSAGE_SET_ADDR_PLAYER_REQ_TIMEOUT)) {
+ mHandler.removeMessages(MESSAGE_SET_ADDR_PLAYER_REQ_TIMEOUT);
+ if (DEBUG)
+ Log.v(TAG, "Addressed player message cleanup as part of doQuit");
+ }
+ }
+
+ public void clearDeviceDependentFeature() {
+ Log.d(TAG, "Enter clearDeviceDependentFeature()");
+ for (int i = 0; i < maxAvrcpConnections; i++) {
+ deviceFeatures[i].keyPressState = KEY_STATE_RELEASE; //Key release state
+ deviceFeatures[i].mCurrentPath = PATH_INVALID;
+ deviceFeatures[i].mMediaUri = Uri.EMPTY;
+ deviceFeatures[i].mCurrentPathUid = null;
+ deviceFeatures[i].mRequestedAddressedPlayerPackageName = null;
+ if (deviceFeatures[i].mVolumeMapping != null)
+ deviceFeatures[i].mVolumeMapping.clear();
+ }
+ Log.d(TAG, "Exit clearDeviceDependentFeature()");
}
public void cleanup() {
+ if (DEBUG)
+ Log.v(TAG, "cleanup");
cleanupNative();
- if (mVolumeMapping != null)
- mVolumeMapping.clear();
+ Log.d(TAG, "Exit cleanup()");
}
private class MediaControllerListener extends MediaController.Callback {
@@ -236,18 +799,85 @@
public void onMetadataChanged(MediaMetadata metadata) {
Log.v(TAG, "MediaController metadata changed");
updateMetadata(metadata);
+ Log.d(TAG, "Exit onMetadataChanged()");
}
@Override
public void onPlaybackStateChanged(PlaybackState state) {
Log.v(TAG, "MediaController playback changed: " + state.toString());
- updatePlaybackState(state);
+ updatePlaybackState(state, null);
+ Log.d(TAG, "Exit onPlaybackStateChanged()");
}
@Override
public void onSessionDestroyed() {
Log.v(TAG, "MediaController session destroyed");
}
+
+ @Override
+ public void onUpdateFolderInfoBrowsedPlayer(String stringUri) {
+ Log.v(TAG, "onClientFolderInfoBrowsedPlayer: stringUri: " + stringUri);
+ if (stringUri != null) {
+ String[] ExternalPath = stringUri.split("/");
+ if (ExternalPath.length < 4) {
+ Log.d(TAG, "Wrong entries.");
+ mHandler.obtainMessage(MSG_UPDATE_BROWSED_PLAYER_FOLDER, 0, INTERNAL_ERROR,
+ null).sendToTarget();
+ return;
+ }
+ Uri uri = Uri.parse(stringUri);
+ Log.v(TAG, "URI received: " + uri);
+ String[] SplitPath = new String[ExternalPath.length - 3];
+ for (int count = 2; count < (ExternalPath.length - 1); count++) {
+ SplitPath[count - 2] = ExternalPath[count];
+ Log.d(TAG, "SplitPath[" + (count - 2) + "] = " + SplitPath[count - 2]);
+ }
+ Log.v(TAG, "folderDepth: " + SplitPath.length);
+ for (int count = 0; count < SplitPath.length; count++) {
+ Log.v(TAG, "folderName: " + SplitPath[count]);
+ }
+ mMediaUriStatic = uri;
+ if (mHandler != null) {
+ // Don't send the complete path to CK as few gets confused by that
+ // Send only the name of the root folder
+ mHandler.obtainMessage(MSG_UPDATE_BROWSED_PLAYER_FOLDER, NUM_ROOT_ELEMENTS,
+ OPERATION_SUCCESSFUL, SplitPath).sendToTarget();
+ }
+ } else {
+ mHandler.obtainMessage(MSG_UPDATE_BROWSED_PLAYER_FOLDER, 0, INTERNAL_ERROR,
+ null).sendToTarget();
+ }
+ Log.d(TAG, "Exit onUpdateFolderInfoBrowsedPlayer()");
+ }
+
+ @Override
+ public void onUpdateNowPlayingEntries(long[] playList) {
+ Log.v(TAG, "onClientUpdateNowPlayingEntries");
+ if (mHandler != null) {
+ mHandler.obtainMessage(MSG_NOW_PLAYING_ENTRIES_RECEIVED, 0, 0,
+ playList).sendToTarget();
+ }
+ Log.d(TAG, "Exit onUpdateNowPlayingEntries()");
+ }
+
+ @Override
+ public void onUpdateNowPlayingContentChange() {
+ Log.v(TAG, "onClientNowPlayingContentChange");
+ if (mHandler != null) {
+ mHandler.obtainMessage(MSG_UPDATE_NOW_PLAYING_CONTENT_CHANGED).sendToTarget();
+ }
+ Log.d(TAG, "Exit onUpdateNowPlayingContentChange()");
+ }
+
+ @Override
+ public void onPlayItemResponse(boolean success) {
+ Log.v(TAG, "onClientPlayItemResponse");
+ if (mHandler != null) {
+ mHandler.obtainMessage(MSG_PLAY_ITEM_RESPONSE, 0, 0, new Boolean(success))
+ .sendToTarget();
+ }
+ Log.d(TAG, "Exit onPlayItemResponse()");
+ }
}
private class MediaSessionChangeListener implements MediaSessionManager.OnActiveSessionsChangedListener {
@@ -260,6 +890,7 @@
if (controllers.size() > 0) {
updateCurrentMediaController(controllers.get(0));
}
+ Log.d(TAG, "Exit onActiveSessionsChanged()");
}
}
@@ -275,8 +906,15 @@
}
mMediaController.registerCallback(mMediaControllerCb, mHandler);
updateMetadata(mMediaController.getMetadata());
+ if (mHandler != null) {
+ Log.v(TAG, "Focus gained for player: " + mMediaController.getPackageName());
+ mHandler.obtainMessage(MSG_UPDATE_RCC_CHANGE, 1, 1,
+ mMediaController.getPackageName()).sendToTarget();
+ }
+ Log.d(TAG, "Exit updateCurrentMediaController()");
}
+
/** Handles Avrcp messages. */
private final class AvrcpMessageHandler extends Handler {
private AvrcpMessageHandler(Looper looper) {
@@ -285,35 +923,213 @@
@Override
public void handleMessage(Message msg) {
+ int deviceIndex = INVALID_DEVICE_INDEX;
switch (msg.what) {
+ case MESSAGE_PLAYERSETTINGS_TIMEOUT:
+ Log.e(TAG, "**MESSAGE_PLAYSTATUS_TIMEOUT: Addr: " +
+ (String)msg.obj + " Msg: " + msg.arg1);
+ synchronized (mPendingCmds) {
+ Integer val = new Integer(msg.arg1);
+ if (!mPendingCmds.contains(val)) {
+ break;
+ }
+ mPendingCmds.remove(val);
+ }
+ switch (msg.arg1) {
+ case GET_ATTRIBUTE_IDS:
+ getListPlayerappAttrRspNative((byte)def_attrib.length ,
+ def_attrib, getByteAddress(
+ mAdapter.getRemoteDevice((String) msg.obj)));
+ break;
+ case GET_VALUE_IDS:
+ switch (mPlayerSettings.attr) {
+ case ATTRIBUTE_REPEATMODE:
+ getPlayerAppValueRspNative((byte)value_repmode.length,
+ value_repmode,
+ getByteAddress(mAdapter.getRemoteDevice(
+ (String) msg.obj)));
+ break;
+ case ATTRIBUTE_SHUFFLEMODE:
+ getPlayerAppValueRspNative((byte)value_shufmode.length,
+ value_shufmode,
+ getByteAddress(mAdapter.getRemoteDevice(
+ (String) msg.obj)));
+ break;
+ default:
+ getPlayerAppValueRspNative((byte)value_default.length,
+ value_default,
+ getByteAddress(mAdapter.getRemoteDevice(
+ (String) msg.obj)));
+ break;
+ }
+ break;
+ case GET_ATTRIBUTE_VALUES:
+ int j = 0;
+ byte [] retVal = new byte [mPlayerSettings.attrIds.length*2];
+ for (int i = 0; i < mPlayerSettings.attrIds.length; i++) {
+ retVal[j++] = mPlayerSettings.attrIds[i];
+ if (mPlayerSettings.attrIds[i] == ATTRIBUTE_REPEATMODE) {
+ retVal[j++] = settingValues.repeat_value;
+ } else if (mPlayerSettings.attrIds[i] == ATTRIBUTE_SHUFFLEMODE) {
+ retVal[j++] = settingValues.shuffle_value;
+ } else {
+ retVal[j++] = 0x0;
+ }
+ }
+ SendCurrentPlayerValueRspNative((byte)retVal.length ,
+ retVal, getByteAddress(mAdapter.getRemoteDevice(
+ (String) msg.obj)));
+ break;
+ case SET_ATTRIBUTE_VALUES :
+ SendSetPlayerAppRspNative(INTERNAL_ERROR, getByteAddress(
+ mAdapter.getRemoteDevice((String) msg.obj)));
+ break;
+ case GET_ATTRIBUTE_TEXT:
+ String [] attribText = new String [mPlayerSettings.attrIds.length];
+ for (int i = 0; i < mPlayerSettings.attrIds.length; i++) {
+ attribText[i] = "";
+ }
+ sendSettingsTextRspNative(mPlayerSettings.attrIds.length ,
+ mPlayerSettings.attrIds, attribText.length,
+ attribText, getByteAddress(mAdapter.getRemoteDevice(
+ (String) msg.obj)));
+ break;
+ case GET_VALUE_TEXT:
+ String [] valueText = new String [mPlayerSettings.attrIds.length];
+ for (int i = 0; i < mPlayerSettings.attrIds.length; i++) {
+ valueText[i] = "";
+ }
+ sendValueTextRspNative(mPlayerSettings.attrIds.length ,
+ mPlayerSettings.attrIds, valueText.length,
+ valueText,getByteAddress(mAdapter.getRemoteDevice(
+ (String) msg.obj)));
+ break;
+ default :
+ Log.e(TAG,"in default case");
+ break;
+ }
+ break;
+
+ case MSG_UPDATE_AVAILABLE_PLAYERS:
+ updateAvailableMediaPlayers();
+ break;
+
+ case MSG_UPDATE_ADDRESSED_PLAYER:
+ updateAddressedMediaPlayer(msg.arg1);
+ break;
+
+ case MSG_UPDATE_BROWSED_PLAYER_FOLDER:
+ Log.v(TAG, "MSG_UPDATE_BROWSED_PLAYER_FOLDER");
+ updateBrowsedPlayerFolder(msg.arg1, msg.arg2, (String [])msg.obj);
+ break;
+
+ case MSG_UPDATE_NOW_PLAYING_CONTENT_CHANGED:
+ Log.v(TAG, "MSG_UPDATE_NOW_PLAYING_CONTENT_CHANGED");
+ updateNowPlayingContentChanged();
+ break;
+
+ case MSG_PLAY_ITEM_RESPONSE:
+ Log.v(TAG, "MSG_PLAY_ITEM_RESPONSE");
+ boolean success = ((Boolean)msg.obj).booleanValue();
+ Log.v(TAG, "success: " + success);
+ updatePlayItemResponse(success);
+ break;
+
+ case MSG_NOW_PLAYING_ENTRIES_RECEIVED:
+ Log.v(TAG, "MSG_NOW_PLAYING_ENTRIES_RECEIVED");
+ updateNowPlayingEntriesReceived((long [])msg.obj);
+ break;
+
case MESSAGE_GET_RC_FEATURES:
+ {
String address = (String) msg.obj;
- if (DEBUG) Log.v(TAG, "MESSAGE_GET_RC_FEATURES: address="+address+
- ", features="+msg.arg1);
- mFeatures = msg.arg1;
- mFeatures = modifyRcFeatureFromBlacklist(mFeatures, address);
- mAudioManager.avrcpSupportsAbsoluteVolume(address, isAbsoluteVolumeSupported());
- mLastLocalVolume = -1;
- mRemoteVolume = -1;
- mLocalVolume = -1;
- mInitialRemoteVolume = -1;
- mAddress = address;
- if (mVolumeMapping != null)
- mVolumeMapping.clear();
- break;
+ if (DEBUG)
+ Log.v(TAG, "MESSAGE_GET_RC_FEATURES: address="+address+
+ ", features="+msg.arg1);
+ BluetoothDevice device = mAdapter.getRemoteDevice(address);
+ deviceIndex = getIndexForDevice(device);
+ if (deviceIndex == INVALID_DEVICE_INDEX) {
+ Log.v(TAG,"device entry not present, bailing out");
+ return;
+ }
+ deviceFeatures[deviceIndex].mFeatures = msg.arg1;
+ deviceFeatures[deviceIndex].mFeatures =
+ modifyRcFeatureFromBlacklist(deviceFeatures[deviceIndex].mFeatures,
+ address);
+ Log.d(TAG, "avrcpct-passthrough pts_test = " + pts_test);
+ if (pts_test) {
+ Log.v(TAG,"fake BTRC_FEAT_ABSOLUTE_VOLUME remote feat support for pts test");
+ deviceFeatures[deviceIndex].mFeatures =
+ deviceFeatures[deviceIndex].mFeatures | BTRC_FEAT_ABSOLUTE_VOLUME;
+ }
+ deviceFeatures[deviceIndex].isAbsoluteVolumeSupportingDevice =
+ ((deviceFeatures[deviceIndex].mFeatures &
+ BTRC_FEAT_ABSOLUTE_VOLUME) != 0);
+ mAudioManager.avrcpSupportsAbsoluteVolume(device.getAddress(),
+ isAbsoluteVolumeSupported());
+ Log.v(TAG," update audio manager for abs vol state = "
+ + isAbsoluteVolumeSupported());
+ deviceFeatures[deviceIndex].mLastLocalVolume = -1;
+ deviceFeatures[deviceIndex].mRemoteVolume = -1;
+ deviceFeatures[deviceIndex].mLocalVolume = -1;
+ deviceFeatures[deviceIndex].mInitialRemoteVolume = -1;
+ if (deviceFeatures[deviceIndex].mVolumeMapping != null)
+ deviceFeatures[deviceIndex].mVolumeMapping.clear();
+ if ((deviceFeatures[deviceIndex].mFeatures &
+ BTRC_FEAT_AVRC_UI_UPDATE) != 0)
+ {
+ int NOTIFICATION_ID = android.R.drawable.stat_sys_data_bluetooth;
+ Notification notification = new Notification.Builder(mContext)
+ .setContentTitle("Bluetooth Media Browsing")
+ .setContentText("Peer supports advanced feature")
+ .setSubText("Re-pair from peer to enable it")
+ .setSmallIcon(android.R.drawable.stat_sys_data_bluetooth)
+ .setDefaults(Notification.DEFAULT_ALL)
+ .build();
+ NotificationManager manager = (NotificationManager)
+ mContext.getSystemService(Context.NOTIFICATION_SERVICE);
+ manager.notify(NOTIFICATION_ID, notification);
+ Log.v(TAG," update notification manager on remote repair request");
+ }
+ break;
+ }
case MESSAGE_GET_PLAY_STATUS:
- if (DEBUG) Log.v(TAG, "MESSAGE_GET_PLAY_STATUS");
- getPlayStatusRspNative(convertPlayStateToPlayStatus(mCurrentPlayState),
- (int)mSongLengthMs, (int)getPlayPosition());
- break;
+ {
+ BluetoothDevice device;
+ int playState, position;
+ if (DEBUG)
+ Log.v(TAG, "MESSAGE_GET_PLAY_STATUS");
+ Log.v(TAG, "Event for device address " + (String)msg.obj);
+ device = mAdapter.getRemoteDevice((String) msg.obj);
+ deviceIndex = getIndexForDevice(device);
+ if (deviceIndex == INVALID_DEVICE_INDEX) {
+ Log.e(TAG,"Invalid device index for play status");
+ break;
+ }
+ playState = convertPlayStateToPlayStatus(deviceFeatures[deviceIndex].mCurrentPlayState);
+ position = (int)getPlayPosition(device);
+ Log.v(TAG, "Play Status for : " + device.getName() +
+ " state: " + playState + " position: " + position);
+ if (position == -1) {
+ Log.v(TAG, "Force play postion to 0, for getPlayStatus Rsp");
+ position = 0;
+ }
+
+ getPlayStatusRspNative(playState, (int)mSongLengthMs, position,
+ getByteAddress(device));
+ break;
+ }
case MESSAGE_GET_ELEM_ATTRS:
String[] textArray;
int[] attrIds;
byte numAttr = (byte) msg.arg1;
- ArrayList<Integer> attrList = (ArrayList<Integer>) msg.obj;
- Log.v(TAG, "MESSAGE_GET_ELEM_ATTRS:numAttr=" + numAttr);
+ ItemAttr itemAttr = (ItemAttr)msg.obj;
+ Log.v(TAG, "event for device address " + itemAttr.mAddress);
+ ArrayList<Integer> attrList = itemAttr.mAttrList;
+ if (DEBUG)
+ Log.v(TAG, "MESSAGE_GET_ELEM_ATTRS:numAttr=" + numAttr);
attrIds = new int[numAttr];
textArray = new String[numAttr];
for (int i = 0; i < numAttr; ++i) {
@@ -322,18 +1138,41 @@
Log.v(TAG, "getAttributeString:attrId=" + attrIds[i] +
" str=" + textArray[i]);
}
- getElementAttrRspNative(numAttr, attrIds, textArray);
+ getElementAttrRspNative(numAttr ,attrIds ,textArray ,
+ getByteAddress(mAdapter.getRemoteDevice(itemAttr.mAddress)));
break;
case MESSAGE_REGISTER_NOTIFICATION:
- if (DEBUG) Log.v(TAG, "MESSAGE_REGISTER_NOTIFICATION:event=" + msg.arg1 +
+ if (DEBUG)
+ Log.v(TAG, "MESSAGE_REGISTER_NOTIFICATION:event=" + msg.arg1 +
" param=" + msg.arg2);
- processRegisterNotification(msg.arg1, msg.arg2);
+ processRegisterNotification(msg.arg1, msg.arg2, (String) msg.obj);
break;
case MESSAGE_PLAY_INTERVAL_TIMEOUT:
- if (DEBUG) Log.v(TAG, "MESSAGE_PLAY_INTERVAL_TIMEOUT");
- sendPlayPosNotificationRsp(false);
+ if (DEBUG)
+ Log.v(TAG, "MESSAGE_PLAY_INTERVAL_TIMEOUT");
+ Log.v(TAG, "event for device address " + (BluetoothDevice)msg.obj);
+ deviceIndex = getIndexForDevice((BluetoothDevice) msg.obj);
+ if (deviceIndex == INVALID_DEVICE_INDEX) {
+ Log.e(TAG,"invalid index for device");
+ break;
+ }
+ sendPlayPosNotificationRsp(false, deviceIndex);
+ break;
+
+ case MESSAGE_SET_ADDR_PLAYER_REQ_TIMEOUT:
+ if (DEBUG)
+ Log.v(TAG, "setAddressedPlayer fails, Times out");
+ deviceIndex = getIndexForDevice(mAdapter.getRemoteDevice((String) msg.obj));
+ if (deviceIndex == INVALID_DEVICE_INDEX) {
+ Log.e(TAG,"invalid device index");
+ break;
+ }
+ Log.v(TAG, "event for device address " + (String)msg.obj);
+ setAdressedPlayerRspNative((byte)PLAYER_NOT_ADDRESSED,
+ getByteAddress(mAdapter.getRemoteDevice((String) msg.obj)));
+ deviceFeatures[deviceIndex].mRequestedAddressedPlayerPackageName = null;
break;
case MESSAGE_VOLUME_CHANGED:
@@ -341,214 +1180,283 @@
if (DEBUG) Log.v(TAG, "ignore MESSAGE_VOLUME_CHANGED");
break;
}
-
- if (DEBUG) Log.v(TAG, "MESSAGE_VOLUME_CHANGED: volume=" + ((byte)msg.arg1 & 0x7f)
+ if (DEBUG)
+ Log.v(TAG, "MESSAGE_VOLUME_CHANGED: volume=" + ((byte)msg.arg1 & 0x7f)
+ " ctype=" + msg.arg2);
-
-
+ Log.v(TAG, "event for device address " + (String)msg.obj);
+ deviceIndex = getIndexForDevice(mAdapter.getRemoteDevice((String) msg.obj));
+ if (deviceIndex == INVALID_DEVICE_INDEX) {
+ Log.e(TAG,"invalid index for device");
+ break;
+ }
boolean volAdj = false;
if (msg.arg2 == AVRC_RSP_ACCEPT || msg.arg2 == AVRC_RSP_REJ) {
- if (mVolCmdAdjustInProgress == false && mVolCmdSetInProgress == false) {
+ if ((deviceFeatures[deviceIndex].mVolCmdSetInProgress == false) &&
+ (deviceFeatures[deviceIndex].mVolCmdAdjustInProgress == false)){
Log.e(TAG, "Unsolicited response, ignored");
break;
}
removeMessages(MESSAGE_ABS_VOL_TIMEOUT);
-
- volAdj = mVolCmdAdjustInProgress;
- mVolCmdAdjustInProgress = false;
- mVolCmdSetInProgress = false;
- mAbsVolRetryTimes = 0;
+ volAdj = deviceFeatures[deviceIndex].mVolCmdAdjustInProgress;
+ deviceFeatures[deviceIndex].mVolCmdSetInProgress = false;
+ deviceFeatures[deviceIndex].mVolCmdAdjustInProgress = false;
+ deviceFeatures[deviceIndex].mAbsVolRetryTimes = 0;
}
-
byte absVol = (byte)((byte)msg.arg1 & 0x7f); // discard MSB as it is RFD
// convert remote volume to local volume
int volIndex = convertToAudioStreamVolume(absVol);
- if (mInitialRemoteVolume == -1) {
- mInitialRemoteVolume = absVol;
- if (mAbsVolThreshold > 0 && mAbsVolThreshold < mAudioStreamMax && volIndex > mAbsVolThreshold) {
- if (DEBUG) Log.v(TAG, "remote inital volume too high " + volIndex + ">" + mAbsVolThreshold);
- Message msg1 = mHandler.obtainMessage(MESSAGE_SET_ABSOLUTE_VOLUME, mAbsVolThreshold , 0);
+ if (DEBUG) Log.v(TAG,"Volume Index = " + volIndex);
+ if (deviceFeatures[deviceIndex].mInitialRemoteVolume == -1) {
+ deviceFeatures[deviceIndex].mInitialRemoteVolume = absVol;
+ if (deviceFeatures[deviceIndex].mAbsVolThreshold > 0 &&
+ deviceFeatures[deviceIndex].mAbsVolThreshold <
+ mAudioStreamMax &&
+ volIndex > deviceFeatures[deviceIndex].mAbsVolThreshold) {
+ if (DEBUG) Log.v(TAG, "remote inital volume too high " + volIndex + ">" +
+ deviceFeatures[deviceIndex].mAbsVolThreshold);
+ Message msg1 = mHandler.obtainMessage(MESSAGE_SET_ABSOLUTE_VOLUME,
+ deviceFeatures[deviceIndex].mAbsVolThreshold , 0);
mHandler.sendMessage(msg1);
- mRemoteVolume = absVol;
- mLocalVolume = volIndex;
+ deviceFeatures[deviceIndex].mRemoteVolume = absVol;
+ deviceFeatures[deviceIndex].mLocalVolume = volIndex;
break;
}
}
-
- if (mLocalVolume != volIndex && (msg.arg2 == AVRC_RSP_ACCEPT ||
+ if (deviceFeatures[deviceIndex].mLocalVolume != volIndex &&
+ (msg.arg2 == AVRC_RSP_ACCEPT ||
msg.arg2 == AVRC_RSP_CHANGED ||
msg.arg2 == AVRC_RSP_INTERIM)) {
/* If the volume has successfully changed */
- mLocalVolume = volIndex;
- if (mLastLocalVolume != -1 && msg.arg2 == AVRC_RSP_ACCEPT) {
- if (mLastLocalVolume != volIndex) {
+ if (!deviceFeatures[deviceIndex].isActiveDevice &&
+ (msg.arg2 == AVRC_RSP_CHANGED || msg.arg2 == AVRC_RSP_INTERIM)) {
+ Log.d(TAG, "Do not change volume from an inactive device");
+ break;
+ }
+
+ deviceFeatures[deviceIndex].mLocalVolume = volIndex;
+ if (deviceFeatures[deviceIndex].mLastLocalVolume != -1
+ && msg.arg2 == AVRC_RSP_ACCEPT) {
+ if (deviceFeatures[deviceIndex].mLastLocalVolume != volIndex) {
/* remote volume changed more than requested due to
* local and remote has different volume steps */
if (DEBUG) Log.d(TAG, "Remote returned volume does not match desired volume "
- + mLastLocalVolume + " vs "
+ + deviceFeatures[deviceIndex].mLastLocalVolume + " vs "
+ volIndex);
- mLastLocalVolume = mLocalVolume;
+ deviceFeatures[deviceIndex].mLastLocalVolume =
+ deviceFeatures[deviceIndex].mLocalVolume;
}
}
// remember the remote volume value, as it's the one supported by remote
if (volAdj) {
- synchronized (mVolumeMapping) {
- mVolumeMapping.put(volIndex, (int)absVol);
+ synchronized (deviceFeatures[deviceIndex].mVolumeMapping) {
+ deviceFeatures[deviceIndex].mVolumeMapping.put(volIndex, (int)absVol);
if (DEBUG) Log.v(TAG, "remember volume mapping " +volIndex+ "-"+absVol);
}
}
-
- notifyVolumeChanged(mLocalVolume);
- mRemoteVolume = absVol;
+ notifyVolumeChanged(deviceFeatures[deviceIndex].mLocalVolume,
+ deviceFeatures[deviceIndex].mCurrentDevice);
+ deviceFeatures[deviceIndex].mRemoteVolume = absVol;
long pecentVolChanged = ((long)absVol * 100) / 0x7f;
Log.e(TAG, "percent volume changed: " + pecentVolChanged + "%");
} else if (msg.arg2 == AVRC_RSP_REJ) {
Log.e(TAG, "setAbsoluteVolume call rejected");
- } else if (volAdj && mLastRemoteVolume > 0 && mLastRemoteVolume < AVRCP_MAX_VOL &&
- mLocalVolume == volIndex &&
- (msg.arg2 == AVRC_RSP_ACCEPT )) {
+ } else if (volAdj && deviceFeatures[deviceIndex].mLastRemoteVolume > 0
+ && deviceFeatures[deviceIndex].mLastRemoteVolume < AVRCP_MAX_VOL &&
+ deviceFeatures[deviceIndex].mLocalVolume == volIndex &&
+ (msg.arg2 == AVRC_RSP_ACCEPT )) {
/* oops, the volume is still same, remote does not like the value
* retry a volume one step up/down */
if (DEBUG) Log.d(TAG, "Remote device didn't tune volume, let's try one more step.");
int retry_volume = Math.min(AVRCP_MAX_VOL,
- Math.max(0, mLastRemoteVolume + mLastDirection));
- if (setVolumeNative(retry_volume)) {
- mLastRemoteVolume = retry_volume;
- sendMessageDelayed(obtainMessage(MESSAGE_ABS_VOL_TIMEOUT),
- CMD_TIMEOUT_DELAY);
- mVolCmdAdjustInProgress = true;
+ Math.max(0, deviceFeatures[deviceIndex].mLastRemoteVolume +
+ deviceFeatures[deviceIndex].mLastDirection));
+ if (setVolumeNative(retry_volume,
+ getByteAddress(deviceFeatures[deviceIndex].mCurrentDevice))) {
+ deviceFeatures[deviceIndex].mLastRemoteVolume = retry_volume;
+ sendMessageDelayed(obtainMessage(MESSAGE_ABS_VOL_TIMEOUT,
+ 0, 0, deviceFeatures[deviceIndex].mCurrentDevice), CMD_TIMEOUT_DELAY);
+ deviceFeatures[deviceIndex].mVolCmdAdjustInProgress = true;
}
+ } else if (msg.arg2 == AVRC_RSP_REJ) {
+ if (DEBUG)
+ Log.v(TAG, "setAbsoluteVolume call rejected");
}
break;
case MESSAGE_ADJUST_VOLUME:
+ {
if (!isAbsoluteVolumeSupported()) {
if (DEBUG) Log.v(TAG, "ignore MESSAGE_ADJUST_VOLUME");
break;
}
- if (DEBUG) Log.d(TAG, "MESSAGE_ADJUST_VOLUME: direction=" + msg.arg1);
-
- if (mVolCmdAdjustInProgress || mVolCmdSetInProgress) {
- if (DEBUG) Log.w(TAG, "There is already a volume command in progress.");
+ if (mA2dpService.isMulticastFeatureEnabled() &&
+ areMultipleDevicesConnected()) {
+ if (DEBUG) Log.v(TAG, "Volume change not entertained as multicast is enabled & multiple devices are connected");
break;
}
+ if (DEBUG)
+ Log.d(TAG, "MESSAGE_ADJUST_VOLUME: direction=" + msg.arg1);
+ for (int i = 0; i < maxAvrcpConnections; i++) {
+ if (deviceFeatures[i].mCurrentDevice != null &&
+ deviceFeatures[i].isActiveDevice) {
+ deviceIndex = i;
+ if ((deviceFeatures[deviceIndex].mVolCmdAdjustInProgress) ||
+ (deviceFeatures[deviceIndex].mVolCmdSetInProgress)){
+ if (DEBUG)
+ Log.w(TAG, "already a volume command in progress" +
+ "for this device.");
+ continue;
+ }
+ if (deviceFeatures[deviceIndex].mInitialRemoteVolume == -1) {
+ if (DEBUG) Log.d(TAG, "remote never tell us initial volume, black list it.");
+ blackListCurrentDevice(deviceIndex);
+ break;
+ }
+ // Wait on verification on volume from device, before changing the volume.
+ if (deviceFeatures[deviceIndex].mRemoteVolume != -1 &&
+ (msg.arg1 == -1 || msg.arg1 == 1)) {
+ int setVol = -1;
+ int targetVolIndex = -1;
+ if (deviceFeatures[deviceIndex].mLocalVolume == 0 && msg.arg1 == -1) {
+ if (DEBUG) Log.w(TAG, "No need to Vol down from 0.");
+ break;
+ }
+ if (deviceFeatures[deviceIndex].mLocalVolume ==
+ mAudioStreamMax && msg.arg1 == 1) {
+ if (DEBUG) Log.w(TAG, "No need to Vol up from max.");
+ break;
+ }
- // Remote device didn't set initial volume. Let's black list it
- if (mInitialRemoteVolume == -1) {
- Log.d(TAG, "remote " + mAddress + " never tell us initial volume, black list it.");
- blackListCurrentDevice();
- break;
- }
+ targetVolIndex = deviceFeatures[deviceIndex].mLocalVolume + msg.arg1;
+ if (DEBUG) Log.d(TAG, "Adjusting volume to " + targetVolIndex);
- // Wait on verification on volume from device, before changing the volume.
- if (mRemoteVolume != -1 && (msg.arg1 == -1 || msg.arg1 == 1)) {
- int setVol = -1;
- int targetVolIndex = -1;
- if (mLocalVolume == 0 && msg.arg1 == -1) {
- if (DEBUG) Log.w(TAG, "No need to Vol down from 0.");
- break;
- }
- if (mLocalVolume == mAudioStreamMax && msg.arg1 == 1) {
- if (DEBUG) Log.w(TAG, "No need to Vol up from max.");
- break;
- }
-
- targetVolIndex = mLocalVolume + msg.arg1;
- if (DEBUG) Log.d(TAG, "Adjusting volume to " + targetVolIndex);
-
- Integer i;
- synchronized (mVolumeMapping) {
- i = mVolumeMapping.get(targetVolIndex);
- }
-
- if (i != null) {
- /* if we already know this volume mapping, use it */
- setVol = i.byteValue();
- if (setVol == mRemoteVolume) {
- if (DEBUG) Log.d(TAG, "got same volume from mapping for " + targetVolIndex + ", ignore.");
- setVol = -1;
+ Integer j;
+ synchronized (deviceFeatures[deviceIndex].mVolumeMapping) {
+ j = deviceFeatures[deviceIndex].mVolumeMapping.get(targetVolIndex);
+ }
+ if (j != null) {
+ /* if we already know this volume mapping, use it */
+ setVol = j.byteValue();
+ if (setVol == deviceFeatures[deviceIndex].mRemoteVolume) {
+ if (DEBUG) Log.d(TAG, "got same volume from mapping for " +
+ targetVolIndex + ", ignore.");
+ setVol = -1;
+ }
+ if (DEBUG) Log.d(TAG, "set volume from mapping " + targetVolIndex + "-" + setVol);
+ }
+