Merge tag 'android-7.1.0_r4' of https://android.googlesource.com/platform/packages/apps/Bluetooth into aosp-7.1

Android 7.1.0 release 4

Change-Id: I44595dac56b16a9b3c24a9d0c3afdc69abae5f92
diff --git a/jni/com_android_bluetooth_hfp.cpp b/jni/com_android_bluetooth_hfp.cpp
index 963949f..983ab2f 100644
--- a/jni/com_android_bluetooth_hfp.cpp
+++ b/jni/com_android_bluetooth_hfp.cpp
@@ -50,6 +50,8 @@
 static jmethodID method_onAtClcc;
 static jmethodID method_onUnknownAt;
 static jmethodID method_onKeyPressed;
+static jmethodID method_onAtBind;
+static jmethodID method_onAtBiev;
 
 static const bthf_interface_t *sBluetoothHfpInterface = NULL;
 static jobject mCallbacksObj = NULL;
@@ -377,6 +379,34 @@
     sCallbackEnv->DeleteLocalRef(addr);
 }
 
+static void at_bind_callback(char *at_string, bt_bdaddr_t *bd_addr) {
+    CHECK_CALLBACK_ENV
+
+    jbyteArray addr = marshall_bda(bd_addr);
+    if (addr == NULL)
+        return;
+
+    jstring js_at_string = sCallbackEnv->NewStringUTF(at_string);
+
+    sCallbackEnv->CallVoidMethod(mCallbacksObj, method_onAtBind, js_at_string, addr);
+    checkAndClearExceptionFromCallback(sCallbackEnv, __FUNCTION__);
+
+    sCallbackEnv->DeleteLocalRef(js_at_string);
+    sCallbackEnv->DeleteLocalRef(addr);
+}
+
+static void at_biev_callback(bthf_hf_ind_type_t ind_id, int ind_value, bt_bdaddr_t *bd_addr) {
+    CHECK_CALLBACK_ENV
+
+    jbyteArray addr = marshall_bda(bd_addr);
+    if (addr == NULL)
+        return;
+
+    sCallbackEnv->CallVoidMethod(mCallbacksObj, method_onAtBiev, ind_id, (jint)ind_value, addr);
+    checkAndClearExceptionFromCallback(sCallbackEnv, __FUNCTION__);
+    sCallbackEnv->DeleteLocalRef(addr);
+}
+
 static bthf_callbacks_t sBluetoothHfpCallbacks = {
     sizeof(sBluetoothHfpCallbacks),
     connection_state_callback,
@@ -395,6 +425,8 @@
     at_cops_callback,
     at_clcc_callback,
     unknown_at_callback,
+    at_bind_callback,
+    at_biev_callback,
     key_pressed_callback
 };
 
@@ -417,6 +449,8 @@
     method_onAtClcc = env->GetMethodID(clazz, "onAtClcc", "([B)V");
     method_onUnknownAt = env->GetMethodID(clazz, "onUnknownAt", "(Ljava/lang/String;[B)V");
     method_onKeyPressed = env->GetMethodID(clazz, "onKeyPressed", "([B)V");
+    method_onAtBind = env->GetMethodID(clazz, "onATBind", "(Ljava/lang/String;[B)V");
+    method_onAtBiev = env->GetMethodID(clazz, "onATBiev", "(II[B)V");
 
     ALOGI("%s: succeeds", __FUNCTION__);
 }
@@ -678,6 +712,30 @@
     return (status == BT_STATUS_SUCCESS) ? JNI_TRUE : JNI_FALSE;
 }
 
+static jboolean bindResponseNative(JNIEnv *env,jobject object,
+                                jint ind_id, jboolean ind_status,
+                                jbyteArray address) {
+    ALOGI("%s: sBluetoothHfpInterface: %p", __FUNCTION__, sBluetoothHfpInterface);
+
+    if (!sBluetoothHfpInterface)
+        return JNI_FALSE;
+
+    jbyte *addr = env->GetByteArrayElements(address, NULL);
+    if (!addr) {
+        jniThrowIOException(env, EINVAL);
+        return JNI_FALSE;
+    }
+
+    bt_status_t status = sBluetoothHfpInterface->bind_response((bthf_hf_ind_type_t) ind_id,
+                   ind_status ? BTHF_HF_IND_ENABLED : BTHF_HF_IND_DISABLED,
+                   (bt_bdaddr_t *)addr);
+
+    if (status != BT_STATUS_SUCCESS)
+        ALOGE("%s: Failed bind_response, status: %d", __FUNCTION__, status);
+
+    env->ReleaseByteArrayElements(address, addr, 0);
+    return (status == BT_STATUS_SUCCESS ? JNI_TRUE : JNI_FALSE);
+}
 
 static jboolean atResponseStringNative(JNIEnv *env, jobject object, jstring response_str,
                                                  jbyteArray address) {
@@ -820,6 +878,7 @@
     {"notifyDeviceStatusNative", "(IIII)Z", (void *) notifyDeviceStatusNative},
     {"copsResponseNative", "(Ljava/lang/String;[B)Z", (void *) copsResponseNative},
     {"cindResponseNative", "(IIIIIII[B)Z", (void *) cindResponseNative},
+    {"bindResponseNative", "(IZ[B)Z", (void *)bindResponseNative},
     {"atResponseStringNative", "(Ljava/lang/String;[B)Z", (void *) atResponseStringNative},
     {"atResponseCodeNative", "(II[B)Z", (void *)atResponseCodeNative},
     {"clccResponseNative", "(IIIIZLjava/lang/String;I[B)Z", (void *) clccResponseNative},
diff --git a/res/values-ca/strings.xml b/res/values-ca/strings.xml
index 53ce817..ed31bb1 100644
--- a/res/values-ca/strings.xml
+++ b/res/values-ca/strings.xml
@@ -27,7 +27,7 @@
     <string name="airplane_error_msg" msgid="8698965595254137230">"No pots utilitzar Bluetooth en mode d\'avió."</string>
     <string name="bt_enable_title" msgid="8657832550503456572"></string>
     <string name="bt_enable_line1" msgid="7203551583048149">"Per utilitzar serveis Bluetooth, primer cal que activeu el Bluetooth."</string>
-    <string name="bt_enable_line2" msgid="4341936569415937994">"Voleu activar el Bluetooth ara?"</string>
+    <string name="bt_enable_line2" msgid="4341936569415937994">"Vols activar el Bluetooth ara?"</string>
     <string name="bt_enable_cancel" msgid="1988832367505151727">"Cancel·la"</string>
     <string name="bt_enable_ok" msgid="3432462749994538265">"Activa"</string>
     <string name="incoming_file_confirm_title" msgid="8139874248612182627">"Transferència del fitxer"</string>
diff --git a/res/values-fa/strings.xml b/res/values-fa/strings.xml
index 9e390dd..bdffe86 100644
--- a/res/values-fa/strings.xml
+++ b/res/values-fa/strings.xml
@@ -99,7 +99,7 @@
     <string name="status_no_sd_card" product="default" msgid="5760944071743325592">"‏هیچ کارت SD موجود نیست. برای ذخیره فایل‌های منتقل شده، یک کارت SD در گوشی قرار دهید."</string>
     <string name="status_connection_error" msgid="947681831523219891">"اتصال ناموفق بود."</string>
     <string name="status_protocol_error" msgid="3245444473429269539">"درخواست به درستی انجام نمی‌شود."</string>
-    <string name="status_unknown_error" msgid="8156660554237824912">"خطای ناشناخته."</string>
+    <string name="status_unknown_error" msgid="8156660554237824912">"خطای ناشناس."</string>
     <string name="btopp_live_folder" msgid="7967791481444474554">"بلوتوث دریافت شد"</string>
     <string name="download_success" msgid="7036160438766730871">"<xliff:g id="FILE_SIZE">%1$s</xliff:g> دریافت کامل شد."</string>
     <string name="upload_success" msgid="4014469387779648949">"<xliff:g id="FILE_SIZE">%1$s</xliff:g> ارسال کامل شد."</string>
diff --git a/res/values-fa/strings_pbap.xml b/res/values-fa/strings_pbap.xml
index 945d442..0667ebb 100644
--- a/res/values-fa/strings_pbap.xml
+++ b/res/values-fa/strings_pbap.xml
@@ -5,7 +5,7 @@
     <string name="pbap_session_key_dialog_header" msgid="2772472422782758981">"به کلید جلسه بلوتوث نیاز است"</string>
     <string name="pbap_acceptance_timeout_message" msgid="1107401415099814293">"‏هنگام پذیرش اتصال به %1$s وقفه زمانی ایجاد شده است"</string>
     <string name="pbap_authentication_timeout_message" msgid="4166979525521902687">"‏هنگام ورود کلید جلسه با %1$s وقفه زمانی ایجاد شده است"</string>
-    <string name="auth_notif_ticker" msgid="1575825798053163744">"‏درخواست راستی‌آزمایی Obex"</string>
+    <string name="auth_notif_ticker" msgid="1575825798053163744">"‏درخواست احراز هویت Obex"</string>
     <string name="auth_notif_title" msgid="7599854855681573258">"کلید جلسه"</string>
     <string name="auth_notif_message" msgid="6667218116427605038">"‏تایپ کلید جلسه برای %1$s"</string>
     <string name="defaultname" msgid="4821590500649090078">"کیت خودرو"</string>
diff --git a/res/values-hy-rAM/strings.xml b/res/values-hy-rAM/strings.xml
index ed242a9..efef63b 100644
--- a/res/values-hy-rAM/strings.xml
+++ b/res/values-hy-rAM/strings.xml
@@ -23,8 +23,8 @@
     <string name="bt_share_picker_label" msgid="6268100924487046932">"Bluetooth"</string>
     <string name="unknown_device" msgid="9221903979877041009">"Անհայտ սարք"</string>
     <string name="unknownNumber" msgid="4994750948072751566">"Անհայտ"</string>
-    <string name="airplane_error_title" msgid="2683839635115739939">"Ինքնաթիռային ռեժիմ"</string>
-    <string name="airplane_error_msg" msgid="8698965595254137230">"Դուք չեք կարող օգտվել Bluetooth-ից ինքնաթիռային ռեժիմում:"</string>
+    <string name="airplane_error_title" msgid="2683839635115739939">"Ինքնաթիռի ռեժիմ"</string>
+    <string name="airplane_error_msg" msgid="8698965595254137230">"Դուք չեք կարող օգտվել Bluetooth-ից Ինքնաթիռի ռեժիմում:"</string>
     <string name="bt_enable_title" msgid="8657832550503456572"></string>
     <string name="bt_enable_line1" msgid="7203551583048149">"Bluetooth ծառայություններից օգտվելու համար նախ պետք է միացնեք Bluetooth-ը:"</string>
     <string name="bt_enable_line2" msgid="4341936569415937994">"Միացնե՞լ Bluetooth-ը հիմա:"</string>
diff --git a/res/values-lt/strings.xml b/res/values-lt/strings.xml
index a7ae86f..69cf285 100644
--- a/res/values-lt/strings.xml
+++ b/res/values-lt/strings.xml
@@ -16,7 +16,7 @@
 
 <resources xmlns:android="http://schemas.android.com/apk/res/android"
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
-    <string name="permlab_bluetoothShareManager" msgid="311492132450338925">"Pasiekti atsisiuntimo tvarkyklę."</string>
+    <string name="permlab_bluetoothShareManager" msgid="311492132450338925">"Pasiekti atsisiuntimo tvarkytuvę."</string>
     <string name="permdesc_bluetoothShareManager" msgid="8930572979123190223">"Leidžiama programai pasiekti „BluetoothShare“ tvarkyklę ir naudoti ją failams perkelti."</string>
     <string name="permlab_bluetoothWhitelist" msgid="7091552898592306386">"Į baltąjį sąrašą įtraukto „Bluetooth“ įrenginio prieiga."</string>
     <string name="permdesc_bluetoothWhitelist" msgid="5494513855192170109">"Leidžiama programai laikinai į baltąjį sąrašą įtraukti „Bluetooth“ įrenginį, suteikiant jam galimybę siųsti failus į šį įrenginį be naudotojo patvirtinimo."</string>
diff --git a/res/values-my-rMM/strings.xml b/res/values-my-rMM/strings.xml
index a71b298..2bbb010 100644
--- a/res/values-my-rMM/strings.xml
+++ b/res/values-my-rMM/strings.xml
@@ -19,7 +19,7 @@
     <string name="permlab_bluetoothShareManager" msgid="311492132450338925">"ဒေါင်းလုပ်မန်နေဂျာကို အသုံးပြုနိုင်မည်"</string>
     <string name="permdesc_bluetoothShareManager" msgid="8930572979123190223">"အပလီကေးရှင်းအား ဘလူးတုသ်မျှဝေမှု မန်နေဂျာကို အသုံးပြုခွင့်ပေးပြီး ဖိုင်လွှဲရန် အသုံးပြုပါ"</string>
     <string name="permlab_bluetoothWhitelist" msgid="7091552898592306386">"ဘလူးတုသ်ယာယီခွင့်ပြုစာရင်းကို အသုံးပြုခွင့်"</string>
-    <string name="permdesc_bluetoothWhitelist" msgid="5494513855192170109">"အပလီကေးရှင်းအား ဘလူးတုသ်စက်ကို ယာယီခွင့်ပြုစာရင်းထဲ ထည့်ရန်ခွင့်ပြုကာ အသုံးပြုသူ၏ အတည်ပြုချက်မရယူပဲ ဖိုင်များကို စက်ထဲသို့ ပို့ခွင့်ပြုမည်"</string>
+    <string name="permdesc_bluetoothWhitelist" msgid="5494513855192170109">"အက်ပ်အား ဘလူးတုသ်စက်ကို ယာယီခွင့်ပြုစာရင်းထဲ ထည့်ရန်ခွင့်ပြုကာ အသုံးပြုသူ၏ အတည်ပြုချက်မရယူပဲ ဖိုင်များကို စက်ထဲသို့ ပို့ခွင့်ပြုမည်"</string>
     <string name="bt_share_picker_label" msgid="6268100924487046932">"ဘလူးတုသ်"</string>
     <string name="unknown_device" msgid="9221903979877041009">"မသိသော စက်"</string>
     <string name="unknownNumber" msgid="4994750948072751566">"အကြောင်းအရာ မသိရှိ"</string>
@@ -28,7 +28,7 @@
     <string name="bt_enable_title" msgid="8657832550503456572"></string>
     <string name="bt_enable_line1" msgid="7203551583048149">"ဘလူးတုသ်ဆားဗစ်ကိုအသုံးပြုရန် ပထမဦးစွာ ဘလူးတုသ်ကိုဖွင့်ပါ"</string>
     <string name="bt_enable_line2" msgid="4341936569415937994">"ယခုပင် ဘလူးတုသ်ကိုဖွင့်မည်လား?"</string>
-    <string name="bt_enable_cancel" msgid="1988832367505151727">"မလုပ်တော့ပါ"</string>
+    <string name="bt_enable_cancel" msgid="1988832367505151727">"မလုပ်တော့"</string>
     <string name="bt_enable_ok" msgid="3432462749994538265">"ဖွင့်မည်"</string>
     <string name="incoming_file_confirm_title" msgid="8139874248612182627">"ဖိုင်လွှဲပြောင်းခြင်း"</string>
     <string name="incoming_file_confirm_content" msgid="2752605552743148036">"ဝင်လာသည့် ဖိုင်ကို လက်ခံမလား?"</string>
@@ -120,12 +120,12 @@
     <string name="transfer_menu_clear_all" msgid="790017462957873132">"စာရင်းကို ဖယ်ရှားရန်"</string>
     <string name="transfer_menu_open" msgid="3368984869083107200">"ဖွင့်ရန်"</string>
     <string name="transfer_menu_clear" msgid="5854038118831427492">"စာရင်းမှ ရှင်းပစ်မည်"</string>
-    <string name="transfer_clear_dlg_title" msgid="2953444575556460386">"ဖယ်ရှားရန်"</string>
-    <string name="bluetooth_map_settings_save" msgid="7635491847388074606">"သိမ်းဆည်းရန်"</string>
-    <string name="bluetooth_map_settings_cancel" msgid="9205350798049865699">"မလုပ်တော့ပါ"</string>
+    <string name="transfer_clear_dlg_title" msgid="2953444575556460386">"ရှင်းရန်"</string>
+    <string name="bluetooth_map_settings_save" msgid="7635491847388074606">"သိမ်းရန်"</string>
+    <string name="bluetooth_map_settings_cancel" msgid="9205350798049865699">"မလုပ်တော့"</string>
     <string name="bluetooth_map_settings_intro" msgid="6482369468223987562">"ဘလူးတုသ်မှ မျှဝေလိုသော အကောင့်များကို ရွေးပါ။ ချိတ်ဆက်သည့်အခါ အကောင့်များအား ဝင်ခွင့်ပြုဖို့ လက်ခံပေးရပါလိမ့်ဦးမည်။"</string>
     <string name="bluetooth_map_settings_count" msgid="4557473074937024833">"ကျန်နေသည့် အပေါက်များ:"</string>
-    <string name="bluetooth_map_settings_app_icon" msgid="7105805610929114707">"အက်ပ် အိုင်ကွန်"</string>
+    <string name="bluetooth_map_settings_app_icon" msgid="7105805610929114707">"အပလီကေးရှင်း သင်္ကေတ"</string>
     <string name="bluetooth_map_settings_title" msgid="7420332483392851321">"ဘလူးတုသ် စာ မျှဝေရေး ဆက်တင်များ"</string>
     <string name="bluetooth_map_settings_no_account_slots_left" msgid="1796029082612965251">"ရွေးထားသည့် အကောင့်ကို မရွေးနိုင်ပါ။ သုည အပေါက်များ ကျန်နေ"</string>
     <string name="bluetooth_connected" msgid="6718623220072656906">"ဘလူးတုသ်အသံ ချိတ်ဆက်ပြီးပါပြီ"</string>
diff --git a/res/values-ro/strings.xml b/res/values-ro/strings.xml
index 9a4a536..85423d7 100644
--- a/res/values-ro/strings.xml
+++ b/res/values-ro/strings.xml
@@ -82,7 +82,7 @@
     <string name="bt_toast_2" msgid="8602553334099066582">"Fișierul nu poate fi primit."</string>
     <string name="bt_toast_3" msgid="6707884165086862518">"Primirea fișierului de la „<xliff:g id="SENDER">%1$s</xliff:g>” a fost anulată"</string>
     <string name="bt_toast_4" msgid="4678812947604395649">"Se trimite fișierul către „<xliff:g id="RECIPIENT">%1$s</xliff:g>”"</string>
-    <string name="bt_toast_5" msgid="2846870992823019494">"Se trimit <xliff:g id="NUMBER">%1$s</xliff:g> (de) fișiere către „<xliff:g id="RECIPIENT">%2$s</xliff:g>”"</string>
+    <string name="bt_toast_5" msgid="2846870992823019494">"Se trimit <xliff:g id="NUMBER">%1$s</xliff:g>   fișiere către „<xliff:g id="RECIPIENT">%2$s</xliff:g>”"</string>
     <string name="bt_toast_6" msgid="1855266596936622458">"Trimiterea fișierului către „<xliff:g id="RECIPIENT">%1$s</xliff:g>” a fost anulată"</string>
     <string name="bt_sm_2_1" product="nosdcard" msgid="352165168004521000">"Nu există spațiu suficient pe stocarea USB pentru a salva fișierul de la „<xliff:g id="SENDER">%1$s</xliff:g>”"</string>
     <string name="bt_sm_2_1" product="default" msgid="1989018443456803630">"Nu există suficient spațiu pe cardul SD pentru a salva fișierul de la „<xliff:g id="SENDER">%1$s</xliff:g>”"</string>
diff --git a/res/values/config.xml b/res/values/config.xml
index dcbe208..90e513d 100644
--- a/res/values/config.xml
+++ b/res/values/config.xml
@@ -35,14 +35,25 @@
          of the location permissions. -->
     <bool name="strict_location_check">true</bool>
 
-    <!-- Specifies the min/max connection interval parameters for high priority
-         and low power GATT configurations. These values are in multiples of
-         1.25ms. -->
+    <!-- Specifies the min/max connection interval parameters for high priority,
+         balanced and low power GATT configurations. These values are in
+         multiples of 1.25ms. -->
     <integer name="gatt_high_priority_min_interval">9</integer>
     <integer name="gatt_high_priority_max_interval">12</integer>
+    <!-- Default specs recommended interval is 30 (24 * 1.25) -> 50 (40 * 1.25)
+         ms. -->
+    <integer name="gatt_balanced_priority_min_interval">24</integer>
+    <integer name="gatt_balanced_priority_max_interval">40</integer>
     <integer name="gatt_low_power_min_interval">80</integer>
     <integer name="gatt_low_power_max_interval">100</integer>
 
+    <!-- Specifies latency parameters for high priority, balanced and low power
+         GATT configurations. These values represents the number of packets a
+         slave device is allowed to skip. -->
+    <integer name="gatt_high_priority_latency">0</integer>
+    <integer name="gatt_balanced_priority_latency">0</integer>
+    <integer name="gatt_low_power_latency">2</integer>
+
     <bool name="headset_client_initial_audio_route_allowed">true</bool>
 
     <!-- For AVRCP absolute volume feature. If the threshold is non-zero,
diff --git a/src/com/android/bluetooth/avrcp/Avrcp.java b/src/com/android/bluetooth/avrcp/Avrcp.java
index 297bae3..82ecf73 100644
--- a/src/com/android/bluetooth/avrcp/Avrcp.java
+++ b/src/com/android/bluetooth/avrcp/Avrcp.java
@@ -5435,6 +5435,7 @@
             ProfileService.println(sb, "mVolCmdAdjustInProgress: " + deviceFeatures[i].mVolCmdAdjustInProgress);
             ProfileService.println(sb, "mAbsVolRetryTimes: " + deviceFeatures[i].mAbsVolRetryTimes);
             ProfileService.println(sb, "mSkipAmount: " + mSkipAmount);
+            ProfileService.println(sb, "mVolumeMapping: " + deviceFeatures[i].mVolumeMapping.toString());
             if (mMediaController != null)
                 ProfileService.println(sb, "mMediaSession pkg: " +
                         mMediaController.getPackageName());
diff --git a/src/com/android/bluetooth/btservice/AdapterService.java b/src/com/android/bluetooth/btservice/AdapterService.java
index fb40cbb..6f0d47f 100644
--- a/src/com/android/bluetooth/btservice/AdapterService.java
+++ b/src/com/android/bluetooth/btservice/AdapterService.java
@@ -538,7 +538,10 @@
         registerReceiver(mAlarmBroadcastReceiver, new IntentFilter(ACTION_ALARM_WAKEUP));
         mProfileObserver = new ProfileObserver(getApplicationContext(), this, new Handler());
         mProfileObserver.start();
+
         mVendor.init();
+
+        setAdapterService(this);
     }
 
     @Override
@@ -597,9 +600,6 @@
             // Ignore.
         }
 
-        //FIXME: Set static instance here???
-        setAdapterService(this);
-
         //Start Gatt service
         setGattProfileServiceState(supportedProfileServices,BluetoothAdapter.STATE_ON);
     }
diff --git a/src/com/android/bluetooth/btservice/RemoteDevices.java b/src/com/android/bluetooth/btservice/RemoteDevices.java
index 19089a7..2545bd4 100644
--- a/src/com/android/bluetooth/btservice/RemoteDevices.java
+++ b/src/com/android/bluetooth/btservice/RemoteDevices.java
@@ -45,13 +45,13 @@
     private static final int UUID_INTENT_DELAY = 6000;
     private static final int MESSAGE_UUID_INTENT = 1;
 
-    private HashMap<BluetoothDevice, DeviceProperties> mDevices;
+    private HashMap<String, DeviceProperties> mDevices;
 
     RemoteDevices(AdapterService service) {
         mAdapter = BluetoothAdapter.getDefaultAdapter();
         mAdapterService = service;
         mSdpTracker = new ArrayList<BluetoothDevice>();
-        mDevices = new HashMap<BluetoothDevice, DeviceProperties>();
+        mDevices = new HashMap<String, DeviceProperties>();
     }
 
 
@@ -70,16 +70,15 @@
 
     DeviceProperties getDeviceProperties(BluetoothDevice device) {
         synchronized (mDevices) {
-            return mDevices.get(device);
+            return mDevices.get(device.getAddress());
         }
     }
 
     BluetoothDevice getDevice(byte[] address) {
         synchronized (mDevices) {
-            for (BluetoothDevice dev : mDevices.keySet()) {
-                if (dev.getAddress().equals(Utils.getAddressStringFromByte(address))) {
-                    return dev;
-                }
+            DeviceProperties p = mDevices.get(Utils.getAddressStringFromByte(address));
+            if (p != null) {
+                return p.getDevice();
             }
         }
         return null;
@@ -88,10 +87,9 @@
     DeviceProperties addDeviceProperties(byte[] address) {
         synchronized (mDevices) {
             DeviceProperties prop = new DeviceProperties();
-            BluetoothDevice device =
-                    mAdapter.getRemoteDevice(Utils.getAddressStringFromByte(address));
+            prop.mDevice = mAdapter.getRemoteDevice(Utils.getAddressStringFromByte(address));
             prop.mAddress = address;
-            mDevices.put(device, prop);
+            mDevices.put(Utils.getAddressStringFromByte(address), prop);
             return prop;
         }
     }
@@ -105,6 +103,7 @@
         private int mDeviceType;
         private String mAlias;
         private int mBondState;
+        private BluetoothDevice mDevice;
 
         DeviceProperties() {
             mBondState = BluetoothDevice.BOND_NONE;
@@ -147,6 +146,15 @@
         }
 
         /**
+         * @return the mDevice
+         */
+        BluetoothDevice getDevice() {
+            synchronized (mObject) {
+                return mDevice;
+            }
+        }
+
+        /**
          * @return mRssi
          */
         short getRssi() {
diff --git a/src/com/android/bluetooth/gatt/AdvertiseManager.java b/src/com/android/bluetooth/gatt/AdvertiseManager.java
index 058bf4b..3fb4aa9 100644
--- a/src/com/android/bluetooth/gatt/AdvertiseManager.java
+++ b/src/com/android/bluetooth/gatt/AdvertiseManager.java
@@ -194,7 +194,7 @@
         private void handleStartAdvertising(AdvertiseClient client) {
             Utils.enforceAdminPermission(mService);
             int clientIf = client.clientIf;
-            if (mAdvertiseClients.contains(clientIf)) {
+            if (mAdvertiseClients.contains(client)) {
                 postCallback(clientIf, AdvertiseCallback.ADVERTISE_FAILED_ALREADY_STARTED);
                 return;
             }
diff --git a/src/com/android/bluetooth/gatt/GattService.java b/src/com/android/bluetooth/gatt/GattService.java
index e1e5e57..0316b42 100644
--- a/src/com/android/bluetooth/gatt/GattService.java
+++ b/src/com/android/bluetooth/gatt/GattService.java
@@ -1627,12 +1627,11 @@
     void connectionParameterUpdate(int clientIf, String address, int connectionPriority) {
         enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission");
 
-        // Default spec recommended interval is 30->50 ms
-        int minInterval = 24; // 24 * 1.25ms = 30ms
-        int maxInterval = 40; // 40 * 1.25ms = 50ms
+        int minInterval;
+        int maxInterval;
 
         // Slave latency
-        int latency = 0;
+        int latency;
 
         // Link supervision timeout is measured in N * 10ms
         int timeout = 2000; // 20s
@@ -1642,12 +1641,22 @@
             case BluetoothGatt.CONNECTION_PRIORITY_HIGH:
                 minInterval = getResources().getInteger(R.integer.gatt_high_priority_min_interval);
                 maxInterval = getResources().getInteger(R.integer.gatt_high_priority_max_interval);
+                latency = getResources().getInteger(R.integer.gatt_high_priority_latency);
                 break;
 
             case BluetoothGatt.CONNECTION_PRIORITY_LOW_POWER:
                 minInterval = getResources().getInteger(R.integer.gatt_low_power_min_interval);
                 maxInterval = getResources().getInteger(R.integer.gatt_low_power_max_interval);
-                latency = 2;
+                latency = getResources().getInteger(R.integer.gatt_low_power_latency);
+                break;
+
+            default:
+                // Using the values for CONNECTION_PRIORITY_BALANCED.
+                minInterval =
+                        getResources().getInteger(R.integer.gatt_balanced_priority_min_interval);
+                maxInterval =
+                        getResources().getInteger(R.integer.gatt_balanced_priority_max_interval);
+                latency = getResources().getInteger(R.integer.gatt_balanced_priority_latency);
                 break;
         }
 
diff --git a/src/com/android/bluetooth/gatt/HandleMap.java b/src/com/android/bluetooth/gatt/HandleMap.java
index 4a20639..27a7a71 100644
--- a/src/com/android/bluetooth/gatt/HandleMap.java
+++ b/src/com/android/bluetooth/gatt/HandleMap.java
@@ -69,7 +69,6 @@
             this.type = type;
             this.handle = handle;
             this.uuid = uuid;
-            this.instance = instance;
             this.serviceHandle = serviceHandle;
         }
 
@@ -78,7 +77,6 @@
             this.type = type;
             this.handle = handle;
             this.uuid = uuid;
-            this.instance = instance;
             this.serviceHandle = serviceHandle;
             this.charHandle = charHandle;
         }
diff --git a/src/com/android/bluetooth/gatt/ScanManager.java b/src/com/android/bluetooth/gatt/ScanManager.java
index b20a54f..bb45bf7 100644
--- a/src/com/android/bluetooth/gatt/ScanManager.java
+++ b/src/com/android/bluetooth/gatt/ScanManager.java
@@ -242,7 +242,7 @@
                         Message msg = mHandler.obtainMessage(MSG_SCAN_TIMEOUT);
                         msg.obj = client;
                         // Only one timeout message should exist at any time
-                        mHandler.removeMessages(SCAN_TIMEOUT_MS);
+                        mHandler.removeMessages(MSG_SCAN_TIMEOUT);
                         mHandler.sendMessageDelayed(msg, SCAN_TIMEOUT_MS);
                     }
                 }
diff --git a/src/com/android/bluetooth/hfp/HeadsetHalConstants.java b/src/com/android/bluetooth/hfp/HeadsetHalConstants.java
index 0742917..dee633f 100644
--- a/src/com/android/bluetooth/hfp/HeadsetHalConstants.java
+++ b/src/com/android/bluetooth/hfp/HeadsetHalConstants.java
@@ -64,4 +64,8 @@
     final static int CALL_STATE_INCOMING = 4;
     final static int CALL_STATE_WAITING = 5;
     final static int CALL_STATE_IDLE = 6;
+
+    // Match up with bthf_hf_ind_type_t of bt_hf.h
+    final static int HF_INDICATOR_ENHANCED_DRIVER_SAFETY = 1;
+    final static int HF_INDICATOR_BATTERY_LEVEL_STATUS = 2;
 }
diff --git a/src/com/android/bluetooth/hfp/HeadsetService.java b/src/com/android/bluetooth/hfp/HeadsetService.java
index c61cb3c..09d2e32 100644
--- a/src/com/android/bluetooth/hfp/HeadsetService.java
+++ b/src/com/android/bluetooth/hfp/HeadsetService.java
@@ -331,6 +331,12 @@
             if (service == null) return false;
             return service.disableWBS();
         }
+
+        public void bindResponse(int ind_id, boolean ind_status) {
+            HeadsetService service = getService();
+            if (service == null) return;
+            service.bindResponse(ind_id, ind_status);
+        }
     };
 
     //API methods
@@ -639,6 +645,24 @@
         return true;
     }
 
+    private boolean bindResponse(int ind_id, boolean ind_status) {
+        for (BluetoothDevice device: getConnectedDevices()) {
+            int connectionState = mStateMachine.getConnectionState(device);
+            if (connectionState != BluetoothProfile.STATE_CONNECTED &&
+                connectionState != BluetoothProfile.STATE_CONNECTING) {
+                continue;
+            }
+            if (DBG) Log.d("Bind Response sent for", device.getAddress());
+            Message msg = mStateMachine.obtainMessage(HeadsetStateMachine.BIND_RESPONSE);
+            msg.obj = device;
+            msg.arg1 = ind_id;
+            msg.arg2 = (ind_status == true) ? 1 : 0;
+            mStateMachine.sendMessage(msg);
+            return true;
+        }
+        return false;
+    }
+
     @Override
     public void dump(StringBuilder sb) {
         super.dump(sb);
diff --git a/src/com/android/bluetooth/hfp/HeadsetStateMachine.java b/src/com/android/bluetooth/hfp/HeadsetStateMachine.java
index bcc560d..d8f3e47 100755
--- a/src/com/android/bluetooth/hfp/HeadsetStateMachine.java
+++ b/src/com/android/bluetooth/hfp/HeadsetStateMachine.java
@@ -70,6 +70,7 @@
 import com.android.internal.util.StateMachine;
 import java.util.ArrayList;
 import java.util.HashMap;
+import java.util.Iterator;
 import java.util.List;
 import java.util.Map;
 import java.util.Set;
@@ -110,10 +111,12 @@
     static final int ENABLE_WBS = 16;
     static final int DISABLE_WBS = 17;
 
-    static final int UPDATE_A2DP_PLAY_STATE = 18;
-    static final int UPDATE_A2DP_CONN_STATE = 19;
-    static final int QUERY_PHONE_STATE_AT_SLC = 20;
-    static final int UPDATE_CALL_TYPE = 21;
+    static final int BIND_RESPONSE = 18;
+
+    static final int UPDATE_A2DP_PLAY_STATE = 28;
+    static final int UPDATE_A2DP_CONN_STATE = 29;
+    static final int QUERY_PHONE_STATE_AT_SLC = 30;
+    static final int UPDATE_CALL_TYPE = 31;
 
     private static final int STACK_EVENT = 101;
     private static final int DIALING_OUT_TIMEOUT = 102;
@@ -627,6 +630,15 @@
                             }
                             processConnectionEvent(event.valueInt, event.device);
                             break;
+
+                        case EVENT_TYPE_BIND:
+                            processAtBind(event.valueString, event.device);
+                            break;
+
+                        case EVENT_TYPE_BIEV:
+                            processAtBiev(event.valueInt, event.valueInt2, event.device);
+                            break;
+
                         default:
                             Log.e(TAG, "Unexpected event: " + event.type);
                             break;
@@ -1069,6 +1081,13 @@
                 case UPDATE_CALL_TYPE:
                     processIntentUpdateCallType((Intent) message.obj);
                     break;
+                case BIND_RESPONSE:
+                {
+                    BluetoothDevice device = (BluetoothDevice) message.obj;
+                    bindResponseNative((int)message.arg1, ((message.arg2 == 1) ? true : false),
+                                        getByteAddress(device));
+                }
+                    break;
                 case START_VR_TIMEOUT:
                 {
                     BluetoothDevice device = (BluetoothDevice) message.obj;
@@ -1149,6 +1168,12 @@
                         case EVENT_TYPE_KEY_PRESSED:
                             processKeyPressed(event.device);
                             break;
+                        case EVENT_TYPE_BIND:
+                            processAtBind(event.valueString, event.device);
+                            break;
+                        case EVENT_TYPE_BIEV:
+                            processAtBiev(event.valueInt, event.valueInt2, event.device);
+                            break;
                         default:
                             Log.e(TAG, "Unknown stack event: " + event.type);
                             break;
@@ -1649,6 +1674,12 @@
                         case EVENT_TYPE_KEY_PRESSED:
                             processKeyPressed(event.device);
                             break;
+                        case EVENT_TYPE_BIND:
+                            processAtBind(event.valueString, event.device);
+                            break;
+                        case EVENT_TYPE_BIEV:
+                            processAtBiev(event.valueInt, event.valueInt2, event.device);
+                            break;
                         default:
                             Log.e(TAG, "Unknown stack event: " + event.type);
                             break;
@@ -2067,6 +2098,12 @@
                         case EVENT_TYPE_KEY_PRESSED:
                             processKeyPressed(event.device);
                             break;
+                        case EVENT_TYPE_BIND:
+                            processAtBind(event.valueString, event.device);
+                            break;
+                        case EVENT_TYPE_BIEV:
+                            processAtBiev(event.valueInt, event.valueInt2, event.device);
+                            break;
                         default:
                             Log.e(TAG, "Unexpected event: " + event.type);
                             break;
@@ -3740,6 +3777,65 @@
         if (DBG) Log.d(TAG, "Exit processKeyPressed()");
     }
 
+    private void sendIndicatorIntent(BluetoothDevice device, int ind_id, String ind_value)
+    {
+        Intent intent = new Intent(BluetoothHeadset.ACTION_HF_INDICATORS_VALUE_CHANGED);
+        intent.putExtra(BluetoothDevice.EXTRA_DEVICE, device);
+        intent.putExtra(BluetoothHeadset.EXTRA_HF_INDICATORS_IND_ID, ind_id);
+        if (ind_value != null)
+            intent.putExtra(BluetoothHeadset.EXTRA_HF_INDICATORS_IND_VALUE, ind_value);
+
+        mService.sendBroadcast(intent, HeadsetService.BLUETOOTH_PERM);
+    }
+
+    private void processAtBind( String at_string, BluetoothDevice device) {
+        log("processAtBind processAtBind: " + at_string);
+
+        // Parse the AT String to find the Indicator Ids that are supported
+        int ind_id = 0;
+        int iter = 0;
+        int iter1 = 0;
+
+        while (iter < at_string.length()) {
+            iter1 = findChar(',', at_string, iter);
+            String id = at_string.substring(iter, iter1);
+
+            try {
+                ind_id = new Integer(id);
+            } catch (NumberFormatException e) {
+                Log.e(TAG, Log.getStackTraceString(new Throwable()));
+            }
+
+            switch (ind_id) {
+                case HeadsetHalConstants.HF_INDICATOR_ENHANCED_DRIVER_SAFETY :
+                    log("Send Broadcast intent for the" +
+                        "Enhanced Driver Safety indicator.");
+                    sendIndicatorIntent(device, ind_id, null);
+                    break;
+                case HeadsetHalConstants.HF_INDICATOR_BATTERY_LEVEL_STATUS :
+                    log("Send Broadcast intent for the" +
+                        "Battery Level indicator.");
+                    sendIndicatorIntent(device, ind_id, null);
+                    break;
+                default:
+                    log("Invalid HF Indicator Received");
+                    break;
+            }
+
+            iter = iter1 + 1; // move past comma
+        }
+    }
+
+    private void processAtBiev( int ind_id, int ind_value, BluetoothDevice device) {
+        log(" Process AT + BIEV Command : " + ind_id + ", " + ind_value);
+
+        String ind_value_str = Integer.toString(ind_value);
+
+        Intent intent = new Intent(BluetoothHeadset.ACTION_HF_INDICATORS_VALUE_CHANGED);
+        intent.putExtra(BluetoothDevice.EXTRA_DEVICE, device);
+        sendIndicatorIntent(device, ind_id, ind_value_str);
+    }
+
     private void onConnectionStateChanged(int state, byte[] address) {
         if (DBG) Log.d(TAG, "Enter onConnectionStateChanged()");
         StackEvent event = new StackEvent(EVENT_TYPE_CONNECTION_STATE_CHANGED);
@@ -3887,6 +3983,21 @@
         if (DBG) Log.d(TAG, "Exit onKeyPressed()");
     }
 
+    private void onATBind(String atString, byte[] address) {
+        StackEvent event = new StackEvent(EVENT_TYPE_BIND);
+        event.valueString = atString;
+        event.device = getDevice(address);
+        sendMessage(STACK_EVENT, event);
+    }
+
+    private void onATBiev(int ind_id, int ind_value, byte[] address) {
+        StackEvent event = new StackEvent(EVENT_TYPE_BIEV);
+        event.valueInt = ind_id;
+        event.valueInt2 = ind_value;
+        event.device = getDevice(address);
+        sendMessage(STACK_EVENT, event);
+    }
+
     private void processIntentBatteryChanged(Intent intent) {
         if (DBG) Log.d(TAG, "Enter processIntentBatteryChanged()");
         int batteryLevel = intent.getIntExtra("level", -1);
@@ -4095,6 +4206,8 @@
     final private static int EVENT_TYPE_UNKNOWN_AT = 15;
     final private static int EVENT_TYPE_KEY_PRESSED = 16;
     final private static int EVENT_TYPE_WBS = 17;
+    final private static int EVENT_TYPE_BIND = 18;
+    final private static int EVENT_TYPE_BIEV = 19;
 
     private class StackEvent {
         int type = EVENT_TYPE_NONE;
@@ -4125,6 +4238,7 @@
     private native boolean cindResponseNative(int service, int numActive, int numHeld,
                                               int callState, int signal, int roam,
                                               int batteryCharge, byte[] address);
+    private native boolean bindResponseNative(int ind_id, boolean ind_status, byte[] address);
     private native boolean notifyDeviceStatusNative(int networkState, int serviceType, int signal,
                                                     int batteryCharge);
 
diff --git a/src/com/android/bluetooth/hfpclient/HeadsetClientStateMachine.java b/src/com/android/bluetooth/hfpclient/HeadsetClientStateMachine.java
old mode 100644
new mode 100755
index 05806fa..76ef463
--- a/src/com/android/bluetooth/hfpclient/HeadsetClientStateMachine.java
+++ b/src/com/android/bluetooth/hfpclient/HeadsetClientStateMachine.java
@@ -339,6 +339,7 @@
         Log.d(TAG, "Enter sendCallChangedIntent()");
         Log.d(TAG, "sendCallChangedIntent " + c);
         Intent intent = new Intent(BluetoothHeadsetClient.ACTION_CALL_CHANGED);
+        intent.addFlags(Intent.FLAG_RECEIVER_FOREGROUND);
         intent.putExtra(BluetoothHeadsetClient.EXTRA_CALL, c);
         mService.sendBroadcast(intent, ProfileService.BLUETOOTH_PERM);
         Log.d(TAG, "Exit sendCallChangedIntent()");
diff --git a/src/com/android/bluetooth/map/BluetoothMapContent.java b/src/com/android/bluetooth/map/BluetoothMapContent.java
index 2370a4d..4f42bfb 100755
--- a/src/com/android/bluetooth/map/BluetoothMapContent.java
+++ b/src/com/android/bluetooth/map/BluetoothMapContent.java
@@ -950,10 +950,14 @@
                     address = c.getString(c.getColumnIndex(Sms.ADDRESS));
                 }
                 if ((address == null) && msgType == Sms.MESSAGE_TYPE_DRAFT) {
-                    //Fetch address for Drafts folder from "canonical_address" table
+                    // Fetch address for Drafts folder from "canonical_address" table
                     int threadIdInd = c.getColumnIndex(Sms.THREAD_ID);
                     String threadIdStr = c.getString(threadIdInd);
-                    address = getCanonicalAddressSms(mResolver, Integer.valueOf(threadIdStr));
+                    // If a draft message has no recipient, it has no thread ID
+                    // hence threadIdStr could possibly be null
+                    if (threadIdStr != null) {
+                        address = getCanonicalAddressSms(mResolver, Integer.valueOf(threadIdStr));
+                    }
                     if(V)  Log.v(TAG, "threadId = " + threadIdStr + " adress:" + address +"\n");
                 }
             } else if (fi.mMsgType == FilterInfo.TYPE_MMS) {
@@ -961,7 +965,7 @@
                 address = getAddressMms(mResolver, id, MMS_TO);
             } else if (fi.mMsgType == FilterInfo.TYPE_EMAIL) {
                 /* Might be another way to handle addresses */
-                address = getRecipientAddressingEmail(e, c,fi);
+                address = getRecipientAddressingEmail(e, c, fi);
             }
             if (V) Log.v(TAG, "setRecipientAddressing: " + address);
             if(address == null)
@@ -1603,7 +1607,8 @@
         } else if (BluetoothMapContract.FOLDER_NAME_SENT.equalsIgnoreCase(folder)) {
             where = Sms.TYPE + " = 2 AND " + Sms.THREAD_ID + " <> -1";
         } else if (BluetoothMapContract.FOLDER_NAME_DRAFT.equalsIgnoreCase(folder)) {
-            where = Sms.TYPE + " = 3 AND " + Sms.THREAD_ID + " <> -1";
+            where = Sms.TYPE + " = 3 AND " +
+                "(" + Sms.THREAD_ID + " IS NULL OR " + Sms.THREAD_ID + " <> -1 )";
         } else if (BluetoothMapContract.FOLDER_NAME_DELETED.equalsIgnoreCase(folder)) {
             where = Sms.THREAD_ID + " = -1";
         }
@@ -1620,7 +1625,8 @@
         } else if (BluetoothMapContract.FOLDER_NAME_SENT.equalsIgnoreCase(folder)) {
             where = Mms.MESSAGE_BOX + " = 2 AND " + Mms.THREAD_ID + " <> -1";
         } else if (BluetoothMapContract.FOLDER_NAME_DRAFT.equalsIgnoreCase(folder)) {
-            where = Mms.MESSAGE_BOX + " = 3 AND " + Mms.THREAD_ID + " <> -1";
+            where = Mms.MESSAGE_BOX + " = 3 AND " +
+                "(" + Mms.THREAD_ID + " IS NULL OR " + Mms.THREAD_ID + " <> -1 )";
         } else if (BluetoothMapContract.FOLDER_NAME_DELETED.equalsIgnoreCase(folder)) {
             where = Mms.THREAD_ID + " = -1";
         }
diff --git a/src/com/android/bluetooth/map/BluetoothMapContentObserver.java b/src/com/android/bluetooth/map/BluetoothMapContentObserver.java
index 878f5be..6edc556 100755
--- a/src/com/android/bluetooth/map/BluetoothMapContentObserver.java
+++ b/src/com/android/bluetooth/map/BluetoothMapContentObserver.java
@@ -37,6 +37,7 @@
 import android.os.ParcelFileDescriptor;
 import android.os.Process;
 import android.os.RemoteException;
+import android.os.UserManager;
 import android.provider.Telephony;
 import android.provider.Telephony.Mms;
 import android.provider.Telephony.MmsSms;
@@ -1184,9 +1185,10 @@
 
     private void initMsgList() throws RemoteException {
         if (V) Log.d(TAG, "initMsgList");
+        UserManager manager = UserManager.get(mContext);
+        if (manager == null || manager.isUserUnlocked()) return;
 
-        if(mEnableSmsMms) {
-
+        if (mEnableSmsMms) {
             HashMap<Long, Msg> msgListSms = new HashMap<Long, Msg>();
 
             Cursor c = mResolver.query(Sms.CONTENT_URI,
@@ -2361,21 +2363,19 @@
         /* Approved MAP spec errata 3445 states that read status initiated
          * by the MCE shall change the MSE read status. */
         if (type == TYPE.SMS_GSM || type == TYPE.SMS_CDMA) {
-            Uri uri = Sms.Inbox.CONTENT_URI;
+            Uri uri = ContentUris.withAppendedId(Sms.CONTENT_URI, handle);
             ContentValues contentValues = new ContentValues();
             contentValues.put(Sms.READ, statusValue);
             contentValues.put(Sms.SEEN, statusValue);
-            String where = Sms._ID+"="+handle;
             String values = contentValues.toString();
-            if (D) Log.d(TAG, " -> SMS Uri: " + uri.toString() +
-                    " Where " + where + " values " + values);
+            if (D) Log.d(TAG, " -> SMS Uri: " + uri.toString() + " values " + values);
             synchronized(getMsgListSms()) {
                 Msg msg = getMsgListSms().get(handle);
                 if(msg != null) { // This will always be the case
                     msg.flagRead = statusValue;
                 }
             }
-            count = mResolver.update(uri, contentValues, where, null);
+            count = mResolver.update(uri, contentValues, null, null);
             if (D) Log.d(TAG, " -> "+count +" rows updated!");
 
         } else if (type == TYPE.MMS) {
@@ -2455,8 +2455,16 @@
         long folderId = -1;
 
         if (recipientList == null) {
-            if (D) Log.d(TAG, "empty recipient list");
-            return -1;
+            if (folderElement.getName().equalsIgnoreCase(BluetoothMapContract.FOLDER_NAME_DRAFT)) {
+                BluetoothMapbMessage.vCard empty =
+                    new BluetoothMapbMessage.vCard("", "", null, null, 0);
+                recipientList = new ArrayList<BluetoothMapbMessage.vCard>();
+                recipientList.add(empty);
+                Log.w(TAG, "Added empty recipient to draft message");
+            } else {
+                Log.e(TAG, "Trying to send a message with no recipients");
+                return -1;
+            }
         }
 
         if ( msg.getType().equals(TYPE.EMAIL) ) {
@@ -3331,6 +3339,8 @@
     private void resendPendingMessages() {
         /* Send pending messages in outbox */
         String where = "type = " + Sms.MESSAGE_TYPE_OUTBOX;
+        UserManager manager = UserManager.get(mContext);
+        if (manager == null || !manager.isUserUnlocked()) return;
         Cursor c = mResolver.query(Sms.CONTENT_URI, SMS_PROJECTION, where, null,
                 null);
         try {
diff --git a/src/com/android/bluetooth/map/BluetoothMapMessageListingElement.java b/src/com/android/bluetooth/map/BluetoothMapMessageListingElement.java
index 88e5605..832060e 100644
--- a/src/com/android/bluetooth/map/BluetoothMapMessageListingElement.java
+++ b/src/com/android/bluetooth/map/BluetoothMapMessageListingElement.java
@@ -27,6 +27,7 @@
 import android.util.Xml;
 
 import com.android.bluetooth.map.BluetoothMapUtils.TYPE;
+import com.android.bluetooth.util.Interop;
 
 public class BluetoothMapMessageListingElement
     implements Comparable<BluetoothMapMessageListingElement> {
@@ -276,9 +277,17 @@
                     BluetoothMapUtils.getMapHandle(mCpHandle, mType));
             if(mSubject != null){
                 String stripped = BluetoothMapUtils.stripInvalidChars(mSubject);
+
+                if (Interop.matchByAddress(Interop.INTEROP_MAP_ASCIIONLY,
+                        BluetoothMapService.getRemoteDevice().getAddress())) {
+                    stripped = stripped.replaceAll("[\\P{ASCII}&\"><]", "");
+                    if (stripped.isEmpty()) stripped = "---";
+                }
+
                 xmlMsgElement.attribute(null, "subject",
                         stripped.substring(0,  stripped.length() < 256 ? stripped.length() : 256));
             }
+
             if(mDateTime != 0)
                 xmlMsgElement.attribute(null, "datetime", this.getDateTimeString());
             if(mSenderName != null)
diff --git a/src/com/android/bluetooth/map/BluetoothMapObexServer.java b/src/com/android/bluetooth/map/BluetoothMapObexServer.java
index f0c26b0..00b2902 100644
--- a/src/com/android/bluetooth/map/BluetoothMapObexServer.java
+++ b/src/com/android/bluetooth/map/BluetoothMapObexServer.java
@@ -24,6 +24,7 @@
 import android.os.Message;
 import android.os.ParcelUuid;
 import android.os.RemoteException;
+import android.os.UserManager;
 import android.text.format.DateUtils;
 import android.util.Log;
 
@@ -402,6 +403,11 @@
         return ResponseCodes.OBEX_HTTP_OK;
     }
 
+    private boolean isUserUnlocked() {
+        UserManager manager = UserManager.get(mContext);
+        return (manager == null || manager.isUserUnlocked());
+    }
+
     @Override
     public int onPut(final Operation op) {
         if (D) Log.d(TAG, "onPut(): enter");
@@ -431,26 +437,34 @@
                      return ResponseCodes.OBEX_HTTP_OK;
                 }
                 return updateInbox();
-            }else if(type.equals(TYPE_SET_NOTIFICATION_REGISTRATION)) {
+            } else if (type.equals(TYPE_SET_NOTIFICATION_REGISTRATION)) {
                 if(V) {
                     Log.d(TAG,"TYPE_SET_NOTIFICATION_REGISTRATION: NotificationStatus: "
                             + appParams.getNotificationStatus());
                 }
                 return mObserver.setNotificationRegistration(appParams.getNotificationStatus());
-            }else if(type.equals(TYPE_SET_NOTIFICATION_FILTER)) {
+            } else if (type.equals(TYPE_SET_NOTIFICATION_FILTER)) {
                 if(V) {
                     Log.d(TAG,"TYPE_SET_NOTIFICATION_FILTER: NotificationFilter: "
                             + appParams.getNotificationFilter());
                 }
+                if (!isUserUnlocked()) {
+                    Log.e(TAG, "Storage locked, " + type + " failed");
+                    return ResponseCodes.OBEX_HTTP_UNAVAILABLE;
+                }
                 mObserver.setNotificationFilter(appParams.getNotificationFilter());
                 return ResponseCodes.OBEX_HTTP_OK;
-            } else if(type.equals(TYPE_SET_MESSAGE_STATUS)) {
+            } else if (type.equals(TYPE_SET_MESSAGE_STATUS)) {
                 if(V) {
                     Log.d(TAG,"TYPE_SET_MESSAGE_STATUS: " +
                               "StatusIndicator: " + appParams.getStatusIndicator()
                             + ", StatusValue: " + appParams.getStatusValue()
                             + ", ExtentedData: " + "" ); // TODO:   appParams.getExtendedImData());
                 }
+                if (!isUserUnlocked()) {
+                    Log.e(TAG, "Storage locked, " + type + " failed");
+                    return ResponseCodes.OBEX_HTTP_UNAVAILABLE;
+                }
                 return setMessageStatus(name, appParams);
             } else if (type.equals(TYPE_MESSAGE)) {
                 if(V) {
@@ -458,6 +472,10 @@
                             + ", retry: " + appParams.getRetry()
                             + ", charset: " + appParams.getCharset());
                 }
+                if (!isUserUnlocked()) {
+                    Log.e(TAG, "Storage locked, " + type + " failed");
+                    return ResponseCodes.OBEX_HTTP_UNAVAILABLE;
+                }
                 return pushMessage(op, name, appParams, mMessageVersion);
             } else if (type.equals(TYPE_SET_OWNER_STATUS)) {
                 if(V) {
@@ -916,6 +934,10 @@
                     Log.d(TAG,"FilterConvoId = " + ((tmpLongLong == null) ? "" :
                         Long.toHexString(tmpLongLong.getLeastSignificantBits()) ) );
                 }
+                if (!isUserUnlocked()) {
+                    Log.e(TAG, "Storage locked, " +  type + " failed");
+                    return ResponseCodes.OBEX_HTTP_UNAVAILABLE;
+                }
                 // Block until all packets have been send.
                 return sendMessageListingRsp(op, appParams, name);
 
@@ -930,6 +952,10 @@
                     Log.d(TAG,"FilterReadStatus = " + appParams.getFilterReadStatus());
                     Log.d(TAG,"FilterRecipient = " + appParams.getFilterRecipient());
                 }
+                if (!isUserUnlocked()) {
+                    Log.e(TAG, "Storage locked, " + type + " failed");
+                    return ResponseCodes.OBEX_HTTP_UNAVAILABLE;
+                }
                 // Block until all packets have been send.
                 return sendConvoListingRsp(op, appParams,name);
             } else if (type.equals(TYPE_GET_MAS_INSTANCE_INFORMATION)) {
@@ -947,6 +973,10 @@
                             ", Charset = " + appParams.getCharset() +
                             ", FractionRequest = " + appParams.getFractionRequest());
                 }
+                if (!isUserUnlocked()) {
+                    Log.e(TAG, "Storage locked, " + type + " failed");
+                    return ResponseCodes.OBEX_HTTP_UNAVAILABLE;
+                }
                 // Block until all packets have been send.
                 return sendGetMessageRsp(op, name, appParams, mMessageVersion);
             } else {
@@ -1028,8 +1058,8 @@
                 return ResponseCodes.OBEX_HTTP_BAD_REQUEST;
             }
             Log.v(TAG,"sendMessageListingRsp: has sms " + folderToList.hasSmsMmsContent() +
-                    "has email " + folderToList.hasEmailContent() +
-                    "has IM " + folderToList.hasImContent() );
+                    ", has email " + folderToList.hasEmailContent() +
+                    ", has IM " + folderToList.hasImContent() );
         }
 
         try {
diff --git a/src/com/android/bluetooth/map/BluetoothMapService.java b/src/com/android/bluetooth/map/BluetoothMapService.java
index 552bb09..cce506c 100755
--- a/src/com/android/bluetooth/map/BluetoothMapService.java
+++ b/src/com/android/bluetooth/map/BluetoothMapService.java
@@ -135,7 +135,7 @@
     private HashMap<BluetoothMapAccountItem, BluetoothMapMasInstance> mMasInstanceMap =
             new HashMap<BluetoothMapAccountItem, BluetoothMapMasInstance>(1);
 
-    private static BluetoothDevice mRemoteDevice = null;
+    private static BluetoothDevice mRemoteDevice = null; // The remote connected device - protect access
 
     private ArrayList<BluetoothMapAccountItem> mEnabledAccounts = null;
     private static String sRemoteDeviceName = null;
diff --git a/src/com/android/bluetooth/opp/BluetoothOppHandoverReceiver.java b/src/com/android/bluetooth/opp/BluetoothOppHandoverReceiver.java
index 2a51aa0..38873da 100644
--- a/src/com/android/bluetooth/opp/BluetoothOppHandoverReceiver.java
+++ b/src/com/android/bluetooth/opp/BluetoothOppHandoverReceiver.java
@@ -42,31 +42,31 @@
                 if (D) Log.d(TAG, "No device attached to handover intent.");
                 return;
             }
+
+            String mimeType = intent.getType();
+            ArrayList<Uri> uris = new ArrayList<Uri>();
             if (action.equals(Constants.ACTION_HANDOVER_SEND)) {
-                String type = intent.getType();
                 Uri stream = (Uri)intent.getParcelableExtra(Intent.EXTRA_STREAM);
-                if (stream != null && type != null) {
-                    // Save type/stream, will be used when adding transfer
-                    // session to DB.
-                    BluetoothOppManager.getInstance(context).saveSendingFileInfo(type,
-                            stream.toString(), true);
-                } else {
-                    if (D) Log.d(TAG, "No mimeType or stream attached to handover request");
-                }
+                if (stream != null) uris.add(stream);
             } else if (action.equals(Constants.ACTION_HANDOVER_SEND_MULTIPLE)) {
-                ArrayList<Uri> uris = new ArrayList<Uri>();
-                String mimeType = intent.getType();
                 uris = intent.getParcelableArrayListExtra(Intent.EXTRA_STREAM);
-                if (mimeType != null && uris != null) {
-                    BluetoothOppManager.getInstance(context).saveSendingFileInfo(mimeType,
-                            uris, true);
-                } else {
-                    if (D) Log.d(TAG, "No mimeType or stream attached to handover request");
-                    return;
-                }
             }
-            // we already know where to send to
-            BluetoothOppManager.getInstance(context).startTransfer(device);
+
+            if (mimeType != null && uris != null && !uris.isEmpty()) {
+                final String finalType = mimeType;
+                final ArrayList<Uri> finalUris = uris;
+                Thread t = new Thread(new Runnable() {
+                    public void run() {
+                        BluetoothOppManager.getInstance(context).saveSendingFileInfo(finalType,
+                            finalUris, true);
+                        BluetoothOppManager.getInstance(context).startTransfer(device);
+                    }
+                });
+                t.start();
+            } else {
+                if (D) Log.d(TAG, "No mimeType or stream attached to handover request");
+                return;
+            }
         } else if (action.equals(Constants.ACTION_WHITELIST_DEVICE)) {
             BluetoothDevice device =
                     (BluetoothDevice)intent.getParcelableExtra(BluetoothDevice.EXTRA_DEVICE);
diff --git a/src/com/android/bluetooth/opp/BluetoothOppObexClientSession.java b/src/com/android/bluetooth/opp/BluetoothOppObexClientSession.java
index 0f23bd3..59e7848 100644
--- a/src/com/android/bluetooth/opp/BluetoothOppObexClientSession.java
+++ b/src/com/android/bluetooth/opp/BluetoothOppObexClientSession.java
@@ -476,13 +476,13 @@
                         outputStream.write(buffer, 0, readLength);
 
                         position += readLength;
+                        /* check remote accept or reject */
+                        responseCode = putOperation.getResponseCode();
 
                         mCallback.removeMessages(BluetoothOppObexSession.MSG_CONNECT_TIMEOUT);
                         synchronized (this) {
                             mWaitingForRemote = false;
                         }
-                        /* check remote accept or reject */
-                        responseCode = putOperation.getResponseCode();
 
                         if (responseCode == ResponseCodes.OBEX_HTTP_CONTINUE
                                 || responseCode == ResponseCodes.OBEX_HTTP_OK) {
@@ -595,7 +595,7 @@
             } finally {
                 try {
                     if (outputStream != null) {
-                      outputStream.close();
+                        outputStream.close();
                     }
 
                     // Close InputStream and remove SendFileInfo from map
diff --git a/src/com/android/bluetooth/opp/BluetoothOppSendFileInfo.java b/src/com/android/bluetooth/opp/BluetoothOppSendFileInfo.java
index 2a9cadb..f60f06c 100644
--- a/src/com/android/bluetooth/opp/BluetoothOppSendFileInfo.java
+++ b/src/com/android/bluetooth/opp/BluetoothOppSendFileInfo.java
@@ -123,6 +123,7 @@
                 Log.e(TAG, "generateFileInfo: " + e);
                 return new BluetoothOppSendFileInfo(fileName, contentType, length, null, 0);
             }
+
             if (metadataCursor != null) {
                 try {
                     if (metadataCursor.moveToFirst()) {
diff --git a/src/com/android/bluetooth/opp/BluetoothOppUtility.java b/src/com/android/bluetooth/opp/BluetoothOppUtility.java
index 65588f1..15f4a89 100644
--- a/src/com/android/bluetooth/opp/BluetoothOppUtility.java
+++ b/src/com/android/bluetooth/opp/BluetoothOppUtility.java
@@ -374,6 +374,9 @@
 
     static void putSendFileInfo(Uri uri, BluetoothOppSendFileInfo sendFileInfo) {
         if (D) Log.d(TAG, "putSendFileInfo: uri=" + uri + " sendFileInfo=" + sendFileInfo);
+        if (sendFileInfo == BluetoothOppSendFileInfo.SEND_FILE_INFO_ERROR) {
+            Log.e(TAG, "putSendFileInfo: bad sendFileInfo, URI: " + uri);
+        }
         sSendFileMap.put(uri, sendFileInfo);
     }
 
diff --git a/src/com/android/bluetooth/pan/PanService.java b/src/com/android/bluetooth/pan/PanService.java
index 05f92ff..6a43b93 100755
--- a/src/com/android/bluetooth/pan/PanService.java
+++ b/src/com/android/bluetooth/pan/PanService.java
@@ -440,8 +440,8 @@
         if (prevState == state) return;
         if (remote_role == BluetoothPan.LOCAL_PANU_ROLE) {
             if (state == BluetoothProfile.STATE_CONNECTED) {
-                if((!mTetherOn)||(local_role == BluetoothPan.LOCAL_PANU_ROLE)){
-                    if(DBG) Log.d(TAG, "handlePanDeviceStateChange BT tethering is off/Local role"
+                if ((!mTetherOn) || (local_role == BluetoothPan.LOCAL_PANU_ROLE)) {
+                    if (DBG) Log.d(TAG, "handlePanDeviceStateChange BT tethering is off/Local role"
                             + " is PANU drop the connection");
                     mPanDevices.remove(device);
                     disconnectPanNative(Utils.getByteAddress(device));
diff --git a/src/com/android/bluetooth/util/Interop.java b/src/com/android/bluetooth/util/Interop.java
new file mode 100644
index 0000000..4861c15
--- /dev/null
+++ b/src/com/android/bluetooth/util/Interop.java
@@ -0,0 +1,87 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.bluetooth.util;
+
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * Centralized Bluetooth Interoperability workaround utilities and database.
+ * This is the Java version. An analagous native version can be found
+ * in /system/bt/devices/include/interop_database.h.
+ */
+public class Interop {
+
+  /**
+   * Simple interop entry consisting of a workarond id (see below)
+   * and a (partial or complete) Bluetooth device address string
+   * to match against.
+   */
+  private static class Entry {
+    String address;
+    int workaround_id;
+
+    public Entry(int workaround_id, String address) {
+      this.workaround_id = workaround_id;
+      this.address = address;
+    }
+  }
+
+  /**
+   * The actual "database" of interop entries.
+   */
+  private static List<Entry> entries = null;
+
+  /**
+   * Workaround ID for deivces which do not accept non-ASCII
+   * characters in SMS messages.
+   */
+  public static final int INTEROP_MAP_ASCIIONLY = 1;
+
+  /**
+   * Initializes the interop datbase with the relevant workaround
+   * entries.
+   * When adding entries, please provide a description for each
+   * device as to what problem the workaround addresses.
+   */
+  private static void lazyInitInteropDatabase() {
+    if (entries != null) return;
+    entries = new ArrayList<Entry>();
+
+    /** Mercedes Benz NTG 4.5 does not handle non-ASCII characters in SMS */
+    entries.add(new Entry(INTEROP_MAP_ASCIIONLY, "00:26:e8"));
+  }
+
+  /**
+   * Checks wheter a given device identified by |address| is a match
+   * for a given workaround identified by |workaround_id|.
+   * Return true if the address matches, false otherwise.
+   */
+  public static boolean matchByAddress(int workaround_id, String address) {
+    if (address == null || address.isEmpty()) return false;
+
+    lazyInitInteropDatabase();
+    for (Entry entry : entries) {
+      if (entry.workaround_id == workaround_id &&
+          entry.address.startsWith(address.toLowerCase())) {
+        return true;
+      }
+    }
+
+    return false;
+  }
+}