PBAP OBEX session uses Handler/HandlerThread now.
am: a3acb17ad3

* commit 'a3acb17ad38d82048816b37cc1eb6b7872540961':
  PBAP OBEX session uses Handler/HandlerThread now.
diff --git a/src/android/bluetooth/client/map/BluetoothMapBmessage.java b/src/android/bluetooth/client/map/BluetoothMapBmessage.java
index 84e4c75..e06b033 100644
--- a/src/android/bluetooth/client/map/BluetoothMapBmessage.java
+++ b/src/android/bluetooth/client/map/BluetoothMapBmessage.java
@@ -159,6 +159,7 @@
             json.put("status", mBmsgStatus);
             json.put("type", mBmsgType);
             json.put("folder", mBmsgFolder);
+            json.put("charset", mBbodyCharset);
             json.put("message", mMessage);
         } catch (JSONException e) {
             // do nothing
diff --git a/src/android/bluetooth/client/map/BluetoothMapBmessageParser.java b/src/android/bluetooth/client/map/BluetoothMapBmessageParser.java
index ef75e7b..5c89e4d 100644
--- a/src/android/bluetooth/client/map/BluetoothMapBmessageParser.java
+++ b/src/android/bluetooth/client/map/BluetoothMapBmessageParser.java
@@ -33,11 +33,13 @@
 
 import java.io.ByteArrayInputStream;
 import java.io.IOException;
+import java.nio.charset.StandardCharsets;
 import java.text.ParseException;
 
 class BluetoothMapBmessageParser {
 
     private final static String TAG = "BluetoothMapBmessageParser";
+    private final static boolean DBG = false;
 
     private final static String CRLF = "\r\n";
 
@@ -75,6 +77,10 @@
     static public BluetoothMapBmessage createBmessage(String str) {
         BluetoothMapBmessageParser p = new BluetoothMapBmessageParser();
 
+        if (DBG) {
+            Log.d(TAG, "actual wired contents: " + str);
+        }
+
         try {
             p.parse(str);
         } catch (IOException e) {
@@ -291,6 +297,17 @@
         } while (!prop.equals(BEGIN_MSG));
 
         /*
+         * check that the charset is always set to UTF-8. We expect only text transfer (in lieu with
+         * the MAPv12 specifying only RFC2822 (text only) for MMS/EMAIL and SMS do not support
+         * non-text content. If the charset is not set to UTF-8, it is safe to set the message as
+         * empty. We force the getMessage (see BluetoothMasClient) to only call getMessage with
+         * UTF-8 as the MCE is not obliged to support native charset.
+         */
+        if (!mBmsg.mBbodyCharset.equals("UTF-8")) {
+            Log.e(TAG, "The charset was not set to charset UTF-8: " + mBmsg.mBbodyCharset);
+        }
+
+        /*
          * <bmessage-body-content>::={ "BEGIN:MSG"<CRLF> 'message'<CRLF>
          * "END:MSG"<CRLF> }
          */
@@ -314,7 +331,11 @@
 
         if (prop != null) {
             if (prop.equals(END_MSG)) {
-                mBmsg.mMessage = new String(data, 0, messageLen);
+                if (mBmsg.mBbodyCharset.equals("UTF-8")) {
+                    mBmsg.mMessage = new String(data, 0, messageLen, StandardCharsets.UTF_8);
+                } else {
+                    mBmsg.mMessage = null;
+                }
             } else {
                 /* Handle possible exception for incorrect LENGTH value
                  * from MSE while parsing  GET Message response */
@@ -346,7 +367,11 @@
                 throw expected(END_MSG);
             }
 
-            mBmsg.mMessage = remng.substring(0, messageLen);
+            if (mBmsg.mBbodyCharset.equals("UTF-8")) {
+                mBmsg.mMessage = remng.substring(0, messageLen);
+            } else {
+                mBmsg.mMessage = null;
+            }
         }
 
         prop = mParser.next();
diff --git a/src/android/bluetooth/client/map/BluetoothMasClient.java b/src/android/bluetooth/client/map/BluetoothMasClient.java
index 7f71693..87f5a38 100644
--- a/src/android/bluetooth/client/map/BluetoothMasClient.java
+++ b/src/android/bluetooth/client/map/BluetoothMasClient.java
@@ -962,7 +962,7 @@
      * @return <code>true</code> if request has been sent, <code>false</code>
      *         otherwise
      */
-    public boolean getMessage(String handle, CharsetType charset, boolean attachment) {
+    public boolean getMessage(String handle, boolean attachment) {
         if (mObexSession == null) {
             return false;
         }
@@ -974,8 +974,10 @@
             return false;
         }
 
-        BluetoothMasRequest request = new BluetoothMasRequestGetMessage(handle, charset,
-                attachment);
+        // Since we support only text messaging via Bluetooth, it is OK to restrict the requests to
+        // force conversion to UTF-8.
+        BluetoothMasRequest request =
+            new BluetoothMasRequestGetMessage(handle, CharsetType.UTF_8, attachment);
         return mObexSession.makeRequest(request);
     }
 
diff --git a/src/android/bluetooth/client/map/BluetoothMasRequestGetMessage.java b/src/android/bluetooth/client/map/BluetoothMasRequestGetMessage.java
index b50fd0f..923bff0 100644
--- a/src/android/bluetooth/client/map/BluetoothMasRequestGetMessage.java
+++ b/src/android/bluetooth/client/map/BluetoothMasRequestGetMessage.java
@@ -25,6 +25,8 @@
 import java.io.ByteArrayOutputStream;
 import java.io.IOException;
 import java.io.InputStream;
+import java.io.UnsupportedEncodingException;
+import java.nio.charset.StandardCharsets;
 
 import javax.obex.ClientSession;
 import javax.obex.HeaderSet;
@@ -69,7 +71,17 @@
             Log.e(TAG, "I/O exception while reading response", e);
         }
 
-        String bmsg = baos.toString();
+        // Convert the input stream using UTF-8 since the attributes in the payload are all encoded
+        // according to it. The actual message body may need to be transcoded depending on
+        // charset/encoding defined for body-content.
+        String bmsg;
+        try {
+            bmsg = baos.toString(StandardCharsets.UTF_8.name());
+        } catch (UnsupportedEncodingException ex) {
+            Log.e(TAG,
+                "Coudn't decode the bmessage with UTF-8. Something must be really messed up.");
+            return;
+        }
 
         mBmessage = BluetoothMapBmessageParser.createBmessage(bmsg);
 
diff --git a/src/android/bluetooth/client/pbap/BluetoothPbapClient.java b/src/android/bluetooth/client/pbap/BluetoothPbapClient.java
index 5e212e8..b1fc441 100644
--- a/src/android/bluetooth/client/pbap/BluetoothPbapClient.java
+++ b/src/android/bluetooth/client/pbap/BluetoothPbapClient.java
@@ -16,6 +16,7 @@
 
 package android.bluetooth.client.pbap;
 
+import android.accounts.Account;
 import android.bluetooth.BluetoothDevice;
 import android.os.Handler;
 import android.os.Message;
@@ -72,6 +73,8 @@
  * connection and disconnection happens automatically internally.
  */
 public class BluetoothPbapClient {
+    private static final boolean DBG = true;
+
     private static final String TAG = "BluetoothPbapClient";
 
     /**
@@ -372,6 +375,7 @@
         DISCONNECTED, CONNECTING, CONNECTED, DISCONNECTING;
     }
 
+    private final Account mAccount;
     private final Handler mClientHandler;
     private final BluetoothPbapSession mSession;
     private ConnectionState mConnectionState = ConnectionState.DISCONNECTED;
@@ -503,14 +507,20 @@
      *
      * @param device BluetoothDevice that corresponds to remote acting in PSE
      *            role
+     * @param account the account to which contacts will be added {@see #pullPhoneBook}.
      * @param handler the handle that will be used by PCE to notify events and
      *            results to application
      * @throws NullPointerException
      */
-    public BluetoothPbapClient(BluetoothDevice device, Handler handler) {
-        if (device == null) {
-            throw new NullPointerException("BluetothDevice is null");
+    public BluetoothPbapClient(BluetoothDevice device, Account account, Handler handler) {
+        if (DBG) {
+            Log.d(TAG, " device " + device + " account " + account);
         }
+        if (device == null) {
+            throw new NullPointerException("BluetoothDevice is null");
+        }
+
+        mAccount = account;
 
         mClientHandler = handler;
 
@@ -696,8 +706,8 @@
      */
     public boolean pullPhoneBook(String pbName, long filter, byte format, int maxListCount,
             int listStartOffset) {
-        BluetoothPbapRequest req = new BluetoothPbapRequestPullPhoneBook(pbName, filter, format,
-                maxListCount, listStartOffset);
+        BluetoothPbapRequest req = new BluetoothPbapRequestPullPhoneBook(
+                pbName, mAccount, filter, format, maxListCount, listStartOffset);
         return mSession.makeRequest(req);
     }
 
@@ -835,7 +845,8 @@
      * @link #EVENT_PULL_VCARD_ERROR} in case of failure
      */
     public boolean pullVcardEntry(String handle, long filter, byte format) {
-        BluetoothPbapRequest req = new BluetoothPbapRequestPullVcardEntry(handle, filter, format);
+        BluetoothPbapRequest req =
+                new BluetoothPbapRequestPullVcardEntry(handle, mAccount, filter, format);
         return mSession.makeRequest(req);
     }
 
diff --git a/src/android/bluetooth/client/pbap/BluetoothPbapRequestPullPhoneBook.java b/src/android/bluetooth/client/pbap/BluetoothPbapRequestPullPhoneBook.java
index 15954b1..411a8de 100644
--- a/src/android/bluetooth/client/pbap/BluetoothPbapRequestPullPhoneBook.java
+++ b/src/android/bluetooth/client/pbap/BluetoothPbapRequestPullPhoneBook.java
@@ -16,6 +16,7 @@
 
 package android.bluetooth.client.pbap;
 
+import android.accounts.Account;
 import android.util.Log;
 
 import com.android.vcard.VCardEntry;
@@ -29,19 +30,24 @@
 
 final class BluetoothPbapRequestPullPhoneBook extends BluetoothPbapRequest {
 
+    private static final boolean DBG = true;
+
     private static final String TAG = "BluetoothPbapRequestPullPhoneBook";
 
     private static final String TYPE = "x-bt/phonebook";
 
     private BluetoothPbapVcardList mResponse;
 
+    private Account mAccount;
+
     private int mNewMissedCalls = -1;
 
     private final byte mFormat;
 
-    public BluetoothPbapRequestPullPhoneBook(String pbName, long filter, byte format,
+    public BluetoothPbapRequestPullPhoneBook(
+            String pbName, Account account, long filter, byte format,
             int maxListCount, int listStartOffset) {
-
+        mAccount = account;
         if (maxListCount < 0 || maxListCount > 65535) {
             throw new IllegalArgumentException("maxListCount should be [0..65535]");
         }
@@ -91,12 +97,15 @@
     protected void readResponse(InputStream stream) throws IOException {
         Log.v(TAG, "readResponse");
 
-        mResponse = new BluetoothPbapVcardList(stream, mFormat);
+        mResponse = new BluetoothPbapVcardList(mAccount, stream, mFormat);
+        if (DBG) {
+            Log.d(TAG, "Read " + mResponse.getCount() + " entries.");
+        }
     }
 
     @Override
     protected void readResponseHeaders(HeaderSet headerset) {
-        Log.v(TAG, "readResponse");
+        Log.v(TAG, "readResponseHeaders");
 
         ObexAppParameters oap = ObexAppParameters.fromHeaderSet(headerset);
 
diff --git a/src/android/bluetooth/client/pbap/BluetoothPbapRequestPullVcardEntry.java b/src/android/bluetooth/client/pbap/BluetoothPbapRequestPullVcardEntry.java
index 42b6692..ed823d5 100644
--- a/src/android/bluetooth/client/pbap/BluetoothPbapRequestPullVcardEntry.java
+++ b/src/android/bluetooth/client/pbap/BluetoothPbapRequestPullVcardEntry.java
@@ -16,6 +16,7 @@
 
 package android.bluetooth.client.pbap;
 
+import android.accounts.Account;
 import android.util.Log;
 
 import com.android.vcard.VCardEntry;
@@ -35,9 +36,14 @@
 
     private BluetoothPbapVcardList mResponse;
 
+    private final Account mAccount;
+
     private final byte mFormat;
 
-    public BluetoothPbapRequestPullVcardEntry(String handle, long filter, byte format) {
+    public BluetoothPbapRequestPullVcardEntry(
+            String handle, Account account, long filter, byte format) {
+        mAccount = account;
+
         mHeaderSet.setHeader(HeaderSet.NAME, handle);
 
         mHeaderSet.setHeader(HeaderSet.TYPE, TYPE);
@@ -64,7 +70,7 @@
     protected void readResponse(InputStream stream) throws IOException {
         Log.v(TAG, "readResponse");
 
-        mResponse = new BluetoothPbapVcardList(stream, mFormat);
+        mResponse = new BluetoothPbapVcardList(mAccount, stream, mFormat);
     }
     @Override
     protected void checkResponseCode(int responseCode) throws IOException {
diff --git a/src/android/bluetooth/client/pbap/BluetoothPbapVcardList.java b/src/android/bluetooth/client/pbap/BluetoothPbapVcardList.java
index 8e23e1a..6bc75de 100644
--- a/src/android/bluetooth/client/pbap/BluetoothPbapVcardList.java
+++ b/src/android/bluetooth/client/pbap/BluetoothPbapVcardList.java
@@ -16,6 +16,9 @@
 
 package android.bluetooth.client.pbap;
 
+import android.accounts.Account;
+
+import com.android.vcard.VCardConfig;
 import com.android.vcard.VCardEntry;
 import com.android.vcard.VCardEntryConstructor;
 import com.android.vcard.VCardEntryCounter;
@@ -32,6 +35,7 @@
 class BluetoothPbapVcardList {
 
     private final ArrayList<VCardEntry> mCards = new ArrayList<VCardEntry>();
+    private final Account mAccount;
 
     class CardEntryHandler implements VCardEntryHandler {
         @Override
@@ -48,7 +52,8 @@
         }
     }
 
-    public BluetoothPbapVcardList(InputStream in, byte format) throws IOException {
+    public BluetoothPbapVcardList(Account account, InputStream in, byte format) throws IOException {
+        mAccount = account;
         parse(in, format);
     }
 
@@ -61,7 +66,8 @@
             parser = new VCardParser_V21();
         }
 
-        VCardEntryConstructor constructor = new VCardEntryConstructor();
+        VCardEntryConstructor constructor =
+            new VCardEntryConstructor(VCardConfig.VCARD_TYPE_V21_GENERIC, mAccount);
         VCardEntryCounter counter = new VCardEntryCounter();
         CardEntryHandler handler = new CardEntryHandler();