Bluetooth: Support OBEX MAP profile on Bluedroid.

Porting changes for MAP on Bluedroid as a part of
BluetoothExt APK. Support MAP 1.0 version features.
Handle MAP Authorization Settings from BluetoothExt
APK instead from AOSP Settings proj.

CRs-fixed: 504042

Change-Id: If12233e8c2861fec3105076b8fcdc58448405e6a
diff --git a/Android.mk b/Android.mk
index 147141b..7ae59ea 100644
--- a/Android.mk
+++ b/Android.mk
@@ -8,6 +8,12 @@
 
 LOCAL_PACKAGE_NAME := BluetoothExt
 LOCAL_CERTIFICATE := platform
+LOCAL_JAVA_LIBRARIES := javax.obex
+LOCAL_JAVA_LIBRARIES += mms-common
+LOCAL_JAVA_LIBRARIES += telephony-common
+LOCAL_STATIC_JAVA_LIBRARIES := com.android.vcard
+
+LOCAL_REQUIRED_MODULES := libbluetooth_jni bluetooth.default
 
 LOCAL_PROGUARD_ENABLED := disabled
 
diff --git a/AndroidManifest.xml b/AndroidManifest.xml
index 7519746..63229db 100644
--- a/AndroidManifest.xml
+++ b/AndroidManifest.xml
@@ -40,7 +40,16 @@
     <uses-permission android:name="android.permission.WAKE_LOCK" />
     <uses-permission android:name="android.permission.CONNECTIVITY_INTERNAL" />
     <uses-permission android:name="android.permission.BLUETOOTH_STACK" />
+    <uses-permission android:name="android.permission.READ_SMS"></uses-permission>
+    <uses-permission android:name="android.permission.WRITE_SMS"></uses-permission>
+    <uses-permission android:name="android.permission.BROADCAST_SMS"></uses-permission>
+    <uses-permission android:name="android.permission.RECEIVE_SMS"></uses-permission>
+    <uses-permission android:name="android.permission.SEND_SMS"></uses-permission>
+    <uses-permission android:name="android.permission.MMS_PUSH"></uses-permission>
+    <uses-permission android:name="com.android.email.permission.ACCESS_PROVIDER"/>
+    <uses-permission android:name="com.android.email.permission.READ_ATTACHMENT"/>
     <application>
+        <uses-library android:name="javax.obex" />
         <service
             android:name = ".btcservice.BTCService">
         </service>
@@ -60,5 +69,29 @@
                 <action android:name="android.bluetooth.input.profile.action.CONNECTION_STATE_CHANGED" />
             </intent-filter>
         </receiver>
+ <activity android:name=".map.BluetoothMasActivity"
+            android:process="@string/process"
+            android:excludeFromRecents="true"
+            android:enabled="@bool/profile_supported_map"
+            android:theme="@*android:style/Theme.Holo.Dialog.Alert">
+            <intent-filter>
+                <category android:name="android.intent.category.DEFAULT" />
+            </intent-filter>
+        </activity>
+        <service
+            android:process="@string/process"
+            android:enabled="@bool/profile_supported_map"
+            android:name=".map.BluetoothMasService" >
+        </service>
+        <receiver
+            android:process="@string/process"
+            android:enabled="@bool/profile_supported_map"
+            android:name=".map.BluetoothMasReceiver">
+            <intent-filter>
+                <action android:name="android.bluetooth.adapter.action.STATE_CHANGED"/>
+                <action android:name="android.bluetooth.device.action.BOND_STATE_CHANGED"/>
+                <action android:name="android.bluetooth.device.action.ACL_DISCONNECTED"/>
+            </intent-filter>
+        </receiver>
     </application>
 </manifest>
diff --git a/res/layout/auth.xml b/res/layout/auth.xml
new file mode 100644
index 0000000..7d1a200
--- /dev/null
+++ b/res/layout/auth.xml
@@ -0,0 +1,50 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+/*
+** Copyright 2009, 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.
+*/
+-->
+
+<ScrollView
+    xmlns:android="http://schemas.android.com/apk/res/android"
+    android:layout_height="match_parent"
+    android:layout_width="match_parent">
+
+    <LinearLayout
+    android:layout_height="match_parent"
+    android:layout_width="match_parent"
+    android:orientation="vertical">
+
+    <TextView
+        android:id="@+id/message"
+        android:layout_width="match_parent"
+        android:layout_height="wrap_content"
+        android:layout_marginLeft="20dip"
+        android:layout_marginRight="20dip"
+        android:gravity="center_horizontal"
+        android:textAppearance="?android:attr/textAppearanceMedium" />
+
+    <EditText
+        android:id="@+id/text"
+        android:layout_width="match_parent"
+        android:layout_height="wrap_content"
+        android:layout_marginTop="20dip"
+        android:layout_marginLeft="20dip"
+        android:layout_marginRight="20dip"
+        android:singleLine="true" />
+
+    </LinearLayout>
+
+</ScrollView>
diff --git a/res/layout/bluetooth_access.xml b/res/layout/bluetooth_access.xml
new file mode 100644
index 0000000..d81305b
--- /dev/null
+++ b/res/layout/bluetooth_access.xml
@@ -0,0 +1,48 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+/*
+** Copyright 2009, 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.
+*/
+-->
+
+<ScrollView
+    xmlns:android="http://schemas.android.com/apk/res/android"
+    android:layout_height="match_parent"
+    android:layout_width="match_parent">
+
+    <LinearLayout
+        android:layout_height="match_parent"
+        android:layout_width="match_parent"
+        android:orientation="vertical">
+
+        <TextView
+            android:id="@+id/message"
+            android:layout_width="match_parent"
+            android:layout_height="wrap_content"
+            android:layout_marginStart="20dip"
+            android:layout_marginEnd="20dip"
+            android:gravity="center_horizontal"
+            android:textAppearance="?android:attr/textAppearanceMedium" />
+
+        <CheckBox android:id="@+id/alwaysallowed"
+            style="?android:attr/textAppearanceMedium"
+            android:layout_width="match_parent"
+            android:layout_height="wrap_content"
+            android:layout_marginTop="2dip"
+            android:text="@string/bluetooth_remember_choice" />
+
+    </LinearLayout>
+
+</ScrollView>
diff --git a/res/values/config.xml b/res/values/config.xml
new file mode 100644
index 0000000..247f362
--- /dev/null
+++ b/res/values/config.xml
@@ -0,0 +1,17 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2009-2012 Broadcom Corporation
+   Licensed under the Apache License, Version 2.0 (the "License");
+   you may not use this file except in compliance with the License.
+   You may obtain a copy of the License at
+
+       http://www.apache.org/licenses/LICENSE-2.0
+
+   Unless required by applicable law or agreed to in writing, software
+   distributed under the License is distributed on an "AS IS" BASIS,
+   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+   See the License for the specific language governing permissions and
+   limitations under the License.
+-->
+<resources>
+    <bool name="profile_supported_map">true</bool>
+</resources>
diff --git a/res/values/strings.xml b/res/values/strings.xml
new file mode 100644
index 0000000..7c01009
--- /dev/null
+++ b/res/values/strings.xml
@@ -0,0 +1,24 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2007 The Android Open Source Project
+
+     Licensed under the Apache License, Version 2.0 (the "License");
+     you may not use this file except in compliance with the License.
+     You may obtain a copy of the License at
+
+          http://www.apache.org/licenses/LICENSE-2.0
+
+     Unless required by applicable law or agreed to in writing, software
+     distributed under the License is distributed on an "AS IS" BASIS,
+     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+     See the License for the specific language governing permissions and
+     limitations under the License.
+-->
+<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="defaultname">Carkit</string>
+    <!-- Do not translate. android:process of this application. -->
+    <string name="process" translate="false"><xliff:g id="x" /></string>
+
+    <!-- Bluetooth FTP or MAP permission Alert Activity checkbox text [CHAR LIMIT=none] -->
+    <string name="bluetooth_remember_choice">Always Allow</string>
+
+</resources>
diff --git a/res/values/strings_map.xml b/res/values/strings_map.xml
new file mode 100644
index 0000000..5ed01c7
--- /dev/null
+++ b/res/values/strings_map.xml
@@ -0,0 +1,49 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+/*
+ * Copyright (c) 2011-2012, The Linux Foundation. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *        * Redistributions of source code must retain the above copyright
+ *            notice, this list of conditions and the following disclaimer.
+ *        * Redistributions in binary form must reproduce the above copyright
+ *            notice, this list of conditions and the following disclaimer in the
+ *            documentation and/or other materials provided with the distribution.
+ *        * Neither the name of The Linux Foundation nor
+ *            the names of its contributors may be used to endorse or promote
+ *            products derived from this software without specific prior written
+ *            permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NON-INFRINGEMENT ARE DISCLAIMED.    IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
+ * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
+ * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
+ * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+-->
+<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="map_acceptance_dialog_title">%1$s would like to access your phone folders. Give access to %2$s?</string>
+    <string name="map_acceptance_dialog_header">Phone Folder Access</string>
+    <string name="map_session_key_dialog_title">Type session key for %1$s</string>
+    <string name="map_session_key_dialog_header">Bluetooth session key required</string>
+    <string name="map_acceptance_timeout_message">There was time out to accept connection with %1$s</string>
+    <string name="map_authentication_timeout_message">There was time out to input session key with %1$s</string>
+    <string name="map_notif_ticker">Bluetooth connection request</string>
+    <!-- Notification title when a Bluetooth device wants to pair with us -->
+    <string name="map_notif_title">Session Key Request for MAP</string>
+    <!-- Notification message when a Bluetooth device wants to pair with us -->
+    <string name="map_notif_message">"Touch to connect to \u0022<xliff:g id="device_name">%1$s</xliff:g>\u0022."</string>
+    <string name="map_alert_conn_failed_message">Bluetooth MAP connection failed.</string>
+    <!-- Activity label of BluetoothMasPermissionActivity, also used as Strings in the permission dialog [CHAR LIMIT=none] -->
+    <string name="bluetooth_mas_request">"Message Access request"</string>
+    <!-- Bluetooth MAS permission Alert Activity text [CHAR LIMIT=none] -->
+    <string name="bluetooth_mas_acceptance_dialog_text">%1$s would like to access your messages. Give access to %2$s?</string>
+
+</resources>
diff --git a/src/org/codeaurora/bluetooth/map/BluetoothMapAuthenticator.java b/src/org/codeaurora/bluetooth/map/BluetoothMapAuthenticator.java
new file mode 100644
index 0000000..314536e
--- /dev/null
+++ b/src/org/codeaurora/bluetooth/map/BluetoothMapAuthenticator.java
@@ -0,0 +1,102 @@
+/*
+ * Copyright (c) 2008-2009, Motorola, Inc.
+ * Copyright (c) 2010-2011, The Linux Foundation. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *        * Redistributions of source code must retain the above copyright
+ *          notice, this list of conditions and the following disclaimer.
+ *        * Redistributions in binary form must reproduce the above copyright
+ *          notice, this list of conditions and the following disclaimer in the
+ *          documentation and/or other materials provided with the distribution.
+ *        * Neither the name of The Linux Foundation nor
+ *          the names of its contributors may be used to endorse or promote
+ *          products derived from this software without specific prior written
+ *          permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NON-INFRINGEMENT ARE DISCLAIMED.    IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
+ * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
+ * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
+ * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+
+package org.codeaurora.bluetooth.map;
+import android.os.Handler;
+import android.os.Message;
+import android.util.Log;
+
+import javax.obex.Authenticator;
+import javax.obex.PasswordAuthentication;
+
+/**
+ * BluetoothMapAuthenticator is a used by BluetoothObexServer for obex
+ * authentication procedure.
+ */
+public class BluetoothMapAuthenticator implements Authenticator {
+    private static final String TAG = "BluetoothMapAuthenticator";
+
+    private boolean mChallenged;
+
+    private boolean mAuthCancelled;
+
+    private String mSessionKey;
+
+    private Handler mCallback;
+
+    public BluetoothMapAuthenticator(final Handler callback) {
+        mCallback = callback;
+        mChallenged = false;
+        mAuthCancelled = false;
+        mSessionKey = null;
+    }
+
+    public final synchronized void setChallenged(final boolean bool) {
+        mChallenged = bool;
+    }
+
+    public final synchronized void setCancelled(final boolean bool) {
+        mAuthCancelled = bool;
+    }
+
+    public final synchronized void setSessionKey(final String string) {
+        mSessionKey = string;
+    }
+
+    private void waitUserConfirmation() {
+        Message msg = Message.obtain(mCallback);
+        msg.what = BluetoothMasService.MSG_OBEX_AUTH_CHALL;
+        msg.sendToTarget();
+        synchronized (this) {
+            while (!mChallenged && !mAuthCancelled) {
+                try {
+                    wait();
+                } catch (InterruptedException e) {
+                    Log.e(TAG, "Interrupted while waiting on isChalled");
+                }
+            }
+        }
+    }
+
+      public PasswordAuthentication onAuthenticationChallenge(final String description,
+            final boolean isUserIdRequired, final boolean isFullAccess) {
+        waitUserConfirmation();
+        if (mSessionKey.trim().length() != 0) {
+            PasswordAuthentication pa = new PasswordAuthentication(null, mSessionKey.getBytes());
+            return pa;
+        }
+        return null;
+    }
+
+    // TODO: Reserved for future use only, in case PSE challenge PCE
+    public byte[] onAuthenticationResponse(final byte[] userName) {
+        return null;
+    }
+}
diff --git a/src/org/codeaurora/bluetooth/map/BluetoothMapRfcommTransport.java b/src/org/codeaurora/bluetooth/map/BluetoothMapRfcommTransport.java
new file mode 100644
index 0000000..154ecb0
--- /dev/null
+++ b/src/org/codeaurora/bluetooth/map/BluetoothMapRfcommTransport.java
@@ -0,0 +1,86 @@
+/*
+ * Copyright (c) 2008-2009, Motorola, Inc.
+ * Copyright (c) 2010-2011, The Linux Foundation. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *        * Redistributions of source code must retain the above copyright
+ *          notice, this list of conditions and the following disclaimer.
+ *        * Redistributions in binary form must reproduce the above copyright
+ *          notice, this list of conditions and the following disclaimer in the
+ *          documentation and/or other materials provided with the distribution.
+ *        * Neither the name of The Linux Foundation nor
+ *          the names of its contributors may be used to endorse or promote
+ *          products derived from this software without specific prior written
+ *          permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NON-INFRINGEMENT ARE DISCLAIMED.    IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
+ * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
+ * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
+ * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package org.codeaurora.bluetooth.map;
+
+import android.bluetooth.BluetoothSocket;
+
+import java.io.DataInputStream;
+import java.io.DataOutputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+
+import javax.obex.ObexTransport;
+
+public class BluetoothMapRfcommTransport implements ObexTransport {
+    private BluetoothSocket mSocket = null;
+
+    public BluetoothMapRfcommTransport(BluetoothSocket rfs) {
+        super();
+        this.mSocket = rfs;
+    }
+
+    public void close() throws IOException {
+        mSocket.close();
+    }
+
+    public DataInputStream openDataInputStream() throws IOException {
+        return new DataInputStream(openInputStream());
+    }
+
+    public DataOutputStream openDataOutputStream() throws IOException {
+        return new DataOutputStream(openOutputStream());
+    }
+
+    public InputStream openInputStream() throws IOException {
+        return mSocket.getInputStream();
+    }
+
+    public OutputStream openOutputStream() throws IOException {
+        return mSocket.getOutputStream();
+    }
+
+    public void connect() throws IOException {
+    }
+
+    public void create() throws IOException {
+    }
+
+    public void disconnect() throws IOException {
+    }
+
+    public void listen() throws IOException {
+    }
+
+    public boolean isConnected() throws IOException {
+        return true;
+    }
+
+}
diff --git a/src/org/codeaurora/bluetooth/map/BluetoothMasActivity.java b/src/org/codeaurora/bluetooth/map/BluetoothMasActivity.java
new file mode 100644
index 0000000..011641c
--- /dev/null
+++ b/src/org/codeaurora/bluetooth/map/BluetoothMasActivity.java
@@ -0,0 +1,313 @@
+ /*
+ * Copyright (c) 2008-2009, Motorola, Inc.
+ * Copyright (c) 2010-2011, The Linux Foundation. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *        * Redistributions of source code must retain the above copyright
+ *          notice, this list of conditions and the following disclaimer.
+ *        * Redistributions in binary form must reproduce the above copyright
+ *          notice, this list of conditions and the following disclaimer in the
+ *          documentation and/or other materials provided with the distribution.
+ *        * Neither the name of The Linux Foundation nor
+ *          the names of its contributors may be used to endorse or promote
+ *          products derived from this software without specific prior written
+ *          permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NON-INFRINGEMENT ARE DISCLAIMED.    IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
+ * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
+ * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
+ * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package org.codeaurora.bluetooth.map;
+
+import android.bluetooth.BluetoothDevice;
+import android.content.BroadcastReceiver;
+import android.content.Context;
+import android.content.DialogInterface;
+import android.content.Intent;
+import android.content.IntentFilter;
+import android.os.Bundle;
+import android.os.Handler;
+import android.os.Message;
+import android.preference.Preference;
+import android.text.TextWatcher;
+import android.util.Log;
+import android.view.View;
+import android.widget.Button;
+import android.widget.CheckBox;
+import android.widget.CompoundButton;
+import android.widget.EditText;
+import android.widget.TextView;
+import android.widget.CompoundButton.OnCheckedChangeListener;
+
+import org.codeaurora.bluetooth.R;
+import com.android.internal.app.AlertActivity;
+import com.android.internal.app.AlertController;
+
+/**
+ * MapActivity shows dialogues for accepting incoming map request
+ * with a  remote Bluetooth device.
+  */
+public class BluetoothMasActivity extends AlertActivity implements
+        DialogInterface.OnClickListener, Preference.OnPreferenceChangeListener, TextWatcher {
+    private static final String TAG = "BluetoothMasActivity";
+
+    private static final boolean V = BluetoothMasService.VERBOSE;
+
+    private static final int BLUETOOTH_OBEX_AUTHKEY_MAX_LENGTH = 16;
+
+    private static final int DIALOG_YES_NO_CONNECT = 1;
+
+    private static final String KEY_USER_TIMEOUT = "user_timeout";
+
+    private View mView;
+
+    private EditText mKeyView;
+
+    private TextView messageView;
+
+    private String mSessionKey = "";
+
+    private int mCurrentDialog;
+
+    private Button mOkButton;
+
+    private CheckBox mAlwaysAllowed;
+
+    private boolean mTimeout = false;
+
+    private boolean mAlwaysAllowedValue = false;
+
+    private static final int DISMISS_TIMEOUT_DIALOG = 0;
+
+    private static final int DISMISS_TIMEOUT_DIALOG_VALUE = 2000;
+
+    private BroadcastReceiver mReceiver = new BroadcastReceiver() {
+        @Override
+        public void onReceive(Context context, Intent intent) {
+            if (!BluetoothMasService.USER_CONFIRM_TIMEOUT_ACTION.equals(intent.getAction())) {
+                return;
+            }
+            onTimeout();
+        }
+    };
+
+    @Override
+    protected void onCreate(Bundle savedInstanceState) {
+        super.onCreate(savedInstanceState);
+        Intent i = getIntent();
+        String action = i.getAction();
+        if (BluetoothMasService.ACCESS_REQUEST_ACTION.equals(action)) {
+            showMapDialog(DIALOG_YES_NO_CONNECT);
+            mCurrentDialog = DIALOG_YES_NO_CONNECT;
+        }
+        else {
+            Log.e(TAG, "Error: this activity may be started only with intent "
+                    + "MAP_ACCESS_REQUEST");
+            finish();
+        }
+        registerReceiver(mReceiver, new IntentFilter(
+                BluetoothMasService.USER_CONFIRM_TIMEOUT_ACTION));
+    }
+
+    private void showMapDialog(int id) {
+        final AlertController.AlertParams p = mAlertParams;
+        switch (id) {
+            case DIALOG_YES_NO_CONNECT:
+                p.mIconId = android.R.drawable.ic_dialog_info;
+                p.mTitle = getString(R.string.bluetooth_mas_request);
+                p.mView = createView(DIALOG_YES_NO_CONNECT);
+                p.mPositiveButtonText = getString(android.R.string.yes);
+                p.mPositiveButtonListener = this;
+                p.mNegativeButtonText = getString(android.R.string.no);
+                p.mNegativeButtonListener = this;
+                mOkButton = mAlert.getButton(DialogInterface.BUTTON_POSITIVE);
+                setupAlert();
+                break;
+            default:
+                break;
+        }
+    }
+
+    private String getRemoteDeviceName() {
+        String remoteDeviceName = null;
+        Intent intent = getIntent();
+        if (intent.hasExtra(BluetoothMasService.EXTRA_BLUETOOTH_DEVICE)) {
+            BluetoothDevice device = intent.getParcelableExtra(BluetoothMasService.EXTRA_BLUETOOTH_DEVICE);
+            if (device != null) {
+                remoteDeviceName = device.getName();
+            }
+        }
+
+        return (remoteDeviceName != null) ? remoteDeviceName : getString(R.string.defaultname);
+    }
+
+    private String createDisplayText(final int id) {
+        String mRemoteName = getRemoteDeviceName();
+        switch (id) {
+        case DIALOG_YES_NO_CONNECT:
+            String mMessage1 = getString(R.string.bluetooth_mas_acceptance_dialog_text, mRemoteName,
+                    mRemoteName);
+            return mMessage1;
+        default:
+            return null;
+        }
+    }
+
+    private View createView(final int id) {
+        switch (id) {
+            case DIALOG_YES_NO_CONNECT:
+                mView = getLayoutInflater().inflate(R.layout.bluetooth_access, null);
+                messageView = (TextView)mView.findViewById(R.id.message);
+                messageView.setText(createDisplayText(id));
+                mAlwaysAllowed = (CheckBox)mView.findViewById(R.id.alwaysallowed);
+                mAlwaysAllowed.setOnCheckedChangeListener(new OnCheckedChangeListener() {
+                    public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) {
+                        if (isChecked) {
+                            mAlwaysAllowedValue = true;
+                        } else {
+                            mAlwaysAllowedValue = false;
+                        }
+                    }
+                });
+                return mView;
+            default:
+                return null;
+        }
+    }
+
+    private void onPositive() {
+        if (!mTimeout) {
+            if (mCurrentDialog == DIALOG_YES_NO_CONNECT) {
+                sendIntentToReceiver(BluetoothMasService.ACCESS_ALLOWED_ACTION,
+                        BluetoothMasService.EXTRA_ALWAYS_ALLOWED, mAlwaysAllowedValue);
+            }
+        }
+        mTimeout = false;
+        finish();
+    }
+
+    private void onNegative() {
+        if (mCurrentDialog == DIALOG_YES_NO_CONNECT) {
+            sendIntentToReceiver(BluetoothMasService.ACCESS_DISALLOWED_ACTION, null, null);
+        }
+        finish();
+    }
+
+    private void sendIntentToReceiver(final String intentName, final String extraName,
+            final String extraValue) {
+        Intent intent = new Intent(intentName);
+        intent.setClassName(BluetoothMasService.THIS_PACKAGE_NAME, BluetoothMasReceiver.class
+                .getName());
+        if (extraName != null) {
+            intent.putExtra(extraName, extraValue);
+        }
+        sendBroadcast(intent);
+    }
+
+    private void sendIntentToReceiver(final String intentName, final String extraName,
+            final boolean extraValue) {
+        Intent intent = new Intent(intentName);
+        intent.setClassName(BluetoothMasService.THIS_PACKAGE_NAME, BluetoothMasReceiver.class
+                .getName());
+        if (extraName != null) {
+            intent.putExtra(extraName, extraValue);
+        }
+        Intent i = getIntent();
+        if (i.hasExtra(BluetoothMasService.EXTRA_BLUETOOTH_DEVICE)) {
+            intent.putExtra(BluetoothMasService.EXTRA_BLUETOOTH_DEVICE, i.getParcelableExtra(BluetoothMasService.EXTRA_BLUETOOTH_DEVICE));
+        }
+        sendBroadcast(intent);
+    }
+
+    public void onClick(DialogInterface dialog, int which) {
+        switch (which) {
+            case DialogInterface.BUTTON_POSITIVE:
+                onPositive();
+                break;
+
+            case DialogInterface.BUTTON_NEGATIVE:
+                onNegative();
+                break;
+            default:
+                break;
+        }
+    }
+
+    private void onTimeout() {
+        mTimeout = true;
+        if (mCurrentDialog == DIALOG_YES_NO_CONNECT) {
+            if(mView != null) {
+                messageView.setText(getString(R.string.map_acceptance_timeout_message,
+                        getRemoteDeviceName()));
+                mAlert.getButton(DialogInterface.BUTTON_NEGATIVE).setVisibility(View.GONE);
+                mAlwaysAllowed.setVisibility(View.GONE);
+                mAlwaysAllowed.clearFocus();
+            }
+        }
+
+        mTimeoutHandler.sendMessageDelayed(mTimeoutHandler.obtainMessage(DISMISS_TIMEOUT_DIALOG),
+                DISMISS_TIMEOUT_DIALOG_VALUE);
+    }
+
+    @Override
+    protected void onRestoreInstanceState(Bundle savedInstanceState) {
+        super.onRestoreInstanceState(savedInstanceState);
+        mTimeout = savedInstanceState.getBoolean(KEY_USER_TIMEOUT);
+        if (V) Log.v(TAG, "onRestoreInstanceState() mTimeout: " + mTimeout);
+
+        if (mTimeout) {
+            onTimeout();
+        }
+    }
+
+    @Override
+    protected void onSaveInstanceState(Bundle outState) {
+        super.onSaveInstanceState(outState);
+        outState.putBoolean(KEY_USER_TIMEOUT, mTimeout);
+    }
+
+    @Override
+    protected void onDestroy() {
+        super.onDestroy();
+        unregisterReceiver(mReceiver);
+    }
+
+    public boolean onPreferenceChange(Preference preference, Object newValue) {
+        return true;
+    }
+
+    public void beforeTextChanged(CharSequence s, int start, int before, int after) {
+    }
+
+    public void onTextChanged(CharSequence s, int start, int before, int count) {
+    }
+
+    public void afterTextChanged(android.text.Editable s) {
+        if (s.length() > 0) {
+            mOkButton.setEnabled(true);
+        }
+    }
+    private final Handler mTimeoutHandler = new Handler() {
+        @Override
+        public void handleMessage(Message msg) {
+            switch (msg.what) {
+                case DISMISS_TIMEOUT_DIALOG:
+                    if (V) Log.v(TAG, "Received DISMISS_TIMEOUT_DIALOG msg.");
+                    finish();
+                    break;
+                default:
+                    break;
+            }
+        }
+    };
+}
diff --git a/src/org/codeaurora/bluetooth/map/BluetoothMasAppEmail.java b/src/org/codeaurora/bluetooth/map/BluetoothMasAppEmail.java
new file mode 100644
index 0000000..f827205
--- /dev/null
+++ b/src/org/codeaurora/bluetooth/map/BluetoothMasAppEmail.java
@@ -0,0 +1,800 @@
+/*
+ * Copyright (c) 2011-2013, The Linux Foundation. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *        * Redistributions of source code must retain the above copyright
+ *          notice, this list of conditions and the following disclaimer.
+ *        * Redistributions in binary form must reproduce the above copyright
+ *          notice, this list of conditions and the following disclaimer in the
+ *          documentation and/or other materials provided with the distribution.
+ *        * Neither the name of The Linux Foundation nor
+ *          the names of its contributors may be used to endorse or promote
+ *          products derived from this software without specific prior written
+ *          permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NON-INFRINGEMENT ARE DISCLAIMED.    IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
+ * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
+ * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
+ * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package org.codeaurora.bluetooth.map;
+
+import android.bluetooth.BluetoothDevice;
+import android.content.ContentResolver;
+import android.content.ContentValues;
+import android.content.Context;
+import android.content.Intent;
+import android.database.ContentObserver;
+import android.database.Cursor;
+import android.net.Uri;
+import android.os.Handler;
+import android.text.format.Time;
+import android.util.Log;
+
+import org.codeaurora.bluetooth.map.MapUtils.BmessageConsts;
+import org.codeaurora.bluetooth.map.MapUtils.CommonUtils;
+import org.codeaurora.bluetooth.map.MapUtils.EmailUtils;
+import org.codeaurora.bluetooth.map.MapUtils.MapUtils;
+import org.codeaurora.bluetooth.map.MapUtils.MsgListingConsts;
+import org.codeaurora.bluetooth.map.MapUtils.SqlHelper;
+import org.codeaurora.bluetooth.map.MapUtils.CommonUtils.BluetoothMasMessageListingRsp;
+import org.codeaurora.bluetooth.map.MapUtils.CommonUtils.BluetoothMasMessageRsp;
+import org.codeaurora.bluetooth.map.MapUtils.CommonUtils.BluetoothMasPushMsgRsp;
+import org.codeaurora.bluetooth.map.MapUtils.CommonUtils.BluetoothMsgListRsp;
+import org.codeaurora.bluetooth.map.MapUtils.MapUtils.BadRequestException;
+
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.FileNotFoundException;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+
+import javax.obex.ResponseCodes;
+
+import static org.codeaurora.bluetooth.map.BluetoothMasService.MSG_SERVERSESSION_CLOSE;
+import static org.codeaurora.bluetooth.map.MapUtils.EmailUtils.TYPE_DELETED;
+import static org.codeaurora.bluetooth.map.MapUtils.EmailUtils.TYPE_DRAFT;
+import static org.codeaurora.bluetooth.map.MapUtils.EmailUtils.TYPE_INBOX;
+import static org.codeaurora.bluetooth.map.MapUtils.EmailUtils.TYPE_OUTBOX;
+import static org.codeaurora.bluetooth.map.MapUtils.EmailUtils.TYPE_SENT;
+import static org.codeaurora.bluetooth.map.MapUtils.SmsMmsUtils.DELETED;
+import static org.codeaurora.bluetooth.map.MapUtils.SmsMmsUtils.DRAFT;
+import static org.codeaurora.bluetooth.map.MapUtils.SmsMmsUtils.DRAFTS;
+import static org.codeaurora.bluetooth.map.MapUtils.SmsMmsUtils.INBOX;
+import static org.codeaurora.bluetooth.map.MapUtils.SmsMmsUtils.OUTBOX;
+import static org.codeaurora.bluetooth.map.MapUtils.SmsMmsUtils.SENT;
+
+public class BluetoothMasAppEmail extends BluetoothMasAppIf {
+    public final String TAG = "BluetoothMasAppEmail";
+    public final boolean V = BluetoothMasService.VERBOSE;
+
+    private ContentObserver mObserver;
+    private static final int[] SPECIAL_MAILBOX_TYPES
+            = {TYPE_DELETED, TYPE_DRAFT, TYPE_INBOX, TYPE_OUTBOX, TYPE_SENT};
+    private static final String[] SPECIAL_MAILBOX_MAP_NAME
+            = {DELETED, DRAFT, INBOX, OUTBOX, SENT};
+    private HashMap<Integer, String> mSpecialMailboxName = new HashMap<Integer, String>();
+
+    public BluetoothMasAppEmail(Context context, Handler handler, BluetoothMns mnsClient,
+            int masId, String remoteDeviceName) {
+        super(context, handler, MESSAGE_TYPE_EMAIL, mnsClient, masId, remoteDeviceName);
+
+        mObserver = new ContentObserver(null) {
+            @Override
+            public void onChange(boolean selfChange) {
+                long id = EmailUtils.getAccountId(mMasId);
+                if (!EmailUtils.hasEmailAccount(mContext, id)) {
+                    // Email account removed, disconnect
+                    // TODO: inform the user
+                    disconnect();
+                }
+                super.onChange(selfChange);
+            }
+        };
+
+        loadSpecialMailboxName();
+        if (V) Log.v(TAG, "BluetoothMasAppEmail Constructor called");
+    }
+
+    private void loadSpecialMailboxName() {
+        mSpecialMailboxName.clear();
+        long id = EmailUtils.getAccountId(mMasId);
+        final String where = EmailUtils.ACCOUNT_KEY + "=" + id + " AND " + EmailUtils.TYPE + "=";
+        String name;
+        for (int i = 0; i < SPECIAL_MAILBOX_TYPES.length; i ++) {
+            name = SqlHelper.getFirstValueForColumn(mContext, EmailUtils.EMAIL_BOX_URI,
+                    EmailUtils.DISPLAY_NAME, where + SPECIAL_MAILBOX_TYPES[i], null);
+            if (name.length() > 0) {
+                mSpecialMailboxName.put(i, name);
+            }
+        }
+    }
+
+    /**
+     * Start an MNS obex client session and push notification whenever available
+     */
+    public void startMnsSession(BluetoothDevice remoteDevice) {
+        if (V) Log.v(TAG, "Start MNS Client");
+        mMnsClient.getHandler().obtainMessage(BluetoothMns.MNS_CONNECT, mMasId,
+                -1, remoteDevice).sendToTarget();
+    }
+
+    /**
+     * Stop pushing notifications and disconnect MNS obex session
+     */
+    public void stopMnsSession(BluetoothDevice remoteDevice) {
+        if (V) Log.v(TAG, "Stop MNS Client");
+        mMnsClient.getHandler().obtainMessage(BluetoothMns.MNS_DISCONNECT, mMasId,
+                -1, remoteDevice).sendToTarget();
+    }
+
+    @Override
+    protected List<String> getCompleteFolderList() {
+        if (V) Log.v(TAG, "getCompleteFolderList mCurrentPath: " + mCurrentPath);
+        long id = EmailUtils.getAccountId(mMasId);
+        List<String> list;
+        String splitStrings[] = mCurrentPath.split("/");
+        ArrayList<String> finalList = new ArrayList<String>();
+        String name;
+        int type;
+        int curType;
+        int len =splitStrings.length;
+        if (V) Log.v(TAG, "getCompleteFolderList splitStrings.len = " + splitStrings.length);
+        //Get Default List at "/telecom/msg/"
+        if(len < 3 &&  (mCurrentPath.equalsIgnoreCase("telecom") ||
+                mCurrentPath.equalsIgnoreCase("telecom/msg"))) {
+            list = EmailUtils.getEmailFolderListAtPath(mContext, id, "");
+            for (int i = 0; i < SPECIAL_MAILBOX_TYPES.length; i ++) {
+                curType = SPECIAL_MAILBOX_TYPES[i];
+                if (V) Log.v(TAG, " getCompleteFolderList: Current Type: " + curType);
+                for (String str : list) {
+                    type = EmailUtils.getTypeForFolder(mContext, id, str);
+                    if (V) Log.v(TAG, " getCompleteFolderList: type: " + type);
+                    if (type == curType) {
+                        if (V) Log.v(TAG, " getCompleteFolderList: removing folder : " + str);
+                        list.remove(str);
+                        break;
+                   }
+                }
+                if (!list.contains(SPECIAL_MAILBOX_MAP_NAME[i])) {
+                    if (V) Log.v(TAG, " getCompleteFolderList: adding default folder : "
+                        + SPECIAL_MAILBOX_MAP_NAME[i]);
+                    list.add(SPECIAL_MAILBOX_MAP_NAME[i]);
+                }
+            }
+            for (String str : list) {
+                type = EmailUtils.getTypeForFolder(mContext, id, str);
+                if (type <= ((EmailUtils.TYPE_DELETED) + 1)) {
+                    if (V) Log.v(TAG, " getCompleteFolderList: Adding a valid folder:" + str);
+                    finalList.add(str);
+                }
+            }
+        }
+        else {
+            //Remove length of "telecom/msg" -> 11 from mCurrentPath , Get folders and subfodlers
+            String path = mCurrentPath.substring(12);
+            list = EmailUtils.getEmailFolderListAtPath(mContext, id, path);
+            for (String str : list) {
+                if (V) Log.v(TAG, " getCompleteFolderList: Processing SerId: " + str);
+                String folderStr = str.substring(path.length()+ 1);
+                String folder[] = folderStr.split("/");
+                if(folder.length == 1){
+                    type = EmailUtils.getTypeForFolder(mContext, id, folder[0]);
+                    if (V) Log.v(TAG, " getCompleteFolderList: Add Folder:" + folder[0]);
+                    finalList.add(folder[0]);
+                }
+            }
+        }
+        if (V) Log.v(TAG, "Returning from CompleteFolderList");
+        return finalList;
+    }
+
+    public boolean checkPrecondition() {
+        long id = EmailUtils.getAccountId(mMasId);
+        if (id == -1) {
+            return false;
+        }
+        return true;
+    }
+
+    public void onConnect() {
+        if (V) Log.v(TAG, "onConnect() registering email account content observer");
+        mContext.getContentResolver().registerContentObserver(
+                EmailUtils.EMAIL_ACCOUNT_URI, true, mObserver);
+    }
+
+    public void onDisconnect() {
+        if (V) Log.v(TAG, "onDisconnect() unregistering email account content observer");
+        mContext.getContentResolver().unregisterContentObserver(mObserver);
+    }
+
+    private void disconnect() {
+        if (V) Log.v(TAG, "disconnect() sending serversession close.");
+        mHandler.obtainMessage(MSG_SERVERSESSION_CLOSE, mMasId, -1).sendToTarget();
+    }
+
+    /*
+     * Email specific methods
+     */
+    @Override
+    protected BluetoothMsgListRsp msgListingSpecific(List<MsgListingConsts> msgList, String name,
+            BluetoothMasMessageListingRsp rsp, BluetoothMasAppParams appParams) {
+        BluetoothMsgListRsp bmlr = new BluetoothMsgListRsp();
+        String fullPath = (name == null || name.length() == 0) ? mCurrentPath :
+                CommonUtils.getFullPath(name, mContext, getCompleteFolderList(), mCurrentPath);
+        if (fullPath == null) {
+            // Child folder not present
+            rsp.rsp = ResponseCodes.OBEX_HTTP_BAD_REQUEST;
+            bmlr.rsp = rsp;
+            return bmlr;
+        }
+
+        if (V) {
+            Log.v(TAG, "appParams.FilterMessageType ::"+ appParams.FilterMessageType);
+            Log.v(TAG, "Condition result:"+ (appParams.FilterMessageType & 0x04));
+        }
+        String splitStrings[] = fullPath.split("/");
+        int len = splitStrings.length;
+        //Add folders and subfolders
+        if (len >= 3) {
+            if (CommonUtils.validateFilterPeriods(appParams) == 0) {
+                rsp.rsp = ResponseCodes.OBEX_HTTP_BAD_REQUEST;
+                bmlr.rsp = rsp;
+                return bmlr;
+            }
+            if (appParams.FilterReadStatus > 0x02) {
+                rsp.rsp = ResponseCodes.OBEX_HTTP_BAD_REQUEST;
+                bmlr.rsp = rsp;
+                return bmlr;
+            }
+
+            if (appParams.FilterPriority == 0 || appParams.FilterPriority == 0x02) {
+                if((appParams.FilterMessageType & 0x04) == 0) {
+                    String folderName;
+                    if (splitStrings.length < 3) {
+                        Log.e(TAG, "The folder path is invalid.");
+                        rsp.rsp = ResponseCodes.OBEX_HTTP_BAD_REQUEST;;
+                        bmlr.rsp = rsp;
+                        return bmlr;
+                    }
+                    if (V) Log.v(TAG, "splitStrings[len-1] = " + splitStrings[len-1]);
+
+                    folderName = EmailUtils.getFolderName(splitStrings);
+                    int index = 0;
+                    long accountId = EmailUtils.getAccountId(mMasId);
+                    for (; index < SPECIAL_MAILBOX_MAP_NAME.length; index ++) {
+                        if (SPECIAL_MAILBOX_MAP_NAME[index].equalsIgnoreCase(folderName)) {
+                            List<String> folders = EmailUtils.getFoldersForType(mContext,
+                                    accountId, SPECIAL_MAILBOX_TYPES[index]);
+                            List<MsgListingConsts> list = null;
+                            for (String folder : folders) {
+                                list = getListEmailFromFolder(folder, rsp, appParams);
+                                if (list.size() > 0) {
+                                    msgList.addAll(list);
+                                }
+                            }
+                            break;
+                        }
+                    }
+                    //Add NON SPECIAL FOLDERS
+                    if (index >= SPECIAL_MAILBOX_MAP_NAME.length) {
+                        msgList = getListEmailFromFolder(folderName, rsp, appParams);
+                    }
+                    rsp.rsp = ResponseCodes.OBEX_HTTP_OK;
+                    bmlr.messageListingSize = rsp.msgListingSize;
+                    bmlr.rsp = rsp;
+                    bmlr.msgList = msgList;
+                    return bmlr;
+                }
+                else {
+                    if (V) Log.v(TAG, "Invalid Message Filter, returning empty list");
+                    rsp.rsp = ResponseCodes.OBEX_HTTP_OK;
+                    bmlr.rsp = rsp;
+                    return bmlr;
+                }
+            } else {
+                if (appParams.FilterPriority > 0x02) {
+                    rsp.rsp = ResponseCodes.OBEX_HTTP_BAD_REQUEST;
+                    bmlr.rsp = rsp;
+                    return bmlr;
+                }
+            }
+        }
+        rsp.rsp = ResponseCodes.OBEX_HTTP_OK;
+        bmlr.rsp = rsp;
+        return bmlr;
+    }
+
+    @Override
+    protected BluetoothMasMessageRsp getMessageSpecific(long msgHandle,
+            BluetoothMasMessageRsp rsp, BluetoothMasAppParams bluetoothMasAppParams) {
+        /*
+         * Spec 5.6.4 says MSE shall reject request with value native
+         * for MMS and Email
+         */
+        if ((int)bluetoothMasAppParams.Charset == 0) {
+            rsp.rsp = ResponseCodes.OBEX_HTTP_BAD_REQUEST;
+            return rsp;
+        }
+
+        long emailMsgID = msgHandle - OFFSET_START;
+        String str = EmailUtils.bldEmailBmsg(emailMsgID, rsp, mContext, mRemoteDeviceName);
+        if (V) Log.v(TAG, "\n" + str + "\n");
+        if (str != null && (str.length() > 0)) {
+            final String FILENAME = "message" + getMasId();
+            FileOutputStream bos = null;
+            File file = new File(mContext.getFilesDir() + "/" + FILENAME);
+            file.delete();
+
+            try {
+                bos = mContext.openFileOutput(FILENAME, Context.MODE_PRIVATE);
+                bos.write(str.getBytes());
+                bos.flush();
+                bos.close();
+            } catch (FileNotFoundException e) {
+                Log.e(TAG, "Unable to write " + FILENAME, e);
+            } catch (IOException e) {
+                Log.e(TAG, "Unable to write " + FILENAME, e);
+            }
+
+            File fileR = new File(mContext.getFilesDir() + "/" + FILENAME);
+            if (fileR.exists() == true) {
+                rsp.file = fileR;
+                rsp.fractionDeliver = 1;
+            } else {
+                rsp.rsp = ResponseCodes.OBEX_HTTP_BAD_REQUEST;
+            }
+        }
+        return rsp;
+    }
+
+    /**
+     * Push a outgoing message from MAS Client to the network
+     *
+     * @return Response to push command
+     */
+    public BluetoothMasPushMsgRsp pushMsg(String name, File file,
+            BluetoothMasAppParams bluetoothMasAppParams) throws BadRequestException {
+        BluetoothMasPushMsgRsp rsp = new BluetoothMasPushMsgRsp();
+        rsp.response = ResponseCodes.OBEX_HTTP_UNAVAILABLE;
+        rsp.msgHandle = null;
+        if((int)bluetoothMasAppParams.Charset == 0) {
+            rsp.response = ResponseCodes.OBEX_HTTP_BAD_REQUEST;
+            return rsp;
+        }
+        if(!checkPath(false, name, false) ||
+                mCurrentPath == null ||
+                mCurrentPath.equals("telecom") ||
+                (mCurrentPath.equals("telecom/msg") && (name == null || name.length() == 0))) {
+            rsp.response = ResponseCodes.OBEX_HTTP_BAD_REQUEST;
+            return rsp;
+        }
+        byte[] readBytes = null;
+        FileInputStream fis = null;
+        try {
+            fis = new FileInputStream(file);
+            if(file.length() > EMAIL_MAX_PUSHMSG_SIZE){
+                rsp.response = ResponseCodes.OBEX_HTTP_ENTITY_TOO_LARGE;
+                rsp.msgHandle = null;
+                Log.d(TAG,"Message body is larger than the max length allowed");
+                return rsp;
+            } else {
+                readBytes = new byte[(int) file.length()];
+                fis.read(readBytes);
+            }
+        } catch (FileNotFoundException e) {
+            Log.e(TAG, e.getMessage());
+            return rsp;
+        } catch (IOException e) {
+            Log.e(TAG, e.getMessage());
+            return rsp;
+        } catch (SecurityException e) {
+            Log.e(TAG, e.getMessage());
+            return rsp;
+        } finally {
+            if (fis != null) {
+                try {
+                    fis.close();
+                } catch (IOException ei) {
+                    Log.e(TAG, "Error while closing stream"+ ei.toString());
+                }
+            }
+        }
+
+        String readStr = "";
+        String type = "";
+        try {
+            readStr = new String(readBytes);
+            type = MapUtils.fetchType(readStr);
+        } catch (Exception e) {
+            throw new BadRequestException(e.getMessage());
+        }
+        if (type != null && type.equalsIgnoreCase("EMAIL")) {
+            rsp = pushMessageEmail(rsp, readStr, name);
+            return rsp;
+        }
+        rsp.response = ResponseCodes.OBEX_HTTP_BAD_REQUEST;
+        return rsp;
+    }
+
+    /**
+     * Sets the message status (read/unread, delete)
+     *
+     * @return Obex response code
+     */
+    public int msgStatus(String msgHandle, BluetoothMasAppParams bluetoothMasAppParams) {
+        if ((bluetoothMasAppParams.StatusIndicator != 0)
+                && (bluetoothMasAppParams.StatusIndicator != 1)) {
+            return ResponseCodes.OBEX_HTTP_PRECON_FAILED;
+        }
+        if ((bluetoothMasAppParams.StatusValue != 0)
+                && (bluetoothMasAppParams.StatusValue != 1)) {
+            return ResponseCodes.OBEX_HTTP_PRECON_FAILED;
+        }
+        final long handle = Long.valueOf(msgHandle);
+        if (handle < OFFSET_START && handle > OFFSET_END) {
+            return ResponseCodes.OBEX_HTTP_NOT_FOUND;
+        }
+        return setMsgStatusEmail(handle, bluetoothMasAppParams);
+    }
+
+    /**
+     * Sets the message update
+     *
+     * @return Obex response code
+     */
+    public int msgUpdate() {
+        if (V) Log.v(TAG, "Message Update");
+        long accountId = EmailUtils.getAccountId(mMasId);
+        if (V) Log.v(TAG, " Account id for Inbox Update: " +accountId);
+
+        Intent emailIn = new Intent();
+
+        emailIn.setAction("com.android.email.intent.action.MAIL_SERVICE_WAKEUP");
+        emailIn.putExtra("com.android.email.intent.extra.ACCOUNT", accountId);
+        mContext.startService(emailIn);
+
+        return ResponseCodes.OBEX_HTTP_OK;
+    }
+
+    /**
+     * Adds an Email to the Email ContentProvider
+     */
+    private String addToEmailFolder(String folder, String address, String text, String subject,
+            String OrigEmail, String OrigName) {
+        if (V) {
+            Log.v(TAG, "-------------");
+            Log.v(TAG, "address " + address);
+            Log.v(TAG, "TEXT " + text);
+        }
+        // TODO: need to insert a row in the body table and update the mailbox table
+        // with the no of messages unread
+        Cursor cr;
+        int folderId = -1;
+        long accountId = -1;
+        Time timeObj = new Time();
+        timeObj.setToNow();
+
+        Cursor cr1;
+        String whereClause1 = "UPPER(emailAddress) LIKE  '"+OrigEmail.toUpperCase().trim()+"'";
+        cr1 = mContext.getContentResolver().query(
+                Uri.parse("content://com.android.email.provider/account"),
+                null, whereClause1, null, null);
+        if (cr1 != null) {
+            if (cr1.getCount() > 0) {
+                cr1.moveToFirst();
+                accountId = cr1.getInt(cr1.getColumnIndex("_id"));
+            }
+            cr1.close();
+        }
+        if (accountId == -1) {
+            accountId = EmailUtils.getAccountId(mMasId);
+        }
+        if (DRAFT.equalsIgnoreCase(folder)) {
+            List<String> folders = EmailUtils.getFoldersForType(mContext, accountId,
+                    EmailUtils.TYPE_DRAFT);
+            if (V) Log.v(TAG, "DRAFT folders: " + folders.toString());
+            if (folders.size() == 0) {
+                // no draft folder
+                return INTERNAL_ERROR;
+            }
+            if (folders.contains(DRAFTS)) {
+                folder = DRAFTS;
+            } else {
+                folder = folders.get(0);
+            }
+        }
+
+        String whereClause = "UPPER(displayName) = '"+folder.toUpperCase().trim()+"'";
+        cr = mContext.getContentResolver().query(
+                Uri.parse("content://com.android.email.provider/mailbox"),
+                null, whereClause, null, null);
+        if (cr != null) {
+            if (cr.getCount() > 0) {
+                cr.moveToFirst();
+                folderId = cr.getInt(cr.getColumnIndex("_id"));
+            }
+            cr.close();
+        }
+        if (folderId == -1) {
+            return INTERNAL_ERROR;
+        }
+
+        if (V){
+            Log.v(TAG, "-------------");
+            Log.v(TAG, "To address " + address);
+            Log.v(TAG, "Text " + text);
+            Log.v(TAG, "Originator email address:: " + OrigEmail);
+            Log.v(TAG, "Originator email name:: " + OrigName);
+            Log.v(TAG, "Time Stamp:: " + timeObj.toMillis(false));
+            Log.v(TAG, "Account Key:: " + accountId);
+            Log.v(TAG, "Folder Id:: " + folderId);
+            Log.v(TAG, "Folder Name:: " + folder);
+            Log.v(TAG, "Subject" + subject);
+        }
+        ContentValues values = new ContentValues();
+        values.put("syncServerTimeStamp", 0);
+        values.put("syncServerId", "5:65");
+        values.put("displayName", OrigName.trim());
+        values.put("timeStamp", timeObj.toMillis(false));
+        values.put("subject", subject.trim());
+        values.put("flagLoaded", "1");
+        values.put("flagFavorite", "0");
+        values.put("flagAttachment", "0");
+        values.put("flags", "0");
+
+        values.put("accountKey", accountId);
+        values.put("fromList", OrigEmail.trim());
+
+        values.put("mailboxKey", folderId);
+        values.put("toList", address.trim());
+        values.put("flagRead", 0);
+
+        Uri uri = mContext.getContentResolver().insert(
+                Uri.parse("content://com.android.email.provider/message"), values);
+        if (V){
+            Log.v(TAG, " NEW URI " + (uri == null ? "null" : uri.toString()));
+        }
+
+        if (uri == null) {
+            return INTERNAL_ERROR;
+        }
+        String str = uri.toString();
+        String[] splitStr = str.split("/");
+        if (splitStr.length < 5) {
+            return INTERNAL_ERROR;
+        }
+        if (V){
+            Log.v(TAG, " NEW HANDLE " + splitStr[4]);
+        }
+
+        // TODO: need to insert into the body table
+        // --seems like body table gets updated automatically
+        ContentValues valuesBody = new ContentValues();
+        valuesBody.put("messageKey", splitStr[4]);
+        valuesBody.put("textContent", text);
+
+        mContext.getContentResolver().insert(
+                Uri.parse("content://com.android.email.provider/body"), valuesBody);
+        long virtualMsgId;
+        virtualMsgId = Long.valueOf(splitStr[4]) + OFFSET_START;
+        return Long.toString(virtualMsgId);
+    }
+
+    private List<MsgListingConsts> getListEmailFromFolder(String folderName,
+            BluetoothMasMessageListingRsp rsp, BluetoothMasAppParams appParams) {
+        List<MsgListingConsts> msgList = new ArrayList<MsgListingConsts>();
+        String urlEmail = "content://com.android.email.provider/message";
+        Uri uriEmail = Uri.parse(urlEmail);
+        ContentResolver crEmail = mContext.getContentResolver();
+
+        String whereClauseEmail  = EmailUtils.getConditionString(folderName, mContext, appParams,
+                mMasId);
+
+        if (V){
+                Log.v(TAG, "## whereClauseEmail ##:" + whereClauseEmail);
+        }
+        Cursor cursor = crEmail.query(uriEmail, null, whereClauseEmail, null, "timeStamp desc");
+
+        if (cursor != null && V){
+                Log.v(TAG, "move to First" + cursor.moveToFirst());
+        }
+        if (cursor != null && V){
+                Log.v(TAG, "move to Liststartoffset"
+                    + cursor.moveToPosition(appParams.ListStartOffset));
+        }
+        if (cursor != null && cursor.moveToFirst()) {
+            int idInd = cursor.getColumnIndex("_id");
+            int displayNameIndex = cursor.getColumnIndex("displayName");
+            int fromIndex = cursor.getColumnIndex("fromList");
+            int toIndex = cursor.getColumnIndex("toList");
+            int dateInd = cursor.getColumnIndex("timeStamp");
+            int readInd = cursor.getColumnIndex("flagRead");
+            int subjectInd = cursor.getColumnIndex("subject");
+            int replyToInd = cursor.getColumnIndex("replyToList");
+
+            do {
+                /*
+                 * Apply remaining filters
+                 */
+
+                if (V) Log.v(TAG, " msgListSize " + rsp.msgListingSize);
+                rsp.msgListingSize++;
+
+                String subject = cursor.getString(subjectInd);
+                String timestamp = cursor.getString(dateInd);
+                String senderName = cursor.getString(displayNameIndex);
+                String senderAddressing = cursor.getString(fromIndex);
+                String recipientName = cursor.getString(toIndex);
+                String recipientAddressing = cursor.getString(toIndex);
+                String msgId = cursor.getString(idInd);
+                String readStatus = cursor.getString(readInd);
+                String replyToStr = cursor.getString(replyToInd);
+
+                /*
+                 * Don't want the listing; just send the listing size after
+                 * applying all the filters.
+                 */
+
+                /*
+                 * TODO: Skip the first ListStartOffset record(s). Don't write
+                 * more than MaxListCount record(s).
+                 */
+
+                MsgListingConsts emailMsg = new MsgListingConsts();
+                emailMsg = EmailUtils.bldEmailMsgLstItem(mContext, folderName, appParams,
+                        subject, timestamp, senderName, senderAddressing,
+                        recipientName, recipientAddressing,
+                        msgId, readStatus, replyToStr, OFFSET_START);
+
+                // New Message?
+                if ((rsp.newMessage == 0) && (cursor.getInt(readInd) == 0)) {
+                    rsp.newMessage = 1;
+                }
+                msgList.add(emailMsg);
+            } while (cursor.moveToNext());
+        }
+        if (cursor != null) {
+            cursor.close();
+        }
+        return msgList;
+    }
+
+    private boolean isAllowedEmailFolderForPush(String folderName) {
+        if (DRAFT.equalsIgnoreCase(folderName) || OUTBOX.equalsIgnoreCase(folderName)) {
+            return true;
+        }
+        long id = EmailUtils.getAccountId(mMasId);
+        int type = EmailUtils.getTypeForFolder(mContext, id, folderName);
+        if (type == EmailUtils.TYPE_DRAFT || type == EmailUtils.TYPE_OUTBOX) {
+            return true;
+        } else {
+            return false;
+        }
+    }
+
+    private BluetoothMasPushMsgRsp pushMessageEmail(BluetoothMasPushMsgRsp rsp,
+                String readStr, String name) throws BadRequestException {
+        if (V) Log.v(TAG, " Before fromBmessageemail method:: "+readStr);
+
+        BmessageConsts bMsg = MapUtils.fromBmessageEmail(mContext,readStr,mMasId);
+        String address = bMsg.getRecipientVcard_email();
+        String text = bMsg.getBody_msg();
+        String subject = bMsg.getSubject();
+        String originator = bMsg.getOriginatorVcard_email();
+        String origName = bMsg.getOriginatorVcard_name();
+
+        String fullPath = (name == null || name.length() == 0)
+                ? mCurrentPath : mCurrentPath + "/" + name;
+        String splitStrings[] = fullPath.split("/");
+        mMnsClient.addMceInitiatedOperation("+");
+        int tmp = splitStrings.length;
+        String folderName;
+        if (name != null) {
+            if (name.length() == 0) {
+                folderName = splitStrings[tmp - 1];
+            } else {
+                folderName = name;
+            }
+        } else {
+            folderName = splitStrings[tmp - 1];
+        }
+        if (!isAllowedEmailFolderForPush(folderName)) {
+            rsp.msgHandle = null;
+            rsp.response = ResponseCodes.OBEX_HTTP_FORBIDDEN;
+            return rsp;
+        }
+        String handle = addToEmailFolder(folderName, address, text, subject, originator, origName);
+        if (INTERNAL_ERROR == handle) { // == comparison valid here
+            rsp.msgHandle = null;
+            rsp.response = ResponseCodes.OBEX_HTTP_NOT_FOUND;
+            return rsp;
+        }
+        rsp.msgHandle = handle;
+        rsp.response = ResponseCodes.OBEX_HTTP_OK;
+
+        long accountId = EmailUtils.getAccountId(mMasId);
+        if (V) Log.v(TAG, " Account id before Mail service:: " + accountId);
+
+        Intent emailIn = new Intent();
+        emailIn.setAction("com.android.email.intent.action.MAIL_SERVICE_SEND_PENDING");
+        emailIn.putExtra("com.android.email.intent.extra.ACCOUNT", accountId);
+        mContext.startService(emailIn);
+        return rsp;
+    }
+
+    private int setMsgStatusEmail(long handle,
+            BluetoothMasAppParams bluetoothMasAppParams){
+        //Query the mailbox table to get the id values for Inbox and Trash folder
+        Uri uri1 = Uri.parse("content://com.android.email.provider/mailbox");
+        Cursor cr1 = mContext.getContentResolver().query(uri1, null,
+                "(UPPER(displayName) = 'INBOX' OR UPPER(displayName) LIKE '%TRASH%')", null, null);
+        int inboxFolderId = 0;
+        int deletedFolderId = 0;
+        int msgFolderId = 0;
+        String folderName;
+        if (cr1 != null && cr1.moveToFirst()) {
+            do {
+                folderName = cr1.getString(cr1.getColumnIndex("displayName"));
+                if(folderName.equalsIgnoreCase("INBOX")){
+                    inboxFolderId = cr1.getInt(cr1.getColumnIndex("_id"));
+                } else {
+                    deletedFolderId = cr1.getInt(cr1.getColumnIndex("_id"));
+                }
+            } while (cr1.moveToNext());
+        }
+        if (cr1 != null) {
+            cr1.close();
+        }
+
+        //Query the message table for the given message id
+        long emailMsgId = handle - 0;
+        emailMsgId = handle - OFFSET_START;
+        Uri uri2 = Uri.parse("content://com.android.email.provider/message/"+emailMsgId);
+        Cursor crEmail = mContext.getContentResolver().query(uri2, null, null, null, null);
+        if (crEmail != null && crEmail.moveToFirst()) {
+            if (bluetoothMasAppParams.StatusIndicator == 0) {
+                /* Read Status */
+                ContentValues values = new ContentValues();
+                values.put("flagRead", bluetoothMasAppParams.StatusValue);
+                mContext.getContentResolver().update(uri2, values, null, null);
+            } else {
+                if (bluetoothMasAppParams.StatusValue == 1) { //if the email is deleted
+                    msgFolderId = crEmail.getInt(crEmail.getColumnIndex("mailboxKey"));
+                    if(msgFolderId == deletedFolderId){
+                        // TODO: need to add notification for deleted email here
+                        mMnsClient.addMceInitiatedOperation(Long.toString(handle));
+                        mContext.getContentResolver().delete(
+                                Uri.parse("content://com.android.email.provider/message/"
+                                + emailMsgId), null, null);
+                    } else {
+                        ContentValues values = new ContentValues();
+                        values.put("mailboxKey", deletedFolderId);
+                        mContext.getContentResolver().update(uri2, values, null, null);
+                    }
+                } else { // if the email is undeleted
+                    // TODO: restore it to original folder
+                    ContentValues values = new ContentValues();
+                    values.put("mailboxKey", inboxFolderId);
+                    mContext.getContentResolver().update(uri2, values, null, null);
+                }
+            }
+        }
+        if (crEmail != null) {
+            crEmail.close();
+        }
+        return ResponseCodes.OBEX_HTTP_OK;
+    }
+}
diff --git a/src/org/codeaurora/bluetooth/map/BluetoothMasAppIf.java b/src/org/codeaurora/bluetooth/map/BluetoothMasAppIf.java
new file mode 100644
index 0000000..90afe68
--- /dev/null
+++ b/src/org/codeaurora/bluetooth/map/BluetoothMasAppIf.java
@@ -0,0 +1,615 @@
+/*
+ * Copyright (c) 2010-2013, The Linux Foundation. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *        * Redistributions of source code must retain the above copyright
+ *          notice, this list of conditions and the following disclaimer.
+ *        * Redistributions in binary form must reproduce the above copyright
+ *          notice, this list of conditions and the following disclaimer in the
+ *          documentation and/or other materials provided with the distribution.
+ *        * Neither the name of The Linux Foundation nor
+ *          the names of its contributors may be used to endorse or promote
+ *          products derived from this software without specific prior written
+ *          permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NON-INFRINGEMENT ARE DISCLAIMED.    IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
+ * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
+ * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
+ * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package org.codeaurora.bluetooth.map;
+
+import android.bluetooth.BluetoothDevice;
+import android.content.Context;
+import android.database.ContentObserver;
+import android.database.Cursor;
+import android.net.Uri;
+import android.os.Handler;
+import android.provider.ContactsContract.Contacts;
+import android.provider.ContactsContract.PhoneLookup;
+import android.provider.ContactsContract.CommonDataKinds.Email;
+import android.telephony.TelephonyManager;
+import android.text.TextUtils;
+import android.util.Log;
+
+import org.codeaurora.bluetooth.map.MapUtils.MapUtils;
+import org.codeaurora.bluetooth.map.MapUtils.MsgListingConsts;
+import org.codeaurora.bluetooth.map.MapUtils.CommonUtils.BluetoothMasMessageListingRsp;
+import org.codeaurora.bluetooth.map.MapUtils.CommonUtils.BluetoothMasMessageRsp;
+import org.codeaurora.bluetooth.map.MapUtils.CommonUtils.BluetoothMsgListRsp;
+import org.codeaurora.bluetooth.map.MapUtils.SmsMmsUtils.VcardContent;
+
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.FileNotFoundException;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.List;
+
+import javax.obex.ResponseCodes;
+
+import static org.codeaurora.bluetooth.map.MapUtils.SmsMmsUtils.DELETED;
+import static org.codeaurora.bluetooth.map.MapUtils.SmsMmsUtils.DRAFT;
+import static org.codeaurora.bluetooth.map.MapUtils.SmsMmsUtils.INBOX;
+import static org.codeaurora.bluetooth.map.MapUtils.SmsMmsUtils.OUTBOX;
+import static org.codeaurora.bluetooth.map.MapUtils.SmsMmsUtils.SENT;
+
+/**
+ * This class provides the application interface for MAS Server It interacts
+ * with the SMS repository using Sms Content Provider to service the MAS
+ * requests. It also initializes BluetoothMns thread which is used for MNS
+ * connection.
+ */
+
+public abstract class BluetoothMasAppIf implements IBluetoothMasApp {
+    public static final String TAG = "BluetoothMasAppIf";
+    public static final boolean D = BluetoothMasService.DEBUG;
+    public static final boolean V = BluetoothMasService.VERBOSE;
+
+    protected static final String INTERNAL_ERROR = "ERROR";
+
+    // IOP work around for BMW carkit
+    // The connection is dropped by the carkit When GetMessagesListing results empty list
+    // So, we ignore improper filtering request by messageType.
+    protected static final String BMW = "BMW";
+
+    protected Context mContext;
+    protected int mSupportedMessageTypes;
+    protected int mMasId;
+    protected String mRemoteDeviceName;
+
+    protected String mCurrentPath = null;
+    protected BluetoothMns mMnsClient;
+    protected Handler mHandler = null;
+
+    protected final long OFFSET_START;
+    protected final long OFFSET_END;
+
+    public BluetoothMasAppIf(Context context, Handler handler, int supportedMessageTypes,
+            BluetoothMns mnsClient, int masId, String remoteDeviceName) {
+        mContext = context;
+        mSupportedMessageTypes = supportedMessageTypes;
+        mMasId = masId;
+        mHandler = handler;
+        mMnsClient = mnsClient;
+        mRemoteDeviceName = remoteDeviceName;
+
+        OFFSET_START = HANDLE_OFFSET[masId];
+        OFFSET_END = HANDLE_OFFSET[masId + 1] - 1;
+
+        if (V) Log.v(TAG, "Constructor called");
+    }
+
+    /**
+     * This must be overridden
+     * @return folder list for corresponding MAS
+     */
+    protected abstract List<String> getCompleteFolderList();
+
+    /**
+     * Check the path to a given folder. If setPathFlag is set,
+     * set the path to the new value. Else, just check if the path
+     * exists and don't change the current path.
+     *
+     * @return true if the path exists, and could be accessed.
+     */
+    public boolean checkPath(boolean up, String name, boolean setPathFlag) {
+        if (V) Log.v(TAG, "setPath called");
+        if (V) {
+            Log.v(TAG, "mCurrentPath::"+mCurrentPath);
+            Log.v(TAG, "name::"+name);
+        }
+        if (up == false) {
+            if (name == null || name.length() == 0) {
+                mCurrentPath = (setPathFlag) ? null : mCurrentPath;
+                return true;
+            }
+        } else {
+            if (mCurrentPath == null) {
+                // Can't go above root
+                return false;
+            } else {
+                int LastIndex;
+                if (mCurrentPath.toUpperCase().contains("GMAIL")) {
+                    LastIndex = mCurrentPath.lastIndexOf('/');
+                    mCurrentPath = mCurrentPath.substring(0, LastIndex);
+                    LastIndex = mCurrentPath.lastIndexOf('/');
+                } else {
+                    LastIndex = mCurrentPath.lastIndexOf('/');
+                }
+                if (LastIndex < 0) {
+                    // Reaches root
+                    mCurrentPath = null;
+                } else {
+                    mCurrentPath = mCurrentPath.substring(0, LastIndex);
+                }
+            }
+            if (name == null || name.length() == 0) {
+                // Only going up by one
+                return true;
+            }
+        }
+
+        if (mCurrentPath == null) {
+            if (TELECOM.equals(name)) {
+                mCurrentPath = (setPathFlag) ? TELECOM : mCurrentPath;
+                return true;
+            } else {
+                return false;
+            }
+        }
+
+        String splitStrings[] = mCurrentPath.split("/");
+
+        boolean Result = false;
+        switch (splitStrings.length) {
+        case 0:
+            Result=false;
+            break;
+        case 1:
+            if (name.equals(MSG)) {
+                mCurrentPath += (setPathFlag) ? ("/" + name) : "";
+                Result = true;
+            }
+            break;
+        //Handle folders and subfolders
+        default:
+            List<String> completeFolderList = getCompleteFolderList();
+            for (String FolderName : completeFolderList) {
+                //added second condition for gmail sent folder
+                if (FolderName.equalsIgnoreCase(name)) {
+                    mCurrentPath += (setPathFlag) ? ("/" + name) : "";
+                    Result = true;
+                    break;
+                }
+            }
+            break;
+        }
+        return Result;
+    }
+
+    /**
+     * Set the path to a given folder.
+     *
+     * @return true if the path exists, and could be accessed.
+     */
+    public boolean setPath(boolean up, String name) {
+        return checkPath(up, name, true);
+    }
+
+    /**
+     * Get the number of messages in the folder
+     *
+     * @return number of messages; -1 if error
+     */
+    public int folderListingSize() {
+        if (V) Log.v(TAG, "folderListingSize called, current path " + mCurrentPath);
+
+        if (mCurrentPath == null) {
+            // at root, only telecom folder should be present
+            return 1;
+        }
+
+        if (mCurrentPath.equals(TELECOM)) {
+            // at root -> telecom, only msg folder should be present
+            return 1;
+        }
+        //Add folders and subfolders
+        List<String> completeFolderList = getCompleteFolderList();
+        return completeFolderList.size();
+
+    }
+
+    /**
+     * Get the XML listing of the folders at CurrenthPath
+     *
+     * @return XML listing of the folders
+     */
+    public String folderListing(BluetoothMasAppParams appParam) {
+        if (V) Log.v(TAG, "folderListing called, current path " + mCurrentPath);
+
+        List<String> list = new ArrayList<String>();
+
+        if (mCurrentPath == null) {
+            // at root, only telecom folder should be present
+            if (appParam.ListStartOffset == 0) {
+                list.add(TELECOM);
+            }
+        }else  if (mCurrentPath.equals(TELECOM)) {
+            // at root -> telecom, only msg folder should be present
+            if (appParam.ListStartOffset == 0) {
+                list.add(MSG);
+            }
+        } else  if (!(mCurrentPath.equals(TELECOM + "/" + MSG + "/" + INBOX) ||
+                mCurrentPath.equals(TELECOM + "/" + MSG + "/" + OUTBOX) ||
+                mCurrentPath.equals(TELECOM + "/" + MSG + "/" + DRAFT) ||
+                mCurrentPath.equals(TELECOM + "/" + MSG + "/" + DELETED) ||
+                mCurrentPath.equals(TELECOM + "/" + MSG + "/" + SENT))) {
+            //Add folders and subfolders NOT for SPECIAL FOLDERS
+            int offset = 0;
+            int added = 0;
+            List<String> completeFolderList = getCompleteFolderList();
+            for (String Folder : completeFolderList) {
+                offset++;
+                if ((offset > appParam.ListStartOffset)
+                        && (added < appParam.MaxListCount)) {
+                    list.add(Folder);
+                    if (V) Log.v(TAG, "folderListing AddFolder " + Folder);
+                    added++;
+                }
+            }
+        }
+
+        return MapUtils.folderListingXML(list);
+
+    }
+
+    static final int PHONELOOKUP_ID_COLUMN_INDEX = 0;
+    static final int PHONELOOKUP_LOOKUP_KEY_COLUMN_INDEX = 1;
+    static final int PHONELOOKUP_DISPLAY_NAME_COLUMN_INDEX = 2;
+
+    static final int EMAIL_DATA_COLUMN_INDEX = 0;
+
+    private List<VcardContent> mVcardList = new ArrayList<VcardContent>();;
+    protected VcardListContentObserver mVcardListObserver = new VcardListContentObserver();
+    protected VcardContent getVcardContent(String phoneAddress) {
+        VcardContent vCard = new VcardContent();
+        vCard.tel = phoneAddress;
+
+        Uri uriContacts = Uri.withAppendedPath(PhoneLookup.CONTENT_FILTER_URI,
+                Uri.encode(phoneAddress));
+        Cursor cursorContacts = mContext.getContentResolver().query(
+                uriContacts,
+                new String[] { PhoneLookup._ID, PhoneLookup.LOOKUP_KEY,
+                        PhoneLookup.DISPLAY_NAME }, null, null, null);
+        if (cursorContacts == null) {
+            return vCard;
+        }
+        cursorContacts.moveToFirst();
+        if (cursorContacts.getCount() > 0) {
+            long contactId = cursorContacts
+                    .getLong(PHONELOOKUP_ID_COLUMN_INDEX);
+            String lookupKey = cursorContacts
+                    .getString(PHONELOOKUP_LOOKUP_KEY_COLUMN_INDEX);
+            vCard.name = cursorContacts
+                    .getString(PHONELOOKUP_DISPLAY_NAME_COLUMN_INDEX);
+
+            Uri lookUpUri = Contacts.getLookupUri(contactId, lookupKey);
+            String Id = lookUpUri.getLastPathSegment();
+
+            Cursor crEm = mContext.getContentResolver().query(Email.CONTENT_URI,
+                    new String[] { Email.DATA }, Email.CONTACT_ID + "=?",
+                    new String[] { Id }, null);
+            if (crEm != null) {
+                if (crEm.moveToFirst()) {
+                    vCard.email = "";
+                    if (crEm.moveToFirst()) {
+                        do {
+                            vCard.email += crEm.getString(EMAIL_DATA_COLUMN_INDEX) + ";";
+                        } while (crEm.moveToNext());
+                    }
+                }
+                crEm.close();
+            }
+        }
+        cursorContacts.close();
+        return vCard;
+    }
+    public final class VcardListContentObserver extends ContentObserver {
+        public VcardListContentObserver() {
+            super(null);
+        }
+
+        @Override
+        public void onChange(boolean selfChange) {
+            Log.v(TAG, "Clear VcardList");
+            if(!mVcardList.isEmpty())
+              mVcardList.clear();
+        }
+   }
+
+
+    /**
+     * Check if the entry is not to be filtered out (allowed)
+     */
+    protected boolean allowEntry(String phoneAddress, String filterString) {
+        boolean found = false;
+        VcardContent foundEntry = null;
+        for (VcardContent elem : mVcardList) {
+            if (elem.tel.contains(phoneAddress)) {
+                found = true;
+                foundEntry = elem;
+            }
+        }
+        if (found == false) {
+            VcardContent vCard = getVcardContent(phoneAddress);
+            if (vCard != null) {
+                mVcardList.add(vCard);
+                found = true;
+                foundEntry = vCard;
+                if (V) {
+                    Log.v(TAG, " NEW VCARD ADDED " + vCard.tel + vCard.name
+                            + vCard.email);
+                }
+            } else {
+                if (V) Log.v(TAG, "VCARD NOT FOUND ERROR");
+            }
+        }
+
+        if (found == true) {
+            String regExp = filterString.replace("*", ".*[0-9A-Za-z].*");
+            if ((foundEntry.tel.matches(".*"+regExp+".*"))
+                    || (foundEntry.name.matches(".*"+regExp+".*"))
+                    || (foundEntry.email.matches(".*"+regExp+".*"))) {
+                return true;
+            }
+        }
+        return false;
+    }
+
+    /**
+     * Get the contact name for the given phone number
+     */
+    protected String getContactName(String phoneNumber) {
+        boolean found = false;
+        VcardContent foundEntry = null;
+        if(phoneNumber == null){
+            return null;
+        }
+        if(V) Log.v(TAG, "getContactName " + phoneNumber);
+        for (VcardContent elem : mVcardList) {
+            if (elem.tel == null){
+                continue;
+            }
+            if (elem.tel.contains(phoneNumber)) {
+                found = true;
+                foundEntry = elem;
+                break;
+            }
+        }
+        if (found == false) {
+            foundEntry = getVcardContent(phoneNumber);
+            if (foundEntry != null) {
+                mVcardList.add(foundEntry);
+                found = true;
+            }
+        }
+        if (found == true) {
+            return foundEntry.name;
+        }
+
+        return null;
+    }
+
+    protected class OwnerInfo {
+        public String Name;
+        public String Number;
+    }
+
+    protected OwnerInfo getOwnerInfo() {
+        OwnerInfo info = new OwnerInfo();
+        TelephonyManager tm = (TelephonyManager)mContext.getSystemService(Context.TELEPHONY_SERVICE);
+        if (tm != null) {
+            String localPhoneNum = tm.getLine1Number();
+            String localPhoneName = tm.getLine1AlphaTag();
+
+            if (TextUtils.isEmpty(localPhoneNum)) {
+                localPhoneNum = "";
+            }
+            if (TextUtils.isEmpty(localPhoneName)) {
+                localPhoneName = mContext.getString(android.R.string.unknownName);
+            }
+            info.Name = localPhoneName;
+            info.Number = localPhoneNum;
+        }
+        return info;
+    }
+
+    private OwnerInfo ownerInfo = null;
+
+    /**
+     * Get the owners name
+     */
+    protected String getOwnerName() {
+        if (ownerInfo == null) {
+            ownerInfo = getOwnerInfo();
+        }
+        return ownerInfo.Name;
+    }
+
+    /**
+     * Get the owners phone number
+     */
+    protected String getOwnerNumber() {
+        if (ownerInfo == null) {
+            ownerInfo = getOwnerInfo();
+        }
+        return ownerInfo.Number;
+    }
+
+    /**
+     * Get the list of message in the given folder.
+     * It must be implemented for MessageType specific
+     * @param msgList
+     * @param name
+     * @param rsp
+     * @param appParams
+     * @return
+     */
+    protected abstract BluetoothMsgListRsp msgListingSpecific(List<MsgListingConsts> msgList,
+            String name, BluetoothMasMessageListingRsp rsp, BluetoothMasAppParams appParams);
+
+    /**
+     * Get the list of messages in the given folder
+     *
+     * @return Listing of messages in MAP-msg-listing format
+     */
+    public BluetoothMasMessageListingRsp msgListing(String name, BluetoothMasAppParams appParams) {
+        BluetoothMasMessageListingRsp rsp = new BluetoothMasMessageListingRsp();
+        boolean fileGenerated = false;
+        final String FILENAME = "msglist" + getMasId();
+
+        List<MsgListingConsts> msgList = new ArrayList<MsgListingConsts>();
+
+        if (appParams == null) {
+            return null;
+        }
+
+        BluetoothMsgListRsp specificRsp = msgListingSpecific(msgList, name, rsp, appParams);
+        rsp = specificRsp.rsp;
+
+        if (rsp.rsp != ResponseCodes.OBEX_HTTP_OK) {
+            return rsp;
+        }
+        msgList = specificRsp.msgList;
+        // Process the list based on MaxListCount and list offset
+        String str = null;
+        int numOfItems = msgList.size();
+        int msgDelta = numOfItems - appParams.ListStartOffset;
+        int startIdx = appParams.ListStartOffset;
+        int stopIdx = 0;
+        if (msgDelta <= 0) {
+            List<MsgListingConsts> msgSubList = new ArrayList<MsgListingConsts>();;
+            str = MapUtils.messageListingXML(msgSubList);
+        } else {
+            if (msgDelta <= appParams.MaxListCount) {
+                stopIdx = startIdx + msgDelta;
+            } else {
+                stopIdx = startIdx + appParams.MaxListCount;
+            }
+            List<MsgListingConsts> msgSubList = msgList.subList(startIdx,
+                    stopIdx);
+            str = MapUtils.messageListingXML(msgSubList);
+        }
+        if (str == null) {
+            rsp.rsp = ResponseCodes.OBEX_HTTP_BAD_REQUEST;
+            return rsp;
+        }
+
+        // String str = "this is a test for the data file";
+        try {
+            FileOutputStream bos = mContext.openFileOutput(FILENAME, Context.MODE_PRIVATE);
+            bos.write(str.getBytes());
+            bos.flush();
+            bos.close();
+        } catch (FileNotFoundException e) {
+            // TODO Auto-generated catch block
+            e.printStackTrace();
+        } catch (IOException e) {
+            // TODO Auto-generated catch block
+            e.printStackTrace();
+        }
+        msgList.clear();
+
+        if (V) {
+            Log.v(TAG, "");
+            Log.v(TAG, " MESSAGE LISTING FULL ( total length)" + str.length());
+            Log.v(TAG, str);
+        }
+
+        try {
+            FileInputStream fis = new FileInputStream(mContext.getFilesDir()
+                    + "/" + FILENAME);
+            fis.close();
+            fileGenerated = true;
+        } catch (FileNotFoundException e) {
+            e.printStackTrace();
+        } catch (IOException e) {
+            e.printStackTrace();
+        }
+
+        if (fileGenerated == true) {
+            File file = new File(mContext.getFilesDir() + "/" + FILENAME);
+            rsp.file = file;
+        }
+        rsp.rsp = ResponseCodes.OBEX_HTTP_OK;
+        return rsp;
+    }
+
+    protected abstract BluetoothMasMessageRsp getMessageSpecific(long msgHandle,
+            BluetoothMasMessageRsp rsp, BluetoothMasAppParams bluetoothMasAppParams);
+
+    /**
+     * Get the message for the given message handle
+     *
+     * @return BMSG object
+     */
+    public BluetoothMasMessageRsp msg(String msgHandle,
+            BluetoothMasAppParams bluetoothMasAppParams) {
+        BluetoothMasMessageRsp rsp = new BluetoothMasMessageRsp();
+        if (msgHandle == null || msgHandle.length() == 0) {
+            rsp.file = null;
+            rsp.rsp = ResponseCodes.OBEX_HTTP_BAD_REQUEST;
+            return rsp;
+        }
+        final long handle = Long.valueOf(msgHandle);
+        if (handle < OFFSET_START && handle > OFFSET_END) {
+            rsp.rsp = ResponseCodes.OBEX_HTTP_NOT_FOUND;
+            return rsp;
+        }
+
+        return getMessageSpecific(handle, rsp, bluetoothMasAppParams);
+    }
+
+    /**
+     * Enable/disable notification
+     *
+     * @return Obex response code
+     */
+    public int notification(BluetoothDevice remoteDevice,
+            BluetoothMasAppParams bluetoothMasAppParams) {
+        if (bluetoothMasAppParams.Notification == 1) {
+            startMnsSession(remoteDevice);
+            return ResponseCodes.OBEX_HTTP_OK;
+        } else if (bluetoothMasAppParams.Notification == 0) {
+            stopMnsSession(remoteDevice);
+            return ResponseCodes.OBEX_HTTP_OK;
+        }
+
+        return ResponseCodes.OBEX_HTTP_PRECON_FAILED;
+    }
+
+    /**
+     * Start an MNS obex client session and push notification whenever available
+     */
+    public abstract void startMnsSession(BluetoothDevice remoteDevice);
+
+    /**
+     * Stop pushing notifications and disconnect MNS obex session
+     */
+    public abstract void stopMnsSession(BluetoothDevice remoteDevice);
+
+    public int getMasId() {
+        return mMasId;
+    }
+}
diff --git a/src/org/codeaurora/bluetooth/map/BluetoothMasAppParams.java b/src/org/codeaurora/bluetooth/map/BluetoothMasAppParams.java
new file mode 100644
index 0000000..4514d4b
--- /dev/null
+++ b/src/org/codeaurora/bluetooth/map/BluetoothMasAppParams.java
@@ -0,0 +1,53 @@
+/*
+ * Copyright (c) 2010-2011, The Linux Foundation. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *        * Redistributions of source code must retain the above copyright
+ *          notice, this list of conditions and the following disclaimer.
+ *        * Redistributions in binary form must reproduce the above copyright
+ *          notice, this list of conditions and the following disclaimer in the
+ *          documentation and/or other materials provided with the distribution.
+ *        * Neither the name of The Linux Foundation nor
+ *          the names of its contributors may be used to endorse or promote
+ *          products derived from this software without specific prior written
+ *          permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NON-INFRINGEMENT ARE DISCLAIMED.    IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
+ * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
+ * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
+ * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package org.codeaurora.bluetooth.map;
+
+public final class BluetoothMasAppParams {
+
+    public int MaxListCount;
+    public int ListStartOffset;
+    public short SubjectLength;
+    public long ParameterMask;
+    public byte FilterMessageType;
+    public byte FilterReadStatus;
+    public byte FilterPriority;
+    public String FilterPeriodBegin;
+    public String FilterPeriodEnd;
+    public String FilterRecipient;
+    public String FilterOriginator;
+    public byte FractionRequest;
+    public byte Charset;
+    public byte Attachment;
+    public byte Retry;
+    public byte Transparent;
+    public byte StatusIndicator;
+    public byte StatusValue;
+    public byte Notification;
+
+};
diff --git a/src/org/codeaurora/bluetooth/map/BluetoothMasAppSmsMms.java b/src/org/codeaurora/bluetooth/map/BluetoothMasAppSmsMms.java
new file mode 100644
index 0000000..69fe818
--- /dev/null
+++ b/src/org/codeaurora/bluetooth/map/BluetoothMasAppSmsMms.java
@@ -0,0 +1,2569 @@
+/*
+ * Copyright (c) 2011-2013, The Linux Foundation. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *        * Redistributions of source code must retain the above copyright
+ *          notice, this list of conditions and the following disclaimer.
+ *        * Redistributions in binary form must reproduce the above copyright
+ *          notice, this list of conditions and the following disclaimer in the
+ *          documentation and/or other materials provided with the distribution.
+ *        * Neither the name of The Linux Foundation nor
+ *          the names of its contributors may be used to endorse or promote
+ *          products derived from this software without specific prior written
+ *          permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NON-INFRINGEMENT ARE DISCLAIMED.    IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
+ * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
+ * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
+ * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+package org.codeaurora.bluetooth.map;
+
+import android.app.ActivityManager;
+import android.bluetooth.BluetoothDevice;
+import android.content.ContentResolver;
+import android.content.ContentValues;
+import android.content.Context;
+import android.content.Intent;
+import android.database.Cursor;
+import android.net.Uri;
+import android.os.Handler;
+import android.telephony.SmsManager;
+import android.telephony.SmsMessage;
+import android.telephony.TelephonyManager;
+import android.text.format.Time;
+import android.util.Log;
+import android.util.TimeFormatException;
+
+import org.codeaurora.bluetooth.map.MapUtils.BmessageConsts;
+import org.codeaurora.bluetooth.map.MapUtils.CommonUtils;
+import org.codeaurora.bluetooth.map.MapUtils.MapUtils;
+import org.codeaurora.bluetooth.map.MapUtils.MsgListingConsts;
+import org.codeaurora.bluetooth.map.MapUtils.SmsMmsUtils;
+import org.codeaurora.bluetooth.map.MapUtils.SortMsgListByDate;
+import org.codeaurora.bluetooth.map.MapUtils.CommonUtils.BluetoothMasMessageListingRsp;
+import org.codeaurora.bluetooth.map.MapUtils.CommonUtils.BluetoothMasMessageRsp;
+import org.codeaurora.bluetooth.map.MapUtils.CommonUtils.BluetoothMasPushMsgRsp;
+import org.codeaurora.bluetooth.map.MapUtils.CommonUtils.BluetoothMsgListRsp;
+import org.codeaurora.bluetooth.map.MapUtils.MapUtils.BadRequestException;
+import org.codeaurora.bluetooth.map.MapUtils.SmsMmsUtils.VcardContent;
+import android.provider.ContactsContract.PhoneLookup;
+
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.FileNotFoundException;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.Date;
+import java.util.List;
+import java.util.Random;
+
+import javax.obex.ResponseCodes;
+
+import static org.codeaurora.bluetooth.map.MapUtils.SmsMmsUtils.DELETED;
+import static org.codeaurora.bluetooth.map.MapUtils.SmsMmsUtils.DRAFT;
+import static org.codeaurora.bluetooth.map.MapUtils.SmsMmsUtils.DRAFTS;
+import static org.codeaurora.bluetooth.map.MapUtils.SmsMmsUtils.INBOX;
+import static org.codeaurora.bluetooth.map.MapUtils.SmsMmsUtils.OUTBOX;
+import static org.codeaurora.bluetooth.map.MapUtils.SmsMmsUtils.QUEUED;
+import static org.codeaurora.bluetooth.map.MapUtils.SmsMmsUtils.SENT;
+
+public class BluetoothMasAppSmsMms extends BluetoothMasAppIf {
+    public final String TAG = "BluetoothMasAppSmsMms";
+
+    private final long SMS_OFFSET_START;
+    private final long MMS_OFFSET_START;
+
+    private static final String SMS_GSM = "SMS_GSM";
+    private static final String SMS_CDMA = "SMS_CDMA";
+    private static final String MMS = "MMS";
+    // OMA-TS-MMS-ENC defined many types in X-Mms-Message-Type.
+    // Only m-send-req (128) m-retrieve-conf (132), m-notification-ind (130)
+    // are interested by user
+    private static final String INTERESTED_MESSAGE_TYPE_CLAUSE =
+                "(m_type = 128 OR m_type = 132 OR m_type = 130)";
+
+    public BluetoothMasAppSmsMms(Context context, Handler handler, BluetoothMns mnsClient,
+            int masId, String remoteDeviceName) {
+        super(context, handler, MESSAGE_TYPE_SMS_MMS, mnsClient, masId, remoteDeviceName);
+        SMS_OFFSET_START = OFFSET_START;
+        MMS_OFFSET_START = OFFSET_START + ((OFFSET_END - OFFSET_START) / 2);
+
+        // Clear out deleted items from database
+        cleanUp();
+
+        if (V) Log.v(TAG, "BluetoothMasAppSmsMms Constructor called");
+    }
+
+    /**
+     * Start an MNS obex client session and push notification whenever available
+     */
+    public void startMnsSession(BluetoothDevice remoteDevice) {
+        if (V) Log.v(TAG, "Start MNS Client");
+        mMnsClient.getHandler()
+                .obtainMessage(BluetoothMns.MNS_CONNECT, 0, -1, remoteDevice)
+                .sendToTarget();
+    }
+
+    /**
+     * Stop pushing notifications and disconnect MNS obex session
+     */
+    public void stopMnsSession(BluetoothDevice remoteDevice) {
+        if (V) Log.v(TAG, "Stop MNS Client");
+        mMnsClient.getHandler()
+                .obtainMessage(BluetoothMns.MNS_DISCONNECT, 0, -1,
+                remoteDevice).sendToTarget();
+    }
+
+    @Override
+    protected List<String> getCompleteFolderList() {
+        return SmsMmsUtils.FORLDER_LIST_SMS_MMS;
+    }
+
+    private void cleanUp() {
+        // Remove the deleted item entries
+        mContext.getContentResolver().delete(Uri.parse("content://mms-sms/conversations/"),
+                "thread_id = " + DELETED_THREAD_ID, null);
+        if(V) Log.v(TAG, "Unregister PhoneLookUP observer");
+        mContext.getContentResolver().unregisterContentObserver(mVcardListObserver);
+    }
+
+    public boolean checkPrecondition() {
+        // TODO: Add any precondition check routine for this MAS instance
+        return true;
+    }
+
+    public void onConnect() {
+        // TODO: Add any routine to be run when OBEX connection established
+        if(V) Log.v(TAG, "Register PhoneLookUP observer");
+        mContext.getContentResolver().registerContentObserver(PhoneLookup.CONTENT_FILTER_URI, true,
+                mVcardListObserver);
+    }
+
+    public void onDisconnect() {
+        cleanUp();
+    }
+
+    @Override
+    protected BluetoothMsgListRsp msgListingSpecific(List<MsgListingConsts> msgList, String name,
+            BluetoothMasMessageListingRsp rsp, BluetoothMasAppParams appParams) {
+        BluetoothMsgListRsp bmlr = new BluetoothMsgListRsp();
+        boolean validFilter = false;
+        String fullPath = (name == null || name.length() == 0) ? mCurrentPath :
+                CommonUtils.getFullPath(name, mContext, getCompleteFolderList(), mCurrentPath);
+        if (fullPath == null) {
+            // Child folder not present
+            rsp.rsp = ResponseCodes.OBEX_HTTP_BAD_REQUEST;
+            bmlr.rsp = rsp;
+            return bmlr;
+        }
+
+        if (V) {
+            Log.v(TAG, "appParams.FilterMessageType ::"+ appParams.FilterMessageType);
+            Log.v(TAG, "Condition result::"+ (appParams.FilterMessageType & 0x0B));
+        }
+        String splitStrings[] = fullPath.split("/");
+        if (splitStrings.length == 3) {
+            String folderName = splitStrings[2];
+            if (V) Log.v(TAG, "folderName: " + folderName);
+            if (CommonUtils.validateFilterPeriods(appParams) == 0) {
+                rsp.rsp = ResponseCodes.OBEX_HTTP_BAD_REQUEST;
+                bmlr.rsp = rsp;
+                return bmlr;
+            }
+            if (appParams.FilterReadStatus > 0x02) {
+                rsp.rsp = ResponseCodes.OBEX_HTTP_BAD_REQUEST;
+                bmlr.rsp = rsp;
+                return bmlr;
+            }
+
+            // TODO: Filter priority?
+            /*
+             * There is no support for FilterPriority in SMS/MMS. So, we will
+             * assume Filter Priority is always non-high which makes sense for
+             * SMS/MMS.We check the content provider only if the Filter Priority
+             * is "unfiltered" or "non-high". Else, simply return an empty
+             * string. If the Filter priority is greater than 2, return a bad
+             * request.
+             */
+
+            if (appParams.FilterPriority == 0 || appParams.FilterPriority == 0x02) {
+                final int phoneType = TelephonyManager.getDefault().getPhoneType();
+                if ((appParams.FilterMessageType & 0x03) == 0 ||
+                        ((appParams.FilterMessageType & 0x01) == 0 &&
+                                phoneType == TelephonyManager.PHONE_TYPE_GSM) ||
+                        ((appParams.FilterMessageType & 0x02) == 0 &&
+                                phoneType == TelephonyManager.PHONE_TYPE_CDMA)) {
+                    validFilter = true;
+                    BluetoothMsgListRsp bmlrSms = msgListSms(msgList, folderName,
+                            rsp, appParams);
+                    bmlr.msgList = bmlrSms.msgList;
+                    bmlr.rsp = bmlrSms.rsp;
+                }
+                // Now that all of the SMS messages have been listed. Look for
+                // any
+                // MMS messages and provide them
+                if((appParams.FilterMessageType & 0x08) == 0) {
+                    Log.v(TAG, "About to retrieve msgListMms ");
+                    // MMS draft folder is called //mms/drafts not //mms/draft like
+                    // SMS
+                    validFilter = true;
+                    if (DRAFT.equalsIgnoreCase(folderName)) {
+                        folderName = DRAFTS;
+                    }
+                    BluetoothMsgListRsp bmlrMms = msgListMms(msgList, folderName, rsp, appParams);
+                    bmlr.msgList = bmlrMms.msgList;
+                    bmlr.rsp = bmlrMms.rsp;
+                }
+                if (validFilter != true) {
+                    if (V) Log.v(TAG, "Invalid message filter, returning empty-list");
+                    rsp.rsp = ResponseCodes.OBEX_HTTP_OK;
+                    bmlr.rsp = rsp;
+                    return bmlr;
+                }
+            } else {
+                if (appParams.FilterPriority > 0x02) {
+                    rsp.rsp = ResponseCodes.OBEX_HTTP_BAD_REQUEST;
+                    bmlr.rsp = rsp;
+                    return bmlr;
+                }
+            }
+        }
+
+        // Now that the message list exists, we can sort the list by date
+        Collections.sort(bmlr.msgList, new SortMsgListByDate());
+        rsp.rsp = ResponseCodes.OBEX_HTTP_OK;
+        bmlr.rsp = rsp;
+        return bmlr;
+    }
+
+    @Override
+    protected BluetoothMasMessageRsp getMessageSpecific(long msgHandle, BluetoothMasMessageRsp rsp,
+            BluetoothMasAppParams bluetoothMasAppParams) {
+        final long handle = Long.valueOf(msgHandle);
+
+        if (handle >= MMS_OFFSET_START) { // MMS
+            /*
+             * Spec 5.6.4 says MSE shall reject request with value native
+             * for MMS and Email
+             */
+            if ((int)bluetoothMasAppParams.Charset == 0) {
+                rsp.rsp = ResponseCodes.OBEX_HTTP_BAD_REQUEST;
+                return rsp;
+            }
+            return getMessageMms(msgHandle, rsp);
+        } else { // SMS
+            return getMessageSms(msgHandle, mContext, rsp, bluetoothMasAppParams);
+        }
+    }
+
+    /**
+     * Push a outgoing message from MAS Client to the network
+     *
+     * @return Response to push command
+     */
+    public BluetoothMasPushMsgRsp pushMsg(String name, File file,
+            BluetoothMasAppParams bluetoothMasAppParams) throws BadRequestException {
+        BluetoothMasPushMsgRsp rsp = new BluetoothMasPushMsgRsp();
+        rsp.response = ResponseCodes.OBEX_HTTP_UNAVAILABLE;
+        rsp.msgHandle = null;
+
+        if(!checkPath(false, name, false) ||
+                mCurrentPath == null ||
+                mCurrentPath.equals("telecom") ||
+                (mCurrentPath.equals("telecom/msg") && (name == null || name.length() == 0))) {
+            rsp.response = ResponseCodes.OBEX_HTTP_BAD_REQUEST;
+            return rsp;
+        }
+
+        ActivityManager am = (ActivityManager)mContext.getSystemService(Context.ACTIVITY_SERVICE);
+        ActivityManager.MemoryInfo outInfo = new ActivityManager.MemoryInfo();
+        am.getMemoryInfo(outInfo);
+        final long allowedMem = outInfo.availMem - outInfo.threshold;
+
+        byte[] readBytes = null;
+        FileInputStream fis = null;
+        try {
+            fis = new FileInputStream(file);
+            if(file.length() > allowedMem){
+                rsp.response = ResponseCodes.OBEX_HTTP_ENTITY_TOO_LARGE;
+                rsp.msgHandle = null;
+                Log.d(TAG,"Message body is larger than the max length allowed");
+                return rsp;
+            } else {
+                readBytes = new byte[(int) file.length()];
+                fis.read(readBytes);
+            }
+        } catch (FileNotFoundException e) {
+            Log.e(TAG, e.getMessage());
+            return rsp;
+        } catch (IOException e) {
+            Log.e(TAG, e.getMessage());
+            return rsp;
+        } catch (SecurityException e) {
+            Log.e(TAG, e.getMessage());
+            return rsp;
+        } finally {
+            if (fis != null) {
+                try {
+                    fis.close();
+                } catch (IOException e) {
+                    Log.e(TAG, "Error while closing stream"+ e.toString());
+                }
+            }
+        }
+
+        String readStr = "";
+        String type = "";
+        try {
+            readStr = new String(readBytes);
+            type = MapUtils.fetchType(readStr);
+        } catch (Exception e) {
+            throw new BadRequestException(e.getMessage());
+        }
+        if (type == null) {
+            rsp.response = ResponseCodes.OBEX_HTTP_BAD_REQUEST;
+            return rsp;
+        }
+        if (SMS_GSM.equalsIgnoreCase(type) || SMS_CDMA.equalsIgnoreCase(type)) {
+            return pushMessageSms(rsp, readStr, name, bluetoothMasAppParams);
+        } else if (MMS.equals(type) && ((int)bluetoothMasAppParams.Charset != 0)) {
+            // If the message to be pushed is an MMS message, extract any text,
+            // discard
+            // any attachments and convert the message to an SMS
+            if (type.equalsIgnoreCase("MMS")) {
+                /*
+                 * The pair of calls below is used to send the MMS message out to
+                 * the network.You need to first move the message to the drafts
+                 * folder and then move the message from drafts to the outbox
+                 * folder. This action causes the message to also be added to the
+                 * pending_msgs table in the database. The transaction service will
+                 * then send the message out to the network the next time it is
+                 * scheduled to run
+                 */
+                rsp = pushMessageMms(rsp, readStr, name);
+                return rsp;
+            }
+        }
+        rsp.response = ResponseCodes.OBEX_HTTP_BAD_REQUEST;
+        return rsp;
+    }
+
+    /**
+     * Sets the message status (read/unread, delete)
+     *
+     * @return Obex response code
+     */
+    public int msgStatus(String msgHandle, BluetoothMasAppParams bluetoothMasAppParams) {
+        if ((bluetoothMasAppParams.StatusIndicator != 0)
+                && (bluetoothMasAppParams.StatusIndicator != 1)) {
+            return ResponseCodes.OBEX_HTTP_PRECON_FAILED;
+        }
+        if ((bluetoothMasAppParams.StatusValue != 0)
+                && (bluetoothMasAppParams.StatusValue != 1)) {
+            return ResponseCodes.OBEX_HTTP_PRECON_FAILED;
+        }
+        final long handle = Long.valueOf(msgHandle);
+        if (handle < OFFSET_START && handle > OFFSET_END) {
+            return ResponseCodes.OBEX_HTTP_NOT_FOUND;
+        }
+        if (handle >= MMS_OFFSET_START) { // MMS
+            return setMsgStatusMms(handle, bluetoothMasAppParams);
+        } else { // SMS
+            return setMsgStatusSms(handle, bluetoothMasAppParams);
+        }
+    }
+
+    /**
+     * Sets the message update
+     *
+     * @return Obex response code
+     */
+    public int msgUpdate() {
+        if (V) Log.v(TAG, "Message Update");
+        // UpdateInbox for MMS/SMS is not supported
+        return ResponseCodes.OBEX_HTTP_NOT_IMPLEMENTED;
+    }
+
+    private boolean isOutgoingSMSMessage(int type) {
+        if (type == 1) {
+            return false;
+        }
+        return true;
+    }
+
+    /**
+     * Get the folder name (MAP representation) based on the folder type value
+     * in SMS database
+     */
+    private String getMAPFolder(String type, String threadId) {
+        String folder = null;
+        if (type == null || threadId == null){
+            if (V) Log.v(TAG, "getMapFolder cannot parse folder type");
+            return null;
+        }
+
+        if (Integer.valueOf(threadId) == DELETED_THREAD_ID) {
+            folder = DELETED;
+        } else {
+            switch (Integer.valueOf(type)) {
+            case 1:
+                folder = INBOX;
+                break;
+            case 2:
+                folder = SENT;
+                break;
+            case 3:
+                folder = DRAFT;
+                break;
+            case 4:
+            case 5:
+            case 6:
+                folder = OUTBOX;
+                break;
+            default:
+                break;
+            }
+        }
+        return folder;
+    }
+
+    /**
+     * Get the folder name (MAP representation) based on the message Handle
+     */
+    private String getContainingFolder(long msgHandle) {
+        String folder = null;
+        Cursor cr = mContext.getContentResolver().query(
+                Uri.parse("content://sms/" + msgHandle),
+                new String[] { "_id", "type", "thread_id"}, null, null, null);
+        if (cr != null) {
+            if (cr.getCount() > 0) {
+                cr.moveToFirst();
+                folder = getMAPFolder(cr.getString(cr.getColumnIndex("type")),
+                        cr.getString(cr.getColumnIndex("thread_id")));
+            }
+            cr.close();
+        }
+        return folder;
+    }
+
+    /**
+     * Get the SMS Deliver PDU for the given SMS
+     */
+    private String getSMSDeliverPdu(String smsBody, String dateTime, String address) {
+        Time time = new Time();
+        time.set(Long.valueOf(dateTime));
+
+        String timeStr = time.format3339(false);
+
+        // Extract the YY, MM, DD, HH, MM, SS from time
+        String tempTimeStr = timeStr.substring(2,4) + timeStr.substring(5, 7)
+                + timeStr.substring(8, 10) + timeStr.substring(11, 13) +
+                timeStr.substring(14, 16) + timeStr.substring(17, 19);
+
+        /* Calculate the time zone offset
+         * An offset of 1 indicates 15 min difference between local
+         * time and GMT. MSB of 1 in offset indicates it is negative
+         */
+        String tZoneStr = timeStr.substring(timeStr.length()- 6);
+        int tempInt = Integer.valueOf(tZoneStr.substring(tZoneStr.length()-2));
+        int tZone15offset = tempInt / 15;
+
+        tZone15offset += (Integer.valueOf(tZoneStr.substring(tZoneStr.length()-5,
+                tZoneStr.length()-3)) * 4);
+        if (timeStr.charAt(timeStr.length()-6) == '-'){
+            tZone15offset = tZone15offset | 0x80;
+        }
+
+        String tZone15OffsetHexStr = "";
+
+        // Add 0 as prefix for single digit offset
+        if(((int) tZone15offset & 0xff) < 0x10){
+            tZone15OffsetHexStr += "0";
+        }
+        tZone15OffsetHexStr += Integer.toHexString(tZone15offset);
+
+        tempTimeStr += tZone15OffsetHexStr;
+
+        // Swap the nibble
+        String encodedTimeStr = "";
+        for (int i=0; i<tempTimeStr.length(); i=i+2){
+            encodedTimeStr += tempTimeStr.substring(i+1, i+2);
+            encodedTimeStr += tempTimeStr.substring(i, i+1);
+        }
+
+        byte[] byteAddress = address.getBytes();
+
+        // Let the service center number be 0000000000
+        String smsPdu = "0681000000000004";
+
+        // Extract only digits out of the phone address
+        StringBuffer strbufAddress = new StringBuffer(address.length() + 1);
+        for (int i=0; i<address.length(); i++){
+            if (V){
+                Log.v(TAG, " VAL " + address.substring(i, i+1));
+            }
+            if (byteAddress[i] >= 48 && byteAddress[i] <= 57){
+                strbufAddress.append(Integer.parseInt(address.substring(i, i+1)));
+            }
+        }
+
+        int addressLength = strbufAddress.length();
+
+        String addressLengthStr = "";
+
+        if(((int) addressLength & 0xff) < 0x10)
+            addressLengthStr += "0";
+        addressLengthStr += Integer.toHexString(addressLength);
+
+        smsPdu = smsPdu + addressLengthStr;
+        smsPdu = smsPdu + "81";
+
+        String strAddress = new String(strbufAddress);
+
+        // Use getSubmitPdu only to obtain the encoded msg and encoded address
+        byte[] msg = SmsMessage.getSubmitPdu(null, strAddress, smsBody, false).encodedMessage;
+
+        int addLength = Integer.valueOf(msg[2]);
+        if (addLength %2 != 0){
+            addLength++;
+        }
+        addLength = addLength / 2;
+
+        // Extract the message from the SubmitPdu
+        int msgOffset = 7 + addLength;
+        int msgLength = msg.length - msgOffset;
+
+        StringBuffer strbufMessage = new StringBuffer(msgLength * 2);
+
+        // Convert from byte to Hex String
+        for (int i=msgOffset; i<msgLength + msgOffset; i++) {
+            if (((int) msg[i] & 0xff) < 0x10) {
+                strbufMessage.append("0");
+            }
+            strbufMessage.append((Long.toString((int) msg[i] & 0xff, 16)));
+        }
+
+        int encodedAddressLength = strAddress.length() / 2;
+        if (strAddress.length() % 2 != 0) {
+            encodedAddressLength++;
+        }
+
+        StringBuffer strbufAddress1 = new StringBuffer(msgLength * 2);
+
+        // Convert from byte to Hex String
+        for(int i=4; i<encodedAddressLength + 4; i++)
+        {
+            if(((int) msg[i] & 0xff) < 0x10)
+                strbufAddress1.append("0");
+            strbufAddress1.append((Long.toString((int) msg[i] & 0xff, 16)));
+        }
+
+        smsPdu += strbufAddress1;
+        smsPdu += "0000";
+        smsPdu += encodedTimeStr;
+
+        int smsBodyLength = smsBody.length();
+        String smsMessageTextLengthStr = "";
+
+        if(((int) smsBodyLength & 0xff) < 0x10){
+            smsMessageTextLengthStr += "0";
+        }
+        smsMessageTextLengthStr += Integer.toHexString(smsBodyLength);
+
+        smsPdu += smsMessageTextLengthStr;
+        smsPdu += strbufMessage;
+        smsPdu = smsPdu.toUpperCase();
+        return smsPdu;
+    }
+
+    /**
+     * Adds a SMS to the Sms ContentProvider
+     */
+    private String addToSmsFolder(String folder, String address, String text) {
+        ContentValues values = new ContentValues();
+        // thread_id is handled by SmsProvider
+        values.put("body", text);
+        values.put("address", address);
+        values.put("read", 0);
+        values.put("seen", 0);
+        /*
+         * status none -1 complete 0 pending 64 failed 128
+         */
+        values.put("status", -1);
+        /*
+         * outbox 4 queued 6
+         */
+        values.put("locked", 0);
+        values.put("error_code", 0);
+        Uri uri = mContext.getContentResolver().insert(
+                Uri.parse("content://sms/" + folder), values);
+        if (V){
+            Log.v(TAG, " NEW URI " + ((uri == null) ? "null" : uri.toString()));
+        }
+
+        if (uri == null) {
+            return INTERNAL_ERROR;
+        }
+        String str = uri.toString();
+        String[] splitStr = str.split("/");
+        if (splitStr.length < 4) {
+            return INTERNAL_ERROR;
+        }
+        if (V){
+            Log.v(TAG, " NEW HANDLE " + splitStr[3]);
+        }
+        return splitStr[3];
+    }
+
+    private void updateMMSThreadId(long handle, int threadId) {
+        ContentValues values = new ContentValues();
+        values.put("thread_id", threadId);
+        mContext.getContentResolver().update(Uri.parse("content://mms/" + handle),
+                values, null, null);
+    }
+
+    private void deleteMMS(long handle) {
+        Cursor cr = mContext.getContentResolver().query(Uri.parse("content://mms/" + handle),
+                null, null, null, null);
+        if (cr != null && cr.moveToFirst()){
+            int threadId = cr.getInt(cr.getColumnIndex(("thread_id")));
+            if (threadId != DELETED_THREAD_ID){
+                // Move to deleted folder
+                updateMMSThreadId(handle, Integer.valueOf(DELETED_THREAD_ID));
+            } else {
+                // Delete the message permanently
+                long msgId = handle + MMS_OFFSET_START;
+                mMnsClient.addMceInitiatedOperation(Long.toString(msgId));
+                mContext.getContentResolver().delete(Uri.parse("content://mms/" + handle),
+                        null, null);
+            }
+        }
+        if (cr != null) {
+            cr.close();
+        }
+    }
+
+    private void unDeleteMMS(long msgHandle) {
+        Cursor cr = mContext.getContentResolver().query(Uri.parse("content://mms/" + msgHandle),
+                null, null, null, null );
+        if (cr == null) {
+            if (V){
+                Log.v(TAG, "unable to query content://mms/" + msgHandle);
+            }
+            return;
+        }
+        if (cr.moveToFirst()){
+            // Make sure that the message is in delete folder
+            String currentThreadId = cr.getString(cr.getColumnIndex("thread_id"));
+            if (currentThreadId != null && Integer.valueOf(currentThreadId) != -1){
+                if (V){
+                    Log.v(TAG, " Not in delete folder");
+                }
+                return;
+            }
+
+            // Fetch the address of the deleted message
+            String address = getMmsMsgAddress(msgHandle);
+
+            // Search the database for the given message ID
+            Cursor crThreadId = mContext.getContentResolver().query(Uri.parse("content://mms/"),
+                    null,"_id = " + msgHandle + " AND thread_id != -1", null, null);
+            if (crThreadId != null && crThreadId.moveToFirst()) {
+                // A thread for the given message ID exists in the database
+                String threadIdStr = crThreadId.getString(crThreadId.getColumnIndex("thread_id"));
+                if (V){
+                    Log.v(TAG, " THREAD ID " + threadIdStr);
+                }
+                updateMMSThreadId(msgHandle, Integer.valueOf(threadIdStr));
+            } else {
+                /* No thread for the given address
+                 * Create a fake message to obtain the thread, use that thread_id
+                 * and then delete the fake message
+                 */
+                ContentValues tempValue = new ContentValues();
+                tempValue.put("address", address);
+                tempValue.put("type", "20");
+                Uri tempUri = mContext.getContentResolver().insert( Uri.parse("content://sms/"),
+                        tempValue);
+
+                if (tempUri != null) {
+                    Cursor tempCr = mContext.getContentResolver().query(tempUri, null, null, null,
+                            null);
+                    if (tempCr != null) {
+                        tempCr.moveToFirst();
+                        String newThreadIdStr = tempCr.getString(
+                                tempCr.getColumnIndex("thread_id"));
+                        tempCr.close();
+                        updateMMSThreadId(msgHandle, Integer.valueOf(newThreadIdStr));
+                    }
+                    mContext.getContentResolver().delete(tempUri, null, null);
+                } else {
+                    Log.e(TAG, "Error in undelete");
+                }
+            }
+            if (crThreadId != null) {
+                crThreadId.close();
+            }
+        } else {
+            if (V) {
+                Log.v(TAG, "msgHandle not found");
+            }
+        }
+        cr.close();
+    }
+
+    private void updateSMSThreadId(long msgHandle, int threadId) {
+        ContentValues values = new ContentValues();
+        values.put("thread_id", threadId);
+        mContext.getContentResolver().update(Uri.parse("content://sms/" + msgHandle),
+                values, null, null);
+    }
+
+    private void deleteSMS(long handle) {
+        Cursor cr = mContext.getContentResolver().query(Uri.parse("content://sms/" + handle),
+                null, null, null, null);
+        if (cr != null && cr.moveToFirst()){
+            int threadId = cr.getInt(cr.getColumnIndex(("thread_id")));
+            if (threadId != DELETED_THREAD_ID){
+                // Move to deleted folder
+                updateSMSThreadId(handle, Integer.valueOf(DELETED_THREAD_ID));
+            } else {
+                // Delete the message permanently
+                long msgHandle = handle + SMS_OFFSET_START;
+                mMnsClient.addMceInitiatedOperation(Long.toString(msgHandle));
+                mContext.getContentResolver().delete(Uri.parse("content://sms/" + handle),
+                        null, null);
+            }
+        }
+        if (cr != null) {
+            cr.close();
+        }
+    }
+
+    private void unDeleteSMS(long msgHandle){
+        Cursor cr = mContext.getContentResolver().query(Uri.parse("content://sms/" + msgHandle),
+                null, null, null, null );
+        if (cr == null) {
+            return;
+        }
+
+        if (cr.moveToFirst()){
+            // Make sure that the message is in delete folder
+            String currentThreadId = cr.getString(cr.getColumnIndex("thread_id"));
+            if (currentThreadId != null && Integer.valueOf(currentThreadId) != -1){
+                if (V){
+                    Log.v(TAG, " Not in delete folder");
+                }
+                return;
+            }
+
+            // Fetch the address of the deleted message
+            String address = cr.getString(cr.getColumnIndex("address"));
+
+            // Search the database for the given address
+            Cursor crThreadId = mContext.getContentResolver().query(Uri.parse("content://sms/"),
+                    null, "address = " + address + " AND thread_id != -1", null, null);
+            if (crThreadId != null && crThreadId.moveToFirst()) {
+                // A thread for the given address exists in the database
+                String threadIdStr = crThreadId.getString(crThreadId.getColumnIndex("thread_id"));
+                if (V){
+                    Log.v(TAG, " THREAD ID " + threadIdStr);
+                }
+                updateSMSThreadId(msgHandle, Integer.valueOf(threadIdStr));
+            } else {
+                /* No thread for the given address
+                 * Create a fake message to obtain the thread, use that thread_id
+                 * and then delete the fake message
+                 */
+                ContentValues tempValue = new ContentValues();
+                tempValue.put("address", address);
+                tempValue.put("type", "20");
+                Uri tempUri = mContext.getContentResolver().insert( Uri.parse("content://sms/"),
+                        tempValue);
+
+                if (tempUri != null) {
+                    Cursor tempCr = mContext.getContentResolver().query(tempUri, null, null, null,
+                            null);
+                    if (tempCr != null) {
+                        tempCr.moveToFirst();
+                        String newThreadIdStr = tempCr.getString(
+                                tempCr.getColumnIndex("thread_id"));
+                        tempCr.close();
+                        updateSMSThreadId(msgHandle, Integer.valueOf(newThreadIdStr));
+                    }
+
+                    mContext.getContentResolver().delete(tempUri, null, null);
+                }
+            }
+            if (crThreadId != null) {
+                crThreadId.close();
+            }
+        } else {
+            if (V) {
+                Log.v(TAG, "msgHandle not found");
+            }
+        }
+        cr.close();
+    }
+
+    /**
+     * Obtain the number of MMS messages
+     */
+    private int getNumMmsMsgs(String name) {
+        int msgCount = 0;
+
+        if ( name.equalsIgnoreCase(DELETED)){
+            Uri uri = Uri.parse("content://mms/");
+            ContentResolver cr = mContext.getContentResolver();
+            Cursor cursor = cr.query(uri, null, "thread_id = " + DELETED_THREAD_ID
+                    + " AND " + INTERESTED_MESSAGE_TYPE_CLAUSE, null, null);
+            if(cursor != null){
+                msgCount = cursor.getCount();
+                cursor.close();
+            }
+        } else {
+            Uri uri = Uri.parse("content://mms/" + name);
+            ContentResolver cr = mContext.getContentResolver();
+            Cursor cursor = cr.query(uri, null, "thread_id <> " + DELETED_THREAD_ID
+                    + " AND " + INTERESTED_MESSAGE_TYPE_CLAUSE, null, null);
+            if(cursor != null){
+                msgCount = cursor.getCount();
+                cursor.close();
+            }
+        }
+        return msgCount;
+    }
+
+    /**
+     * Obtain the MMS message ID from Handle
+     */
+    private Long getMmsMsgHndToID(long msgHandle) {
+        long msgID = -1;
+        String whereClause = " mid= " + (msgHandle - MMS_OFFSET_START);
+        Uri uri = Uri.parse("content://mms/part");
+        ContentResolver cr = mContext.getContentResolver();
+        Cursor cursor = cr.query(uri, null, whereClause, null, null);
+        if (cursor != null) {
+            if (cursor.getCount() > 0) {
+                cursor.moveToFirst();
+                int handleInd = cursor.getColumnIndex("mid");
+                msgID = cursor.getLong(handleInd);
+            }
+            cursor.close();
+        }
+        return msgID;
+    }
+
+    /**
+     * Obtain the MMS message MID list
+     */
+    private List<Integer> getMmsMsgMIDs(String whereClause) {
+        List<Integer> idList = new ArrayList<Integer>();
+        Uri uri = Uri.parse("content://mms");
+        ContentResolver cr = mContext.getContentResolver();
+        Cursor crID = cr.query(uri, null, whereClause, null, null);
+        if (crID != null) {
+            int idInd = crID.getColumnIndex("_id");
+            if (crID.getCount() != 0) {
+                crID.moveToFirst();
+                do {
+                    idList.add(Integer.valueOf(crID.getInt(idInd)));
+                } while (crID.moveToNext());
+            }
+            crID.close();
+        }
+        return idList;
+    }
+
+    /**
+     * Build a whereclause for MMS filtering
+     */
+    private String bldMmsWhereClause(BluetoothMasAppParams appParams, int foldertype) {
+        String whereClause = "";
+        if ( foldertype != -1) {
+            // Inbox, Outbox, Sent, Draft folders
+            whereClause = "msg_box=" + foldertype + " AND thread_id <> " + DELETED_THREAD_ID
+                    + " AND " + INTERESTED_MESSAGE_TYPE_CLAUSE;
+        } else {
+            // Deleted folder
+            whereClause =  "thread_id = " + DELETED_THREAD_ID + " AND "
+                    + INTERESTED_MESSAGE_TYPE_CLAUSE;
+        }
+
+        /* Filter readstatus: 0 no filtering, 0x01 get unread, 0x10 get read */
+        if (appParams.FilterReadStatus != 0) {
+            if ((appParams.FilterReadStatus & 0x1) != 0) {
+                if (whereClause.length() != 0) {
+                    whereClause += " AND ";
+                }
+                whereClause += " read=0 ";
+            }
+            if ((appParams.FilterReadStatus & 0x02) != 0) {
+                if (whereClause.length() != 0) {
+                    whereClause += " AND ";
+                }
+                whereClause += " read=1 ";
+            }
+        }
+
+        /* Filter Period Begin */
+        if ((appParams.FilterPeriodBegin != null)
+                && (appParams.FilterPeriodBegin.length() > 0)) {
+            Time time = new Time();
+            try {
+                time.parse(appParams.FilterPeriodBegin.trim());
+                if (whereClause.length() != 0) {
+                    whereClause += " AND ";
+                }
+                whereClause += "date >= " + (time.toMillis(false))/1000;
+            } catch (TimeFormatException e) {
+                Log.d(TAG, "Bad formatted FilterPeriodBegin "
+                        + appParams.FilterPeriodBegin);
+            }
+        }
+
+        /* Filter Period End */
+        if ((appParams.FilterPeriodEnd != null)
+                && (appParams.FilterPeriodEnd.length() > 0)) {
+            Time time = new Time();
+            try {
+                time.parse(appParams.FilterPeriodEnd.trim());
+                if (whereClause.length() != 0) {
+                    whereClause += " AND ";
+                }
+                whereClause += "date < " + (time.toMillis(false))/1000;
+            } catch (TimeFormatException e) {
+                Log.d(TAG, "Bad formatted FilterPeriodEnd "
+                        + appParams.FilterPeriodEnd);
+            }
+        }
+        //Delivery report check
+        if (whereClause.length() != 0) {
+            whereClause += " AND ";
+        }
+        whereClause += "d_rpt > 0";
+
+        return whereClause;
+    }
+
+    /**
+     * Obtain the MMS msg_box id
+     */
+    private int getMmsMsgBox(long msgID) {
+        int val = -1;
+        String whereClause = " _id= " + msgID;
+        Uri uri = Uri.parse("content://mms/");
+        ContentResolver cr = mContext.getContentResolver();
+        Cursor cursor = cr.query(uri, null, whereClause, null, null);
+        if (cursor != null) {
+            if (cursor.getCount() > 0) {
+                cursor.moveToFirst();
+                int msgBoxInd = cursor.getColumnIndex("msg_box");
+                val = cursor.getInt(msgBoxInd);
+            }
+            cursor.close();
+        }
+        return val;
+    }
+
+    /**
+     * Obtain MMS message text
+     */
+    private String getMmsMsgTxt(long msgID) {
+        String text = null;
+        String whereClause = " mid= " + msgID + " AND ct=\"text/plain\"";
+        Uri uri = Uri.parse("content://mms/part");
+        ContentResolver cr = mContext.getContentResolver();
+        Cursor cursor = cr.query(uri, null, whereClause, null, null);
+        if (cursor != null) {
+            if (cursor.getCount() > 0) {
+                cursor.moveToFirst();
+                int textInd = cursor.getColumnIndex("text");
+                text = cursor.getString(textInd);
+            }
+            cursor.close();
+        }
+        return text;
+    }
+
+    /**
+     * Obtain the MMS message Subject
+     */
+    private String getMmsMsgSubject(long msgID) {
+        String text = null;
+        String whereClause = " _id= " + msgID;
+        Uri uri = Uri.parse("content://mms/");
+        ContentResolver cr = mContext.getContentResolver();
+        Cursor cursor = cr.query(uri, null, whereClause, null, null);
+        if (cursor != null) {
+            if (cursor.getCount() > 0) {
+                cursor.moveToFirst();
+                int subjectInd = cursor.getColumnIndex("sub");
+                text = cursor.getString(subjectInd);
+            }
+            cursor.close();
+        }
+        return text;
+    }
+
+    /**
+     * Obtain the MMS message Date
+     */
+    private Date getMmsMsgDate(long msgID) {
+        long  date = 0;
+        String whereClause = " _id= " + msgID;
+        Uri uri = Uri.parse("content://mms/");
+        ContentResolver cr = mContext.getContentResolver();
+        Cursor cursor = cr.query(uri, null, whereClause, null, null);
+        if (cursor != null) {
+            if (cursor.moveToFirst()) {
+                int dateInd = cursor.getColumnIndex("date");
+                date = 1000 * cursor.getLong(dateInd);
+            }
+            cursor.close();
+        }
+        if (V) Log.v(TAG, "DATE:millisces "+ date);
+        return new Date(date);
+
+    }
+
+    /**
+     * Obtain the MMS attachment size
+     */
+    private int getMmsMsgAttachSize(long msgID) {
+        int attachSize = 0;
+        String whereClause = " _id= " + msgID;
+        Uri uri = Uri.parse("content://mms/");
+        ContentResolver cr = mContext.getContentResolver();
+        Cursor cursor = cr.query(uri, null, whereClause, null, null);
+        if (cursor != null) {
+            if (cursor.getCount() > 0) {
+                cursor.moveToFirst();
+                int sizeInd = cursor.getColumnIndex("m_size");
+                attachSize = cursor.getInt(sizeInd);
+            }
+            cursor.close();
+        }
+        return attachSize;
+
+    }
+
+    /**
+     * Obtain the MMS message read status
+     */
+    private String getMmsMsgReadStatus(long msgID) {
+        String text = null;
+        String whereClause = " _id= " + msgID;
+        Uri uri = Uri.parse("content://mms/");
+        ContentResolver cr = mContext.getContentResolver();
+        Cursor cursor = cr.query(uri, null, whereClause, null, null);
+        if (cursor != null) {
+            if (cursor.getCount() > 0) {
+                cursor.moveToFirst();
+                int readInd = cursor.getColumnIndex("read");
+                if (cursor.getInt(readInd) == 0) {
+                    text = "no";
+                } else {
+                    text = "yes";
+                }
+            }
+            cursor.close();
+        }
+        return text;
+    }
+
+    /**
+     * Obtain the MMS message read sent
+     */
+    private String getMmsMsgReadSent(long msgID) {
+        String text = null;
+        if ( getMmsMsgBox(msgID) == 2 ) {
+            // Sent folder
+            text = "yes";
+        } else {
+            text = "no";
+        }
+        return text;
+    }
+
+    /**
+     * Obtain the MMS message priority
+     */
+    private String getMmsMsgPriority(long msgID) {
+        final int PRIORITY_LOW = 0X80;
+        final int PRIORITY_NORMAL = 0X81;
+        final int PRIORITY_HIGH = 0X82;
+
+        String text = null;
+        String whereClause = " _id= " + msgID;
+        Uri uri = Uri.parse("content://mms/");
+        ContentResolver cr = mContext.getContentResolver();
+        Cursor cursor = cr.query(uri, null, whereClause, null, null);
+        if (cursor != null && cursor.getCount() > 0) {
+            cursor.moveToFirst();
+            int priInd = cursor.getColumnIndex("pri");
+
+            switch (cursor.getInt(priInd)) {
+            case PRIORITY_LOW:
+                text = "no";
+                break;
+            case PRIORITY_NORMAL:
+                text = "no";
+                break;
+            case PRIORITY_HIGH:
+                text = "yes";
+                break;
+
+            default:
+                text = "no";
+                break;
+            }
+        }
+        if (cursor != null) {
+            cursor.close();
+        }
+        return text;
+    }
+
+    /**
+     * Obtain the MMS message read protected
+     */
+    private String getMmsMsgProtected(long msgID) {
+        String text = null;
+        String whereClause = " _id= " + msgID;
+        Uri uri = Uri.parse("content://mms/");
+        ContentResolver cr = mContext.getContentResolver();
+        Cursor cursor = cr.query(uri, null, whereClause, null, null);
+        if (cursor != null) {
+            if (cursor.getCount() > 0) {
+                cursor.moveToFirst();
+                int readInd = cursor.getColumnIndex("locked");
+                if (cursor.getInt(readInd) == 0) {
+                    text = "no";
+                } else {
+                    text = "yes";
+                }
+            }
+            cursor.close();
+        }
+        return text;
+
+    }
+
+    /**
+     * Obtain MMS message address
+     * When Multiple addresses are present return the addresses separated by semicolon
+     */
+    private String getMmsMsgAddress(long msgID) {
+        String text = "";
+        String whereClause = " address != \"insert-address-token\"";
+        Uri uri = Uri.parse("content://mms/" + msgID + "/addr");
+        //FOR INCOMING MMS ONLY "FROM" ADDRESS IS REQUIRED
+        if (isOutgoingMMSMessage(msgID) == false)
+            whereClause = " address != \"insert-address-token\" AND type=137";
+        ContentResolver cr = mContext.getContentResolver();
+        Cursor cursor = cr.query(uri, null, whereClause, null, null);
+        if (cursor != null) {
+            if (V) Log.v(TAG, "cursor.getCount(): " + cursor.getCount());
+            if (cursor.getCount() == 1) {
+                cursor.moveToFirst();
+                int addressInd = cursor.getColumnIndex("address");
+                text = cursor.getString(addressInd);
+            }
+            else if (cursor.getCount() > 1) {
+                cursor.moveToFirst();
+                int addressInd = 0;
+                do {
+                    addressInd = cursor.getColumnIndex("address");
+                    text = text.concat(cursor.getString(addressInd) + ";");
+                    if (V) Log.v(TAG, "address: " + cursor.getString(cursor.getColumnIndex("address")));
+                } while (cursor.moveToNext());
+
+            }
+            cursor.close();
+        }
+        if (V) Log.v(TAG, "final MMS address: " + text);
+        return text;
+    }
+
+    /**
+     * Get the folder name (MAP representation) based on the message Handle
+     */
+    private int getMmsContainingFolder(long msgID) {
+        int folderNum = 0;
+        String whereClause = " _id= " + msgID;
+        Uri uri = Uri.parse("content://mms/");
+        ContentResolver cr = mContext.getContentResolver();
+        Cursor cursor = cr.query(uri, null, whereClause, null, null);
+        if (cursor != null) {
+            if (cursor.getCount() > 0) {
+                cursor.moveToFirst();
+                int msgboxInd = cursor.getColumnIndex("msg_box");
+                String thread_id = cursor.getString(cursor.getColumnIndex("thread_id"));
+                if ( Integer.valueOf(thread_id) == DELETED_THREAD_ID) {
+                    // Deleted folder
+                    folderNum = 0;
+                } else {
+                    folderNum = cursor.getInt(msgboxInd);
+                }
+            }
+            cursor.close();
+        }
+        return folderNum;
+    }
+
+    /**
+     * Get MMS folder name based on value Inbox = 1 Sent = 2 Drafts = 3 Outbox =
+     * 4 Queued = 6
+     *
+     */
+    private String getMmsMapVirtualFolderName(int type) {
+        String folderName = null;
+
+        switch (type) {
+        case 0:
+            folderName = DELETED;
+            break;
+        case 1:
+            folderName = INBOX;
+            break;
+        case 2:
+            folderName = SENT;
+            break;
+        case 3:
+            folderName = DRAFT;
+            break;
+        case 4: // outbox
+        case 5: // failed
+        case 6: // queued
+            folderName = OUTBOX;
+            break;
+
+        default:
+            break;
+        }
+        return folderName;
+    }
+
+    /**
+     * Build an MMS bMessage when given a message handle
+     */
+    private BluetoothMasMessageRsp bldMmsBmsg(long msgID, BluetoothMasMessageRsp rsp) {
+        Cursor cr = null;
+        Uri uri = Uri.parse("content://mms/");
+        String whereClause = " _id = " + msgID;
+        String address = null;
+        String addressTokens[] = null;
+        cr = mContext.getContentResolver().query(uri, null, whereClause, null,
+                null);
+        if (cr == null) {
+            rsp.rsp = ResponseCodes.OBEX_HTTP_NOT_ACCEPTABLE;
+            return rsp;
+        }
+        if (cr.getCount() > 0) {
+            cr.moveToFirst();
+            String containingFolder = getMmsMapVirtualFolderName((getMmsContainingFolder(msgID)));
+            BmessageConsts bmsg = new BmessageConsts();
+
+            // Create a bMessage
+            bmsg.setType("MMS");
+
+            bmsg.setBmsg_version("1.0");
+            if (cr.getString(cr.getColumnIndex("read")).equalsIgnoreCase("1")) {
+                bmsg.setStatus("READ");
+            } else {
+                bmsg.setStatus("UNREAD");
+            }
+
+            bmsg.setFolder(TELECOM + "/" + MSG + "/" + containingFolder);
+
+            bmsg.setVcard_version("2.1");
+            address = getMmsMsgAddress(msgID);
+            if ((address != null) && address.contains(";")) {
+                addressTokens = address.split(";");
+                String name = "";
+                String tel = "";
+                VcardContent vcardInternal;
+                for (int i=0; i < addressTokens.length; i++) {
+                    vcardInternal = getVcardContent(addressTokens[i]);
+                    if ((vcardInternal.name == null) || (vcardInternal.name.length() == 0)) {
+                        name = name.concat(" " + ";");
+                        if(V) Log.v (TAG, "name not present: ");
+                    } else {
+                        if(V) Log.v (TAG, "name present");
+                        name = name.concat(vcardInternal.name + ";");
+                    }
+                    tel = tel.concat(vcardInternal.tel + ";");
+                }
+                String type = cr.getString(cr.getColumnIndex("msg_box"));
+                // Inbox is type 1.
+                if (type.equalsIgnoreCase("1")) {
+                    bmsg.setOriginatorVcard_name(name);
+                    bmsg.setOriginatorVcard_phone_number(tel);
+                    bmsg.setRecipientVcard_name(getOwnerName());
+                    bmsg.setRecipientVcard_phone_number(getOwnerNumber());
+                } else {
+                    bmsg.setRecipientVcard_name(name);
+                    bmsg.setRecipientVcard_phone_number(tel);
+                    bmsg.setOriginatorVcard_name(getOwnerName());
+                    bmsg.setOriginatorVcard_phone_number(getOwnerNumber());
+                }
+            }
+            else {
+                VcardContent vcard = getVcardContent(address);
+                String type = cr.getString(cr.getColumnIndex("msg_box"));
+                // Inbox is type 1.
+                if (type.equalsIgnoreCase("1")) {
+                    bmsg.setOriginatorVcard_name(vcard.name);
+                    bmsg.setOriginatorVcard_phone_number(vcard.tel);
+                    bmsg.setRecipientVcard_name(getOwnerName());
+                    bmsg.setRecipientVcard_phone_number(getOwnerNumber());
+                } else {
+                    bmsg.setRecipientVcard_name(vcard.name);
+                    bmsg.setRecipientVcard_phone_number(vcard.tel);
+                    bmsg.setOriginatorVcard_name(getOwnerName());
+                    bmsg.setOriginatorVcard_phone_number(getOwnerNumber());
+                }
+            }
+            StringBuilder sb = new StringBuilder();
+            sb.append("Date: ").append(getMmsMsgDate(msgID).toString()).append("\r\n");
+
+            boolean MIME = true;
+            boolean msgFormat = MIME;
+            sb.append(bldMMSBody(bmsg, msgFormat, msgID));
+            bmsg.setBody_msg(sb.toString());
+            bmsg.setBody_length(sb.length() + 22);
+            bmsg.setBody_encoding("8BIT");
+            // Send a bMessage
+            String str = MapUtils.toBmessageMMS(bmsg);
+            if (V) Log.v(TAG, str);
+            if (str != null && (str.length() > 0)) {
+                final String FILENAME = "message" + getMasId();
+                FileOutputStream bos = null;
+                File file = new File(mContext.getFilesDir() + "/" + FILENAME);
+                file.delete();
+
+                try {
+                    bos = mContext.openFileOutput(FILENAME, Context.MODE_PRIVATE);
+                    bos.write(str.getBytes());
+                    bos.flush();
+                    bos.close();
+                } catch (FileNotFoundException e) {
+                    Log.e(TAG, "Unable to write " + FILENAME, e);
+                } catch (IOException e) {
+                    Log.e(TAG, "Unable to write " + FILENAME, e);
+                }
+
+                File fileR = new File(mContext.getFilesDir() + "/" + FILENAME);
+                if (fileR.exists() == true) {
+                    rsp.file = fileR;
+                    rsp.fractionDeliver = 1;
+                }
+            }
+        }
+        cr.close();
+        return rsp;
+    }
+
+    private boolean isOutgoingMMSMessage(long mmsMsgID) {
+        if (getMmsMsgBox(mmsMsgID) == 1) {
+            return false;
+        }
+        return true;
+    }
+
+    /**
+     * This method constructs an MMS message that is added to the message list
+     * which is used to construct a message listing
+     */
+    private MsgListingConsts bldMmsMsgLstItem(long mmsMsgID, BluetoothMasAppParams appParams,
+            String folderName, String datetimeStr) {
+
+        MsgListingConsts ml = new MsgListingConsts();
+        String address = getMmsMsgAddress(mmsMsgID);
+        boolean isMultipleAddress = false;
+        String addressTokens[] = null;
+        if ((address != null) && address.contains(";")) {
+            isMultipleAddress = true;
+            addressTokens = address.split(";");
+            for (int i=0; i < addressTokens.length; i++) {
+                if (V) Log.v ( TAG, "addressTokens[" + i + "] = " + addressTokens[i]);
+            }
+            if (V) Log.v (TAG, "addressTokens.length: " + addressTokens.length);
+        }
+        if (V) Log.v (TAG, "isMultipleAddress: " + isMultipleAddress);
+
+
+        // Set the message handle
+        ml.setMsg_handle(mmsMsgID + MMS_OFFSET_START);
+
+        // Set the message subject
+        if ((appParams.ParameterMask & BIT_SUBJECT) != 0) {
+            ml.setSubject(getMmsMsgSubject(mmsMsgID));
+            ml.sendSubject = true;
+        }
+
+        // Construct datetime value
+        if ((appParams.ParameterMask & BIT_DATETIME) != 0) {
+            ml.setDatetime(datetimeStr);
+        }
+
+        // Construct msg body
+        if ((appParams.ParameterMask & BIT_TEXT) != 0) {
+            if ((getMmsMsgTxt(mmsMsgID) != null)) {
+                ml.setContains_text("yes");
+            } else {
+                ml.setContains_text("no");
+            }
+
+        }
+
+        // Set text size
+        if ((appParams.ParameterMask & BIT_SIZE) != 0) {
+            final String mmsMsgTxt = getMmsMsgTxt(mmsMsgID);
+            ml.setSize(mmsMsgTxt == null ? 0 : mmsMsgTxt.length());
+        }
+
+        // Set message type
+        if ((appParams.ParameterMask & BIT_TYPE) != 0) {
+            ml.setType("MMS");
+        }
+
+        if ((appParams.ParameterMask & BIT_RECIPIENT_NAME) != 0) {
+            String recipientName = "";
+            if (isOutgoingMMSMessage(mmsMsgID) == false) {
+                recipientName = getOwnerName();
+            } else {
+                if(!isMultipleAddress) {
+                    recipientName = getContactName(getMmsMsgAddress(mmsMsgID));
+                } else {
+                    for (int i=0; i < addressTokens.length; i++) {
+                        recipientName = recipientName.concat(getContactName(addressTokens[i]) + ";");
+                        if (V) Log.v ( TAG, "recipientName: " + recipientName);
+                    }
+                }
+            }
+            ml.setRecepient_name(recipientName);
+        }
+
+        if ((appParams.ParameterMask & BIT_RECIPIENT_ADDRESSING) != 0) {
+            // TODO: In case of a SMS this is the recipient's phone number
+            // in canonical form (chapter 2.4.1 of [5])
+            String recipientAddressing = null;
+            if (isOutgoingMMSMessage(mmsMsgID) == false) {
+                recipientAddressing = getOwnerNumber();
+            } else {
+                recipientAddressing = getMmsMsgAddress(mmsMsgID);
+            }
+            ml.setRecepient_addressing(recipientAddressing);
+            ml.setSendRecipient_addressing(true);
+        }
+
+        if ((appParams.ParameterMask & BIT_SENDER_NAME) != 0) {
+            String senderName = "";
+            if (isOutgoingMMSMessage(mmsMsgID) == true) {
+                senderName = getOwnerName();
+            } else {
+                if(!isMultipleAddress) {
+                    senderName = getContactName(getMmsMsgAddress(mmsMsgID));
+                } else {
+                    for (int i=0; i < addressTokens.length; i++) {
+                        senderName = senderName.concat(getContactName(addressTokens[i]) + ";");
+                        if (V) Log.v ( TAG, "senderName: " + senderName);
+                    }
+                }
+            }
+            ml.setSender_name(senderName);
+        }
+
+        if ((appParams.ParameterMask & BIT_SENDER_ADDRESSING) != 0) {
+            String senderAddressing = null;
+            if (isOutgoingMMSMessage(mmsMsgID) == true) {
+                senderAddressing = getOwnerNumber();
+            } else {
+                senderAddressing = getMmsMsgAddress(mmsMsgID);
+            }
+            ml.setSender_addressing(senderAddressing);
+        }
+
+        // Set read status
+        if ((appParams.ParameterMask & BIT_READ) != 0) {
+            final String mmsMsgStatus = getMmsMsgReadStatus(mmsMsgID);
+            if (mmsMsgStatus != null && mmsMsgStatus.equalsIgnoreCase("yes")) {
+                ml.setRead("yes");
+            } else {
+                ml.setRead("no");
+            }
+        }
+
+        // Set priority
+        if ((appParams.ParameterMask & BIT_PRIORITY) != 0) {
+            ml.setPriority(getMmsMsgPriority(mmsMsgID));
+        }
+
+        // Set Protected
+        if ((appParams.ParameterMask & BIT_PROTECTED) != 0) {
+            ml.setMsg_protected(getMmsMsgProtected(mmsMsgID));
+        }
+
+        // Set sent
+        if ((appParams.ParameterMask & BIT_SENT) != 0) {
+            ml.setSent(getMmsMsgReadSent(mmsMsgID));
+        }
+
+        // Set reception status
+        if ((appParams.ParameterMask & BIT_RECEPTION_STATUS) != 0) {
+            ml.setReception_status("complete");
+        }
+
+        // Set attachment size
+        if ((appParams.ParameterMask & BIT_ATTACHMENT_SIZE) != 0) {
+            ml.setAttachment_size(getMmsMsgAttachSize(mmsMsgID));
+        }
+
+        return ml;
+    }
+
+    /**
+     * This method is used to take an MMS in the drafts folder and move it to
+     * the outbox This action is required to add the MMS to the pending_msgs
+     * table which is used to send the MMS out to the network
+     */
+    private void moveMMSfromDraftstoOutbox() {
+
+        String handle = null;
+
+        // scan drafts folder for an MMS to send
+        // fetch the message handle
+        Uri uri = Uri.parse("content://mms/drafts");
+        ContentResolver cr = mContext.getContentResolver();
+        Cursor crID = cr.query(uri, null, null, null, null);
+        if (crID != null) {
+            if (crID.getCount() > 0) {
+                crID.moveToFirst();
+                int msgIDInd = crID.getColumnIndex("_id");
+                handle = crID.getString(msgIDInd);
+            }
+            crID.close();
+        }
+
+        if (handle != null) {
+            String whereClause = " _id= " + handle;
+            uri = Uri.parse("content://mms");
+            crID = cr.query(uri, null, whereClause, null, null);
+            if (crID != null) {
+                if (crID.getCount() > 0) {
+                    crID.moveToFirst();
+                    ContentValues values = new ContentValues();
+                    values.put("msg_box", 4);
+                    cr.update(uri, values, whereClause, null);
+                }
+                crID.close();
+            }
+        }
+    }
+
+    /**
+     * This method is used to take a Bmessage that was pushed and move it to the
+     * folder
+     */
+    private String addToMmsFolder(String folderName, String mmsMsg) throws BadRequestException {
+        if (folderName == null) {
+            return null;
+        }
+        if (folderName.equalsIgnoreCase(DRAFT)) {
+            folderName = DRAFTS;
+        }
+        int folderType = SmsMmsUtils.getFolderTypeMms(folderName);
+        BmessageConsts bMsg = MapUtils.fromBmessageMMS(mmsMsg);
+        String address = bMsg.getRecipientVcard_phone_number();
+        String mmsText = bMsg.getBody_msg();
+
+        /**
+         * The PTS tester does not contain the same message format as CE4A This
+         * code /* looks at the pushed message and checks for the message boundary. If
+         * it does not /* find it then it then it assumes PTS tester format
+         */
+        mmsText = MapUtils.fetchBodyEmail(mmsText);
+        ContentValues values = new ContentValues();
+        values.put("msg_box", folderType);
+
+        if (folderName.equalsIgnoreCase(DELETED)) {
+            values.put("thread_id", -1);
+        } else {
+            values.put("thread_id", createMMSThread(address));
+        }
+
+        // function that creates a thread ID
+        values.put("read", 0);
+        values.put("seen", 0);
+        values.put("sub_cs", 106);
+        values.put("ct_t", "application/vnd.wap.multipart.related");
+        values.put("exp", 604800);
+        values.put("m_cls", "personal");
+        values.put("m_type", 128);
+        values.put("v", 18);
+        values.put("pri", 129);
+        values.put("rr", 129);
+        values.put("tr_id", "T12dc2e87182");
+        values.put("d_rpt", 129);
+        values.put("locked", 0);
+
+        Uri uri;
+        if (folderName.equalsIgnoreCase(DELETED)) {
+            uri = Uri.parse("content://mms/inbox");
+        } else {
+            uri = Uri.parse("content://mms/" + folderName);
+        }
+        ContentResolver cr = mContext.getContentResolver();
+        uri = cr.insert(uri, values);
+
+        if (uri == null) {
+            // unable to insert MMS
+            Log.e(TAG, "Unabled to insert MMS " + values);
+            return INTERNAL_ERROR;
+        }
+        String msgNum = uri.getLastPathSegment();
+        long msgID = Long.parseLong(msgNum);
+        if (V){
+            Log.v(TAG, " NEW URI " + uri.toString());
+        }
+        long virtualMsgId = (msgID + MMS_OFFSET_START);
+
+        // Build the \mms\part portion
+        values.clear();
+
+        values.put("seq", -1);
+        values.put("ct", "application/smil");
+        values.put("cid", "<smil>");
+        values.put("cl", "smil.xml");
+        values.put("text", "<smil><head><layout><root-layout width=\"320px\" height=\"480px\"/>" +
+                "<region id=\"Text\" left=\"0\" top=\"320\" width=\"320px\" height=\"160px\"" +
+                " fit=\"meet\"/></layout></head><body><par dur=\"5000ms\">" +
+                "<text src=\"text_0.txt\" region=\"Text\"/></par></body></smil>");
+
+        uri = Uri.parse("content://mms/" + msgID + "/part");
+        uri = cr.insert(uri, values);
+        if (uri != null && V){
+            Log.v(TAG, " NEW URI " + uri.toString());
+        }
+
+        values.clear();
+        values.put("seq", 0);
+        values.put("ct", "text/plain");
+        values.put("name", "null");
+        values.put("chset", 106);
+        values.put("cd", "null");
+        values.put("fn", "null");
+        values.put("cid", "<smil>");
+        values.put("cl", "text_0.txt");
+        values.put("ctt_s", "null");
+        values.put("ctt_t", "null");
+        values.put("_data", "null");
+        values.put("text", mmsText);
+
+        uri = Uri.parse("content://mms/" + msgID + "/part");
+        uri = cr.insert(uri, values);
+        if (uri != null && V){
+            Log.v(TAG, " NEW URI " + uri.toString());
+        }
+
+        values.clear();
+        values.put("contact_id", "null");
+        values.put("address", "insert-address-token");
+        values.put("type", 137);
+        values.put("charset", 106);
+
+        uri = Uri.parse("content://mms/" + msgID + "/addr");
+        uri = cr.insert(uri, values);
+        if (uri != null && V){
+            Log.v(TAG, " NEW URI " + uri.toString());
+        }
+
+        values.clear();
+        values.put("contact_id", "null");
+        values.put("address", address);
+        values.put("type", 151);
+        values.put("charset", 106);
+
+        uri = Uri.parse("content://mms/" + msgID + "/addr");
+        uri = cr.insert(uri, values);
+        if (uri != null && V){
+            Log.v(TAG, " NEW URI " + uri.toString());
+        }
+
+        String virtualMsgIdStr = String.valueOf(virtualMsgId);
+        String whereClause = "address LIKE '" + address + "' AND type = 125";
+        if (!folderName.equalsIgnoreCase("deleted")) {
+            mContext.getContentResolver().delete(Uri.parse("content://sms/"), whereClause, null);
+        }
+        return virtualMsgIdStr;
+    }
+
+    /**
+     * Method to construct body of bmessage using either MIME or no MIME
+     *
+     */
+    private String bldMMSBody(BmessageConsts bMsg, boolean msgType, long msgID) {
+        boolean MIME = true;
+        StringBuilder sb = new StringBuilder();
+
+        if (msgType == MIME) {
+            Random randomGenerator = new Random();
+            int randomInt = randomGenerator.nextInt(1000);
+            String boundary = "MessageBoundary."+randomInt;
+            final String mmsMsgTxt = getMmsMsgTxt(msgID);
+            if(mmsMsgTxt != null){
+                while(mmsMsgTxt.contains(boundary)){
+                    randomInt = randomGenerator.nextInt(1000);
+                    boundary = "MessageBoundary."+randomInt;
+                }
+            }
+            sb.append("To:").append(bMsg.recipient_vcard_phone_number)
+                    .append("\r\n");
+            sb.append("Mime-Version: 1.0").append("\r\n");
+            sb.append(
+                    "Content-Type: multipart/mixed; boundary=\""+boundary+"\"")
+                    .append("\r\n");
+            sb.append("Content-Transfer-Encoding: 7bit").append("\r\n")
+                    .append("\r\n");
+            sb.append("MIME Message").append("\r\n");
+            sb.append("--"+boundary).append("\r\n");
+            sb.append("Content-Type: text/plain").append("\r\n");
+            sb.append("Content-Transfer-Encoding: 8bit").append("\r\n");
+            sb.append("Content-Disposition:inline").append("\r\n")
+                    .append("\r\n");
+            sb.append(getMmsMsgTxt(msgID)).append("\r\n");
+            sb.append("--"+boundary+"--").append("\r\n")
+                    .append("\r\n");
+        } else {
+            sb.append("Subject:").append("Not Implemented").append("\r\n");
+            sb.append("From:").append(bMsg.originator_vcard_phone_number)
+                    .append("\r\n");
+            sb.append(getMmsMsgTxt(msgID)).append("\r\n").append("\r\n");
+        }
+        return sb.toString();
+    }
+
+    /**
+     * Method to create a thread for a pushed MMS message
+     *
+     */
+    private int createMMSThread(String address) {
+        int returnValue = 0;
+        if (address != null) {
+            if (V){
+                Log.v(TAG, "Inside adress not null");
+            }
+            ContentValues tempValue = new ContentValues();
+            tempValue.put("address", address);
+            tempValue.put("type", 125);
+            Uri tempUri = mContext.getContentResolver().insert(
+                    Uri.parse("content://sms/"), tempValue);
+
+            if (tempUri != null) {
+                Cursor tempCr = mContext.getContentResolver().query(tempUri, null,
+                        null, null, null);
+                if (tempCr != null) {
+                    tempCr.moveToFirst();
+                    String newThreadIdStr = tempCr.getString(tempCr
+                            .getColumnIndex("thread_id"));
+                    tempCr.close();
+                    returnValue = Integer.valueOf(newThreadIdStr);
+                }
+                if (V){
+                    Log.v(TAG, "Thread ID::"+returnValue);
+                }
+            }
+        }
+
+        return returnValue;
+    }
+
+    private String bldSmsBmsg(long msgHandle, Context context, Cursor cr,
+                BluetoothMasAppParams bluetoothMasAppParams) {
+        String str = null;
+        int body_length = 0;
+        if (cr.getCount() > 0) {
+            cr.moveToFirst();
+            String containingFolder = getContainingFolder(msgHandle);
+            BmessageConsts bmsg = new BmessageConsts();
+
+            // Create a bMessage
+
+            if (TelephonyManager.getDefault().getPhoneType() == TelephonyManager.PHONE_TYPE_CDMA) {
+                bmsg.setType("SMS_CDMA");
+            } else {
+                bmsg.setType("SMS_GSM");
+            }
+
+            bmsg.setBmsg_version("1.0");
+            if (cr.getString(cr.getColumnIndex("read")).equalsIgnoreCase("1")) {
+                bmsg.setStatus("READ");
+            } else {
+                bmsg.setStatus("UNREAD");
+            }
+
+            bmsg.setFolder(TELECOM + "/" + MSG + "/" + containingFolder);
+
+            bmsg.setVcard_version("2.1");
+            VcardContent vcard = getVcardContent(cr.getString(cr
+                    .getColumnIndex("address")));
+
+            String type = cr.getString(cr.getColumnIndex("type"));
+            if (type.equalsIgnoreCase("1")) {
+                // The address in database is of originator
+                bmsg.setOriginatorVcard_name(vcard.name);
+                bmsg.setOriginatorVcard_phone_number(vcard.tel);
+                bmsg.setRecipientVcard_name(getOwnerName());
+                bmsg.setRecipientVcard_phone_number(getOwnerNumber());
+            } else {
+                bmsg.setRecipientVcard_name(vcard.name);
+                bmsg.setRecipientVcard_phone_number(vcard.tel);
+                bmsg.setOriginatorVcard_name(getOwnerName());
+                bmsg.setOriginatorVcard_phone_number(getOwnerNumber());
+            }
+
+            String smsBody = " ";
+
+            if ( (int)bluetoothMasAppParams.Charset == 1){
+                bmsg.setBody_charset("UTF-8");
+                smsBody = cr.getString(cr.getColumnIndex("body"));
+            }
+
+            if ( (int)bluetoothMasAppParams.Charset == 0){
+                bmsg.setBody_encoding("G-7BIT");
+                String smsBodyUnicode = cr.getString(cr.getColumnIndex("body"));
+                smsBody = getSMSDeliverPdu(smsBodyUnicode, cr.getString(cr.getColumnIndex("date")),
+                        vcard.tel);
+            }
+            if (V) {
+              Log.v(TAG, "Unicode String Length :: " + smsBody.length());
+            }
+            try {
+               switch ((int)bluetoothMasAppParams.Charset) {
+               case 1:
+                   byte[] b = smsBody.getBytes("UTF-8");
+                   body_length = b.length;
+                   break;
+               case 0:
+               default:
+                   body_length = smsBody.length();
+                   break;
+               }
+            }catch (Exception e) {
+               if (V) {
+                   Log.v(TAG, "Exception::Getting String UTF-8 Length:: " + smsBody.length());
+               }
+               body_length = smsBody.length();
+            }
+            if (V) {
+               Log.v(TAG, "BT SMS Total length :: 22 + " + body_length);
+            }
+            bmsg.setBody_length(22 + body_length);
+            bmsg.setBody_msg(smsBody);
+            cr.close();
+            // Send a bMessage
+           if (V){
+               Log.v(TAG, "bMessageSMS test\n");
+               Log.v(TAG, "=======================\n\n");
+            }
+            str = MapUtils.toBmessageSMS(bmsg);
+        }
+        return str;
+    }
+
+    private MsgListingConsts bldSmsMsgLstItem(BluetoothMasAppParams appParams,
+                String subject, String timestamp, String address, String msgId,
+                String readStatus, int msgType) {
+        MsgListingConsts ml = new MsgListingConsts();
+        ml.setMsg_handle(Integer.valueOf(msgId));
+
+        Time time = new Time();
+        time.set(Long.valueOf(timestamp));
+
+        String datetimeStr = time.toString().substring(0, 15);
+
+        ml.msgInfo.setDateTime(datetimeStr);
+
+        if ((appParams.ParameterMask & BIT_SUBJECT) != 0) {
+            /* SMS doesn't have subject. Append Body
+             * so that remote client doesn't have to do
+             * GetMessage
+             */
+            ml.setSendSubject(true);
+            if (subject == null) {
+                subject = "";
+            } else if (subject != null && subject.length() > appParams.SubjectLength ) {
+                subject = subject.substring(0,
+                        appParams.SubjectLength);
+            }
+            ml.setSubject(subject);
+        }
+
+        if ((appParams.ParameterMask & BIT_DATETIME) != 0) {
+            ml.setDatetime(datetimeStr);
+        }
+
+        if ((appParams.ParameterMask & BIT_SENDER_NAME) != 0) {
+            // TODO: Query the Contacts database
+            String senderName = null;
+            if (isOutgoingSMSMessage(msgType) == true) {
+                senderName = getOwnerName();
+            } else {
+                senderName = getContactName(address);
+            }
+            ml.setSender_name(senderName);
+        }
+
+        if ((appParams.ParameterMask & BIT_SENDER_ADDRESSING) != 0) {
+            // TODO: In case of a SMS this is the sender's phone number in canonical form
+            // (chapter 2.4.1 of [5]).
+            String senderAddressing = null;
+            if (isOutgoingSMSMessage(msgType) == true) {
+                senderAddressing = getOwnerNumber();
+            } else {
+                senderAddressing = address;
+            }
+            ml.setSender_addressing(senderAddressing);
+        }
+
+        if ((appParams.ParameterMask & BIT_RECIPIENT_NAME) != 0) {
+            // TODO: "recipient_name" is the name of the recipient of
+            // the message, when it is known by the MSE device.
+            String recipientName = null;
+            if (isOutgoingSMSMessage(msgType) == false) {
+                recipientName = getOwnerName();
+            } else {
+                recipientName = getContactName(address);
+            }
+            ml.setRecepient_name(recipientName);
+        }
+
+        if ((appParams.ParameterMask & BIT_RECIPIENT_ADDRESSING) != 0) {
+            // TODO: In case of a SMS this is the recipient's phone
+            // number in canonical form (chapter 2.4.1 of [5])
+            String recipientAddressing = null;
+            if (isOutgoingSMSMessage(msgType) == false) {
+                recipientAddressing = getOwnerNumber();
+            } else {
+                recipientAddressing = address;
+            }
+            ml.setRecepient_addressing(recipientAddressing);
+            ml.setSendRecipient_addressing(true);
+        }
+
+        if ((appParams.ParameterMask & BIT_TYPE) != 0) {
+            final int phoneType = TelephonyManager.getDefault().getPhoneType();
+            if (phoneType == TelephonyManager.PHONE_TYPE_CDMA) {
+                ml.setType("SMS_CDMA");
+            } else {
+                ml.setType("SMS_GSM");
+            }
+        }
+
+        if ((appParams.ParameterMask & BIT_SIZE) != 0) {
+            ml.setSize(subject.length());
+        }
+
+        if ((appParams.ParameterMask & BIT_RECEPTION_STATUS) != 0) {
+            ml.setReception_status("complete");
+        }
+
+        if ((appParams.ParameterMask & BIT_TEXT) != 0) {
+            // TODO: Set text to "yes"
+            ml.setContains_text("yes");
+        }
+
+        if ((appParams.ParameterMask & BIT_ATTACHMENT_SIZE) != 0) {
+            ml.setAttachment_size(0);
+        }
+
+        if ((appParams.ParameterMask & BIT_PRIORITY) != 0) {
+            // TODO: Get correct priority
+            ml.setPriority("no");
+        }
+
+        if ((appParams.ParameterMask & BIT_READ) != 0) {
+            if (readStatus.equalsIgnoreCase("1")) {
+                ml.setRead("yes");
+            } else {
+                ml.setRead("no");
+            }
+        }
+
+        if ((appParams.ParameterMask & BIT_SENT) != 0) {
+            // TODO: Get sent status?
+            if (msgType == 2) {
+                ml.setSent("yes");
+            } else {
+                ml.setSent("no");
+            }
+        }
+
+        if ((appParams.ParameterMask & BIT_PROTECTED) != 0) {
+            ml.setMsg_protected("no");
+        }
+
+        return ml;
+    }
+    private BluetoothMsgListRsp msgListSms(List<MsgListingConsts> msgList, String folder,
+            BluetoothMasMessageListingRsp rsp, BluetoothMasAppParams appParams) {
+        BluetoothMsgListRsp bmlr = new BluetoothMsgListRsp();
+        String url = "content://sms/";
+        Uri uri = Uri.parse(url);
+        ContentResolver cr = mContext.getContentResolver();
+        String whereClause  = SmsMmsUtils.getConditionStringSms(folder, appParams);
+
+        Cursor cursor = cr.query(uri, null, whereClause, null,
+                "date desc");
+
+        if (cursor != null && V) {
+                Log.v(TAG, "move to First" + cursor.moveToFirst());
+        }
+        if (cursor != null && V) {
+                Log.v(TAG, "move to Liststartoffset"
+                    + cursor.moveToPosition(appParams.ListStartOffset));
+        }
+        if (cursor != null && cursor.moveToFirst()) {
+            int idInd = cursor.getColumnIndex("_id");
+            int addressInd = cursor.getColumnIndex("address");
+            int personInd = cursor.getColumnIndex("person");
+            int dateInd = cursor.getColumnIndex("date");
+            int readInd = cursor.getColumnIndex("read");
+            int statusInd = cursor.getColumnIndex("status");
+            int subjectInd = cursor.getColumnIndex("subject");
+            int typeInd = cursor.getColumnIndex("type");
+            int bodyInd = cursor.getColumnIndex("body");
+
+            do {
+                /*
+                 * Apply remaining filters
+                 */
+
+                /*
+                 * For incoming message, originator is the remote
+                 * contact For outgoing message, originator is the
+                 * owner.
+                 */
+                String filterString = null;
+                String oname = getOwnerName();
+                if (oname == null) {
+                    oname = "";
+                }
+                String onumber = getOwnerNumber();
+                if (onumber == null) {
+                    onumber = "";
+                }
+
+                int msgType = cursor.getInt(typeInd);
+                String regExpOrig = null;
+                String regExpRecipient = null;
+                if (appParams.FilterOriginator != null) {
+                    regExpOrig = appParams.FilterOriginator.replace("*", ".*[0-9A-Za-z].*");
+                }
+                if (appParams.FilterRecipient != null) {
+                    regExpRecipient = appParams.FilterRecipient.replace("*", ".*[0-9A-Za-z].*");
+                }
+                if (isOutgoingSMSMessage(msgType) == true) {
+                    if ((appParams.FilterOriginator != null)
+                            && (appParams.FilterOriginator.length() != 0)
+                            && !(oname.matches(".*"+regExpOrig+".*"))
+                            && !(onumber.matches(".*"+regExpOrig+".*"))) {
+                        continue;
+                    }
+                    if ((appParams.FilterRecipient != null)
+                            && (appParams.FilterRecipient.length() != 0)) {
+                        filterString = appParams.FilterRecipient.trim();
+                        if (V){
+                                Log.v(TAG, "appParams.FilterRecipient"
+                                    + appParams.FilterRecipient);
+                        }
+                    }
+                }
+                if (isOutgoingSMSMessage(msgType) == false) {
+                    if ((appParams.FilterRecipient != null)
+                            && (appParams.FilterRecipient.length() != 0)
+                            && !(oname.matches(".*"+regExpRecipient+".*"))
+                            && !(onumber.matches(".*"+regExpRecipient+".*"))) {
+                        continue;
+                    }
+                    if ((appParams.FilterOriginator != null)
+                            && (appParams.FilterOriginator.length() != 0)) {
+                        filterString = appParams.FilterOriginator.trim();
+                        if (V){
+                                Log.v(TAG, "appParams.FilterOriginator"
+                                    + appParams.FilterOriginator);
+                        }
+                    }
+                }
+
+                if (filterString != null) {
+                    if (V){
+                        Log.v(TAG, "filterString = " + filterString);
+                    }
+                    if (allowEntry(cursor.getString(addressInd),
+                            filterString) == true) {
+                        if (V){
+                                Log.v(TAG,
+                                    " ALLOWED : "
+                                    + cursor.getString(addressInd)
+                                    + " - " + cursor.getPosition());
+                        }
+                    } else {
+                        if (V){
+                                Log.v(TAG,
+                                    " DENIED : "
+                                    + cursor.getString(addressInd)
+                                    + " - " + cursor.getPosition());
+                        }
+                        continue;
+                    }
+                }
+                if (V) Log.v(TAG, " msgListSize " + rsp.msgListingSize);
+                rsp.msgListingSize++;
+
+                /*
+                 * Don't want the listing; just send the listing size
+                 * after applying all the filters.
+                 */
+                if (appParams.MaxListCount == 0) {
+                    continue;
+                }
+                String msgIdSms = cursor.getString(idInd);
+                String subjectSms = cursor.getString(bodyInd);
+                String timestampSms = cursor.getString(dateInd);
+                String addressSms = cursor.getString(addressInd);
+                String readStatusSms = cursor.getString(readInd);
+
+                MsgListingConsts ml = bldSmsMsgLstItem(appParams, subjectSms,
+                                timestampSms, addressSms, msgIdSms,
+                                readStatusSms, msgType);
+
+                // New Message?
+                if ((rsp.newMessage == 0)
+                        && (cursor.getInt(readInd) == 0)) {
+                    rsp.newMessage = 1;
+                }
+
+                msgList.add(ml);
+            } while (cursor.moveToNext());
+        }
+        if (cursor != null) {
+            cursor.close();
+        }
+        rsp.rsp = ResponseCodes.OBEX_HTTP_OK;
+        bmlr.messageListingSize = rsp.msgListingSize;
+        bmlr.rsp = rsp;
+        bmlr.msgList = msgList;
+        return bmlr;
+    }
+
+    private BluetoothMsgListRsp msgListMms(List<MsgListingConsts> msgList, String name,
+            BluetoothMasMessageListingRsp rsp, BluetoothMasAppParams appParams) {
+        BluetoothMsgListRsp bmlr = new BluetoothMsgListRsp();
+        String filterString = null;
+
+        String oname = getOwnerName();
+        if (oname == null) {
+            oname = "";
+        }
+
+        String onumber = getOwnerNumber();
+        if (onumber == null) {
+            onumber = "";
+        }
+
+        Log.v(TAG, "oname = " + oname + "onumber = " + onumber);
+
+        String regExpOrig = null;
+        String regExpRecipient = null;
+
+        if (appParams.FilterOriginator != null) {
+            regExpOrig = appParams.FilterOriginator.replace("*", ".*[0-9A-Za-z].*");
+        }
+
+        if (appParams.FilterRecipient != null) {
+            regExpRecipient = appParams.FilterRecipient.replace("*", ".*[0-9A-Za-z].*");
+        }
+
+        Log.v(TAG, " regExpOrig = " + regExpOrig + " regExpRecipient = " + regExpRecipient);
+
+        if (getNumMmsMsgs(name) != 0) {
+            List<Integer> list = getMmsMsgMIDs(bldMmsWhereClause(
+                    appParams, SmsMmsUtils.getFolderTypeMms(name)));
+            for (int msgId : list) {
+                if (V){
+                        Log.v(TAG, "\n MMS Text message ==> "
+                            + getMmsMsgTxt(msgId));
+                }
+                if (V){
+                        Log.v(TAG, "\n MMS message subject ==> "
+                            + getMmsMsgSubject(msgId));
+                }
+                if (isOutgoingMMSMessage(msgId) == false) {
+                    if ((appParams.FilterRecipient != null)
+                        && (appParams.FilterRecipient.length() != 0)
+                        && !(oname.matches(".*"+regExpRecipient+".*"))
+                        && !(onumber.matches(".*"+regExpRecipient+".*"))) {
+                            continue;
+                        }
+                    if ((appParams.FilterOriginator != null)
+                        && (appParams.FilterOriginator.length() != 0)) {
+                        filterString = appParams.FilterOriginator.trim();
+                        if (V){
+                            Log.v(TAG, " appParams.FilterOriginator"
+                                + appParams.FilterOriginator);
+                        }
+                    }
+                }
+
+                if (isOutgoingMMSMessage(msgId) == true) {
+                    if ((appParams.FilterOriginator != null)
+                        && (appParams.FilterOriginator.length() != 0)
+                        && !(oname.matches(".*"+regExpOrig+".*"))
+                        && !(onumber.matches(".*"+regExpOrig+".*"))) {
+                        continue;
+                    }
+
+                    if ((appParams.FilterRecipient != null)
+                        && (appParams.FilterRecipient.length() != 0)) {
+                        filterString = appParams.FilterRecipient.trim();
+                        if (V){
+                            Log.v(TAG, " appParams.FilterRecipient"
+                                + appParams.FilterRecipient);
+                        }
+                    }
+                }
+
+                if (filterString != null) {
+                    if (V){
+                        Log.v(TAG, " filterString = " + filterString);
+                    }
+                    String ContactName = null;
+                    String ContactNum = null;
+
+                    ContactName = getContactName(getMmsMsgAddress(msgId));
+                    ContactNum = getMmsMsgAddress(msgId);
+
+                    if (ContactName.matches(filterString) || ContactNum.matches(filterString)) {
+                        if (V){
+                            Log.v(TAG, " ALLOWED : "
+                                + ContactName + " - " + ContactNum );
+                        }
+                    } else {
+                        if (V){
+                            Log.v(TAG, " DENIED : "
+                                + ContactName + " - " + ContactNum );
+                        }
+                        continue;
+                    }
+                }
+
+                Time time = new Time();
+                time.set(getMmsMsgDate(msgId).getTime());
+
+                String datetimeStr = time.toString().substring(0, 15);
+
+                MsgListingConsts mmsl = bldMmsMsgLstItem(msgId, appParams, name, datetimeStr);
+                mmsl.msgInfo.setDateTime(datetimeStr);
+
+                if ((rsp.newMessage == 0)
+                        && "no".equalsIgnoreCase(getMmsMsgReadStatus(msgId))) {
+                    rsp.newMessage = 1;
+                }
+
+                msgList.add(mmsl);
+                rsp.msgListingSize++;
+            }
+        }
+        rsp.rsp = ResponseCodes.OBEX_HTTP_OK;
+        bmlr.messageListingSize = rsp.msgListingSize;
+        bmlr.rsp = rsp;
+        bmlr.msgList = msgList;
+        return bmlr;
+    }
+
+    private BluetoothMasMessageRsp getMessageSms(long msgHandle, Context context,
+            BluetoothMasMessageRsp rsp, BluetoothMasAppParams bluetoothMasAppParams) {
+        long smsHandle = msgHandle - SMS_OFFSET_START;
+        Cursor cr = null;
+        Uri uri = Uri.parse("content://sms/");
+        String whereClause = " _id = " + smsHandle;
+        try {
+            cr = context.getContentResolver().query(uri, null, whereClause, null,
+                    null);
+        } catch (Exception e){
+            rsp.rsp = ResponseCodes.OBEX_HTTP_BAD_REQUEST;
+            return rsp;
+        }
+        if (cr == null) {
+            rsp.rsp = ResponseCodes.OBEX_HTTP_BAD_REQUEST;
+            return rsp;
+        }
+        String strSms = bldSmsBmsg(smsHandle, context, cr, bluetoothMasAppParams);
+        cr.close();
+        if (V) Log.v(TAG, strSms);
+
+        if (strSms != null && (strSms.length() > 0)) {
+            final String FILENAME = "message" + getMasId();
+            FileOutputStream bos = null;
+            File file = new File(context.getFilesDir() + "/" + FILENAME);
+            file.delete();
+
+            try {
+                bos = context.openFileOutput(FILENAME, Context.MODE_PRIVATE);
+                bos.write(strSms.getBytes());
+                bos.flush();
+                bos.close();
+            } catch (FileNotFoundException e) {
+                Log.e(TAG, "Unable to write " + FILENAME, e);
+            } catch (IOException e) {
+                Log.e(TAG, "Unable to write " + FILENAME, e);
+            }
+
+            File fileR = new File(context.getFilesDir() + "/" + FILENAME);
+            if (fileR.exists() == true) {
+                rsp.file = fileR;
+                rsp.fractionDeliver = 1;
+            }
+        }
+        return rsp;
+    }
+
+    private BluetoothMasMessageRsp getMessageMms(long msgHandle, BluetoothMasMessageRsp rsp) {
+        long mmsMsgID = 0;
+        try {
+            mmsMsgID = getMmsMsgHndToID(msgHandle);
+        } catch (Exception e) {
+            rsp.rsp = ResponseCodes.OBEX_HTTP_BAD_REQUEST;
+            return rsp;
+        }
+        if (mmsMsgID > 0) {
+            rsp = bldMmsBmsg(mmsMsgID, rsp);
+        }
+        return rsp;
+    }
+
+    private BluetoothMasPushMsgRsp pushMessageMms(BluetoothMasPushMsgRsp rsp,
+            String readStr, String name) throws BadRequestException {
+        String fullPath = (name == null || name.length() == 0) ? mCurrentPath : mCurrentPath + "/" + name;
+        if (fullPath.equalsIgnoreCase("telecom/msg/outbox")) {
+            String handle = addToMmsFolder(DRAFTS, readStr);
+            if (INTERNAL_ERROR == handle) {  // == comparison valid here
+                rsp.response = ResponseCodes.OBEX_HTTP_NOT_FOUND;
+                return rsp;
+            }
+            moveMMSfromDraftstoOutbox();
+            if (V) Log.v(TAG, "\nBroadcasting Intent to MmsSystemEventReceiver\n ");
+            Intent sendIntent = new Intent("android.intent.action.MMS_PUSH");
+            mContext.sendBroadcast(sendIntent);
+            rsp.msgHandle = handle;
+            rsp.response = ResponseCodes.OBEX_HTTP_OK;
+            return rsp;
+        } else {
+            String splitStrings[] = mCurrentPath.split("/");
+            mMnsClient.addMceInitiatedOperation("+");
+            int tmp = splitStrings.length;
+            String folderName;
+            if (name != null) {
+                if (name.length() == 0) {
+                    folderName = splitStrings[tmp - 1];
+                } else {
+                    folderName = name;
+                }
+            } else {
+                folderName = splitStrings[tmp - 1];
+            }
+            if (folderName != null && folderName.equalsIgnoreCase(DRAFT)){
+                String handle = addToMmsFolder(folderName, readStr);
+                if (INTERNAL_ERROR == handle) {  // == comparison valid here
+                    rsp.msgHandle = null;
+                    rsp.response = ResponseCodes.OBEX_HTTP_NOT_FOUND;
+                    return rsp;
+                }
+                rsp.msgHandle = handle;
+                rsp.response = ResponseCodes.OBEX_HTTP_OK;
+                return rsp;
+            } else {
+                rsp.msgHandle = null;
+                rsp.response = ResponseCodes.OBEX_HTTP_FORBIDDEN;
+                return rsp;
+            }
+        }
+    }
+
+    private static int toByte(char c) throws BadRequestException{
+        if (c >= '0' && c <= '9') return (c - '0');
+        if (c >= 'A' && c <= 'F') return (c - 'A' + 10);
+        if (c >= 'a' && c <= 'f') return (c - 'a' + 10);
+        throw new BadRequestException ("Invalid hex char '" + c + "'");
+    }
+
+    private static byte[] hexStringToByteArray(String hexString) throws BadRequestException{
+        int length = hexString.length();
+        if((length % 2) != 0)
+            throw new BadRequestException ("HexString must be even number of characters");
+
+        byte[] buffer = new byte[length / 2];
+
+        for (int i = 0 ; i < length ; i += 2) {
+           buffer[i / 2] =  (byte)((toByte(hexString.charAt(i)) << 4) | toByte(hexString.charAt(i+1)));
+        }
+        return buffer;
+    }
+
+    private String parseSMSDeliverPdu(String pdu)throws BadRequestException{
+        SmsMessage sms = SmsMessage.createFromPdu(hexStringToByteArray(pdu));
+        return sms.getMessageBody();
+    }
+
+    private BluetoothMasPushMsgRsp pushMessageSms(BluetoothMasPushMsgRsp rsp, String readStr,
+            String name, BluetoothMasAppParams bluetoothMasAppParams) throws BadRequestException {
+        BmessageConsts bMsg = MapUtils.fromBmessageSMS(readStr);
+        String address = bMsg.getRecipientVcard_phone_number();
+        String smsText;
+        if((int)bluetoothMasAppParams.Charset == 0)
+            smsText = parseSMSDeliverPdu(bMsg.getBody_msg());
+        else
+            smsText = bMsg.getBody_msg();
+        String fullPath = (name == null || name.length() == 0) ? mCurrentPath : mCurrentPath + "/" + name;
+        if(!"telecom/msg/outbox".equalsIgnoreCase(fullPath)) {
+            String splitStrings[] = mCurrentPath.split("/");
+            mMnsClient.addMceInitiatedOperation("+");
+            int tmp = splitStrings.length;
+            String folderName;
+            if (name != null) {
+                if (name.length() == 0){
+                    folderName = splitStrings[tmp - 1];
+                } else {
+                    folderName = name;
+                }
+            } else {
+                folderName = splitStrings[tmp - 1];
+            }
+            if (folderName != null && folderName.equalsIgnoreCase(DRAFT)) {
+                String handle = addToSmsFolder(folderName, address, smsText);
+                if (INTERNAL_ERROR == handle) {  // == comparison valid here
+                    rsp.msgHandle = null;
+                    rsp.response = ResponseCodes.OBEX_HTTP_NOT_FOUND;
+                    return rsp;
+                }
+                rsp.msgHandle = handle;
+                rsp.response = ResponseCodes.OBEX_HTTP_OK;
+                return rsp;
+            } else {
+                rsp.msgHandle = null;
+                rsp.response = ResponseCodes.OBEX_HTTP_FORBIDDEN;
+                return rsp;
+            }
+        }
+        rsp.msgHandle = "";
+        if (bluetoothMasAppParams.Transparent == 0) {
+            mMnsClient.addMceInitiatedOperation("+");
+            String handle = addToSmsFolder(QUEUED, address, smsText);
+            if (INTERNAL_ERROR == handle) {  // == comparison valid here
+                rsp.msgHandle = null;
+                rsp.response = ResponseCodes.OBEX_HTTP_NOT_FOUND;
+                return rsp;
+            }
+            rsp.msgHandle = handle;
+            rsp.response = ResponseCodes.OBEX_HTTP_OK;
+        } else if (bluetoothMasAppParams.Transparent == 1) {
+            ArrayList<String> parts = new ArrayList<String>();
+            SmsManager sms = SmsManager.getDefault();
+            parts = sms.divideMessage(smsText);
+
+            mMnsClient.addMceInitiatedOperation("+");
+            sms.sendMultipartTextMessage(address, null, parts, null, null);
+            rsp.msgHandle = "-1";
+            rsp.response = ResponseCodes.OBEX_HTTP_OK;
+            return rsp;
+        }
+
+        if (V) {
+            Log.v(TAG, " Trying to send SMS ");
+            Log.v(TAG, " Text " + smsText + " address " + address);
+        }
+
+        try {
+            Intent sendIntentSms = new Intent("com.android.mms.transaction.SEND_MESSAGE");
+            sendIntentSms.putExtra(android.content.Intent.EXTRA_PHONE_NUMBER, address);
+            sendIntentSms.putExtra(android.content.Intent.EXTRA_TEXT, smsText);
+            mContext.sendBroadcast(sendIntentSms);
+        } catch (Exception e) {
+            e.printStackTrace();
+        }
+        return rsp;
+    }
+
+    private int setMsgStatusSms(long msgHandle, BluetoothMasAppParams bluetoothMasAppParams){
+        long handle = msgHandle - SMS_OFFSET_START;
+        Uri uri = Uri.parse("content://sms/" + handle);
+        Cursor cr = mContext.getContentResolver().query(uri, null, null, null, null);
+        if (cr != null && cr.moveToFirst()) {
+            if (bluetoothMasAppParams.StatusIndicator == 0) {
+                /* Read Status */
+                ContentValues values = new ContentValues();
+                values.put("read", bluetoothMasAppParams.StatusValue);
+                mContext.getContentResolver().update(uri, values, null, null);
+            } else {
+                if (bluetoothMasAppParams.StatusValue == 1) {
+                    deleteSMS(handle);
+                } else if (bluetoothMasAppParams.StatusValue == 0) {
+                    unDeleteSMS(handle);
+                }
+            }
+        }
+        if (cr != null) {
+            cr.close();
+        }
+        // Do we need to return ResponseCodes.OBEX_HTTP_BAD_REQUEST when cr == null?
+        return ResponseCodes.OBEX_HTTP_OK;
+    }
+
+    private int setMsgStatusMms(long msgHandle, BluetoothMasAppParams bluetoothMasAppParams){
+        long handle = getMmsMsgHndToID(msgHandle);
+        String whereClause = " _id= " + handle;
+        Uri uri = Uri.parse("content://mms/");
+        if(handle > 0){
+            Cursor cr = mContext.getContentResolver().query(uri, null, null, null, null);
+            if (cr != null) {
+                if (cr.getCount() > 0) {
+                    cr.moveToFirst();
+                    if (bluetoothMasAppParams.StatusIndicator == 0) {
+                        /* Read Status */
+                        ContentValues values = new ContentValues();
+                        values.put("read", bluetoothMasAppParams.StatusValue);
+                        int rowUpdate = mContext.getContentResolver().update(uri,
+                                values, whereClause, null);
+                        if (V){
+                                Log.v(TAG, "\nRows updated => " + Integer.toString(rowUpdate));
+                        }
+                        return ResponseCodes.OBEX_HTTP_OK;
+                    } else {
+                        if (bluetoothMasAppParams.StatusValue == 1) {
+                            deleteMMS(handle);
+                        } else if (bluetoothMasAppParams.StatusValue == 0) {
+                            unDeleteMMS(handle);
+                        }
+                        return ResponseCodes.OBEX_HTTP_OK;
+                    }
+                }
+                cr.close();
+            }
+        }
+        return ResponseCodes.OBEX_HTTP_OK;
+    }
+}
diff --git a/src/org/codeaurora/bluetooth/map/BluetoothMasMsg.java b/src/org/codeaurora/bluetooth/map/BluetoothMasMsg.java
new file mode 100644
index 0000000..81248f5
--- /dev/null
+++ b/src/org/codeaurora/bluetooth/map/BluetoothMasMsg.java
@@ -0,0 +1,38 @@
+/*
+ * Copyright (c) 2010-2011, The Linux Foundation. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *        * Redistributions of source code must retain the above copyright
+ *          notice, this list of conditions and the following disclaimer.
+ *        * Redistributions in binary form must reproduce the above copyright
+ *          notice, this list of conditions and the following disclaimer in the
+ *          documentation and/or other materials provided with the distribution.
+ *        * Neither the name of The Linux Foundation nor
+ *          the names of its contributors may be used to endorse or promote
+ *          products derived from this software without specific prior written
+ *          permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NON-INFRINGEMENT ARE DISCLAIMED.    IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
+ * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
+ * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
+ * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package org.codeaurora.bluetooth.map;
+
+import java.io.File;
+
+public class BluetoothMasMsg {
+
+    byte fractionDeliver;
+    File file;
+
+}
diff --git a/src/org/codeaurora/bluetooth/map/BluetoothMasObexServer.java b/src/org/codeaurora/bluetooth/map/BluetoothMasObexServer.java
new file mode 100644
index 0000000..c2accc3
--- /dev/null
+++ b/src/org/codeaurora/bluetooth/map/BluetoothMasObexServer.java
@@ -0,0 +1,1320 @@
+/*
+ * Copyright (c) 2008-2009, Motorola, Inc.
+ * Copyright (c) 2010-2012, The Linux Foundation. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *        * Redistributions of source code must retain the above copyright
+ *          notice, this list of conditions and the following disclaimer.
+ *        * Redistributions in binary form must reproduce the above copyright
+ *          notice, this list of conditions and the following disclaimer in the
+ *          documentation and/or other materials provided with the distribution.
+ *        * Neither the name of The Linux Foundation nor
+ *          the names of its contributors may be used to endorse or promote
+ *          products derived from this software without specific prior written
+ *          permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NON-INFRINGEMENT ARE DISCLAIMED.    IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
+ * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
+ * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
+ * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package org.codeaurora.bluetooth.map;
+
+import android.bluetooth.BluetoothDevice;
+import android.content.Context;
+import android.os.Handler;
+import android.os.Message;
+import android.os.PowerManager;
+import android.text.format.Time;
+import android.util.Log;
+
+import org.codeaurora.bluetooth.map.MapUtils.CommonUtils.BluetoothMasMessageListingRsp;
+import org.codeaurora.bluetooth.map.MapUtils.CommonUtils.BluetoothMasMessageRsp;
+import org.codeaurora.bluetooth.map.MapUtils.CommonUtils.BluetoothMasPushMsgRsp;
+
+import java.io.BufferedInputStream;
+import java.io.BufferedOutputStream;
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.FileNotFoundException;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+import java.util.Arrays;
+
+import javax.obex.ApplicationParameter;
+import javax.obex.HeaderSet;
+import javax.obex.Operation;
+import javax.obex.ResponseCodes;
+import javax.obex.ServerOperation;
+import javax.obex.ServerRequestHandler;
+
+import org.codeaurora.bluetooth.map.MapUtils.MapUtils.BadRequestException;
+
+public class BluetoothMasObexServer extends ServerRequestHandler {
+
+    private static final String TAG = "BluetoothMasObexServer";
+
+    private static final boolean D = BluetoothMasService.DEBUG;
+
+    private static final boolean V = BluetoothMasService.VERBOSE;
+
+    private static final int UUID_LENGTH = 16;
+
+    // type for list folder contents
+    private static final String TYPE_LISTING = "x-obex/folder-listing";
+    private static final String TYPE_MESSAGE_LISTING = "x-bt/MAP-msg-listing";
+    private static final String TYPE_MESSAGE = "x-bt/message";
+    private static final String TYPE_MESSAGE_STATUS = "x-bt/messageStatus";
+    private static final String TYPE_MESSAGE_UPDATE = "x-bt/MAP-messageUpdate";
+    private static final String TYPE_MESSAGE_NOTIFICATION = "x-bt/MAP-NotificationRegistration";
+
+    public long mConnectionId;
+
+    private Handler mCallback = null;
+
+    public Context mContext;
+
+    public static boolean sIsAborted = false;
+
+    private PowerManager.WakeLock mWakeLock = null;
+
+    public enum MasState {
+        MAS_SERVER_CONNECTING,
+        MAS_SERVER_DISCONNECTING,
+        MAS_SERVER_CONNECTED,
+        MAS_SERVER_DISCONNECTED,
+        MAS_SERVER_SET_FOLDER,
+        MAS_SERVER_GET_FILE_PENDING,
+        MAS_SERVER_BROWSE_FOLDER_PENDING,
+        MAS_SERVER_BROWSE_FOLDER,
+        MAS_SERVER_GET_MSG_LIST_PENDING,
+        MAS_SERVER_GET_MSG_LIST,
+        MAS_SERVER_GET_MSG_PENDING,
+        MAS_SERVER_GET_MSG,
+        MAS_SERVER_SET_MSG_STATUS,
+        MAS_SERVER_SET_NOTIFICATION_REG,
+        MAS_SERVER_UPDATE_INBOX,
+        MAS_SERVER_PUSH_MESSAGE
+    };
+    private MasState mState = MasState.MAS_SERVER_DISCONNECTED;
+
+     // 128 bit UUID for MAS
+    private static final byte[] MAS_TARGET = new byte[] {
+        (byte)0xbb, (byte)0x58, (byte)0x2b, (byte)0x40, (byte)0x42, (byte)0x0c, (byte)0x11, (byte)0xdb,
+        (byte)0xb0, (byte)0xde, (byte)0x08, (byte)0x00, (byte)0x20, (byte)0x0c, (byte)0x9a, (byte)0x66
+    };
+
+    private IBluetoothMasApp mAppIf;
+
+    private BluetoothDevice mRemoteDevice;
+
+    private class MasAppParamsStore {
+
+        private BluetoothMasAppParams appParams = null;
+
+        public final void clear() {
+            if (D) Log.d(TAG, "Clear AppParams : Enter");
+            appParams.MaxListCount = BluetoothMasSpecParams.MAS_DEFAULT_MAX_LIST_COUNT;
+            appParams.ListStartOffset = 0;
+            appParams.SubjectLength = BluetoothMasSpecParams.MAS_DEFAULT_SUBJECT_LENGTH;
+            appParams.ParameterMask = BluetoothMasSpecParams.MAS_DEFAULT_PARAMETER_MASK;
+            appParams.FilterMessageType = 0;
+            appParams.FilterReadStatus = 0;
+            appParams.FilterPriority = 0;
+            appParams.FilterPeriodBegin = null;
+            appParams.FilterPeriodEnd = null;
+            appParams.FilterRecipient = null;
+            appParams.FilterOriginator = null;
+            appParams.Retry = 1;
+            appParams.Transparent = 0;
+            appParams.FractionRequest = BluetoothMasSpecParams.MAS_FRACTION_REQUEST_NOT_SET;
+            appParams.Charset = 0x01;
+        }
+
+        public MasAppParamsStore() {
+            super();
+            appParams = new BluetoothMasAppParams();
+            clear();
+        }
+
+        public final BluetoothMasAppParams get() {
+            if (D) Log.d(TAG, "Create MasAppParams struct for service : Enter");
+            BluetoothMasAppParams tmp = new BluetoothMasAppParams();
+
+            tmp.MaxListCount = appParams.MaxListCount;
+            tmp.ListStartOffset = appParams.ListStartOffset;
+            tmp.SubjectLength = appParams.SubjectLength;
+            tmp.ParameterMask = appParams.ParameterMask;
+
+            tmp.Attachment = appParams.Attachment;
+            tmp.Charset = appParams.Charset;
+
+            tmp.StatusIndicator = appParams.StatusIndicator;
+            tmp.StatusValue = appParams.StatusValue;
+            tmp.Retry = appParams.Retry;
+
+            tmp.FilterMessageType = appParams.FilterMessageType;
+            tmp.FilterReadStatus = appParams.FilterReadStatus;
+            tmp.FilterPriority = appParams.FilterPriority;
+
+            tmp.FilterPeriodBegin = (appParams.FilterPeriodBegin == null) ? null : new String(appParams.FilterPeriodBegin);
+            tmp.FilterPeriodEnd = (appParams.FilterPeriodEnd == null) ? null : new String(appParams.FilterPeriodEnd);
+            tmp.FilterRecipient = (appParams.FilterRecipient == null) ? null : new String(appParams.FilterRecipient);
+            tmp.FilterOriginator = (appParams.FilterOriginator == null) ? null : new String(appParams.FilterOriginator);
+            tmp.Retry = appParams.Retry;
+            tmp.Transparent = appParams.Transparent;
+            tmp.FractionRequest = appParams.FractionRequest;
+            tmp.Notification = appParams.Notification;
+
+            return tmp;
+        }
+
+        public final boolean isMaxListCountZero() {
+
+            return (appParams.MaxListCount == 0) ? true : false;
+
+        }
+
+        private final int getUint16BigEndian(byte b1, byte b2) {
+            int retVal;
+            retVal = (int) ((0x0000FF00 & (int) (b1 << 0x8)) | (0x000000FF & (int) b2));
+            return retVal;
+        }
+
+        private final long getUint32BigEndian(byte b1, byte b2, byte b3, byte b4) {
+            long retVal;
+            retVal = (long) ((0xFF000000 & (long) (b1 << 0x24))
+                    | (0x00FF0000 & (long) (b2 << 0x16))
+                    | (0x0000FF00 & (long) (b3 << 0x8)) | (0x000000FF & (long) b4));
+            return retVal;
+        }
+
+        private final boolean validateTag(long tagVal, long tagLen, long tagMinVal, long tagMaxVal, long tagActualLen) {
+
+            if (tagLen != tagActualLen) {
+                return false;
+            }
+
+            if ( tagVal < tagMinVal || tagVal > tagMaxVal){
+                return false;
+            }
+            return true;
+        }
+
+        public final boolean parse(byte[] params) {
+            int i = 0;
+
+
+            if (D) Log.d(TAG, "Parse App. Params: Enter");
+
+            if (params == null){
+                if (D) Log.d(TAG, "No App. Params to parse: Exit");
+                return true;
+            }
+
+            while (i < params.length) {
+                switch (params[i]) {
+                case BluetoothMasSpecParams.MAS_TAG_MAX_LIST_COUNT:
+                    i += 2;
+                    appParams.MaxListCount = getUint16BigEndian(params[i], params[i + 1]);
+                    if (V){
+                        Log.v(TAG, " params i " + params[i] + " params i+1"
+                                + params[i + 1] + " maxlistcount "
+                                + appParams.MaxListCount);
+                    }
+                    if(validateTag((long)appParams.MaxListCount, (long) params[i-1],
+                                   (long) BluetoothMasSpecParams.MAS_TAG_MAX_LIST_COUNT_MIN_VAL,
+                                   (long) BluetoothMasSpecParams.MAS_TAG_MAX_LIST_COUNT_MAX_VAL,
+                                   (long) BluetoothMasSpecParams.MAS_TAG_MAX_LIST_COUNT_LEN ) == false){
+                        return false;
+                    }
+                    i += BluetoothMasSpecParams.MAS_TAG_MAX_LIST_COUNT_LEN;
+                    break;
+
+                case BluetoothMasSpecParams.MAS_TAG_LIST_START_OFFSET:
+                    i += 2;
+                    appParams.ListStartOffset = getUint16BigEndian(params[i],
+                            params[i + 1]);
+                    if (V){
+                        Log.v(TAG, " params i " + params[i] + " params i+1"
+                                + params[i + 1] + " maxlistcount "
+                                + appParams.ListStartOffset);
+                    }
+                    if(validateTag((long)appParams.ListStartOffset, (long) params[i-1],
+                                   (long) BluetoothMasSpecParams.MAS_TAG_LIST_START_OFFSET_MIN_VAL,
+                                   (long) BluetoothMasSpecParams.MAS_TAG_LIST_START_OFFSET_MAX_VAL,
+                                   (long) BluetoothMasSpecParams.MAS_TAG_LIST_START_OFFSET_LEN ) == false){
+                        return false;
+                    }
+                    i += BluetoothMasSpecParams.MAS_TAG_LIST_START_OFFSET_LEN;
+                    break;
+
+                case BluetoothMasSpecParams.MAS_TAG_FILTER_PERIOD_BEGIN:
+                    i += 1;
+                    appParams.FilterPeriodBegin = new String("");
+                    for (int j = 1; j <= params[i]; j++) {
+                        appParams.FilterPeriodBegin += (char) params[i + j];
+                    }
+                    if (V){
+                        Log.v(TAG, "FilterPeriodBegin "
+                                + appParams.FilterPeriodBegin);
+                    }
+                    i += params[i];
+                    i += 1;
+                    break;
+
+                case BluetoothMasSpecParams.MAS_TAG_FILTER_PERIOD_END:
+                    i += 1;
+                    appParams.FilterPeriodEnd = new String("");
+                    for (int j = 1; j <= params[i]; j++) {
+                        appParams.FilterPeriodEnd += (char) params[i + j];
+                    }
+                    if (V){
+                        Log.v(TAG, "FilterPeriodEnd " + appParams.FilterPeriodEnd);
+                    }
+                    i += params[i];
+                    i += 1;
+                    break;
+
+                case BluetoothMasSpecParams.MAS_TAG_FILTER_RECIPIENT:
+                    i += 1;
+                    appParams.FilterRecipient = new String("");
+                    for (int j = 1; j <= params[i]; j++) {
+                        appParams.FilterRecipient += (char)params[i + j];
+
+                    }
+                    if (V){
+                        Log.v(TAG, "FilterPeriodRecipient "
+                                + appParams.FilterRecipient);
+                    }
+                    i += params[i];
+                    i += 1;
+                    break;
+
+                case BluetoothMasSpecParams.MAS_TAG_FILTER_ORIGINATOR:
+                    i += 1;
+                    appParams.FilterOriginator = new String("");
+                    for (int j = 1; j <= params[i]; j++) {
+                        appParams.FilterOriginator += (char) params[i+ j];
+                    }
+                    if (V){
+                        Log.v(TAG, "FilterPeriodOriginator "
+                                + appParams.FilterOriginator);
+                    }
+                    i += params[i];
+                    i += 1;
+                    break;
+
+                case BluetoothMasSpecParams.MAS_TAG_FILTER_MESSAGE_TYPE:
+                    i += 2;
+                    appParams.FilterMessageType = params[i];
+                    if (V){
+                        Log.v(TAG, " params i " + params[i] + " FilterMessageType "
+                                + appParams.FilterMessageType);
+                    }
+                    if(validateTag((long)appParams.FilterMessageType, (long) params[i-1],
+                                   (long) BluetoothMasSpecParams.MAS_TAG_FILTER_MESSAGE_TYPE_MIN_VAL,
+                                   (long) BluetoothMasSpecParams.MAS_TAG_FILTER_MESSAGE_TYPE_MAX_VAL,
+                                   (long) BluetoothMasSpecParams.MAS_TAG_FILTER_MESSAGE_TYPE_LEN ) == false){
+                        return false;
+                    }
+                    i += BluetoothMasSpecParams.MAS_TAG_FILTER_MESSAGE_TYPE_LEN;
+                    break;
+
+                case BluetoothMasSpecParams.MAS_TAG_FILTER_READ_STATUS:
+                    i += 2;
+                    appParams.FilterReadStatus = params[i];
+                    if (V){
+                        Log.v(TAG, " params i " + params[i] + " FilterReadStatus "
+                                + appParams.FilterReadStatus);
+                    }
+                    if(validateTag((long)appParams.FilterReadStatus, (long) params[i-1],
+                                   (long) BluetoothMasSpecParams.MAS_TAG_FILTER_READ_STATUS_MIN_VAL,
+                                   (long) BluetoothMasSpecParams.MAS_TAG_FILTER_READ_STATUS_MAX_VAL,
+                                   (long) BluetoothMasSpecParams.MAS_TAG_FILTER_READ_STATUS_LEN ) == false){
+                        return false;
+                    }
+                    i += BluetoothMasSpecParams.MAS_TAG_FILTER_READ_STATUS_LEN;
+                    break;
+
+                case BluetoothMasSpecParams.MAS_TAG_FILTER_PRIORITY:
+                    i += 2;
+                    appParams.FilterPriority = params[i];
+                    if (V){
+                        Log.v(TAG, " params i " + params[i] + " FilterPriority "
+                                + appParams.FilterPriority);
+                    }
+                    if(validateTag((long)appParams.FilterPriority, (long) params[i-1],
+                                   (long) BluetoothMasSpecParams.MAS_TAG_FILTER_PRIORITY_MIN_VAL,
+                                   (long) BluetoothMasSpecParams.MAS_TAG_FILTER_PRIORITY_MAX_VAL,
+                                   (long) BluetoothMasSpecParams.MAS_TAG_FILTER_PRIORITY_LEN ) == false){
+                        return false;
+                    }
+                    i += BluetoothMasSpecParams.MAS_TAG_FILTER_PRIORITY_LEN;
+                    break;
+
+                case BluetoothMasSpecParams.MAS_TAG_STATUS_INDICATOR:
+                    i += 2;
+                    appParams.StatusIndicator = params[i];
+                    if (V){
+                        Log.v(TAG, " params i " + params[i] + " StatusIndicator "
+                                + appParams.StatusIndicator);
+                    }
+                    if(validateTag((long)appParams.StatusIndicator, (long) params[i-1],
+                                   (long) BluetoothMasSpecParams.MAS_TAG_STATUS_INDICATOR_MIN_VAL,
+                                   (long) BluetoothMasSpecParams.MAS_TAG_STATUS_INDICATOR_MAX_VAL,
+                                   (long) BluetoothMasSpecParams.MAS_TAG_STATUS_INDICATOR_LEN ) == false){
+                        return false;
+                    }
+                    i += BluetoothMasSpecParams.MAS_TAG_STATUS_INDICATOR_LEN;
+                    break;
+
+                case BluetoothMasSpecParams.MAS_TAG_STATUS_VALUE:
+                    i += 2;
+                    appParams.StatusValue = params[i];
+                    if (V){
+                        Log.v(TAG, " params i " + params[i] + " StatusValue "
+                                + appParams.StatusValue);
+                    }
+                    if(validateTag((long)appParams.StatusValue, (long) params[i-1],
+                                   (long) BluetoothMasSpecParams.MAS_TAG_STATUS_VALUE_MIN_VAL,
+                                   (long) BluetoothMasSpecParams.MAS_TAG_STATUS_VALUE_MAX_VAL,
+                                   (long) BluetoothMasSpecParams.MAS_TAG_STATUS_VALUE_LEN ) == false){
+                        return false;
+                    }
+                    i += BluetoothMasSpecParams.MAS_TAG_STATUS_VALUE_LEN;
+                    break;
+
+                case BluetoothMasSpecParams.MAS_TAG_SUBJECT_LENGTH:
+                    i += 2;
+                    appParams.SubjectLength = (short)(params[i] & 0x00FF);
+                    if (V){
+                        Log.v(TAG, " params i " + params[i] + " SubjectLen "
+                                + appParams.SubjectLength);
+                    }
+                    if(validateTag((long)appParams.SubjectLength, (long) params[i-1],
+                                         (long) BluetoothMasSpecParams.MAS_TAG_SUBJECT_LENGTH_MIN_VAL,
+                                         (long) BluetoothMasSpecParams.MAS_TAG_SUBJECT_LENGTH_MAX_VAL,
+                                         (long) BluetoothMasSpecParams.MAS_TAG_SUBJECT_LENGTH_LEN ) == false){
+                        return false;
+                    }
+                    i += BluetoothMasSpecParams.MAS_TAG_SUBJECT_LENGTH_LEN;
+                    break;
+
+                case BluetoothMasSpecParams.MAS_TAG_PARAMETER_MASK:
+                    i += 2;
+                    appParams.ParameterMask = getUint32BigEndian(params[i],
+                            params[i + 1], params[i + 2], params[i + 3]);
+                    if ( appParams.ParameterMask == 0 ){
+                        // If it is 0, send all parameters
+                        appParams.ParameterMask = BluetoothMasSpecParams.MAS_DEFAULT_PARAMETER_MASK;
+                    }
+                    if (V){
+                        Log.v(TAG, " params i " + params[i] + " params i+1"
+                                + params[i + 1] + "params[i+2]" + params[i + 2]
+                                + "params[i+3" + params[i + 3] + " ParameterMask "
+                                + appParams.ParameterMask);
+                    }
+                    if(validateTag((long)appParams.ParameterMask, (long) params[i-1],
+                                   (long) BluetoothMasSpecParams.MAS_TAG_PARAMETER_MASK_MIN_VAL,
+                                   (long) BluetoothMasSpecParams.MAS_TAG_PARAMETER_MASK_MAX_VAL,
+                                   (long) BluetoothMasSpecParams.MAS_TAG_PARAMETER_MASK_LEN ) == false){
+                        return false;
+                    }
+                    i += BluetoothMasSpecParams.MAS_TAG_PARAMETER_MASK_LEN;
+                    break;
+
+                case BluetoothMasSpecParams.MAS_TAG_CHARSET:
+                    i += 2;
+                    appParams.Charset = params[i];
+                    if (V){
+                        Log.v(TAG, " params i " + params[i] + " Charset "
+                                + appParams.Charset);
+                    }
+                    if(validateTag((long)appParams.Charset, (long) params[i-1],
+                                          (long) BluetoothMasSpecParams.MAS_TAG_CHARSET_MIN_VAL,
+                                          (long) BluetoothMasSpecParams.MAS_TAG_CHARSET_MAX_VAL,
+                                          (long) BluetoothMasSpecParams.MAS_TAG_CHARSET_LEN ) == false){
+                        return false;
+                    }
+                    i += BluetoothMasSpecParams.MAS_TAG_CHARSET_LEN;
+                    break;
+
+                case BluetoothMasSpecParams.MAS_TAG_TRANSPARENT:
+                    i += 2;
+                    appParams.Transparent = params[i];
+                    if (V){
+                        Log.v(TAG, " params i " + params[i] + " Transparent "
+                                + appParams.Transparent);
+                    }
+                    if(validateTag((long)appParams.Transparent, (long) params[i-1],
+                                   (long) BluetoothMasSpecParams.MAS_TAG_TRANSPARENT_MIN_VAL,
+                                   (long) BluetoothMasSpecParams.MAS_TAG_TRANSPARENT_MAX_VAL,
+                                   (long) BluetoothMasSpecParams.MAS_TAG_TRANSPARENT_LEN ) == false){
+                        return false;
+                    }
+                    i += BluetoothMasSpecParams.MAS_TAG_TRANSPARENT_LEN;
+                    break;
+
+                case BluetoothMasSpecParams.MAS_TAG_RETRY:
+                    i += 2;
+                    appParams.Retry = params[i];
+                    if (V){
+                        Log.v(TAG, " params i " + params[i] + " Retry "
+                                + appParams.Retry);
+                    }
+                    if(validateTag((long)appParams.Retry, (long) params[i-1],
+                                   (long) BluetoothMasSpecParams.MAS_TAG_RETRY_MIN_VAL,
+                                   (long) BluetoothMasSpecParams.MAS_TAG_RETRY_MAX_VAL,
+                                   (long) BluetoothMasSpecParams.MAS_TAG_RETRY_LEN ) == false){
+                        return false;
+                    }
+                    i += BluetoothMasSpecParams.MAS_TAG_RETRY_LEN;
+                    break;
+
+                case BluetoothMasSpecParams.MAS_TAG_ATTACHMENT:
+                    i += 2;
+                    appParams.Attachment = params[i];
+                    if (V){
+                        Log.v(TAG, " params i " + params[i] + " Attachment "
+                                + appParams.Attachment);
+                    }
+                    if(validateTag((long)appParams.Attachment, (long) params[i-1],
+                                   (long) BluetoothMasSpecParams.MAS_TAG_ATTACHMENT_MIN_VAL,
+                                   (long) BluetoothMasSpecParams.MAS_TAG_ATTACHMENT_MAX_VAL,
+                                   (long) BluetoothMasSpecParams.MAS_TAG_ATTACHMENT_LEN ) == false){
+                        return false;
+                    }
+                    i += BluetoothMasSpecParams.MAS_TAG_ATTACHMENT_LEN;
+                    break;
+
+                case BluetoothMasSpecParams.MAS_TAG_FRACTION_REQUEST:
+                    i += 2;
+                    appParams.FractionRequest = params[i];
+                    if (V){
+                        Log.v(TAG, " params i " + params[i] + " Fraction Request "
+                                + appParams.FractionRequest);
+                    }
+                    if(validateTag((long)appParams.FractionRequest, (long) params[i-1],
+                                   (long) BluetoothMasSpecParams.MAS_TAG_FRACTION_REQUEST_MIN_VAL,
+                                   (long) BluetoothMasSpecParams.MAS_TAG_FRACTION_REQUEST_MAX_VAL,
+                                   (long) BluetoothMasSpecParams.MAS_TAG_FRACTION_REQUEST_LEN ) == false){
+                        return false;
+                    }
+                    i += BluetoothMasSpecParams.MAS_TAG_FRACTION_REQUEST_LEN;
+                    break;
+
+                case BluetoothMasSpecParams.MAS_TAG_NOTIFICATION_STATUS:
+                    i += 2;
+                    appParams.Notification = params[i];
+                    if(validateTag((long)appParams.MaxListCount, (long) params[i-1],
+                                   (long) BluetoothMasSpecParams.MAS_TAG_NOTIFICATION_STATUS_MIN_VAL,
+                                   (long) BluetoothMasSpecParams.MAS_TAG_NOTIFICATION_STATUS_MAX_VAL,
+                                   (long) BluetoothMasSpecParams.MAS_TAG_NOTIFICATION_STATUS_LEN ) == false){
+                        return false;
+
+                    }
+                    i += BluetoothMasSpecParams.MAS_TAG_NOTIFICATION_STATUS_LEN;
+                    break;
+                default:
+                    break;
+
+                }
+            }
+            return true;
+        }
+    }
+
+    private MasAppParamsStore masAppParams = new MasAppParamsStore();
+
+    public BluetoothMasObexServer(Handler callback, BluetoothDevice remoteDevice,
+            Context context, IBluetoothMasApp appIf) {
+        super();
+        mAppIf = appIf;
+
+        mConnectionId = -1;
+        mCallback = callback;
+        mContext = context;
+        mRemoteDevice = remoteDevice;
+        if (V){
+            Log.v(TAG, "BlueoothMasObexServer const called");
+        }
+        // set initial value when ObexServer created
+        if (D) Log.d(TAG, "Initialize MasObexServer");
+    }
+
+    @Override
+    public int onConnect(final HeaderSet request, HeaderSet reply) {
+        if (V) Log.v(TAG, "BluetoothMasObexServer: onConnect");
+        acquireMasLock();
+        int retVal = onConnectInternal(request, reply);
+        if (V) Log.v(TAG, "BluetoothMasObexServer: exiting from onConnect");
+        releaseMasLock();
+        return retVal;
+    }
+
+    private int onConnectInternal(final HeaderSet request, HeaderSet reply) {
+        if (D) Log.d(TAG, "onConnect()");
+        try {
+            byte[] uuid = (byte[]) request.getHeader(HeaderSet.TARGET);
+            if (uuid == null) {
+                Log.w(TAG, "Null UUID ");
+                return ResponseCodes.OBEX_HTTP_NOT_ACCEPTABLE;
+            }
+            if (D)
+                Log.d(TAG, "onConnect(): uuid=" + Arrays.toString(uuid));
+
+            if (uuid.length != UUID_LENGTH) {
+                Log.w(TAG, "Wrong UUID length");
+                return ResponseCodes.OBEX_HTTP_NOT_ACCEPTABLE;
+            }
+            for (int i = 0; i < UUID_LENGTH; i++) {
+                if (uuid[i] != MAS_TARGET[i]) {
+                    Log.w(TAG, "Wrong UUID");
+                    return ResponseCodes.OBEX_HTTP_NOT_ACCEPTABLE;
+                }
+            }
+            if (!mAppIf.checkPrecondition()) {
+                return ResponseCodes.OBEX_HTTP_UNAVAILABLE;
+            }
+            reply.setHeader(HeaderSet.WHO, uuid);
+        } catch (IOException e) {
+            Log.e(TAG, e.toString());
+            return ResponseCodes.OBEX_HTTP_INTERNAL_ERROR;
+        } catch (IllegalArgumentException e) {
+            Log.e(TAG, e.toString());
+            return ResponseCodes.OBEX_HTTP_BAD_REQUEST;
+        }
+
+        try {
+            byte[] remote = (byte[]) request.getHeader(HeaderSet.WHO);
+            if (remote != null) {
+                if (D) Log.d(TAG, "onConnect(): remote=" + Arrays.toString(remote));
+                reply.setHeader(HeaderSet.TARGET, remote);
+            }
+        } catch (IOException e) {
+            Log.e(TAG, e.toString());
+            return ResponseCodes.OBEX_HTTP_INTERNAL_ERROR;
+        }
+
+        if (V) Log.v(TAG, "onConnect(): uuid is ok, will send out "
+            + "MSG_SESSION_ESTABLISHED msg.");
+
+        Message msg = Message.obtain(mCallback);
+        msg.what = BluetoothMasService.MSG_SESSION_ESTABLISHED;
+        msg.sendToTarget();
+
+        mState = MasState.MAS_SERVER_CONNECTED;
+        if (D) Log.d(TAG, "Connect(): Success");
+        mAppIf.onConnect();
+        return ResponseCodes.OBEX_HTTP_OK;
+    }
+
+    @Override
+    public void onDisconnect(final HeaderSet req, final HeaderSet resp) {
+        if (V) Log.v(TAG, "BluetoothMasObexServer: onDisconnect");
+        acquireMasLock();
+        onDisconnectInternal(req, resp);
+        if (V) Log.v(TAG, "BluetoothMasObexServer: exiting from onDisconnect");
+        releaseMasLock();
+    }
+
+    private void onDisconnectInternal(final HeaderSet req, final HeaderSet resp) {
+        if (D) Log.d(TAG, "onDisconnect(): enter");
+        mAppIf.onDisconnect();
+
+        resp.responseCode = ResponseCodes.OBEX_HTTP_OK;
+        if (mCallback != null) {
+            Message msg = Message.obtain(mCallback);
+            msg.what = BluetoothMasService.MSG_SESSION_DISCONNECTED;
+            msg.sendToTarget();
+            if (V) Log.v(TAG,"onDisconnect(): msg MSG_SESSION_DISCONNECTED sent out.");
+        }
+
+        // MNS Service
+        mAppIf.stopMnsSession(mRemoteDevice);
+        mState = MasState.MAS_SERVER_DISCONNECTED;
+    }
+
+    @Override
+    public int onAbort(HeaderSet request, HeaderSet reply) {
+        if (D) Log.d(TAG, "onAbort(): enter.");
+        sIsAborted = true;
+        return ResponseCodes.OBEX_HTTP_OK;
+    }
+
+    @Override
+    public int onSetPath(final HeaderSet request, final HeaderSet reply,
+            final boolean backup, final boolean create) {
+        if (V) Log.v(TAG, "BluetoothMasObexServer: onSetPath");
+        acquireMasLock();
+        int retVal = onSetPathInternal(request, reply, backup, create);
+        if (V) Log.v(TAG, "BluetoothMasObexServer: exiting from onSetPath");
+        releaseMasLock();
+        return retVal;
+    }
+
+    private int onSetPathInternal(final HeaderSet request, final HeaderSet reply,
+            final boolean backup, final boolean create) {
+
+        if (D) Log.d(TAG, "onSetPath(): supports SetPath request.");
+
+        String tmpPath = null;
+        boolean retVal = false;
+        boolean tmpBackup = backup;
+
+        if (tmpBackup && create) {
+            tmpBackup = true;
+        } else {
+            tmpBackup = false;
+        }
+        if (mState != MasState.MAS_SERVER_CONNECTED) {
+            if (D)
+                Log.e(TAG, "onSetPath() Failed: Mas Server not connected");
+            return ResponseCodes.OBEX_HTTP_UNAVAILABLE;
+        }
+        mState = MasState.MAS_SERVER_SET_FOLDER;
+
+        try {
+            tmpPath = (String) request.getHeader(HeaderSet.NAME);
+        } catch (IOException e) {
+            Log.e(TAG, "Get name header fail: " + e);
+            return ResponseCodes.OBEX_HTTP_INTERNAL_ERROR;
+        } catch (IllegalArgumentException e) {
+            Log.e(TAG, "Get name header fail: " + e);
+            return ResponseCodes.OBEX_HTTP_BAD_REQUEST;
+        }
+        if (D)
+            Log.d(TAG, "backup=" + backup + " create=" + create + " name="
+                    + tmpPath);
+
+        retVal = mAppIf.setPath(backup, tmpPath);
+        mState = MasState.MAS_SERVER_CONNECTED;
+        if (retVal == true) {
+            if (V)
+                Log.v(TAG, "SetPath to" + tmpPath + "SUCCESS");
+            return ResponseCodes.OBEX_HTTP_OK;
+        } else {
+            Log.e(TAG, "Path not found");
+            return ResponseCodes.OBEX_HTTP_NOT_FOUND;
+        }
+    }
+
+    @Override
+    public void onClose() {
+        if (V) Log.v(TAG, "BluetoothMasObexServer: onClose");
+        acquireMasLock();
+        onCloseInternal();
+        if (V) Log.v(TAG, "BluetoothMasObexServer: exiting from onClose");
+        releaseMasLock();
+    }
+
+    public void onCloseInternal() {
+        mAppIf.stopMnsSession(mRemoteDevice);
+
+        if (mCallback != null) {
+            Message msg = Message.obtain(mCallback);
+            msg.what = BluetoothMasService.MSG_SERVERSESSION_CLOSE;
+            msg.arg1 = mAppIf.getMasId();
+            msg.sendToTarget();
+            if (D) Log.d(TAG, "onClose(): msg MSG_SERVERSESSION_CLOSE sent out.");
+        }
+    }
+
+    @Override
+    public int onGet(Operation op) {
+        if (V) Log.v(TAG, "BluetoothMasObexServer: onGet");
+        acquireMasLock();
+        int retVal = onGetInternal(op);
+        if (V) Log.v(TAG, "BluetoothMasObexServer: exiting from onGet");
+        releaseMasLock();
+        return retVal;
+    }
+
+    private int onGetInternal(Operation op) {
+
+        byte[] appParams = null;
+        boolean retVal = true;
+
+        if (D) Log.d(TAG, "onGet(): support GET request.");
+
+        sIsAborted = false;
+        HeaderSet request = null;
+        String type = "";
+        String name = "";
+
+        // TBD - IncompleteGet handling
+        try {
+            request = op.getReceivedHeader();
+            type = (String) request.getHeader(HeaderSet.TYPE);
+            name = (String) request.getHeader(HeaderSet.NAME);
+            appParams = (byte[])request.getHeader(HeaderSet.APPLICATION_PARAMETER);
+        } catch (IOException e) {
+            Log.e(TAG, "request headers error: " + e);
+            return ResponseCodes.OBEX_HTTP_INTERNAL_ERROR;
+        } catch (IllegalArgumentException e) {
+            Log.e(TAG, "request headers error: " + e);
+            return ResponseCodes.OBEX_HTTP_BAD_REQUEST;
+        }
+
+        masAppParams.clear();
+        retVal = masAppParams.parse(appParams);
+
+        if (type == null || (retVal == false) ) {
+            return ResponseCodes.OBEX_HTTP_BAD_REQUEST;
+        }
+
+        if (V) Log.v(TAG, "type = " + type);
+
+        if (type.equals(TYPE_LISTING)) {
+            return sendFolderListing(op);
+        }
+        if (type.equals(TYPE_MESSAGE_LISTING)) {
+            return sendMsgListing(op, name);
+        }
+        if (type.equals(TYPE_MESSAGE)) {
+            return sendMsg(op, name);
+        }
+
+        if (V) Log.v(TAG, "get returns HTTP_BAD_REQUEST");
+        return ResponseCodes.OBEX_HTTP_BAD_REQUEST;
+
+    }
+
+    private final int pushMsg(Operation op, String name) {
+        // TBD - Need to do this on a per masinstance basis
+        String fileName = "PushMsg" + mAppIf.getMasId();
+        int outputBufferSize = op.getMaxPacketSize();
+        int readLength = 0;
+        long timestamp = 0;
+        int position = 0;
+        byte[] b = new byte[outputBufferSize];
+        BufferedOutputStream bos = null;
+        InputStream is = null;
+        boolean error = false;
+        File file = null;
+        BluetoothMasPushMsgRsp pMsg = new BluetoothMasPushMsgRsp();;
+
+        file = new File(mContext.getFilesDir() + "/" + fileName);
+
+        try {
+            is = op.openInputStream();
+        } catch (IOException e1) {
+            Log.e(TAG, "Error while opening InputStream");
+            error = true;
+        }
+
+        if (error != true) {
+            try {
+
+                FileOutputStream fos = mContext.openFileOutput(fileName,
+                        Context.MODE_PRIVATE);
+
+                bos = new BufferedOutputStream(fos);
+            } catch (FileNotFoundException e) {
+                // TODO Auto-generated catch block
+                e.printStackTrace();
+            }
+        }
+
+        if (error != true) {
+            try {
+                while (true) {
+                    if (V) {
+                        timestamp = System.currentTimeMillis();
+                    }
+                    readLength = is.read(b);
+                    if (readLength == -1) {
+                        if (D) {
+                            Log.d(TAG, "Receive file reached stream end at position" + position);
+                        }
+                        break;
+                    }
+                    bos.write(b, 0, readLength);
+                    position += readLength;
+                    if (V) {
+                        Log.v(TAG, "Receive file position = " + position
+                                + " readLength " + readLength + " bytes took "
+                                + (System.currentTimeMillis() - timestamp)
+                                + " ms");
+                    }
+                }
+            } catch (IOException e1) {
+                Log.e(TAG, "Error when receiving file");
+                error = true;
+            }
+        }
+
+        if (bos != null) {
+            try {
+                bos.close();
+            } catch (IOException e) {
+                Log.e(TAG, "Error when closing stream after send");
+                error = true;
+            }
+        }
+
+        if (error != true) {
+            try {
+                pMsg = mAppIf.pushMsg(name, file, masAppParams.get());
+            } catch (BadRequestException e) {
+                if (V) Log.v(TAG, "BadRequestException:" + e.getMessage(), e);
+                return ResponseCodes.OBEX_HTTP_BAD_REQUEST;
+            }
+
+            if ((pMsg.msgHandle != null)
+                    && (pMsg.response == ResponseCodes.OBEX_HTTP_OK)) {
+                HeaderSet reply;
+                reply = new HeaderSet();
+                reply.setHeader(HeaderSet.NAME, pMsg.msgHandle);
+                return pushHeader(op, reply);
+            } else {
+                return pMsg.response;
+            }
+        } else {
+            return ResponseCodes.OBEX_HTTP_BAD_REQUEST;
+        }
+    }
+
+    private final int msgStatus(Operation op, String name) {
+        if (D) Log.d(TAG, "msgStatus: Enter");
+        if (name == null || name.length() == 0) {
+            return ResponseCodes.OBEX_HTTP_BAD_REQUEST;
+        }
+        return mAppIf.msgStatus(name, masAppParams.get());
+    }
+
+    private final int msgUpdate(Operation op) {
+        if (D) Log.d(TAG, "msgUpdate: Enter");
+        return mAppIf.msgUpdate();
+    }
+
+    private final int notification(Operation op) {
+        return mAppIf.notification(mRemoteDevice, masAppParams.get());
+    }
+
+    @Override
+    public int onPut(Operation op) {
+        if (V) Log.v(TAG, "BluetoothMasObexServer: onPut");
+        acquireMasLock();
+        int retVal = onPutInternal(op);
+        if (V) Log.v(TAG, "BluetoothMasObexServer: exiting from onPut");
+        releaseMasLock();
+        return retVal;
+    }
+
+    private int onPutInternal(Operation op) {
+
+        byte[] appParams = null;
+        boolean retVal = true;
+        BluetoothMasAppParams tmp;
+        InputStream inputStream = null;
+        byte[] readByte = new byte[10];
+
+        if (D) Log.d(TAG, "onPut(): support PUT request.");
+
+        sIsAborted = false;
+        HeaderSet request = null;
+        String type = "";
+        String name = "";
+
+        // TBD - IncompleteGet handling
+        try {
+            request = op.getReceivedHeader();
+            type = (String) request.getHeader(HeaderSet.TYPE);
+            name = (String) request.getHeader(HeaderSet.NAME);
+            appParams = (byte[])request.getHeader(HeaderSet.APPLICATION_PARAMETER);
+        } catch (IOException e) {
+            Log.e(TAG, "request headers error: " + e);
+            return ResponseCodes.OBEX_HTTP_INTERNAL_ERROR;
+        } catch (IllegalArgumentException e) {
+            Log.e(TAG, "request headers error: " + e);
+            return ResponseCodes.OBEX_HTTP_BAD_REQUEST;
+        }
+        masAppParams.clear();
+        if ( appParams != null ){
+            masAppParams.parse(appParams);
+        }
+        if(type == null || retVal == false) {
+            return ResponseCodes.OBEX_HTTP_BAD_REQUEST;
+        }
+
+        tmp = masAppParams.get();
+
+        if (V) Log.v(TAG, "type = " + type);
+
+        if (type.equals(TYPE_MESSAGE)) {
+            return pushMsg(op, name);
+        }
+        if (type.equals(TYPE_MESSAGE_STATUS)) {
+            return msgStatus(op, name);
+        }
+        if (type.equals(TYPE_MESSAGE_UPDATE)) {
+            return msgUpdate(op);
+        }
+        if (type.equals(TYPE_MESSAGE_NOTIFICATION)) {
+            if (V) Log.v(TAG, "entered TYPE_MESSAGE_NOTIFICATION");
+            // Following section of code ensures if the Body/EOB
+            // payload is not present in the same Obex packet
+            // Containing headers, but is pushed in the continuation
+            // packet, then we take the corresponding action only
+            // after reading the complete obex packet.
+            // And as the Body / EOB payload contains dummy body as
+            // '0' [0x30 (48)], hence we discard the same.
+            if (!(((ServerOperation) op).finalBitSet)) {
+                if (V) Log.v(TAG, "Not the final Obex packet");
+                try {
+                    inputStream = op.openInputStream();
+                    int readLength = -1;
+                    while(true) {
+                        if (V) Log.v(TAG, "Inside while loop: TYPE_MESSAGE_NOTIFICATION");
+                        readLength = inputStream.read(readByte);
+                        if (readLength == -1) {
+                            if (V) Log.v(TAG, "Complete Obex packet read, Proceeding");
+                            break;
+                        } else {
+                            if (V) Log.v(TAG, "readLength: " + readLength);
+                            if (V) Log.v(TAG, "readByte[0]: " + readByte[0]);
+                            // Compare first byte to check if '0' is received as Body/ EOB
+                            // And Length of the Body Payload is 1, If not, print Error
+                            if ((readByte[0] == 0x30) && (readLength == 1)) {
+                                if (V) Log.v(TAG, "Body / EOB contains '0'");
+                            } else {
+                                Log.e(TAG, "Body / EOB does not contain '0'");
+                            }
+                        }
+                    }
+                } catch (IOException ioException) {
+                    Log.e(TAG, "Error while opening InputStream");
+                } finally {
+                    try {
+                        if (inputStream != null) {
+                            inputStream.close();
+                        }
+                    } catch (IOException ioException) {
+                        Log.e(TAG, "Error when closing stream");
+                    }
+                }
+            }
+            return notification(op);
+        }
+        if (V) Log.v(TAG, "put returns HTTP_BAD_REQUEST");
+        return ResponseCodes.OBEX_HTTP_BAD_REQUEST;
+
+    }
+
+    /**
+    */
+    private final int pushHeader(final Operation op, final HeaderSet reply) {
+
+        if (D) Log.d(TAG, "Push Header");
+        if (D) Log.d(TAG, reply.toString());
+
+        int pushResult = ResponseCodes.OBEX_HTTP_OK;
+        try {
+            op.sendHeaders(reply);
+        } catch (IOException e) {
+            Log.e(TAG, e.toString());
+            pushResult = ResponseCodes.OBEX_HTTP_INTERNAL_ERROR;
+        } catch (IllegalArgumentException e) {
+            Log.e(TAG, e.toString());
+            pushResult = ResponseCodes.OBEX_HTTP_BAD_REQUEST;
+        }
+        if (D) Log.d(TAG, "Push Header: Exit : RetVal " + pushResult);
+        return pushResult;
+    }
+
+    /** Function to send folder data to client */
+    private final int sendFolderListingBody(Operation op,
+            final String folderlistString) {
+
+        if (folderlistString == null) {
+            Log.e(TAG, "folderlistString is null!");
+            return ResponseCodes.OBEX_HTTP_OK;
+        }
+
+        int folderlistStringLen = folderlistString.length();
+        if (D) Log.d(TAG, "Send Folder Listing Body: len=" + folderlistStringLen);
+
+        OutputStream outputStream = null;
+        int pushResult = ResponseCodes.OBEX_HTTP_OK;
+        try {
+            outputStream = op.openOutputStream();
+        } catch (IOException e) {
+            Log.e(TAG, "open outputstrem failed" + e.toString());
+            return ResponseCodes.OBEX_HTTP_UNAVAILABLE;
+        }
+
+        int position = 0;
+        long timestamp = 0;
+        int outputBufferSize = op.getMaxPacketSize();
+        if (V) Log.v(TAG, "outputBufferSize = " + outputBufferSize);
+        while (position != folderlistStringLen) {
+            if (sIsAborted) {
+                ((ServerOperation) op).isAborted = true;
+                sIsAborted = false;
+                break;
+            }
+            if (V) timestamp = System.currentTimeMillis();
+            int readLength = outputBufferSize;
+            if (folderlistStringLen - position < outputBufferSize) {
+                readLength = folderlistStringLen - position;
+            }
+            String subStr = folderlistString.substring(position, position + readLength);
+            try {
+                outputStream.write(subStr.getBytes(), 0, readLength);
+            } catch (IOException e) {
+                Log.e(TAG, "write outputstream failed" + e.toString());
+                pushResult = ResponseCodes.OBEX_HTTP_UNAVAILABLE;
+                break;
+            }
+            if (V) {
+                Log.d(TAG, "Sending folderlist String position = " + position
+                        + " readLength " + readLength + " bytes took "
+                        + (System.currentTimeMillis() - timestamp) + " ms");
+            }
+            position += readLength;
+        }
+
+        if (V) Log.v(TAG, "Send Data complete!");
+
+        if (!closeStream(outputStream, op)) {
+            Log.e(TAG,"Send Folder Listing Body - Close output stream error! ");
+            pushResult = ResponseCodes.OBEX_HTTP_UNAVAILABLE;
+        }
+        if (V) Log.v(TAG, "Send Folder Listing Body complete! result = " + pushResult);
+        return pushResult;
+    }
+
+    private final int sendBody(Operation op, File fileinfo) {
+        if (V) Log.v(TAG, "sendFile = " + fileinfo.getName());
+        int position = 0;
+        int readLength = 0;
+        int outputBufferSize = op.getMaxPacketSize();
+        long timestamp = 0;
+        FileInputStream fileInputStream = null;
+        OutputStream outputStream;
+        BufferedInputStream bis;
+
+        if (D) Log.d(TAG, "Send Body: Enter");
+        try {
+            byte[] buffer = new byte[outputBufferSize];
+            fileInputStream = new FileInputStream(fileinfo);
+            outputStream = op.openOutputStream();
+            bis = new BufferedInputStream(fileInputStream, 0x4000);
+            while ((position != fileinfo.length())) {
+                timestamp = System.currentTimeMillis();
+                if (position != fileinfo.length()) {
+                    readLength = bis.read(buffer, 0, outputBufferSize);
+                }
+                outputStream.write(buffer, 0, readLength);
+                position += readLength;
+                if (V) {
+                    Log.v(TAG, "Sending file position = " + position
+                            + " readLength " + readLength + " bytes took "
+                            + (System.currentTimeMillis() - timestamp) + " ms");
+                }
+            }
+        } catch (IOException e) {
+            return ResponseCodes.OBEX_HTTP_BAD_REQUEST;
+        } finally {
+            if (fileInputStream != null) {
+                try {
+                    fileInputStream.close();
+                } catch (IOException ei) {
+                    Log.e(TAG, "Error while closing stream"+ ei.toString());
+                }
+            }
+        }
+        if (position == fileinfo.length()) {
+            if (D) Log.d(TAG, "SendBody : Exit: OK");
+            return ResponseCodes.OBEX_HTTP_OK;
+        }
+        else {
+            if (D) Log.d(TAG, "SendBody : Exit: CONTINUE");
+            return ResponseCodes.OBEX_HTTP_CONTINUE;
+        }
+    }
+
+    /** Send a bMessage to client */
+    private final int sendMsg(Operation op, String name) {
+        BluetoothMasMessageRsp msg = new BluetoothMasMessageRsp();
+        byte[] val = new byte[1];
+
+        if (D) Log.d(TAG, "SendMsg : Enter");
+        msg = mAppIf.msg(name, masAppParams.get());
+        if(msg == null || msg.rsp != ResponseCodes.OBEX_HTTP_OK) {
+            return msg.rsp;
+        }
+
+        if(masAppParams.get().FractionRequest == 1){
+            HeaderSet reply;
+            val[0] = msg.fractionDeliver;
+            ApplicationParameter ap = new ApplicationParameter();
+            ap.addAPPHeader(
+                (byte) BluetoothMasSpecParams.MAS_TAG_FRACTION_DELIVER,
+                (byte) BluetoothMasSpecParams.MAS_TAG_FRACTION_DELIVER_LEN,
+                 val);
+
+            reply = new HeaderSet();
+            reply.setHeader(HeaderSet.APPLICATION_PARAMETER, ap.getAPPparam());
+
+            int retVal;
+            retVal = pushHeader(op, reply);
+            if (retVal != ResponseCodes.OBEX_HTTP_OK) {
+                if (D) Log.d(TAG, "SendMsg : FAILED: RetVal " + retVal);
+                 return retVal;
+            }
+        }
+        if (D) Log.d(TAG, "SendMsg : SUCCESS");
+        return sendBody(op, msg.file);
+    }
+
+    /** Send an XML format String to client for Folder listing */
+    private final int sendFolderListing(Operation op) {
+        int folderListSize = 0;
+        if (D) Log.d(TAG, "SendFolderListing : Enter");
+        folderListSize = mAppIf.folderListingSize();
+        byte[] size = new byte[2];
+        size[0] = (byte) ((folderListSize / 0x100) & 0xff);
+        size[1] = (byte) ((folderListSize % 0x100) & 0xff);
+
+        HeaderSet reply;
+        ApplicationParameter ap = new ApplicationParameter();
+        ap.addAPPHeader(
+            (byte) BluetoothMasSpecParams.MAS_TAG_FOLDER_LISTING_SIZE,
+            (byte) BluetoothMasSpecParams.MAS_TAG_FOLDER_LISTING_SIZE_LEN,
+            size);
+        reply = new HeaderSet();
+        reply.setHeader(HeaderSet.APPLICATION_PARAMETER, ap.getAPPparam());
+
+        if (!masAppParams.isMaxListCountZero()) {
+            int retVal;
+            retVal = pushHeader(op, reply);
+            if (retVal != ResponseCodes.OBEX_HTTP_OK) {
+                if (D) Log.d(TAG, "SendFolderListing : FAILED : RetVal" + retVal);
+                return retVal;
+            }
+            return sendFolderListingBody(op, mAppIf.folderListing(masAppParams.get()));
+        } else {
+            op.noEndofBody();
+            return pushHeader(op, reply);
+        }
+    }
+
+    /** Send an XML format String to client for Message listing */
+    private final int sendMsgListing(Operation op, String name) {
+
+        byte[] val = new byte[2];
+        BluetoothMasMessageListingRsp appIfMsgListRsp = new BluetoothMasMessageListingRsp();
+        if (D) Log.d(TAG, "SendMsgListing : Enter");
+        appIfMsgListRsp = mAppIf.msgListing(name, masAppParams.get());
+
+        if(appIfMsgListRsp == null || appIfMsgListRsp.rsp != ResponseCodes.OBEX_HTTP_OK) {
+            return appIfMsgListRsp.rsp;
+        }
+
+        Time time = new Time();
+        time.setToNow();
+
+        String time3339 = time.format3339(false);
+        int timeStrLength = time3339.length();
+
+        String datetimeStr = time.toString().substring(0, 15) +
+                time3339.substring(timeStrLength - 6, timeStrLength - 3) +
+                time3339.substring(timeStrLength - 2, timeStrLength);
+
+        byte[] MSETime = datetimeStr.getBytes();
+
+        HeaderSet reply;
+        ApplicationParameter ap = new ApplicationParameter();
+        ap.addAPPHeader((byte) BluetoothMasSpecParams.MAS_TAG_MSE_TIME,
+                (byte) BluetoothMasSpecParams.MAS_TAG_MSE_TIME_LEN, MSETime);
+        val[0] = appIfMsgListRsp.newMessage;
+        ap.addAPPHeader((byte) BluetoothMasSpecParams.MAS_TAG_NEW_MESSAGE,
+                (byte) BluetoothMasSpecParams.MAS_TAG_NEW_MESSAGE_LEN, val);
+
+        val[0] = (byte) ((appIfMsgListRsp.msgListingSize / 0x100) & 0xff);
+        val[1] = (byte) ((appIfMsgListRsp.msgListingSize % 0x100) & 0xff);
+
+        ap.addAPPHeader(
+                (byte) BluetoothMasSpecParams.MAS_TAG_MESSAGE_LISTING_SIZE,
+                (byte) BluetoothMasSpecParams.MAS_TAG_MESSAGE_LISTING_SIZE_LEN,
+                val);
+
+        reply = new HeaderSet();
+        reply.setHeader(HeaderSet.APPLICATION_PARAMETER, ap.getAPPparam());
+
+        if (!masAppParams.isMaxListCountZero()) {
+            int retVal;
+            retVal = pushHeader(op, reply);
+            if (retVal != ResponseCodes.OBEX_HTTP_OK) {
+                if (D) Log.d(TAG, "SendMsgListing : Failed : RetVal " + retVal);
+                return retVal;
+            }
+            return sendBody(op, appIfMsgListRsp.file);
+        } else {
+            return pushHeader(op, reply);
+        }
+    }
+
+    public static boolean closeStream(final OutputStream out, final Operation op) {
+        boolean returnvalue = true;
+        try {
+            if (out != null) {
+                out.close();
+            }
+        } catch (IOException e) {
+            Log.e(TAG, "outputStream close failed" + e.toString());
+            returnvalue = false;
+        }
+        try {
+            if (op != null) {
+                op.close();
+            }
+        } catch (IOException e) {
+            Log.e(TAG, "operation close failed" + e.toString());
+            returnvalue = false;
+        }
+        return returnvalue;
+    }
+
+    private void acquireMasLock() {
+        if (V) Log.v(TAG, "About to acquire Mas:mWakeLock");
+        if (mWakeLock == null) {
+            PowerManager pm = (PowerManager) mContext.getSystemService(Context.POWER_SERVICE);
+            mWakeLock = pm.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, "MasPartialWakeLock");
+            mWakeLock.setReferenceCounted(false);
+            mWakeLock.acquire();
+            if (V) Log.v(TAG, "Mas:mWakeLock acquired");
+        }
+        else
+        {
+            Log.e(TAG, "Mas:mWakeLock already acquired");
+        }
+    }
+
+    private void releaseMasLock() {
+        if (V) Log.v(TAG, "About to release Mas:mWakeLock");
+        if (mWakeLock != null) {
+            if (mWakeLock.isHeld()) {
+                mWakeLock.release();
+                if (V) Log.v(TAG, "Mas:mWakeLock released");
+            } else {
+                if (V) Log.v(TAG, "Mas:mWakeLock already released");
+            }
+            mWakeLock = null;
+        }
+    }
+};
diff --git a/src/org/codeaurora/bluetooth/map/BluetoothMasReceiver.java b/src/org/codeaurora/bluetooth/map/BluetoothMasReceiver.java
new file mode 100644
index 0000000..ce29570
--- /dev/null
+++ b/src/org/codeaurora/bluetooth/map/BluetoothMasReceiver.java
@@ -0,0 +1,87 @@
+/*
+ * Copyright (c) 2008-2009, Motorola, Inc.
+ * Copyright (c) 2010-2011, The Linux Foundation. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *        * Redistributions of source code must retain the above copyright
+ *          notice, this list of conditions and the following disclaimer.
+ *        * Redistributions in binary form must reproduce the above copyright
+ *          notice, this list of conditions and the following disclaimer in the
+ *          documentation and/or other materials provided with the distribution.
+ *        * Neither the name of The Linux Foundation nor
+ *          the names of its contributors may be used to endorse or promote
+ *          products derived from this software without specific prior written
+ *          permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NON-INFRINGEMENT ARE DISCLAIMED.    IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
+ * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
+ * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
+ * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package org.codeaurora.bluetooth.map;
+
+import android.bluetooth.BluetoothAdapter;
+import android.content.BroadcastReceiver;
+import android.content.Context;
+import android.content.Intent;
+import android.util.Log;
+
+public class BluetoothMasReceiver extends BroadcastReceiver {
+    private static final String TAG = "BluetoothMasReceiver";
+    private static final boolean V = BluetoothMasService.VERBOSE;
+
+    @Override
+    public void onReceive(Context context, Intent intent) {
+        if (V) Log.v(TAG, "BluetoothMasReceiver onReceive :" + intent.getAction());
+
+        Intent in = new Intent();
+        in.putExtras(intent);
+        in.setClass(context, BluetoothMasService.class);
+        String action = intent.getAction();
+        in.putExtra("action", action);
+        boolean startService = true;
+        if (BluetoothAdapter.ACTION_STATE_CHANGED.equals(action)) {
+            int state = in.getIntExtra(BluetoothAdapter.EXTRA_STATE,
+                    BluetoothAdapter.ERROR);
+            in.putExtra(BluetoothAdapter.EXTRA_STATE, state);
+            /*
+             * Other than Tranistioning state, start the MAP service whenever BT
+             * transitioned to OFF/ON, or Adapter returns error
+             */
+            Log.d(TAG, "Bluetooth STATE CHANGED to " + state);
+
+            if ((state == BluetoothAdapter.STATE_TURNING_ON)
+                    || (state == BluetoothAdapter.STATE_TURNING_OFF)) {
+                startService = false;
+            }
+            if (state == BluetoothAdapter.ERROR) {
+                Log.d(TAG, " BluetoothAdapter returns ERROR");
+            }
+
+            if ((state == BluetoothAdapter.STATE_OFF)) {
+
+                startService = false;
+                // Stop MAS service
+                context.stopService(in);
+
+                // TODO - Stop MNS service?
+            }
+
+        } else if (action.equals(Intent.ACTION_MEDIA_EJECT)
+                || action.equals(Intent.ACTION_MEDIA_MOUNTED))
+            startService = true;
+
+        if (startService) {
+            context.startService(in);
+        }
+    }
+}
diff --git a/src/org/codeaurora/bluetooth/map/BluetoothMasService.java b/src/org/codeaurora/bluetooth/map/BluetoothMasService.java
new file mode 100644
index 0000000..e53c2f3
--- /dev/null
+++ b/src/org/codeaurora/bluetooth/map/BluetoothMasService.java
@@ -0,0 +1,982 @@
+/*
+ * Copyright (c) 2008-2009, Motorola, Inc. All rights reserved.
+ * Copyright (c) 2010-2013, The Linux Foundation. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *        * Redistributions of source code must retain the above copyright
+ *          notice, this list of conditions and the following disclaimer.
+ *        * Redistributions in binary form must reproduce the above copyright
+ *          notice, this list of conditions and the following disclaimer in the
+ *          documentation and/or other materials provided with the distribution.
+ *        * Neither the name of The Linux Foundation nor
+ *          the names of its contributors may be used to endorse or promote
+ *          products derived from this software without specific prior written
+ *          permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NON-INFRINGEMENT ARE DISCLAIMED.    IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
+ * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
+ * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
+ * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package org.codeaurora.bluetooth.map;
+
+import android.app.Notification;
+import android.app.NotificationManager;
+import android.app.PendingIntent;
+import android.app.Service;
+import android.bluetooth.BluetoothAdapter;
+import android.bluetooth.BluetoothDevice;
+import android.bluetooth.BluetoothServerSocket;
+import android.bluetooth.BluetoothSocket;
+import android.content.Context;
+import android.content.Intent;
+import android.database.ContentObserver;
+import android.os.Handler;
+import android.os.IBinder;
+import android.os.Message;
+import android.text.TextUtils;
+import android.os.ParcelUuid;
+import android.util.Log;
+import android.bluetooth.BluetoothUuid;
+
+import org.codeaurora.bluetooth.R;
+import org.codeaurora.bluetooth.map.BluetoothMns.MnsClient;
+import org.codeaurora.bluetooth.map.MapUtils.EmailUtils;
+
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.HashSet;
+import java.io.FileInputStream;
+import java.io.DataInputStream;
+import java.io.BufferedReader;
+import java.io.InputStreamReader;
+import java.io.FileNotFoundException;
+import javax.obex.ServerSession;
+
+import static org.codeaurora.bluetooth.map.IBluetoothMasApp.MESSAGE_TYPE_EMAIL;
+import static org.codeaurora.bluetooth.map.IBluetoothMasApp.MESSAGE_TYPE_MMS;
+import static org.codeaurora.bluetooth.map.IBluetoothMasApp.MESSAGE_TYPE_SMS;
+import static org.codeaurora.bluetooth.map.IBluetoothMasApp.MESSAGE_TYPE_SMS_MMS;
+
+public class BluetoothMasService extends Service {
+    private static final String TAG = "BluetoothMasService";
+
+    /**
+     * To enable MAP DEBUG/VERBOSE logging - run below cmd in adb shell, and
+     * restart com.android.bluetooth process. only enable DEBUG log:
+     * "setprop log.tag.BluetoothMapService DEBUG"; enable both VERBOSE and
+     * DEBUG log: "setprop log.tag.BluetoothMapService VERBOSE"
+     */
+    public static final boolean DEBUG = true;
+    public static final boolean VERBOSE = true;
+  //  public static final boolean DEBUG = false;
+    //public static final boolean VERBOSE = false;
+
+    /**
+     * Intent indicating incoming connection request which is sent to
+     * BluetoothMasActivity
+     */
+    public static final String ACCESS_REQUEST_ACTION = "org.codeaurora.bluetooth.map.accessrequest";
+
+    /**
+     * Intent indicating incoming connection request accepted by user which is
+     * sent from BluetoothMasActivity
+     */
+    public static final String ACCESS_ALLOWED_ACTION = "org.codeaurora.bluetooth.map.accessallowed";
+
+    /**
+     * Intent indicating incoming connection request denied by user which is
+     * sent from BluetoothMasActivity
+     */
+    public static final String ACCESS_DISALLOWED_ACTION = "org.codeaurora.bluetooth.map.accessdisallowed";
+
+    /**
+     * Intent indicating incoming obex authentication request which is from
+     * PCE(Carkit)
+     */
+    public static final String AUTH_CHALL_ACTION = "org.codeaurora.bluetooth.map.authchall";
+
+    /**
+     * Intent indicating obex session key input complete by user which is sent
+     * from BluetoothMasActivity
+     */
+    public static final String AUTH_RESPONSE_ACTION = "org.codeaurora.bluetooth.map.authresponse";
+
+    /**
+     * Intent indicating user canceled obex authentication session key input
+     * which is sent from BluetoothMasActivity
+     */
+    public static final String AUTH_CANCELLED_ACTION = "org.codeaurora.bluetooth.map.authcancelled";
+
+    /**
+     * Intent indicating timeout for user confirmation, which is sent to
+     * BluetoothMasActivity
+     */
+    public static final String USER_CONFIRM_TIMEOUT_ACTION = "org.codeaurora.bluetooth.map.userconfirmtimeout";
+
+    public static final String THIS_PACKAGE_NAME = "org.codeaurora.bluetooth";
+
+    /**
+     * Intent Extra name indicating always allowed which is sent from
+     * BluetoothMasActivity
+     */
+    public static final String EXTRA_ALWAYS_ALLOWED = "org.codeaurora.bluetooth.map.alwaysallowed";
+
+    /**
+     * Intent Extra name indicating session key which is sent from
+     * BluetoothMasActivity
+     */
+    public static final String EXTRA_SESSION_KEY = "org.codeaurora.bluetooth.map.sessionkey";
+
+    /**
+     * Intent Extra name indicating BluetoothDevice which is sent to
+     * BluetoothMasActivity
+     */
+    public static final String EXTRA_BLUETOOTH_DEVICE = "org.codeaurora.bluetooth.map.bluetoothdevice";
+
+    private static final String BLUETOOTH_PERM = android.Manifest.permission.BLUETOOTH;
+
+    private static final String BLUETOOTH_ADMIN_PERM = android.Manifest.permission.BLUETOOTH_ADMIN;
+
+    public static final int MSG_SERVERSESSION_CLOSE = 5004;
+
+    public static final int MSG_SESSION_ESTABLISHED = 5005;
+
+    public static final int MSG_SESSION_DISCONNECTED = 5006;
+
+    public static final int MSG_OBEX_AUTH_CHALL = 5007;
+
+    private static final int MSG_INTERNAL_START_LISTENER = 1;
+
+    private static final int MSG_INTERNAL_USER_TIMEOUT = 2;
+
+    private static final int USER_CONFIRM_TIMEOUT_VALUE = 30000;
+
+    public static final int MAS0_PORT_NUM = 16;
+
+    public static final int MAS1_PORT_NUM = 17;
+
+    public static final ParcelUuid MessageAccessServer =
+            ParcelUuid.fromString("00001132-0000-1000-8000-00805f9b34fb");
+
+    public static final ParcelUuid MessageNotificationServer =
+            ParcelUuid.fromString("00001133-0000-1000-8000-00805f9b34fb");
+
+    // Ensure not conflict with Opp notification ID
+    private static final int NOTIFICATION_ID_ACCESS = -1000005;
+
+    private BluetoothAdapter mAdapter;
+
+    private Object mAuthSync = new Object();
+
+    private BluetoothMapAuthenticator mAuth = null;
+
+    BluetoothMasObexConnectionManager mConnectionManager = null;
+
+    BluetoothMns mnsClient;
+    private static BluetoothDevice mRemoteDevice = null;
+    private static HashSet<BluetoothDevice> trustDevices = new HashSet<BluetoothDevice>();
+
+    private boolean mHasStarted = false;
+    private int mStartId = -1;
+
+    private boolean mIsEmailEnabled = true;
+
+    /**
+     * The flag indicating MAP request has been notified.
+     * This is set on when initiate notification and set off after accept/time out
+     */
+    private volatile boolean mIsRequestBeingNotified = false;
+
+
+    public static class MasInstanceInfo {
+        int mSupportedMessageTypes;
+        Class<? extends MnsClient> mMnsClientClass;
+        int mRfcommPort;
+
+        public MasInstanceInfo(int smt, Class<? extends MnsClient> _class, int port) {
+            mSupportedMessageTypes = smt;
+            mMnsClientClass = _class;
+            mRfcommPort = port;
+        }
+    }
+
+    public static final int MAX_INSTANCES = 2;
+    public static final int EMAIL_MAS_START = 1;
+    public static final int EMAIL_MAS_END = 1;
+    public static final MasInstanceInfo MAS_INS_INFO[] = new MasInstanceInfo[MAX_INSTANCES];
+
+    // The following information must match with corresponding
+    // SDP records supported message types and port number
+    // Please refer sdptool.c, BluetoothService.java, & init.qcom.rc
+    static {
+        MAS_INS_INFO[0] = new MasInstanceInfo(MESSAGE_TYPE_SMS_MMS, BluetoothMnsSmsMms.class, MAS0_PORT_NUM);
+        MAS_INS_INFO[1] = new MasInstanceInfo(MESSAGE_TYPE_EMAIL, BluetoothMnsEmail.class, MAS1_PORT_NUM);
+    }
+
+    private ContentObserver mEmailAccountObserver;
+
+    private static final String CONF_FILE_PATH =
+            "/etc/bluetooth/main.conf";
+
+    public void CheckEmailEnabled() {
+        if (VERBOSE)
+            Log.v(TAG, " CheckEmailEnabled: Loading from conf");
+        FileInputStream fstream = null;
+        try {
+            fstream = new FileInputStream(CONF_FILE_PATH);
+            DataInputStream in = new DataInputStream(fstream);
+            BufferedReader file = new BufferedReader(new InputStreamReader(in));
+            String line;
+            while((line = file.readLine()) != null) {
+                line = line.trim();
+                if (line.length() == 0 || line.startsWith("#")) continue;
+                String[] value = line.split(" = ");
+                if (value != null && value.length == 2) {
+                    if (value[0].equalsIgnoreCase("BluetoothMapEmailEnabled")) {
+                        if (value[1].equalsIgnoreCase("false")) {
+                            mIsEmailEnabled = false;
+                        }
+                        else {
+                            mIsEmailEnabled = true;
+                        }
+                        Log.v(TAG, "CheckEmailEnabled: IsEmailEnabled: " + mIsEmailEnabled);
+                    }
+                }
+            }
+        } catch (FileNotFoundException e) {
+            Log.e(TAG, "Main.conf File Not found");
+        } catch (IOException e) {
+            Log.e(TAG, "IOException: read Main.conf File " + e);
+        } finally {
+            if (fstream != null) {
+                try {
+                    fstream.close();
+                } catch (IOException e) {
+                }
+            }
+        }
+    }
+
+    private void updateEmailAccount() {
+        if (VERBOSE) Log.v(TAG, "updateEmailAccount()");
+        List<Long> list = EmailUtils.getEmailAccountIdList(this);
+        ArrayList<Long> notAssigned = new ArrayList<Long>();
+        EmailUtils.removeMasIdIfNotPresent(list);
+        for (Long id : list) {
+            int masId = EmailUtils.getMasId(id);
+            if (masId == -1) {
+                notAssigned.add(id);
+            }
+        }
+        for (int i = EMAIL_MAS_START; i <= EMAIL_MAS_END; i ++) {
+            long accountId = EmailUtils.getAccountId(i);
+            if (accountId == -1 && notAssigned.size() > 0) {
+                EmailUtils.updateMapTable(notAssigned.remove(0), i);
+            }
+        }
+    }
+
+    public BluetoothMasService() {
+        CheckEmailEnabled();
+        mConnectionManager = new BluetoothMasObexConnectionManager();
+        if (VERBOSE)
+           Log.v(TAG, "BluetoothMasService: mIsEmailEnabled: " + mIsEmailEnabled);
+        if(mIsEmailEnabled) {
+            mEmailAccountObserver = new ContentObserver(null) {
+                @Override
+                public void onChange(boolean selfChange) {
+                    updateEmailAccount();
+                }
+            };
+        }
+    }
+
+    @Override
+    public void onCreate() {
+        super.onCreate();
+        if (VERBOSE)
+            Log.v(TAG, "Map Service onCreate");
+
+        mConnectionManager.init();
+        mAdapter = BluetoothAdapter.getDefaultAdapter();
+
+        if (!mHasStarted) {
+            mHasStarted = true;
+            if (VERBOSE) Log.v(TAG, "Starting MAS instances");
+
+            int state = mAdapter.getState();
+            if (state == BluetoothAdapter.STATE_ON) {
+                mSessionStatusHandler.sendEmptyMessage(MSG_INTERNAL_START_LISTENER);
+            } else if (VERBOSE) {
+                Log.v(TAG, "BT is not ON, no start");
+            }
+        }
+        Log.v(TAG, "onCreate: mIsEmailEnabled: " + mIsEmailEnabled);
+        if(mIsEmailEnabled) {
+            getContentResolver().registerContentObserver(
+                EmailUtils.EMAIL_ACCOUNT_URI, true, mEmailAccountObserver);
+        }
+    }
+
+    @Override
+    public int onStartCommand(Intent intent, int flags, int startId) {
+        if (VERBOSE)
+            Log.v(TAG, "Map Service onStartCommand");
+        int retCode = super.onStartCommand(intent, flags, startId);
+        if (retCode == START_STICKY) {
+            mStartId = startId;
+            if (mAdapter == null) {
+                Log.w(TAG, "Stopping BluetoothMasService: "
+                        + "device does not have BT or device is not ready");
+                // Release all resources
+                closeService();
+            } else {
+                // No need to handle the null intent case, because we have
+                // all restart work done in onCreate()
+                if (intent != null) {
+                    parseIntent(intent);
+                }
+            }
+        }
+        return retCode;
+    }
+
+    // process the intent from receiver
+    private void parseIntent(final Intent intent) {
+        String action = intent.getStringExtra("action");
+        if (VERBOSE)
+            Log.v(TAG, "action: " + action);
+
+        int state = intent.getIntExtra(BluetoothAdapter.EXTRA_STATE,
+                BluetoothAdapter.ERROR);
+        boolean removeTimeoutMsg = true;
+        if (BluetoothAdapter.ACTION_STATE_CHANGED.equals(action)) {
+            if (state == BluetoothAdapter.STATE_TURNING_OFF) {
+                // Release all resources
+                closeService();
+            } else {
+                removeTimeoutMsg = false;
+                Log.v(TAG, "parseIntent 1: mIsEmailEnabled: " + mIsEmailEnabled);
+                if(mIsEmailEnabled) {
+                    if (state == BluetoothAdapter.STATE_ON) {
+                        updateEmailAccount();
+                    }
+                }
+            }
+        } else if (action.equals(BluetoothDevice.ACTION_ACL_DISCONNECTED) &&
+                                 mIsRequestBeingNotified) {
+            if (mRemoteDevice == null) {
+               Log.e(TAG, "Unexpected error!");
+               return;
+            }
+            if (mSessionStatusHandler != null) {
+               /* Let the user timeout handle this case as well */
+               mSessionStatusHandler.sendMessage(mSessionStatusHandler
+                   .obtainMessage(MSG_INTERNAL_USER_TIMEOUT));
+               removeTimeoutMsg = false;
+            }
+        } else if (action.equals(ACCESS_ALLOWED_ACTION)) {
+            if (mRemoteDevice == null) {
+               Log.e(TAG, "Unexpected error!");
+               return;
+            }
+            if (intent.getBooleanExtra(BluetoothMasService.EXTRA_ALWAYS_ALLOWED, false) == true) {
+                   trustDevices.add(mRemoteDevice);
+                  Log.v(TAG, "setTrust() TRUE " + mRemoteDevice.getName());
+            }
+            Log.v(TAG, "parseIntent 2: mIsEmailEnabled: " + mIsEmailEnabled);
+            if(mIsEmailEnabled) {
+                  updateEmailAccount();
+            }
+            mConnectionManager.initiateObexServerSession(mRemoteDevice);
+        }  else if (action.equals(ACCESS_DISALLOWED_ACTION)) {
+            mIsRequestBeingNotified = false;
+            mConnectionManager.stopObexServerSessionWaiting();
+        } else if (AUTH_RESPONSE_ACTION.equals(action)) {
+            String sessionkey = intent.getStringExtra(EXTRA_SESSION_KEY);
+            notifyAuthKeyInput(sessionkey);
+        } else if (AUTH_CANCELLED_ACTION.equals(action)) {
+            notifyAuthCancelled();
+        } else if ( BluetoothDevice.ACTION_BOND_STATE_CHANGED.equals(action)) {
+
+            if (intent.hasExtra(BluetoothDevice.EXTRA_DEVICE)) {
+               BluetoothDevice device = intent.getParcelableExtra(BluetoothDevice.EXTRA_DEVICE);
+                if(device != null)
+                    Log.d(TAG,"device: "+ device.getName());
+                if(mRemoteDevice != null)
+                    Log.d(TAG," Remtedevie: "+mRemoteDevice.getName());
+               if (device != null && trustDevices.contains(device) &&
+                     intent.getIntExtra(BluetoothDevice.EXTRA_BOND_STATE, BluetoothDevice.BOND_NONE) == BluetoothDevice.BOND_NONE) {
+                   Log.d(TAG,"BOND_STATE_CHANGED REFRESH trustDevices"+ device.getName());
+                   trustDevices.remove(device);
+               }
+            }
+
+        } else {
+            removeTimeoutMsg = false;
+        }
+
+        if (removeTimeoutMsg) {
+            mSessionStatusHandler.removeMessages(MSG_INTERNAL_USER_TIMEOUT);
+            if (VERBOSE) Log.v(TAG, "MAS access request notification flag off");
+            mIsRequestBeingNotified = false;
+        }
+    }
+
+    @Override
+    public void onDestroy() {
+        if (VERBOSE)
+            Log.v(TAG, "Map Service onDestroy");
+
+        super.onDestroy();
+        Log.v(TAG, "onDestroy: mIsEmailEnabled: " + mIsEmailEnabled);
+        if(mIsEmailEnabled) {
+            getContentResolver().unregisterContentObserver(mEmailAccountObserver);
+            EmailUtils.clearMapTable();
+        }
+        closeService();
+    }
+
+    private final void closeService() {
+        if (VERBOSE) Log.v(TAG, "MNS_BT: inside closeService");
+        try {
+            if(mnsClient!=null) {
+                if (VERBOSE) Log.v(TAG, "MNS_BT: about to send MNS_BLUETOOTH_OFF");
+                mnsClient.getHandler().sendEmptyMessage(BluetoothMns.MNS_BLUETOOTH_OFF);
+                mnsClient = null;
+            }
+        } catch (Exception e) {
+            Log.e(TAG, "MNS_BT: exception while sending MNS_BLUETOOTH_OFF");
+        } finally {
+            if (VERBOSE) Log.v(TAG, "MNS_BT: successfully sent MNS_BLUETOOTH_OFF");
+            mConnectionManager.closeAll();
+            mHasStarted = false;
+            if (stopSelfResult(mStartId)) {
+                if (VERBOSE) Log.v(TAG, "successfully stopped map service");
+            }
+        }
+    }
+
+    @Override
+    public IBinder onBind(Intent intent) {
+        if (VERBOSE) Log.v(TAG, "Map Service onBind");
+        return null;
+    }
+
+    private final Handler mSessionStatusHandler = new Handler() {
+        @Override
+        public void handleMessage(Message msg) {
+            if (VERBOSE) Log.v(TAG, "Handler(): got msg=" + msg.what);
+            Context context = getApplicationContext();
+            if (mnsClient == null) {
+                Log.v(TAG, "handleMessage: mIsEmailEnabled" + mIsEmailEnabled);
+                mnsClient = new BluetoothMns(context, mIsEmailEnabled);
+            }
+
+            switch (msg.what) {
+                case MSG_INTERNAL_START_LISTENER:
+                    if (mAdapter.isEnabled()) {
+                        mConnectionManager.startAll();
+                    } else {
+                        closeService();
+                    }
+                    break;
+                case MSG_INTERNAL_USER_TIMEOUT:
+                    Intent intent = new Intent(USER_CONFIRM_TIMEOUT_ACTION);
+                    intent.putExtra(BluetoothDevice.EXTRA_DEVICE, mRemoteDevice);
+                    sendBroadcast(intent);
+                    removeMapNotification(NOTIFICATION_ID_ACCESS);
+                    if (VERBOSE) Log.v(TAG, "MAS access request notification flag off");
+                    mIsRequestBeingNotified = false;
+                    mConnectionManager.stopObexServerSessionWaiting();
+                    break;
+                case MSG_SERVERSESSION_CLOSE:
+                {
+                    final int masId = msg.arg1;
+                    mConnectionManager.stopObexServerSession(masId);
+                    break;
+                }
+                case MSG_SESSION_ESTABLISHED:
+                    break;
+                case MSG_SESSION_DISCONNECTED:
+                    break;
+                default:
+                    break;
+            }
+        }
+    };
+
+    private void createMapNotification(BluetoothDevice device) {
+        if (VERBOSE) Log.v(TAG, "Creating MAS access notification");
+        mIsRequestBeingNotified = true;
+
+        NotificationManager nm = (NotificationManager)
+                getSystemService(Context.NOTIFICATION_SERVICE);
+
+        // Create an intent triggered by clicking on the status icon.
+        Intent clickIntent = new Intent();
+        clickIntent.setClass(this, BluetoothMasActivity.class);
+        clickIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
+        clickIntent.setAction(ACCESS_REQUEST_ACTION);
+        clickIntent.putExtra(EXTRA_BLUETOOTH_DEVICE, device);
+
+        // Create an intent triggered by clicking on the
+        // "Clear All Notifications" button
+        Intent deleteIntent = new Intent();
+        deleteIntent.setClass(this, BluetoothMasReceiver.class);
+
+        Notification notification = null;
+        String name = device.getName();
+        if (TextUtils.isEmpty(name)) {
+            name = getString(R.string.defaultname);
+        }
+
+        deleteIntent.setAction(ACCESS_DISALLOWED_ACTION);
+        notification = new Notification(android.R.drawable.stat_sys_data_bluetooth,
+            getString(R.string.map_notif_ticker), System.currentTimeMillis());
+        notification.setLatestEventInfo(this, getString(R.string.map_notif_ticker),
+                getString(R.string.map_notif_message, name), PendingIntent
+                        .getActivity(this, 0, clickIntent, 0));
+
+        notification.flags |= Notification.FLAG_AUTO_CANCEL;
+        notification.flags |= Notification.FLAG_ONLY_ALERT_ONCE;
+        notification.defaults = Notification.DEFAULT_SOUND;
+        notification.deleteIntent = PendingIntent.getBroadcast(this, 0, deleteIntent, 0);
+        nm.notify(NOTIFICATION_ID_ACCESS, notification);
+
+
+        if (VERBOSE) Log.v(TAG, "Awaiting Authorization : MAS Connection : " + device.getName());
+
+    }
+
+    private void removeMapNotification(int id) {
+        Context context = getApplicationContext();
+        NotificationManager nm = (NotificationManager) context
+                .getSystemService(Context.NOTIFICATION_SERVICE);
+        nm.cancel(id);
+    }
+
+    private void notifyAuthKeyInput(final String key) {
+        synchronized (mAuthSync) {
+            if (key != null) {
+                mAuth.setSessionKey(key);
+            }
+            mAuth.setChallenged(true);
+            mAuth.notify();
+        }
+    }
+
+    private void notifyAuthCancelled() {
+        synchronized (mAuthSync) {
+            mAuth.setCancelled(true);
+            mAuth.notify();
+        }
+    }
+
+    class BluetoothMasObexConnectionManager {
+        private ArrayList<BluetoothMasObexConnection> mConnections =
+                new ArrayList<BluetoothMasObexConnection>();
+
+        public BluetoothMasObexConnectionManager() {
+            int numberOfSupportedInstances = MAX_INSTANCES;
+            Log.e(TAG, "BluetoothMasObexConnectionManager: mIsEmailEnabled: " + mIsEmailEnabled);
+            if(!mIsEmailEnabled) {
+                numberOfSupportedInstances = 1; /*Email instance not supported*/
+            }
+            for (int i = 0; i < numberOfSupportedInstances; i ++) {
+                mConnections.add(new BluetoothMasObexConnection(
+                        MAS_INS_INFO[i].mSupportedMessageTypes, i, MAS_INS_INFO[i].mRfcommPort));
+            }
+        }
+
+        public void initiateObexServerSession(BluetoothDevice device) {
+            try {
+                for (BluetoothMasObexConnection connection : mConnections) {
+                    if (connection.mConnSocket != null && connection.mWaitingForConfirmation) {
+                        connection.mWaitingForConfirmation = false;
+                        connection.startObexServerSession(device, mnsClient);
+                    }
+                }
+            } catch (IOException ex) {
+                Log.e(TAG, "Caught the error: " + ex.toString());
+            }
+        }
+
+        public void setWaitingForConfirmation(int masId) {
+            if (masId < mConnections.size()) {
+                final BluetoothMasObexConnection connect = mConnections.get(masId);
+                connect.mWaitingForConfirmation = true;
+            } else {
+                Log.e(TAG, "Attempt to set waiting for user confirmation for MAS id: " + masId);
+                Log.e(TAG, "out of index");
+            }
+        }
+
+        public void stopObexServerSession(int masId) {
+            if (masId < mConnections.size()) {
+                final BluetoothMasObexConnection connect = mConnections.get(masId);
+                if (connect.mConnSocket != null) {
+                    connect.stopObexServerSession();
+                } else {
+                    Log.w(TAG, "Attempt to stop OBEX Server session for MAS id: " + masId);
+                    Log.w(TAG, "when there is no connected socket");
+                }
+            } else {
+                Log.e(TAG, "Attempt to stop OBEX Server session for MAS id: " + masId);
+                Log.e(TAG, "out of index");
+            }
+        }
+
+        public void stopObexServerSessionWaiting() {
+            for (BluetoothMasObexConnection connection : mConnections) {
+                if (connection.mConnSocket != null && connection.mWaitingForConfirmation) {
+                    connection.mWaitingForConfirmation = false;
+                    connection.stopObexServerSession();
+                }
+            }
+        }
+
+        public void stopObexServerSessionAll() {
+            for (BluetoothMasObexConnection connection : mConnections) {
+                if (connection.mConnSocket != null) {
+                    connection.stopObexServerSession();
+                }
+            }
+        }
+
+        public void closeAll() {
+            for (BluetoothMasObexConnection connection : mConnections) {
+                // Stop the possible trying to init serverSocket
+                connection.mInterrupted = true;
+                connection.closeConnection();
+            }
+        }
+
+        public void startAll() {
+            for (BluetoothMasObexConnection connection : mConnections) {
+                connection.startRfcommSocketListener(mnsClient);
+            }
+        }
+
+        public void init() {
+            for (BluetoothMasObexConnection connection: mConnections) {
+                connection.mInterrupted = false;
+            }
+        }
+
+        public boolean isAllowedConnection(BluetoothDevice remoteDevice) {
+            String remoteAddress = remoteDevice.getAddress();
+            if (remoteAddress == null) {
+                if (VERBOSE) Log.v(TAG, "Connection request from unknown device");
+                return false;
+            }
+            final int size = mConnections.size();
+            for (int i = 0; i < size; i ++) {
+                final BluetoothMasObexConnection connection = mConnections.get(i);
+                BluetoothSocket socket = connection.mConnSocket;
+                if (socket != null) {
+                    BluetoothDevice device = socket.getRemoteDevice();
+                    if (device != null) {
+                        String address = device.getAddress();
+                        if (address != null) {
+                            if (remoteAddress.equalsIgnoreCase(address)) {
+                                if (VERBOSE) {
+                                    Log.v(TAG, "Connection request from " + remoteAddress);
+                                    Log.v(TAG, "when MAS id:" + i + " is connected to " + address);
+                                }
+                                return true;
+                            } else {
+                                if (VERBOSE) {
+                                    Log.v(TAG, "Connection request from " + remoteAddress);
+                                    Log.v(TAG, "when MAS id:" + i + " is connected to " + address);
+                                }
+                                return false;
+                            }
+                        } else {
+                            // shall not happen, connected device must has address
+                            // just for null pointer dereference
+                            Log.w(TAG, "Connected device has no address!");
+                        }
+                    }
+                }
+            }
+
+            if (VERBOSE) {
+                Log.v(TAG, "Connection request from " + remoteAddress);
+                Log.v(TAG, "when no MAS instance is connected.");
+            }
+            return true;
+        }
+    }
+
+    private class BluetoothMasObexConnection {
+        private volatile boolean mInterrupted;
+        private BluetoothServerSocket mServerSocket = null;
+        private SocketAcceptThread mAcceptThread = null;
+        private BluetoothSocket mConnSocket = null;
+        private ServerSession mServerSession = null;
+        private BluetoothMasObexServer mMapServer = null;
+
+        private int mSupportedMessageTypes;
+        private int mPortNum;
+        private int mMasId;
+        boolean mWaitingForConfirmation = false;
+
+        public BluetoothMasObexConnection(int supportedMessageTypes, int masId, int portNumber) {
+                mSupportedMessageTypes = supportedMessageTypes;
+                mMasId = masId;
+                mPortNum = portNumber;
+        }
+
+        private void startRfcommSocketListener(BluetoothMns mnsClient) {
+            if (VERBOSE)
+                Log.v(TAG, "Map Service startRfcommSocketListener");
+
+            if (mServerSocket == null) {
+                if (!initSocket()) {
+                    closeService();
+                    return;
+                }
+            }
+            if (mAcceptThread == null) {
+                mAcceptThread = new SocketAcceptThread(mnsClient, mMasId);
+                mAcceptThread.setName("BluetoothMapAcceptThread " + mPortNum);
+                mAcceptThread.start();
+            }
+        }
+
+        private final boolean initSocket() {
+            if (VERBOSE)
+                Log.v(TAG, "Map Service initSocket");
+
+            boolean initSocketOK = false;
+            final int CREATE_RETRY_TIME = 10;
+
+            // It's possible that create will fail in some cases. retry for 10 times
+            for (int i = 0; i < CREATE_RETRY_TIME && !mInterrupted; i++) {
+                try {
+                    //mServerSocket = mAdapter.listenUsingRfcommOn(mPortNum);
+                    if(mPortNum == MAS1_PORT_NUM)
+                       mServerSocket  = mAdapter.listenUsingRfcommWithServiceRecord("Email Message Access", MessageAccessServer.getUuid());
+                    else
+                       mServerSocket  = mAdapter.listenUsingRfcommWithServiceRecord("SMS/MMS Message Access", MessageAccessServer.getUuid());
+                    initSocketOK = true;
+                } catch (IOException e) {
+                    Log.e(TAG, "Error create RfcommServerSocket " + e.toString());
+                    initSocketOK = false;
+                }
+
+                if (!initSocketOK) {
+                    synchronized (this) {
+                        try {
+                            if (VERBOSE) Log.v(TAG, "wait 3 seconds");
+                            Thread.sleep(3000);
+                        } catch (InterruptedException e) {
+                            Log.e(TAG, "socketAcceptThread thread was interrupted (3)");
+                            mInterrupted = true;
+                        }
+                    }
+                } else {
+                    break;
+                }
+            }
+
+            if (initSocketOK) {
+                if (VERBOSE)
+                    Log.v(TAG, "Succeed to create listening socket on channel "
+                            + mPortNum);
+            } else {
+                Log.e(TAG, "Error to create listening socket after "
+                        + CREATE_RETRY_TIME + " try");
+            }
+            return initSocketOK;
+        }
+
+        private final void closeSocket() throws IOException {
+            if (mConnSocket != null) {
+                mConnSocket.close();
+                mConnSocket = null;
+            }
+        }
+
+        public void closeConnection() {
+            if (VERBOSE) Log.v(TAG, "Mas connection closing");
+
+            if (mAcceptThread != null) {
+                try {
+                    mAcceptThread.shutdown();
+                    mAcceptThread.join();
+                } catch (InterruptedException ex) {
+                    Log.w(TAG, "mAcceptThread  close error" + ex);
+                } finally {
+                    mAcceptThread = null;
+                }
+            }
+
+            if (mServerSession != null) {
+                mServerSession.close();
+                mServerSession = null;
+            }
+
+            try {
+                closeSocket();
+            } catch (IOException ex) {
+                Log.e(TAG, "CloseSocket error: " + ex);
+            }
+            if (VERBOSE) Log.v(TAG, "Mas connection closed");
+        }
+
+        private final void startObexServerSession(BluetoothDevice device, BluetoothMns mnsClient)
+                        throws IOException {
+            if (VERBOSE)
+                Log.v(TAG, "Map Service startObexServerSession ");
+
+            Context context = getApplicationContext();
+
+            IBluetoothMasApp appIf = null;
+            if (((mSupportedMessageTypes & ~MESSAGE_TYPE_SMS_MMS) == 0x00) &&
+                    ((mSupportedMessageTypes & MESSAGE_TYPE_SMS) != 0x00) &&
+                    ((mSupportedMessageTypes & MESSAGE_TYPE_MMS) != 0x00)) {
+                // BluetoothMasAppZero if and only if both SMS and MMS
+                appIf = new BluetoothMasAppSmsMms(context, mSessionStatusHandler, mnsClient,
+                        mMasId, device.getName());
+            } else if (((mSupportedMessageTypes & ~MESSAGE_TYPE_EMAIL) == 0x0) &&
+                    ((mSupportedMessageTypes & MESSAGE_TYPE_EMAIL) != 0x0)) {
+                // BluetoothMasAppOne if and only if email
+                appIf = new BluetoothMasAppEmail(context, mSessionStatusHandler, mnsClient,
+                        mMasId, device.getName());
+            }
+
+            mMapServer = new BluetoothMasObexServer(mSessionStatusHandler,
+                    device, context, appIf);
+            synchronized (mAuthSync) {
+                mAuth = new BluetoothMapAuthenticator(mSessionStatusHandler);
+                mAuth.setChallenged(false);
+                mAuth.setCancelled(false);
+            }
+            BluetoothMapRfcommTransport transport = new BluetoothMapRfcommTransport(
+                    mConnSocket);
+            mServerSession = new ServerSession(transport, mMapServer, mAuth);
+
+            if (VERBOSE) Log.v(TAG, "startObexServerSession() success!");
+        }
+
+        private void stopObexServerSession() {
+            if (VERBOSE) Log.v(TAG, "Map Service stopObexServerSession ");
+
+            closeConnection();
+
+            // Last obex transaction is finished, we start to listen for incoming
+            // connection again
+            if (mAdapter.isEnabled()) {
+                startRfcommSocketListener(mnsClient);
+            }
+        }
+
+        /**
+         * A thread that runs in the background waiting for remote rfcomm
+         * connect.Once a remote socket connected, this thread shall be
+         * shutdown.When the remote disconnect,this thread shall run again waiting
+         * for next request.
+         */
+        private class SocketAcceptThread extends Thread {
+            private boolean stopped = false;
+            private BluetoothMns mnsObj;
+            private int mMasId;
+
+            public SocketAcceptThread(BluetoothMns mnsClient, int masId) {
+                mnsObj = mnsClient;
+                mMasId = masId;
+            }
+
+            @Override
+            public void run() {
+                while (!stopped) {
+                    try {
+                        BluetoothSocket connSocket = mServerSocket.accept();
+
+                        BluetoothDevice device = connSocket.getRemoteDevice();
+                        mRemoteDevice = device;
+                        if (device == null) {
+                            Log.i(TAG, "getRemoteDevice() = null");
+                            break;
+                        }
+                        String remoteDeviceName = device.getName();
+                        // In case getRemoteName failed and return null
+                        if (TextUtils.isEmpty(remoteDeviceName)) {
+                            remoteDeviceName = getString(R.string.defaultname);
+                        }
+
+                        if (!mConnectionManager.isAllowedConnection(device)) {
+                            connSocket.close();
+                            continue;
+                        }
+                        mConnSocket = connSocket;
+                        boolean trust = false;
+                        if (trustDevices != null)
+                           trust = trustDevices.contains(device);
+                        if (VERBOSE) Log.v(TAG, "GetTrustState() = " + trust);
+                        if (mIsRequestBeingNotified) {
+                            if (VERBOSE) Log.v(TAG, "Request notification is still on going.");
+                            mConnectionManager.setWaitingForConfirmation(mMasId);
+                            break;
+                        } else if (trust) {
+                            if (VERBOSE) {
+                                Log.v(TAG, "trust is true::");
+                                Log.v(TAG, "incomming connection accepted from: "
+                                        + remoteDeviceName + " automatically as trusted device");
+                            }
+                            try {
+                                startObexServerSession(device, mnsObj);
+                            } catch (IOException ex) {
+                                Log.e(TAG, "catch exception starting obex server session"
+                                    + ex.toString());
+                            }
+                        } else {
+                            if (VERBOSE) Log.v(TAG, "trust is false.");
+                            mConnectionManager.setWaitingForConfirmation(mMasId);
+                            createMapNotification(device);
+                            if (VERBOSE) Log.v(TAG, "incomming connection accepted from: "
+                                + remoteDeviceName);
+                            mSessionStatusHandler.sendMessageDelayed(
+                                mSessionStatusHandler.obtainMessage(MSG_INTERNAL_USER_TIMEOUT),
+                                    USER_CONFIRM_TIMEOUT_VALUE);
+                        }
+                        stopped = true; // job done ,close this thread;
+                    } catch (IOException ex) {
+                        if (stopped) {
+                            break;
+                        }
+                        if (VERBOSE)
+                            Log.v(TAG, "Accept exception: " + ex.toString());
+                    }
+                }
+            }
+
+            void shutdown() {
+                if (VERBOSE) Log.v(TAG, "AcceptThread shutdown for MAS id: " + mMasId);
+                stopped = true;
+                interrupt();
+                if (mServerSocket != null) {
+                    try {
+                        mServerSocket.close();
+                        mServerSocket = null;
+                    } catch (IOException e) {
+                        Log.e(TAG, "Failed to close socket", e);
+                    }
+                }
+            }
+        }
+    }
+};
diff --git a/src/org/codeaurora/bluetooth/map/BluetoothMasSpecParams.java b/src/org/codeaurora/bluetooth/map/BluetoothMasSpecParams.java
new file mode 100644
index 0000000..d6b874b
--- /dev/null
+++ b/src/org/codeaurora/bluetooth/map/BluetoothMasSpecParams.java
@@ -0,0 +1,127 @@
+/*
+ * Copyright (c) 2010-2011, The Linux Foundation. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *        * Redistributions of source code must retain the above copyright
+ *          notice, this list of conditions and the following disclaimer.
+ *        * Redistributions in binary form must reproduce the above copyright
+ *          notice, this list of conditions and the following disclaimer in the
+ *          documentation and/or other materials provided with the distribution.
+ *        * Neither the name of The Linux Foundation nor
+ *          the names of its contributors may be used to endorse or promote
+ *          products derived from this software without specific prior written
+ *          permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NON-INFRINGEMENT ARE DISCLAIMED.    IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
+ * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
+ * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
+ * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package org.codeaurora.bluetooth.map;
+
+public final class BluetoothMasSpecParams {
+
+    public static final int MAS_TAG_MAX_LIST_COUNT            = 0x01;
+    public static final int MAS_TAG_LIST_START_OFFSET         = 0x02;
+    public static final int MAS_TAG_FILTER_MESSAGE_TYPE       = 0x03;
+    public static final int MAS_TAG_FILTER_PERIOD_BEGIN       = 0x04;
+    public static final int MAS_TAG_FILTER_PERIOD_END         = 0x05;
+    public static final int MAS_TAG_FILTER_READ_STATUS        = 0x06;
+    public static final int MAS_TAG_FILTER_RECIPIENT          = 0x07;
+    public static final int MAS_TAG_FILTER_ORIGINATOR         = 0x08;
+    public static final int MAS_TAG_FILTER_PRIORITY           = 0x09;
+    public static final int MAS_TAG_ATTACHMENT                = 0x0A;
+    public static final int MAS_TAG_TRANSPARENT               = 0x0B;
+    public static final int MAS_TAG_RETRY                     = 0x0C;
+    public static final int MAS_TAG_NEW_MESSAGE               = 0x0D;
+    public static final int MAS_TAG_NOTIFICATION_STATUS       = 0x0E;
+    public static final int MAS_TAG_MAS_INSTANCE_ID           = 0x0F;
+    public static final int MAS_TAG_PARAMETER_MASK            = 0x10;
+    public static final int MAS_TAG_FOLDER_LISTING_SIZE       = 0x11;
+    public static final int MAS_TAG_MESSAGE_LISTING_SIZE     = 0x12;
+    public static final int MAS_TAG_SUBJECT_LENGTH            = 0x13;
+    public static final int MAS_TAG_CHARSET                   = 0x14;
+    public static final int MAS_TAG_FRACTION_REQUEST          = 0x15;
+    public static final int MAS_TAG_FRACTION_DELIVER          = 0x16;
+    public static final int MAS_TAG_STATUS_INDICATOR          = 0x17;
+    public static final int MAS_TAG_STATUS_VALUE              = 0x18;
+    public static final int MAS_TAG_MSE_TIME                  = 0x19;
+
+    public static final int MAS_TAG_MAX_LIST_COUNT_LEN            = 0x02;
+    public static final int MAS_TAG_LIST_START_OFFSET_LEN         = 0x02;
+    public static final int MAS_TAG_SUBJECT_LENGTH_LEN            = 0x01;
+    public static final int MAS_TAG_FILTER_MESSAGE_TYPE_LEN       = 0x01;
+    public static final int MAS_TAG_FILTER_READ_STATUS_LEN        = 0x01;
+    public static final int MAS_TAG_FILTER_PRIORITY_LEN           = 0x01;
+    public static final int MAS_TAG_PARAMETER_MASK_LEN            = 0x04;
+    public static final int MAS_TAG_ATTACHMENT_LEN                = 0x01;
+    public static final int MAS_TAG_TRANSPARENT_LEN               = 0x01;
+    public static final int MAS_TAG_RETRY_LEN                     = 0x01;
+    public static final int MAS_TAG_NEW_MESSAGE_LEN               = 0x01;
+    public static final int MAS_TAG_NOTIFICATION_STATUS_LEN       = 0x01;
+    public static final int MAS_TAG_MAS_INSTANCE_ID_LEN           = 0x01;
+    public static final int MAS_TAG_FOLDER_LISTING_SIZE_LEN       = 0x02;
+    public static final int MAS_TAG_MESSAGE_LISTING_SIZE_LEN     = 0x02;
+    public static final int MAS_TAG_CHARSET_LEN                   = 0x01;
+    public static final int MAS_TAG_FRACTION_REQUEST_LEN         = 0x01;
+    public static final int MAS_TAG_FRACTION_DELIVER_LEN          = 0x01;
+    public static final int MAS_TAG_STATUS_INDICATOR_LEN          = 0x01;
+    public static final int MAS_TAG_STATUS_VALUE_LEN              = 0x01;
+    public static final int MAS_TAG_MSE_TIME_LEN                  = 0x14;
+
+    public static final int MAS_DEFAULT_MAX_LIST_COUNT = 1024;
+    public static final int MAS_DEFAULT_SUBJECT_LENGTH = 255;
+    public static final int MAS_DEFAULT_PARAMETER_MASK = 0xFFFF;
+
+    public static final int MAS_FRACTION_REQUEST_NOT_SET = 0x02;
+
+    public static final int MAS_TAG_MAX_LIST_COUNT_MIN_VAL        = 0x0;
+    public static final int MAS_TAG_MAX_LIST_COUNT_MAX_VAL        = 0xFFFF;
+    public static final int MAS_TAG_LIST_START_OFFSET_MIN_VAL     = 0x00;
+    public static final int MAS_TAG_LIST_START_OFFSET_MAX_VAL     = 0xFFFF;
+    public static final int MAS_TAG_SUBJECT_LENGTH_MIN_VAL        = 0x01;
+    public static final int MAS_TAG_SUBJECT_LENGTH_MAX_VAL        = 0xFF;
+    public static final int MAS_TAG_FILTER_MESSAGE_TYPE_MIN_VAL   = 0x00;
+    public static final int MAS_TAG_FILTER_MESSAGE_TYPE_MAX_VAL   = 0x0F;
+    public static final int MAS_TAG_FILTER_READ_STATUS_MIN_VAL    = 0x00;
+    public static final int MAS_TAG_FILTER_READ_STATUS_MAX_VAL    = 0x02;
+    public static final int MAS_TAG_FILTER_PRIORITY_MIN_VAL       = 0x00;
+    public static final int MAS_TAG_FILTER_PRIORITY_MAX_VAL       = 0x02;
+    public static final int MAS_TAG_PARAMETER_MASK_MIN_VAL        = 0x0;
+    public static final int MAS_TAG_PARAMETER_MASK_MAX_VAL        = 0xFFFF;
+    public static final int MAS_TAG_ATTACHMENT_MIN_VAL            = 0x00;
+    public static final int MAS_TAG_ATTACHMENT_MAX_VAL            = 0x01;
+    public static final int MAS_TAG_TRANSPARENT_MIN_VAL           = 0x00;
+    public static final int MAS_TAG_TRANSPARENT_MAX_VAL           = 0x01;
+    public static final int MAS_TAG_RETRY_MIN_VAL                 = 0x00;
+    public static final int MAS_TAG_RETRY_MAX_VAL                 = 0x01;
+    public static final int MAS_TAG_NEW_MESSAGE_MIN_VAL           = 0x00;
+    public static final int MAS_TAG_NEW_MESSAGE_MAX_VAL           = 0x01;
+    public static final int MAS_TAG_NOTIFICATION_STATUS_MIN_VAL   = 0x00;
+    public static final int MAS_TAG_NOTIFICATION_STATUS_MAX_VAL   = 0x01;
+    public static final int MAS_TAG_MAS_INSTANCE_ID_MIN_VAL       = 0x00;
+    public static final int MAS_TAG_MAS_INSTANCE_ID_MAX_VAL       = 0xFF;
+    public static final int MAS_TAG_FOLDER_LISTING_SIZE_MIN_VAL   = 0x00;
+    public static final int MAS_TAG_FOLDER_LISTING_SIZE_MAX_VAL   = 0xFFFF;
+    public static final int MAS_TAG_MESSAGE_LISTING_SIZE_MIN_VAL  = 0x00;
+    public static final int MAS_TAG_MESSAGE_LISTING_SIZE_MAX_VAL  = 0xFFFF;
+    public static final int MAS_TAG_CHARSET_MIN_VAL               = 0x00;
+    public static final int MAS_TAG_CHARSET_MAX_VAL               = 0x01;
+    public static final int MAS_TAG_FRACTION_REQUEST_MIN_VAL      = 0x00;
+    public static final int MAS_TAG_FRACTION_REQUEST_MAX_VAL      = 0x01;
+    public static final int MAS_TAG_FRACTION_DELIVER_MIN_VAL      = 0x00;
+    public static final int MAS_TAG_FRACTION_DELIVER_MAX_VAL      = 0x01;
+    public static final int MAS_TAG_STATUS_INDICATOR_MIN_VAL      = 0x00;
+    public static final int MAS_TAG_STATUS_INDICATOR_MAX_VAL      = 0x01;
+    public static final int MAS_TAG_STATUS_VALUE_MIN_VAL          = 0x00;
+    public static final int MAS_TAG_STATUS_VALUE_MAX_VAL          = 0x01;
+};
diff --git a/src/org/codeaurora/bluetooth/map/BluetoothMasTestActivity.java b/src/org/codeaurora/bluetooth/map/BluetoothMasTestActivity.java
new file mode 100644
index 0000000..708e5be
--- /dev/null
+++ b/src/org/codeaurora/bluetooth/map/BluetoothMasTestActivity.java
@@ -0,0 +1,306 @@
+ /*
+ * Copyright (c) 2008-2009, Motorola, Inc.
+ * Copyright (c) 2010-2011, The Linux Foundation. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *        * Redistributions of source code must retain the above copyright
+ *          notice, this list of conditions and the following disclaimer.
+ *        * Redistributions in binary form must reproduce the above copyright
+ *          notice, this list of conditions and the following disclaimer in the
+ *          documentation and/or other materials provided with the distribution.
+ *        * Neither the name of The Linux Foundation nor
+ *          the names of its contributors may be used to endorse or promote
+ *          products derived from this software without specific prior written
+ *          permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NON-INFRINGEMENT ARE DISCLAIMED.    IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
+ * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
+ * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
+ * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package org.codeaurora.bluetooth.map;
+
+import android.app.Activity;
+import android.bluetooth.BluetoothDevice;
+import android.content.BroadcastReceiver;
+import android.content.Context;
+import android.content.DialogInterface;
+import android.content.Intent;
+import android.content.IntentFilter;
+import android.os.Bundle;
+import android.os.Handler;
+import android.os.Message;
+import android.preference.Preference;
+import android.text.InputFilter;
+import android.text.TextWatcher;
+import android.text.InputFilter.LengthFilter;
+import android.util.Log;
+import android.view.View;
+import android.widget.Button;
+import android.widget.CheckBox;
+import android.widget.CompoundButton;
+import android.widget.EditText;
+import android.widget.TextView;
+import android.widget.CompoundButton.OnCheckedChangeListener;
+
+import org.codeaurora.bluetooth.R;
+
+import static org.codeaurora.bluetooth.map.BluetoothMasService.EXTRA_BLUETOOTH_DEVICE;
+
+/**
+ * MapActivity shows two dialogues: One for accepting incoming map request and
+ * the other prompts the user to enter a session key for authentication with a
+ * remote Bluetooth device.
+ */
+public class BluetoothMasTestActivity extends Activity implements
+        DialogInterface.OnClickListener, Preference.OnPreferenceChangeListener, TextWatcher {
+    private static final String TAG = "BluetoothMasActivity";
+
+    private static final boolean V = BluetoothMasService.VERBOSE;
+
+    private static final int BLUETOOTH_OBEX_AUTHKEY_MAX_LENGTH = 16;
+
+    private static final int DIALOG_YES_NO_CONNECT = 1;
+
+    private static final int DIALOG_YES_NO_AUTH = 2;
+
+    private static final String KEY_USER_TIMEOUT = "user_timeout";
+
+    private View mView;
+
+    private EditText mKeyView;
+
+    private TextView messageView;
+
+    private String mSessionKey = "";
+
+    private int mCurrentDialog;
+
+    private Button mOkButton;
+
+    private CheckBox mAlwaysAllowed;
+
+    private boolean mTimeout = false;
+
+    private boolean mAlwaysAllowedValue = true;
+
+    private static final int DISMISS_TIMEOUT_DIALOG = 0;
+
+    private static final int DISMISS_TIMEOUT_DIALOG_VALUE = 2000;
+
+    private BroadcastReceiver mReceiver = new BroadcastReceiver() {
+        @Override
+        public void onReceive(Context context, Intent intent) {
+            if (!BluetoothMasService.USER_CONFIRM_TIMEOUT_ACTION.equals(intent.getAction())) {
+                return;
+            }
+            onTimeout();
+        }
+    };
+
+    @Override
+    protected void onCreate(Bundle savedInstanceState) {
+        super.onCreate(savedInstanceState);
+        Intent i = getIntent();
+        String action = i.getAction();
+        if (action.equals(BluetoothMasService.ACCESS_REQUEST_ACTION)) {
+            showMapDialog(DIALOG_YES_NO_CONNECT);
+            mCurrentDialog = DIALOG_YES_NO_CONNECT;
+        } else if (action.equals(BluetoothMasService.AUTH_CHALL_ACTION)) {
+            showMapDialog(DIALOG_YES_NO_AUTH);
+            mCurrentDialog = DIALOG_YES_NO_AUTH;
+        } else {
+            Log.e(TAG, "Error: this activity may be started only with intent "
+                    + "MAP_ACCESS_REQUEST");
+            finish();
+        }
+        registerReceiver(mReceiver, new IntentFilter(
+                BluetoothMasService.USER_CONFIRM_TIMEOUT_ACTION));
+    }
+
+    private void showMapDialog(int id) {
+    }
+
+    private String getRemoteDeviceName() {
+        String remoteDeviceName = null;
+        Intent intent = getIntent();
+        if (intent.hasExtra(EXTRA_BLUETOOTH_DEVICE)) {
+            BluetoothDevice device = intent.getParcelableExtra(EXTRA_BLUETOOTH_DEVICE);
+            if (device != null) {
+                remoteDeviceName = device.getName();
+            }
+        }
+
+        return (remoteDeviceName != null) ? remoteDeviceName : getString(R.string.defaultname);
+    }
+
+    private String createDisplayText(final int id) {
+        String mRemoteName = getRemoteDeviceName();
+        return null;
+    }
+
+    private View createView(final int id) {
+        switch (id) {
+            case DIALOG_YES_NO_CONNECT:
+                //mView = getLayoutInflater().inflate(R.layout.access, null);
+                messageView = (TextView)mView.findViewById(R.id.message);
+                messageView.setText(createDisplayText(id));
+                //mAlwaysAllowed = (CheckBox)mView.findViewById(R.id.alwaysallowed);
+                mAlwaysAllowed.setChecked(true);
+                mAlwaysAllowed.setOnCheckedChangeListener(new OnCheckedChangeListener() {
+                    public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) {
+                        if (isChecked) {
+                            mAlwaysAllowedValue = true;
+                        } else {
+                            mAlwaysAllowedValue = false;
+                        }
+                    }
+                });
+                return mView;
+            case DIALOG_YES_NO_AUTH:
+                mView = getLayoutInflater().inflate(R.layout.auth, null);
+                messageView = (TextView)mView.findViewById(R.id.message);
+                messageView.setText(createDisplayText(id));
+                mKeyView = (EditText)mView.findViewById(R.id.text);
+                mKeyView.addTextChangedListener(this);
+                mKeyView.setFilters(new InputFilter[] {
+                    new LengthFilter(BLUETOOTH_OBEX_AUTHKEY_MAX_LENGTH)
+                });
+                return mView;
+            default:
+                return null;
+        }
+    }
+
+    private void onPositive() {
+        if (!mTimeout) {
+            if (mCurrentDialog == DIALOG_YES_NO_CONNECT) {
+                sendIntentToReceiver(BluetoothMasService.ACCESS_ALLOWED_ACTION,
+                        BluetoothMasService.EXTRA_ALWAYS_ALLOWED, mAlwaysAllowedValue);
+            } else if (mCurrentDialog == DIALOG_YES_NO_AUTH) {
+                sendIntentToReceiver(BluetoothMasService.AUTH_RESPONSE_ACTION,
+                        BluetoothMasService.EXTRA_SESSION_KEY, mSessionKey);
+                mKeyView.removeTextChangedListener(this);
+            }
+        }
+        mTimeout = false;
+        finish();
+    }
+
+    private void onNegative() {
+        if (mCurrentDialog == DIALOG_YES_NO_CONNECT) {
+            sendIntentToReceiver(BluetoothMasService.ACCESS_DISALLOWED_ACTION, null, null);
+        } else if (mCurrentDialog == DIALOG_YES_NO_AUTH) {
+            sendIntentToReceiver(BluetoothMasService.AUTH_CANCELLED_ACTION, null, null);
+            mKeyView.removeTextChangedListener(this);
+        }
+        finish();
+    }
+
+    private void sendIntentToReceiver(final String intentName, final String extraName,
+            final String extraValue) {
+        Intent intent = new Intent(intentName);
+        intent.setClassName(BluetoothMasService.THIS_PACKAGE_NAME, BluetoothMasReceiver.class
+                .getName());
+        if (extraName != null) {
+            intent.putExtra(extraName, extraValue);
+        }
+        sendBroadcast(intent);
+    }
+
+    private void sendIntentToReceiver(final String intentName, final String extraName,
+            final boolean extraValue) {
+        Intent intent = new Intent(intentName);
+        intent.setClassName(BluetoothMasService.THIS_PACKAGE_NAME, BluetoothMasReceiver.class
+                .getName());
+        if (extraName != null) {
+            intent.putExtra(extraName, extraValue);
+        }
+        sendBroadcast(intent);
+    }
+
+    public void onClick(DialogInterface dialog, int which) {
+        switch (which) {
+            case DialogInterface.BUTTON_POSITIVE:
+                if (mCurrentDialog == DIALOG_YES_NO_AUTH) {
+                    mSessionKey = mKeyView.getText().toString();
+                }
+                onPositive();
+                break;
+
+            case DialogInterface.BUTTON_NEGATIVE:
+                onNegative();
+                break;
+            default:
+                break;
+        }
+    }
+
+
+    private void onTimeout() {
+        // TODO - Implement appropriate timeout function
+    }
+
+    @Override
+    protected void onRestoreInstanceState(Bundle savedInstanceState) {
+        super.onRestoreInstanceState(savedInstanceState);
+        mTimeout = savedInstanceState.getBoolean(KEY_USER_TIMEOUT);
+        if (V) Log.e(TAG, "onRestoreInstanceState() mTimeout: " + mTimeout);
+
+        if (mTimeout) {
+            onTimeout();
+        }
+
+    }
+
+    @Override
+    protected void onSaveInstanceState(Bundle outState) {
+        super.onSaveInstanceState(outState);
+        outState.putBoolean(KEY_USER_TIMEOUT, mTimeout);
+    }
+
+    @Override
+    protected void onDestroy() {
+        super.onDestroy();
+        unregisterReceiver(mReceiver);
+    }
+
+    public boolean onPreferenceChange(Preference preference, Object newValue) {
+        return true;
+    }
+
+    public void beforeTextChanged(CharSequence s, int start, int before, int after) {
+    }
+
+    public void onTextChanged(CharSequence s, int start, int before, int count) {
+    }
+
+    public void afterTextChanged(android.text.Editable s) {
+        if (s.length() > 0) {
+            mOkButton.setEnabled(true);
+        }
+    }
+
+    private final Handler mTimeoutHandler = new Handler() {
+        @Override
+        public void handleMessage(Message msg) {
+            switch (msg.what) {
+                case DISMISS_TIMEOUT_DIALOG:
+                    if (V) Log.e(TAG, "Received DISMISS_TIMEOUT_DIALOG msg.");
+                    finish();
+                    break;
+                default:
+                    break;
+            }
+        }
+    };
+}
diff --git a/src/org/codeaurora/bluetooth/map/BluetoothMns.java b/src/org/codeaurora/bluetooth/map/BluetoothMns.java
new file mode 100644
index 0000000..41c794b
--- /dev/null
+++ b/src/org/codeaurora/bluetooth/map/BluetoothMns.java
@@ -0,0 +1,784 @@
+/*
+ * Copyright (c) 2010-2013, The Linux Foundation. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *        * Redistributions of source code must retain the above copyright
+ *          notice, this list of conditions and the following disclaimer.
+ *        * Redistributions in binary form must reproduce the above copyright
+ *          notice, this list of conditions and the following disclaimer in the
+ *          documentation and/or other materials provided with the distribution.
+ *        * Neither the name of The Linux Foundation nor
+ *          the names of its contributors may be used to endorse or promote
+ *          products derived from this software without specific prior written
+ *          permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NON-INFRINGEMENT ARE DISCLAIMED.    IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
+ * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
+ * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
+ * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package org.codeaurora.bluetooth.map;
+
+import android.bluetooth.BluetoothAdapter;
+import android.bluetooth.BluetoothDevice;
+import android.bluetooth.BluetoothSocket;
+import android.content.BroadcastReceiver;
+import android.content.Context;
+import android.content.Intent;
+import android.content.IntentFilter;
+import android.os.Handler;
+import android.os.Message;
+import android.os.ParcelUuid;
+import android.text.format.Time;
+import android.util.Log;
+import android.util.Pair;
+
+import org.codeaurora.bluetooth.map.IBluetoothMasApp.MessageNotificationListener;
+import org.codeaurora.bluetooth.map.IBluetoothMasApp.MnsRegister;
+import org.codeaurora.bluetooth.map.MapUtils.MapUtils;
+
+import java.io.File;
+import java.io.FileNotFoundException;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.lang.reflect.Constructor;
+import java.lang.reflect.InvocationTargetException;
+import java.util.ArrayList;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Queue;
+import java.util.concurrent.ConcurrentLinkedQueue;
+
+import javax.obex.ObexTransport;
+
+import static org.codeaurora.bluetooth.map.BluetoothMasService.MAS_INS_INFO;
+import static org.codeaurora.bluetooth.map.BluetoothMasService.MAX_INSTANCES;
+import static org.codeaurora.bluetooth.map.IBluetoothMasApp.HANDLE_OFFSET;
+import static org.codeaurora.bluetooth.map.IBluetoothMasApp.MSG;
+import static org.codeaurora.bluetooth.map.IBluetoothMasApp.TELECOM;
+
+/**
+ * This class run an MNS session.
+ */
+public class BluetoothMns implements MessageNotificationListener {
+    private static final String TAG = "BtMns";
+
+    private static final boolean V = BluetoothMasService.VERBOSE;
+
+    public static final int RFCOMM_ERROR = 10;
+
+    public static final int RFCOMM_CONNECTED = 11;
+
+    public static final int MNS_CONNECT = 13;
+
+    public static final int MNS_DISCONNECT = 14;
+
+    public static final int MNS_SEND_EVENT = 15;
+
+    public static final int MNS_SEND_EVENT_DONE = 16;
+
+    public static final int MNS_SEND_TIMEOUT = 17;
+
+    public static final int MNS_BLUETOOTH_OFF = 18;
+
+    public static final int MNS_SEND_TIMEOUT_DURATION = 30000; // 30 secs
+
+    private static final short MNS_UUID16 = 0x1133;
+
+    public static final String NEW_MESSAGE = "NewMessage";
+
+    public static final String DELIVERY_SUCCESS = "DeliverySuccess";
+
+    public static final String SENDING_SUCCESS = "SendingSuccess";
+
+    public static final String DELIVERY_FAILURE = "DeliveryFailure";
+
+    public static final String SENDING_FAILURE = "SendingFailure";
+
+    public static final String MEMORY_FULL = "MemoryFull";
+
+    public static final String MEMORY_AVAILABLE = "MemoryAvailable";
+
+    public static final String MESSAGE_DELETED = "MessageDeleted";
+
+    public static final String MESSAGE_SHIFT = "MessageShift";
+
+    private Context mContext;
+
+    private BluetoothAdapter mAdapter;
+
+    private BluetoothMnsObexSession mSession;
+
+    private EventHandler mSessionHandler;
+
+    private List<MnsClient> mMnsClients = new ArrayList<MnsClient>();
+    public static final ParcelUuid BluetoothUuid_ObexMns = ParcelUuid
+            .fromString("00001133-0000-1000-8000-00805F9B34FB");
+
+    private HashSet<Integer> mWaitingMasId = new HashSet<Integer>();
+    private final Queue<Pair<Integer, String>> mEventQueue = new ConcurrentLinkedQueue<Pair<Integer, String>>();
+    private boolean mSendingEvent = false;
+
+
+    public BluetoothMns(Context context, boolean isEmailEnabled) {
+        /* check Bluetooth enable status */
+        /*
+         * normally it's impossible to reach here if BT is disabled. Just check
+         * for safety
+         */
+
+        mAdapter = BluetoothAdapter.getDefaultAdapter();
+        mContext = context;
+        int numOfSupportedInstances = MAX_INSTANCES;
+        Log.v(TAG, "BluetoothMns: isEmailEnabled: " + isEmailEnabled);
+        if(!isEmailEnabled) {
+            numOfSupportedInstances = 1; /*Email is not supported*/
+        }
+        for (int i = 0; i < numOfSupportedInstances; i ++) {
+            try {
+                // TODO: must be updated when Class<? extends MnsClient>'s constructor is changed
+                Constructor<? extends MnsClient> constructor;
+                constructor = MAS_INS_INFO[i].mMnsClientClass.getConstructor(Context.class,
+                        Integer.class);
+                mMnsClients.add(constructor.newInstance(mContext, i));
+            } catch (IllegalArgumentException e) {
+                Log.e(TAG, "The " + MAS_INS_INFO[i].mMnsClientClass.getName()
+                        + "'s constructor arguments mismatch", e);
+            } catch (InstantiationException e) {
+                Log.e(TAG, "The " + MAS_INS_INFO[i].mMnsClientClass.getName()
+                        + " cannot be instantiated", e);
+            } catch (IllegalAccessException e) {
+                Log.e(TAG, "The " + MAS_INS_INFO[i].mMnsClientClass.getName()
+                        + " cannot be instantiated", e);
+            } catch (InvocationTargetException e) {
+                Log.e(TAG, "Exception during " + MAS_INS_INFO[i].mMnsClientClass.getName()
+                        + "'s constructor invocation", e);
+            } catch (SecurityException e) {
+                Log.e(TAG, MAS_INS_INFO[i].mMnsClientClass.getName()
+                        + "'s constructor is not accessible", e);
+            } catch (NoSuchMethodException e) {
+                Log.e(TAG, MAS_INS_INFO[i].mMnsClientClass.getName()
+                        + " has no matched constructor", e);
+            }
+        }
+
+        if (!mAdapter.isEnabled()) {
+            Log.e(TAG, "Can't send event when Bluetooth is disabled ");
+            return;
+        }
+
+        mSessionHandler = new EventHandler();
+
+        IntentFilter filter = new IntentFilter();
+        filter.addAction(Intent.ACTION_DEVICE_STORAGE_LOW);
+        filter.addAction(Intent.ACTION_DEVICE_STORAGE_OK);
+        mContext.registerReceiver(mStorageStatusReceiver, filter);
+    }
+
+    public Handler getHandler() {
+        return mSessionHandler;
+    }
+
+    /**
+     * Asserting masId
+     * @param masId
+     * @return true if MnsClient is created for masId; otherwise false.
+     */
+    private boolean assertMasid(final int masId) {
+        final int size = mMnsClients.size();
+        if (masId < 0 || masId >= size) {
+            Log.e(TAG, "MAS id: " + masId + " is out of maximum number of MAS instances: " + size);
+            return false;
+        }
+        return true;
+    }
+
+    private boolean register(final int masId) {
+        if (!assertMasid(masId)) {
+            Log.e(TAG, "Attempt to register MAS id: " + masId);
+            return false;
+        }
+        final MnsClient client = mMnsClients.get(masId);
+        if (!client.isRegistered()) {
+            try {
+                client.register(BluetoothMns.this);
+            } catch (Exception e) {
+                Log.e(TAG, "Exception occured while register MNS for MAS id: " + masId, e);
+                return false;
+            }
+        }
+        return true;
+    }
+
+    private synchronized boolean canDisconnect() {
+        for (MnsClient client : mMnsClients) {
+            if (client.isRegistered()) {
+                return false;
+            }
+        }
+        return true;
+    }
+
+    private void deregister(final int masId) {
+        if (!assertMasid(masId)) {
+            Log.e(TAG, "Attempt to register MAS id: " + masId);
+            return;
+        }
+        final MnsClient client = mMnsClients.get(masId);
+        if (client.isRegistered()) {
+            client.register(null);
+        }
+    }
+
+    private void deregisterAll() {
+        for (MnsClient client : mMnsClients) {
+            if (client.isRegistered()) {
+                client.register(null);
+            }
+        }
+    }
+
+    private void mnsCleanupInstances() {
+        if (V) Log.v(TAG, "MNS_BT: entered mnsCleanupInstances");
+        if(mStorageStatusReceiver != null) {
+            mContext.unregisterReceiver(mStorageStatusReceiver);
+            mStorageStatusReceiver = null;
+        }
+        for (MnsClient client : mMnsClients) {
+            if (V) Log.v(TAG, "MNS_BT: mnsCleanupInstances: inside for loop");
+            if (client.isRegistered()) {
+                if (V) Log.v(TAG, "MNS_BT: mnsCleanupInstances: Attempt to deregister MnsClient");
+                client.register(null);
+                client = null;
+                if (V) Log.v(TAG, "MNS_BT: mnsCleanupInstances: made client = null");
+            }
+        }
+        if (V) Log.v(TAG, "Relase MNS lock during MNS close service");
+        if(mSession != null) {
+            mSession.releaseMnsLock();
+        }
+    }
+
+    /*
+     * Receives events from mConnectThread & mSession back in the main thread.
+     */
+    private class EventHandler extends Handler {
+        public EventHandler() {
+            super();
+        }
+
+        @Override
+        public void handleMessage(Message msg) {
+            if (V){
+                Log.v(TAG, " Handle Message " + msg.what);
+            }
+            switch (msg.what) {
+                case MNS_CONNECT:
+                {
+                    final int masId = msg.arg1;
+                    final BluetoothDevice device = (BluetoothDevice)msg.obj;
+                    if (mSession != null) {
+                        if (V) Log.v(TAG, "is MNS session connected? " + mSession.isConnected());
+                        if (mSession.isConnected()) {
+                            if (!register(masId)) {
+                                // failed to register, disconnect
+                                obtainMessage(MNS_DISCONNECT, masId, -1).sendToTarget();
+                            }
+                            break;
+                        }
+                    }
+                    if (mWaitingMasId.isEmpty()) {
+                        mWaitingMasId.add(masId);
+                        mConnectThread = new SocketConnectThread(device);
+                        mConnectThread.start();
+                    } else {
+                        mWaitingMasId.add(masId);
+                    }
+                    break;
+                }
+                case MNS_DISCONNECT:
+                {
+                    final int masId = msg.arg1;
+                    new Thread(new Runnable() {
+                        public void run() {
+                            deregister(masId);
+                            if (canDisconnect()) {
+                                stop();
+                            }
+                        }
+                    }).start();
+                    break;
+                }
+                case MNS_BLUETOOTH_OFF:
+                    if (V) Log.v(TAG, "MNS_BT: receive MNS_BLUETOOTH_OFF msg");
+                    new Thread(new Runnable() {
+                        public void run() {
+                            if (V) Log.v(TAG, "MNS_BT: Started Deregister Thread");
+                            if (canDisconnect()) {
+                                stop();
+                            }
+                            mnsCleanupInstances();
+                        }
+                    }).start();
+                    break;
+                /*
+                 * RFCOMM connect fail is for outbound share only! Mark batch
+                 * failed, and all shares in batch failed
+                 */
+                case RFCOMM_ERROR:
+                    if (V) Log.v(TAG, "receive RFCOMM_ERROR msg");
+                    deregisterAll();
+                    if (canDisconnect()) {
+                        stop();
+                    }
+                    break;
+                /*
+                 * RFCOMM connected. Do an OBEX connect by starting the session
+                 */
+                case RFCOMM_CONNECTED:
+                {
+                    if (V) Log.v(TAG, "Transfer receive RFCOMM_CONNECTED msg");
+                    ObexTransport transport = (ObexTransport) msg.obj;
+                    try {
+                        startObexSession(transport);
+                    } catch (NullPointerException ne) {
+                        sendEmptyMessage(RFCOMM_ERROR);
+                        return;
+                    }
+                    for (int masId : mWaitingMasId) {
+                        register(masId);
+                    }
+                    mWaitingMasId.clear();
+                    break;
+                }
+                /* Handle the error state of an Obex session */
+                case BluetoothMnsObexSession.MSG_SESSION_ERROR:
+                    if (V) Log.v(TAG, "receive MSG_SESSION_ERROR");
+                    deregisterAll();
+                    stop();
+                    break;
+                case MNS_SEND_EVENT:
+                {
+                    final String xml = (String)msg.obj;
+                    final int masId = msg.arg1;
+                    if (mSendingEvent) {
+                        mEventQueue.add(new Pair<Integer, String>(masId, xml));
+                    } else {
+                        mSendingEvent = true;
+                        new Thread(new SendEventTask(xml, masId)).start();
+                    }
+                    break;
+                }
+                case MNS_SEND_EVENT_DONE:
+                    if (mEventQueue.isEmpty()) {
+                        mSendingEvent = false;
+                    } else {
+                        final Pair<Integer, String> p = mEventQueue.remove();
+                        final int masId = p.first;
+                        final String xml = p.second;
+                        new Thread(new SendEventTask(xml, masId)).start();
+                    }
+                    break;
+                case MNS_SEND_TIMEOUT:
+                {
+                    if (V) Log.v(TAG, "MNS_SEND_TIMEOUT disconnecting.");
+                    deregisterAll();
+                    stop();
+                    break;
+                }
+            }
+        }
+
+        private void setTimeout(int masId) {
+            if (V) Log.v(TAG, "setTimeout MNS_SEND_TIMEOUT for instance " + masId);
+            sendMessageDelayed(obtainMessage(MNS_SEND_TIMEOUT, masId, -1),
+                    MNS_SEND_TIMEOUT_DURATION);
+        }
+
+        private void removeTimeout() {
+            if (hasMessages(MNS_SEND_TIMEOUT)) {
+                removeMessages(MNS_SEND_TIMEOUT);
+                sendEventDone();
+            }
+        }
+
+        private void sendEventDone() {
+            if (V) Log.v(TAG, "post MNS_SEND_EVENT_DONE");
+            obtainMessage(MNS_SEND_EVENT_DONE).sendToTarget();
+        }
+
+        class SendEventTask implements Runnable {
+            final String mXml;
+            final int mMasId;
+            SendEventTask (String xml, int masId) {
+                mXml = xml;
+                mMasId = masId;
+            }
+
+            public void run() {
+                if (V) Log.v(TAG, "MNS_SEND_EVENT started");
+                setTimeout(mMasId);
+                sendEvent(mXml, mMasId);
+                removeTimeout();
+                if (V) Log.v(TAG, "MNS_SEND_EVENT finished");
+            }
+        }
+    }
+
+    /*
+     * Class to hold message handle for MCE Initiated operation
+     */
+    public class BluetoothMnsMsgHndlMceInitOp {
+        public String msgHandle;
+        Time time;
+    }
+
+    /*
+     * Keep track of Message Handles on which the operation was
+     * initiated by MCE
+     */
+    List<BluetoothMnsMsgHndlMceInitOp> opList = new ArrayList<BluetoothMnsMsgHndlMceInitOp>();
+
+    /*
+     * Adds the Message Handle to the list for tracking
+     * MCE initiated operation
+     */
+    public void addMceInitiatedOperation(String msgHandle) {
+        BluetoothMnsMsgHndlMceInitOp op = new BluetoothMnsMsgHndlMceInitOp();
+        op.msgHandle = msgHandle;
+        op.time = new Time();
+        op.time.setToNow();
+        opList.add(op);
+    }
+    /*
+     * Removes the Message Handle from the list for tracking
+     * MCE initiated operation
+     */
+    public void removeMceInitiatedOperation(int location) {
+        opList.remove(location);
+    }
+
+    /*
+     * Finds the location in the list of the given msgHandle, if
+     * available. "+" indicates the next (any) operation
+     */
+    public int findLocationMceInitiatedOperation( String msgHandle) {
+        int location = -1;
+
+        Time currentTime = new Time();
+        currentTime.setToNow();
+
+        if (V) Log.v(TAG, "findLocationMceInitiatedOperation " + msgHandle);
+
+        List<BluetoothMnsMsgHndlMceInitOp> staleOpList = new ArrayList<BluetoothMnsMsgHndlMceInitOp>();
+        for (BluetoothMnsMsgHndlMceInitOp op: opList) {
+            if (currentTime.toMillis(false) - op.time.toMillis(false) > 10000) {
+                // add stale entries
+                staleOpList.add(op);
+            }
+        }
+        if (!staleOpList.isEmpty()) {
+            for (BluetoothMnsMsgHndlMceInitOp op: staleOpList) {
+                // Remove stale entries
+                opList.remove(op);
+            }
+        }
+
+        for (BluetoothMnsMsgHndlMceInitOp op: opList) {
+            if (op.msgHandle.equalsIgnoreCase(msgHandle)){
+                location = opList.indexOf(op);
+                break;
+            }
+        }
+
+        if (location == -1) {
+            for (BluetoothMnsMsgHndlMceInitOp op: opList) {
+                if (op.msgHandle.equalsIgnoreCase("+")) {
+                    location = opList.indexOf(op);
+                    break;
+                }
+            }
+        }
+        if (V) Log.v(TAG, "findLocationMce loc" + location);
+        return location;
+    }
+
+
+    /**
+     * Post a MNS Event to the MNS thread
+     */
+    public void sendMnsEvent(int masId, String msg, String handle, String folder,
+            String old_folder, String msgType) {
+        if (V) {
+            Log.v(TAG, "sendMnsEvent()");
+            Log.v(TAG, "msg: " + msg);
+            Log.v(TAG, "handle: " + handle);
+            Log.v(TAG, "folder: " + folder);
+            Log.v(TAG, "old_folder: " + old_folder);
+            Log.v(TAG, "msgType: " + msgType);
+        }
+        int location = -1;
+
+        /* Send the notification, only if it was not initiated
+         * by MCE. MEMORY_FULL and MEMORY_AVAILABLE cannot be
+         * MCE initiated
+         */
+        if (msg.equals(MEMORY_AVAILABLE) || msg.equals(MEMORY_FULL)) {
+            location = -1;
+        } else {
+            /* Consider SENDING_SUCESS as non MCE Initiated operation and remove
+             * message handle from MCE Initiated OpList when MmsContentObserver
+             * is not triggered for both OUTBOX and SENT folder even though
+             * message is pushed sucessfully.
+             */
+            location = findLocationMceInitiatedOperation(handle);
+            if (location != -1 && msg.equals(SENDING_SUCCESS)) {
+               if (V) Log.v(TAG, "Handle Pending MCE Initiated list " + location);
+               removeMceInitiatedOperation(location);
+               location = -1;
+            }
+        }
+
+        if (location == -1) {
+            String str = MapUtils.mapEventReportXML(msg, handle, folder, old_folder, msgType);
+            if (V) Log.v(TAG, "Notification to MAS " + masId + ", msgType = " + msgType);
+            mSessionHandler.obtainMessage(MNS_SEND_EVENT, masId, -1, str).sendToTarget();
+        } else {
+            if (V) Log.v(TAG, "REMOVE location" + location);
+            removeMceInitiatedOperation(location);
+        }
+    }
+
+    /**
+     * Push the message over Obex client session
+     */
+    private void sendEvent(String str, int masId) {
+        if (str != null && (str.length() > 0)) {
+            if (V){
+                Log.v(TAG, "--------------");
+                Log.v(TAG, " CONTENT OF EVENT REPORT FILE: " + str);
+            }
+
+            final String FILENAME = "EventReport" + masId;
+            FileOutputStream fos = null;
+            File file = new File(mContext.getFilesDir() + "/" + FILENAME);
+            file.delete();
+            try {
+                fos = mContext.openFileOutput(FILENAME, Context.MODE_PRIVATE);
+                fos.write(str.getBytes());
+                fos.flush();
+                fos.close();
+            } catch (FileNotFoundException e) {
+                e.printStackTrace();
+            } catch (IOException e) {
+                e.printStackTrace();
+            }
+
+            File fileR = new File(mContext.getFilesDir() + "/" + FILENAME);
+            if (fileR.exists() == true) {
+                if (V) {
+                    Log.v(TAG, " Sending event report file for Mas " + masId);
+                }
+                try {
+                    if (mSession != null) {
+                        mSession.sendEvent(fileR, (byte) masId);
+                    }
+                } catch (Exception e) {
+                    e.printStackTrace();
+                }
+            } else {
+                if (V){
+                    Log.v(TAG, " ERROR IN CREATING SEND EVENT OBJ FILE");
+                }
+            }
+        } else if (V) {
+            Log.v(TAG, "sendEvent(null, " + masId + ")");
+        }
+    }
+
+    private BroadcastReceiver mStorageStatusReceiver = new BroadcastReceiver() {
+        @Override
+        public void onReceive(Context context, Intent intent) {
+            if (intent != null && mSession != null) {
+                final String action = intent.getAction();
+                if (Intent.ACTION_DEVICE_STORAGE_LOW.equals(action)) {
+                    Log.d(TAG, " Memory Full ");
+                    sendMnsEventMemory(MEMORY_FULL);
+                } else if (Intent.ACTION_DEVICE_STORAGE_OK.equals(action)) {
+                    Log.d(TAG, " Memory Available ");
+                    sendMnsEventMemory(MEMORY_AVAILABLE);
+                }
+            }
+        }
+    };
+
+    /**
+     * Stop the transfer
+     */
+    public synchronized void stop() {
+        if (V) Log.v(TAG, "stop");
+            if (mSession != null) {
+                if (V) Log.v(TAG, "Stop mSession");
+                mSession.disconnect();
+                mSession = null;
+            }
+    }
+
+    /**
+     * Connect the MNS Obex client to remote server
+     */
+    private void startObexSession(ObexTransport transport) throws NullPointerException {
+        if (V) Log.v(TAG, "Create Client session with transport " + transport.toString());
+        mSession = new BluetoothMnsObexSession(mContext, transport);
+        mSession.connect();
+    }
+
+    private SocketConnectThread mConnectThread;
+    /**
+     * This thread is used to establish rfcomm connection to
+     * remote device
+     */
+    private class SocketConnectThread extends Thread {
+        private final BluetoothDevice device;
+
+        private long timestamp;
+
+        /* create a Rfcomm Socket */
+        public SocketConnectThread(BluetoothDevice device) {
+            super("Socket Connect Thread");
+            this.device = device;
+        }
+
+        public void interrupt() {
+        }
+
+        @Override
+        public void run() {
+            timestamp = System.currentTimeMillis();
+
+            BluetoothSocket btSocket = null;
+            try {
+                btSocket = device.createInsecureRfcommSocketToServiceRecord(
+                        BluetoothUuid_ObexMns.getUuid());
+                btSocket.connect();
+            } catch (IOException e) {
+                Log.e(TAG, "BtSocket Connect error " + e.getMessage(), e);
+                markConnectionFailed(btSocket);
+                return;
+            }
+
+            if (V) Log.v(TAG, "Rfcomm socket connection attempt took "
+                    + (System.currentTimeMillis() - timestamp) + " ms");
+            ObexTransport transport;
+            transport = new BluetoothMnsRfcommTransport(btSocket);
+            if (V) Log.v(TAG, "Send transport message " + transport.toString());
+
+            mSessionHandler.obtainMessage(RFCOMM_CONNECTED, transport).sendToTarget();
+        }
+
+        /**
+         * RFCOMM connection failed
+         */
+        private void markConnectionFailed(BluetoothSocket s) {
+            try {
+                if (s != null) {
+                    s.close();
+                }
+            } catch (IOException e) {
+                Log.e(TAG, "Error when close socket");
+            }
+            mSessionHandler.obtainMessage(RFCOMM_ERROR).sendToTarget();
+            return;
+        }
+    }
+
+    public void sendMnsEventMemory(String msg) {
+        // Sending "MemoryFull" or "MemoryAvailable" to all registered Mas Instances
+        for (MnsClient client : mMnsClients) {
+            if (client.isRegistered()) {
+                sendMnsEvent(client.getMasId(), msg, null, null, null, null);
+            }
+        }
+    }
+
+    public void onDeliveryFailure(int masId, String handle, String folder, String msgType) {
+        sendMnsEvent(masId, DELIVERY_FAILURE, handle, folder, null, msgType);
+    }
+
+    public void onDeliverySuccess(int masId, String handle, String folder, String msgType) {
+        sendMnsEvent(masId, DELIVERY_SUCCESS, handle, folder, null, msgType);
+    }
+
+    public void onMessageShift(int masId, String handle, String toFolder,
+            String fromFolder, String msgType) {
+        sendMnsEvent(masId, MESSAGE_SHIFT, handle, toFolder, fromFolder, msgType);
+    }
+
+    public void onNewMessage(int masId, String handle, String folder, String msgType) {
+        sendMnsEvent(masId, NEW_MESSAGE, handle, folder, null, msgType);
+    }
+
+    public void onSendingFailure(int masId, String handle, String folder, String msgType) {
+        sendMnsEvent(masId, SENDING_FAILURE, handle, folder, null, msgType);
+    }
+
+    public void onSendingSuccess(int masId, String handle, String folder, String msgType) {
+        sendMnsEvent(masId, SENDING_SUCCESS, handle, folder, null, msgType);
+    }
+
+    public void onMessageDeleted(int masId, String handle, String folder, String msgType) {
+        sendMnsEvent(masId, MESSAGE_DELETED, handle, folder, null, msgType);
+    }
+
+    public static abstract class MnsClient implements MnsRegister {
+        public static final String TAG = "MnsClient";
+        public static final boolean V = BluetoothMasService.VERBOSE;
+        protected static final String PRE_PATH = TELECOM + "/" + MSG + "/";
+
+        protected Context mContext;
+        protected MessageNotificationListener mListener = null;
+        protected int mMasId;
+
+        protected final long OFFSET_START;
+        protected final long OFFSET_END;
+
+        public MnsClient(Context context, int masId) {
+            mContext = context;
+            mMasId = masId;
+            OFFSET_START = HANDLE_OFFSET[masId];
+            OFFSET_END = HANDLE_OFFSET[masId + 1] - 1;
+        }
+
+        public synchronized void register(MessageNotificationListener listener) {
+            if (V) Log.v(TAG, "MNS_BT: register entered");
+            if (listener != null) {
+                mListener = listener;
+                registerContentObserver();
+            } else {
+                if (V) Log.v(TAG, "MNS_BT: register(null)");
+                unregisterContentObserver();
+                mListener = null;
+            }
+        }
+
+        public boolean isRegistered() {
+            return mListener != null;
+        }
+
+        public int getMasId() {
+            return mMasId;
+        }
+
+        protected abstract void registerContentObserver();
+        protected abstract void unregisterContentObserver();
+    }
+}
diff --git a/src/org/codeaurora/bluetooth/map/BluetoothMnsEmail.java b/src/org/codeaurora/bluetooth/map/BluetoothMnsEmail.java
new file mode 100644
index 0000000..c348685
--- /dev/null
+++ b/src/org/codeaurora/bluetooth/map/BluetoothMnsEmail.java
@@ -0,0 +1,315 @@
+/*
+ * Copyright (c) 2011, The Linux Foundation. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *        * Redistributions of source code must retain the above copyright
+ *          notice, this list of conditions and the following disclaimer.
+ *        * Redistributions in binary form must reproduce the above copyright
+ *          notice, this list of conditions and the following disclaimer in the
+ *          documentation and/or other materials provided with the distribution.
+ *        * Neither the name of The Linux Foundation nor
+ *          the names of its contributors may be used to endorse or promote
+ *          products derived from this software without specific prior written
+ *          permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NON-INFRINGEMENT ARE DISCLAIMED.    IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
+ * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
+ * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
+ * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package org.codeaurora.bluetooth.map;
+
+import android.content.ContentResolver;
+import android.content.Context;
+import android.database.ContentObserver;
+import android.database.Cursor;
+import android.os.Handler;
+import android.util.Log;
+
+import org.codeaurora.bluetooth.map.BluetoothMns.MnsClient;
+import org.codeaurora.bluetooth.map.MapUtils.EmailUtils;
+
+import java.util.Collection;
+import java.util.HashMap;
+
+import static org.codeaurora.bluetooth.map.MapUtils.EmailUtils.EMAIL_BOX_COLUMN_ACCOUNT_KEY;
+import static org.codeaurora.bluetooth.map.MapUtils.EmailUtils.EMAIL_BOX_COLUMN_DISPLAY_NAME;
+import static org.codeaurora.bluetooth.map.MapUtils.EmailUtils.EMAIL_BOX_COLUMN_RECORD_ID;
+import static org.codeaurora.bluetooth.map.MapUtils.EmailUtils.EMAIL_BOX_COLUMN_TYPE;
+import static org.codeaurora.bluetooth.map.MapUtils.EmailUtils.EMAIL_BOX_PROJECTION;
+import static org.codeaurora.bluetooth.map.MapUtils.EmailUtils.EMAIL_BOX_URI;
+import static org.codeaurora.bluetooth.map.MapUtils.EmailUtils.EMAIL_MESSAGE_PROJECTION;
+import static org.codeaurora.bluetooth.map.MapUtils.EmailUtils.EMAIL_MESSAGE_URI;
+import static org.codeaurora.bluetooth.map.MapUtils.EmailUtils.EMAIL_URI;
+import static org.codeaurora.bluetooth.map.MapUtils.EmailUtils.MSG_COL_ACCOUNT_KEY;
+import static org.codeaurora.bluetooth.map.MapUtils.EmailUtils.MSG_COL_MAILBOX_KEY;
+import static org.codeaurora.bluetooth.map.MapUtils.EmailUtils.MSG_COL_RECORD_ID;
+import static org.codeaurora.bluetooth.map.MapUtils.EmailUtils.TYPE_DELETED;
+import static org.codeaurora.bluetooth.map.MapUtils.EmailUtils.TYPE_DRAFT;
+import static org.codeaurora.bluetooth.map.MapUtils.EmailUtils.TYPE_INBOX;
+import static org.codeaurora.bluetooth.map.MapUtils.EmailUtils.TYPE_OUTBOX;
+import static org.codeaurora.bluetooth.map.MapUtils.EmailUtils.TYPE_SENT;
+
+/**
+ * This class run an MNS session.
+ */
+public class BluetoothMnsEmail extends MnsClient {
+    private static final String TAG = "BluetoothMnsEmail";
+    private static final boolean V = BluetoothMasService.VERBOSE;
+    private static final String EMAIL_TO_MAP[] = {
+        "inbox",    // TYPE_INBOX = 0;
+        "",         // TYPE_MAIL = 1;
+        "",         // TYPE_PARENT = 2;
+        "draft",    // TYPE_DRAFTS = 3;
+        "outbox",   // TYPE_OUTBOX = 4;
+        "sent",     // TYPE_SENT = 5;
+        "deleted",  // TYPE_TRASH = 6;
+        ""          // TYPE_JUNK = 7;
+    };
+    private static final String EMAIL = "EMAIL";
+    private EmailContentObserver mEmailObserver = new EmailContentObserver();
+    private long mAccountKey;
+
+    public BluetoothMnsEmail(Context context, Integer masId) {
+        super(context, masId);
+    }
+
+    @Override
+    protected void registerContentObserver() {
+        if (V) Log.v(TAG, "REGISTERING EMAIL MNS");
+        mAccountKey = EmailUtils.getAccountId(mMasId);
+        mEmailObserver.updateEmailBox();
+        mEmailObserver.update(true);
+        mContext.getContentResolver().registerContentObserver(EMAIL_URI, true, mEmailObserver);
+        if (V) Log.v(TAG, "REGISTERING EMAIL MNS DONE");
+    }
+
+    @Override
+    protected void unregisterContentObserver() {
+        if (V) Log.v(TAG, "UNREGISTERING MNS EMAIL");
+        mContext.getContentResolver().unregisterContentObserver(mEmailObserver);
+        if (V) Log.v(TAG, "UNREGISTERED MNS EMAIL");
+    }
+
+    static class EmailBox {
+        long mId;
+        String mDisplayName;
+        long mAccountKey;
+        int mType;
+
+        public EmailBox(long id, String displayName, long accountKey, int type) {
+            mId = id;
+            mDisplayName = displayName;
+            mAccountKey = accountKey;
+            mType = type;
+        }
+
+        @Override
+        public String toString() {
+            return "[id:" + mId + ", display name:" + mDisplayName + ", account key:" +
+                    mAccountKey + ", type:" + mType + "]";
+        }
+    }
+
+    static class EmailMessage {
+        long mId;
+        long mAccountKey;
+        String mFolderName;
+        int mType;
+
+        public EmailMessage(long id, long accountKey, String folderName, int type) {
+            mId = id;
+            mAccountKey = accountKey;
+            mFolderName = folderName;
+            mType = type;
+        }
+
+        @Override
+        public String toString() {
+            return "[id:" + mId + ", folder name:" + mFolderName + ", account key:" + mAccountKey +
+                    ", type:" + mType + "]";
+        }
+    }
+
+    private class EmailContentObserver extends ContentObserver {
+        private static final String TAG = "EmailContentObserver";
+        private HashMap<Long, EmailBox> mEmailBoxList = new HashMap<Long, EmailBox>();
+        private HashMap<Long, EmailMessage> mEmailList = new HashMap<Long, EmailMessage>();
+        /** List of deleted message, do not notify */
+        private HashMap<Long, EmailMessage> mDeletedList = new HashMap<Long, EmailMessage>();
+        private HashMap<Long, EmailMessage> mEmailAddedList = new HashMap<Long, EmailMessage>();
+        /** List of newly deleted message, notify */
+        private HashMap<Long, EmailMessage> mEmailDeletedList = new HashMap<Long, EmailMessage>();
+
+        private static final int UPDATE = 0;
+        private static final int THRESHOLD = 3000;  // 3 sec
+
+        public EmailContentObserver() {
+            super(null);
+        }
+
+        @Override
+        public void onChange(boolean selfChange) {
+            if (V) Log.v(TAG, "onChange(" + selfChange + ")");
+            if (mHandler.hasMessages(UPDATE)) {
+                mHandler.removeMessages(UPDATE);
+            }
+            mHandler.sendEmptyMessageDelayed(UPDATE, THRESHOLD);
+        }
+
+        private Handler mHandler = new Handler() {
+            private static final String TAG = "EmailContentObserver.Hanlder";
+            @Override
+            public void handleMessage(android.os.Message msg) {
+                if (V) Log.v(TAG, "handleMessage(" + msg.what + ") mas Id: " + mMasId);
+                switch (msg.what) {
+                    case UPDATE:
+                        new Thread(new Runnable() {
+                            public void run() {
+                                updateEmailBox();
+                                update(false);
+                                sendEvents();
+                            }
+                        }, "Email Content Observer Thread").start();
+                        break;
+                }
+            }
+        };
+
+        void updateEmailBox() {
+            mEmailBoxList.clear();
+            final ContentResolver resolver = mContext.getContentResolver();
+            Cursor crBox = resolver.query(EMAIL_BOX_URI, EMAIL_BOX_PROJECTION, null, null, null);
+            if (crBox != null) {
+                if (crBox.moveToFirst()) {
+                    do {
+                        final long id = crBox.getLong(EMAIL_BOX_COLUMN_RECORD_ID);
+                        final String displayName = crBox.getString(EMAIL_BOX_COLUMN_DISPLAY_NAME);
+                        final long accountKey = crBox.getLong(EMAIL_BOX_COLUMN_ACCOUNT_KEY);
+                        final int type = crBox.getInt(EMAIL_BOX_COLUMN_TYPE);
+                        final EmailBox box = new EmailBox(id, displayName, accountKey, type);
+                        mEmailBoxList.put(id, box);
+                        if (V) Log.v(TAG, box.toString());
+                    } while (crBox.moveToNext());
+                }
+                crBox.close();
+            }
+        }
+
+        void update(boolean init) {
+            if (init) {
+                clear();
+            }
+            final ContentResolver resolver = mContext.getContentResolver();
+            Cursor crEmail = resolver.query(EMAIL_MESSAGE_URI, EMAIL_MESSAGE_PROJECTION,
+                    null, null, null);
+            if (crEmail != null) {
+                if (crEmail.moveToFirst()) {
+                    final HashMap<Long, EmailBox> boxList = mEmailBoxList;
+                    HashMap<Long, EmailMessage> oldEmailList = mEmailList;
+                    HashMap<Long, EmailMessage> emailList = new HashMap<Long, EmailMessage>();
+                    do {
+                        final long accountKey = crEmail.getLong(MSG_COL_ACCOUNT_KEY);
+                        if (accountKey != mAccountKey) {
+                            continue;
+                        }
+                        final long id = crEmail.getLong(MSG_COL_RECORD_ID);
+                        final long mailboxKey = crEmail.getLong(MSG_COL_MAILBOX_KEY);
+                        if (boxList.containsKey(mailboxKey)) {
+                            final EmailBox box = boxList.get(mailboxKey);
+                            if (box == null) {
+                                continue;
+                            }
+                            final String folderName = isMapFolder(box.mType)
+                                    ? EMAIL_TO_MAP[box.mType] : box.mDisplayName;
+                            final EmailMessage msg = new EmailMessage(id, accountKey,
+                                    folderName, box.mType);
+                            if (box.mType == EmailUtils.TYPE_DELETED) {
+                                if (init) {
+                                    mDeletedList.put(id, msg);
+                                } else if (!mDeletedList.containsKey(id) &&
+                                        !mEmailDeletedList.containsKey(id)) {
+                                    mEmailDeletedList.put(id, msg);
+                                }
+                            } else {
+                                emailList.put(id, msg);
+                                if (!oldEmailList.containsKey(id) && !init &&
+                                        !mEmailAddedList.containsKey(id)) {
+                                    mEmailAddedList.put(id, msg);
+                                }
+                            }
+                        } else {
+                            Log.e(TAG, "Mailbox is not updated");
+                        }
+                    } while (crEmail.moveToNext());
+                    mEmailList = emailList;
+                }
+                crEmail.close();
+            }
+        }
+
+        private void sendEvents() {
+            if (mEmailAddedList.size() > 0) {
+                newEmail();
+                mEmailAddedList.clear();
+            }
+            if (mEmailDeletedList.size() > 0) {
+                mDeletedList.putAll(mEmailDeletedList);
+                deletedEmail();
+                mEmailDeletedList.clear();
+            }
+        }
+
+        private void clear() {
+            mEmailList.clear();
+            mDeletedList.clear();
+            mEmailAddedList.clear();
+            mEmailDeletedList.clear();
+        }
+
+        private boolean isMapFolder(int type) {
+            if (type == TYPE_INBOX || type == TYPE_OUTBOX || type == TYPE_SENT ||
+                    type == TYPE_DRAFT || type == TYPE_DELETED) {
+                return true;
+            }
+            return false;
+        }
+
+        private void newEmail() {
+            if (V) Log.v(TAG, "newEmail()");
+            if (mListener != null) {
+                Collection<EmailMessage> values = mEmailAddedList.values();
+                for (EmailMessage email : values) {
+                    if (V) Log.v(TAG, email.toString());
+                    mListener.onNewMessage(mMasId, String.valueOf(email.mId + OFFSET_START),
+                            PRE_PATH + email.mFolderName, EMAIL);
+                    if (email.mType == TYPE_SENT) {
+                        mListener.onSendingSuccess(mMasId, String.valueOf(email.mId + OFFSET_START),
+                                PRE_PATH + email.mFolderName, EMAIL);
+                    }
+                }
+            }
+        }
+
+        private void deletedEmail() {
+            if (V) Log.v(TAG, "deletedEmail()");
+            if (mListener != null) {
+                Collection<EmailMessage> values = mEmailDeletedList.values();
+                for (EmailMessage email : values) {
+                    if (V) Log.v(TAG, email.toString());
+                    mListener.onMessageDeleted(mMasId, String.valueOf(email.mId + OFFSET_START),
+                            PRE_PATH + email.mFolderName, EMAIL);
+                }
+            }
+        }
+    };
+}
diff --git a/src/org/codeaurora/bluetooth/map/BluetoothMnsObexSession.java b/src/org/codeaurora/bluetooth/map/BluetoothMnsObexSession.java
new file mode 100644
index 0000000..6430ecc
--- /dev/null
+++ b/src/org/codeaurora/bluetooth/map/BluetoothMnsObexSession.java
@@ -0,0 +1,312 @@
+/*
+ * Copyright (c) 2008-2009, Motorola, Inc.
+ * Copyright (c) 2010-2012, The Linux Foundation. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *        * Redistributions of source code must retain the above copyright
+ *          notice, this list of conditions and the following disclaimer.
+ *        * Redistributions in binary form must reproduce the above copyright
+ *          notice, this list of conditions and the following disclaimer in the
+ *          documentation and/or other materials provided with the distribution.
+ *        * Neither the name of The Linux Foundation nor
+ *          the names of its contributors may be used to endorse or promote
+ *          products derived from this software without specific prior written
+ *          permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NON-INFRINGEMENT ARE DISCLAIMED.    IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
+ * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
+ * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
+ * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package org.codeaurora.bluetooth.map;
+
+import android.content.Context;
+import android.os.Handler;
+import android.util.Log;
+import android.os.PowerManager;
+
+import java.io.BufferedInputStream;
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.IOException;
+import java.io.OutputStream;
+
+import javax.obex.ApplicationParameter;
+import javax.obex.ClientOperation;
+import javax.obex.ClientSession;
+import javax.obex.HeaderSet;
+import javax.obex.ObexTransport;
+import javax.obex.ResponseCodes;
+
+/**
+ * This class runs as an OBEX client
+ */
+
+// TBD - Do applications need to do anything for power management?
+
+public class BluetoothMnsObexSession {
+
+    private static final String TAG = "BtMns ObexClient";
+    private static final boolean D = BluetoothMasService.DEBUG;
+    private static final boolean V = BluetoothMasService.VERBOSE;
+
+    public final static int MSG_SESSION_ERROR = 1;
+    public final static int MSG_CONNECT_TIMEOUT = 2;
+
+    private ObexTransport mTransport;
+
+    private Context mContext;
+
+    private volatile boolean mWaitingForRemote;
+
+    private PowerManager.WakeLock mWakeLock = null;
+
+    private static final String TYPE_EVENT = "x-bt/MAP-event-report";
+
+    public BluetoothMnsObexSession(Context context, ObexTransport transport) {
+        if (transport == null) {
+            throw new NullPointerException("transport is null");
+        }
+        mContext = context;
+        mTransport = transport;
+    }
+
+    private ClientSession mCs;
+
+    private boolean mConnected = false;
+
+    public boolean isConnected() {
+        return mConnected;
+    }
+
+    public void disconnect() {
+        if(D) Log.d(TAG, "BluetoothMnsObexSession: disconnect");
+        acquireMnsLock();
+        try {
+            if (mCs != null) {
+                mCs.disconnect(null);
+            }
+            mCs = null;
+            if (D) Log.d(TAG, "OBEX session disconnected");
+        } catch (IOException e) {
+            Log.w(TAG, "OBEX session disconnect error " + e.getMessage());
+        }
+        try {
+            if (mCs != null) {
+                if (D) Log.d(TAG, "OBEX session close mCs");
+                mCs.close();
+                if (D) Log.d(TAG, "OBEX session closed");
+            }
+        } catch (IOException e) {
+            Log.w(TAG, "OBEX session close error" + e.getMessage());
+        }
+        try {
+            if (mTransport != null) {
+                mTransport.close();
+            }
+        } catch (IOException e) {
+            Log.e(TAG, "mTransport.close error " + e.getMessage());
+        }
+        if(D) Log.d(TAG, "BluetoothMnsObexSession: exiting from disconnect");
+        releaseMnsLock();
+    }
+
+    private HeaderSet hsConnect = null;
+
+    public void connect() {
+        if(D) Log.d(TAG, "BluetoothMnsObexSession: Create ClientSession with transport " + mTransport.toString());
+        acquireMnsLock();
+        try {
+            mCs = new ClientSession(mTransport);
+            mConnected = true;
+        } catch (IOException e1) {
+            Log.e(TAG, "OBEX session create error " + e1.getMessage());
+        }
+        if (mConnected && mCs != null) {
+            mConnected = false;
+            HeaderSet hs = new HeaderSet();
+            // bb582b41-420c-11db-b0de-0800200c9a66
+            byte[] mnsTarget = { (byte) 0xbb, (byte) 0x58, (byte) 0x2b, (byte) 0x41,
+                                 (byte) 0x42, (byte) 0x0c, (byte) 0x11, (byte) 0xdb,
+                                 (byte) 0xb0, (byte) 0xde, (byte) 0x08, (byte) 0x00,
+                                 (byte) 0x20, (byte) 0x0c, (byte) 0x9a, (byte) 0x66 };
+            hs.setHeader(HeaderSet.TARGET, mnsTarget);
+
+            synchronized (this) {
+                mWaitingForRemote = true;
+            }
+            try {
+                hsConnect = mCs.connect(hs);
+                if (D) Log.d(TAG, "OBEX session created");
+                mConnected = true;
+            } catch (IOException e) {
+                Log.e(TAG, "OBEX session connect error " + e.getMessage());
+            }
+        }
+            synchronized (this) {
+                mWaitingForRemote = false;
+        }
+        if(D) Log.d(TAG, "BluetoothMnsObexSession: exiting from connect");
+        releaseMnsLock();
+    }
+
+    public int sendEvent(File file, byte masInstanceId) {
+        Log.d(TAG, "BluetoothMnsObexSession: sendEvent");
+        acquireMnsLock();
+        boolean error = false;
+        int responseCode = -1;
+        HeaderSet request;
+        byte[] val = new byte[1];
+        val[0] =  masInstanceId;
+        request = new HeaderSet();
+        ApplicationParameter ap = new ApplicationParameter();
+        ap.addAPPHeader((byte)BluetoothMasSpecParams.MAS_TAG_MAS_INSTANCE_ID,
+                        (byte)BluetoothMasSpecParams.MAS_TAG_MAS_INSTANCE_ID_LEN,
+                         val);
+        request.setHeader(HeaderSet.TYPE, TYPE_EVENT);
+        request.setHeader(HeaderSet.APPLICATION_PARAMETER, ap.getAPPparam());
+
+        request.mConnectionID = new byte[4];
+        System.arraycopy(hsConnect.mConnectionID, 0, request.mConnectionID, 0, 4);
+
+        ClientOperation putOperation = null;
+        OutputStream outputStream = null;
+        FileInputStream fileInputStream = null;
+        try {
+            synchronized (this) {
+                mWaitingForRemote = true;
+            }
+            // Send the header first and then the body
+            try {
+                if (V) Log.v(TAG, "Send headerset Event ");
+                putOperation = (ClientOperation)mCs.put(request);
+                // TODO - Should this be kept or Removed
+
+            } catch (IOException e) {
+                Log.e(TAG, "Error when put HeaderSet " + e.getMessage());
+                error = true;
+            }
+            synchronized (this) {
+                mWaitingForRemote = false;
+            }
+            if (!error) {
+                try {
+                    if (V) Log.v(TAG, "Send headerset Event ");
+                    outputStream = putOperation.openOutputStream();
+                } catch (IOException e) {
+                    Log.e(TAG, "Error when opening OutputStream " + e.getMessage());
+                    error = true;
+                }
+            }
+
+            if (!error) {
+                int position = 0;
+                int readLength = 0;
+                long timestamp = 0;
+                int outputBufferSize = putOperation.getMaxPacketSize();
+                byte[] buffer = new byte[outputBufferSize];
+
+                fileInputStream = new FileInputStream(file);
+                BufferedInputStream a = new BufferedInputStream(fileInputStream, 0x4000);
+
+                while ((position != file.length())) {
+                    if (V) timestamp = System.currentTimeMillis();
+
+                    readLength = a.read(buffer, 0, outputBufferSize);
+                    outputStream.write(buffer, 0, readLength);
+
+                    position += readLength;
+                    if (V) {
+                        Log.v(TAG, "Sending file position = " + position
+                                + " readLength " + readLength + " bytes took "
+                                + (System.currentTimeMillis() - timestamp) + " ms");
+                    }
+                }
+                if (position == file.length()) {
+                    Log.i(TAG, "SendFile finished send out file " + file.length()
+                            + " length " + file.length());
+                    outputStream.close();
+                } else {
+                    error = true;
+                    // TBD - Is Output stream close needed here
+                    putOperation.abort();
+                    Log.i(TAG, "SendFile interrupted when send out file "
+                            + " at " + position + " of " + file.length());
+                }
+            }
+        } catch (IOException e) {
+            handleSendException(e.toString());
+            error = true;
+        } catch (IndexOutOfBoundsException e) {
+            handleSendException(e.toString());
+            error = true;
+        } finally {
+            if (fileInputStream != null) {
+                try {
+                    fileInputStream.close();
+                } catch (IOException ei) {
+                    Log.e(TAG, "Error while closing stream"+ ei.toString());
+                }
+            }
+            try {
+                if (!error) {
+                    responseCode = putOperation.getResponseCode();
+                    if (responseCode != -1) {
+                        if (V) Log.v(TAG, "Get response code " + responseCode);
+                        if (responseCode != ResponseCodes.OBEX_HTTP_OK) {
+                            Log.i(TAG, "Response error code is " + responseCode);
+                        }
+                    }
+                }
+                if (putOperation != null) {
+                   putOperation.close();
+                }
+            } catch (IOException e) {
+                Log.e(TAG, "Error when closing stream after send " + e.getMessage());
+            }
+        }
+        if(D) Log.d(TAG, "BluetoothMnsObexSession: exiting from sendEvent");
+        releaseMnsLock();
+        return responseCode;
+    }
+
+    private void handleSendException(String exception) {
+        Log.e(TAG, "Error when sending event: " + exception);
+    }
+
+    private void acquireMnsLock() {
+        if (V) Log.v(TAG, "About to acquire Mns:mWakeLock");
+        if (mWakeLock == null) {
+            PowerManager pm = (PowerManager) mContext.getSystemService(Context.POWER_SERVICE);
+            mWakeLock = pm.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, "MnsPartialWakeLock");
+            mWakeLock.setReferenceCounted(false);
+            mWakeLock.acquire();
+            if (V) Log.v(TAG, "Mns:mWakeLock acquired");
+        }
+        else {
+            Log.e(TAG, "Mns:mWakeLock already acquired");
+        }
+    }
+
+    public void releaseMnsLock() {
+        if (V) Log.v(TAG, "About to release Mns:mWakeLock");
+        if (mWakeLock != null) {
+            if (mWakeLock.isHeld()) {
+                mWakeLock.release();
+                if (V) Log.v(TAG, "Mns:mWakeLock released");
+            } else {
+                if (V) Log.v(TAG, "Mns:mWakeLock already released");
+            }
+            mWakeLock = null;
+        }
+    }
+}
diff --git a/src/org/codeaurora/bluetooth/map/BluetoothMnsPreference.java b/src/org/codeaurora/bluetooth/map/BluetoothMnsPreference.java
new file mode 100644
index 0000000..4d4d673
--- /dev/null
+++ b/src/org/codeaurora/bluetooth/map/BluetoothMnsPreference.java
@@ -0,0 +1,167 @@
+/*
+ * Copyright (c) 2008-2009, Motorola, Inc.
+ * Copyright (c) 2010-2011, The Linux Foundation. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *        * Redistributions of source code must retain the above copyright
+ *          notice, this list of conditions and the following disclaimer.
+ *        * Redistributions in binary form must reproduce the above copyright
+ *          notice, this list of conditions and the following disclaimer in the
+ *          documentation and/or other materials provided with the distribution.
+ *        * Neither the name of The Linux Foundation nor
+ *          the names of its contributors may be used to endorse or promote
+ *          products derived from this software without specific prior written
+ *          permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NON-INFRINGEMENT ARE DISCLAIMED.    IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
+ * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
+ * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
+ * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package org.codeaurora.bluetooth.map;
+
+import android.bluetooth.BluetoothDevice;
+import android.content.Context;
+import android.content.SharedPreferences;
+import android.content.SharedPreferences.Editor;
+import android.util.Log;
+
+import java.util.HashMap;
+
+/**
+ * This class cache Bluetooth device name and channel locally. Its a temp
+ * solution which should be replaced by bluetooth_devices in SettingsProvider
+ */
+public class BluetoothMnsPreference {
+
+    private static final String TAG = "BluetoothMnsPreference";
+    private static final boolean D = BluetoothMasService.DEBUG;
+    private static final boolean V = BluetoothMasService.VERBOSE;
+
+    private static BluetoothMnsPreference INSTANCE;
+
+    /* Used when obtaining a reference to the singleton instance. */
+    private static Object INSTANCE_LOCK = new Object();
+
+    private boolean mInitialized;
+
+    private Context mContext;
+
+    private SharedPreferences mNamePreference;
+
+    private SharedPreferences mChannelPreference;
+
+    private HashMap<String, Integer> mChannels = new HashMap<String, Integer>();
+
+    private HashMap<String, String> mNames = new HashMap<String, String>();
+
+    public static final String BLUETOOTHMNS_NAME_PREFERENCE = "btmns_names";
+
+    public static final String BLUETOOTHMNS_CHANNEL_PREFERENCE = "btmns_channels";
+
+
+    public static BluetoothMnsPreference getInstance(Context context) {
+        synchronized (INSTANCE_LOCK) {
+            if (INSTANCE == null) {
+                INSTANCE = new BluetoothMnsPreference();
+            }
+            if (!INSTANCE.init(context)) {
+                return null;
+            }
+            return INSTANCE;
+        }
+    }
+
+    private boolean init(Context context) {
+        if (mInitialized)
+            return true;
+        mInitialized = true;
+
+        mContext = context;
+
+        mNamePreference = mContext.getSharedPreferences(
+                BLUETOOTHMNS_NAME_PREFERENCE, Context.MODE_PRIVATE);
+        mChannelPreference = mContext.getSharedPreferences(
+                BLUETOOTHMNS_CHANNEL_PREFERENCE, Context.MODE_PRIVATE);
+
+        mNames = (HashMap<String, String>) mNamePreference.getAll();
+        mChannels = (HashMap<String, Integer>) mChannelPreference.getAll();
+
+        return true;
+    }
+
+    private String getChannelKey(BluetoothDevice remoteDevice, int uuid) {
+        return remoteDevice.getAddress() + "_" + Integer.toHexString(uuid);
+    }
+
+    public String getName(BluetoothDevice remoteDevice) {
+        if (remoteDevice.getAddress().equals("FF:FF:FF:00:00:00")) {
+            return "localhost";
+        }
+        if (!mNames.isEmpty()) {
+            String name = mNames.get(remoteDevice.getAddress());
+            if (name != null) {
+                return name;
+            }
+        }
+        return null;
+    }
+
+    public int getChannel(BluetoothDevice remoteDevice, int uuid) {
+        String key = getChannelKey(remoteDevice, uuid);
+        if (V) Log.v(TAG, "getChannel " + key);
+        Integer channel = null;
+        if (mChannels != null) {
+            channel = mChannels.get(key);
+            if (V) Log.v(TAG, "getChannel for " + remoteDevice + "_"
+                + Integer.toHexString(uuid) + " as " + channel);
+        }
+        return (channel != null) ? channel : -1;
+    }
+
+    public void setName(BluetoothDevice remoteDevice, String name) {
+        if (V) Log.v(TAG, "Setname for " + remoteDevice + " to " + name);
+        if (!name.equals(getName(remoteDevice))) {
+            Editor ed = mNamePreference.edit();
+            ed.putString(remoteDevice.getAddress(), name);
+            ed.commit();
+            mNames.put(remoteDevice.getAddress(), name);
+        }
+    }
+
+    public void setChannel(BluetoothDevice remoteDevice, int uuid, int channel) {
+        if (V) Log.v(TAG, "Setchannel for " + remoteDevice + "_"
+            + Integer.toHexString(uuid) + " to " + channel);
+        if (channel != getChannel(remoteDevice, uuid)) {
+            String key = getChannelKey(remoteDevice, uuid);
+            Editor ed = mChannelPreference.edit();
+            ed.putInt(key, channel);
+            ed.commit();
+            mChannels.put(key, channel);
+        }
+    }
+
+    public void removeChannel(BluetoothDevice remoteDevice, int uuid) {
+        String key = getChannelKey(remoteDevice, uuid);
+        Editor ed = mChannelPreference.edit();
+        ed.remove(key);
+        ed.commit();
+        mChannels.remove(key);
+    }
+
+    public void dump() {
+        Log.d(TAG, "Dumping Names:  ");
+        Log.d(TAG, mNames.toString());
+        Log.d(TAG, "Dumping Channels:  ");
+        Log.d(TAG, mChannels.toString());
+    }
+}
diff --git a/src/org/codeaurora/bluetooth/map/BluetoothMnsRfcommTransport.java b/src/org/codeaurora/bluetooth/map/BluetoothMnsRfcommTransport.java
new file mode 100644
index 0000000..3a3cfd9
--- /dev/null
+++ b/src/org/codeaurora/bluetooth/map/BluetoothMnsRfcommTransport.java
@@ -0,0 +1,93 @@
+/*
+ * Copyright (c) 2010-2011, The Linux Foundation. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *        * Redistributions of source code must retain the above copyright
+ *          notice, this list of conditions and the following disclaimer.
+ *        * Redistributions in binary form must reproduce the above copyright
+ *          notice, this list of conditions and the following disclaimer in the
+ *          documentation and/or other materials provided with the distribution.
+ *        * Neither the name of The Linux Foundation nor
+ *          the names of its contributors may be used to endorse or promote
+ *          products derived from this software without specific prior written
+ *          permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NON-INFRINGEMENT ARE DISCLAIMED.    IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
+ * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
+ * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
+ * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package org.codeaurora.bluetooth.map;
+
+import android.bluetooth.BluetoothSocket;
+
+import java.io.DataInputStream;
+import java.io.DataOutputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+
+import javax.obex.ObexTransport;
+
+public class BluetoothMnsRfcommTransport implements ObexTransport {
+
+    private final BluetoothSocket mSocket;
+
+    public BluetoothMnsRfcommTransport(BluetoothSocket socket) {
+        super();
+        this.mSocket = socket;
+    }
+
+    public void close() throws IOException {
+        mSocket.close();
+    }
+
+    public DataInputStream openDataInputStream() throws IOException {
+        return new DataInputStream(openInputStream());
+    }
+
+    public DataOutputStream openDataOutputStream() throws IOException {
+        return new DataOutputStream(openOutputStream());
+    }
+
+    public InputStream openInputStream() throws IOException {
+        return mSocket.getInputStream();
+    }
+
+    public OutputStream openOutputStream() throws IOException {
+        return mSocket.getOutputStream();
+    }
+
+    public void connect() throws IOException {
+    }
+
+    public void create() throws IOException {
+    }
+
+    public void disconnect() throws IOException {
+    }
+
+    public void listen() throws IOException {
+    }
+
+    public boolean isConnected() throws IOException {
+        // TODO: add implementation
+        return true;
+    }
+
+    public String getRemoteAddress() {
+        if (mSocket == null)
+            return null;
+        return mSocket.getRemoteDevice().getAddress();
+    }
+
+}
diff --git a/src/org/codeaurora/bluetooth/map/BluetoothMnsSmsMms.java b/src/org/codeaurora/bluetooth/map/BluetoothMnsSmsMms.java
new file mode 100644
index 0000000..b7a062a
--- /dev/null
+++ b/src/org/codeaurora/bluetooth/map/BluetoothMnsSmsMms.java
@@ -0,0 +1,366 @@
+/*
+ * Copyright (c) 2011, The Linux Foundation. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *        * Redistributions of source code must retain the above copyright
+ *          notice, this list of conditions and the following disclaimer.
+ *        * Redistributions in binary form must reproduce the above copyright
+ *          notice, this list of conditions and the following disclaimer in the
+ *          documentation and/or other materials provided with the distribution.
+ *        * Neither the name of The Linux Foundation nor
+ *          the names of its contributors may be used to endorse or promote
+ *          products derived from this software without specific prior written
+ *          permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NON-INFRINGEMENT ARE DISCLAIMED.    IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
+ * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
+ * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
+ * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package org.codeaurora.bluetooth.map;
+
+import android.content.ContentResolver;
+import android.content.Context;
+import android.database.ContentObserver;
+import android.database.Cursor;
+import android.os.Handler;
+import android.provider.Telephony.Mms;
+import android.provider.Telephony.MmsSms;
+import android.provider.Telephony.Sms;
+import android.telephony.TelephonyManager;
+import android.util.Log;
+
+import org.codeaurora.bluetooth.map.BluetoothMns.MnsClient;
+import com.google.android.mms.pdu.PduHeaders;
+
+import java.util.Collection;
+import java.util.concurrent.ConcurrentHashMap;
+
+import static org.codeaurora.bluetooth.map.MapUtils.SmsMmsUtils.DELETED;
+import static org.codeaurora.bluetooth.map.MapUtils.SmsMmsUtils.OUTBOX;
+import static org.codeaurora.bluetooth.map.MapUtils.SmsMmsUtils.SENT;
+
+/**
+ * This class run an MNS session.
+ */
+public class BluetoothMnsSmsMms extends MnsClient {
+    private static final String TAG = "BluetoothMnsSmsMms";
+    private static final boolean V = BluetoothMasService.VERBOSE;
+    private static final String MSG_TO_MAP[] = {
+        "",         // ALL
+        "inbox",    // INBOX
+        "sent",     // SENT
+        "draft",    // DRAFT
+        "outbox",   // OUTBOX
+        "outbox",   // FAILED
+        "outbox",   // QUEUED
+        "inbox",    // INBOX_SUB1
+        "inbox",    // INBOX_SUB2
+    };
+    private static final String SMS_GSM = "SMS_GSM";
+    private static final String SMS_CDMA = "SMS_CDMA";
+    private static final String MMS = "MMS";
+    private final long SMS_OFFSET_START;
+    private final long MMS_OFFSET_START;
+
+    private MmsSmsContentObserver mMmsSmsObserver = new MmsSmsContentObserver();
+
+    public BluetoothMnsSmsMms(Context context, Integer masId) {
+        super(context, masId);
+        SMS_OFFSET_START = OFFSET_START;
+        MMS_OFFSET_START = OFFSET_START + ((OFFSET_END - OFFSET_START) / 2);
+    }
+
+    @Override
+    protected void registerContentObserver() {
+        if (V) Log.v(TAG, "REGISTERING SMS/MMS MNS");
+        mMmsSmsObserver.update(true);
+        mContext.getContentResolver().registerContentObserver(MmsSms.CONTENT_URI, true,
+                mMmsSmsObserver);
+        if (V) Log.v(TAG, "REGISTERING SMS/MMS MNS DONE");
+    }
+
+    @Override
+    protected void unregisterContentObserver() {
+        if (V) Log.v(TAG, "UNREGISTERING MNS SMS/MMS");
+        mContext.getContentResolver().unregisterContentObserver(mMmsSmsObserver);
+        if (V) Log.v(TAG, "UNREGISTERING MNS SMS/MMS DONE");
+    }
+
+    private static final String[] MMS_PROJECTION = new String[] {Mms._ID, Mms.MESSAGE_BOX,
+        Mms.THREAD_ID, Mms.MESSAGE_TYPE, Mms.DATE};
+    private static final int MMS_ID_COL = 0;
+    private static final int MMS_BOX_TYPE_COL = 1;
+    private static final int MMS_THREAD_ID_COL = 2;
+    private static final int MMS_MSG_TYPE_COL = 3;
+    private static final int MMS_DATE_COL = 4;
+
+    private static final String[] SMS_PROJECTION = new String[] {Sms._ID, Sms.TYPE, Sms.THREAD_ID,
+        Sms.DATE};
+    private static final int SMS_ID_COL = 0;
+    private static final int SMS_TYPE_COL = 1;
+    private static final int SMS_THREAD_ID_COL = 2;
+    private static final int SMS_DATE_COL = 3;
+
+    static class Message {
+        long mId;
+        String mFolderName;
+        int mType;
+        long mThreadId;
+        long mDate;
+
+        public Message(long id, String folderName, int type, long threadId, long date) {
+            mId = id;
+            mFolderName = folderName;
+            mType = type;
+            mThreadId = threadId;
+            mDate = date;
+        }
+    }
+
+    private class MmsSmsContentObserver extends ContentObserver {
+        private static final String TAG = "MmsSmsContentObserver";
+        private ConcurrentHashMap<Long, Message> mSmsList = new ConcurrentHashMap<Long, Message>();
+        private ConcurrentHashMap<Long, Message> mSmsAddedList = new ConcurrentHashMap<Long, Message>();
+        private ConcurrentHashMap<Long, Message> mSmsDeletedList = new ConcurrentHashMap<Long, Message>();
+
+        private ConcurrentHashMap<Long, Message> mMmsList = new ConcurrentHashMap<Long, Message>();
+        private ConcurrentHashMap<Long, Message> mMmsAddedList = new ConcurrentHashMap<Long, Message>();
+        private ConcurrentHashMap<Long, Message> mMmsDeletedList = new ConcurrentHashMap<Long, Message>();
+
+        private static final int UPDATE = 0;
+        private static final int THRESHOLD = 1500;  // 1.5 sec
+
+        public MmsSmsContentObserver() {
+            super(null);
+        }
+
+        @Override
+        public void onChange(boolean selfChange) {
+            if (V) Log.v(TAG, "onChange(" + selfChange + ") SMS mas Id: " + mMasId);
+            if (mHandler.hasMessages(UPDATE)) {
+                mHandler.removeMessages(UPDATE);
+            }
+            mHandler.sendEmptyMessageDelayed(UPDATE, THRESHOLD);
+        }
+
+        private Handler mHandler = new Handler() {
+            private static final String TAG = "MmsSmsContentObserver.Hanlder";
+            @Override
+            public void handleMessage(android.os.Message msg) {
+                if (V) Log.v(TAG, "handleMessage(" + msg.what + ") mas Id: " + mMasId);
+                switch (msg.what) {
+                    case UPDATE:
+                        new Thread(new Runnable() {
+                            public void run() {
+                                update(false);
+                                sendEvents();
+                            }
+                        }, "MmsSms Content Observer Thread").start();
+                        break;
+                }
+            }
+        };
+
+        void update(boolean init) {
+            updateSms(init);
+            updateMms(init);
+        }
+
+        void updateSms(boolean init) {
+            if (init) {
+                clearSms();
+            }
+
+           if(V) Log.v(TAG, "MNS_SMS: updateSMS");
+            final ContentResolver resolver = mContext.getContentResolver();
+            Cursor crSms = resolver.query(Sms.CONTENT_URI, SMS_PROJECTION, null, null, null);
+            if (crSms != null) {
+                if (crSms.moveToFirst()) {
+                    ConcurrentHashMap<Long, Message> oldSmsList = mSmsList;
+                    ConcurrentHashMap<Long, Message> newSmsList = new ConcurrentHashMap<Long, Message>();
+                    do {
+                        final long id = crSms.getLong(SMS_ID_COL);
+                        final int type = crSms.getInt(SMS_TYPE_COL);
+                        final long threadId = crSms.getLong(SMS_THREAD_ID_COL);
+                        final long date = crSms.getLong(SMS_DATE_COL);
+                        if (type > 0 && type < MSG_TO_MAP.length) {
+                            final Message msg = new Message(id, MSG_TO_MAP[type], type, threadId,
+                                    date);
+                            if(V) Log.v(TAG, "MNS_SMS: Found: id: " + id+ "  type: " + type + " threadId: " + threadId + " date: " + date +"\n");
+                            newSmsList.put(id, msg);
+                            if (oldSmsList.containsKey(id)) {
+                                Message old_msg = oldSmsList.remove(id);
+                                if (msg.mType != old_msg.mType) {
+                                    if(V) Log.v(TAG, "MNS_SMS: Add to mSmsAddedList from old");
+                                    mSmsAddedList.put(id, msg);
+                                }
+                            }
+                            else {
+                                final Message oldMsg = oldSmsList.remove(id);
+                                if (!init && (oldMsg == null || oldMsg.mDate != date)) {
+                                    if(V) Log.v(TAG, "MNS_SMS: Add to mSmsAddedList");
+                                    mSmsAddedList.put(id, msg);
+                                }
+                            }
+                        }
+                    } while (crSms.moveToNext());
+                    mSmsList = newSmsList;
+                    mSmsDeletedList = oldSmsList;
+                }
+                else
+                {
+                    // Last sms to be deleted
+                    if(mSmsList.size() > 0)
+                    {
+                        if(V) Log.v(TAG, "MNS_SMS: mSmsList Length: " + mSmsList.size());
+                        mSmsDeletedList = mSmsList;
+                    }
+                }
+                crSms.close();
+            }
+        }
+
+        void updateMms(boolean init) {
+            if (init) {
+                clearMms();
+            }
+            final ContentResolver resolver = mContext.getContentResolver();
+            Cursor crMms = resolver.query(Mms.CONTENT_URI, MMS_PROJECTION, null, null, null);
+            if (crMms != null) {
+                if (crMms.moveToFirst()) {
+                    ConcurrentHashMap<Long, Message> oldMmsList = mMmsList;
+                    ConcurrentHashMap<Long, Message> newMmsList = new ConcurrentHashMap<Long, Message>();
+                    do {
+                        final long id = crMms.getInt(MMS_ID_COL);
+                        final int boxType = crMms.getInt(MMS_BOX_TYPE_COL);
+                        final long threadId = crMms.getLong(MMS_THREAD_ID_COL);
+                        final int msgType = crMms.getInt(MMS_MSG_TYPE_COL);
+                        final long date = crMms.getLong(MMS_DATE_COL);
+                        // TODO need to filter out Pdu by message type?
+                        if (msgType != PduHeaders.MESSAGE_TYPE_NOTIFICATION_IND &&
+                                msgType != PduHeaders.MESSAGE_TYPE_DELIVERY_IND) {
+                            final Message msg = new Message(id, MSG_TO_MAP[boxType],
+                                    boxType, threadId, date);
+                            newMmsList.put(id, msg);
+                            final Message oldMsg = oldMmsList.remove(id);
+                            if (!init && (oldMsg == null || oldMsg.mDate != date)) {
+                                mMmsAddedList.put(id, msg);
+                            }
+                        }
+                    } while (crMms.moveToNext());
+                    mMmsList = newMmsList;
+                    mMmsDeletedList = oldMmsList;
+                }
+                crMms.close();
+            }
+        }
+
+        private void sendEvents() {
+            if (mSmsAddedList.size() > 0) {
+                newSms();
+                mSmsAddedList.clear();
+            }
+            if (mSmsDeletedList.size() > 0) {
+                deletedSms();
+                mSmsDeletedList.clear();
+            }
+            if (mMmsAddedList.size() > 0) {
+                newMms();
+                mMmsAddedList.clear();
+            }
+            if (mMmsDeletedList.size() > 0) {
+                deletedMms();
+                mMmsDeletedList.clear();
+            }
+        }
+
+        private void clearSms() {
+            mSmsList.clear();
+            mSmsAddedList.clear();
+            mSmsDeletedList.clear();
+        }
+
+        private void clearMms() {
+            mMmsList.clear();
+            mMmsAddedList.clear();
+            mMmsDeletedList.clear();
+        }
+
+        private void newSms() {
+            if (V) Log.v(TAG, "newSms() SMS mas Id: " + mMasId);
+            if (mListener != null) {
+                final int phoneType = TelephonyManager.getDefault().getPhoneType();
+                final String type = (phoneType == TelephonyManager.PHONE_TYPE_CDMA)
+                        ? SMS_CDMA : SMS_GSM;
+                Collection<Message> values = mSmsAddedList.values();
+                for (Message msg : values) {
+                    if (msg.mType == Sms.MESSAGE_TYPE_SENT) {
+                        mListener.onSendingSuccess(mMasId, String.valueOf(SMS_OFFSET_START +
+                                msg.mId), PRE_PATH + SENT, type);
+                    } else if (msg.mType == Sms.MESSAGE_TYPE_FAILED) {
+                        mListener.onSendingFailure(mMasId, String.valueOf(SMS_OFFSET_START +
+                                msg.mId), PRE_PATH + OUTBOX, type);
+                    } else if ((msg.mType == Sms.MESSAGE_TYPE_INBOX) ||
+                               (msg.mType == Sms.MESSAGE_TYPE_OUTBOX)) {
+                        String folderName = (msg.mThreadId == -1) ? DELETED : msg.mFolderName;
+                        mListener.onNewMessage(mMasId, String.valueOf(SMS_OFFSET_START + msg.mId),
+                                PRE_PATH + folderName, type);
+                    }
+                }
+            }
+        }
+
+        private void deletedSms() {
+            if (V) Log.v(TAG, "deletedSms() SMS mas Id: " + mMasId);
+            if (mListener != null) {
+                final int phoneType = TelephonyManager.getDefault().getPhoneType();
+                final String type = (phoneType == TelephonyManager.PHONE_TYPE_CDMA)
+                        ? SMS_CDMA : SMS_GSM;
+                Collection<Message> values = mSmsDeletedList.values();
+                for (Message msg : values) {
+                    String folderName = (msg.mThreadId == -1) ? DELETED : msg.mFolderName;
+                    mListener.onMessageDeleted(mMasId, String.valueOf(SMS_OFFSET_START + msg.mId),
+                            PRE_PATH + folderName, type);
+                }
+            }
+        }
+
+        private void newMms() {
+            if (V) Log.v(TAG, "newMms() MMS mas Id: " + mMasId);
+            if (mListener != null) {
+                Collection<Message> values = mMmsAddedList.values();
+                for (Message msg : values) {
+                    String folderName = (msg.mThreadId == -1) ? DELETED : msg.mFolderName;
+                    mListener.onNewMessage(mMasId, String.valueOf(MMS_OFFSET_START + msg.mId),
+                            PRE_PATH + folderName, MMS);
+                    if (msg.mType == Mms.MESSAGE_BOX_SENT) {
+                        mListener.onSendingSuccess(mMasId, String.valueOf(SMS_OFFSET_START +
+                                msg.mId), PRE_PATH + SENT, MMS);
+                    }
+                }
+            }
+        }
+
+        private void deletedMms() {
+            if (V) Log.v(TAG, "deletedMms() MMS mas Id: " + mMasId);
+            if (mListener != null) {
+                Collection<Message> values = mMmsDeletedList.values();
+                for (Message msg : values) {
+                    String folderName = (msg.mThreadId == -1) ? DELETED : msg.mFolderName;
+                    mListener.onMessageDeleted(mMasId, String.valueOf(MMS_OFFSET_START + msg.mId),
+                            PRE_PATH + folderName, MMS);
+                }
+            }
+        }
+    };
+}
diff --git a/src/org/codeaurora/bluetooth/map/IBluetoothMasApp.java b/src/org/codeaurora/bluetooth/map/IBluetoothMasApp.java
new file mode 100644
index 0000000..c5bf471
--- /dev/null
+++ b/src/org/codeaurora/bluetooth/map/IBluetoothMasApp.java
@@ -0,0 +1,144 @@
+/*
+ * Copyright (c) 2011, The Linux Foundation. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *        * Redistributions of source code must retain the above copyright
+ *          notice, this list of conditions and the following disclaimer.
+ *        * Redistributions in binary form must reproduce the above copyright
+ *          notice, this list of conditions and the following disclaimer in the
+ *          documentation and/or other materials provided with the distribution.
+ *        * Neither the name of The Linux Foundation nor
+ *          the names of its contributors may be used to endorse or promote
+ *          products derived from this software without specific prior written
+ *          permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NON-INFRINGEMENT ARE DISCLAIMED.    IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
+ * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
+ * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
+ * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package org.codeaurora.bluetooth.map;
+
+import android.bluetooth.BluetoothDevice;
+
+import org.codeaurora.bluetooth.map.MapUtils.CommonUtils.BluetoothMasMessageListingRsp;
+import org.codeaurora.bluetooth.map.MapUtils.CommonUtils.BluetoothMasMessageRsp;
+import org.codeaurora.bluetooth.map.MapUtils.CommonUtils.BluetoothMasPushMsgRsp;
+import org.codeaurora.bluetooth.map.MapUtils.MapUtils.BadRequestException;
+
+import java.io.File;
+
+public interface IBluetoothMasApp {
+    public static final int BIT_SUBJECT = 0x1;
+    public static final int BIT_DATETIME = 0x2;
+    public static final int BIT_SENDER_NAME = 0x4;
+    public static final int BIT_SENDER_ADDRESSING = 0x8;
+
+    public static final int BIT_RECIPIENT_NAME = 0x10;
+    public static final int BIT_RECIPIENT_ADDRESSING = 0x20;
+    public static final int BIT_TYPE = 0x40;
+    public static final int BIT_SIZE = 0x80;
+
+    public static final int BIT_RECEPTION_STATUS = 0x100;
+    public static final int BIT_TEXT = 0x200;
+    public static final int BIT_ATTACHMENT_SIZE = 0x400;
+    public static final int BIT_PRIORITY = 0x800;
+
+    public static final int BIT_READ = 0x1000;
+    public static final int BIT_SENT = 0x2000;
+    public static final int BIT_PROTECTED = 0x4000;
+    public static final int BIT_REPLYTO_ADDRESSING = 0x8000;
+
+    /**
+     * Message Access Profile SPEC V10
+     * 3.1.1 Handle
+     * The handle shall be a 64 bit unsigned integer whose value is defined by the MSE.
+     * 7.1 SDP Interoperability Requirements
+     * Up to 12 MAS Instances may be supported by a MSE device.
+     * The value range of the MASInstanceID. shall be 0..255.
+     * Although id range is 0..255, 0~11 are selected for conventional indexing
+     */
+    public static final long HANDLE_OFFSET[] = {
+        0,          // MAS id 0
+        1 << 59,    // MAS id 1
+        2 << 59,    // MAS id 2
+        3 << 59,    // MAS id 3
+        4 << 59,    // MAS id 4
+        5 << 59,    // MAS id 5
+        6 << 59,    // MAS id 6
+        7 << 59,    // MAS id 7
+        8 << 59,    // MAS id 8
+        9 << 59,    // MAS id 9
+        10 << 59,   // MAS id 10
+        11 << 59,   // MAS id 11
+        Long.MAX_VALUE
+    };
+
+    public static final int EMAIL_MAX_PUSHMSG_SIZE = 409600;
+
+    public static final int DELETED_THREAD_ID = -1;
+
+    public static final String TELECOM = "telecom";
+    public static final String MSG = "msg";
+
+    public static final int MESSAGE_TYPE_EMAIL = 1 << 0;
+    public static final int MESSAGE_TYPE_SMS_GSM = 1 << 1;
+    public static final int MESSAGE_TYPE_SMS_CDMA = 1 << 2;
+    public static final int MESSAGE_TYPE_MMS = 1 << 3;
+    public static final int MESSAGE_TYPE_SMS = MESSAGE_TYPE_SMS_GSM | MESSAGE_TYPE_SMS_CDMA;
+    public static final int MESSAGE_TYPE_SMS_MMS = MESSAGE_TYPE_SMS | MESSAGE_TYPE_MMS;
+
+    public boolean setPath(boolean up, String name);
+    public boolean checkPath(boolean up, String name, boolean setPathFlag);
+    public int folderListingSize();
+    public String folderListing(BluetoothMasAppParams appParam);
+    //private String getFullPath(String child);
+    public BluetoothMasMessageListingRsp msgListing(String name,
+        BluetoothMasAppParams appParams);
+    public BluetoothMasMessageRsp msg(String msgHandle,
+        BluetoothMasAppParams bluetoothMasAppParams);
+    public BluetoothMasPushMsgRsp pushMsg(String name, File file,
+        BluetoothMasAppParams bluetoothMasAppParams) throws BadRequestException;
+    public int msgStatus(String msgHandle, BluetoothMasAppParams bluetoothMasAppParams);
+    public int msgUpdate();
+    public void onConnect();
+    public void onDisconnect();
+    public int notification(BluetoothDevice remoteDevice,
+        BluetoothMasAppParams bluetoothMasAppParams);
+    public void startMnsSession(BluetoothDevice remoteDevice);
+    public void stopMnsSession(BluetoothDevice remoteDevice);
+    public int getMasId();
+    public boolean checkPrecondition();
+
+    public interface MessageNotificationListener {
+        public static final String NEW_MESSAGE = "NewMessage";
+        public static final String DELIVERY_SUCCESS = "DeliverySuccess";
+        public static final String SENDING_SUCCESS = "SendingSuccess";
+        public static final String DELIVERY_FAILURE = "DeliveryFailure";
+        public static final String SENDING_FAILURE = "SendingFailure";
+        public static final String MESSAGE_DELETED = "MessageDeleted";
+        public static final String MESSAGE_SHIFT = "MessageShift";
+        public void onNewMessage(int masId, String handle, String folder, String msgType);
+        public void onDeliverySuccess(int masId, String handle, String folder, String msgType);
+        public void onSendingSuccess(int masId, String handle, String folder, String msgType);
+        public void onDeliveryFailure(int masId, String handle, String folder, String msgType);
+        public void onSendingFailure(int masId, String handle, String folder, String msgType);
+        public void onMessageDeleted(int masId, String handle, String folder, String msgType);
+        public void onMessageShift(int masId, String handle, String toFolder, String fromFolder,
+                String msgType);
+    }
+
+    public interface MnsRegister {
+        public void register(MessageNotificationListener listener);
+        public boolean isRegistered();
+    }
+}
diff --git a/src/org/codeaurora/bluetooth/map/MapUtils/BmessageConsts.java b/src/org/codeaurora/bluetooth/map/MapUtils/BmessageConsts.java
new file mode 100644
index 0000000..e424653
--- /dev/null
+++ b/src/org/codeaurora/bluetooth/map/MapUtils/BmessageConsts.java
@@ -0,0 +1,210 @@
+/*
+ * Copyright (c) 2010-2011, The Linux Foundation. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *        * Redistributions of source code must retain the above copyright
+ *          notice, this list of conditions and the following disclaimer.
+ *        * Redistributions in binary form must reproduce the above copyright
+ *          notice, this list of conditions and the following disclaimer in the
+ *          documentation and/or other materials provided with the distribution.
+ *        * Neither the name of The Linux Foundation nor
+ *          the names of its contributors may be used to endorse or promote
+ *          products derived from this software without specific prior written
+ *          permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NON-INFRINGEMENT ARE DISCLAIMED.    IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
+ * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
+ * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
+ * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package org.codeaurora.bluetooth.map.MapUtils;
+
+public class BmessageConsts {
+
+    public String bmsg_version = null;
+    public String status = null;
+    public String type = null;
+    public String folder = null;
+    public String vcard_version = null;
+    public String recipient_vcard_name = null;
+    public String recipient_vcard_phone_number = null;
+    public String recipient_vcard_email = null;
+    public String originator_vcard_name = null;
+    public String originator_vcard_phone_number = null;
+    public String originator_vcard_email = null;
+    public String body_encoding = null;
+    public int body_length = 0;
+    public String body_msg = null;
+    public String body_part_ID = null;
+    public String body_language = null;
+    public String body_charset = null;
+    public String subject = null;
+
+    // Setters and Getters
+
+    public String getBody_language() {
+        return body_language;
+    }
+
+    public void setBody_language(String body_language) {
+        this.body_language = body_language;
+    }
+
+    public String getBody_part_ID() {
+        return body_part_ID;
+    }
+
+    public void setBody_part_ID(String body_part_ID) {
+        this.body_part_ID = body_part_ID;
+    }
+
+    public String getBmsg_version() {
+        return bmsg_version;
+    }
+
+    public void setBmsg_version(String bmsg_version) {
+        this.bmsg_version = bmsg_version;
+    }
+
+    public String getStatus() {
+        return status;
+    }
+
+    public void setStatus(String status) {
+        this.status = status;
+    }
+
+    public String getType() {
+        return type;
+    }
+
+    public void setType(String type) {
+        this.type = type;
+    }
+
+    public String getFolder() {
+        return folder;
+    }
+
+    public void setFolder(String folder) {
+        this.folder = folder;
+    }
+
+    public String getVcard_version() {
+        return vcard_version;
+    }
+
+    public void setVcard_version(String vcard_version) {
+        this.vcard_version = vcard_version;
+    }
+
+    // Name
+    public String getRecipientVcard_name() {
+        return recipient_vcard_name;
+    }
+
+    public String getOriginatorVcard_name() {
+        return originator_vcard_name;
+    }
+
+    // recipient_vcard_name
+
+    public void setRecipientVcard_name(String vcard_name) {
+        this.recipient_vcard_name = vcard_name;
+    }
+
+    public void setOriginatorVcard_name(String vcard_name) {
+        this.originator_vcard_name = vcard_name;
+    }
+
+    // Name
+
+    // Phone
+    public String getRecipientVcard_email() {
+        return recipient_vcard_email;
+    }
+
+    public String getOriginatorVcard_email() {
+        return originator_vcard_email;
+    }
+
+    public void setRecipientVcard_email(String email) {
+        this.recipient_vcard_email = email;
+    }
+
+    public void setOriginatorVcard_email(String email) {
+        this.originator_vcard_email = email;
+    }
+
+    // Phone
+    //Email
+    public String getRecipientVcard_phone_number() {
+        return recipient_vcard_phone_number;
+    }
+
+    public String getOriginatorVcard_phone_number() {
+        return originator_vcard_phone_number;
+    }
+
+    public void setRecipientVcard_phone_number(String vcard_phone_number) {
+        this.recipient_vcard_phone_number = vcard_phone_number;
+    }
+
+    public void setOriginatorVcard_phone_number(String vcard_phone_number) {
+        this.originator_vcard_phone_number = vcard_phone_number;
+    }
+
+
+
+
+    //end Email
+
+    public String getBody_charset() {
+        return body_charset;
+    }
+
+    public void setBody_charset(String body_charset) {
+        this.body_charset = body_charset;
+    }
+
+    public String getBody_encoding() {
+        return body_encoding;
+    }
+
+    public void setBody_encoding(String body_encoding) {
+        this.body_encoding = body_encoding;
+    }
+
+    public int getBody_length() {
+        return body_length;
+    }
+
+    public void setBody_length(int body_length) {
+        this.body_length = body_length;
+    }
+
+    public String getBody_msg() {
+        return body_msg;
+    }
+
+    public void setBody_msg(String body_msg) {
+        this.body_msg = body_msg;
+    }
+    public String getSubject() {
+        return subject;
+    }
+
+    public void setSubject(String subject) {
+        this.subject = subject;
+    }
+
+}
diff --git a/src/org/codeaurora/bluetooth/map/MapUtils/CommonUtils.java b/src/org/codeaurora/bluetooth/map/MapUtils/CommonUtils.java
new file mode 100644
index 0000000..73546d0
--- /dev/null
+++ b/src/org/codeaurora/bluetooth/map/MapUtils/CommonUtils.java
@@ -0,0 +1,156 @@
+/*
+ * Copyright (c) 2010-2013, The Linux Foundation. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *        * Redistributions of source code must retain the above copyright
+ *          notice, this list of conditions and the following disclaimer.
+ *        * Redistributions in binary form must reproduce the above copyright
+ *          notice, this list of conditions and the following disclaimer in the
+ *          documentation and/or other materials provided with the distribution.
+ *        * Neither the name of The Linux Foundation nor
+ *          the names of its contributors may be used to endorse or promote
+ *          products derived from this software without specific prior written
+ *          permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NON-INFRINGEMENT ARE DISCLAIMED.    IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
+ * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
+ * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
+ * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package org.codeaurora.bluetooth.map.MapUtils;
+
+import android.content.Context;
+import android.text.format.Time;
+import android.util.Log;
+import android.util.TimeFormatException;
+
+import org.codeaurora.bluetooth.map.BluetoothMasAppParams;
+
+import java.io.File;
+import java.util.ArrayList;
+import java.util.List;
+
+import javax.obex.ResponseCodes;
+
+public class CommonUtils {
+    public static final String TAG = "CommonUtils";
+
+    public static final ArrayList<String> FIXED_FOLDERS;
+
+    static {
+        FIXED_FOLDERS = new ArrayList<String>();
+        FIXED_FOLDERS.add("inbox");
+        FIXED_FOLDERS.add("sent");
+        FIXED_FOLDERS.add("deleted");
+        FIXED_FOLDERS.add("outbox");
+        FIXED_FOLDERS.add("draft");
+    }
+
+    public static class BluetoothMasPushMsgRsp {
+        public int response;
+        public String msgHandle;
+    }
+
+    public static class BluetoothMasMessageListingRsp {
+        public File file = null;
+        public int msgListingSize = 0;
+        public byte newMessage = 0;
+        public int rsp = ResponseCodes.OBEX_HTTP_OK;
+    }
+
+    public static class BluetoothMasMessageRsp {
+        public byte fractionDeliver = 0;
+        public File file = null;
+        public int rsp = ResponseCodes.OBEX_HTTP_OK;
+    }
+
+    public static class BluetoothMsgListRsp {
+        public int messageListingSize = 0;
+        public BluetoothMasMessageListingRsp rsp;
+        public List<MsgListingConsts> msgList = new ArrayList<MsgListingConsts>();
+    }
+
+    public static String getFullPath(String child, Context context, List<String> folderList, String CurrentPath) {
+        String tempPath = null;
+        if (child != null) {
+            if (CurrentPath == null) {
+                if (child.equals("telecom")) {
+                    // Telecom is fine
+                    tempPath = "telecom";
+                }
+            }
+            else if (CurrentPath.equals("telecom")) {
+                if (child.equals("msg")) {
+                    tempPath = CurrentPath + "/" + child;
+                }
+            }
+            else { //Get Path for any folder or subfolder
+                for (String folder : folderList) { //TODO NEED TO LOOK INTO THIS
+                    if(child.toUpperCase().contains("GMAIL")){
+                        if (folder.equalsIgnoreCase(child)
+                                || folder.toUpperCase().contains(child.toUpperCase())) {
+                            //added second condition above for gmail sent folder
+                            tempPath = CurrentPath + "/" + folder;
+                            break;
+                        }
+                    }
+                    else{
+                        if (folder.equalsIgnoreCase(child)) {
+                            tempPath = CurrentPath + "/" + folder;
+                            break;
+                        }
+                    }
+                }
+            }
+        }
+        return tempPath;
+    }
+
+    public static int validateFilterPeriods(BluetoothMasAppParams appParams) {
+        int filterPeriodValid = -1;
+        String periodStr = "";
+        /* Filter Period Begin */
+        if ((appParams.FilterPeriodBegin != null)
+                && (appParams.FilterPeriodBegin.length() > 0)) {
+            Time time = new Time();
+            try {
+                time.parse(appParams.FilterPeriodBegin.trim());
+                if (periodStr.length() != 0) {
+                        periodStr += " AND ";
+                }
+                periodStr += "date >= " + time.toMillis(false);
+            } catch (TimeFormatException e) {
+                Log.d(TAG, "Bad formatted FilterPeriodBegin "
+                        + appParams.FilterPeriodBegin);
+                filterPeriodValid = 0;
+            }
+        }
+
+        /* Filter Period End */
+        if ((appParams.FilterPeriodEnd != null)
+                && (appParams.FilterPeriodEnd.length() > 0 )) {
+            Time time = new Time();
+            try {
+                time.parse(appParams.FilterPeriodEnd.trim());
+                if (periodStr.length() != 0) {
+                        periodStr += " AND ";
+                }
+                periodStr += "date < " + time.toMillis(false);
+            } catch (TimeFormatException e) {
+                Log.d(TAG, "Bad formatted FilterPeriodEnd "
+                        + appParams.FilterPeriodEnd);
+                filterPeriodValid = 0;
+            }
+        }
+        return filterPeriodValid;
+    }
+}
diff --git a/src/org/codeaurora/bluetooth/map/MapUtils/EmailUtils.java b/src/org/codeaurora/bluetooth/map/MapUtils/EmailUtils.java
new file mode 100644
index 0000000..5f92b3c
--- /dev/null
+++ b/src/org/codeaurora/bluetooth/map/MapUtils/EmailUtils.java
@@ -0,0 +1,1054 @@
+/*
+ * Copyright (c) 2010-2013, The Linux Foundation. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *        * Redistributions of source code must retain the above copyright
+ *          notice, this list of conditions and the following disclaimer.
+ *        * Redistributions in binary form must reproduce the above copyright
+ *          notice, this list of conditions and the following disclaimer in the
+ *          documentation and/or other materials provided with the distribution.
+ *        * Neither the name of The Linux Foundation nor
+ *          the names of its contributors may be used to endorse or promote
+ *          products derived from this software without specific prior written
+ *          permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NON-INFRINGEMENT ARE DISCLAIMED.    IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
+ * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
+ * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
+ * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package org.codeaurora.bluetooth.map.MapUtils;
+
+import android.content.Context;
+import android.database.Cursor;
+import android.net.Uri;
+import android.text.Html;
+import android.text.format.Time;
+import android.util.Log;
+import android.util.TimeFormatException;
+
+import org.codeaurora.bluetooth.map.BluetoothMasAppParams;
+import org.codeaurora.bluetooth.map.BluetoothMasService;
+import org.codeaurora.bluetooth.map.MapUtils.CommonUtils.BluetoothMasMessageRsp;
+
+import java.text.SimpleDateFormat;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.Date;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Random;
+import java.util.Locale;
+
+public class EmailUtils {
+    public static final String TAG = "EmailUtils";
+    public static final boolean V = BluetoothMasService.VERBOSE;
+
+    public static final int BIT_SUBJECT = 0x1;
+    public static final int BIT_DATETIME = 0x2;
+    public static final int BIT_SENDER_NAME = 0x4;
+    public static final int BIT_SENDER_ADDRESSING = 0x8;
+
+    public static final int BIT_RECIPIENT_NAME = 0x10;
+    public static final int BIT_RECIPIENT_ADDRESSING = 0x20;
+    public static final int BIT_TYPE = 0x40;
+    public static final int BIT_SIZE = 0x80;
+
+    public static final int BIT_RECEPTION_STATUS = 0x100;
+    public static final int BIT_TEXT = 0x200;
+    public static final int BIT_ATTACHMENT_SIZE = 0x400;
+    public static final int BIT_PRIORITY = 0x800;
+
+    public static final int BIT_READ = 0x1000;
+    public static final int BIT_SENT = 0x2000;
+    public static final int BIT_PROTECTED = 0x4000;
+    public static final int BIT_REPLYTO_ADDRESSING = 0x8000;
+
+    public static final String BMW = "BMW";
+
+    public static List<String> folderListEmail(List<String> folderList, Context context) {
+        String[] projection = new String[] {"displayName"};
+        Uri uri = Uri.parse("content://com.android.email.provider/mailbox");
+        Cursor cr = context.getContentResolver().query(uri, projection, null, null, null);
+
+        if (cr != null && cr.moveToFirst()) {
+            do {
+                if (V){
+                    Log.v(TAG, " Column Name: "+ cr.getColumnName(0) + " Value: " + cr.getString(0));
+                }
+                int folderFlag = 0;
+                for(int i=0; i< folderList.size(); i++){
+                    if(folderList.get(i).equalsIgnoreCase(cr.getString(0))){
+                        folderFlag = 1;
+                        break;
+                    }
+                }
+                if(cr.getString(0).equalsIgnoreCase("Drafts")){ //TODO need to remove this hardcoded value
+                    folderFlag = 1;
+                }
+                if(folderFlag == 0){
+                    folderList.add(cr.getString(0));
+                }
+
+            } while ( cr.moveToNext());
+        }
+        if (V){
+            Log.v(TAG, " Folder Listing of SMS,MMS and EMAIL: "+folderList);
+        }
+        if (cr != null) {
+            cr.close();
+        }
+        return folderList;
+    }
+
+    public static String getWhereIsQueryForTypeEmail(String folder, Context context, int masId) {
+        String query = "mailboxKey = -1";
+        String folderId;
+        Uri uri = Uri.parse("content://com.android.email.provider/mailbox");
+        if (folder == null) {
+            return query;
+        }
+        if (folder.contains("'")){
+            folder = folder.replace("'", "''");
+        }
+        Cursor cr = context.getContentResolver().query(
+                uri, null, "(" + ACCOUNT_KEY + "=" + getAccountId(masId) +
+                ") AND (UPPER(displayName) = '"+ folder.toUpperCase()+"')" , null, null);
+        if (cr != null) {
+            if ( cr.moveToFirst()) {
+                do {
+                    folderId = cr.getString(cr.getColumnIndex("_id"));
+                    query = "mailboxKey = "+ folderId;
+                    break;
+                } while ( cr.moveToNext());
+            }
+            cr.close();
+        }
+        return query;
+    }
+
+    public static int getMessageSizeEmail(long messageId, Context context) {
+        if (V){
+            Log.v(TAG, ":: Message Id in getMessageSizeEmail ::"+ messageId);
+        }
+        int msgSize = 0;
+        String textContent, htmlContent;
+        Uri uri = Uri.parse("content://com.android.email.provider/body");
+
+        Cursor cr = context.getContentResolver().query(
+                uri, null, "messageKey = "+ messageId , null, null);
+
+        if (cr != null && cr.moveToFirst()) {
+            do {
+                textContent = cr.getString(cr.getColumnIndex("textContent"));
+                htmlContent = cr.getString(cr.getColumnIndex("htmlContent"));
+                if(textContent != null && textContent.length() != 0){
+                    msgSize = textContent.length();
+                }
+                else if(textContent == null){
+                    if(htmlContent != null && htmlContent.length() != 0){
+                        msgSize = htmlContent.length();
+                    }
+                }
+                break;
+            } while (cr.moveToNext());
+        }
+        if (cr != null) {
+            cr.close();
+        }
+        return msgSize;
+    }
+
+    public static int getAttachmentSizeEmail(long messageId, Context context) {
+        if (V){
+            Log.v(TAG, ":: Message Id in getAttachmentSizeEmail ::"+ messageId);
+        }
+        int attchSize = 0;
+        Uri uri = Uri.parse("content://com.android.email.provider/attachment");
+
+        Cursor cr = context.getContentResolver().query(
+                uri, new String[]{"size"}, "messageKey = "+ messageId , null, null);
+        if (cr != null && cr.moveToFirst()) {
+            do {
+                attchSize += cr.getInt(0);
+            } while (cr.moveToNext());
+        }
+        if (cr != null) {
+            cr.close();
+        }
+        return attchSize;
+    }
+
+    public static String getFolderName(String[] splitStringsEmail) {
+        String folderName=" ";
+        int len = splitStringsEmail.length;
+        if (V){
+            Log.v(TAG, ":: Split Strings Array in getFolderName ::"+ splitStringsEmail);
+        }
+        folderName = splitStringsEmail[len -1];
+        if (V){
+            Log.v(TAG, "folderName :: " + folderName);
+        }
+        return folderName;
+    }
+
+    public static String getConditionString(String folderName, Context context,
+            BluetoothMasAppParams appParams, int masId) {
+        String whereClauseEmail = getWhereIsQueryForTypeEmail(folderName, context, masId);
+
+        /* Filter readstatus: 0 no filtering, 0x01 get unread, 0x10 get read */
+        if (appParams.FilterReadStatus != 0) {
+            if ((appParams.FilterReadStatus & 0x1) != 0) {
+                if (whereClauseEmail.length() != 0) {
+                    whereClauseEmail += " AND ";
+                }
+                whereClauseEmail += " flagRead = 0 ";
+            }
+            if (V){
+                Log.v(TAG, "Filter Read Status Value:"+appParams.FilterReadStatus);
+                Log.v(TAG, "Filter Read Status Condition Value:"+(appParams.FilterReadStatus & 0x02));
+            }
+            if ((appParams.FilterReadStatus & 0x02) != 0) {
+                if (whereClauseEmail.length() != 0) {
+                    whereClauseEmail += " AND ";
+                }
+                whereClauseEmail += " flagRead = 1 ";
+            }
+        }
+        if (V){
+            Log.v(TAG, "Filter Recipient Value:"+appParams.FilterRecipient);
+            Log.v(TAG, "Filter Recipient Condition 1 :"+(appParams.FilterRecipient != null));
+            if (appParams.FilterRecipient != null) {
+                Log.v(TAG, "Filter Recipient Condition 2 :"+(appParams.FilterRecipient.length() != 0));
+            }
+        }
+        //Filter Recipient
+        if ((appParams.FilterRecipient != null) && (appParams.FilterRecipient.length() > 0)
+                && !(appParams.FilterRecipient.equalsIgnoreCase("*"))) {
+                if(appParams.FilterRecipient.contains("*")){
+                    appParams.FilterRecipient = appParams.FilterRecipient.replace('*', '%');
+                }
+                if (whereClauseEmail.length() != 0) {
+                    whereClauseEmail += " AND ";
+                }
+            whereClauseEmail += " toList LIKE '%"+appParams.FilterRecipient.trim()+"%'";
+        }
+
+        // TODO Filter Originator
+
+        if ((appParams.FilterOriginator != null)
+                && (appParams.FilterOriginator.length() > 0)
+                && !(appParams.FilterOriginator.equalsIgnoreCase("*"))) {
+                if(appParams.FilterOriginator.contains("*")){
+                    appParams.FilterOriginator = appParams.FilterOriginator.replace('*', '%');
+                }
+            // For incoming message
+            if (whereClauseEmail.length() != 0) {
+                whereClauseEmail += " AND ";
+            }
+            String str1 = appParams.FilterOriginator;
+            whereClauseEmail += "fromList LIKE '%"+appParams.FilterOriginator.trim()+"%'";
+        }
+        if (V){
+            Log.v(TAG, "whereClauseEmail :" + whereClauseEmail);
+        }// TODO Filter priority?
+
+        /* Filter Period Begin */
+        if ((appParams.FilterPeriodBegin != null)
+                && (appParams.FilterPeriodBegin.length() != 0)) {
+            Time time = new Time();
+
+            try {
+                time.parse(appParams.FilterPeriodBegin.trim());
+                if (whereClauseEmail.length() != 0) {
+                    whereClauseEmail += " AND ";
+                }
+                whereClauseEmail += "timeStamp >= " + time.toMillis(false);
+
+            } catch (TimeFormatException e) {
+                Log.d(TAG, "Bad formatted FilterPeriodBegin, Ignore"
+                        + appParams.FilterPeriodBegin);
+            }
+        }
+
+        /* Filter Period End */
+        if ((appParams.FilterPeriodEnd != null)
+                && (appParams.FilterPeriodEnd.length() != 0)) {
+            Time time = new Time();
+            try {
+                time.parse(appParams.FilterPeriodEnd.trim());
+                if (whereClauseEmail.length() != 0) {
+                    whereClauseEmail += " AND ";
+                }
+                whereClauseEmail += "timeStamp < " + time.toMillis(false);
+            } catch (TimeFormatException e) {
+                Log.d(TAG, "Bad formatted FilterPeriodEnd, Ignore"
+                        + appParams.FilterPeriodEnd);
+            }
+        }
+
+        return whereClauseEmail;
+    }
+
+    public static MsgListingConsts bldEmailMsgLstItem(Context context, String folderName,
+                BluetoothMasAppParams appParams, String subject, String timestamp,
+                String senderName, String senderAddressing, String recipientName,
+                String recipientAddressing, String msgId, String readStatus, String replyToStr,
+                long offset) {
+
+        MsgListingConsts emailMsg = new MsgListingConsts();
+        emailMsg.setMsg_handle(Long.valueOf(msgId)+ offset);
+
+        Time time = new Time();
+        time.set(Long.valueOf(timestamp));
+
+        String datetimeStr = time.toString().substring(0, 15);
+
+        emailMsg.msgInfo.setDateTime(datetimeStr);
+
+        if (V){
+            Log.v(TAG, "bldEmailMsgLstItem");
+            Log.v(TAG, "Subject: " + subject);
+            Log.v(TAG, "timestamp: " + timestamp);
+            Log.v(TAG, "senderName: " + senderName);
+            Log.v(TAG, "senderAddressing: " + senderAddressing);
+            Log.v(TAG, "recipientName: " + recipientName);
+            Log.v(TAG, "recipientAddressing: " + recipientAddressing);
+            Log.v(TAG, "msgId: " + msgId);
+            Log.v(TAG, "readStatus: " + readStatus);
+            Log.v(TAG, "replyToStr: " + replyToStr);
+            Log.v(TAG, "offset: " + offset);
+        }
+
+        if ((appParams.ParameterMask & BIT_SUBJECT) != 0) {
+
+            if (V){
+                Log.v(TAG, "bldEmailMsgLstItem :: Subject " + subject);
+            }
+            if ((subject != null && appParams.SubjectLength > 0)
+                    && (subject.length() > appParams.SubjectLength)) {
+                subject = subject.substring(0,
+                        appParams.SubjectLength);
+            }
+            if(subject != null){
+                emailMsg.setSubject(subject.trim());
+            }
+            emailMsg.sendSubject = true;
+       }
+
+        if ((appParams.ParameterMask & BIT_DATETIME) != 0) {
+            /*emailMsg.setDatetime(time.toString().substring(0, 15)
+                    + "-0700");*/
+            emailMsg.setDatetime(datetimeStr);
+        }
+
+        if ((appParams.ParameterMask & BIT_SENDER_NAME) != 0) {
+            if(senderName != null) {
+                if(senderName.contains("")){
+                    String[] senderStr = senderName.split("");
+                    if(senderStr !=null && senderStr.length > 0){
+                        if (V){
+                            Log.v(TAG, " ::Sender name split String 0:: " + senderStr[0]
+                                    + "::Sender name split String 1:: " + senderStr[1]);
+                        }
+                        emailMsg.setSender_name(senderStr[1].trim());
+                    }
+                }
+                else{
+                    emailMsg.setSender_name(senderName.trim());
+                }
+            }
+       }
+
+        if ((appParams.ParameterMask & BIT_SENDER_ADDRESSING) != 0) {
+            if(senderAddressing != null) {
+                if(senderAddressing.contains("")){
+                    String[] senderAddrStr = senderAddressing.split("");
+                    if(senderAddrStr !=null && senderAddrStr.length > 0){
+                        if (V){
+                            Log.v(TAG, " ::Sender Addressing split String 0:: " + senderAddrStr[0]
+                                    + "::Sender Addressing split String 1:: " + senderAddrStr[1]);
+                        }
+                        emailMsg.setSender_addressing(senderAddrStr[0].trim());
+                    }
+                }
+                else{
+                    emailMsg.setSender_addressing(senderAddressing.trim());
+                }
+            }
+        }
+
+        if ((appParams.ParameterMask & BIT_RECIPIENT_NAME) != 0) {
+            String multiRecepients = "";
+            if(recipientName != null){
+                if(recipientName.contains("")){
+                    List<String> recipientNameArr = new ArrayList<String>();
+                    List<String> recipientEmailArr = new ArrayList<String>();
+                    String[] multiRecipientStr = recipientName.split("");
+                    for(int i=0; i < multiRecipientStr.length ; i++){
+                        if(multiRecipientStr[i].contains("")){
+                            String[] recepientStr = multiRecipientStr[i].split("");
+                            recipientNameArr.add(recepientStr[1]);
+                            recipientEmailArr.add(recepientStr[0]);
+                        }
+                    }
+                    if(recipientNameArr != null && recipientNameArr.size() > 0){
+                        for(int i=0; i < recipientNameArr.size() ; i++){
+                            if(i < (recipientNameArr.size()-1)){
+                                multiRecepients += recipientNameArr.get(i)+";";
+                            }
+                            else{
+                                multiRecepients += recipientNameArr.get(i);
+                            }
+                        }
+                    }
+                    emailMsg.setRecepient_name(multiRecepients.trim());
+                }
+                else if(recipientName.contains("")){
+                    String[] recepientStr = recipientName.split("");
+                    if(recepientStr !=null && recepientStr.length > 0){
+                        if (V){
+                            Log.v(TAG, " ::Recepient name split String 0:: " + recepientStr[0]
+                                    + "::Recepient name split String 1:: " + recepientStr[1]);
+                        }
+                        emailMsg.setRecepient_name(recepientStr[1].trim());
+                    }
+                }
+                else{
+                    emailMsg.setRecepient_name(recipientName.trim());
+                }
+            }
+        }
+
+        if ((appParams.ParameterMask & BIT_RECIPIENT_ADDRESSING) != 0) {
+            String multiRecepientAddrs = "";
+
+            if (recipientAddressing != null) {
+                if (recipientAddressing.contains("")) {
+                    List<String> recipientNameArr = new ArrayList<String>();
+                    List<String> recipientEmailArr = new ArrayList<String>();
+                    if  (recipientName != null) {
+                        String[] multiRecipientStr = recipientName.split("");
+                        for (int i=0; i < multiRecipientStr.length ; i++) {
+                            if (multiRecipientStr[i].contains("")) {
+                                String[] recepientStr = multiRecipientStr[i].split("");
+                                recipientNameArr.add(recepientStr[1]);
+                                recipientEmailArr.add(recepientStr[0]);
+                            }
+                        }
+                    }
+                    final int recipientEmailArrSize = recipientEmailArr.size();
+                    if (recipientEmailArrSize > 0) {
+                        for (int i=0; i < recipientEmailArrSize ; i++) {
+                            if (i < (recipientEmailArrSize-1)) {
+                                multiRecepientAddrs += recipientEmailArr.get(i)+";";
+                            } else {
+                                multiRecepientAddrs += recipientEmailArr.get(i);
+                            }
+                        }
+                    }
+                    emailMsg.setRecepient_addressing(multiRecepientAddrs.trim());
+                    emailMsg.setSendRecipient_addressing(true);
+                } else if (recipientAddressing.contains("")) {
+                    String[] recepientAddrStr = recipientAddressing.split("");
+                    if (recepientAddrStr !=null && recepientAddrStr.length > 0) {
+                        if (V){
+                            Log.v(TAG, " ::Recepient addressing split String 0:: " + recepientAddrStr[0]
+                                    + "::Recepient addressing split String 1:: " + recepientAddrStr[1]);
+                        }
+                        emailMsg.setRecepient_addressing(recepientAddrStr[0].trim());
+                        emailMsg.setSendRecipient_addressing(true);
+                    }
+                } else {
+                    emailMsg.setRecepient_addressing(recipientAddressing.trim());
+                    emailMsg.setSendRecipient_addressing(true);
+                }
+            }
+        }
+
+        if ((appParams.ParameterMask & BIT_TYPE) != 0) {
+            emailMsg.setType("EMAIL");
+        }
+
+        if ((appParams.ParameterMask & BIT_SIZE) != 0) {
+            int  msgSize = 0;
+            msgSize = getMessageSizeEmail(Long.valueOf(msgId), context);
+            emailMsg.setSize(msgSize);
+        }
+
+        if ((appParams.ParameterMask & BIT_RECEPTION_STATUS) != 0) {
+            emailMsg.setReception_status("complete");
+        }
+
+        if ((appParams.ParameterMask & BIT_TEXT) != 0) {
+            // TODO Set text to "yes"
+            emailMsg.setContains_text("yes");
+        }
+
+        if ((appParams.ParameterMask & BIT_ATTACHMENT_SIZE) != 0) {
+            emailMsg.setAttachment_size(getAttachmentSizeEmail(Long.valueOf(msgId), context));
+        }
+
+        if ((appParams.ParameterMask & BIT_PRIORITY) != 0) {
+            // TODO Get correct priority
+            emailMsg.setPriority("no");
+        }
+
+        if ((appParams.ParameterMask & BIT_READ) != 0) {
+            if (V){
+                Log.v(TAG, " ::Read Status:: " + readStatus);
+            }
+            if (readStatus.equalsIgnoreCase("1")) {
+                emailMsg.setRead("yes");
+            } else {
+                emailMsg.setRead("no");
+            }
+        }
+
+        if ((appParams.ParameterMask & BIT_SENT) != 0) {
+            // TODO Get sent status?
+            if (folderName.equalsIgnoreCase("sent") || folderName.toUpperCase().contains("SENT")) {
+                emailMsg.setSent("yes");
+            } else {
+                emailMsg.setSent("no");
+            }
+        }
+
+        if ((appParams.ParameterMask & BIT_PROTECTED) != 0) {
+            emailMsg.setMsg_protected("no");
+        }
+
+        if ((appParams.ParameterMask & BIT_REPLYTO_ADDRESSING) != 0) {
+            //TODO need to test
+            if (V){
+                Log.v(TAG, " ::Reply To addressing:: " + replyToStr);
+            }
+            if (replyToStr !=null && replyToStr.length() != 0){
+                if (replyToStr.contains("")){
+                    String replyToStrArr[] = replyToStr.split("");
+                    replyToStr = replyToStrArr[0];
+                }
+                emailMsg.setReplyTo_addressing(replyToStr);
+            } else{
+                emailMsg.setReplyTo_addressing(emailMsg.getSender_addressing());
+            }
+        }
+        return emailMsg;
+    }
+
+    public static String bldEmailBmsg(long msgHandle, BluetoothMasMessageRsp rsp, Context context,
+            String remoteDeviceName) {
+        String str = null;
+        //Query the message table for obtaining the message related details
+        Cursor cr1 = null;
+        int folderId;
+        String timeStamp = null;
+        String subjectText = null;
+        Uri uri1 = Uri.parse("content://com.android.email.provider/message");
+        String whereClause = " _id = " + msgHandle;
+
+        // Create a bMessage
+        BmessageConsts bmsg = new BmessageConsts();
+
+        cr1 = context.getContentResolver().query(uri1, null, whereClause, null,
+                null);
+        if (cr1 != null && cr1.getCount() > 0) {
+            cr1.moveToFirst();
+            folderId = cr1.getInt(cr1.getColumnIndex("mailboxKey"));
+            String containingFolder = getContainingFolderEmail(folderId, context);
+            timeStamp = cr1.getString(cr1.getColumnIndex("timeStamp"));
+            subjectText = cr1.getString(cr1.getColumnIndex("subject"));
+
+            // TODO Get Current type
+            bmsg.setType("EMAIL");
+
+            bmsg.setBmsg_version("1.0");
+            if (cr1.getString(cr1.getColumnIndex("flagRead")).equalsIgnoreCase("1")) {
+                bmsg.setStatus("READ");
+            } else {
+                bmsg.setStatus("UNREAD");
+            }
+
+            bmsg.setFolder(MapUtilsConsts.Telecom + "/" + MapUtilsConsts.Msg +
+                        "/" + containingFolder);
+
+            bmsg.setVcard_version("2.1");
+
+            String senderName = null;
+            if((senderName = cr1.getString(cr1.getColumnIndex("fromList"))) != null ){
+                if(senderName.contains("")){
+                    String[] senderStr = senderName.split("");
+                    if(senderStr !=null && senderStr.length > 0){
+                        bmsg.setOriginatorVcard_name(senderStr[1].trim());
+                        bmsg.setOriginatorVcard_email(senderStr[0].trim());
+                    }
+                }
+                else{
+                    bmsg.setOriginatorVcard_name(senderName.trim());
+                    bmsg.setOriginatorVcard_email(senderName.trim());
+                }
+            }
+            String recipientName = null;
+            String multiRecepients = null;
+            if((recipientName = cr1.getString(cr1.getColumnIndex("toList"))) != null){
+                if(recipientName.contains("")){
+                    String[] recepientStr = recipientName.split("");
+                    if(recepientStr !=null && recepientStr.length > 0){
+                        bmsg.setRecipientVcard_name(recepientStr[1].trim());
+                        bmsg.setRecipientVcard_email(recepientStr[0].trim());
+                    }
+                }
+                else if(recipientName.contains("")){
+                    multiRecepients = recipientName.replace('', ';');
+                    if(multiRecepients != null){
+                        if (V){
+                            Log.v(TAG, " ::Recepient name :: " + multiRecepients);
+                        }
+                        bmsg.setRecipientVcard_name(multiRecepients.trim());
+                        bmsg.setRecipientVcard_email(multiRecepients.trim());
+                    }
+                }
+                else{
+                    bmsg.setRecipientVcard_name(recipientName.trim());
+                    bmsg.setRecipientVcard_email(recipientName.trim());
+                }
+            }
+        }
+        if (cr1 != null) {
+            cr1.close();
+        }
+
+        //Query the body table for obtaining the message body
+        Cursor cr2 = null;
+        Uri uri2 = Uri.parse("content://com.android.email.provider/body");
+        String whereStr = " messageKey = " + msgHandle;
+        cr2 = context.getContentResolver().query(uri2, null, whereStr, null,
+                null);
+        if (cr2 != null) {
+            StringBuilder sb = new StringBuilder();
+            String emailBody = null;
+
+            //Set content type according to content from body table
+            String contenType = "text/plain";
+            if (cr2.getCount() > 0) {
+                cr2.moveToFirst();
+                emailBody = cr2.getString(cr2.getColumnIndex("textContent"));
+                if (emailBody == null || emailBody.length() == 0){
+                    String msgBody = cr2.getString(cr2.getColumnIndex("htmlContent"));
+                    if (msgBody != null){
+                        CharSequence msgText = Html.fromHtml(msgBody);
+                        emailBody = msgText.toString();
+                    }
+                }
+            }
+
+            Date date = new Date(Long.parseLong(timeStamp));
+            sb.append("From:").append(bmsg.getOriginatorVcard_email()).append("\r\n");
+            sb.append("To:").append(bmsg.getRecipientVcard_email()).append("\r\n");
+            if (remoteDeviceName != null && remoteDeviceName.startsWith(BMW)) {
+                sb.append("Mime-Version: 1.0").append("\r\n");
+                sb.append("Content-Type: text/plain; charset=\"UTF-8\"").append("\r\n");
+                sb.append("Content-Transfer-Encoding: 8bit").append("\r\n");
+                // BMW 14692 carkit accepts Date format in "EEE, dd MMM yyyy HH:mm:ss Z"
+                sb.append("Date:");
+                sb.append(new SimpleDateFormat("EEE, dd MMM yyyy HH:mm:ss Z", Locale.US).format(date));
+                sb.append("\r\n");
+                sb.append("Subject:").append(subjectText).append("\r\n").append("\r\n");
+                sb.append(emailBody).append("\r\n");
+            } else {
+                Random randomGenerator = new Random();
+                int randomInt = randomGenerator.nextInt(1000);
+                String boundary = "MessageBoundary."+randomInt;
+                if (emailBody != null){
+                    while (emailBody.contains(boundary)){
+                        randomInt = randomGenerator.nextInt(1000);
+                        boundary = "MessageBoundary."+randomInt;
+                    }
+                }
+                sb.append("Date: ").append(date.toString()).append("\r\n");
+                sb.append("Subject:").append(subjectText).append("\r\n");
+                sb.append("Mime-Version: 1.0").append("\r\n");
+                sb.append(
+                        "Content-Type: multipart/mixed; boundary=\""+boundary+"\"")
+                        .append("\r\n");
+                sb.append("Content-Transfer-Encoding: 8bit").append("\r\n")
+                        .append("\r\n");
+                sb.append("MIME Message").append("\r\n");
+                sb.append("--"+boundary).append("\r\n");
+                sb.append("Content-Type: text/plain; charset=\"UTF-8\"").append("\r\n");
+                sb.append("Content-Transfer-Encoding: 8bit").append("\r\n");
+                sb.append("Content-Disposition:inline").append("\r\n")
+                        .append("\r\n");
+                sb.append(emailBody).append("\r\n");
+                sb.append("--"+boundary+"--").append("\r\n");
+            }
+
+            bmsg.setBody_msg(sb.toString());
+            bmsg.setBody_length(sb.length() + 22);
+            // Send a bMessage
+            if (V){
+                Log.v(TAG, "bMessageEmail test\n");
+                Log.v(TAG, "=======================\n\n");
+            }
+            str = MapUtils.toBmessageEmail(bmsg);
+            cr2.close();
+        }
+        if (V){
+            Log.v(TAG, "======FINAL BMSG=================\n\n");
+            Log.v(TAG, str);
+            Log.v(TAG, "\n\n");
+        }
+        return str;
+    }
+
+    /**
+     * Get the folder name (MAP representation) for Email based on the
+     * mailboxKey value in message table
+     */
+    public static String getContainingFolderEmail(int folderId, Context context) {
+        Cursor cr;
+        String folderName = null;
+        String whereClause = "_id = " + folderId;
+        cr = context.getContentResolver().query(
+                Uri.parse("content://com.android.email.provider/mailbox"),
+                null, whereClause, null, null);
+        if (cr != null) {
+            if (cr.getCount() > 0) {
+                cr.moveToFirst();
+                folderName = cr.getString(cr.getColumnIndex("displayName"));
+            }
+            cr.close();
+        }
+        return folderName;
+    }
+
+    public static final String AUTHORITY = "com.android.email.provider";
+    public static final Uri EMAIL_URI = Uri.parse("content://" + AUTHORITY);
+    public static final Uri EMAIL_ACCOUNT_URI = Uri.withAppendedPath(EMAIL_URI, "account");
+    public static final Uri EMAIL_BOX_URI = Uri.withAppendedPath(EMAIL_URI, "mailbox");
+    public static final Uri EMAIL_MESSAGE_URI = Uri.withAppendedPath(EMAIL_URI, "message");
+    public static final String RECORD_ID = "_id";
+    public static final String DISPLAY_NAME = "displayName";
+    public static final String SERVER_ID = "serverId";
+    public static final String ACCOUNT_KEY = "accountKey";
+    public static final String MAILBOX_KEY = "mailboxKey";
+    public static final String EMAIL_ADDRESS = "emailAddress";
+    public static final String IS_DEFAULT = "isDefault";
+    public static final String TYPE = "type";
+    public static final String[] EMAIL_BOX_PROJECTION = new String[] {
+        RECORD_ID, DISPLAY_NAME, ACCOUNT_KEY, TYPE
+    };
+    public static final int EMAIL_BOX_COLUMN_RECORD_ID = 0;
+    public static final int EMAIL_BOX_COLUMN_DISPLAY_NAME = 1;
+    public static final int EMAIL_BOX_COLUMN_ACCOUNT_KEY = 2;
+    public static final int EMAIL_BOX_COLUMN_TYPE = 3;
+    public static final String[] EMAIL_MESSAGE_PROJECTION = new String[] {
+        RECORD_ID, MAILBOX_KEY, ACCOUNT_KEY
+    };
+    public static final int MSG_COL_RECORD_ID = 0;
+    public static final int MSG_COL_MAILBOX_KEY = 1;
+    public static final int MSG_COL_ACCOUNT_KEY = 2;
+    private static final String[] ACCOUNT_ID_PROJECTION = new String[] {
+        RECORD_ID, EMAIL_ADDRESS, IS_DEFAULT
+    };
+    private static final String[] ACCOUNT_ID_NAME_PROJECTION = new String[] {
+       RECORD_ID, EMAIL_ADDRESS, DISPLAY_NAME
+    };
+    // Types of mailboxes. From EmailContent.java
+    // inbox
+    public static final int TYPE_INBOX = 0;
+    // draft
+    public static final int TYPE_DRAFT = 3;
+    // outbox
+    public static final int TYPE_OUTBOX = 4;
+    // sent
+    public static final int TYPE_SENT = 5;
+    // deleted
+    public static final int TYPE_DELETED = 6;
+
+    public static HashMap<Long, Integer> sAccToMas = new HashMap<Long, Integer>();
+    public static HashMap<Integer, Long> sMasToAcc = new HashMap<Integer, Long>();
+
+    public static void clearMapTable() {
+        sAccToMas.clear();
+        sMasToAcc.clear();
+    }
+
+    public static void updateMapTable(long accountId, int masId) {
+        if (sAccToMas.containsKey(accountId)) {
+            sAccToMas.remove(accountId);
+        }
+        if (sMasToAcc.containsKey(masId)) {
+            sMasToAcc.remove(masId);
+        }
+        sAccToMas.put(accountId, masId);
+        sMasToAcc.put(masId, accountId);
+    }
+
+    public static long getAccountId(int masId) {
+        Long accountId = sMasToAcc.get(masId);
+        return (accountId != null) ? accountId : -1;
+    }
+
+    public static int getMasId(long accountId) {
+        Integer masId = sAccToMas.get(accountId);
+        return (masId != null) ? masId : -1;
+    }
+
+    public static void removeMasIdIfNotPresent(List<Long> accountIdList) {
+        Collection<Long> oldList = sMasToAcc.values();
+        ArrayList<Long> toRemove = new ArrayList<Long>();
+        for (long oldId : oldList) {
+            if (!accountIdList.contains(oldId)) {
+                // remove it
+                toRemove.add(oldId);
+            }
+        }
+        for (long accountId : toRemove) {
+            Integer masId = sAccToMas.remove(accountId);
+            if (masId != null) {
+                sMasToAcc.remove(masId);
+            }
+        }
+    }
+
+    public static int countEmailAccount(Context context) {
+        return SqlHelper.count(context, EMAIL_ACCOUNT_URI, null, null);
+    }
+
+    /**
+     * Returns whether Email account exists
+     * @param context the calling Context
+     * @return true if any Email account exists; false otherwise
+     */
+    public static boolean hasEmailAccount(Context context) {
+        int numAccounts = SqlHelper.count(context, EMAIL_ACCOUNT_URI, null, null);
+        if (numAccounts > 0) {
+            return true;
+        } else {
+            return false;
+        }
+    }
+
+    public static boolean hasEmailAccount(Context context, long accountId) {
+        String where = RECORD_ID + "=" + accountId;
+        int numAccounts = SqlHelper.count(context, EMAIL_ACCOUNT_URI, where, null);
+        if (numAccounts > 0) {
+            return true;
+        } else {
+            return false;
+        }
+    }
+
+    /**
+     * Returns the first Email account id that satisfies where condition
+     * @param context the calling Context
+     * @param where the condition respect to {@link #RECORD_ID}, {@link #EMAIL_ADDRESS}, {@link #IS_DEFAULT}
+     * @return Email account id
+     */
+    public static long getEmailAccountId(Context context, String where) {
+        if (V) Log.v(TAG, "getEmailAccountId(" + where + ")");
+        long id = -1;
+        Cursor cursor = context.getContentResolver().query(EMAIL_ACCOUNT_URI,
+                ACCOUNT_ID_PROJECTION, where, null, null);
+        if (cursor != null) {
+            if (cursor.moveToFirst()) {
+                id = cursor.getLong(0);
+            }
+            cursor.close();
+        }
+        if (V) Log.v(TAG, "id = " + id);
+        return id;
+    }
+
+    /**
+     * Returns the first Email account id name that satisfies where condition
+     * @param context the calling Context
+     * @param where the condition respect to {@link #RECORD_ID}, {@link #EMAIL_ADDRESS}, {@link #IS_DEFAULT}
+     * @return Email account id Email
+     */
+    public static String getEmailAccountIdEmail(Context context, String where) {
+        if (V) Log.v(TAG, "getEmailAccountIdName(" + where + ")");
+        String idEmail = null;
+        Cursor cursor = context.getContentResolver().query(EMAIL_ACCOUNT_URI,
+                ACCOUNT_ID_NAME_PROJECTION, where, null, null);
+        if (cursor != null) {
+            if (cursor.moveToFirst()) {
+                idEmail = cursor.getString(1);
+            }
+            cursor.close();
+        }
+        if (V) Log.v(TAG, "idEmail = " + idEmail);
+        return idEmail;
+    }
+
+    /**
+     * Returns the first Email account id name that satisfies where condition
+     * @param context the calling Context
+     * @param where the condition respect to {@link #RECORD_ID}, {@link #EMAIL_ADDRESS}, {@link #IS_DEFAULT}
+     * @return Email account id Display Name
+     */
+    public static String getEmailAccountDisplayName(Context context, String where) {
+        if (V) Log.v(TAG, "getEmailAccountIdName(" + where + ")");
+        String displayName = null;
+        Cursor cursor = context.getContentResolver().query(EMAIL_ACCOUNT_URI,
+                ACCOUNT_ID_NAME_PROJECTION, where, null, null);
+        if (cursor != null) {
+            if (cursor.moveToFirst()) {
+                displayName = cursor.getString(2);
+            }
+            cursor.close();
+        }
+        if (V) Log.v(TAG, "displayName = " + displayName);
+        return displayName;
+    }
+
+
+    /**
+     * Returns the default Email account id; the first account id if no default account
+     * @param context the calling Context
+     * @return the default Email account id
+     */
+    public static long getDefaultEmailAccountId(Context context) {
+        if (V) Log.v(TAG, "getDefaultEmailAccountId()");
+        long id = getEmailAccountId(context, IS_DEFAULT + "=1");
+        if (id == -1) {
+            id = getEmailAccountId(context, null);
+        }
+        if (V) Log.v(TAG, "id = " + id);
+        return id;
+    }
+
+    public static List<Long> getEmailAccountIdList(Context context) {
+        if (V) Log.v(TAG, "getEmailAccountIdList()");
+        long id = -1;
+        ArrayList<Long> list = new ArrayList<Long>();
+        Cursor cursor = context.getContentResolver().query(EMAIL_ACCOUNT_URI,
+                ACCOUNT_ID_PROJECTION, null, null, null);
+        if (cursor != null) {
+            if (cursor.moveToFirst()) {
+                do {
+                    id = cursor.getLong(0);
+                    list.add(id);
+                    if (V) Log.v(TAG, "id = " + id);
+                } while (cursor.moveToNext());
+            }
+            cursor.close();
+        }
+        return list;
+    }
+
+    /**
+     * Return Email sub folder list for the id and serverId path
+     * @param context the calling Context
+     * @param id the email account id
+     * @return the list of  server id's of Email sub folder
+     */
+    public static List<String> getEmailFolderListAtPath(Context context, long id, String path) {
+        if (V) Log.v(TAG, "getEmailFolderListAtPath: id = " + id + "path: " + path);
+        StringBuilder sb = new StringBuilder();
+        if (id > 0) {
+            sb.append(ACCOUNT_KEY);
+            sb.append("=");
+            sb.append(id);
+            sb.append(" AND ");
+        }
+        //Return Default List for no path
+        if(path.equals("")) {
+            sb.append(SERVER_ID);
+            sb.append("=");
+            sb.append(DISPLAY_NAME);
+        }
+        else {
+            sb.append(SERVER_ID);
+            sb.append(" LIKE '");
+            sb.append(path);
+            sb.append("/%'");
+        }
+        return SqlHelper.getListForColumn(context, EMAIL_BOX_URI, SERVER_ID, sb.toString(), null);
+    }
+    /**
+     * Return Email folder list for the id
+     * @param context the calling Context
+     * @param id the email account id
+     * @return the list of Email folder
+     */
+    public static List<String> getEmailFolderList(Context context, long id) {
+        if (V) Log.v(TAG, "getEmailFolderList: id = " + id);
+        StringBuilder sb = new StringBuilder();
+        if (id > 0) {
+            sb.append(ACCOUNT_KEY);
+            sb.append("=");
+            sb.append(id);
+        }
+        return SqlHelper.getListForColumn(context, EMAIL_BOX_URI, DISPLAY_NAME, sb.toString(), null);
+    }
+
+    /**
+     * Return folder name for the type of mailbox
+     * @param context the calling Context
+     * @param id the email account id
+     * @param type
+     * @return
+     */
+    public static String getFolderForType(Context context, long id, int type) {
+        if (V) Log.v(TAG, "getFolderForType: id = " + id + ", type = " + type);
+        StringBuilder sb = new StringBuilder();
+        if (id > 0) {
+            sb.append(ACCOUNT_KEY);
+            sb.append("=");
+            sb.append(id);
+            sb.append(" AND ");
+        }
+        sb.append(TYPE);
+        sb.append("=");
+        sb.append(type);
+        return SqlHelper.getFirstValueForColumn(context, EMAIL_BOX_URI, DISPLAY_NAME, sb.toString(), null);
+    }
+
+    /**
+     * Return list of folder names for the type of mailbox
+     * @param context the calling Context
+     * @param id the email account id
+     * @param type
+     * @return
+     */
+    public static List<String> getFoldersForType(Context context, long id, int type) {
+        if (V) Log.v(TAG, "getFolderForType: id = " + id + ", type = " + type);
+        StringBuilder sb = new StringBuilder();
+        if (id > 0) {
+            sb.append(ACCOUNT_KEY);
+            sb.append("=");
+            sb.append(id);
+            sb.append(" AND ");
+        }
+        sb.append(TYPE);
+        sb.append("=");
+        sb.append(type);
+        return SqlHelper.getListForColumn(context, EMAIL_BOX_URI, DISPLAY_NAME, sb.toString(), null);
+    }
+
+    public static int getTypeForFolder(Context context, long id, String folderName) {
+        if (V) Log.v(TAG, "getTypeForFolder: id = " + id + ", folderName = " + folderName);
+        StringBuilder sb = new StringBuilder();
+        if (id > 0) {
+            sb.append(ACCOUNT_KEY);
+            sb.append("=");
+            sb.append(id);
+            sb.append(" AND ");
+        }
+        sb.append(DISPLAY_NAME);
+        sb.append("=");
+        sb.append("'"+folderName+"'");
+        return SqlHelper.getFirstIntForColumn(context, EMAIL_BOX_URI, TYPE, sb.toString(), null);
+    }
+}
diff --git a/src/org/codeaurora/bluetooth/map/MapUtils/MapUtils.java b/src/org/codeaurora/bluetooth/map/MapUtils/MapUtils.java
new file mode 100644
index 0000000..6def60e
--- /dev/null
+++ b/src/org/codeaurora/bluetooth/map/MapUtils/MapUtils.java
@@ -0,0 +1,1986 @@
+/*
+ * Copyright (c) 2010-2012, The Linux Foundation. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *        * Redistributions of source code must retain the above copyright
+ *          notice, this list of conditions and the following disclaimer.
+ *        * Redistributions in binary form must reproduce the above copyright
+ *          notice, this list of conditions and the following disclaimer in the
+ *          documentation and/or other materials provided with the distribution.
+ *        * Neither the name of The Linux Foundation nor
+ *          the names of its contributors may be used to endorse or promote
+ *          products derived from this software without specific prior written
+ *          permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NON-INFRINGEMENT ARE DISCLAIMED.    IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
+ * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
+ * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
+ * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package org.codeaurora.bluetooth.map.MapUtils;
+
+import android.content.Context;
+import com.android.vcard.VCardProperty;
+import com.android.vcard.VCardInterpreter;
+import com.android.vcard.VCardParser;
+import com.android.vcard.VCardParser_V21;
+import com.android.vcard.VCardParser_V30;
+import com.android.vcard.exception.VCardException;
+import com.android.vcard.exception.VCardVersionException;
+import android.util.Log;
+import android.util.Xml;
+import java.io.UnsupportedEncodingException;
+
+import org.codeaurora.bluetooth.map.BluetoothMasService;
+
+import org.xmlpull.v1.XmlSerializer;
+import com.android.internal.util.FastXmlSerializer;
+
+import java.io.IOException;
+import java.io.StringBufferInputStream;
+import java.io.ByteArrayInputStream;
+import java.io.StringWriter;
+import java.util.List;
+import java.io.ByteArrayOutputStream;
+import java.io.OutputStreamWriter;
+
+import static com.android.vcard.VCardConstants.PROPERTY_EMAIL;
+import static com.android.vcard.VCardConstants.PROPERTY_FN;
+import static com.android.vcard.VCardConstants.PROPERTY_N;
+import static com.android.vcard.VCardConstants.PROPERTY_TEL;
+import static com.android.vcard.VCardConstants.VERSION_V21;
+import static com.android.vcard.VCardConstants.VERSION_V30;
+
+/**
+ * MapUtils is a class of utility methods that provide routines for converting
+ * data to either XML or bMessage formats. The class shall also support parsing
+ * XML and bMessage formatted data.
+ * <p>
+ * The following methods are currently supported:
+ * <p>
+ * folderListingXML()
+ *
+ * @version 0.1
+ *
+ */
+public class MapUtils {
+    public static final String TAG = "MapUtils";
+    public static final boolean V = BluetoothMasService.VERBOSE;
+    private static final String CRLF = "\r\n";
+
+    /**
+     * folderListingXML
+     *
+     * This method takes a list of folder names and returns a String with the
+     * XML version of the List
+     *
+     * @param list
+     *            An array of strings where each element represents a folder
+     *            name
+     * @return This method returns either null or a String
+     */
+    public static String folderListingXML(List<String> list) {
+        String str = "<?xml version=\"1.0\"?><!DOCTYPE folder-listing SYSTEM \"obex-folder-listing.dtd\"><folder-listing version=\"1.0\">";
+
+        for (String s : list) {
+            str += "<folder name=\"";
+            str += s;
+            str += "\"/>";
+        }
+
+        str += "</folder-listing>";
+
+        return str;
+    }
+
+    /**
+     * messageListingXML
+     *
+     * This method takes a list of message objects and returns a String with the
+     * XML version of the List
+     *
+     * @param list
+     *            An array of message objects where each element represents a
+     *            message
+     * @return This method returns either null or a String
+     */
+    public static String messageListingXML(List<MsgListingConsts> list) {
+        XmlSerializer serializer = new FastXmlSerializer();
+        ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
+        OutputStreamWriter myOutputStreamWriter = null;
+        try {
+            myOutputStreamWriter = new OutputStreamWriter(outputStream, "UTF-8");
+        } catch (UnsupportedEncodingException e) {
+            Log.e(TAG, "Failed to encode: charset=" + "UTF-8");
+            return null;
+        }
+        try {
+            String str1;
+            String str2 = "<?xml version=\"1.0\"?>";
+            serializer.setOutput(myOutputStreamWriter);
+            serializer.startDocument("UTF-8", true);
+            serializer.setFeature("http://xmlpull.org/v1/doc/features.html#indent-output", true);
+            serializer.text("\n");
+            serializer.startTag(null, "MAP-msg-listing");
+            serializer.attribute(null, "version", "1.0");
+            for (MsgListingConsts msg : list) {
+                serializer.startTag(null, "msg");
+
+                serializer.attribute(null, "handle", ("" + msg.msg_handle));
+                if (msg.sendSubject == true) {
+                    if (msg.subject == null){
+                        serializer.attribute(null, "subject", "");
+                    } else {
+                        serializer.attribute(null, "subject", msg.subject);
+                    }
+
+                }
+                if (msg.datetime != null) {
+                    serializer.attribute(null, "datetime", msg.datetime);
+                }
+                if (msg.sender_name != null) {
+                    serializer.attribute(null, "sender_name", msg.sender_name);
+                }
+
+                if (msg.sender_addressing != null) {
+                    serializer.attribute(null, "sender_addressing",
+                            msg.sender_addressing);
+                }
+
+                if (msg.replyto_addressing != null) {
+                    serializer.attribute(null, "replyto_addressing",
+                            msg.replyto_addressing);
+                }
+
+                if (msg.recepient_name != null) {
+                    serializer.attribute(null, "recipient_name",
+                            msg.recepient_name);
+                }
+                if (msg.sendRecipient_addressing == true) {
+                    if (msg.recepient_addressing != null) {
+                        serializer.attribute(null, "recipient_addressing",
+                                msg.recepient_addressing);
+                    } else {
+                        serializer.attribute(null, "recipient_addressing", "");
+                    }
+                }
+                if (msg.type != null) {
+                    serializer.attribute(null, "type", msg.type);
+                }
+                if (msg.size != -1) {
+                    serializer.attribute(null, "size", ("" + msg.size));
+                }
+
+                if (msg.contains_text != null) {
+                    serializer.attribute(null, "text", msg.contains_text);
+                }
+
+                if (msg.reception_status != null) {
+                    serializer.attribute(null, "reception_status",
+                            msg.reception_status);
+                }
+
+                if (msg.attachment_size != -1) {
+                    serializer.attribute(null, "attachment_size",
+                            ("" + Integer.toString(msg.attachment_size)));
+                }
+
+                if (msg.priority != null) {
+                    serializer.attribute(null, "priority", msg.priority);
+                }
+
+                if (msg.read != null) {
+                    serializer.attribute(null, "read", msg.read);
+                }
+
+                if (msg.sent != null) {
+                    serializer.attribute(null, "sent", msg.sent);
+                }
+
+                if (msg.msg_protected != null) {
+                    serializer.attribute(null, "protected", msg.msg_protected);
+                }
+
+                serializer.endTag(null, "msg");
+
+            }
+            serializer.endTag(null, "MAP-msg-listing");
+            serializer.endDocument();
+            try {
+                str1 = outputStream.toString("UTF-8");
+                if (V) Log.v(TAG, "Printing XML-Converted String: " + str1);
+            int line1 = 0;
+            line1 = str1.indexOf("\n");
+            str2 += str1.substring(line1 + 1);
+            if (list.size() > 0) {
+                int indxHandle = str2.indexOf("msg handle");
+                String str3 = "<" + str2.substring(indxHandle);
+                str2 = str2.substring(0, (indxHandle - 1)) + str3;
+            }
+            return str2;
+            } catch (UnsupportedEncodingException e) {
+                Log.e(TAG, "Failed to encode: charset=" + "UTF-8");
+                return null;
+            }
+        } catch (IllegalArgumentException e) {
+
+            e.printStackTrace();
+        } catch (IllegalStateException e) {
+
+            e.printStackTrace();
+        } catch (IOException e) {
+
+            e.printStackTrace();
+        }
+        return null;
+    }
+
+    /**
+     * msgListingGetHdrXML
+     *
+     * This method returns a String with the XML header
+     *
+     * @return This method returns a String
+     */
+    public static String msgListingGetHdrXML() {
+        String str1 = "<MAP-msg-listing version = \"1.0\">\n";
+        return str1;
+    }
+
+    /**
+     * msgListingGetFooterXML
+     *
+     * This method returns a String with the XML footer
+     *
+     * @return This method returns a String
+     */
+    public static String msgListingGetFooterXML() {
+        String str1 = "</MAP-msg-listing>\n";
+        return str1;
+    }
+
+    /**
+     * msgListingGetMsgsXML
+     *
+     * This method takes a list of message objects and returns a String with the
+     * XML messages
+     *
+     * @param list
+     *            An array of message objects where each element represents a
+     *            message
+     * @return This method returns either null or a String
+     */
+    public static String msgListingGetMsgsXML(List<MsgListingConsts> list) {
+        XmlSerializer serializer = Xml.newSerializer();
+        StringWriter writer = new StringWriter();
+        try {
+            String str1;
+            serializer.setOutput(writer);
+            serializer.startDocument("", false);
+            serializer.text("\n");
+            for (MsgListingConsts msg : list) {
+                serializer.startTag("", "msg");
+                serializer.attribute("", "handle", ("" + msg.msg_handle));
+                if (msg.subject != null) {
+                    serializer.attribute("", "subject", msg.subject);
+                } else {
+
+                }
+                if (msg.datetime != null) {
+                    serializer.attribute("", "datetime", msg.datetime);
+                } else {
+
+                }
+                if (msg.sender_name != null) {
+                    serializer.attribute("", "sender_name", msg.sender_name);
+                } else {
+
+                }
+
+                if (msg.sender_addressing != null) {
+                    serializer.attribute("", "sender_addressing",
+                            msg.sender_addressing);
+                } else {
+
+                }
+                if (msg.recepient_name != null) {
+                    serializer.attribute("", "recipient_name",
+                            msg.recepient_name);
+                } else {
+
+                }
+                if (msg.recepient_addressing != null) {
+                    serializer.attribute("", "recipient_addressing",
+                            msg.recepient_addressing);
+                } else {
+
+                }
+                if (msg.type != null) {
+                    serializer.attribute("", "type", msg.type);
+                } else {
+
+                }
+                if (msg.size != 0) {
+                    serializer.attribute("", "size", ("" + msg.size));
+                } else {
+
+                }
+                if (msg.attachment_size != -1) {
+                    serializer.attribute("", "attachment_size",
+                            ("" + Integer.toString(msg.attachment_size)));
+                } else {
+
+                }
+                if (msg.contains_text != null) {
+                    serializer.attribute("", "text", msg.contains_text);
+                } else {
+
+                }
+                if (msg.priority != null) {
+                    serializer.attribute("", "priority", msg.priority);
+                } else {
+
+                }
+                if (msg.read != null) {
+                    serializer.attribute("", "read", msg.read);
+                } else {
+
+                }
+                if (msg.sent != null) {
+                    serializer.attribute("", "sent", msg.sent);
+                } else {
+
+                }
+
+                if (msg.replyto_addressing != null) {
+                    serializer.attribute("", "replyto_addressing",
+                            msg.replyto_addressing);
+                } else {
+
+                }
+
+                serializer.endTag("", "msg");
+
+            }
+            serializer.endDocument();
+            str1 = writer.toString();
+
+            int line1 = 0;
+            line1 = str1.indexOf("\n");
+            if (line1 > 0) {
+                return (str1.substring((line1)));
+            } else {
+                return str1;
+            }
+
+        } catch (IllegalArgumentException e) {
+            e.printStackTrace();
+        } catch (IllegalStateException e) {
+            e.printStackTrace();
+        } catch (IOException e) {
+            e.printStackTrace();
+        }
+        return null;
+    }
+
+    /**
+     * mapEventReportXML
+     *
+     * This method takes a list of map event report object and returns a String
+     * with the XML message
+     *
+     * @param type
+     *            Report type (e.g. NewMessage)
+     * @param handle
+     *            handle created by caller
+     * @param folder
+     *            Path to folder to use
+     * @param Oldfolder
+     *            Path to old folder to use
+     * @param msgType
+     *            Type of message (SMS_GSM, SMS_CDMA)
+     *
+     * @return This method returns either null or a String
+     */
+    public static String mapEventReportXML(String type, String handle, String folder,
+            String oldFolder, String msgType) {
+        XmlSerializer serializer = Xml.newSerializer();
+        StringWriter writer = new StringWriter();
+
+        try {
+            String str1;
+            serializer.setOutput(writer);
+            serializer.startDocument("", false);
+            serializer.text("\n");
+            serializer.startTag("", "MAP-event-report");
+            serializer.attribute("", "version", "1.0");
+            serializer.text("\n");
+
+            serializer.startTag("", "event");
+            if (type != null) {
+                serializer.attribute("", "type", ("" + type));
+            } else {
+
+            }
+            if (handle != null) {
+                serializer.attribute("", "handle", ("" + handle));
+            } else {
+
+            }
+            if (folder != null) {
+                serializer.attribute("", "folder", ("" + folder));
+            } else {
+
+            }
+            if (oldFolder != null) {
+                serializer.attribute("", "old_folder", ("" + oldFolder));
+            } else {
+
+            }
+
+            if (msgType != null) {
+                serializer.attribute("", "msg_type", ("" + msgType));
+            } else {
+
+            }
+            serializer.endTag("", "event");
+            serializer.text("\n");
+            serializer.endTag("", "MAP-event-report");
+            serializer.endDocument();
+            str1 = writer.toString();
+            int line1 = 0;
+            line1 = str1.indexOf("\n");
+            if (line1 > 0) {
+                int index = str1.indexOf("event type");
+                String str2 = "<" + str1.substring(index);
+                str1 = "<MAP-event-report version=\"1.0\">" + "\n" + str2;
+                return str1;
+            } else {
+                return str1;
+            }
+
+        } catch (IllegalArgumentException e) {
+            e.printStackTrace();
+        } catch (IllegalStateException e) {
+            e.printStackTrace();
+        } catch (IOException e) {
+            e.printStackTrace();
+        }
+
+        return null;
+    }
+
+    /**
+     * toBmessageSMS
+     *
+     * This method takes as input a list of BmessageConsts objects and creates a
+     * String in the bMessage format.
+     *
+     * @param list
+     *            An array of message objects where each element represents a
+     *            message
+     * @return This method returns either null or a String
+     */
+    public static String toBmessageSMS(BmessageConsts bmsg) {
+        StringBuilder sb = new StringBuilder();
+
+        try {
+            sb.append("BEGIN:BMSG");
+            sb.append("\r\n");
+            if (bmsg.bmsg_version != null) {
+                sb.append("VERSION:").append(bmsg.bmsg_version).append("\r\n");
+            } else {
+
+            }
+            if (bmsg.status != null) {
+                sb.append("STATUS:").append(bmsg.status).append("\r\n");
+            } else {
+
+            }
+            if (bmsg.type != null) {
+                sb.append("TYPE:").append(bmsg.type).append("\r\n");
+            } else {
+
+            }
+            if (bmsg.folder != null) {
+                sb.append("FOLDER:").append(bmsg.folder).append("\r\n");
+            } else {
+
+            }
+
+            // Originator
+            sb.append("BEGIN:VCARD").append("\r\n");
+
+            if (bmsg.vcard_version != null) {
+                sb.append("VERSION:").append(bmsg.vcard_version).append("\r\n");
+            } else {
+
+            }
+            if (bmsg.originator_vcard_name != null) {
+                sb.append("N:").append(bmsg.originator_vcard_name)
+                        .append("\r\n");
+            } else {
+
+            }
+            if (bmsg.originator_vcard_phone_number != null) {
+                sb.append("TEL:").append(bmsg.originator_vcard_phone_number)
+                        .append("\r\n");
+            } else {
+
+            }
+
+            sb.append("END:VCARD").append("\r\n");
+            // End Originator
+
+            sb.append("BEGIN:BENV").append("\r\n");
+
+            // Recipient
+            sb.append("BEGIN:VCARD").append("\r\n");
+
+            if (bmsg.vcard_version != null) {
+                sb.append("VERSION:").append(bmsg.vcard_version).append("\r\n");
+            } else {
+
+            }
+            if (bmsg.recipient_vcard_name != null) {
+                sb.append("N:").append(bmsg.recipient_vcard_name)
+                        .append("\r\n");
+            } else {
+
+            }
+            if (bmsg.recipient_vcard_phone_number != null) {
+                sb.append("TEL:").append(bmsg.recipient_vcard_phone_number)
+                        .append("\r\n");
+            } else {
+
+            }
+            sb.append("END:VCARD").append("\r\n");
+            // End Recipient
+
+            sb.append("BEGIN:BBODY").append("\r\n");
+
+            if (bmsg.body_charset != null) {
+                sb.append("CHARSET:").append(bmsg.body_charset)
+                        .append("\r\n");
+            } else {
+
+            }
+
+            if (bmsg.body_encoding != null) {
+                sb.append("ENCODING:").append(bmsg.body_encoding)
+                        .append("\r\n");
+            } else {
+
+            }
+            if (bmsg.body_length != 0) {
+                sb.append("LENGTH:").append(bmsg.body_length).append("\r\n");
+            } else {
+
+            }
+            if (bmsg.body_msg != null) {
+                sb.append("BEGIN:MSG\r\n");
+                sb.append(bmsg.body_msg).append("\r\n");
+                sb.append("END:MSG\r\n");
+
+            } else {
+
+            }
+
+            sb.append("END:BBODY").append("\r\n");
+            sb.append("END:BENV").append("\r\n");
+
+            sb.append("END:BMSG");
+            sb.append("\r\n");
+        } catch (Exception e) {
+            e.printStackTrace();
+        }
+
+        return sb.toString();
+
+    }
+
+    /**
+     * toBmessageMMS
+     *
+     * This method takes as input a list of BmessageConsts objects and creates a
+     * String in the bMessage format.
+     *
+     * @param list
+     *            An array of message objects where each element represents a
+     *            message
+     * @return This method returns either null or a String
+     */
+    public static String toBmessageMMS(BmessageConsts bmsg) {
+        StringBuilder sb = new StringBuilder();
+
+        try {
+            sb.append("BEGIN:BMSG");
+            sb.append("\r\n");
+            if (bmsg.bmsg_version != null) {
+                sb.append("VERSION:").append(bmsg.bmsg_version).append("\r\n");
+            } else {
+
+            }
+            if (bmsg.status != null) {
+                sb.append("STATUS:").append(bmsg.status).append("\r\n");
+            } else {
+
+            }
+            if (bmsg.type != null) {
+                sb.append("TYPE:").append(bmsg.type).append("\r\n");
+            } else {
+
+            }
+            if (bmsg.folder != null) {
+                sb.append("FOLDER:").append(bmsg.folder).append("\r\n");
+            } else {
+
+            }
+
+            // Originator
+            sb.append("BEGIN:VCARD").append("\r\n");
+
+            if (bmsg.vcard_version != null) {
+                sb.append("VERSION:").append(bmsg.vcard_version).append("\r\n");
+            } else {
+
+            }
+            if (bmsg.originator_vcard_name != null) {
+                sb.append("N:").append(bmsg.originator_vcard_name)
+                        .append("\r\n");
+            } else {
+
+            }
+            if (bmsg.originator_vcard_phone_number != null) {
+                sb.append("TEL:").append(bmsg.originator_vcard_phone_number)
+                        .append("\r\n");
+            } else {
+
+            }
+
+            sb.append("END:VCARD").append("\r\n");
+            // End Originator
+
+            sb.append("BEGIN:BENV").append("\r\n");
+
+            if ((bmsg.recipient_vcard_phone_number != null) &&
+                            (bmsg.recipient_vcard_phone_number.contains(";"))) {
+                if (V) Log.v (TAG, "recipient_vcard_name:" + bmsg.recipient_vcard_name);
+                if (V) Log.v (TAG, "recipient_vcard_phone_number:" + bmsg.recipient_vcard_phone_number);
+                String numberTokens[] = bmsg.recipient_vcard_phone_number.split(";");
+                String nameTokens[] = bmsg.recipient_vcard_name.split(";");
+                if (V) Log.v (TAG, "Length:name:" + nameTokens.length + "number:" + numberTokens.length);
+                for (int i=0; i < numberTokens.length; i++) {
+                    // Recipient
+                    sb.append("BEGIN:VCARD").append("\r\n");
+
+                    if (bmsg.vcard_version != null) {
+                        sb.append("VERSION:").append(bmsg.vcard_version).append("\r\n");
+                    } else {
+
+                    }
+
+                    if (nameTokens[i] != null) {
+                        sb.append("N:").append(nameTokens[i])
+                            .append("\r\n");
+                    } else {
+
+                    }
+
+                    if (numberTokens[i] != null) {
+                        sb.append("TEL:").append(numberTokens[i])
+                            .append("\r\n");
+                    } else {
+
+                    }
+
+                    sb.append("END:VCARD").append("\r\n");
+                    // End Recipient
+                }
+            } else {
+                // Recipient
+                sb.append("BEGIN:VCARD").append("\r\n");
+
+                if (bmsg.vcard_version != null) {
+                    sb.append("VERSION:").append(bmsg.vcard_version).append("\r\n");
+                } else {
+
+                }
+                if (bmsg.recipient_vcard_name != null) {
+                    sb.append("N:").append(bmsg.recipient_vcard_name)
+                        .append("\r\n");
+                } else {
+
+                }
+                if (bmsg.recipient_vcard_phone_number != null) {
+                    sb.append("TEL:").append(bmsg.recipient_vcard_phone_number)
+                        .append("\r\n");
+                } else {
+
+                }
+                sb.append("END:VCARD").append("\r\n");
+                // End Recipient
+            }
+
+            sb.append("BEGIN:BBODY").append("\r\n");
+
+            sb.append("PARTID:26988").append("\r\n");
+
+            if (bmsg.body_encoding != null) {
+                sb.append("ENCODING:").append(bmsg.body_encoding)
+                        .append("\r\n");
+            } else {
+
+            }
+            sb.append("CHARSET:UTF-8").append("\r\n");
+            sb.append("LANGUAGE:").append("\r\n");
+
+            if (bmsg.body_length != 0) {
+                sb.append("LENGTH:").append(bmsg.body_length).append("\r\n");
+            } else {
+
+            }
+            if (bmsg.body_msg != null) {
+                sb.append("BEGIN:MSG\r\n");
+                sb.append(bmsg.body_msg).append("\r\n");
+                sb.append("END:MSG\r\n");
+
+            } else {
+
+            }
+
+            sb.append("END:BBODY").append("\r\n");
+            sb.append("END:BENV").append("\r\n");
+
+            sb.append("END:BMSG");
+            sb.append("\r\n");
+        } catch (Exception e) {
+            e.printStackTrace();
+        }
+
+        return sb.toString();
+    }
+
+    /**
+     * toBmessageEmail
+     *
+     * This method takes as input a list of BmessageConsts objects and creates a
+     * String in the bMessage format.
+     *
+     * @param list
+     *            An array of message objects where each element represents a
+     *            message
+     * @return This method returns either null or a String
+     */
+    public static String toBmessageEmail(BmessageConsts bmsg) {
+        StringBuilder sb = new StringBuilder();
+
+        try {
+            sb.append("BEGIN:BMSG");
+            sb.append("\r\n");
+            if (bmsg.bmsg_version != null) {
+                sb.append("VERSION:").append(bmsg.bmsg_version).append("\r\n");
+            } else {
+
+            }
+            if (bmsg.status != null) {
+                sb.append("STATUS:").append(bmsg.status).append("\r\n");
+            } else {
+
+            }
+            if (bmsg.type != null) {
+                sb.append("TYPE:").append(bmsg.type).append("\r\n");
+            } else {
+
+            }
+            if (bmsg.folder != null) {
+                sb.append("FOLDER:").append(bmsg.folder).append("\r\n");
+            } else {
+
+            }
+
+            // Originator
+            sb.append("BEGIN:VCARD").append("\r\n");
+
+            if (bmsg.vcard_version != null) {
+                sb.append("VERSION:").append(bmsg.vcard_version).append("\r\n");
+            } else {
+
+            }
+            if (bmsg.originator_vcard_name != null) {
+                sb.append("N:").append(bmsg.originator_vcard_name)
+                        .append("\r\n");
+            } else {
+
+            }
+            sb.append("TEL:").append("\r\n");
+            if (bmsg.originator_vcard_email != null) {
+                sb.append("EMAIL:").append(bmsg.originator_vcard_email)
+                        .append("\r\n");
+            } else {
+
+            }
+
+            sb.append("END:VCARD").append("\r\n");
+            // End Originator
+
+            sb.append("BEGIN:BENV").append("\r\n");
+
+            // Recipient
+            sb.append("BEGIN:VCARD").append("\r\n");
+
+            if (bmsg.vcard_version != null) {
+                sb.append("VERSION:").append(bmsg.vcard_version).append("\r\n");
+            } else {
+
+            }
+            if (bmsg.recipient_vcard_name != null) {
+                sb.append("N:").append(bmsg.recipient_vcard_name)
+                        .append("\r\n");
+            } else {
+
+            }
+            if (bmsg.recipient_vcard_name != null) {
+                sb.append("FN:").append(bmsg.recipient_vcard_name)
+                        .append("\r\n");
+            } else {
+
+            }
+            sb.append("TEL:").append("\r\n");
+            if (bmsg.recipient_vcard_email != null) {
+                sb.append("EMAIL:").append(bmsg.recipient_vcard_email)
+                        .append("\r\n");
+            } else {
+
+            }
+            sb.append("END:VCARD").append("\r\n");
+            // End Recipient
+
+            sb.append("BEGIN:BBODY").append("\r\n");
+
+            if (bmsg.body_encoding != null) {
+                sb.append("ENCODING:").append(bmsg.body_encoding)
+                        .append("\r\n");
+            } else {
+                sb.append("ENCODING:8BIT").append("\r\n");
+            }
+
+            sb.append("CHARSET:UTF-8").append("\r\n");
+
+            if (bmsg.body_length != 0) {
+                sb.append("LENGTH:").append(bmsg.body_length).append("\r\n");
+            } else {
+
+            }
+            if (bmsg.body_msg != null) {
+                sb.append("BEGIN:MSG\r\n");
+                sb.append(bmsg.body_msg).append("\r\n");
+                sb.append("END:MSG\r\n");
+
+            } else {
+
+            }
+
+            sb.append("END:BBODY").append("\r\n");
+            sb.append("END:BENV").append("\r\n");
+
+            sb.append("END:BMSG");
+            sb.append("\r\n");
+        } catch (Exception e) {
+            e.printStackTrace();
+        }
+
+        return sb.toString();
+
+    }
+
+
+    /**
+     * fromBmessageSMS
+     *
+     * This method takes as input a String in the bMessage format. It parses the
+     * String and loads a BmessageConsts object that is returned
+     *
+     * @param String
+     *            - which is a bMessage formatted SMS message
+     * @return This method returns a BmessageConsts object
+     * @throws BadRequestException
+     */
+    public static BmessageConsts fromBmessageSMS(String bmsg) throws BadRequestException {
+        BmessageConsts bMsgObj = new BmessageConsts();
+        String vCard = fetchRecipientVcard(bmsg);
+
+        RecipientVCard recipient = parseVCard(vCard);
+        if (recipient.mTel.length() == 0) {
+            throw new BadRequestException("No TEL in vCard");
+        }
+        bMsgObj.setRecipientVcard_phone_number(recipient.mTel);
+        if (V) Log.v(TAG, "Tel: " + recipient.mTel);
+
+        // Extract vCard Version
+        bMsgObj.setVcard_version(recipient.mVersion);
+
+        // Extract bMessage Version
+        bMsgObj.setBmsg_version(fetchVersion(bmsg));
+
+        // Extract Message Status
+        bMsgObj.setStatus(fetchReadStatus(bmsg));
+
+        // Extract Message Type
+        bMsgObj.setType(fetchType(bmsg));
+
+        // Extract Message Folder
+        bMsgObj.setFolder(fetchFolder(bmsg));
+
+        // Fetch Message Length
+        bMsgObj.setBody_length(fetchBodyLength(bmsg));
+
+        // Extract Message
+        bMsgObj.setBody_msg(fetchBodyMsg(bmsg));
+
+        // Extract Message encoding
+        bMsgObj.setBody_encoding(fetchBodyEncoding(bmsg));
+
+        return bMsgObj;
+    }
+
+    /**
+     * fromBmessageMMS
+     *
+     * This method takes as input a String in the bMessage format. It parses the
+     * String and loads a BmessageConsts object that is returned
+     *
+     * @param String
+     *            - which is a bMessage formatted SMS message
+     * @return This method returns a BmessageConsts object
+     * @throws BadRequestException
+     */
+    public static BmessageConsts fromBmessageMMS(String bmsg) throws BadRequestException {
+        BmessageConsts bMsgObj = new BmessageConsts();
+
+        String phoneNumber = null;
+        String vCard = fetchRecipientVcard(bmsg);
+        if (V) Log.v(TAG, "vCard Info: " + vCard);
+
+        RecipientVCard recipient = parseVCard(vCard);
+        if (recipient.mEmail.length() > 0) {
+            phoneNumber = recipient.mEmail;
+        } else if (recipient.mTel.length() > 0) {
+            phoneNumber = recipient.mTel;
+        } else {
+            throw new BadRequestException("No Email/Tel in vCard");
+        }
+
+        if (V) Log.v(TAG, "Email: " + recipient.mEmail);
+        if (V) Log.v(TAG, "Tel: " + recipient.mTel);
+        if (V) Log.v(TAG, "Recipeint address: " + phoneNumber);
+        bMsgObj.setRecipientVcard_phone_number(phoneNumber);
+
+        // Extract vCard Version
+        bMsgObj.setVcard_version(recipient.mVersion);
+
+        // Extract bMessage Version
+        bMsgObj.setBmsg_version(fetchVersion(bmsg));
+
+        // Extract Message Status
+        bMsgObj.setStatus(fetchReadStatus(bmsg));
+
+        // Extract Message Type
+        bMsgObj.setType(fetchType(bmsg));
+
+        // Extract Message Folder
+        bMsgObj.setFolder(fetchFolder(bmsg));
+
+        // Fetch Message Length
+        bMsgObj.setBody_length(fetchBodyLength(bmsg));
+
+        // Extract Message
+        bMsgObj.setBody_msg(fetchBodyMsgMMS(bmsg));
+
+        // Extract Message encoding
+        bMsgObj.setBody_encoding(fetchBodyEncoding(bmsg));
+
+        return bMsgObj;
+    }
+
+    /**
+     * fromBmessageEmail
+     *
+     * This method takes as input a String in the bMessage format. It parses the
+     * String and loads a BmessageConsts object that is returned
+     *
+     * @param String
+     *            - which is a bMessage formatted Email message
+     * @return This method returns a BmessageConsts object
+     */
+
+    public static BmessageConsts fromBmessageEmail(Context context,
+                        String bmsg, int mMasId) throws BadRequestException {
+        BmessageConsts bMsgObj = new BmessageConsts();
+        String vCard = fetchRecipientVcard(bmsg);
+        if (V) Log.v(TAG, "vCard Info: " + vCard);
+
+        RecipientVCard recipient = parseVCard(vCard);
+        if (recipient.mEmail.length() == 0) {
+            throw new BadRequestException("No Email in recipient vCard");
+        }
+        bMsgObj.setRecipientVcard_email(recipient.mEmail);
+        if (V) Log.v(TAG, "Email: " + recipient.mEmail);
+
+        String vcardOrig = fetchOriginatorVcardEmail(bmsg);
+        RecipientVCard originator = parseVCard(vcardOrig);
+        if (originator.mEmail.length() == 0) {
+            long accountId = -1;
+            accountId = EmailUtils.getAccountId(mMasId);
+            if ((accountId != -1) && (context != null)) {
+                originator.mEmail = EmailUtils.getEmailAccountIdEmail
+                (context,EmailUtils.RECORD_ID + "=" + accountId);
+                Log.v(TAG, "Orig Email inserted by MSE as: " + originator.mEmail);
+                originator.mFormattedName = EmailUtils.getEmailAccountDisplayName
+                (context,EmailUtils.RECORD_ID + "=" + accountId);
+                Log.v(TAG, "Orig F-Name inserted by MSE as: " + originator.mFormattedName);
+            }
+            if (originator.mEmail.length() == 0) {
+                throw new BadRequestException("No Email in originator vCard");
+            }
+        }
+        bMsgObj.setOriginatorVcard_email(originator.mEmail);
+        if (V) Log.v(TAG, "Orig Email: " + originator.mEmail);
+        if (originator.mFormattedName.length() > 0) {
+            if (V) Log.v(TAG, "Orig Formatted Name: " + originator.mFormattedName);
+            bMsgObj.setOriginatorVcard_name(originator.mFormattedName);
+        } else {
+            if (V) Log.v(TAG, "Orig Name: " + originator.mName);
+            bMsgObj.setOriginatorVcard_name(originator.mName);
+        }
+
+        if (V){
+            Log.v(TAG, "Bmsg version:: "+fetchVersion(bmsg));
+        }
+        // Extract bMessage Version
+        bMsgObj.setBmsg_version(fetchVersion(bmsg));
+
+        if (V){
+            Log.v(TAG, "Read status:: "+fetchReadStatus(bmsg));
+        }
+        // Extract Message Status
+        bMsgObj.setStatus(fetchReadStatus(bmsg));
+
+        if (V){
+            Log.v(TAG, "Message Type:: "+fetchType(bmsg));
+        }
+        // Extract Message Type
+        bMsgObj.setType(fetchType(bmsg));
+
+        if (V){
+            Log.v(TAG, "Folder:: "+fetchFolder(bmsg));
+        }
+        // Extract Message Folder
+        bMsgObj.setFolder(fetchFolder(bmsg));
+
+        if (V){
+            Log.v(TAG, "body length:: "+fetchBodyLength(bmsg));
+        }
+        // Fetch Message Length
+        bMsgObj.setBody_length(fetchBodyLength(bmsg));
+        // Extract Message
+        bMsgObj.setBody_msg(fetchBodyEmail(bmsg));
+
+        if (V){
+            Log.v(TAG, "Message encoding:: "+fetchBodyEncoding(bmsg));
+        }
+        // Extract Message encoding
+        bMsgObj.setBody_encoding(fetchBodyEncoding(bmsg));
+
+        // Extract Subject of the email
+        bMsgObj.setSubject(fetchSubjectEmail(bmsg));
+
+        return bMsgObj;
+    }
+
+    /**
+     * fetchVcardEmail
+     *
+     * This method takes as input a vCard formatted String. It parses the String
+     * and returns the vCard Email as a String
+     *
+     * @param
+     * @return String This method returns a vCard Email String
+     */
+    private static String fetchVcardEmail(String vCard) {
+        int pos = vCard.indexOf(("EMAIL:"));
+        int beginVersionPos = pos + (("EMAIL:").length());
+        if (V){
+            Log.v(TAG,"Begin Version Position Email:: "+beginVersionPos);
+        }
+        int endVersionPos = vCard.indexOf("\n", beginVersionPos);
+        if (V){
+            Log.v(TAG,"End version Pos Email:: "+endVersionPos);
+        }
+        return vCard.substring(beginVersionPos, endVersionPos);
+    }
+
+    private static String fetchOriginatorEmail(String vCard) {
+        int pos = vCard.indexOf(("From:"));
+        int beginVersionPos = pos + (("From:").length());
+        int endVersionPos = vCard.indexOf(CRLF, beginVersionPos);
+        return vCard.substring(beginVersionPos, endVersionPos);
+    }
+
+    private static String fetchRecipientEmail(String vCard) {
+        int pos = vCard.indexOf(("To:"));
+        int beginVersionPos = pos + (("To:").length());
+        int endVersionPos = vCard.indexOf(CRLF, beginVersionPos);
+        return vCard.substring(beginVersionPos, endVersionPos);
+    }
+
+    private static String fetchRecepientVcardEmail(String bmsg) {
+        // Find the position of the first vCard in the string
+        int pos = bmsg.indexOf("BEGIN:BENV");
+        if (V){
+            Log.v(TAG, "vCard start position:: "+pos);
+        }
+        if (pos > 0) {
+            if (V){
+                Log.v(TAG, "vCard start position greater than 0::");
+            }
+            int beginVcardPos = pos + ("\r\n".length());
+            int endVcardPos = bmsg.indexOf("END:BENV");
+
+            return bmsg.substring(beginVcardPos, endVcardPos);
+
+        } else {
+            return "";
+        }
+    }
+
+    private static String fetchOriginatorVcardEmail(String bmsg) throws BadRequestException {
+        // Find the position of the first vCard in the string
+        int vCardBeginPos = bmsg.indexOf("BEGIN:VCARD");
+        if (vCardBeginPos == -1) {
+            // no vCard bad request
+            throw new BadRequestException("No Vcard");
+        }
+        if (V) Log.v(TAG, "vCard start position: " + vCardBeginPos);
+        int bEnvPos = bmsg.indexOf("BEGIN:BENV");
+        if (vCardBeginPos > bEnvPos) {
+            // the first vCard is not originator
+            return "";
+        }
+        int vCardEndPos = bmsg.indexOf("END:VCARD", vCardBeginPos);
+        if (vCardEndPos == -1) {
+            // no END:VCARD bad request
+            throw new BadRequestException("No END:VCARD");
+        }
+        vCardEndPos += "END:VCARD".length();
+
+        return bmsg.substring(vCardBeginPos, vCardEndPos);
+    }
+
+    private static String fetchSubjectEmail(String body) {
+        int pos = body.indexOf("Subject:");
+
+        if (pos > 0) {
+            int beginVersionPos = pos + (("Subject:").length());
+            int endVersionPos = body.indexOf("\n", beginVersionPos);
+            return body.substring(beginVersionPos, endVersionPos);
+        } else {
+            return "";
+        }
+    }
+
+    /**
+     * fetchVersion
+     *
+     * This method takes as input a String in the bMessage format. It parses the
+     * String and returns the bMessage version string
+     *
+     * @param
+     * @return String This method returns a Version String
+     */
+    private static String fetchVersion(String bmsg) {
+        int pos = bmsg.indexOf("VERSION:");
+        if (pos > 0) {
+
+            int beginVersionPos = pos + (("VERSION:").length());
+            int endVersionPos = bmsg.indexOf(CRLF, beginVersionPos);
+            return bmsg.substring(beginVersionPos, endVersionPos);
+
+        } else {
+            return "";
+        }
+    }
+
+    /**
+     * fetchOriginatorVcard
+     *
+     * This method takes as input a String in the bMessage format. It parses the
+     * String and returns the orginator vCard string
+     *
+     * @param
+     * @return String This method returns a vCard String
+     */
+    private static String fetchOriginatorVcard(String bmsg) {
+        // Find the position of the first vCard in the string
+        int pos = bmsg.indexOf("\r\nBEGIN:VCARD");
+        if (pos > 0) {
+            int beginVcardPos = pos + ("\r\n".length());
+            int endVcardPos = bmsg.indexOf("END:VCARD");
+
+            return bmsg.substring(beginVcardPos, endVcardPos);
+
+        } else {
+
+            return null;
+
+        }
+    }
+
+    /**
+     * fetchRecipientVcard
+     *
+     * This method takes as input a String in the bMessage format. It parses the
+     * String looking for the first envelope. Once it finds the envelop, it then
+     * looks for the first vCard and returns it as a String
+     *
+     * @param
+     * @return String This method returns a Vcard String
+     */
+    private static String fetchRecipientVcard(String bmsg) throws BadRequestException {
+        // Locate BENV
+        int locBENV = 0;
+        int pos = 0;
+        locBENV = bmsg.indexOf(CRLF + "BEGIN:BENV");
+        pos = bmsg.indexOf(CRLF + "BEGIN:VCARD", locBENV);
+        if (pos < 0) {
+            // no vCard in BENV
+            throw new BadRequestException("No vCard in BENV");
+        }
+        if (pos > 0) {
+            int beginVcardPos = pos + CRLF.length();
+            int endVcardPos = bmsg.indexOf("END:VCARD", pos);
+            if (endVcardPos < 0) {
+                // no END:VCARD in BENV
+                throw new BadRequestException("No END:VCARD in BENV");
+            }
+            endVcardPos += "END:VCARD".length();
+            return bmsg.substring(beginVcardPos, endVcardPos);
+
+        } else {
+            return "";
+        }
+    }
+
+    /**
+     * fetchReadStatus
+     *
+     * This method takes as input a String in the bMessage format. It parses the
+     * String and returns the bMessage read status String
+     *
+     * @param
+     * @return String This method returns a Read Status String
+     */
+    private static String fetchReadStatus(String bmsg) {
+        int pos = bmsg.indexOf("STATUS:");
+        if (pos > 0) {
+
+            int beginStatusPos = pos + (("STATUS:").length());
+            int endStatusPos = bmsg.indexOf(CRLF, beginStatusPos);
+            return bmsg.substring(beginStatusPos, endStatusPos);
+
+        } else {
+            return "";
+        }
+    }
+
+    /**
+     * fetchType
+     *
+     * This method takes as input a String in the bMessage format. It parses the
+     * String and returns the bMessage type String
+     *
+     * @param
+     * @return String This method returns a message type String
+     */
+    public static String fetchType(String bmsg) {
+        int pos = bmsg.indexOf("TYPE:");
+        if (pos > 0) {
+            int beginTypePos = pos + (("TYPE:").length());
+            int endTypePos = bmsg.indexOf(CRLF, beginTypePos);
+
+            return bmsg.substring(beginTypePos, endTypePos);
+
+        } else {
+            return null;
+        }
+    }
+
+    /**
+     * fetchFolder
+     *
+     * This method takes as input a String in the bMessage format. It parses the
+     * String and returns the bMessage Folder path String
+     *
+     * @param
+     * @return String This method returns a Folder path String
+     */
+    private static String fetchFolder(String bmsg) {
+        int pos = bmsg.indexOf("FOLDER:");
+        if (pos > 0) {
+            int beginVersionPos = pos + (("FOLDER:").length());
+            int endVersionPos = bmsg.indexOf(CRLF, beginVersionPos);
+
+            return bmsg.substring(beginVersionPos, endVersionPos);
+
+        } else {
+            return null;
+        }
+    }
+
+    /**
+     * fetchBody
+     *
+     * This method takes as input a String in the bMessage format. It parses the
+     * String and returns the bMessage Body as a String
+     *
+     * @param
+     * @return String This method returns a Body String
+     */
+    @SuppressWarnings("unused")
+    private static String fetchBody(String bmsg) {
+        int pos = bmsg.indexOf("BEGIN:BBODY");
+        if (pos > 0) {
+            int beginVersionPos = pos + (("BEGIN:BBODY").length());
+            int endVersionPos = bmsg.indexOf("END:BBODY", beginVersionPos);
+
+            return bmsg.substring(beginVersionPos, endVersionPos);
+
+        } else {
+            return null;
+        }
+
+    }
+
+    /**
+     * fetchBodyPartID
+     *
+     * This method takes as input a String consisting of the body portion of the
+     * bMessage. It parses the String and returns the bMessage Body Part ID as a
+     * String
+     *
+     * @param
+     * @return String This method returns a Body Part ID String
+     */
+    @SuppressWarnings("unused")
+    private static String fetchBodyPartID(String body) {
+        int pos = body.indexOf("PARTID:");
+        if (pos > 0) {
+            int beginVersionPos = pos + (("PARTID:").length());
+            int endVersionPos = body.indexOf(CRLF, beginVersionPos);
+            return body.substring(beginVersionPos, endVersionPos);
+
+        } else {
+            return null;
+        }
+    }
+
+    /**
+     * fetchCharset
+     *
+     * This method takes as input a String consisting of the body portion of the
+     * bMessage. It parses the String and returns the bMessage Charset as a
+     * String
+     *
+     * @param
+     * @return String This method returns a Charset String
+     */
+    @SuppressWarnings("unused")
+    private static String fetchCharset(String body) {
+        int pos = body.indexOf("CHARSET:");
+        if (pos > 0) {
+
+            int beginVersionPos = pos + (("CHARSET:").length());
+            int endVersionPos = body.indexOf(CRLF, beginVersionPos);
+
+            return body.substring(beginVersionPos, endVersionPos);
+
+        } else {
+
+            return null;
+        }
+    }
+
+    /**
+     * fetchBodyEncoding
+     *
+     * This method takes as input a String consisting of the body portion of the
+     * bMessage. It parses the String and returns the bMessage Body Encoding as
+     * a String
+     *
+     * @param
+     * @return String This method returns a Body Encoding String
+     */
+    private static String fetchBodyEncoding(String body) {
+        int pos = body.indexOf("ENCODING:");
+        if (pos > 0) {
+            int beginVersionPos = pos + (("ENCODING:").length());
+            int endVersionPos = body.indexOf(CRLF, beginVersionPos);
+            return body.substring(beginVersionPos, endVersionPos);
+
+        } else {
+
+            return null;
+        }
+    }
+
+    /**
+     * fetchBodyLanguage
+     *
+     * This method takes as input a String consisting of the body portion of the
+     * bMessage. It parses the String and returns the bMessage Body Language as
+     * a String
+     *
+     * @param
+     * @return String This method returns a Body Language String
+     */
+    @SuppressWarnings("unused")
+    private static String fetchBodyLanguage(String body) {
+        int pos = body.indexOf("LANGUAGE:");
+        if (pos > 0) {
+            int beginVersionPos = pos + (("LANGUAGE:").length());
+            int endVersionPos = body.indexOf(CRLF, beginVersionPos);
+            return body.substring(beginVersionPos, endVersionPos);
+
+        } else {
+
+            return null;
+        }
+    }
+
+    /**
+     * fetchBodyLength
+     *
+     * This method takes as input a String consisting of the body portion of the
+     * bMessage. It parses the String and returns the bMessage Body Length as an
+     * Integer
+     *
+     * @param
+     * @return String This method returns a Body Length Integer
+     */
+    private static Integer fetchBodyLength(String body) {
+        int pos = body.indexOf("LENGTH:");
+        if (pos > 0) {
+            int beginVersionPos = pos + (("LENGTH:").length());
+            int endVersionPos = body.indexOf(CRLF, beginVersionPos);
+            String bd = body.substring(beginVersionPos,
+                       endVersionPos);
+            Integer bl = Integer.valueOf(bd);
+            return bl;
+
+        } else {
+
+            return null;
+        }
+    }
+
+    /**
+     * fetchBodyMsg
+     *
+     * This method takes as input a String consisting of the body portion of the
+     * bMessage. It parses the String and returns the bMessage Body Message as a
+     * String
+     *
+     * @param
+     * @return String This method returns a Body Message String
+     */
+    private static String fetchBodyMsg(String body) {
+        int pos = body.indexOf("BEGIN:MSG");
+        if (pos > 0) {
+            int beginVersionPos = pos
+                    + (("BEGIN:MSG").length() + CRLF.length());
+            int endVersionPos = (body.indexOf("END:MSG", beginVersionPos) - CRLF
+                    .length());
+            return body.substring(beginVersionPos, endVersionPos);
+        } else {
+            return "";
+        }
+    }
+
+    private static String fetchBodyMsgMMS(String body) {
+        int pos = body.indexOf("BEGIN:MSG");
+        if (pos > 0) {
+            /* MMS body should contain both Begin and End message tags */
+            int beginVersionPos = pos;
+            int endVersionPos = body.indexOf("END:MSG", beginVersionPos) + ("END:MSG").length();
+            return body.substring(beginVersionPos, endVersionPos);
+        } else {
+            return "";
+        }
+    }
+
+    private static String fetchBodyMsgEmail(String body) {
+        if (V){
+            Log.v(TAG, "bMessageEmail inside fetch body ::"+body);
+        }
+        int pos = body.indexOf("Content-Disposition:inline");
+        if (pos > 0) {
+            int beginVersionPos = pos
+                    + (("Content-Disposition:inline").length() + CRLF.length());
+            int endVersionPos = (body.indexOf("--RPI-Messaging", beginVersionPos) - CRLF
+                    .length());
+
+            return body.substring(beginVersionPos, endVersionPos);
+
+        } else {
+
+            return null;
+        }
+    }
+
+    private static String fetchBoundary(String body) {
+        int pos = body.indexOf("boundary=\"");
+        if (pos > 0) {
+            int beginVersionPos = pos + (("boundary=\"").length());
+            int endVersionPos = body.indexOf("\"", beginVersionPos);
+            return body.substring(beginVersionPos, endVersionPos);
+
+        } else {
+
+            return null;
+        }
+    }
+
+    public static String fetchBodyEmail(String body) throws BadRequestException {
+        if (V) Log.v(TAG, "inside fetch body Email :"+body);
+
+        int beginVersionPos = -1;
+        int rfc822Flag = 0;
+        int mimeFlag = 0;
+        int beginVersionPos1 = -1;
+        String contentType;
+        int pos1 = 0;
+        String boundary = fetchBoundary(body);
+        if(boundary != null && !boundary.equalsIgnoreCase("")){
+            pos1 = body.indexOf("--"+boundary);
+            mimeFlag = 1;
+        }
+        else{
+            pos1 = body.indexOf("Date:");
+            mimeFlag = 0;
+        }
+        int contentIndex = body.indexOf("Content-Type",pos1);
+        if(contentIndex > 0){
+            contentType = fetchContentType(body, boundary);
+            if(contentType != null && contentType.trim().equalsIgnoreCase("message/rfc822")){
+                rfc822Flag = 1;
+            }
+        }
+        int pos = body.indexOf(CRLF, pos1) + CRLF.length();
+        while (pos > 0) {
+            if(body.startsWith(CRLF, pos)) {
+                beginVersionPos = pos + CRLF.length();
+                break;
+            } else {
+                final int next = body.indexOf(CRLF, pos);
+                if (next == -1) {
+                    // throw new BadRequestException("Ill-formatted bMessage, no empty line");
+                    // PTS: Instead of throwing Exception, return MSG
+                    int beginMsg = body.indexOf("BEGIN:MSG");
+                    if (beginMsg == -1) {
+                        throw new BadRequestException("Ill-formatted bMessage, no BEGIN:MSG");
+                    }
+                    int endMsg = body.indexOf("END:MSG", beginMsg);
+                    if (endMsg == -1) {
+                        throw new BadRequestException("Ill-formatted bMessage, no END:MSG");
+                    }
+                    return body.substring(beginMsg + "BEGIN:MSG".length(), endMsg - CRLF.length());
+                } else {
+                    pos = next + CRLF.length();
+                }
+            }
+        }
+        if(beginVersionPos > 0){
+            int endVersionPos;
+            if(rfc822Flag == 0){
+                if(mimeFlag == 0) {
+                    endVersionPos = body.indexOf("END:MSG", beginVersionPos) ;
+                    if (endVersionPos != -1) {
+                        return body.substring(beginVersionPos, (endVersionPos - CRLF.length()));
+                    }
+                    else {
+                        return body.substring(beginVersionPos);
+                    }
+                } else {
+                    endVersionPos = (body.indexOf("--"+boundary+"--", beginVersionPos) - CRLF.length());
+                }
+                try {
+                    return body.substring(beginVersionPos, endVersionPos);
+                } catch (IndexOutOfBoundsException e) {
+                    throw new BadRequestException("Ill-formatted bMessage, no end boundary");
+                }
+            }
+            else if(rfc822Flag == 1){
+                endVersionPos = (body.indexOf("--"+boundary+"--", beginVersionPos));
+                try {
+                    body = body.substring(beginVersionPos, endVersionPos);
+                } catch (IndexOutOfBoundsException e) {
+                    throw new BadRequestException("Ill-formatted bMessage, no end boundary");
+                }
+                int pos2 = body.indexOf(CRLF) + CRLF.length();
+                while (pos2 > 0) {
+                    if(body.startsWith(CRLF, pos2)) {
+                        beginVersionPos1 = pos2 + CRLF.length();
+                        break;
+                    } else {
+                        final int next = body.indexOf(CRLF, pos2);
+                        if (next == -1) {
+                            throw new BadRequestException("Ill-formatted bMessage, no empty line");
+                        } else {
+                            pos2 = next + CRLF.length();
+                        }
+                    }
+                }
+                if(beginVersionPos1 > 0){
+                    return body.substring(beginVersionPos1);
+                }
+            }
+        }
+        return null;
+    }
+
+    private static String fetchContentType(String bmsg, String boundary) {
+        int pos1 = bmsg.indexOf("--"+boundary);
+        int pos = bmsg.indexOf("Content-Type:", pos1);
+        if (pos > 0) {
+
+            int beginVersionPos = pos + (("Content-Type:").length());
+            int endVersionPos = bmsg.indexOf(CRLF, beginVersionPos);
+            return bmsg.substring(beginVersionPos, endVersionPos);
+
+        } else {
+
+            return null;
+
+        }
+    }
+
+    /**
+     * fetchNumEnv
+     *
+     * This method takes as input a String in the bMessage format. It parses the
+     * String and returns the number of envelope headers that it finds as an
+     * Integer
+     *
+     * @param
+     * @return String This method returns the number of Envelope headers as an
+     *         Integer
+     */
+    @SuppressWarnings("unused")
+    private static Integer fetchNumEnv(String bmsg) {
+        int envCnt = 0;
+        int pos = 0;
+        int loopCnt = 0;
+        pos = bmsg.indexOf((CRLF + "BEGIN:BENV"), pos);
+        if (pos < 0) {
+            loopCnt = 4;
+        } else {
+            envCnt = envCnt + 1;
+        }
+        while (loopCnt < 4) {
+            pos = bmsg.indexOf((CRLF + "BEGIN:BENV"), pos + CRLF.length());
+            if (pos < 0) {
+                loopCnt = 4;
+            } else {
+                envCnt = envCnt + 1;
+            }
+        }
+
+        return envCnt;
+    }
+
+    /**
+     * fetchVcardVersion
+     *
+     * This method takes as input a vCard formatted String. It parses the String
+     * and returns the vCard version as a String
+     *
+     * @param
+     * @return String This method returns a vCard version String
+     */
+    private static String fetchVcardVersion(String vCard) {
+        int pos = vCard.indexOf(CRLF + "VERSION:");
+        int beginVersionPos = pos + (("VERSION:").length() + CRLF.length());
+        int endVersionPos = vCard.indexOf(CRLF, beginVersionPos);
+
+        return vCard.substring(beginVersionPos, endVersionPos);
+    }
+
+    /**
+     * fetchVcardName
+     *
+     * This method takes as input a vCard formatted String. It parses the String
+     * and returns the vCard name as a String
+     *
+     * @param
+     * @return String This method returns a vCard name String
+     */
+    @SuppressWarnings("unused")
+    private static String fetchVcardName(String vCard) {
+
+        int pos = vCard.indexOf((CRLF + "N:"));
+        int beginNPos = pos + "N:".length() + CRLF.length();
+        int endNPos = vCard.indexOf(CRLF, beginNPos);
+        return vCard.substring(beginNPos, endNPos);
+    }
+
+    /**
+     * fetchVcardTel
+     *
+     * This method takes as input a vCard formatted String. It parses the String
+     * and returns the vCard phone number as a String
+     *
+     * @param
+     * @return String This method returns a vCard phone number String
+     */
+    private static String fetchVcardTel(String vCard) {
+
+        int pos = vCard.indexOf((CRLF + "TEL:"));
+        int beginVersionPos = pos + (("TEL:").length() + CRLF.length());
+        int endVersionPos = vCard.indexOf(CRLF, beginVersionPos);
+        return vCard.substring(beginVersionPos, endVersionPos);
+    }
+
+    /**
+     * fetchVcardEmail
+     *
+     * This method takes as input a vCard formatted String. It parses the String
+     * and returns the vCard phone number as a String
+     *
+     * @param
+     * @return String This method returns a vCard phone number String
+     */
+    private static String fetchVcardEmailforMms(String vCard) {
+        int pos = vCard.indexOf((CRLF + "EMAIL:"));
+        int beginVersionPos = pos + (("EMAIL:").length() + CRLF.length());
+        int endVersionPos = vCard.indexOf(CRLF, beginVersionPos);
+        return vCard.substring(beginVersionPos, endVersionPos);
+    }
+
+    public static class BadRequestException extends Exception {
+        public BadRequestException(String reason) {
+            super("BadRequestException: " + reason);
+        }
+    }
+
+    public static class RecipientVCard implements VCardInterpreter {
+        String mName = "";
+        String mFormattedName = "";
+        String mTel = "";
+        String mEmail = "";
+        String mVersion = "";
+        String mCurrentProperty = "";
+
+        /* This is a workaround to replace predifined XML escaping entities
+         * with original characters from VCardInterpreter to handle the XML
+         * parsing limitation of MCE (Denso Carkit).
+         * TODO: Above limitaion MUST be handled @MCE itself.
+        */
+        private static String replaceSpecialVcardString(String input) {
+                String str = input;
+                if(str != null){
+                    str = str.toLowerCase().replace("&lt;", "<");
+                    str = str.toLowerCase().replace("&gt;", ">");
+                }
+                return str;
+        }
+
+        public void end() {
+            if (V) Log.v(TAG, "end()");
+        }
+
+        public void endEntry() {
+            if (V) Log.v(TAG, "endEntry()");
+        }
+
+        public void endProperty() {
+            if (V) Log.v(TAG, "endProperty()");
+            mCurrentProperty = "";
+        }
+
+        public void propertyGroup(String group) {
+            if (V) Log.v(TAG, "propertyGroup(" + group + ")");
+        }
+
+        public void propertyName(String name) {
+            if (V) Log.v(TAG, "propertyName(" + name + ")");
+            mCurrentProperty = name;
+        }
+
+        public void propertyParamType(String type) {
+            if (V) Log.v(TAG, "propertyParamType(" + type + ")");
+        }
+
+        public void onPropertyCreated(VCardProperty property) {
+            List <String> values;
+
+            if (V) Log.v(TAG, "onPropertyCreated(" + property + ")");
+
+            values = property.getValueList();
+            if (values == null){
+                Log.e(TAG, "NULL Value List received");
+                return;
+            }
+            /* TODO: replaceSpecialVcardString() is a temporary fix bought to
+             * overcome the limitation of XML parsing with MCE (Denso Carkit)
+             * and later this must to be handled  from MCE itself.
+             */
+            // The first appeared property in a vCard will be used
+            if (PROPERTY_N.equals(property.getName()) && mName.length() == 0) {
+                StringBuilder sb = new StringBuilder();
+
+                sb.append(values.get(0));
+                final int size = values.size();
+                for (int i = 0; i < size; i ++) {
+                    sb.append(", ");
+                    sb.append(values.get(i));
+                }
+                mName = RecipientVCard.replaceSpecialVcardString(sb.toString());
+                if (V) Log.v(TAG, PROPERTY_N + ": " + mName);
+            } else if (PROPERTY_TEL.equals(property.getName()) && mTel.length() == 0) {
+                mTel = RecipientVCard.replaceSpecialVcardString(values.get(0));
+                if (V) Log.v(TAG, PROPERTY_TEL + ": " + mTel);
+            } else if (PROPERTY_EMAIL.equals(property.getName()) && mEmail.length() == 0) {
+                mEmail = RecipientVCard.replaceSpecialVcardString(values.get(0));
+                if (V) Log.v(TAG, PROPERTY_EMAIL + ": " + mEmail);
+            } else if (PROPERTY_FN.equals(property.getName()) && mFormattedName.length() == 0) {
+                mFormattedName = RecipientVCard.replaceSpecialVcardString(values.get(0));
+                if (V) Log.v(TAG, PROPERTY_FN + ": " + mFormattedName);
+            }
+        }
+
+        public void onVCardStarted() {
+            if (V) Log.v(TAG, "onVCardStarted");
+        }
+
+        public void onVCardEnded() {
+            if (V) Log.v(TAG, "onVCardEnded");
+        }
+
+        public void onEntryStarted() {
+            if (V) Log.v(TAG, "onEntryStarted");
+            mName = "";
+            mFormattedName = "";
+            mTel = "";
+            mEmail = "";
+        }
+
+        public void onEntryEnded() {
+            if (V) Log.v(TAG, "onEntryEnded");
+        }
+
+
+        public void propertyParamValue(String value) {
+            if (V) Log.v(TAG, "propertyParamValue(" + value + ")");
+        }
+
+        public void propertyValues(List<String> values) {
+            if (V) Log.v(TAG, "propertyValues(" + values.toString() + "), Property=" + mCurrentProperty);
+            /* TODO: replaceSpecialVcardString() is a temporary fix bought to
+             * overcome the limitation of XML parsing with MCE (Denso Carkit)
+             * and later this must to be handled  from MCE itself.
+             */
+            // The first appeared property in a vCard will be used
+            if (PROPERTY_N.equals(mCurrentProperty) && mName.length() == 0) {
+                StringBuilder sb = new StringBuilder();
+                sb.append(values.get(0));
+                final int size = values.size();
+                for (int i = 0; i < size; i ++) {
+                    sb.append(", ");
+                    sb.append(values.get(i));
+                }
+                mName = RecipientVCard.replaceSpecialVcardString(sb.toString());
+                if (V) Log.v(TAG, PROPERTY_N + ": " + mName);
+            } else if (PROPERTY_TEL.equals(mCurrentProperty) && mTel.length() == 0) {
+                mTel = RecipientVCard.replaceSpecialVcardString(values.get(0));
+                if (V) Log.v(TAG, PROPERTY_TEL + ": " + mTel);
+            } else if (PROPERTY_EMAIL.equals(mCurrentProperty) && mEmail.length() == 0) {
+                 mEmail = RecipientVCard.replaceSpecialVcardString(values.get(0));
+                if (V) Log.v(TAG, PROPERTY_EMAIL + ": " + mEmail);
+            } else if (PROPERTY_FN.equals(mCurrentProperty) && mFormattedName.length() == 0) {
+                mFormattedName = RecipientVCard.replaceSpecialVcardString(values.get(0));
+                if (V) Log.v(TAG, PROPERTY_FN + ": " + mFormattedName);
+            }
+        }
+
+        public void start() {
+            if (V) Log.v(TAG, "start()");
+        }
+
+        public void startEntry() {
+            if (V) Log.v(TAG, "startEntry()");
+            mName = "";
+            mFormattedName = "";
+            mTel = "";
+            mEmail = "";
+        }
+
+        public void startProperty() {
+            if (V) Log.v(TAG, "startProperty()");
+            mCurrentProperty = "";
+        }
+    }
+
+    static RecipientVCard parseVCard(String vCard) throws BadRequestException {
+        if (V) Log.v(TAG, "parseVCard(" + vCard + ")");
+        RecipientVCard recipient = new RecipientVCard();
+
+        if (vCard.length() == 0) {
+            return recipient;
+        }
+
+        try {
+            ByteArrayInputStream is = null;
+            try {
+                byte vCardBytes[] = vCard.getBytes("UTF-8");
+                is = new ByteArrayInputStream(vCardBytes);
+            } catch (UnsupportedEncodingException ex) {
+                Log.w(TAG, "Unable to parse vCard", ex);
+                throw new BadRequestException("Unable to parse vCard");
+            }
+            VCardParser parser = new VCardParser_V21();
+            try {
+                if (V) Log.v(TAG, "try " + VERSION_V21);
+                recipient.mVersion = VERSION_V21;
+                parser.parse(is, recipient);
+            } catch (VCardVersionException e) {
+                is.close();
+                is = new ByteArrayInputStream(vCard.getBytes("UTF-8"));
+                try {
+                    if (V) Log.v(TAG, "try " + VERSION_V30);
+                    recipient.mVersion = VERSION_V30;
+                    parser = new VCardParser_V30();
+                    parser.parse(is, recipient);
+                } catch (VCardVersionException e1) {
+                    throw new VCardException("vCard with unsupported version.");
+                }
+            }
+        } catch (IOException e) {
+            Log.w(TAG, "Unable to parse vCard", e);
+            throw new BadRequestException("Unable to parse vCard");
+        } catch (VCardException e) {
+            Log.w(TAG, "Unable to parse vCard", e);
+            throw new BadRequestException("Unable to parse vCard");
+        }
+
+        return recipient;
+    }
+}
diff --git a/src/org/codeaurora/bluetooth/map/MapUtils/MapUtilsConsts.java b/src/org/codeaurora/bluetooth/map/MapUtils/MapUtilsConsts.java
new file mode 100644
index 0000000..a77b3dd
--- /dev/null
+++ b/src/org/codeaurora/bluetooth/map/MapUtils/MapUtilsConsts.java
@@ -0,0 +1,53 @@
+/*
+ * Copyright (c) 2010-2011, The Linux Foundation. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *        * Redistributions of source code must retain the above copyright
+ *          notice, this list of conditions and the following disclaimer.
+ *        * Redistributions in binary form must reproduce the above copyright
+ *          notice, this list of conditions and the following disclaimer in the
+ *          documentation and/or other materials provided with the distribution.
+ *        * Neither the name of The Linux Foundation nor
+ *          the names of its contributors may be used to endorse or promote
+ *          products derived from this software without specific prior written
+ *          permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NON-INFRINGEMENT ARE DISCLAIMED.    IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
+ * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
+ * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
+ * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package org.codeaurora.bluetooth.map.MapUtils;
+
+/**
+ * List of strings used to construct Bmessages
+ *
+ */
+public final class MapUtilsConsts {
+    public static final String BEGIN_BMSG = "BEGIN:BMSG";
+    public static final String END_BMSG = "END:BMSG";
+    public static final String BEGIN_BENV = "BEGIN:BENV";
+    public static final String END_BBENV = "END:BENV";
+
+    public static final String Telecom = "telecom";
+    public static final String Msg = "msg";
+
+    public static final String Inbox = "inbox";
+    public static final String Outbox = "outbox";
+    public static final String Sent = "sent";
+    public static final String Deleted = "deleted";
+    public static final String Draft = "draft";
+    public static final String Drafts = "drafts";
+    public static final String Undelivered = "undelivered";
+    public static final String Failed = "failed";
+    public static final String Queued = "queued";
+}
diff --git a/src/org/codeaurora/bluetooth/map/MapUtils/MsgListingConsts.java b/src/org/codeaurora/bluetooth/map/MapUtils/MsgListingConsts.java
new file mode 100644
index 0000000..3d81642
--- /dev/null
+++ b/src/org/codeaurora/bluetooth/map/MapUtils/MsgListingConsts.java
@@ -0,0 +1,212 @@
+/*
+ * Copyright (c) 2010-2011, The Linux Foundation. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *        * Redistributions of source code must retain the above copyright
+ *          notice, this list of conditions and the following disclaimer.
+ *        * Redistributions in binary form must reproduce the above copyright
+ *          notice, this list of conditions and the following disclaimer in the
+ *          documentation and/or other materials provided with the distribution.
+ *        * Neither the name of The Linux Foundation nor
+ *          the names of its contributors may be used to endorse or promote
+ *          products derived from this software without specific prior written
+ *          permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NON-INFRINGEMENT ARE DISCLAIMED.    IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
+ * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
+ * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
+ * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package org.codeaurora.bluetooth.map.MapUtils;
+
+public class MsgListingConsts {
+
+    public class MsgInfo {
+        public String dateTime = null;
+
+        public String getDateTime() {
+            return dateTime;
+        }
+
+        public void setDateTime(String dateTime) {
+            this.dateTime = dateTime;
+        }
+
+    }
+    public MsgInfo msgInfo = new MsgInfo();
+
+    public long msg_handle = 0;
+    public String subject = null;
+    public boolean  sendSubject = false;
+    public String datetime = null;
+    public String sender_name = null;
+    public String sender_addressing = null;
+    public String recepient_name = null;
+    public boolean sendRecipient_addressing = false;
+    public String recepient_addressing = null;
+    public String type = null;
+    public int size = -1;
+    public String reception_status = null;
+    public int attachment_size = -1;
+    public String contains_text = null;
+    public String priority = null;
+    public String read = null;
+    public String sent = null;
+    public String msg_protected = null;
+    public String replyto_addressing = null;
+
+    public long getMsg_handle() {
+        return msg_handle;
+    }
+
+    public void setMsg_handle(long msg_handle) {
+        this.msg_handle = msg_handle;
+    }
+
+    public String getSubject() {
+        return subject;
+    }
+
+    public void setSubject(String subject) {
+        this.subject = subject;
+    }
+
+    public void setSendSubject(boolean flag) {
+        this.sendSubject = flag;
+    }
+
+    public String getDatetime() {
+        return datetime;
+    }
+
+    public void setDatetime(String datetime) {
+        this.datetime = datetime;
+    }
+
+    public String getSender_name() {
+        return sender_name;
+    }
+
+    public void setSender_name(String sender_name) {
+        this.sender_name = sender_name;
+    }
+
+    public String getSender_addressing() {
+        return sender_addressing;
+    }
+
+    public void setSender_addressing(String sender_addressing) {
+        this.sender_addressing = sender_addressing;
+    }
+
+    public String getRecepient_name() {
+        return recepient_name;
+    }
+
+    public void setRecepient_name(String recepient_name) {
+        this.recepient_name = recepient_name;
+    }
+
+    public String getRecepient_addressing() {
+        return recepient_addressing;
+    }
+
+    public void setSendRecipient_addressing(boolean flag) {
+        this.sendRecipient_addressing = flag;
+    }
+
+
+    public void setRecepient_addressing(String recepient_addressing) {
+        this.recepient_addressing = recepient_addressing;
+    }
+
+    public String getType() {
+        return type;
+    }
+
+    public void setType(String type) {
+        this.type = type;
+    }
+
+    public int getSize() {
+        return size;
+    }
+
+    public void setSize(int size) {
+        this.size = size;
+    }
+
+    public String getReception_status() {
+        return reception_status;
+    }
+
+    public void setReception_status(String reception_status) {
+        this.reception_status = reception_status;
+    }
+
+    public int getAttachment_size() {
+        return attachment_size;
+    }
+
+    public void setAttachment_size(int attachment_size) {
+        this.attachment_size = attachment_size;
+    }
+
+    public String getContains_text() {
+        return contains_text;
+    }
+
+    public void setContains_text(String contains_text) {
+        this.contains_text = contains_text;
+    }
+
+    public String getPriority() {
+        return priority;
+    }
+
+    public void setPriority(String priority) {
+        this.priority = priority;
+    }
+
+    public String getRead() {
+        return read;
+    }
+
+    public void setRead(String read) {
+        this.read = read;
+    }
+
+    public void setSent(String sent) {
+        this.sent = sent;
+    }
+
+    public String getSent() {
+        return sent;
+    }
+
+    public String getMsg_protected() {
+        return msg_protected;
+    }
+
+    public void setMsg_protected(String msg_protected) {
+        this.msg_protected = msg_protected;
+    }
+
+    public String getReplyTo_addressing() {
+        return sender_addressing;
+    }
+
+    public void setReplyTo_addressing(String replyto_addressing) {
+        this.replyto_addressing = replyto_addressing;
+    }
+
+}
diff --git a/src/org/codeaurora/bluetooth/map/MapUtils/SmsMmsUtils.java b/src/org/codeaurora/bluetooth/map/MapUtils/SmsMmsUtils.java
new file mode 100644
index 0000000..73964c7
--- /dev/null
+++ b/src/org/codeaurora/bluetooth/map/MapUtils/SmsMmsUtils.java
@@ -0,0 +1,237 @@
+/*
+ * Copyright (c) 2010-2011, The Linux Foundation. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *        * Redistributions of source code must retain the above copyright
+ *          notice, this list of conditions and the following disclaimer.
+ *        * Redistributions in binary form must reproduce the above copyright
+ *          notice, this list of conditions and the following disclaimer in the
+ *          documentation and/or other materials provided with the distribution.
+ *        * Neither the name of The Linux Foundation nor
+ *          the names of its contributors may be used to endorse or promote
+ *          products derived from this software without specific prior written
+ *          permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NON-INFRINGEMENT ARE DISCLAIMED.    IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
+ * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
+ * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
+ * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package org.codeaurora.bluetooth.map.MapUtils;
+
+import android.content.ContentResolver;
+import android.content.Context;
+import android.database.Cursor;
+import android.net.Uri;
+import android.text.format.Time;
+import android.util.Log;
+import android.util.TimeFormatException;
+
+import org.codeaurora.bluetooth.map.BluetoothMasAppParams;
+
+import java.util.ArrayList;
+
+public class SmsMmsUtils {
+    public static final String TAG = "SmsMmsUtils";
+    public static final int BIT_SUBJECT = 0x1;
+    public static final int BIT_DATETIME = 0x2;
+    public static final int BIT_SENDER_NAME = 0x4;
+    public static final int BIT_SENDER_ADDRESSING = 0x8;
+
+    public static final int BIT_RECIPIENT_NAME = 0x10;
+    public static final int BIT_RECIPIENT_ADDRESSING = 0x20;
+    public static final int BIT_TYPE = 0x40;
+    public static final int BIT_SIZE = 0x80;
+
+    public static final int BIT_RECEPTION_STATUS = 0x100;
+    public static final int BIT_TEXT = 0x200;
+    public static final int BIT_ATTACHMENT_SIZE = 0x400;
+    public static final int BIT_PRIORITY = 0x800;
+
+    public static final int BIT_READ = 0x1000;
+    public static final int BIT_SENT = 0x2000;
+    public static final int BIT_PROTECTED = 0x4000;
+    public static final int BIT_REPLYTO_ADDRESSING = 0x8000;
+
+    public static final String INBOX = "inbox";
+    public static final String OUTBOX = "outbox";
+    public static final String SENT = "sent";
+    public static final String DELETED = "deleted";
+    public static final String DRAFT = "draft";
+    public static final String DRAFTS = "drafts";
+    public static final String UNDELIVERED = "undelivered";
+    public static final String FAILED = "failed";
+    public static final String QUEUED = "queued";
+
+    public static final int DELETED_THREAD_ID = -1;
+
+    static final int PHONELOOKUP_ID_COLUMN_INDEX = 0;
+    static final int PHONELOOKUP_LOOKUP_KEY_COLUMN_INDEX = 1;
+    static final int PHONELOOKUP_DISPLAY_NAME_COLUMN_INDEX = 2;
+
+    static final int EMAIL_DATA_COLUMN_INDEX = 0;
+
+    public static class VcardContent {
+        public String name = "";
+        public String tel = "";
+        public String email = "";
+    }
+
+    public static final ArrayList<String> FORLDER_LIST_SMS_MMS;
+    public static final ArrayList<String> FORLDER_LIST_SMS_MMS_MNS;
+
+    static {
+        FORLDER_LIST_SMS_MMS = new ArrayList<String>();
+        FORLDER_LIST_SMS_MMS.add(INBOX);
+        FORLDER_LIST_SMS_MMS.add(OUTBOX);
+        FORLDER_LIST_SMS_MMS.add(SENT);
+        FORLDER_LIST_SMS_MMS.add(DELETED);
+        FORLDER_LIST_SMS_MMS.add(DRAFT);
+
+        FORLDER_LIST_SMS_MMS_MNS = new ArrayList<String>();
+        FORLDER_LIST_SMS_MMS_MNS.add(INBOX);
+        FORLDER_LIST_SMS_MMS_MNS.add(OUTBOX);
+        FORLDER_LIST_SMS_MMS_MNS.add(SENT);
+        FORLDER_LIST_SMS_MMS_MNS.add(DRAFT);
+        FORLDER_LIST_SMS_MMS_MNS.add(FAILED);
+        FORLDER_LIST_SMS_MMS_MNS.add(QUEUED);
+    }
+
+    public static int getFolderTypeMms(String folder) {
+        int folderType = -5 ;
+
+        if (INBOX.equalsIgnoreCase(folder)) {
+            folderType = 1;
+        }
+        else if (OUTBOX.equalsIgnoreCase(folder)) {
+            folderType = 4;
+        }
+        else if (SENT.equalsIgnoreCase(folder)) {
+            folderType = 2;
+        }
+        else if (DRAFT.equalsIgnoreCase(folder) || DRAFTS.equalsIgnoreCase(folder)) {
+            folderType = 3;
+        }
+        else if (DELETED.equalsIgnoreCase(folder)) {
+            folderType = -1;
+        }
+        return folderType;
+    }
+
+    public static String getWhereIsQueryForType(String folder) {
+        String query = null;
+
+        if (INBOX.equalsIgnoreCase(folder)) {
+            query = "type = 1 AND thread_id <> " + DELETED_THREAD_ID;
+        }
+        else if (OUTBOX.equalsIgnoreCase(folder)) {
+            query = "(type = 4 OR type = 5 OR type = 6) AND thread_id <> " + DELETED_THREAD_ID;
+        }
+        else if (SENT.equalsIgnoreCase(folder)) {
+            query = "type = 2 AND thread_id <> " + DELETED_THREAD_ID;
+        }
+        else if (DRAFT.equalsIgnoreCase(folder)) {
+            query = "type = 3 AND thread_id <> " + DELETED_THREAD_ID;
+        }
+        else if (DELETED.equalsIgnoreCase(folder)) {
+            query = "thread_id = " + DELETED_THREAD_ID;
+        }
+        else{
+            query = "type = -1";
+        }
+        return query;
+    }
+
+    public static String getConditionStringSms(String folderName, BluetoothMasAppParams appParams) {
+        String whereClause = getWhereIsQueryForType(folderName);
+
+         /* Filter readstatus: 0 no filtering, 0x01 get unread, 0x10 get read */
+        if (appParams.FilterReadStatus != 0) {
+             if ((appParams.FilterReadStatus & 0x1) != 0) {
+                 if (whereClause.length() != 0) {
+                     whereClause += " AND ";
+                 }
+                 whereClause += " read=0 ";
+             }
+             if ((appParams.FilterReadStatus & 0x02) != 0) {
+                 if (whereClause.length() != 0) {
+                     whereClause += " AND ";
+                 }
+                 whereClause += " read=1 ";
+             }
+         }
+         // TODO Filter priority?
+
+         /* Filter Period Begin */
+         if ((appParams.FilterPeriodBegin != null)
+                 && (appParams.FilterPeriodBegin.length() > 0)) {
+             Time time = new Time();
+             try {
+                 time.parse(appParams.FilterPeriodBegin.trim());
+                 if (whereClause.length() != 0) {
+                     whereClause += " AND ";
+                 }
+                 whereClause += "date >= " + time.toMillis(false);
+             } catch (TimeFormatException e) {
+                 Log.d(TAG, "Bad formatted FilterPeriodBegin "
+                         + appParams.FilterPeriodBegin);
+             }
+         }
+
+         /* Filter Period End */
+         if ((appParams.FilterPeriodEnd != null)
+                 && (appParams.FilterPeriodEnd.length() > 0 )) {
+             Time time = new Time();
+             try {
+                 time.parse(appParams.FilterPeriodEnd.trim());
+                 if (whereClause.length() != 0) {
+                     whereClause += " AND ";
+                 }
+                 whereClause += "date < " + time.toMillis(false);
+             } catch (TimeFormatException e) {
+                 Log.d(TAG, "Bad formatted FilterPeriodEnd "
+                         + appParams.FilterPeriodEnd);
+             }
+         }
+         return whereClause;
+    }
+
+    public static final Uri SMS_URI = Uri.parse("content://sms");
+    public static final Uri MMS_URI = Uri.parse("content://mms");
+    public static final String[] THREAD_ID_COLUMN = new String[]{"thread_id"};
+
+    /**
+     * Obtain the number of MMS messages
+     */
+    public static int getNumMmsMsgs(Context context, String name) {
+        int msgCount = 0;
+
+        if (DELETED.equalsIgnoreCase(name)) {
+            Uri uri = Uri.parse("content://mms/");
+            ContentResolver cr = context.getContentResolver();
+            Cursor cursor = cr.query(uri, null, "thread_id = " + DELETED_THREAD_ID, null, null);
+            if(cursor != null){
+                msgCount = cursor.getCount();
+                cursor.close();
+            }
+        } else {
+            Uri uri = Uri.parse("content://mms/" + name);
+            ContentResolver cr = context.getContentResolver();
+            Cursor cursor = cr.query(uri, null, "thread_id <> " + DELETED_THREAD_ID, null, null);
+            if(cursor != null){
+                msgCount = cursor.getCount();
+                cursor.close();
+            }
+        }
+        return msgCount;
+    }
+}
diff --git a/src/org/codeaurora/bluetooth/map/MapUtils/SortMsgListByDate.java b/src/org/codeaurora/bluetooth/map/MapUtils/SortMsgListByDate.java
new file mode 100644
index 0000000..a230895
--- /dev/null
+++ b/src/org/codeaurora/bluetooth/map/MapUtils/SortMsgListByDate.java
@@ -0,0 +1,41 @@
+/*
+ * Copyright (c) 2010-2011, The Linux Foundation. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *        * Redistributions of source code must retain the above copyright
+ *          notice, this list of conditions and the following disclaimer.
+ *        * Redistributions in binary form must reproduce the above copyright
+ *          notice, this list of conditions and the following disclaimer in the
+ *          documentation and/or other materials provided with the distribution.
+ *        * Neither the name of The Linux Foundation nor
+ *          the names of its contributors may be used to endorse or promote
+ *          products derived from this software without specific prior written
+ *          permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NON-INFRINGEMENT ARE DISCLAIMED.    IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
+ * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
+ * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
+ * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package org.codeaurora.bluetooth.map.MapUtils;
+
+import java.util.Comparator;
+import org.codeaurora.bluetooth.map.MapUtils.MsgListingConsts;
+
+public class SortMsgListByDate implements Comparator<MsgListingConsts> {
+
+    public int compare(MsgListingConsts object1, MsgListingConsts object2) {
+
+        return object2.msgInfo.getDateTime().compareTo(object1.msgInfo.getDateTime());
+    }
+
+}
diff --git a/src/org/codeaurora/bluetooth/map/MapUtils/SqlHelper.java b/src/org/codeaurora/bluetooth/map/MapUtils/SqlHelper.java
new file mode 100644
index 0000000..e4e87c6
--- /dev/null
+++ b/src/org/codeaurora/bluetooth/map/MapUtils/SqlHelper.java
@@ -0,0 +1,147 @@
+/*
+ * Copyright (c) 2011, The Linux Foundation. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *        * Redistributions of source code must retain the above copyright
+ *          notice, this list of conditions and the following disclaimer.
+ *        * Redistributions in binary form must reproduce the above copyright
+ *          notice, this list of conditions and the following disclaimer in the
+ *          documentation and/or other materials provided with the distribution.
+ *        * Neither the name of The Linux Foundation nor
+ *          the names of its contributors may be used to endorse or promote
+ *          products derived from this software without specific prior written
+ *          permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NON-INFRINGEMENT ARE DISCLAIMED.    IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
+ * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
+ * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
+ * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package org.codeaurora.bluetooth.map.MapUtils;
+
+import android.content.Context;
+import android.database.Cursor;
+import android.net.Uri;
+import android.util.Log;
+
+import org.codeaurora.bluetooth.map.BluetoothMasService;
+
+import java.util.ArrayList;
+import java.util.List;
+
+public class SqlHelper {
+    public static final String TAG = "SqlHelper";
+    public static final boolean V = BluetoothMasService.VERBOSE;
+    private static final String[] COUNT_COLUMNS = new String[]{"count(*)"};
+
+    /**
+     * Generic count method that can be used for any ContentProvider
+     * @param resolver the calling Context
+     * @param uri the Uri for the provider query
+     * @param selection as with a query call
+     * @param selectionArgs as with a query call
+     * @return the number of items matching the query (or zero)
+     */
+    public static int count(Context context, Uri uri, String selection, String[] selectionArgs) {
+        if (V) Log.v(TAG, "count(" + uri + ", " + selection + ", " + selectionArgs + ")");
+        int cnt = 0;
+        Cursor cursor = context.getContentResolver().query(uri,
+                COUNT_COLUMNS, selection, selectionArgs, null);
+        if (cursor != null) {
+            if (cursor.moveToFirst()) {
+                cnt = cursor.getInt(0);
+                if (V) Log.v(TAG, "count = " + cnt);
+            }
+            cursor.close();
+        }
+        return cnt;
+    }
+
+    /**
+     * Generic method to retrieve the first value for the column
+     * @param resolver the calling Context
+     * @param uri the Uri for the provider query
+     * @param columnName the column name to be retrieved
+     * @param selection as with a query call
+     * @param selectionArgs as with a query call
+     * @return the value first of that column
+     */
+    public static String getFirstValueForColumn(Context context, Uri uri,
+            String columnName, String selection, String[] selectionArgs) {
+        if (V) Log.v(TAG, "getFirstValueForColumn(" + uri + ", " + columnName +
+                ", " + selection + ", " + selectionArgs + ")");
+        String value = "";
+        Cursor cr = context.getContentResolver().query(uri, null, selection, selectionArgs, null);
+        if (cr != null) {
+            if (cr.moveToFirst()) {
+                value = cr.getString(cr.getColumnIndex(columnName));
+                if (V) Log.v(TAG, "value = " + value);
+            }
+            cr.close();
+        }
+        return value;
+    }
+
+    /**
+     * Generic method to retrieve the first value for the column
+     * @param resolver the calling Context
+     * @param uri the Uri for the provider query
+     * @param columnName the column name to be retrieved
+     * @param selection as with a query call
+     * @param selectionArgs as with a query call
+     * @return the value first of that column
+     */
+    public static int getFirstIntForColumn(Context context, Uri uri,
+            String columnName, String selection, String[] selectionArgs) {
+        if (V) Log.v(TAG, "getFirstIntForColumn(" + uri + ", " + columnName +
+                ", " + selection + ", " + selectionArgs + ")");
+        int value = -1;
+        Cursor cr = context.getContentResolver().query(uri, null, selection, selectionArgs, null);
+        if (cr != null) {
+            if (cr.moveToFirst()) {
+                value = cr.getInt(cr.getColumnIndex(columnName));
+                if (V) Log.v(TAG, "value = " + value);
+            }
+            cr.close();
+        }
+        return value;
+    }
+
+    /**
+     * Generic method to retrieve the list for the column
+     * @param resolver the calling Context's ContentResolver
+     * @param uri the Uri for the provider query
+     * @param columnName the column name to be retrieved
+     * @param selection as with a query call
+     * @param selectionArgs as with a query call
+     * @return the value first of that column
+     */
+    public static List<String> getListForColumn(Context context, Uri uri,
+            String columnName, String selection, String[] selectionArgs) {
+        ArrayList<String> list = new ArrayList<String>();
+        if (V) Log.v(TAG, "getListForColumn(" + uri + ", " + columnName + ", " +
+                selection + ", " + selectionArgs + ")");
+        Cursor cr = context.getContentResolver().query(uri, null, selection, selectionArgs, null);
+        if (cr != null) {
+            if (cr.moveToFirst()) {
+                final int columnIndex = cr.getColumnIndex(columnName);
+                do {
+                    final String value = cr.getString(columnIndex);
+                    list.add(value);
+                    if (V) Log.v(TAG, "adding: " + value);
+                } while (cr.moveToNext());
+            }
+            cr.close();
+        }
+        return list;
+    }
+}