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, &param)) != BT_STATUS_SUCCESS) {
+    if ((status =
+                sBluetoothMultiAvrcpInterface->register_notification_rsp(
+                BTRC_EVT_PLAY_STATUS_CHANGED,
+                (btrc_notification_type_t)type, &param, (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, &param)) != BT_STATUS_SUCCESS) {
+    if ((status = sBluetoothMultiAvrcpInterface->register_notification_rsp(BTRC_EVT_TRACK_CHANGE,
+                  (btrc_notification_type_t)type, &param, (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, &param)) != BT_STATUS_SUCCESS) {
+    if ((status = sBluetoothMultiAvrcpInterface->register_notification_rsp(
+                  BTRC_EVT_PLAY_POS_CHANGED,
+                  (btrc_notification_type_t)type, &param, (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,
+                   &param, (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,
+                   &param, (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, &param,
+            (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(&param,
+                                            (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(&param,
+                                            (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(&param,
+                                            (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">&lt;provider  android:authorities="[PROVIDER AUTHORITY]"
+              android:exported="true"
+              android:enabled="true"
+              android:permission="android.permission.BLUETOOTH_MAP"&gt;
+ *   ...
+ *      &lt;intent-filter&gt;
+           &lt;action android:name="android.content.action.BLEUETOOT_MAP_PROVIDER" /&gt;
+        &lt;/intent-filter&gt;
+ *   ...
+ *   &lt;/provider&gt;
+ * [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);
+                           }
+