Merge "PBAP: Add change to return properly on PullvCardEntry fail"
diff --git a/Android.mk b/Android.mk
index bbd17ec..518661b 100644
--- a/Android.mk
+++ b/Android.mk
@@ -2,10 +2,14 @@
 include $(CLEAR_VARS)
 
 LOCAL_MODULE_TAGS := optional
-src_dirs:= src/org/codeaurora/bluetooth/btcservice \
-           src/org/codeaurora/bluetooth/map \
+src_dirs:= src/org/codeaurora/bluetooth/map \
            src/org/codeaurora/bluetooth/ftp \
-           src/org/codeaurora/bluetooth/sap
+           src/org/codeaurora/bluetooth/sap \
+           src/org/codeaurora/bluetooth/dun
+
+ifeq ($(BOARD_HAS_QCA_BT_AR3002),true)
+src_dirs += src/org/codeaurora/bluetooth/btcservice
+endif
 
 LOCAL_SRC_FILES := \
         $(call all-java-files-under, $(src_dirs))
diff --git a/AndroidManifest.xml b/AndroidManifest.xml
index 2452b54..ff8189c 100644
--- a/AndroidManifest.xml
+++ b/AndroidManifest.xml
@@ -48,13 +48,15 @@
     <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>
+    <application android:process="com.android.bluetooth">
         <uses-library android:name="javax.obex" />
         <service
+            android:enabled="@bool/profile_supported_btc"
             android:name = ".btcservice.BTCService">
         </service>
         <receiver
             android:exported="true"
+            android:enabled="@bool/profile_supported_btc"
             android:name=".btcservice.BTCEventProvider">
             <intent-filter>
                 <action android:name="android.bluetooth.adapter.action.STATE_CHANGED" />
@@ -144,5 +146,32 @@
                 <action android:name="android.bluetooth.device.action.ACL_DISCONNECTED"/>
             </intent-filter>
         </receiver>
+        <activity android:name=".dun.BluetoothDunPermissionActivity"
+            android:process="@string/process"
+            android:excludeFromRecents="true"
+            android:enabled="@bool/profile_supported_dun"
+            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:name=".dun.BluetoothDunService"
+            android:enabled="@bool/profile_supported_dun" >
+            <intent-filter>
+                <action android:name="android.bluetooth.IBluetoothDun" />
+            </intent-filter>
+       </service>
+        <receiver
+            android:process="@string/process"
+            android:enabled="@bool/profile_supported_dun"
+            android:name=".dun.BluetoothDunReceiver">
+            <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/bttestapp/src/org/codeaurora/bluetooth/bttestapp/MapTestActivity.java b/bttestapp/src/org/codeaurora/bluetooth/bttestapp/MapTestActivity.java
index 1aa5165..c35b0be 100644
--- a/bttestapp/src/org/codeaurora/bluetooth/bttestapp/MapTestActivity.java
+++ b/bttestapp/src/org/codeaurora/bluetooth/bttestapp/MapTestActivity.java
@@ -1032,13 +1032,37 @@
 
     public void onClickGetMessagesListing(View view) {
         String folder = mSpinnerFolders.getSelectedItem().toString();
-        int maxListCount = Integer.parseInt(
-                mEditTextMaxListCountMessages.getText().toString());
-        int listStartOffset = Integer.parseInt(
-                mEditTextListStartOffsetMessages.getText().toString());
-        int subjectLength = Integer.parseInt(
-                mEditTextSubjectLength.getText().toString());
 
+        int maxListCount = 0;
+        int listStartOffset = 0;
+        int subjectLength = 0;
+
+        try {
+            maxListCount = Integer.parseInt(
+                    mEditTextMaxListCountMessages.getText().toString());
+        } catch (NumberFormatException e) {
+            Toast.makeText(this,
+                   "Incorrect maxListCount. Some defaults will be used",
+                         Toast.LENGTH_LONG).show();
+        }
+
+        try {
+            listStartOffset = Integer.parseInt(
+                    mEditTextListStartOffsetMessages.getText().toString());
+        } catch (NumberFormatException e) {
+            Toast.makeText(this,
+                   "Incorrect listStartOffset. Some defaults will be used",
+                         Toast.LENGTH_LONG).show();
+        }
+
+        try {
+            subjectLength = Integer.parseInt(
+                    mEditTextSubjectLength.getText().toString());
+        } catch (NumberFormatException e) {
+            Toast.makeText(this,
+                   "Incorrect subject lenght. Some defaults will be used",
+                         Toast.LENGTH_LONG).show();
+        }
         if (folder.equals(".")) {
             folder = "";
         }
diff --git a/bttestapp/src/org/codeaurora/bluetooth/bttestapp/ServicesFragment.java b/bttestapp/src/org/codeaurora/bluetooth/bttestapp/ServicesFragment.java
index 2776ebd..4d3feb9 100644
--- a/bttestapp/src/org/codeaurora/bluetooth/bttestapp/ServicesFragment.java
+++ b/bttestapp/src/org/codeaurora/bluetooth/bttestapp/ServicesFragment.java
@@ -503,7 +503,12 @@
                 break;
 
             case HFP:
-                intent = new Intent(getActivity(), HfpTestActivity.class);
+                if (mActivity.mProfileService.getHfpClient() != null)
+                    intent = new Intent(getActivity(), HfpTestActivity.class);
+                else {
+                    Log.v(TAG, "HfpClient service is null");
+                    return;
+                }
                 break;
 
             case MAP:
diff --git a/res/values/config.xml b/res/values/config.xml
index ce26ba4..6c6a875 100644
--- a/res/values/config.xml
+++ b/res/values/config.xml
@@ -16,4 +16,6 @@
     <bool name="profile_supported_ftp">true</bool>
     <bool name="profile_supported_map">true</bool>
     <bool name="profile_supported_sap">true</bool>
+    <bool name="profile_supported_dun">true</bool>
+    <bool name="profile_supported_btc">false</bool>
 </resources>
diff --git a/res/values/strings_dun.xml b/res/values/strings_dun.xml
new file mode 100644
index 0000000..f491270
--- /dev/null
+++ b/res/values/strings_dun.xml
@@ -0,0 +1,43 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+/*
+ * 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.
+ */
+-->
+<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="dun_notif_ticker">Bluetooth DUN request</string>
+    <!-- Notification title when a Bluetooth device wants to pair with us -->
+    <string name="dun_notif_title">Session Key Request for DUN</string>
+    <!-- Notification message when a Bluetooth device wants to pair with us -->
+    <string name="dun_notif_message">"Touch to connect to \u0022<xliff:g id="device_name">%1$s</xliff:g>\u0022."</string>
+    <string name="dun_acceptance_timeout_message">There was time out to accept connection with %1$s</string>
+    <!-- Activity label of BluetoothDunPermissionActivity, also used as Strings in the permission dialog [CHAR LIMIT=none] -->
+    <string name="bluetooth_dun_request">"DUN request"</string>
+    <!-- Bluetooth DUN permission Alert Activity text [CHAR LIMIT=none] -->
+    <string name="bluetooth_dun_acceptance_dialog_text">%1$s would like to use DUN. Give access to %2$s?</string>
+
+</resources>
diff --git a/src/org/codeaurora/bluetooth/dun/BluetoothDunPermissionActivity.java b/src/org/codeaurora/bluetooth/dun/BluetoothDunPermissionActivity.java
new file mode 100644
index 0000000..74ffd94
--- /dev/null
+++ b/src/org/codeaurora/bluetooth/dun/BluetoothDunPermissionActivity.java
@@ -0,0 +1,309 @@
+/*
+ * Copyright (c) 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.dun;
+
+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;
+
+/**
+ * DunPermissionActivity shows dialogues for accepting incoming DUN request
+ * with a remote Bluetooth device.
+  */
+
+public class BluetoothDunPermissionActivity extends AlertActivity implements
+        DialogInterface.OnClickListener, Preference.OnPreferenceChangeListener, TextWatcher {
+    private static final String TAG = "BluetoothDunPermissionActivity";
+
+    private static final boolean V = BluetoothDunService.VERBOSE;
+
+    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 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 (!BluetoothDunService.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 (BluetoothDunService.DUN_ACCESS_REQUEST_ACTION.equals(action)) {
+            showDunDialog(DIALOG_YES_NO_CONNECT);
+            mCurrentDialog = DIALOG_YES_NO_CONNECT;
+        }
+        else {
+            Log.e(TAG, "Error: this activity may be started only with intent "
+                    + "DUN_ACCESS_REQUEST");
+            finish();
+        }
+        registerReceiver(mReceiver, new IntentFilter(
+                BluetoothDunService.USER_CONFIRM_TIMEOUT_ACTION));
+    }
+
+    private void showDunDialog(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_dun_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(BluetoothDunService.EXTRA_BLUETOOTH_DEVICE)) {
+            BluetoothDevice device = intent.getParcelableExtra(BluetoothDunService.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_dun_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(BluetoothDunService.DUN_ACCESS_ALLOWED_ACTION,
+                        BluetoothDunService.DUN_EXTRA_ALWAYS_ALLOWED, mAlwaysAllowedValue);
+            }
+        }
+        mTimeout = false;
+        finish();
+    }
+
+    private void onNegative() {
+        if (mCurrentDialog == DIALOG_YES_NO_CONNECT) {
+            sendIntentToReceiver(BluetoothDunService.DUN_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(BluetoothDunService.THIS_PACKAGE_NAME, BluetoothDunReceiver.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(BluetoothDunService.THIS_PACKAGE_NAME, BluetoothDunReceiver.class
+                .getName());
+        if (extraName != null) {
+            intent.putExtra(extraName, extraValue);
+        }
+        Intent i = getIntent();
+        if (i.hasExtra(BluetoothDunService.EXTRA_BLUETOOTH_DEVICE)) {
+            intent.putExtra(BluetoothDunService.EXTRA_BLUETOOTH_DEVICE, i.getParcelableExtra(BluetoothDunService.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.dun_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/dun/BluetoothDunReceiver.java b/src/org/codeaurora/bluetooth/dun/BluetoothDunReceiver.java
new file mode 100644
index 0000000..60f3dc7
--- /dev/null
+++ b/src/org/codeaurora/bluetooth/dun/BluetoothDunReceiver.java
@@ -0,0 +1,82 @@
+/*
+ * Copyright (c) 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.dun;
+
+import android.bluetooth.BluetoothAdapter;
+import android.content.BroadcastReceiver;
+import android.content.Context;
+import android.content.Intent;
+import android.util.Log;
+import android.os.SystemProperties;
+
+public class BluetoothDunReceiver extends BroadcastReceiver {
+
+    private static final String TAG = "BluetoothDunReceiver";
+
+    private static final boolean V = BluetoothDunService.VERBOSE;
+
+    @Override
+    public void onReceive(Context context, Intent intent) {
+
+        if(SystemProperties.getBoolean("ro.bluetooth.dun", false) == false) {
+            return;
+        }
+
+        if (V) Log.v(TAG, "DunReceiver onReceive ");
+
+        Intent in = new Intent();
+        in.putExtras(intent);
+        in.setClass(context, BluetoothDunService.class);
+        String action = intent.getAction();
+        in.putExtra("action", action);
+        if (V) Log.v(TAG,"action = " + action);
+
+        boolean startService = true;
+
+        if (action.equals(BluetoothAdapter.ACTION_STATE_CHANGED)) {
+            int state = intent.getIntExtra(BluetoothAdapter.EXTRA_STATE, BluetoothAdapter.ERROR);
+            in.putExtra(BluetoothAdapter.EXTRA_STATE, state);
+            if (V) Log.v(TAG," Bluetooth Adapter state = " + state);
+            if ((state == BluetoothAdapter.STATE_TURNING_ON)
+                    || (state == BluetoothAdapter.STATE_OFF)) {
+                startService = false;
+            }
+        } else {
+            // Don't forward intent unless device has bluetooth and bluetooth is enabled.
+            BluetoothAdapter adapter = BluetoothAdapter.getDefaultAdapter();
+            if (adapter == null || !adapter.isEnabled()) {
+                startService = false;
+            }
+        }
+        if (startService) {
+            if (V) Log.v(TAG,"Calling DUN service start service with action = " + in.getAction());
+            context.startService(in);
+        }
+    }
+}
diff --git a/src/org/codeaurora/bluetooth/dun/BluetoothDunService.java b/src/org/codeaurora/bluetooth/dun/BluetoothDunService.java
new file mode 100644
index 0000000..88ea756
--- /dev/null
+++ b/src/org/codeaurora/bluetooth/dun/BluetoothDunService.java
@@ -0,0 +1,1436 @@
+/*
+ * Copyright (c) 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.dun;
+
+import android.app.Notification;
+import android.app.NotificationManager;
+import android.app.PendingIntent;
+import android.bluetooth.BluetoothDevice;
+import android.bluetooth.BluetoothDun;
+import android.bluetooth.BluetoothProfile;
+import android.bluetooth.IBluetoothDun;
+import android.content.Context;
+import android.content.Intent;
+import android.os.Handler;
+import android.os.Message;
+import android.util.Log;
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.List;
+import android.bluetooth.BluetoothAdapter;
+import android.os.SystemProperties;
+import android.app.Service;
+import android.bluetooth.IBluetooth;
+import android.bluetooth.BluetoothServerSocket;
+import android.bluetooth.BluetoothSocket;
+import android.net.LocalSocket;
+import android.net.LocalSocketAddress;
+import java.io.OutputStream;
+import java.io.InputStream;
+import android.os.IBinder;
+import java.nio.ByteBuffer;
+import java.io.IOException;
+import android.content.ServiceConnection;
+import android.os.ParcelUuid;
+import java.util.UUID;
+import android.text.TextUtils;
+import android.content.ComponentName;
+import android.os.RemoteException;
+import org.codeaurora.bluetooth.R;
+
+/**
+ * Provides Bluetooth Dun profile, as a service in the BluetoothExt APK.
+ * @hide
+ */
+
+public class BluetoothDunService extends Service {
+
+    private static final String TAG                             = "BluetoothDunService";
+    private static final boolean DBG                            = false;
+    public  static final boolean VERBOSE                        = false;
+
+    private final static String DUN_SERVER                      = "qcom.dun.server";
+
+    private static final int MESSAGE_START_LISTENER             = 0x01;
+    private static final int MESSAGE_DUN_USER_TIMEOUT           = 0x02;
+
+    private static final int USER_CONFIRM_TIMEOUT_VALUE         = 30000; // 30 seconds
+
+    private static final int MON_THREAD_SLEEP_INTERVAL          = 200; // 200 mseconds
+
+    /**
+     * IPC message types which can be exchanged between
+     * Dun server process and BluetoothDunService
+     */
+    private static final byte DUN_IPC_MSG_DUN_REQUEST           = 0x00;
+    private static final byte DUN_IPC_MSG_DUN_RESPONSE          = 0x01;
+    private static final byte DUN_IPC_MSG_CTRL_REQUEST          = 0x02;
+    private static final byte DUN_IPC_MSG_CTRL_RESPONSE         = 0x03;
+    private static final byte DUN_IPC_MSG_MDM_STATUS            = 0x04;
+
+    /**
+     * Control message types between Dun server and
+     * BluetoothDunService
+     */
+    private static final byte DUN_CRTL_MSG_DISCONNECT_REQ       = 0x00;
+    private static final byte DUN_CRTL_MSG_CONNECTED_RESP       = 0x01;
+    private static final byte DUN_CRTL_MSG_DISCONNECTED_RESP    = 0x02;
+
+    /**
+     * Offsets of message data with in the IPC message
+     */
+    private static final byte DUN_IPC_MSG_OFF_MSG_TYPE          = 0x00;
+    private static final byte DUN_IPC_MSG_OFF_MSG_LEN           = 0x01;
+    private static final byte DUN_IPC_MSG_OFF_MSG               = 0x03;
+
+    /**
+     * Server supported DUN Profile's maximum message size
+     */
+    private static final int DUN_MAX_MSG_LEN                    = 32764;
+
+    /**
+     * IPC message's header size
+     */
+    private static final int DUN_IPC_HEADER_SIZE                = 0x03;
+
+    /**
+     * IPC message's maximum message size (Dun server <---> DUN service)
+     */
+    private static final int DUN_MAX_IPC_MSG_LEN                = DUN_MAX_MSG_LEN +
+                                                                  DUN_IPC_HEADER_SIZE;
+    /**
+     * IPC message's control message size
+     */
+    private static final byte DUN_IPC_CTRL_MSG_SIZE             = 0x01;
+
+    /**
+     * IPC message's modem status message size
+     */
+    private static final byte DUN_IPC_MDM_STATUS_MSG_SIZE       = 0x01;
+
+    /**
+     * BT socket options
+     */
+    private static final int BTSOCK_OPT_GET_MODEM_BITS          = 0x01;
+    private static final int BTSOCK_OPT_SET_MODEM_BITS          = 0x02;
+    private static final int BTSOCK_OPT_CLR_MODEM_BITS          = 0x03;
+
+    private static IBluetooth mAdapterService                   = null;
+
+    private static BluetoothDevice mRemoteDevice                = null;
+
+    private static volatile SocketAcceptThread mAcceptThread    = null;
+
+    /**
+     * Thread for reading the requests from DUN client using the socket
+     * mRfcommSocket and upload the same requests to DUNd using the
+     * socket mDundSocket
+     */
+    private static volatile UplinkThread mUplinkThread          = null;
+
+    /**
+     * Thtread for reading(download) the responses from DUNd using
+     * the socket mDundSocket and route the same responses to
+     * DUN client using the socket mRfcommSocket
+     */
+    private static volatile DownlinkThread mDownlinkThread      = null;
+
+    /**
+     * Thtread for monitoring the modem status and base on the status
+     * chnage it will update the DUN server with the updated status
+     */
+    private static volatile MonitorThread mMonitorThread      = null;
+
+    /**
+     * Listening Rfcomm Socket
+     */
+    private static volatile BluetoothServerSocket mListenSocket = null;
+
+    /**
+     * Connected Rfcomm socket
+     */
+    private static volatile BluetoothSocket mRfcommSocket       = null;
+
+    /**
+     * Socket represents the connection between DUNd and DUN service
+     */
+    private static volatile LocalSocket mDundSocket             = null;
+
+    private boolean mIsWaitingAuthorization                     = false;
+
+    private volatile boolean mInterrupted                       = false;
+
+    private boolean mDunEnable                                  = false;
+
+    private byte mRmtMdmStatus                                  = 0x00;
+
+    /**
+     * DUN profile's UUID
+     */
+    private static final String DUN_UUID = "00001103-0000-1000-8000-00805F9B34FB";
+
+    /**
+     * Intent indicating incoming connection request which is sent to
+     * BluetoothDunPermissionActivity
+     */
+    public static final String DUN_ACCESS_REQUEST_ACTION =
+                                     "org.codeaurora.bluetooth.dun.accessrequest";
+
+    /**
+     * Intent indicating incoming connection request accepted by user which is
+     * sent from BluetoothDunPermissionActivity
+     */
+    public static final String DUN_ACCESS_ALLOWED_ACTION =
+                                     "org.codeaurora.bluetooth.dun.accessallowed";
+
+    /**
+     * Intent indicating incoming connection request denied by user which is
+     * sent from BluetoothDunPermissionActivity
+     */
+    public static final String DUN_ACCESS_DISALLOWED_ACTION =
+                                  "org.codeaurora.bluetooth.dun.accessdisallowed";
+
+    /**
+     * Intent indicating timeout for user confirmation, which is sent to
+     * BluetoothDunPermissionActivity
+     */
+    public static final String USER_CONFIRM_TIMEOUT_ACTION =
+                                "org.codeaurora.bluetooth.dun.userconfirmtimeout";
+
+    public static final String THIS_PACKAGE_NAME = "org.codeaurora.bluetooth";
+
+    /**
+     * Intent Extra name indicating always allowed which is sent from
+     * BluetoothDunPermissionActivity
+     */
+    public static final String DUN_EXTRA_ALWAYS_ALLOWED =
+                                     "org.codeaurora.bluetooth.dun.alwaysallowed";
+
+    // Ensure not conflict with Opp notification ID
+    private static final int DUN_NOTIFICATION_ID_ACCESS = -1000006;
+
+    /**
+     * Intent Extra name indicating BluetoothDevice which is sent to
+     * BluetoothDunPermissionActivity
+     */
+    public static final String EXTRA_BLUETOOTH_DEVICE =
+                                   "org.codeaurora.bluetooth.dun.bluetoothdevice";
+
+    /**
+     * String signifies the DUN profile status
+     */
+    public static final String BLUETOOTH_DUN_PROFILE_STATUS = "bluetooth.dun.status";
+
+    public static final ParcelUuid DUN = ParcelUuid.fromString(DUN_UUID);
+
+    private static final Object mAcceptLock = new Object();
+
+    private static final Object mUplinkLock = new Object();
+
+    private static final Object mDownlinkLock = new Object();
+
+    private static final Object mMonitorLock = new Object();
+
+    private static final Object mAuthLock = new Object();
+
+    private HashMap<BluetoothDevice, BluetoothDunDevice> mDunDevices;
+
+    private IBinder mDunBinder;
+
+    private BluetoothAdapter mAdapter;
+
+    /**
+     * package and class name to which we send intent to check DUN profile
+     * access permission
+     */
+    private static final String ACCESS_AUTHORITY_PACKAGE = "com.android.settings";
+    private static final String ACCESS_AUTHORITY_CLASS =
+        "com.android.settings.bluetooth.BluetoothPermissionRequest";
+
+    private static final String BLUETOOTH_ADMIN_PERM =
+                    android.Manifest.permission.BLUETOOTH_ADMIN;
+
+    private static final String BLUETOOTH_PERM =
+                    android.Manifest.permission.BLUETOOTH;
+
+    @Override
+    public void onCreate() {
+
+        super.onCreate();
+
+        if (VERBOSE) Log.v(TAG, "Dun Service onCreate");
+
+        mDunDevices = new HashMap<BluetoothDevice, BluetoothDunDevice>();
+
+        mAdapter = BluetoothAdapter.getDefaultAdapter();
+        mDunBinder = initBinder();
+    }
+
+    @Override
+    public int onStartCommand(Intent intent, int flags, int startId) {
+
+        mInterrupted = false;
+
+        if (mAdapter == null) {
+            Log.w(TAG, "Stopping BluetoothDunService: "
+                    + "device does not have BT or device is not ready");
+            // Release all resources
+            closeDunService();
+        } else {
+            if (intent != null) {
+                parseIntent(intent);
+            }
+        }
+        return START_NOT_STICKY;
+    }
+
+    @Override
+    public void onDestroy() {
+        if (VERBOSE) Log.v(TAG, "Dun Service onDestroy");
+
+        super.onDestroy();
+
+        if(mDunDevices != null)
+            mDunDevices.clear();
+
+        closeDunService();
+        if(mDunHandler != null) {
+            mDunHandler.removeCallbacksAndMessages(null);
+        }
+    }
+
+    @Override
+    public IBinder onBind(Intent intent) {
+        if (VERBOSE) Log.v(TAG, "Dun Service onBind");
+        return mDunBinder;
+    }
+
+    private IBinder initBinder() {
+        return new BluetoothDunBinder(this);
+    }
+
+    private final Handler mDunHandler = new Handler() {
+        @Override
+            public void handleMessage(Message msg) {
+                switch (msg.what) {
+                    case MESSAGE_START_LISTENER:
+                        if (mAdapter.isEnabled()) {
+                            startRfcommListenerThread();
+                        }
+                        break;
+                    case MESSAGE_DUN_USER_TIMEOUT:
+                        {
+                            synchronized(mAuthLock) {
+                                if (!mIsWaitingAuthorization) {
+                                    // already handled, ignore it
+                                    break;
+                                }
+
+                                mIsWaitingAuthorization = false;
+                            }
+                            Intent intent = new Intent(USER_CONFIRM_TIMEOUT_ACTION);
+                            intent.putExtra(BluetoothDevice.EXTRA_DEVICE, mRemoteDevice);
+                            sendBroadcast(intent);
+                            removeDunNotification(DUN_NOTIFICATION_ID_ACCESS);
+                            /* close the rfcomm socket and restart the listener thread */
+                            closeRfcommSocket();
+                            startRfcommListenerThread();
+
+                            if (DBG) Log.d(TAG,"DUN user Authorization Timeout");
+                        }
+                        break;
+                }
+            }
+    };
+
+    // process the intent from DUN 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);
+        if (VERBOSE) Log.v(TAG, "state: " + state);
+
+        boolean removeTimeoutMsg = true;
+        if (action.equals(BluetoothAdapter.ACTION_STATE_CHANGED)) {
+
+            if ((state == BluetoothAdapter.STATE_ON) && (!mDunEnable)) {
+
+                synchronized(mConnection) {
+                    try {
+                        if (mAdapterService == null) {
+                            if ( !bindService(new Intent(IBluetooth.class.getName()), mConnection, 0)) {
+                                Log.v(TAG, "Could not bind to AdapterService");
+                                return;
+                            }
+                        }
+                    } catch (Exception e) {
+                        Log.e(TAG, "bindService Exception", e);
+                        return;
+                    }
+                }
+
+                Log.v(TAG, "Starting DUN server process");
+                SystemProperties.set(BLUETOOTH_DUN_PROFILE_STATUS, "running");
+
+                mDunHandler.sendMessage(mDunHandler
+                        .obtainMessage(MESSAGE_START_LISTENER));
+
+                mDunEnable = true;
+            }
+            else if (state == BluetoothAdapter.STATE_TURNING_OFF) {
+                // Send any pending timeout now, as this service will be destroyed.
+                if (mDunHandler.hasMessages(MESSAGE_DUN_USER_TIMEOUT)) {
+                    Intent timeoutIntent = new Intent(BluetoothDevice.ACTION_CONNECTION_ACCESS_CANCEL);
+                    timeoutIntent.setClassName(ACCESS_AUTHORITY_PACKAGE, ACCESS_AUTHORITY_CLASS);
+                    sendBroadcast(timeoutIntent, BLUETOOTH_ADMIN_PERM);
+                }
+
+                if (mDunEnable) {
+                    Log.v(TAG, "Stopping DUN server process");
+                    SystemProperties.set(BLUETOOTH_DUN_PROFILE_STATUS, "stopped");
+
+                    synchronized(mConnection) {
+                        try {
+                            mAdapterService = null;
+                            unbindService(mConnection);
+                        } catch (Exception e) {
+                            Log.e(TAG, "", e);
+                        }
+                    }
+
+                    // Release all resources
+                    closeDunService();
+                    mDunEnable = false;
+                }
+                synchronized(mAuthLock) {
+                    if (mIsWaitingAuthorization) {
+                        removeDunNotification(DUN_NOTIFICATION_ID_ACCESS);
+                        mIsWaitingAuthorization = false;
+                    }
+                }
+
+            } else {
+                removeTimeoutMsg = false;
+            }
+        } else if (action.equals(BluetoothDevice.ACTION_ACL_DISCONNECTED) &&
+                                 mIsWaitingAuthorization) {
+            if (mRemoteDevice == null) {
+               Log.e(TAG, "Unexpected error!");
+               return;
+            }
+            if (mDunHandler != null) {
+               /* Let the user timeout handle this case as well */
+               mDunHandler.sendMessage(mDunHandler.obtainMessage(MESSAGE_DUN_USER_TIMEOUT));
+               removeTimeoutMsg = false;
+            }
+
+        }  else if (action.equals(DUN_ACCESS_ALLOWED_ACTION)) {
+            if (mRemoteDevice == null) {
+               Log.e(TAG, "Unexpected error!");
+               return;
+            }
+
+            if (VERBOSE) {
+
+                Log.v(TAG, "DunService-Received ACCESS_ALLOWED_ACTION");
+            }
+            synchronized(mAuthLock) {
+                if (!mIsWaitingAuthorization) {
+                    // this reply is not for us
+                    return;
+                }
+
+                mIsWaitingAuthorization = false;
+            }
+
+            if (intent.getBooleanExtra(BluetoothDunService.DUN_EXTRA_ALWAYS_ALLOWED, false) == true) {
+                  if(mRemoteDevice != null) {
+                     mRemoteDevice.setTrust(true);
+                     Log.v(TAG, "setTrust() TRUE " + mRemoteDevice.getName());
+                  }
+            }
+            /* start the uplink thread */
+            startUplinkThread();
+
+        } else if (action.equals(DUN_ACCESS_DISALLOWED_ACTION)) {
+            /* close the rfcomm socket and restart the listener thread */
+            closeRfcommSocket();
+            startRfcommListenerThread();
+        } 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) && (mRemoteDevice != null) && (device.equals(mRemoteDevice)) &&
+                    (mRemoteDevice.getTrustState()) && (intent.getIntExtra(BluetoothDevice.EXTRA_BOND_STATE,
+                               BluetoothDevice.BOND_NONE) == BluetoothDevice.BOND_NONE)) {
+
+                   Log.d(TAG,"BOND_STATE_CHANGED REFRESH trustDevices"+ device.getName());
+                   mRemoteDevice.setTrust(false);
+               }
+            }
+
+        } else {
+            removeTimeoutMsg = false;
+        }
+
+        if (removeTimeoutMsg) {
+            mDunHandler.removeMessages(MESSAGE_DUN_USER_TIMEOUT);
+        }
+    }
+
+    private void startRfcommListenerThread() {
+        if (VERBOSE) Log.v(TAG, "DUN Service startRfcommListenerThread");
+
+        synchronized(mAcceptLock) {
+            // if it is already running,close it
+            if (mAcceptThread != null) {
+                try {
+                    mAcceptThread.shutdown();
+                    mAcceptThread.join();
+                    mAcceptThread = null;
+                } catch (InterruptedException ex) {
+                    Log.w(TAG, "mAcceptThread close error" + ex);
+                }
+            }
+            if (mAcceptThread == null) {
+                mAcceptThread = new SocketAcceptThread();
+                mAcceptThread.setName("BluetoothDunAcceptThread");
+                mAcceptThread.start();
+            }
+        }
+    }
+
+    private void startUplinkThread() {
+        if (VERBOSE) Log.v(TAG, "DUN Service startUplinkThread");
+
+        synchronized(mUplinkLock) {
+            if (mUplinkThread != null) {
+                try {
+                    mUplinkThread.shutdown();
+                    mUplinkThread.join();
+                    mUplinkThread = null;
+                } catch (InterruptedException ex) {
+                    Log.w(TAG, "mUplinkThread close error" + ex);
+                }
+            }
+            if (mUplinkThread == null) {
+                mUplinkThread = new UplinkThread();
+                mUplinkThread.setName("BluetoothDunUplinkThread");
+                mUplinkThread.start();
+            }
+        }
+    }
+
+    private void startDownlinkThread() {
+        if (VERBOSE) Log.v(TAG, "DUN Service startDownlinkThread");
+
+        synchronized(mDownlinkLock) {
+            if (mDownlinkThread == null) {
+                mDownlinkThread = new DownlinkThread();
+                mDownlinkThread.setName("BluetoothDunDownlinkThread");
+                mDownlinkThread.start();
+            }
+        }
+    }
+
+    private void startMonitorThread() {
+        if (VERBOSE) Log.v(TAG, "DUN Service startMonitorThread");
+
+        synchronized(mMonitorLock) {
+            if (mMonitorThread  == null) {
+                mMonitorThread  = new MonitorThread();
+                mMonitorThread .setName("BluetoothDunMonitorThread");
+                mMonitorThread .start();
+            }
+        }
+    }
+
+    private final boolean initRfcommSocket() {
+        if (VERBOSE) Log.v(TAG, "Dun Service initRfcommSocket");
+
+        boolean initRfcommSocketOK = 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++) {
+            initRfcommSocketOK = true;
+            try {
+                // start listening for incoming rfcomm channel connection
+                mListenSocket = mAdapter.listenUsingRfcommWithServiceRecord("Dial up Networking", DUN.getUuid());
+
+            } catch (IOException e) {
+                Log.e(TAG, "Error create RfcommListenSocket " + e.toString());
+                initRfcommSocketOK = false;
+            }
+            if (!initRfcommSocketOK) {
+                // Need to break out of this loop if BT is being turned off.
+                if (mAdapter == null) break;
+                int state = mAdapter.getState();
+                if ((state != BluetoothAdapter.STATE_TURNING_ON) && (state != BluetoothAdapter.STATE_ON)) {
+                    Log.w(TAG, "initRfcommSocket failed as BT is (being) turned off");
+                    break;
+                }
+                try {
+                    if (VERBOSE) Log.v(TAG, "wait 300 ms");
+                    Thread.sleep(300);
+                } catch (InterruptedException e) {
+                    Log.e(TAG, "socketAcceptThread thread was interrupted (3)");
+                    break;
+                }
+            } else {
+                break;
+            }
+        }
+
+        if (mInterrupted) {
+            initRfcommSocketOK = false;
+            //close the listen socket to avoid resource leakage
+            closeListenSocket();
+        }
+
+        if (initRfcommSocketOK) {
+            if (VERBOSE) Log.v(TAG, "Succeed to create listening socket ");
+
+        } else {
+            Log.e(TAG, "Error to create listening socket after " + CREATE_RETRY_TIME + " try");
+        }
+        return initRfcommSocketOK;
+    }
+
+    private final boolean initDundClientSocket() {
+        if (VERBOSE) Log.v(TAG, "DUN initDundClientSocket");
+
+        boolean initDundSocketOK = false;
+
+        try {
+            LocalSocketAddress locSockAddr = new LocalSocketAddress(DUN_SERVER);
+            mDundSocket = new LocalSocket();
+            mDundSocket.connect(locSockAddr);
+            initDundSocketOK = true;
+        } catch (java.io.IOException e) {
+            Log.e(TAG, "cant connect: " + e);
+        }
+
+        if (initDundSocketOK) {
+            if (VERBOSE) Log.v(TAG, "Succeed to create Dund socket ");
+
+        } else {
+            Log.e(TAG, "Error to create Dund socket");
+        }
+        return initDundSocketOK;
+    }
+
+    private final synchronized void closeListenSocket() {
+        // exit SocketAcceptThread early
+        if (VERBOSE) Log.v(TAG, "Close listen Socket : " );
+        if (mListenSocket != null) {
+            try {
+                // this will cause mListenSocket.accept() return early with
+                // IOException
+                mListenSocket.close();
+                mListenSocket = null;
+            } catch (IOException ex) {
+                Log.e(TAG, "Close listen Socket error: " + ex);
+            }
+        }
+    }
+
+    private final synchronized void closeRfcommSocket() {
+        if (VERBOSE) Log.v(TAG, "Close rfcomm Socket : " );
+        if (mRfcommSocket != null) {
+            try {
+                mRfcommSocket.close();
+                mRfcommSocket = null;
+            } catch (IOException e) {
+                Log.e(TAG, "Close Rfcomm Socket error: " + e.toString());
+            }
+        }
+    }
+
+    private final synchronized void closeDundSocket() {
+        if (VERBOSE) Log.v(TAG, "Close dund  Socket : " );
+        if (mDundSocket != null) {
+            try {
+                mDundSocket.close();
+                mDundSocket = null;
+            } catch (IOException e) {
+                Log.e(TAG, "Close Dund Socket error: " + e.toString());
+            }
+        }
+    }
+
+    private final void closeDunService() {
+        if (VERBOSE) Log.v(TAG, "Dun Service closeDunService in");
+
+        // exit initRfcommSocket early
+        mInterrupted = true;
+
+        closeListenSocket();
+
+        closeRfcommSocket();
+
+        closeDundSocket();
+
+        synchronized(mMonitorLock) {
+            if (mMonitorThread != null) {
+                try {
+                    mMonitorThread.shutdown();
+                    mMonitorThread.join();
+                    mMonitorThread = null;
+                } catch (InterruptedException ex) {
+                    Log.w(TAG, "mMonitorThread close error" + ex);
+                }
+            }
+        }
+
+        synchronized(mDownlinkLock) {
+            if (mDownlinkThread != null) {
+                try {
+                    mDownlinkThread.shutdown();
+                    mDownlinkThread.join();
+                    mDownlinkThread = null;
+                } catch (InterruptedException ex) {
+                    Log.w(TAG, "mDownlinkThread close error" + ex);
+                }
+            }
+        }
+
+        synchronized(mUplinkLock) {
+            if (mUplinkThread != null) {
+                try {
+                    mUplinkThread.shutdown();
+                    mUplinkThread.join();
+                    mUplinkThread = null;
+                } catch (InterruptedException ex) {
+                    Log.w(TAG, "mUplinkThread close error" + ex);
+                }
+            }
+        }
+        /* remove the MESSAGE_START_LISTENER message which might be posted
+         * by the uplink thread */
+        mDunHandler.removeMessages(MESSAGE_START_LISTENER);
+
+        synchronized(mAcceptLock) {
+            if (mAcceptThread != null) {
+                try {
+                    mAcceptThread.shutdown();
+                    mAcceptThread.join();
+                    mAcceptThread = null;
+                } catch (InterruptedException ex) {
+                    Log.w(TAG, "mAcceptThread close error" + ex);
+                }
+            }
+        }
+
+        stopSelf();
+
+        if (VERBOSE) Log.v(TAG, "Dun Service closeDunService out");
+    }
+
+    private void createDunNotification(BluetoothDevice device) {
+        if (VERBOSE) Log.v(TAG, "Creating DUN access notification");
+
+        NotificationManager nm = (NotificationManager)
+                getSystemService(Context.NOTIFICATION_SERVICE);
+
+        // Create an intent triggered by clicking on the status icon.
+        Intent clickIntent = new Intent();
+        clickIntent.setClass(this, BluetoothDunPermissionActivity.class);
+        clickIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
+        clickIntent.setAction(DUN_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, BluetoothDunReceiver.class);
+
+        Notification notification = null;
+        String name = device.getName();
+        if (TextUtils.isEmpty(name)) {
+            name = getString(R.string.defaultname);
+        }
+
+        deleteIntent.setAction(DUN_ACCESS_DISALLOWED_ACTION);
+        notification = new Notification(android.R.drawable.stat_sys_data_bluetooth,
+            getString(R.string.dun_notif_ticker), System.currentTimeMillis());
+        notification.setLatestEventInfo(this, getString(R.string.dun_notif_ticker),
+                getString(R.string.dun_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(DUN_NOTIFICATION_ID_ACCESS, notification);
+
+
+        if (VERBOSE) Log.v(TAG, "Awaiting Authorization : DUN Connection : " + device.getName());
+
+    }
+
+    private void removeDunNotification(int id) {
+        Context context = getApplicationContext();
+        NotificationManager nm = (NotificationManager) context
+                .getSystemService(Context.NOTIFICATION_SERVICE);
+        nm.cancel(id);
+    }
+
+    /**
+     * 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;
+
+        @Override
+        public void run() {
+            BluetoothServerSocket listenSocket;
+            if (mListenSocket == null) {
+                if (!initRfcommSocket()) {
+                    return;
+                }
+            }
+
+            while (!stopped) {
+                try {
+                    if (VERBOSE) Log.v(TAG, "Accepting socket connection...");
+                    listenSocket = mListenSocket;
+                    if (listenSocket == null) {
+                        Log.w(TAG, "mListenSocket is null");
+                        break;
+                    }
+
+                    mRfcommSocket = mListenSocket.accept();
+
+                    synchronized (BluetoothDunService.this) {
+                        if (mRfcommSocket == null) {
+                            Log.w(TAG, " mRfcommSocket is null");
+                            break;
+                        }
+                        mRemoteDevice = mRfcommSocket.getRemoteDevice();
+                    }
+                    if (mRemoteDevice == null) {
+                        /* close the rfcomm socket */
+                        closeRfcommSocket();
+                        Log.i(TAG, "getRemoteDevice() = null");
+                        break;
+                    }
+                    boolean trust = false;
+                    if (mRemoteDevice != null)
+                        trust = mRemoteDevice.getTrustState();
+                    if (VERBOSE) Log.v(TAG, "GetTrustState() = " + trust);
+
+                    if (trust) {
+                        /* start the uplink thread */
+                        startUplinkThread();
+                    } else {
+                        createDunNotification(mRemoteDevice);
+
+                        if (VERBOSE) Log.v(TAG, "incoming connection accepted from: "+ mRemoteDevice);
+                        mIsWaitingAuthorization = true;
+
+                        mDunHandler.sendMessageDelayed(mDunHandler.obtainMessage(MESSAGE_DUN_USER_TIMEOUT),
+                                 USER_CONFIRM_TIMEOUT_VALUE);
+                    }
+                    stopped = true; // job done ,close this thread;
+                    if (VERBOSE) Log.v(TAG, "SocketAcceptThread stopped ");
+                } catch (IOException ex) {
+                    stopped=true;
+                    if (VERBOSE) Log.v(TAG, "Accept thread exception: " + ex.toString());
+                }
+            }
+        }
+
+        void shutdown() {
+            stopped = true;
+            interrupt();
+        }
+    }
+
+    /**
+     * A thread that runs in the background waiting for remote DUNd socket
+     * connection.Once the connection is established, this thread starts the
+     * downlink thread and starts forwarding the DUN profile requests to
+     * the DUNd on receiving the requests from rfcomm channel.
+     */
+    private class UplinkThread extends Thread {
+
+        private boolean stopped = false;
+        private boolean IntExit = false;
+        private OutputStream mDundOutputStream = null;
+        private InputStream mRfcommInputStream = null;
+        ByteBuffer IpcMsgBuffer = ByteBuffer.allocate(DUN_MAX_IPC_MSG_LEN);
+        private int NumRead = 0;
+        private byte TempByte = 0;
+
+        @Override
+        public void run() {
+            if (mDundSocket == null) {
+                if (!initDundClientSocket()) {
+                    /*restart the listener thread */
+                    mDunHandler.sendMessage(mDunHandler.obtainMessage(MESSAGE_START_LISTENER));
+                    return;
+                }
+            }
+            if(mDundSocket != null) {
+                if( mDundSocket.isConnected())
+                    try {
+                        mDundOutputStream = mDundSocket.getOutputStream();
+                    } catch (IOException ex) {
+                        if (VERBOSE)
+                            Log.v(TAG, "mDundOutputStream exception: " + ex.toString());
+                    }
+                else return;
+            }
+            else return;
+
+            if (mRfcommSocket != null) {
+                if(mRfcommSocket.isConnected())
+                    try {
+                        mRfcommInputStream = mRfcommSocket.getInputStream();
+                    } catch (IOException ex) {
+                        if (VERBOSE)
+                            Log.v(TAG, "mRfcommInputStream exception: " + ex.toString());
+                    }
+                else return;
+            }
+            else return;
+
+            // create the downlink thread for reading the responses
+            //from DUN server process
+            startDownlinkThread();
+            // create the modem status monitor thread
+            startMonitorThread();
+
+            while (!stopped) {
+                try {
+                    if (VERBOSE)
+                        Log.v(TAG, "Reading the DUN requests from Rfcomm channel");
+                    /* Read the DUN request from Rfcomm channel */
+                    NumRead = mRfcommInputStream.read(IpcMsgBuffer.array(), DUN_IPC_MSG_OFF_MSG,
+                                                                                DUN_MAX_MSG_LEN);
+                    if ( NumRead < 0) {
+                        IntExit = true;
+                        break;
+                    }
+                    else if ( NumRead != 0) {
+                        /* Wrtie the same DUN request to the DUN server socket with
+                           some additional parameters */
+                        IpcMsgBuffer.put(DUN_IPC_MSG_OFF_MSG_TYPE, DUN_IPC_MSG_DUN_REQUEST);
+                        IpcMsgBuffer.putShort(DUN_IPC_MSG_OFF_MSG_LEN, (short)NumRead);
+                        /* swap bytes of message len as buffer is Big Endian */
+                        TempByte = IpcMsgBuffer.get(DUN_IPC_MSG_OFF_MSG_LEN);
+
+                        IpcMsgBuffer.put( DUN_IPC_MSG_OFF_MSG_LEN, IpcMsgBuffer.get(DUN_IPC_MSG_OFF_MSG_LEN + 1));
+
+                        IpcMsgBuffer.put(DUN_IPC_MSG_OFF_MSG_LEN + 1, TempByte);
+                        try {
+                            mDundOutputStream.write(IpcMsgBuffer.array(), 0, NumRead + DUN_IPC_HEADER_SIZE);
+                        } catch (IOException ex) {
+                            break;
+                        }
+                    }
+                } catch (IOException ex) {
+                    IntExit = true;
+                    if (VERBOSE) Log.v(TAG, "Rfcomm channel Read exception: " + ex.toString());
+                    break;
+                }
+            }
+            // send the disconneciton request immediate to Dun server
+            if (IntExit) {
+                disconnect(mRemoteDevice);
+            }
+
+            /* close the dund socket */
+            closeDundSocket();
+            /* close the rfcomm socket */
+            closeRfcommSocket();
+            // Intimate the Settings APP about the disconnection
+            handleDunDeviceStateChange(mRemoteDevice, BluetoothProfile.STATE_DISCONNECTED);
+
+            /* wait for dun downlink thread to close */
+                if (VERBOSE) Log.v(TAG, "wait for dun downlink thread to close");
+            synchronized (mDownlinkLock) {
+                if (mDownlinkThread != null) {
+                    try {
+                        mDownlinkThread.shutdown();
+                        mDownlinkThread.join();
+                        mDownlinkThread = null;
+                    } catch (InterruptedException ex) {
+                        Log.w(TAG, "mDownlinkThread close error" + ex);
+                    }
+                }
+            }
+
+            /* wait for dun monitor thread to close */
+                if (VERBOSE) Log.v(TAG, "wait for dun monitor thread to close ");
+            synchronized (mMonitorLock) {
+                if (mMonitorThread != null) {
+                    try {
+                        mMonitorThread.shutdown();
+                        mMonitorThread.join();
+                        mMonitorThread = null;
+                    } catch (InterruptedException ex) {
+                        Log.w(TAG, "mMonitorThread  close error" + ex);
+                    }
+                }
+            }
+
+            if (IntExit && !stopped) {
+                if (VERBOSE) Log.v(TAG, "starting the listener thread ");
+                /* start the listener thread */
+                mDunHandler.sendMessage(mDunHandler.obtainMessage(MESSAGE_START_LISTENER));
+            }
+            /* reset the modem status */
+            mRmtMdmStatus = 0x00;
+            if (VERBOSE) Log.v(TAG, "uplink thread exited ");
+        }
+
+        void shutdown() {
+            stopped = true;
+            interrupt();
+        }
+    }
+
+
+    /**
+     * A thread that runs in the background and monitors the modem status of rfcomm
+     * channel and when there is a change in the modem status it will update the
+     * DUN server process with the new status.
+     */
+    private class MonitorThread extends Thread {
+
+        private boolean stopped = false;
+        private int len = 0;
+        private byte modemStatus = 0;
+        ByteBuffer IpcMsgBuffer = ByteBuffer.allocate(DUN_IPC_MDM_STATUS_MSG_SIZE);
+        private byte TempByte = 0;
+
+        byte mdmBits = 0;
+
+        @Override
+        public void run() {
+
+            while (!stopped) {
+                try {
+                    //wait for 200 ms
+                    Thread.sleep(MON_THREAD_SLEEP_INTERVAL);
+                } catch (InterruptedException e) {
+                    Log.e(TAG, "MonitorThread thread was interrupted");
+                    break;
+                }
+                try {
+                    if ((mRfcommSocket == null) || (!mRfcommSocket.isConnected())) {
+                        break;
+                    }
+                    len = mRfcommSocket.getSocketOpt(BTSOCK_OPT_GET_MODEM_BITS, IpcMsgBuffer.array());
+                    if(len != DUN_IPC_MDM_STATUS_MSG_SIZE)  {
+                        Log.e(TAG, "getSocketOpt return length of socket option mismatch:");
+                        continue;
+                    }
+                } catch (IOException ex) {
+                    if (VERBOSE) Log.v(TAG, "getSocketOpt Exception: " + ex.toString());
+                }
+                if( mdmBits != IpcMsgBuffer.get(0)) {
+                    mdmBits = IpcMsgBuffer.get(0);
+                    notifyModemStatus(mdmBits);
+                }
+            }
+            if (VERBOSE) Log.v(TAG, "MonitorThread thread exited ");
+        }
+
+        void shutdown() {
+            stopped = true;
+            interrupt();
+        }
+    }
+
+
+    /**
+     * A thread that runs in the background and starts forwarding the
+     * DUN profile responses received from Dund to the rfcomm channel.
+     */
+    private class DownlinkThread extends Thread {
+
+        private boolean stopped = false;
+        private OutputStream mRfcommOutputStream = null;
+        private InputStream mDundInputStream = null;
+        ByteBuffer IpcMsgBuffer = ByteBuffer.allocate(2*DUN_MAX_IPC_MSG_LEN);
+        private int NumRead = 0;
+        private int ReadIndex = 0;
+        private short MsgLen = 0;
+        private byte TempByte = 0;
+
+        @Override
+        public void run() {
+
+            if (mDundSocket != null) {
+                if ( mDundSocket.isConnected())
+                    try {
+                        mDundInputStream = mDundSocket.getInputStream();
+                    } catch (IOException ex) {
+                        if (VERBOSE) Log.v(TAG, "mDundInputStream exception: " + ex.toString());
+                    }
+                else return;
+            }
+            else return;
+
+            if (mRfcommSocket != null) {
+                if (mRfcommSocket.isConnected())
+                    try {
+                        mRfcommOutputStream = mRfcommSocket.getOutputStream();
+                    } catch (IOException ex) {
+                        if (VERBOSE) Log.v(TAG, "mRfcommOutputStream exception: " + ex.toString());
+                    }
+                else return;
+            }
+            else return;
+
+            while (!stopped) {
+                try {
+                    if (VERBOSE) Log.v(TAG, "Reading the DUN responses from Dund");
+                    /* Read the DUN responses from DUN server */
+                    NumRead = mDundInputStream.read(IpcMsgBuffer.array(),0, DUN_MAX_IPC_MSG_LEN);
+                    if (VERBOSE) Log.v(TAG, "NumRead" + NumRead);
+                    if ( NumRead < 0) {
+                        break;
+                    }
+                    else if( NumRead != 0) {
+                        /* some times read buffer contains multiple responses */
+                        do {
+                            /* swap bytes of message len as buffer is Big Endian */
+                            TempByte = IpcMsgBuffer.get(ReadIndex + DUN_IPC_MSG_OFF_MSG_LEN);
+
+                            IpcMsgBuffer.put(ReadIndex + DUN_IPC_MSG_OFF_MSG_LEN, IpcMsgBuffer.get(ReadIndex +
+                                         DUN_IPC_MSG_OFF_MSG_LEN + 1));
+
+                            IpcMsgBuffer.put(ReadIndex + DUN_IPC_MSG_OFF_MSG_LEN + 1, TempByte);
+
+                            MsgLen =  IpcMsgBuffer.getShort(ReadIndex + DUN_IPC_MSG_OFF_MSG_LEN);
+
+                            if((ReadIndex + MsgLen + DUN_IPC_HEADER_SIZE) > NumRead) {
+                                if (VERBOSE) Log.v(TAG, "Boundary condition hit");
+                                mDundInputStream.read(IpcMsgBuffer.array(), NumRead , ReadIndex + MsgLen + DUN_IPC_HEADER_SIZE - NumRead);
+
+                            }
+
+                            if (IpcMsgBuffer.get(ReadIndex + DUN_IPC_MSG_OFF_MSG_TYPE) == DUN_IPC_MSG_DUN_RESPONSE) {
+                                try {
+                                    mRfcommOutputStream.write(IpcMsgBuffer.array(), ReadIndex + DUN_IPC_MSG_OFF_MSG,
+                                           IpcMsgBuffer.getShort(ReadIndex + DUN_IPC_MSG_OFF_MSG_LEN));
+                                } catch (IOException ex) {
+                                    stopped = true;
+                                    break;
+                                }
+                                if (VERBOSE)
+                                    Log.v(TAG, "DownlinkThread Msg written to Rfcomm");
+                            }
+                            else if (IpcMsgBuffer.get(ReadIndex + DUN_IPC_MSG_OFF_MSG_TYPE) == DUN_IPC_MSG_CTRL_RESPONSE) {
+
+                                if (IpcMsgBuffer.get(ReadIndex + DUN_IPC_MSG_OFF_MSG) == DUN_CRTL_MSG_CONNECTED_RESP) {
+                                    handleDunDeviceStateChange(mRemoteDevice, BluetoothProfile.STATE_CONNECTED);
+                                }
+                                else if (IpcMsgBuffer.get(ReadIndex + DUN_IPC_MSG_OFF_MSG) == DUN_CRTL_MSG_DISCONNECTED_RESP) {
+                                    handleDunDeviceStateChange(mRemoteDevice, BluetoothProfile.STATE_DISCONNECTED);
+                                }
+                                if (VERBOSE)
+                                    Log.v(TAG, "DownlinkThread control message received");
+                            }
+                            else if (IpcMsgBuffer.get(ReadIndex + DUN_IPC_MSG_OFF_MSG_TYPE) == DUN_IPC_MSG_MDM_STATUS) {
+                                /* handle the modem status messages */
+                                handleModemStatusChange(IpcMsgBuffer.get(ReadIndex + DUN_IPC_MSG_OFF_MSG));
+                                if (VERBOSE)
+                                    Log.v(TAG, "DownlinkThread modem status message received");
+                            }
+
+                            ReadIndex = ReadIndex + IpcMsgBuffer.getShort(ReadIndex + DUN_IPC_MSG_OFF_MSG_LEN);
+                            ReadIndex = ReadIndex + DUN_IPC_HEADER_SIZE;
+                        } while(ReadIndex < NumRead);
+                    }
+
+                } catch (IOException ex) {
+                    stopped = true;
+                    if (VERBOSE) Log.v(TAG, "Dund Read exception: " + ex.toString());
+                }
+                /* reset the ReadIndex */
+                ReadIndex = 0;
+            }
+            /* close the dund socket */
+            closeDundSocket();
+            /* close the rfcomm socket */
+            closeRfcommSocket();
+
+            if (VERBOSE) Log.v(TAG, "downlink thread exited ");
+        }
+
+        void shutdown() {
+            stopped = true;
+            interrupt();
+        }
+    }
+
+
+    boolean disconnect(BluetoothDevice device) {
+        enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission");
+        OutputStream mDundOutputStream = null;
+        int WriteLen = DUN_IPC_HEADER_SIZE + DUN_IPC_CTRL_MSG_SIZE;
+        ByteBuffer IpcMsgBuffer = ByteBuffer.allocate(WriteLen);
+
+        if (mDundSocket != null) {
+            if ( mDundSocket.isConnected())
+                try {
+                    mDundOutputStream = mDundSocket.getOutputStream();
+                } catch (IOException ex) {
+                    if (VERBOSE) Log.v(TAG, "mDundOutputStream exception: " + ex.toString());
+                }
+            else return false;
+        }
+        else return false;
+
+        IpcMsgBuffer.put(DUN_IPC_MSG_OFF_MSG_TYPE, DUN_IPC_MSG_CTRL_REQUEST);
+        IpcMsgBuffer.putShort(DUN_IPC_MSG_OFF_MSG_LEN,DUN_IPC_CTRL_MSG_SIZE);
+        IpcMsgBuffer.put(DUN_IPC_MSG_OFF_MSG, DUN_CRTL_MSG_DISCONNECT_REQ);
+        try {
+            mDundOutputStream.write(IpcMsgBuffer.array(), 0, WriteLen);
+        } catch (IOException ex) {
+            if (VERBOSE) Log.v(TAG, "mDundOutputStream wrtie exception: " + ex.toString());
+        }
+        return true;
+    }
+
+    boolean notifyModemStatus(byte status) {
+        enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission");
+        OutputStream mDundOutputStream = null;
+        int WriteLen = DUN_IPC_HEADER_SIZE + DUN_IPC_MDM_STATUS_MSG_SIZE;
+        ByteBuffer IpcMsgBuffer = ByteBuffer.allocate(WriteLen);
+        if (DBG) Log.d(TAG, "notifyModemStatus: status: " + status);
+
+        if (mDundSocket != null) {
+            if ( mDundSocket.isConnected())
+                try {
+                    mDundOutputStream = mDundSocket.getOutputStream();
+                } catch (IOException ex) {
+                    if (VERBOSE) Log.v(TAG, "mDundOutputStream exception: " + ex.toString());
+                }
+            else return false;
+        }
+        else return false;
+
+        IpcMsgBuffer.put(DUN_IPC_MSG_OFF_MSG_TYPE, DUN_IPC_MSG_MDM_STATUS);
+        IpcMsgBuffer.putShort(DUN_IPC_MSG_OFF_MSG_LEN,DUN_IPC_MDM_STATUS_MSG_SIZE);
+        IpcMsgBuffer.put(DUN_IPC_MSG_OFF_MSG, status);
+        try {
+            mDundOutputStream.write(IpcMsgBuffer.array(), 0, WriteLen);
+        } catch (IOException ex) {
+            if (VERBOSE) Log.v(TAG, "mDundOutputStream wrtie exception: " + ex.toString());
+        }
+        return true;
+    }
+
+    int getConnectionState(BluetoothDevice device) {
+        BluetoothDunDevice dunDevice = mDunDevices.get(device);
+        if (dunDevice == null) {
+            return BluetoothDun.STATE_DISCONNECTED;
+        }
+        return dunDevice.mState;
+    }
+
+    public void notifyProfileConnectionStateChanged(BluetoothDevice device,
+            int profileId, int newState, int prevState) {
+        if (mAdapterService != null) {
+            try {
+                mAdapterService.sendConnectionStateChange(device, profileId, newState, prevState);
+            }catch (RemoteException re) {
+                Log.e(TAG, "",re);
+            }
+        }
+    }
+
+    private final void handleDunDeviceStateChange(BluetoothDevice device, int state) {
+        if (DBG) Log.d(TAG, "handleDunDeviceStateChange: device: " + device + " state: " + state);
+        int prevState;
+        BluetoothDunDevice dunDevice = mDunDevices.get(device);
+        if (dunDevice == null) {
+            prevState = BluetoothProfile.STATE_DISCONNECTED;
+        } else {
+            prevState = dunDevice.mState;
+        }
+        Log.d(TAG, "handleDunDeviceStateChange preState: " + prevState + " state: " + state);
+        if (prevState == state) return;
+
+        if (dunDevice == null) {
+            dunDevice = new BluetoothDunDevice(state);
+            mDunDevices.put(device, dunDevice);
+        } else {
+            dunDevice.mState = state;
+        }
+
+        /* Notifying the connection state change of the profile before sending
+           the intent for connection state change, as it was causing a race
+           condition, with the UI not being updated with the correct connection
+           state. */
+        Log.d(TAG, "Dun Device state : device: " + device + " State:" +
+                       prevState + "->" + state);
+        notifyProfileConnectionStateChanged(device, BluetoothProfile.DUN, state, prevState);
+        Intent intent = new Intent(BluetoothDun.ACTION_CONNECTION_STATE_CHANGED);
+        intent.putExtra(BluetoothDevice.EXTRA_DEVICE, device);
+        intent.putExtra(BluetoothDun.EXTRA_PREVIOUS_STATE, prevState);
+        intent.putExtra(BluetoothDun.EXTRA_STATE, state);
+        sendBroadcast(intent, BLUETOOTH_PERM);
+
+    }
+
+    private final void handleModemStatusChange(byte status) {
+        byte mdmBits = 0;
+        int WriteLen = DUN_IPC_MDM_STATUS_MSG_SIZE;
+        ByteBuffer IpcMsgBuffer = ByteBuffer.allocate(WriteLen);
+
+        if (DBG) Log.d(TAG, "handleModemStatusChange  status: " + status);
+
+        if ((mRfcommSocket == null) || (!mRfcommSocket.isConnected())) {
+            return;
+        }
+        if(mRmtMdmStatus != status) {
+            mdmBits = (byte)((~(int)mRmtMdmStatus) & ((int)mRmtMdmStatus ^ (int)status));
+            if (DBG) Log.d(TAG, "handleModemStatusChange  mdmBits " + mdmBits);
+            if(mdmBits > 0) {
+                IpcMsgBuffer.put(0, mdmBits);
+                try {
+                    mRfcommSocket.setSocketOpt(BTSOCK_OPT_SET_MODEM_BITS, IpcMsgBuffer.array(), WriteLen);
+                } catch (IOException ex) {
+                    if (VERBOSE) Log.v(TAG, "getSocketOpt Exception: " + ex.toString());
+                }
+            }
+            mdmBits = (byte)(((int)mRmtMdmStatus) & ((int)mRmtMdmStatus ^ (int)status));
+            if (DBG) Log.d(TAG, "handleModemStatusChange  mdmBits " + mdmBits);
+            if(mdmBits > 0) {
+                IpcMsgBuffer.put(0, mdmBits);
+                try {
+                    mRfcommSocket.setSocketOpt(BTSOCK_OPT_CLR_MODEM_BITS, IpcMsgBuffer.array(), WriteLen);
+                } catch (IOException ex) {
+                    if (VERBOSE) Log.v(TAG, "getSocketOpt Exception: " + ex.toString());
+                }
+            }
+            mRmtMdmStatus = status;
+        }
+    }
+
+    List<BluetoothDevice> getConnectedDevices() {
+        enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission");
+        List<BluetoothDevice> devices = getDevicesMatchingConnectionStates(
+                new int[] {BluetoothProfile.STATE_CONNECTED});
+        return devices;
+    }
+
+    List<BluetoothDevice> getDevicesMatchingConnectionStates(int[] states) {
+         enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission");
+        List<BluetoothDevice> dunDevices = new ArrayList<BluetoothDevice>();
+
+        for (BluetoothDevice device: mDunDevices.keySet()) {
+            int dunDeviceState = getConnectionState(device);
+            for (int state : states) {
+                if (state == dunDeviceState) {
+                    dunDevices.add(device);
+                    break;
+                }
+            }
+        }
+        return dunDevices;
+    }
+
+    private ServiceConnection mConnection = new ServiceConnection() {
+        public void onServiceConnected(ComponentName className, IBinder service) {
+            mAdapterService = IBluetooth.Stub.asInterface(service);
+        }
+
+        public void onServiceDisconnected(ComponentName className) {
+            mAdapterService = null;
+        }
+    };
+
+    /**
+     * Handlers for incoming service calls
+     */
+    private static class BluetoothDunBinder extends IBluetoothDun.Stub {
+
+        private BluetoothDunService mService;
+
+        public BluetoothDunBinder(BluetoothDunService svc) {
+            mService = svc;
+        }
+
+        private BluetoothDunService getService() {
+            if (mService != null)
+                return mService;
+            return null;
+        }
+
+        public boolean disconnect(BluetoothDevice device) {
+            BluetoothDunService service = getService();
+            if (service == null) return false;
+            return service.disconnect(device);
+        }
+
+        public int getConnectionState(BluetoothDevice device) {
+            BluetoothDunService service = getService();
+            if (service == null) return BluetoothDun.STATE_DISCONNECTED;
+            return service.getConnectionState(device);
+        }
+        public List<BluetoothDevice> getConnectedDevices() {
+            BluetoothDunService service = getService();
+            if (service == null) return new ArrayList<BluetoothDevice>(0);
+            return service.getConnectedDevices();
+        }
+
+        public List<BluetoothDevice> getDevicesMatchingConnectionStates(int[] states) {
+            BluetoothDunService service = getService();
+            if (service == null) return new ArrayList<BluetoothDevice>(0);
+            return service.getDevicesMatchingConnectionStates(states);
+        }
+    };
+
+    private class BluetoothDunDevice {
+        private int mState;
+
+        BluetoothDunDevice(int state) {
+            mState = state;
+        }
+    }
+}
diff --git a/src/org/codeaurora/bluetooth/ftp/BluetoothFtpActivity.java b/src/org/codeaurora/bluetooth/ftp/BluetoothFtpActivity.java
index a4ad2f8..57c7591 100644
--- a/src/org/codeaurora/bluetooth/ftp/BluetoothFtpActivity.java
+++ b/src/org/codeaurora/bluetooth/ftp/BluetoothFtpActivity.java
@@ -101,7 +101,7 @@
             if (!BluetoothFtpService.USER_CONFIRM_TIMEOUT_ACTION.equals(intent.getAction())) {
                 return;
             }
-            onTimeout();
+            finish();
         }
     };