DO NOT MERGE Fix SMS delivered successfully but stuck SENDING issue am: abb54d6d36 am: 6e9165b908  -s ours am: 44c3652b4c am: 5a1ae16f4e am: d59eb61a3e  -s ours
am: d6e1fa7c20

* commit 'd6e1fa7c20fbddbf9ea5fa7b042bfdf276c1463a':
  DO NOT MERGE Fix SMS delivered successfully but stuck SENDING issue
diff --git a/Android.mk b/Android.mk
index e03acdc..7b724d0 100644
--- a/Android.mk
+++ b/Android.mk
@@ -22,7 +22,7 @@
 
 LOCAL_JNI_SHARED_LIBRARIES := libbluetooth_jni
 LOCAL_JAVA_LIBRARIES := javax.obex telephony-common libprotobuf-java-micro
-LOCAL_STATIC_JAVA_LIBRARIES := com.android.vcard  bluetooth.mapsapi sap-api-java-static
+LOCAL_STATIC_JAVA_LIBRARIES := com.android.vcard  bluetooth.mapsapi sap-api-java-static android-support-v4
 
 LOCAL_REQUIRED_MODULES := bluetooth.default
 LOCAL_MULTILIB := 32
diff --git a/AndroidManifest.xml b/AndroidManifest.xml
index 85910aa..3652fd3 100644
--- a/AndroidManifest.xml
+++ b/AndroidManifest.xml
@@ -27,6 +27,7 @@
     <uses-permission android:name="android.permission.BLUETOOTH_MAP" />
     <uses-permission android:name="android.permission.WAKE_LOCK" />
     <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
+    <uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
     <uses-permission android:name="android.permission.READ_CONTACTS" />
     <!-- WRITE_CONTACTS is used for test cases only -->
     <uses-permission android:name="android.permission.WRITE_CONTACTS" />
@@ -80,6 +81,14 @@
                     android:pathPrefix="/btopp"
                     android:permission="android.permission.ACCESS_BLUETOOTH_SHARE" />
         </provider>
+        <provider android:name="android.support.v4.content.FileProvider"
+            android:authorities="com.google.android.bluetooth.fileprovider"
+            android:grantUriPermissions="true"
+            android:exported="false">
+            <meta-data
+                android:name="android.support.FILE_PROVIDER_PATHS"
+                android:resource="@xml/file_paths" />
+        </provider>
         <service
             android:process="@string/process"
             android:name = ".btservice.AdapterService">
diff --git a/res/values-fa/test_strings.xml b/res/values-fa/test_strings.xml
index 000f3f7..0db14ba 100644
--- a/res/values-fa/test_strings.xml
+++ b/res/values-fa/test_strings.xml
@@ -1,7 +1,7 @@
 <?xml version="1.0" encoding="UTF-8"?>
 <resources xmlns:android="http://schemas.android.com/apk/res/android"
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
-    <string name="hello" msgid="1740533743008967039">"سلام بر دنیا، تست فعالیت"</string>
+    <string name="hello" msgid="1740533743008967039">"سلام به همه، تست فعالیت"</string>
     <string name="app_name" msgid="1203877025577761792">"اشتراک بلوتوث"</string>
     <string name="insert_record" msgid="1450997173838378132">"درج سابقه"</string>
     <string name="update_record" msgid="2480425402384910635">"تأیید سابقه"</string>
diff --git a/res/values-nl/strings.xml b/res/values-nl/strings.xml
index ed657c0..45d784b 100644
--- a/res/values-nl/strings.xml
+++ b/res/values-nl/strings.xml
@@ -24,9 +24,9 @@
     <string name="unknown_device" msgid="9221903979877041009">"Onbekend apparaat"</string>
     <string name="unknownNumber" msgid="4994750948072751566">"Onbekend"</string>
     <string name="airplane_error_title" msgid="2683839635115739939">"Vliegtuigmodus"</string>
-    <string name="airplane_error_msg" msgid="8698965595254137230">"U kunt Bluetooth niet gebruiken in Vliegtuigmodus."</string>
+    <string name="airplane_error_msg" msgid="8698965595254137230">"Je kunt Bluetooth niet gebruiken in Vliegtuigmodus."</string>
     <string name="bt_enable_title" msgid="8657832550503456572"></string>
-    <string name="bt_enable_line1" msgid="7203551583048149">"Als u Bluetooth-services wilt gebruiken, moet u eerst Bluetooth inschakelen."</string>
+    <string name="bt_enable_line1" msgid="7203551583048149">"Als je Bluetooth-services wilt gebruiken, moet je eerst Bluetooth inschakelen."</string>
     <string name="bt_enable_line2" msgid="4341936569415937994">"Bluetooth nu inschakelen?"</string>
     <string name="bt_enable_cancel" msgid="1988832367505151727">"Annuleren"</string>
     <string name="bt_enable_ok" msgid="3432462749994538265">"Inschakelen"</string>
@@ -37,8 +37,8 @@
     <string name="incoming_file_confirm_timeout_ok" msgid="1414676773249857278">"OK"</string>
     <string name="incoming_file_confirm_timeout_content" msgid="172779756093975981">"Er is een time-out opgetreden bij het accepteren van een inkomend bestand van \'<xliff:g id="SENDER">%1$s</xliff:g>\'"</string>
     <string name="incoming_file_confirm_Notification_title" msgid="2958227698135117210">"Delen via Bluetooth: inkomend bestand"</string>
-    <string name="incoming_file_confirm_Notification_caption" msgid="6671081128475981157">"Wilt u dit bestand ontvangen?"</string>
-    <string name="incoming_file_toast_msg" msgid="1733710749992901811">"Er is een inkomend bestand van een ander apparaat beschikbaar. Bevestig dat u dit bestand wilt ontvangen."</string>
+    <string name="incoming_file_confirm_Notification_caption" msgid="6671081128475981157">"Wilt je dit bestand ontvangen?"</string>
+    <string name="incoming_file_toast_msg" msgid="1733710749992901811">"Er is een inkomend bestand van een ander apparaat beschikbaar. Bevestig dat je dit bestand wilt ontvangen."</string>
     <string name="notification_receiving" msgid="4674648179652543984">"Delen via Bluetooth: <xliff:g id="FILE">%1$s</xliff:g> ontvangen"</string>
     <string name="notification_received" msgid="3324588019186687985">"Delen via Bluetooth: <xliff:g id="FILE">%1$s</xliff:g> ontvangen"</string>
     <string name="notification_received_fail" msgid="3619350997285714746">"Delen via Bluetooth: bestand <xliff:g id="FILE">%1$s</xliff:g> niet ontvangen"</string>
@@ -79,7 +79,7 @@
     <string name="not_exist_file_desc" msgid="4059531573790529229">"Het bestand bestaat niet. \n"</string>
     <string name="enabling_progress_title" msgid="436157952334723406">"Een ogenblik geduld..."</string>
     <string name="enabling_progress_content" msgid="4601542238119927904">"Bluetooth inschakelen…"</string>
-    <string name="bt_toast_1" msgid="972182708034353383">"Het bestand wordt ontvangen. U kunt de voortgang controleren in het venster \'Meldingen\'."</string>
+    <string name="bt_toast_1" msgid="972182708034353383">"Het bestand wordt ontvangen. Je kunt de voortgang controleren in het venster \'Meldingen\'."</string>
     <string name="bt_toast_2" msgid="8602553334099066582">"Het bestand kan niet worden opgehaald."</string>
     <string name="bt_toast_3" msgid="6707884165086862518">"Ontvangen van bestand van \'<xliff:g id="SENDER">%1$s</xliff:g>\' is beëindigd"</string>
     <string name="bt_toast_4" msgid="4678812947604395649">"Bestand verzenden naar \'<xliff:g id="RECIPIENT">%1$s</xliff:g>\'"</string>
@@ -124,7 +124,7 @@
     <string name="transfer_clear_dlg_title" msgid="2953444575556460386">"Wissen"</string>
     <string name="bluetooth_map_settings_save" msgid="7635491847388074606">"Opslaan"</string>
     <string name="bluetooth_map_settings_cancel" msgid="9205350798049865699">"Annuleren"</string>
-    <string name="bluetooth_map_settings_intro" msgid="6793938602201480648">"Selecteer de accounts die u wilt delen via Bluetooth. U moet nog steeds elke toegang tot de accounts accepteren wanneer er verbinding wordt gemaakt."</string>
+    <string name="bluetooth_map_settings_intro" msgid="6793938602201480648">"Selecteer de accounts die je wilt delen via Bluetooth. Je moet nog steeds elke toegang tot de accounts accepteren wanneer er verbinding wordt gemaakt."</string>
     <string name="bluetooth_map_settings_count" msgid="4557473074937024833">"Plaatsen over:"</string>
     <string name="bluetooth_map_settings_app_icon" msgid="7105805610929114707">"App-pictogram"</string>
     <string name="bluetooth_map_settings_title" msgid="7420332483392851321">"Instellingen voor delen van berichten via Bluetooth"</string>
diff --git a/res/values-ro/strings.xml b/res/values-ro/strings.xml
index 8fa4161..3b3238b 100644
--- a/res/values-ro/strings.xml
+++ b/res/values-ro/strings.xml
@@ -19,17 +19,17 @@
     <string name="permlab_bluetoothShareManager" msgid="311492132450338925">"Accesaţi managerul de descărcare."</string>
     <string name="permdesc_bluetoothShareManager" msgid="8930572979123190223">"Permite aplicațiilor să acceseze managerul Distribuire prin Bluetooth și să-l utilizeze la transferul fișierelor."</string>
     <string name="permlab_bluetoothWhitelist" msgid="7091552898592306386">"Acces la dispozitivele Bluetooth din lista albă."</string>
-    <string name="permdesc_bluetoothWhitelist" msgid="5494513855192170109">"Permite aplicației să treacă temporar pe lista albă un dispozitiv Bluetooth, permiţându-i să trimită fişiere la acest dispozitiv fără confirmare din partea utilizatorului."</string>
+    <string name="permdesc_bluetoothWhitelist" msgid="5494513855192170109">"Permite aplicației să treacă temporar pe lista albă un dispozitiv Bluetooth, permițându-i să trimită fişiere la acest dispozitiv fără confirmare din partea utilizatorului."</string>
     <string name="bt_share_picker_label" msgid="6268100924487046932">"Bluetooth"</string>
     <string name="unknown_device" msgid="9221903979877041009">"Dispozitiv necunoscut"</string>
     <string name="unknownNumber" msgid="4994750948072751566">"Necunoscut"</string>
     <string name="airplane_error_title" msgid="2683839635115739939">"Mod Avion"</string>
     <string name="airplane_error_msg" msgid="8698965595254137230">"Nu puteţi utiliza Bluetooth în modul Avion."</string>
     <string name="bt_enable_title" msgid="8657832550503456572"></string>
-    <string name="bt_enable_line1" msgid="7203551583048149">"Pentru a putea utiliza serviciile Bluetooth, trebuie mai întâi să le activaţi."</string>
-    <string name="bt_enable_line2" msgid="4341936569415937994">"Activaţi acum Bluetooth?"</string>
+    <string name="bt_enable_line1" msgid="7203551583048149">"Pentru a putea utiliza serviciile Bluetooth, trebuie mai întâi să le activați."</string>
+    <string name="bt_enable_line2" msgid="4341936569415937994">"Activați acum Bluetooth?"</string>
     <string name="bt_enable_cancel" msgid="1988832367505151727">"Anulați"</string>
-    <string name="bt_enable_ok" msgid="3432462749994538265">"Activaţi"</string>
+    <string name="bt_enable_ok" msgid="3432462749994538265">"Activați"</string>
     <string name="incoming_file_confirm_title" msgid="8139874248612182627">"Transfer de fişier"</string>
     <string name="incoming_file_confirm_content" msgid="2752605552743148036">"Acceptați fișierul primit?"</string>
     <string name="incoming_file_confirm_cancel" msgid="2973321832477704805">"Refuzaţi"</string>
@@ -52,7 +52,7 @@
     <string name="download_line3" msgid="4384821622908676061">"Dimensiunea fişierului: <xliff:g id="SIZE">%1$s</xliff:g>"</string>
     <string name="download_line4" msgid="8535996869722666525"></string>
     <string name="download_line5" msgid="3069560415845295386">"Se primeşte fişierul..."</string>
-    <string name="download_cancel" msgid="9177305996747500768">"Opriţi"</string>
+    <string name="download_cancel" msgid="9177305996747500768">"Opriți"</string>
     <string name="download_ok" msgid="5000360731674466039">"Ascundeţi"</string>
     <string name="incoming_line1" msgid="2127419875681087545">"De la"</string>
     <string name="incoming_line2" msgid="3348994249285315873">"Numele fișierului"</string>
@@ -70,7 +70,7 @@
     <string name="upload_succ_ok" msgid="7705428476405478828">"OK"</string>
     <string name="upload_fail_line1" msgid="7899394672421491701">"Fişierul nu a fost trimis la „<xliff:g id="RECIPIENT">%1$s</xliff:g>”."</string>
     <string name="upload_fail_line1_2" msgid="2108129204050841798">"Fişier: <xliff:g id="FILE">%1$s</xliff:g>"</string>
-    <string name="upload_fail_ok" msgid="5807702461606714296">"Încercaţi din nou"</string>
+    <string name="upload_fail_ok" msgid="5807702461606714296">"Încercați din nou"</string>
     <string name="upload_fail_cancel" msgid="9118496285835687125">"Închideţi"</string>
     <string name="bt_error_btn_ok" msgid="5965151173011534240">"OK"</string>
     <string name="unknown_file" msgid="6092727753965095366">"Fişier necunoscut"</string>
@@ -88,7 +88,7 @@
     <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>
     <string name="bt_sm_2_2" msgid="2965243265852680543">"Spaţiu necesar: <xliff:g id="SIZE">%1$s</xliff:g>"</string>
-    <string name="ErrorTooManyRequests" msgid="8578277541472944529">"Există prea multe solicitări în curs de procesare. Încercaţi din nou mai târziu."</string>
+    <string name="ErrorTooManyRequests" msgid="8578277541472944529">"Există prea multe solicitări în curs de procesare. Încercați din nou mai târziu."</string>
     <string name="status_pending" msgid="2503691772030877944">"Transferul fişierului nu a început încă."</string>
     <string name="status_running" msgid="6562808920311008696">"Transferul fişierului este în curs de desfăşurare."</string>
     <string name="status_success" msgid="239573225847565868">"Transferul fişierului s-a încheiat."</string>
diff --git a/res/xml/file_paths.xml b/res/xml/file_paths.xml
new file mode 100644
index 0000000..72d848d
--- /dev/null
+++ b/res/xml/file_paths.xml
@@ -0,0 +1,3 @@
+<paths xmlns:android="https://schemas.android.com/apk/res/android">
+    <external-path name="bluetooth" path="/" />
+</paths>
diff --git a/src/com/android/bluetooth/Utils.java b/src/com/android/bluetooth/Utils.java
index a16d3b9..6a467f9 100644
--- a/src/com/android/bluetooth/Utils.java
+++ b/src/com/android/bluetooth/Utils.java
@@ -288,14 +288,7 @@
             return true;
         }
         // Enforce location permission for apps targeting M and later versions
-        boolean enforceLocationPermission = true;
-        try {
-            enforceLocationPermission = context.getPackageManager().getApplicationInfo(
-                    callingPackage, 0).targetSdkVersion >= Build.VERSION_CODES.M;
-        } catch (PackageManager.NameNotFoundException e) {
-            // In case of exception, enforce permission anyway
-        }
-        if (enforceLocationPermission) {
+        if (isMApp(context, callingPackage)) {
             throw new SecurityException("Need ACCESS_COARSE_LOCATION or "
                     + "ACCESS_FINE_LOCATION permission to get scan results");
         } else {
@@ -317,6 +310,20 @@
                 android.Manifest.permission.PEERS_MAC_ADDRESS) == PackageManager.PERMISSION_GRANTED;
     }
 
+    public static boolean isLegacyForegroundApp(Context context, String pkgName) {
+        return !isMApp(context, pkgName) && isForegroundApp(context, pkgName);
+    }
+
+    private static boolean isMApp(Context context, String pkgName) {
+        try {
+            return context.getPackageManager().getApplicationInfo(pkgName, 0)
+                    .targetSdkVersion >= Build.VERSION_CODES.M;
+        } catch (PackageManager.NameNotFoundException e) {
+            // In case of exception, assume M app
+        }
+        return true;
+    }
+
     /**
      * Return true if the specified package name is a foreground app.
      *
diff --git a/src/com/android/bluetooth/gatt/GattService.java b/src/com/android/bluetooth/gatt/GattService.java
index a04d5a3..26ef18d 100644
--- a/src/com/android/bluetooth/gatt/GattService.java
+++ b/src/com/android/bluetooth/gatt/GattService.java
@@ -641,12 +641,13 @@
     private boolean hasScanResultPermission(final ScanClient client) {
         final boolean requiresLocationEnabled =
                 getResources().getBoolean(R.bool.strict_location_check);
-        final boolean locationEnabled = Settings.Secure.getInt(getContentResolver(),
+        final boolean locationEnabledSetting = Settings.Secure.getInt(getContentResolver(),
                 Settings.Secure.LOCATION_MODE, Settings.Secure.LOCATION_MODE_OFF)
                 != Settings.Secure.LOCATION_MODE_OFF;
-
-        return (client.hasPeersMacAddressPermission ||
-                (client.hasLocationPermission && (!requiresLocationEnabled || locationEnabled)));
+        final boolean locationEnabled = !requiresLocationEnabled || locationEnabledSetting
+                || client.legacyForegroundApp;
+        return (client.hasPeersMacAddressPermission
+                || (client.hasLocationPermission && locationEnabled));
     }
 
     // Check if a scan record matches a specific filters.
@@ -1378,12 +1379,12 @@
         if (needsPrivilegedPermissionForScan(settings)) {
             enforcePrivilegedPermission();
         }
-        boolean hasLocationPermission = Utils.checkCallerHasLocationPermission(this,
-                mAppOps, callingPackage);
         final ScanClient scanClient = new ScanClient(appIf, isServer, settings, filters, storages);
-        scanClient.hasLocationPermission = hasLocationPermission;
+        scanClient.hasLocationPermission = Utils.checkCallerHasLocationPermission(this, mAppOps,
+                callingPackage);
         scanClient.hasPeersMacAddressPermission = Utils.checkCallerHasPeersMacAddressPermission(
                 this);
+        scanClient.legacyForegroundApp = Utils.isLegacyForegroundApp(this, callingPackage);
         mScanManager.startScan(scanClient);
     }
 
diff --git a/src/com/android/bluetooth/gatt/ScanClient.java b/src/com/android/bluetooth/gatt/ScanClient.java
index d42df39..64d3e1f 100644
--- a/src/com/android/bluetooth/gatt/ScanClient.java
+++ b/src/com/android/bluetooth/gatt/ScanClient.java
@@ -40,6 +40,8 @@
     boolean appDied;
     boolean hasLocationPermission;
     boolean hasPeersMacAddressPermission;
+    // Pre-M apps are allowed to get scan results even if location is disabled
+    boolean legacyForegroundApp;
 
     private static final ScanSettings DEFAULT_SCAN_SETTINGS = new ScanSettings.Builder()
             .setScanMode(ScanSettings.SCAN_MODE_LOW_LATENCY).build();
diff --git a/src/com/android/bluetooth/hfp/HeadsetStateMachine.java b/src/com/android/bluetooth/hfp/HeadsetStateMachine.java
index d226e7c..0cf5f33 100644
--- a/src/com/android/bluetooth/hfp/HeadsetStateMachine.java
+++ b/src/com/android/bluetooth/hfp/HeadsetStateMachine.java
@@ -2763,6 +2763,11 @@
         } else {
             Log.e(TAG,"processNoiceReductionEvent: AudioParamNrec is null ");
         }
+
+        if (mActiveScoDevice != null && mActiveScoDevice.equals(device)
+                && mAudioState == BluetoothHeadset.STATE_AUDIO_CONNECTED) {
+            setAudioParameters(device);
+        }
     }
 
     // 2 - WBS on
diff --git a/src/com/android/bluetooth/map/BluetoothMapContent.java b/src/com/android/bluetooth/map/BluetoothMapContent.java
index 28f5d42..7ced91b 100644
--- a/src/com/android/bluetooth/map/BluetoothMapContent.java
+++ b/src/com/android/bluetooth/map/BluetoothMapContent.java
@@ -117,7 +117,7 @@
     // MAP specification states that the default value for parameter mask are
     // the #REQUIRED attributes in the DTD, and not all enabled
     public static final long PARAMETER_MASK_ALL_ENABLED = 0xFFFFFFFFL;
-    public static final long PARAMETER_MASK_DEFAULT = 0x5E3L;
+    public static final long PARAMETER_MASK_DEFAULT = 0x5EBL;
     public static final long CONVO_PARAMETER_MASK_ALL_ENABLED = 0xFFFFFFFFL;
     public static final long CONVO_PARAMETER_MASK_DEFAULT =
             CONVO_PARAM_MASK_CONVO_NAME |
diff --git a/src/com/android/bluetooth/map/BluetoothMapContentObserver.java b/src/com/android/bluetooth/map/BluetoothMapContentObserver.java
index 417be25..9aae8cf 100644
--- a/src/com/android/bluetooth/map/BluetoothMapContentObserver.java
+++ b/src/com/android/bluetooth/map/BluetoothMapContentObserver.java
@@ -3040,6 +3040,9 @@
                     if(status != 0/*0 is success*/) {
                         msgInfo.statusDelivered = status;
                         if(D) Log.d(TAG, "msgInfo.statusDelivered = " + status);
+                        Sms.moveMessageToFolder(mContext, msgInfo.uri, Sms.MESSAGE_TYPE_FAILED, 0);
+                    } else {
+                        Sms.moveMessageToFolder(mContext, msgInfo.uri, Sms.MESSAGE_TYPE_SENT, 0);
                     }
                 }
                 if (msgInfo.partsDelivered == msgInfo.parts) {
diff --git a/src/com/android/bluetooth/opp/BluetoothOppUtility.java b/src/com/android/bluetooth/opp/BluetoothOppUtility.java
index ea465f4..58f4677 100644
--- a/src/com/android/bluetooth/opp/BluetoothOppUtility.java
+++ b/src/com/android/bluetooth/opp/BluetoothOppUtility.java
@@ -54,6 +54,7 @@
 import java.util.List;
 import java.util.concurrent.ConcurrentHashMap;
 
+import android.support.v4.content.FileProvider;
 /**
  * This class has some utilities for Opp application;
  */
@@ -186,7 +187,8 @@
             return;
         }
 
-        Uri path = Uri.parse(fileName);
+        Uri path = FileProvider.getUriForFile(context,
+                       "com.google.android.bluetooth.fileprovider", f);
         // If there is no scheme, then it must be a file
         if (path.getScheme() == null) {
             path = Uri.fromFile(new File(fileName));
@@ -196,7 +198,22 @@
             Intent activityIntent = new Intent(Intent.ACTION_VIEW);
             activityIntent.setDataAndTypeAndNormalize(path, mimetype);
 
+            List<ResolveInfo> resInfoList = context.getPackageManager()
+                .queryIntentActivities(activityIntent,
+                        PackageManager.MATCH_DEFAULT_ONLY);
+
+            // Grant permissions for any app that can handle a file to access it
+            for (ResolveInfo resolveInfo : resInfoList) {
+                String packageName = resolveInfo.activityInfo.packageName;
+                context.grantUriPermission(packageName, path,
+                        Intent.FLAG_GRANT_WRITE_URI_PERMISSION |
+                        Intent.FLAG_GRANT_READ_URI_PERMISSION);
+            }
+
             activityIntent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
+            activityIntent.setFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);
+            activityIntent.setFlags(Intent.FLAG_GRANT_WRITE_URI_PERMISSION);
+
             try {
                 if (V) Log.d(TAG, "ACTION_VIEW intent sent out: " + path + " / " + mimetype);
                 context.startActivity(activityIntent);
diff --git a/src/com/android/bluetooth/pbap/BluetoothPbapVcardManager.java b/src/com/android/bluetooth/pbap/BluetoothPbapVcardManager.java
index de8275e..748e7a5 100644
--- a/src/com/android/bluetooth/pbap/BluetoothPbapVcardManager.java
+++ b/src/com/android/bluetooth/pbap/BluetoothPbapVcardManager.java
@@ -623,7 +623,6 @@
         HandlerForStringBuffer buffer = null;
         try {
 
-            VCardFilter vcardfilter = new VCardFilter(ignorefilter ? null : filter);
             composer = new BluetoothPbapCallLogComposer(mContext);
             buffer = new HandlerForStringBuffer(op, ownerVCard);
             if (!composer.init(CallLog.Calls.CONTENT_URI, selection, null, CALLLOG_SORT_ORDER)
@@ -638,9 +637,6 @@
                     break;
                 }
                 String vcard = composer.createOneEntry(vcardType21);
-                if (vcard != null) {
-                    vcard = vcardfilter.apply(vcard, vcardType21);
-                }
                 if (vcard == null) {
                     Log.e(TAG,
                             "Failed to read a contact. Error reason: " + composer.getErrorReason());
diff --git a/src/com/android/bluetooth/sap/SapMessage.java b/src/com/android/bluetooth/sap/SapMessage.java
index 848745d..e836fba 100644
--- a/src/com/android/bluetooth/sap/SapMessage.java
+++ b/src/com/android/bluetooth/sap/SapMessage.java
@@ -28,7 +28,7 @@
     public static final String TAG = "SapMessage";
     public static final boolean DEBUG = SapService.DEBUG;
     public static final boolean VERBOSE = SapService.VERBOSE;
-    public static final boolean TEST = SapService.PTS_TEST;
+    public static final boolean TEST = false;
 
     /* Message IDs - SAP specification */
     public static final int ID_CONNECT_REQ        = 0x00;
@@ -404,10 +404,8 @@
         case ID_DISCONNECT_REQ:    /* No params */
             break;
         default:
-            if(TEST == false) {
-                Log.e(TAG, "Unknown request type");
-                return null;
-            }
+            Log.e(TAG, "Unknown request type");
+            return null;
         }
         return newMessage;
     }
@@ -456,18 +454,25 @@
         int paramId;
         int paramLength;
         boolean success = true;
+        int skipLen = 0;
+
         for(int i = 0; i < count; i++) {
             paramId = is.read();
             is.read(); // Skip the reserved byte
             paramLength = is.read();
             paramLength = paramLength << 8 | is.read();
+
+            // As per SAP spec padding should be 0-3 bytes
+            if ((paramLength % 4) != 0)
+                skipLen = 4 - (paramLength % 4);
+
             if(VERBOSE) Log.i(TAG, "parsing paramId: " + paramId + " with length: " + paramLength);
             switch(paramId) {
             case PARAM_MAX_MSG_SIZE_ID:
                 if(paramLength != PARAM_MAX_MSG_SIZE_LENGTH) {
                     Log.e(TAG, "Received PARAM_MAX_MSG_SIZE with wrong length: " +
                             paramLength + " skipping this parameter.");
-                    skip(is, paramLength + (4 - (paramLength % 4)));
+                    skip(is, paramLength + skipLen);
                     success = false;
                 } else {
                     mMaxMsgSize = is.read();
@@ -478,18 +483,18 @@
             case PARAM_COMMAND_APDU_ID:
                 mApdu = new byte[paramLength];
                 read(is, mApdu);
-                skip(is, 4 - (paramLength % 4));
+                skip(is, skipLen);
                 break;
             case PARAM_COMMAND_APDU7816_ID:
                 mApdu7816 = new byte[paramLength];
                 read(is, mApdu7816);
-                skip(is, 4 - (paramLength % 4));
+                skip(is, skipLen);
                 break;
             case PARAM_TRANSPORT_PROTOCOL_ID:
                 if(paramLength != PARAM_TRANSPORT_PROTOCOL_LENGTH) {
                     Log.e(TAG, "Received PARAM_TRANSPORT_PROTOCOL with wrong length: " +
                             paramLength + " skipping this parameter.");
-                    skip(is, paramLength + (4 - (paramLength % 4)));
+                    skip(is, paramLength + skipLen);
                     success = false;
                 } else {
                     mTransportProtocol = is.read();
@@ -497,95 +502,81 @@
                 }
                 break;
             case PARAM_CONNECTION_STATUS_ID:
-                // not needed - server -> client
-                if(TEST) {
-                    if(paramLength != PARAM_CONNECTION_STATUS_LENGTH) {
-                        Log.e(TAG, "Received PARAM_CONNECTION_STATUS with wrong length: " +
-                                paramLength + " skipping this parameter.");
-                        skip(is, paramLength + (4 - (paramLength % 4)));
-                        success = false;
-                    } else {
-                        mConnectionStatus = is.read();
-                        skip(is, 4 - PARAM_CONNECTION_STATUS_LENGTH);
-                    }
-                    break;
-                } // Fall through if TEST == false
+                // not needed for server role, but used for module test
+                if(paramLength != PARAM_CONNECTION_STATUS_LENGTH) {
+                    Log.e(TAG, "Received PARAM_CONNECTION_STATUS with wrong length: " +
+                            paramLength + " skipping this parameter.");
+                    skip(is, paramLength + skipLen);
+                    success = false;
+                } else {
+                    mConnectionStatus = is.read();
+                    skip(is, 4 - PARAM_CONNECTION_STATUS_LENGTH);
+                }
+                break;
             case PARAM_CARD_READER_STATUS_ID:
-                // not needed - server -> client
-                if(TEST) {
-                    if(paramLength != PARAM_CARD_READER_STATUS_LENGTH) {
-                        Log.e(TAG, "Received PARAM_CARD_READER_STATUS with wrong length: " +
-                                paramLength + " skipping this parameter.");
-                        skip(is, paramLength + (4 - (paramLength % 4)));
-                        success = false;
-                    } else {
-                        mCardReaderStatus = is.read();
-                        skip(is, 4 - PARAM_CARD_READER_STATUS_LENGTH);
-                    }
-                    break;
-                } // Fall through if TEST == false
+                // not needed for server role, but used for module test
+                if(paramLength != PARAM_CARD_READER_STATUS_LENGTH) {
+                    Log.e(TAG, "Received PARAM_CARD_READER_STATUS with wrong length: " +
+                            paramLength + " skipping this parameter.");
+                    skip(is, paramLength + skipLen);
+                    success = false;
+                } else {
+                    mCardReaderStatus = is.read();
+                    skip(is, 4 - PARAM_CARD_READER_STATUS_LENGTH);
+                }
+                break;
             case PARAM_STATUS_CHANGE_ID:
-                // not needed - server -> client
-                if(TEST) {
-                    if(paramLength != PARAM_STATUS_CHANGE_LENGTH) {
-                        Log.e(TAG, "Received PARAM_STATUS_CHANGE with wrong length: " +
-                                paramLength + " skipping this parameter.");
-                        skip(is, paramLength + (4 - (paramLength % 4)));
-                        success = false;
-                    } else {
-                        mStatusChange = is.read();
-                        skip(is, 4 - PARAM_STATUS_CHANGE_LENGTH);
-                    }
-                    break;
-                } // Fall through if TEST == false
+                // not needed for server role, but used for module test
+                if(paramLength != PARAM_STATUS_CHANGE_LENGTH) {
+                    Log.e(TAG, "Received PARAM_STATUS_CHANGE with wrong length: " +
+                            paramLength + " skipping this parameter.");
+                    skip(is, paramLength + skipLen);
+                    success = false;
+                } else {
+                    mStatusChange = is.read();
+                    skip(is, 4 - PARAM_STATUS_CHANGE_LENGTH);
+                }
+                break;
             case PARAM_RESULT_CODE_ID:
-                // not needed - server -> client
-                if(TEST) {
-                    if(paramLength != PARAM_RESULT_CODE_LENGTH) {
-                        Log.e(TAG, "Received PARAM_RESULT_CODE with wrong length: " +
-                                paramLength + " skipping this parameter.");
-                        skip(is, paramLength + (4 - (paramLength % 4)));
-                        success = false;
-                    } else {
-                        mResultCode = is.read();
-                        skip(is, 4 - PARAM_RESULT_CODE_LENGTH);
-                    }
-                    break;
-                } // Fall through if TEST == false
+                // not needed for server role, but used for module test
+                if(paramLength != PARAM_RESULT_CODE_LENGTH) {
+                    Log.e(TAG, "Received PARAM_RESULT_CODE with wrong length: " +
+                            paramLength + " skipping this parameter.");
+                    skip(is, paramLength + skipLen);
+                    success = false;
+                } else {
+                    mResultCode = is.read();
+                    skip(is, 4 - PARAM_RESULT_CODE_LENGTH);
+                }
+                break;
             case PARAM_DISCONNECT_TYPE_ID:
-                // not needed - server -> client
-                if(TEST) {
-                    if(paramLength != PARAM_DISCONNECT_TYPE_LENGTH) {
-                        Log.e(TAG, "Received PARAM_DISCONNECT_TYPE_ID with wrong length: " +
-                                paramLength + " skipping this parameter.");
-                        skip(is, paramLength + (4 - (paramLength % 4)));
-                        success = false;
-                    } else {
-                        mDisconnectionType = is.read();
-                        skip(is, 4 - PARAM_DISCONNECT_TYPE_LENGTH);
-                    }
-                    break;
-                } // Fall through if TEST == false
+                // not needed for server role, but used for module test
+                if(paramLength != PARAM_DISCONNECT_TYPE_LENGTH) {
+                    Log.e(TAG, "Received PARAM_DISCONNECT_TYPE_ID with wrong length: " +
+                            paramLength + " skipping this parameter.");
+                    skip(is, paramLength + skipLen);
+                    success = false;
+                } else {
+                    mDisconnectionType = is.read();
+                    skip(is, 4 - PARAM_DISCONNECT_TYPE_LENGTH);
+                }
+                break;
             case PARAM_RESPONSE_APDU_ID:
-                // not needed - server -> client
-                if(TEST) {
-                    mApduResp = new byte[paramLength];
-                    read(is, mApduResp);
-                    skip(is, 4 - (paramLength % 4));
-                    break;
-                } // Fall through if TEST == false
+                // not needed for server role, but used for module test
+                mApduResp = new byte[paramLength];
+                read(is, mApduResp);
+                skip(is, skipLen);
+                break;
             case PARAM_ATR_ID:
-                // not needed - server -> client
-                if(TEST) {
-                    mAtr = new byte[paramLength];
-                    read(is, mAtr);
-                    skip(is, 4 - (paramLength % 4));
-                    break;
-                } // Fall through if TEST == false
+                // not needed for server role, but used for module test
+                mAtr = new byte[paramLength];
+                read(is, mAtr);
+                skip(is, skipLen);
+                break;
             default:
                 Log.e(TAG, "Received unknown parameter ID: " + paramId + " length: " +
                         paramLength + " skipping this parameter.");
-                skip(is, paramLength + (4 - (paramLength % 4)));
+                skip(is, paramLength + skipLen);
             }
         }
         return success;
@@ -643,8 +634,10 @@
 
         /* Payload */
         os.write(value);
-        for(int i = 0, n = 4 - (value.length % 4) ; i < n; i++) {
-            os.write(0); // Padding
+        if (value.length % 4 != 0) {
+            for (int i = 0; i < (4 - (value.length % 4)); ++i) {
+                os.write(0); // Padding
+            }
         }
     }
 
@@ -668,7 +661,7 @@
             writeParameter(os, PARAM_RESULT_CODE_ID, mResultCode,
                             PARAM_RESULT_CODE_LENGTH);
         }
-        if(mDisconnectionType != INVALID_VALUE && TEST) {
+        if(mDisconnectionType != INVALID_VALUE) {
             writeParameter(os, PARAM_DISCONNECT_TYPE_ID, mDisconnectionType,
                             PARAM_DISCONNECT_TYPE_LENGTH);
         }
@@ -680,14 +673,14 @@
             writeParameter(os, PARAM_STATUS_CHANGE_ID, mStatusChange,
                             PARAM_STATUS_CHANGE_LENGTH);
         }
-        if(mTransportProtocol != INVALID_VALUE && TEST) {
+        if(mTransportProtocol != INVALID_VALUE) {
             writeParameter(os, PARAM_TRANSPORT_PROTOCOL_ID, mTransportProtocol,
                             PARAM_TRANSPORT_PROTOCOL_LENGTH);
         }
-        if(mApdu != null && TEST) {
+        if(mApdu != null) {
             writeParameter(os, PARAM_COMMAND_APDU_ID, mApdu);
         }
-        if(mApdu7816 != null  && TEST) {
+        if(mApdu7816 != null) {
             writeParameter(os, PARAM_COMMAND_APDU7816_ID, mApdu7816);
         }
         if(mApduResp != null) {
@@ -838,10 +831,8 @@
             break;
         }
         default:
-            if(TEST == false) {
-                Log.e(TAG, "Unknown request type");
-                throw new IllegalArgumentException();
-            }
+            Log.e(TAG, "Unknown request type");
+            throw new IllegalArgumentException();
         }
         /* Update the ongoing requests queue */
         if(mClearRilQueue == true) {
@@ -1214,7 +1205,7 @@
 
 
     public static String getMsgTypeName(int msgType) {
-        if(TEST || VERBOSE) {
+        if(DEBUG || VERBOSE) {
             switch (msgType)
             {
                 case ID_CONNECT_REQ: return "ID_CONNECT_REQ";
diff --git a/src/com/android/bluetooth/sap/SapServer.java b/src/com/android/bluetooth/sap/SapServer.java
index 4135a26..339f676 100644
--- a/src/com/android/bluetooth/sap/SapServer.java
+++ b/src/com/android/bluetooth/sap/SapServer.java
@@ -8,6 +8,7 @@
 import java.util.concurrent.CountDownLatch;
 
 import com.android.bluetooth.R;
+
 import android.app.AlarmManager;
 import android.app.Notification;
 import android.app.NotificationManager;
@@ -26,8 +27,10 @@
 import android.os.Message;
 import android.os.Parcel;
 import android.os.SystemClock;
+import android.os.SystemProperties;
 import android.telephony.TelephonyManager;
 import android.util.Log;
+
 //import com.android.internal.telephony.RIL;
 import com.google.protobuf.micro.CodedOutputStreamMicro;
 
@@ -48,7 +51,6 @@
     private static final String TAG_HANDLER = "SapServerHandler";
     public static final boolean DEBUG = SapService.DEBUG;
     public static final boolean VERBOSE = SapService.VERBOSE;
-    public static final boolean PTS_TEST = SapService.PTS_TEST;
 
     private enum SAP_STATE    {
         DISCONNECTED, CONNECTING, CONNECTING_CALL_ONGOING, CONNECTED,
@@ -212,6 +214,13 @@
         String title, text, button, ticker;
         Notification notification;
         if(VERBOSE) Log.i(TAG, "setNotification type: " + type);
+        /* For PTS TC_SERVER_DCN_BV_03_I we need to expose the option to send immediate disconnect
+         * without first sending a graceful disconnect.
+         * To enable this option set
+         * bt.sap.pts="true" */
+        String pts_enabled = SystemProperties.get("bt.sap.pts");
+        Boolean pts_test = Boolean.parseBoolean(pts_enabled);
+
         /* put notification up for the user to be able to disconnect from the client*/
         Intent sapDisconnectIntent = new Intent(SapServer.SAP_DISCONNECT_ACTION);
         if(type == SapMessage.DISC_GRACEFULL){
@@ -225,7 +234,7 @@
             text = mContext.getString(R.string.bluetooth_sap_notif_disconnecting);
             ticker = mContext.getString(R.string.bluetooth_sap_notif_ticker);
         }
-        if(!PTS_TEST)
+        if(!pts_test)
         {
             sapDisconnectIntent.putExtra(SapServer.SAP_DISCONNECT_TYPE_EXTRA, type);
             PendingIntent pIntentDisconnect = PendingIntent.getBroadcast(mContext, type,
@@ -413,7 +422,10 @@
              *        - Initiate a FORCED shutdown
              *        - Wait for RIL deinit to complete
              */
-            if(mState != SAP_STATE.DISCONNECTED) {
+            if (mState == SAP_STATE.CONNECTING_CALL_ONGOING) {
+                /* Most likely remote device closed rfcomm, update state */
+                changeState(SAP_STATE.DISCONNECTED);
+            } else if (mState != SAP_STATE.DISCONNECTED) {
                 if(mState != SAP_STATE.DISCONNECTING &&
                         mIsLocalInitDisconnect != true) {
                     sendDisconnectInd(SapMessage.DISC_FORCED);
@@ -500,7 +512,6 @@
             if (isCallOngoing() == true) {
                 /* If a call is ongoing we set the state, inform the SAP client and wait for a state
                  * change intent from the TelephonyManager with state IDLE. */
-                changeState(SAP_STATE.CONNECTING_CALL_ONGOING);
                 reply.setConnectionStatus(SapMessage.CON_STATUS_OK_ONGOING_CALL);
             } else {
                 /* no call is ongoing, initiate the connect sequence:
@@ -642,11 +653,12 @@
 
         if(DEBUG) Log.i(TAG_HANDLER, "in Shutdown()");
         try {
-            mRfcommOut.close();
+            if (mRfcommOut != null)
+                mRfcommOut.close();
         } catch (IOException e) {}
         try {
-            mRfcommIn.close();
-
+            if (mRfcommIn != null)
+                mRfcommIn.close();
         } catch (IOException e) {}
         mRfcommIn = null;
         mRfcommOut = null;
@@ -705,19 +717,25 @@
             switch(sapMsg.getMsgType()) {
 
                 case SapMessage.ID_CONNECT_RESP:
-                    if (sapMsg.getConnectionStatus() == SapMessage.CON_STATUS_OK) {
-                        // This is successful connect response from RIL/modem.
-                        changeState(SAP_STATE.CONNECTED);
-                    } else if(sapMsg.getConnectionStatus() == SapMessage.CON_STATUS_OK_ONGOING_CALL
-                              && mState != SAP_STATE.CONNECTING_CALL_ONGOING) {
-                        changeState(SAP_STATE.CONNECTING_CALL_ONGOING);
-                    } else if(mState == SAP_STATE.CONNECTING_CALL_ONGOING) {
+                    if(mState == SAP_STATE.CONNECTING_CALL_ONGOING) {
                         /* Hold back the connect resp if a call was ongoing when the connect req
-                         *  was received.
+                         * was received.
+                         * A response with status call-ongoing was sent, and the connect response
+                         * received from the RIL when call ends must be discarded.
                          */
+                        if (sapMsg.getConnectionStatus() == SapMessage.CON_STATUS_OK) {
+                            // This is successful connect response from RIL/modem.
+                            changeState(SAP_STATE.CONNECTED);
+                        }
                         if(VERBOSE) Log.i(TAG, "Hold back the connect resp, as a call was ongoing" +
                                 " when the initial response were sent.");
                         sapMsg = null;
+                    } else if (sapMsg.getConnectionStatus() == SapMessage.CON_STATUS_OK) {
+                        // This is successful connect response from RIL/modem.
+                        changeState(SAP_STATE.CONNECTED);
+                    } else if(sapMsg.getConnectionStatus() ==
+                            SapMessage.CON_STATUS_OK_ONGOING_CALL) {
+                        changeState(SAP_STATE.CONNECTING_CALL_ONGOING);
                     } else if(sapMsg.getConnectionStatus() != SapMessage.CON_STATUS_OK) {
                         /* Most likely the peer will try to connect again, hence we keep the
                          * connection to RIL open and stay in connecting state.
@@ -822,11 +840,18 @@
         if(VERBOSE) Log.i(TAG_HANDLER, "sendRilMessage() - "
                 + SapMessage.getMsgTypeName(sapMsg.getMsgType()));
         try {
-            sapMsg.writeReqToStream(mRilBtOutStream);
+            if(mRilBtOutStream != null) {
+                sapMsg.writeReqToStream(mRilBtOutStream);
+            } /* Else SAP was enabled on a build that did not support SAP, which we will not
+               * handle. */
         } catch (IOException e) {
             Log.e(TAG_HANDLER, "Unable to send message to RIL", e);
             SapMessage errorReply = new SapMessage(SapMessage.ID_ERROR_RESP);
             sendClientMessage(errorReply);
+        } catch (IllegalArgumentException e) {
+            Log.e(TAG_HANDLER, "Unable encode message", e);
+            SapMessage errorReply = new SapMessage(SapMessage.ID_ERROR_RESP);
+            sendClientMessage(errorReply);
         }
     }
 
diff --git a/src/com/android/bluetooth/sap/SapService.java b/src/com/android/bluetooth/sap/SapService.java
index d5caf80..23d69ff 100644
--- a/src/com/android/bluetooth/sap/SapService.java
+++ b/src/com/android/bluetooth/sap/SapService.java
@@ -47,7 +47,6 @@
     private static final String TAG = "SapService";
     public static final boolean DEBUG = false;
     public static final boolean VERBOSE = false;
-    public static final boolean PTS_TEST = false;
 
     /* Message ID's */
     private static final int START_LISTENER = 1;