Bluetooth: Changes for LPP

This patch includes implementation of a new Bluetooth
Adapter named QBluetoothAdapter for implementing LE
specific tasks that the default adapter doesnt perform.
This patch also implements Low power proximity.

CRs-Fixed: 581289

Conflicts:
	services/core/java/com/android/server/BluetoothManagerService.java

Change-Id: I63e0593f79c8929d4e18cee0472fcb401c8892e7
diff --git a/Android.mk b/Android.mk
index fa0b579..cb8dfaa 100755
--- a/Android.mk
+++ b/Android.mk
@@ -97,10 +97,12 @@
 	core/java/android/wipower/IWipower.aidl \
 	core/java/android/wipower/IWipowerManagerCallback.aidl \
 	core/java/android/bluetooth/IBluetooth.aidl \
+	core/java/android/bluetooth/IQBluetooth.aidl \
 	core/java/android/bluetooth/IBluetoothA2dp.aidl \
 	core/java/android/bluetooth/IBluetoothA2dpSink.aidl \
 	core/java/android/bluetooth/IBluetoothAvrcpController.aidl \
 	core/java/android/bluetooth/IBluetoothCallback.aidl \
+	core/java/android/bluetooth/IQBluetoothAdapterCallback.aidl \
 	core/java/android/bluetooth/IBluetoothHeadset.aidl \
 	core/java/android/bluetooth/IBluetoothHeadsetPhone.aidl \
 	core/java/android/bluetooth/IBluetoothHealth.aidl \
@@ -109,6 +111,7 @@
 	core/java/android/bluetooth/IBluetoothPan.aidl \
 	core/java/android/bluetooth/IBluetoothManager.aidl \
 	core/java/android/bluetooth/IBluetoothManagerCallback.aidl \
+	core/java/android/bluetooth/IQBluetoothManagerCallback.aidl \
 	core/java/android/bluetooth/IBluetoothPbap.aidl \
 	core/java/android/bluetooth/IBluetoothMap.aidl \
 	core/java/android/bluetooth/IBluetoothStateChangeCallback.aidl \
diff --git a/core/java/android/bluetooth/BluetoothLwPwrProximityMonitor.java b/core/java/android/bluetooth/BluetoothLwPwrProximityMonitor.java
new file mode 100644
index 0000000..4f886c4
--- /dev/null
+++ b/core/java/android/bluetooth/BluetoothLwPwrProximityMonitor.java
@@ -0,0 +1,367 @@
+/*
+ * 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 "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 android.bluetooth;
+
+import android.bluetooth.BluetoothAdapter;
+import android.bluetooth.QBluetoothAdapter;
+import android.bluetooth.BluetoothGatt;
+import android.bluetooth.IBluetoothGatt;
+import android.bluetooth.BluetoothGattCallback;
+import android.bluetooth.BluetoothRssiMonitorCallback;
+import android.bluetooth.BluetoothDevice;
+import android.bluetooth.IBluetoothManager;
+import android.bluetooth.BluetoothManager;
+import android.content.Context;
+import android.os.RemoteException;
+import android.util.Log;
+import java.util.Timer;
+import java.util.TimerTask;
+
+/** @hide */
+public final class  BluetoothLwPwrProximityMonitor implements QBluetoothAdapter.LeLppCallback {
+    private static final     String TAG = "BluetoothLwPwrProximityMonitor";
+    private static final     boolean DBG = true;
+    private BluetoothDevice  mDevice = null;
+    private Context          mContext = null;
+    private BluetoothGatt    mGattProfile = null;
+    private QBluetoothAdapter     mQAdapter = null;
+    private BluetoothRssiMonitorCallback mMonitorCbk;
+    private boolean   mAutoConnect = false;
+    private int       mState;
+    private final Object mStateLock = new Object();
+    /* This timer is triggered in case that BluetoothGatt does not callback when we perform connect/disconnect */
+    private Timer     mTimer = null;
+    private final int mTimeOutValue = 10*1000;
+    private final class ConnectTimeOutTask extends TimerTask {
+        public void run() {
+            if (DBG) Log.d(TAG, "connect timer triggered!");
+            boolean notify = false;
+            synchronized(mStateLock) {
+                if (mState == MONITOR_STATE_STARTING) {
+                    mState = MONITOR_STATE_IDLE;
+                    if (mQAdapter != null && mDevice != null) {
+                        mQAdapter.registerLppClient(BluetoothLwPwrProximityMonitor.this, mDevice.getAddress(), false);
+                    }
+                    notify = true;
+                }
+            }
+            if (notify && mMonitorCbk != null) {
+                mMonitorCbk.onStopped();
+            }
+        }
+    };
+
+    private final class DisconnectTimeOutTask extends TimerTask {
+        public void run() {
+            if (DBG) Log.d(TAG, "disconnect timer triggered");
+            boolean notify = false;
+            synchronized(mStateLock) {
+                if (mState == MONITOR_STATE_STOPPING) {
+                    mState = MONITOR_STATE_IDLE;
+                    if (mQAdapter != null && mDevice != null) {
+                        mQAdapter.registerLppClient(BluetoothLwPwrProximityMonitor.this, mDevice.getAddress(), false);
+                    }
+                    notify = true;
+                }
+            }
+            if (notify && mMonitorCbk != null) {
+                mMonitorCbk.onStopped();
+            }
+        }
+    };
+    /* Monitor state constants */
+    private static final int MONITOR_STATE_IDLE     = 0;
+    private static final int MONITOR_STATE_STARTING = 1;
+    private static final int MONITOR_STATE_STOPPING = 2;
+    private static final int MONITOR_STATE_STARTED  = 3;
+    private static final int MONITOR_STATE_CLOSED   = 4;
+
+    /* constants for rssi threshold event */
+    /** @hide */
+    public static final int RSSI_MONITOR_DISABLED = 0x00;
+    /** @hide */
+    public static final int RSSI_HIGH_ALERT       = 0x01;
+    /** @hide */
+    public static final int RSSI_MILD_ALERT       = 0x02;
+    /** @hide */
+    public static final int RSSI_NO_ALERT         = 0x03;
+
+    /* command status */
+    /** @hide */
+    public static final int COMMAND_STATUS_SUCCESS = 0x00;
+    /** @hide */
+    public static final int COMMAND_STATUS_FAILED  = 0x01;
+    private int mLowerLimit;
+    private int mUpperLimit;
+
+    private final BluetoothGattCallback mGattCallback = new BluetoothGattCallback () {
+        public void onConnectionStateChange (BluetoothGatt gatt, int status,
+                                             int newState) {
+            if (DBG) Log.d(TAG, "onConnectionStateChange() + newState=" + newState);
+            if (mDevice == null) return;
+            if (mGattProfile != gatt) return;
+            if(mQAdapter == null) return;
+            boolean stop = false;
+            synchronized(mStateLock){
+                cancelTimer();
+                if (newState == BluetoothProfile.STATE_DISCONNECTED) {
+                    if (mState != MONITOR_STATE_CLOSED) {
+                        mQAdapter.registerLppClient(BluetoothLwPwrProximityMonitor.this,mDevice.getAddress(), false);
+                        mState = MONITOR_STATE_IDLE;
+                        stop = true;
+                    }
+                } else if (newState == BluetoothProfile.STATE_CONNECTED) {
+                    if (mState == MONITOR_STATE_STARTING) {
+                        if (status == BluetoothGatt.GATT_SUCCESS) {
+                            if(!mQAdapter.writeRssiThreshold(BluetoothLwPwrProximityMonitor.this, mLowerLimit, mUpperLimit)) {
+                                mGattProfile.disconnect();
+                                mState = MONITOR_STATE_STOPPING;
+                                setTimer(BluetoothLwPwrProximityMonitor.this.new DisconnectTimeOutTask(), mTimeOutValue);
+                            }
+                        } else {
+                            stop = true;
+                        }
+                    }
+                }
+            }
+            if (stop && mMonitorCbk != null){
+                mMonitorCbk.onStopped();
+                if (DBG) Log.d(TAG, "Monitor is stopped");
+            }
+        }
+    };
+
+    public BluetoothLwPwrProximityMonitor (Context cxt, String device, BluetoothRssiMonitorCallback cbk) {
+        mContext    = cxt;
+        mState      = MONITOR_STATE_CLOSED;
+        mMonitorCbk = cbk;
+
+        try {
+            mDevice     = new BluetoothDevice(device);
+        } catch (IllegalArgumentException e) {
+            mDevice = null;
+            if (DBG) Log.e(TAG, "", e);
+        }
+
+        mQAdapter  = QBluetoothAdapter.getDefaultAdapter();
+        if (mDevice != null && mQAdapter != null) {
+            mState = MONITOR_STATE_IDLE;
+        } else {
+            mDevice = null;
+            mQAdapter = null;
+        }
+    }
+
+    protected void finalize() throws Throwable {
+        try {
+            close();
+        } finally {
+            super.finalize();
+        }
+    }
+
+    private void setTimer(TimerTask task, int delay) {
+        if (DBG) Log.d(TAG, "setTimer() delay=" + delay);
+        mTimer = new Timer();
+        mTimer.schedule(task, delay);
+    }
+
+    private void cancelTimer() {
+        if (DBG) Log.d(TAG, "cancelTimer()");
+        if (mTimer != null) {
+            mTimer.cancel();
+        }
+        mTimer = null;
+    }
+
+    /** @hide */
+    public boolean start (int thresh_min, int thresh_max) {
+        if (DBG) Log.d(TAG, "start() low=" + thresh_min + ", upper=" + thresh_max);
+        synchronized(mStateLock){
+            if (mState != MONITOR_STATE_IDLE) {
+                if (DBG) Log.d(TAG, "start() invalid state, monitor is not idle");
+                return false;
+            }
+            if (!mQAdapter.registerLppClient(this, mDevice.getAddress(), true)) {
+                if (DBG) Log.d(TAG, "cannot register LPP Client");
+                return false;
+            }
+            mState = MONITOR_STATE_STARTING;
+            mLowerLimit = thresh_min;
+            mUpperLimit = thresh_max;
+
+            try {
+                if (mGattProfile == null) {
+                    mGattProfile = mDevice.connectGatt(mContext, mAutoConnect, mGattCallback);
+                    if (mGattProfile == null){
+                        mQAdapter.registerLppClient(this, mDevice.getAddress(), false);
+                        mState = MONITOR_STATE_IDLE;
+                        return false;
+                    }
+                } else {
+                    if (!mGattProfile.connect()) {
+                        mQAdapter.registerLppClient(this, mDevice.getAddress(), false);
+                        mState = MONITOR_STATE_IDLE;
+                        return false;
+                    }
+                }
+            } catch (IllegalStateException e) {
+                mQAdapter.registerLppClient(this, mDevice.getAddress(), false);
+                mState = MONITOR_STATE_IDLE;
+                return false;
+            }
+            setTimer(this.new ConnectTimeOutTask(), mTimeOutValue);
+        }
+        if (DBG) Log.d(TAG, "Monitor is starting");
+        return true;
+    }
+
+    /** @hide */
+    public boolean readRssiThreshold() {
+        if (DBG) Log.d(TAG, "readRssiThreshold()");
+        synchronized(mStateLock) {
+            if (mState == MONITOR_STATE_STARTED) {
+                if (mQAdapter != null) {
+                    mQAdapter.readRssiThreshold(this);
+                    return true;
+                }
+            }
+        }
+        if (DBG) Log.e(TAG, "readRssiThreshold() fail");
+        return false;
+    }
+
+    /** @hide */
+    public void stop() {
+        if (DBG) Log.d(TAG, "stop()");
+        synchronized(mStateLock) {
+            if(mDevice != null && mQAdapter != null && mGattProfile != null &&
+               (mState == MONITOR_STATE_STARTING ||
+                 mState == MONITOR_STATE_STARTED)) {
+                cancelTimer();
+                mQAdapter.enableRssiMonitor(this, false);
+                mGattProfile.disconnect();
+                mState = MONITOR_STATE_STOPPING;
+                setTimer(this.new DisconnectTimeOutTask(), mTimeOutValue);
+                mQAdapter.registerLppClient(this, mDevice.getAddress(), false);
+                if (DBG) Log.d(TAG, "Monitor is stopping");
+            }
+        }
+    }
+    /** @hide */
+    public void close() {
+        if (DBG) Log.d(TAG, "close()");
+        if(MONITOR_STATE_CLOSED == mState)
+            return;
+
+        synchronized(mStateLock) {
+            cancelTimer();
+            if(mDevice != null && mGattProfile != null && mQAdapter != null){
+               if(mState == MONITOR_STATE_STARTING ||
+                 mState == MONITOR_STATE_STARTED) {
+                    if(mState == MONITOR_STATE_STARTED)  mQAdapter.enableRssiMonitor(this, false);
+                    mGattProfile.disconnect();
+                }
+                mGattProfile.close();
+                mQAdapter.registerLppClient(this, mDevice.getAddress(), false);
+            }
+            if (DBG) Log.d(TAG, "Monitor is closed");
+            mState = MONITOR_STATE_CLOSED;
+            mDevice = null;
+            mQAdapter = null;
+            mGattProfile = null;
+            mMonitorCbk = null;
+        }
+    }
+    /** @hide */
+    public void onWriteRssiThreshold(int status) {
+        if (DBG) Log.d(TAG, "onWriteRssiThreshold() status=" + status);
+        synchronized (mStateLock) {
+            if (mState == MONITOR_STATE_STARTING){
+                if (status == BluetoothGatt.GATT_SUCCESS){
+                    if (mQAdapter != null) {
+                        mQAdapter.enableRssiMonitor(this, true);
+                    }
+                } else {
+                    if (mGattProfile != null) {
+                        mGattProfile.disconnect();
+                        mState = MONITOR_STATE_STOPPING;
+                        setTimer(this.new DisconnectTimeOutTask(), mTimeOutValue);
+                    }
+                }
+            }
+        }
+    }
+    /** @hide */
+    public void onReadRssiThreshold(int low, int upper, int alert, int status) {
+        if (DBG) Log.d(TAG, "onReadRssiThreshold() LowerLimit=" + low +
+                       ", UpperLimit=" + upper + ", Alert=" + alert + ", status=" + status);
+        if (mMonitorCbk != null) {
+            mMonitorCbk.onReadRssiThreshold(low, upper, alert, (status == 0)?COMMAND_STATUS_SUCCESS:COMMAND_STATUS_FAILED);
+        }
+    }
+    /** @hide */
+    public void onEnableRssiMonitor(int enable, int status) {
+        if (DBG) Log.d(TAG, "onEnableRssiMonitor() enable=" + enable + ", status=" + status);
+        synchronized(mStateLock) {
+            if (mState == MONITOR_STATE_STARTING) {
+                if (status == BluetoothGatt.GATT_SUCCESS && (enable != 0)) {
+                    mState = MONITOR_STATE_STARTED;
+                    if (DBG) Log.d(TAG, "Monitor is started successfully");
+                } else {
+                    if (mGattProfile != null) {
+                        mGattProfile.disconnect();
+                        mState = MONITOR_STATE_STOPPING;
+                        setTimer(this.new DisconnectTimeOutTask(), mTimeOutValue);
+                    }
+                }
+            }
+        }
+
+        if (mState == MONITOR_STATE_STARTED && mMonitorCbk != null) {
+            if (DBG) Log.d(TAG, "Notify users that monitor has been started successfully");
+            mMonitorCbk.onStarted();
+        }
+    }
+    /** @hide */
+    public void onRssiThresholdEvent(int evtType, int rssi) {
+        if (DBG) Log.d(TAG, "onRssiThresholdEvent() event=" + evtType + ", rssi=" + rssi);
+        if (mMonitorCbk != null) mMonitorCbk.onAlert(evtType, rssi);
+    }
+
+    /** @hide */
+    public boolean onUpdateLease() {
+        if (DBG) Log.d(TAG, "onUpdateLease()");
+        synchronized(mStateLock) {
+            return (mState != MONITOR_STATE_IDLE && mState != MONITOR_STATE_CLOSED);
+        }
+    }
+}
diff --git a/core/java/android/bluetooth/BluetoothRssiMonitorCallback.java b/core/java/android/bluetooth/BluetoothRssiMonitorCallback.java
new file mode 100644
index 0000000..41e0266
--- /dev/null
+++ b/core/java/android/bluetooth/BluetoothRssiMonitorCallback.java
@@ -0,0 +1,49 @@
+/*
+ * 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 "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 android.bluetooth;
+
+/** @hide */
+public abstract class BluetoothRssiMonitorCallback {
+    /** @hide */
+    public void onStarted() {
+    }
+
+    /** @hide */
+    public void onStopped() {
+    }
+
+    /** @hide */
+    public void onReadRssiThreshold(int thresh_min, int thresh_max, int alert, int status) {
+    }
+
+    /** @hide */
+    public void onAlert(int evtType, int rssi) {
+    }
+};
diff --git a/core/java/android/bluetooth/IBluetoothManager.aidl b/core/java/android/bluetooth/IBluetoothManager.aidl
index 493d2f8..93360b9 100644
--- a/core/java/android/bluetooth/IBluetoothManager.aidl
+++ b/core/java/android/bluetooth/IBluetoothManager.aidl
@@ -18,7 +18,9 @@
 
 import android.bluetooth.IBluetooth;
 import android.bluetooth.IBluetoothGatt;
+import android.bluetooth.IQBluetooth;
 import android.bluetooth.IBluetoothManagerCallback;
+import android.bluetooth.IQBluetoothManagerCallback;
 import android.bluetooth.IBluetoothStateChangeCallback;
 
 /**
@@ -29,7 +31,9 @@
 interface IBluetoothManager
 {
     IBluetooth registerAdapter(in IBluetoothManagerCallback callback);
+    IQBluetooth registerQAdapter(in IQBluetoothManagerCallback callback);
     void unregisterAdapter(in IBluetoothManagerCallback callback);
+    void unregisterQAdapter(in IQBluetoothManagerCallback callback);
     void registerStateChangeCallback(in IBluetoothStateChangeCallback callback);
     void unregisterStateChangeCallback(in IBluetoothStateChangeCallback callback);
     boolean isEnabled();
@@ -37,6 +41,7 @@
     boolean enableNoAutoConnect();
     boolean disable(boolean persist);
     IBluetoothGatt getBluetoothGatt();
+    IQBluetooth getQBluetooth();
 
     String getAddress();
     String getName();
diff --git a/core/java/android/bluetooth/IQBluetooth.aidl b/core/java/android/bluetooth/IQBluetooth.aidl
new file mode 100644
index 0000000..f6db263
--- /dev/null
+++ b/core/java/android/bluetooth/IQBluetooth.aidl
@@ -0,0 +1,51 @@
+/*
+ * 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 "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 android.bluetooth;
+
+import android.bluetooth.IBluetoothCallback;
+import android.bluetooth.IQBluetoothAdapterCallback;
+import android.bluetooth.IBluetooth;
+import android.bluetooth.IBluetoothStateChangeCallback;
+import android.bluetooth.BluetoothDevice;
+import android.os.ParcelUuid;
+import android.os.ParcelFileDescriptor;
+
+/**
+ * System private API for talking with the Bluetooth service.
+ *
+ * {@hide}
+ */
+interface IQBluetooth
+{
+    boolean registerLeLppRssiMonitorClient(in String address, in IQBluetoothAdapterCallback client, in boolean add);
+    void writeLeLppRssiThreshold(in String address, in byte min, in byte max);
+    void readLeLppRssiThreshold(in String address);
+    void enableLeLppRssiMonitor(in String address, in boolean enable);
+}
diff --git a/core/java/android/bluetooth/IQBluetoothAdapterCallback.aidl b/core/java/android/bluetooth/IQBluetoothAdapterCallback.aidl
new file mode 100644
index 0000000..b254d15
--- /dev/null
+++ b/core/java/android/bluetooth/IQBluetoothAdapterCallback.aidl
@@ -0,0 +1,46 @@
+/*
+ * 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 "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 android.bluetooth;
+
+
+
+/**
+ * Callback interface definition for interact with BLE/QAdapterService
+ * @hide
+ */
+
+interface IQBluetoothAdapterCallback {
+    void onWriteRssiThreshold(in String address, in int status);
+    void onReadRssiThreshold(in String address, in int low, in int upper,
+                             in int alert, in int status);
+    void onEnableRssiMonitor(in String address, in int enable, in int status);
+    void onRssiThresholdEvent(in String address, in int evtType, in int rssi);
+    boolean onUpdateLease();
+}
diff --git a/core/java/android/bluetooth/IQBluetoothManagerCallback.aidl b/core/java/android/bluetooth/IQBluetoothManagerCallback.aidl
new file mode 100644
index 0000000..f9983c3
--- /dev/null
+++ b/core/java/android/bluetooth/IQBluetoothManagerCallback.aidl
@@ -0,0 +1,42 @@
+/*
+ * 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 "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 android.bluetooth;
+
+import android.bluetooth.IQBluetooth;
+
+/**
+ * API for Communication between BluetoothAdapter and BluetoothManager
+ *
+ * {@hide}
+ */
+interface IQBluetoothManagerCallback {
+    void onQBluetoothServiceUp(in IQBluetooth QbluetoothService);
+    void onQBluetoothServiceDown();
+}
diff --git a/core/java/android/bluetooth/QBluetoothAdapter.java b/core/java/android/bluetooth/QBluetoothAdapter.java
new file mode 100644
index 0000000..401d0fc
--- /dev/null
+++ b/core/java/android/bluetooth/QBluetoothAdapter.java
@@ -0,0 +1,399 @@
+/*
+ * 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 "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 android.bluetooth;
+
+import android.annotation.SdkConstant;
+import android.annotation.SdkConstant.SdkConstantType;
+import android.content.Context;
+import android.os.Binder;
+import android.os.IBinder;
+import android.os.Message;
+import android.os.ParcelUuid;
+import android.os.RemoteException;
+import android.os.ServiceManager;
+import android.bluetooth.IQBluetoothAdapterCallback;
+import android.util.Log;
+import android.util.Pair;
+
+import java.io.IOException;
+import java.lang.ref.WeakReference;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.HashSet;
+import java.util.HashMap;
+import java.util.LinkedList;
+import java.util.Map;
+import java.util.Random;
+import java.util.Set;
+import java.util.UUID;
+
+/**
+ * Represents the local device BLE adapter. The {@link QBluetoothAdapter}
+ * lets you perform fundamental Bluetooth tasks,
+ *  such as register and remove LPP clients.
+ *
+ * <p>To get a {@link QBluetoothAdapter} representing this specific Bluetooth
+ * adapter, call the
+ * static {@link #getDefaultAdapter} method; when running on JELLY_BEAN_MR2 and
+ * higher. Once you have the local adapter, you can register for LPP clients
+ * and receive alerts based on proximity of the remote device
+ */
+ /** @hide */
+public final class QBluetoothAdapter {
+    private static final String TAG = "QBluetoothAdapter";
+    private static final boolean DBG = false;
+    private static final boolean VDBG = false;
+
+    private static QBluetoothAdapter sAdapter;
+    private static BluetoothAdapter mAdapter;
+
+    private final IBluetoothManager mManagerService;
+    private IBluetooth mService;
+    private IQBluetooth mQService;
+
+    private final Map<LeLppCallback, LeLppClientWrapper> mLppClients = new HashMap<LeLppCallback, LeLppClientWrapper>();
+    /**
+     * Get a handle to the default local QBluetooth adapter.
+     * <p>Currently Android only supports one QBluetooth adapter, but the API
+     * could be extended to support more. This will always return the default
+     * adapter.
+     * @return the default local adapter, or null if Bluetooth is not supported
+     *         on this hardware platform
+     */
+    public static synchronized QBluetoothAdapter getDefaultAdapter() {
+        mAdapter=BluetoothAdapter.getDefaultAdapter();
+        IBluetoothManager managerService=mAdapter.getBluetoothManager();
+        sAdapter = new QBluetoothAdapter(managerService);
+        return sAdapter;
+    }
+
+    /**
+     * Use {@link #getDefaultAdapter} to get the BluetoothAdapter instance.
+     */
+    QBluetoothAdapter(IBluetoothManager managerService){
+        if (managerService == null) {
+            throw new IllegalArgumentException("bluetooth manager service is null");
+        }
+        try {
+            //mService = managerService.registerAdapter(mManagerCallback);
+            mService=mAdapter.getBluetoothService(mAdapterServiceCallback);
+           //mQService=managerService.getQBluetooth();
+           mQService=managerService.registerQAdapter(mManagerCallback);
+           Log.i(TAG,"mQService= :" + mQService);
+        } catch (RemoteException e) {Log.e(TAG, "", e);}
+        mManagerService = managerService;
+        /* read property value to check if the bluetooth controller support le extended scan */
+     }
+
+    public interface LeLppCallback {
+        public void onWriteRssiThreshold(int status);
+
+        public void onReadRssiThreshold(int low,int upper, int alert, int status);
+
+        public void onEnableRssiMonitor(int enable, int status);
+
+        public void onRssiThresholdEvent(int evtType, int rssi);
+
+        public boolean onUpdateLease();
+    };
+
+    /** @hide */
+    public boolean registerLppClient(LeLppCallback client, String address, boolean add) {
+        synchronized(mLppClients) {
+            if (add) {
+                if(mLppClients.containsKey(client)) {
+                    Log.e(TAG, "Lpp Client has been already registered");
+                    return false;
+                }
+
+                LeLppClientWrapper wrapper = new LeLppClientWrapper(this, mQService, address, client);
+                if(wrapper != null && wrapper.register2(true)) {
+                    mLppClients.put(client, wrapper);
+                    return true;
+                }
+                return false;
+            } else {
+                LeLppClientWrapper wrapper = mLppClients.remove(client);
+                if (wrapper != null) {
+                    wrapper.register2(false);
+                    return true;
+                }
+                return false;
+            }
+        }
+    }
+
+   /**
+     * Write the rssi threshold for a connected remote device.
+     *
+     * <p>The {@link BluetoothGattCallback#onWriteRssiThreshold} callback will be
+     * invoked when the write request has been sent.
+     *
+     * <p>Requires {@link android.Manifest.permission#BLUETOOTH} permission.
+     *
+     * @param min    The lower limit of rssi threshold
+     * @param max    The upper limit of rssi threshold
+     * @return true, if the rssi threshold writing request has been sent tsuccessfully
+     */
+    /** @hide */
+    public boolean writeRssiThreshold(LeLppCallback client, int min, int max) {
+        LeLppClientWrapper wrapper = null;
+        synchronized(mLppClients) {
+            wrapper = mLppClients.get(client);
+        }
+        if (wrapper == null) return false;
+
+        wrapper.writeRssiThreshold((byte)min, (byte)max);
+        return true;
+    }
+
+    /**
+     * Enable rssi monitoring for a connected remote device.
+     *
+     * <p>The {@link BluetoothGattCallback#onEnableRssiMonitor} callback will be
+     * invoked when the rssi monitor enable/disable request has been sent.
+     *
+     * <p>Requires {@link android.Manifest.permission#BLUETOOTH} permission.
+     *
+     * @param enable disable/enable rssi monitor
+     * @return true, if the rssi monitoring request has been sent tsuccessfully
+     */
+     /** @hide */
+    public boolean enableRssiMonitor(LeLppCallback client, boolean enable){
+        LeLppClientWrapper wrapper = null;
+        synchronized(mLppClients) {
+            wrapper = mLppClients.get(client);
+        }
+        if (wrapper == null) return false;
+
+        wrapper.enableMonitor(enable);
+        return true;
+    }
+
+   /**
+     * Read the rssi threshold for a connected remote device.
+     *
+     * <p>The {@link BluetoothGattCallback#onReadRssiThreshold} callback will be
+     * invoked when the rssi threshold has been read.
+     *
+     * <p>Requires {@link android.Manifest.permission#BLUETOOTH} permission.
+     *
+     * @return true, if the rssi threshold has been requested successfully
+     */
+     /** @hide */
+    public boolean readRssiThreshold(LeLppCallback client) {
+        LeLppClientWrapper wrapper = null;
+        synchronized(mLppClients) {
+            wrapper = mLppClients.get(client);
+        }
+        if (wrapper == null) return false;
+
+        wrapper.readRssiThreshold();
+        return true;
+    }
+
+    protected void finalize() throws Throwable {
+        try {
+            mManagerService.unregisterQAdapter(mManagerCallback);
+        } catch (RemoteException e) {
+            Log.e(TAG, "", e);
+        } finally {
+            super.finalize();
+        }
+    }
+
+    private static class LeLppClientWrapper extends IQBluetoothAdapterCallback.Stub {
+            private final WeakReference<QBluetoothAdapter> mAdapter;
+            private final IQBluetooth mQBluetoothAdapterService;
+            private final String mDevice;
+            private final LeLppCallback client;
+
+            public LeLppClientWrapper (QBluetoothAdapter adapter, IQBluetooth adapterService,
+                                       String address, LeLppCallback callback) {
+                this.mAdapter = new WeakReference<QBluetoothAdapter> (adapter);
+                this.mQBluetoothAdapterService = adapterService;
+                this.mDevice = address;
+                this.client = callback;
+            }
+
+            public boolean register2(boolean add) {
+                if(mQBluetoothAdapterService != null) {
+                    try {
+                        return mQBluetoothAdapterService.registerLeLppRssiMonitorClient(mDevice, this, add);
+                    } catch (RemoteException e) {
+                        Log.w(TAG, "", e);
+                    }
+                }
+                return false;
+            }
+
+            public void writeRssiThreshold(byte min, byte max) {
+                if(mQBluetoothAdapterService != null) {
+                    try {
+                        mQBluetoothAdapterService.writeLeLppRssiThreshold(mDevice, min, max);
+                    } catch (RemoteException e) {
+                        Log.w(TAG, "", e);
+                    }
+                }
+            }
+
+            public void enableMonitor(boolean enable) {
+                if(mQBluetoothAdapterService != null) {
+                    try {
+                        mQBluetoothAdapterService.enableLeLppRssiMonitor(mDevice, enable);
+                    } catch (RemoteException e) {
+                        Log.w(TAG, "", e);
+                    }
+                }
+            }
+
+            public void readRssiThreshold() {
+                if(mQBluetoothAdapterService != null) {
+                    try {
+                        mQBluetoothAdapterService.readLeLppRssiThreshold(mDevice);
+                    } catch (RemoteException e) {
+                        Log.w(TAG, "", e);
+                    }
+                }
+            }
+
+           /**
+             * Rssi threshold has been written
+             * @hide
+             */
+            public void onWriteRssiThreshold(String address, int status){
+                if (client == null) {
+                    return;
+                }
+                try {
+                    client.onWriteRssiThreshold(status);
+                } catch (Exception ex) {
+                    Log.w(TAG, "Unhandled exception: " + ex);
+                }
+            }
+
+            /**
+             * RSSI threshold has been read
+             * @hide
+             */
+            public void onReadRssiThreshold(String address, int low, int upper,
+                                            int alert, int status){
+                if (client == null) {
+                    return;
+                }
+                try {
+                    client.onReadRssiThreshold(low, upper, alert, status);
+                } catch (Exception ex) {
+                    Log.w(TAG, "Unhandled exception: " + ex);
+                }
+            }
+
+           /**
+             * Remote Device RSSI monitoring has been enabled/disabled
+             * @hide
+             */
+            public void onEnableRssiMonitor(String address, int enable, int status){
+                if (client == null) {
+                    return;
+                }
+                try {
+                    client.onEnableRssiMonitor(enable, status);
+                } catch (Exception ex) {
+                    Log.w(TAG, "Unhandled exception: " + ex);
+                }
+            }
+
+            /**
+             * RSSI threshold event reported
+             * @hide
+             */
+            public void onRssiThresholdEvent(String address, int evtType, int rssi){
+                if (client == null) {
+                    return;
+                }
+                try {
+                    client.onRssiThresholdEvent(evtType, rssi);
+                } catch (Exception ex) {
+                    Log.w(TAG, "Unhandled exception: " + ex);
+                }
+            }
+
+            public boolean onUpdateLease() {
+                if (client == null) return false;
+                try {
+                    return client.onUpdateLease();
+                } catch (Exception ex) {
+                    Log.w(TAG, "Unhandled exception: " + ex);
+                    return false;
+                }
+            }
+    }
+
+    final private IBluetoothManagerCallback mAdapterServiceCallback =
+        new IBluetoothManagerCallback.Stub() {
+            public void onBluetoothServiceUp(IBluetooth bluetoothService) {
+                synchronized (mAdapterServiceCallback) {
+                    //initialize the global params again
+                    mService = bluetoothService;
+                    Log.i(TAG,"onBluetoothServiceUp Adapter ON: mService: "+ mService + " mQService: " + mQService+ " ManagerService:" + mManagerService);
+                }
+            }
+
+            public void onBluetoothServiceDown() {
+                synchronized (mAdapterServiceCallback) {
+                    mService = null;
+                    Log.i(TAG,"onBluetoothServiceDown Adapter OFF: mService: "+ mService + " mQService: " + mQService);
+                }
+            }
+    };
+
+
+    final private IQBluetoothManagerCallback mManagerCallback =
+        new IQBluetoothManagerCallback.Stub() {
+            public void onQBluetoothServiceUp(IQBluetooth qcbluetoothService) {
+                if (VDBG) Log.i(TAG, "on QBluetoothServiceUp: " + qcbluetoothService);
+                synchronized (mManagerCallback) {
+                    //initialize the global params again
+                    mQService = qcbluetoothService;
+                    Log.i(TAG,"onQBluetoothServiceUp: Adapter ON: mService: "+ mService + " mQService: " + mQService+ " ManagerService:" + mManagerService);
+                }
+            }
+
+            public void onQBluetoothServiceDown() {
+                if (VDBG) Log.i(TAG, "onQBluetoothServiceDown: " + mService);
+                synchronized (mManagerCallback) {
+                    mQService=null;
+                    Log.i(TAG,"onQBluetoothServiceDown: Adapter OFF: mService: "+ mService + " mQService: " + mQService);
+                }
+            }
+    };
+
+}
diff --git a/services/core/java/com/android/server/BluetoothManagerService.java b/services/core/java/com/android/server/BluetoothManagerService.java
old mode 100644
new mode 100755
index 4630331..78e5358
--- a/services/core/java/com/android/server/BluetoothManagerService.java
+++ b/services/core/java/com/android/server/BluetoothManagerService.java
@@ -19,10 +19,12 @@
 import android.app.ActivityManager;
 import android.bluetooth.BluetoothAdapter;
 import android.bluetooth.IBluetooth;
+import android.bluetooth.IQBluetooth;
 import android.bluetooth.IBluetoothGatt;
 import android.bluetooth.IBluetoothCallback;
 import android.bluetooth.IBluetoothManager;
 import android.bluetooth.IBluetoothManagerCallback;
+import android.bluetooth.IQBluetoothManagerCallback;
 import android.bluetooth.IBluetoothStateChangeCallback;
 import android.content.BroadcastReceiver;
 import android.content.ComponentName;
@@ -32,6 +34,7 @@
 import android.content.IntentFilter;
 import android.content.ServiceConnection;
 import android.content.pm.PackageManager;
+import android.app.AppOpsManager;
 import android.os.Binder;
 import android.os.Handler;
 import android.os.IBinder;
@@ -68,6 +71,8 @@
     private static final int MESSAGE_DISABLE = 2;
     private static final int MESSAGE_REGISTER_ADAPTER = 20;
     private static final int MESSAGE_UNREGISTER_ADAPTER = 21;
+    private static final int MESSAGE_REGISTER_Q_ADAPTER = 22;
+    private static final int MESSAGE_UNREGISTER_Q_ADAPTER = 23;
     private static final int MESSAGE_REGISTER_STATE_CHANGE_CALLBACK = 30;
     private static final int MESSAGE_UNREGISTER_STATE_CHANGE_CALLBACK = 31;
     private static final int MESSAGE_BLUETOOTH_SERVICE_CONNECTED = 40;
@@ -94,6 +99,7 @@
 
     private static final int SERVICE_IBLUETOOTH = 1;
     private static final int SERVICE_IBLUETOOTHGATT = 2;
+    private static final int SERVICE_IBLUETOOTHQ = 3;
 
     private final Context mContext;
 
@@ -103,8 +109,10 @@
     private String mName;
     private final ContentResolver mContentResolver;
     private final RemoteCallbackList<IBluetoothManagerCallback> mCallbacks;
+    private final RemoteCallbackList<IQBluetoothManagerCallback> mQCallbacks;
     private final RemoteCallbackList<IBluetoothStateChangeCallback> mStateChangeCallbacks;
     private IBluetooth mBluetooth;
+    private IQBluetooth mQBluetooth;
     private IBluetoothGatt mBluetoothGatt;
     private boolean mBinding;
     private boolean mUnbinding;
@@ -122,6 +130,7 @@
     private final BluetoothHandler mHandler;
     private int mErrorRecoveryRetryCounter;
     private final int mSystemUiUid;
+    private boolean mIsBluetoothServiceConnected = false;
 
     private void registerForAirplaneMode(IntentFilter filter) {
         final ContentResolver resolver = mContext.getContentResolver();
@@ -197,6 +206,7 @@
 
         mContext = context;
         mBluetooth = null;
+        mQBluetooth = null;
         mBinding = false;
         mUnbinding = false;
         mEnable = false;
@@ -208,6 +218,7 @@
         mErrorRecoveryRetryCounter = 0;
         mContentResolver = context.getContentResolver();
         mCallbacks = new RemoteCallbackList<IBluetoothManagerCallback>();
+        mQCallbacks = new RemoteCallbackList<IQBluetoothManagerCallback>();
         mStateChangeCallbacks = new RemoteCallbackList<IBluetoothStateChangeCallback>();
         IntentFilter filter = new IntentFilter(Intent.ACTION_BOOT_COMPLETED);
         filter.addAction(BluetoothAdapter.ACTION_LOCAL_NAME_CHANGED);
@@ -342,6 +353,22 @@
         mHandler.sendMessage(msg);
     }
 
+    public IQBluetooth registerQAdapter(IQBluetoothManagerCallback callback){
+        Message msg = mHandler.obtainMessage(MESSAGE_REGISTER_Q_ADAPTER);
+        msg.obj = callback;
+        mHandler.sendMessage(msg);
+        synchronized(mConnection) {
+            return mQBluetooth;
+        }
+    }
+
+    public void unregisterQAdapter(IQBluetoothManagerCallback callback) {
+        mContext.enforceCallingOrSelfPermission(BLUETOOTH_PERM,
+                                                "Need BLUETOOTH permission");
+        Message msg = mHandler.obtainMessage(MESSAGE_UNREGISTER_Q_ADAPTER);
+        msg.obj = callback;
+        mHandler.sendMessage(msg);
+    }
     public void registerStateChangeCallback(IBluetoothStateChangeCallback callback) {
         mContext.enforceCallingOrSelfPermission(BLUETOOTH_PERM,
                                                 "Need BLUETOOTH permission");
@@ -480,6 +507,7 @@
                 }
                 if (DBG) Log.d(TAG, "Sending unbind request.");
                 mBluetooth = null;
+                mQBluetooth = null;
                 //Unbind
                 mContext.unbindService(mConnection);
                 mUnbinding = false;
@@ -495,6 +523,11 @@
         return mBluetoothGatt;
     }
 
+    public IQBluetooth getQBluetooth() {
+        // sync protection
+        return mQBluetooth;
+    }
+
     private void sendBluetoothStateCallback(boolean isUp) {
         int n = mStateChangeCallbacks.beginBroadcast();
         if (DBG) Log.d(TAG,"Broadcasting onBluetoothStateChange("+isUp+") to " + n + " receivers.");
@@ -544,6 +577,39 @@
             mCallbacks.finishBroadcast();
         }
     }
+
+    /**
+     * Inform QBluetoothAdapter instances that QAdapter service is up
+     */
+    private void sendQBluetoothServiceUpCallback() {
+        if (DBG) Log.d(TAG,"Calling onQBluetoothServiceUp callbacks");
+        int n = mQCallbacks.beginBroadcast();
+        Log.d(TAG,"Broadcasting onQBluetoothServiceUp() to " + n + " receivers.");
+        for (int i=0; i <n;i++) {
+            try {
+                mQCallbacks.getBroadcastItem(i).onQBluetoothServiceUp(mQBluetooth);
+            }  catch (RemoteException e) {
+                Log.e(TAG, "Unable to call onQBluetoothServiceUp() on callback #" + i, e);
+            }
+        }
+        mQCallbacks.finishBroadcast();
+    }
+    /**
+     * Inform BluetoothAdapter instances that Adapter service is down
+     */
+    private void sendQBluetoothServiceDownCallback() {
+        if (DBG) Log.d(TAG,"Calling onQBluetoothServiceDown callbacks");
+        int n = mQCallbacks.beginBroadcast();
+        Log.d(TAG,"Broadcasting onQBluetoothServiceDown() to " + n + " receivers.");
+        for (int i=0; i <n;i++) {
+            try {
+                mQCallbacks.getBroadcastItem(i).onQBluetoothServiceDown();
+            }  catch (RemoteException e) {
+                Log.e(TAG, "Unable to call onQBluetoothServiceDown() on callback #" + i, e);
+            }
+        }
+            mQCallbacks.finishBroadcast();
+    }
     public String getAddress() {
         mContext.enforceCallingOrSelfPermission(BLUETOOTH_PERM,
                                                 "Need BLUETOOTH permission");
@@ -615,6 +681,9 @@
                 // } else if (className.getClassName().equals(IBluetoothGatt.class.getName())) {
             } else if (className.getClassName().equals("com.android.bluetooth.gatt.GattService")) {
                 msg.arg1 = SERVICE_IBLUETOOTHGATT;
+            } else if (className.getClassName().equals("com.android.bluetooth.btservice.QAdapterService")) {
+                msg.arg1 = SERVICE_IBLUETOOTHQ;
+                Log.e(TAG, "Bluetooth Q service connected: " + className.getClassName());
             } else {
                 Log.e(TAG, "Unknown service connected: " + className.getClassName());
                 return;
@@ -632,6 +701,8 @@
                 msg.arg1 = SERVICE_IBLUETOOTH;
             } else if (className.getClassName().equals("com.android.bluetooth.gatt.GattService")) {
                 msg.arg1 = SERVICE_IBLUETOOTHGATT;
+            } else if (className.getClassName().equals("com.android.bluetooth.btservice.QAdapterService")) {
+                msg.arg1 = SERVICE_IBLUETOOTHQ;
             } else {
                 Log.e(TAG, "Unknown service disconnected: " + className.getClassName());
                 return;
@@ -776,6 +847,14 @@
                     Log.d(TAG,"Added callback: " +  (callback == null? "null": callback)  +":" +added );
                 }
                     break;
+
+                case MESSAGE_REGISTER_Q_ADAPTER:
+                {
+                    IQBluetoothManagerCallback callback = (IQBluetoothManagerCallback) msg.obj;
+                    boolean added = mQCallbacks.register(callback);
+                    Log.d(TAG,"Q Added callback: " +  (callback == null? "null": callback)  +":" +added );
+                }
+                    break;
                 case MESSAGE_UNREGISTER_ADAPTER:
                 {
                     IBluetoothManagerCallback callback = (IBluetoothManagerCallback) msg.obj;
@@ -783,6 +862,13 @@
                     Log.d(TAG,"Removed callback: " +  (callback == null? "null": callback)  +":" + removed);
                     break;
                 }
+                case MESSAGE_UNREGISTER_Q_ADAPTER:
+                {
+                    IQBluetoothManagerCallback callback = (IQBluetoothManagerCallback) msg.obj;
+                    boolean removed = mQCallbacks.unregister(callback);
+                    Log.d(TAG,"Q Removed callback: " +  (callback == null? "null": callback)  +":" + removed);
+                    break;
+                }
                 case MESSAGE_REGISTER_STATE_CHANGE_CALLBACK:
                 {
                     IBluetoothStateChangeCallback callback = (IBluetoothStateChangeCallback) msg.obj;
@@ -803,6 +889,8 @@
                 {
                     if (DBG) Log.d(TAG,"MESSAGE_BLUETOOTH_SERVICE_CONNECTED: " + msg.arg1);
 
+                    mIsBluetoothServiceConnected = true;
+
                     IBinder service = (IBinder) msg.obj;
                     synchronized(mConnection) {
                         if (msg.arg1 == SERVICE_IBLUETOOTHGATT) {
@@ -810,6 +898,13 @@
                             break;
                         } // else must be SERVICE_IBLUETOOTH
 
+                        if (msg.arg1 == SERVICE_IBLUETOOTHQ) {
+                            mQBluetooth = IQBluetooth.Stub.asInterface(service);
+                            Log.d(TAG,"mQBluetooth: " + mQBluetooth);
+                            sendQBluetoothServiceUpCallback();
+                            break;
+                        } // else must be SERVICE_IBLUETOOTH
+
                         //Remove timeout
                         mHandler.removeMessages(MESSAGE_TIMEOUT_BIND);
 
@@ -901,6 +996,9 @@
                 case MESSAGE_BLUETOOTH_SERVICE_DISCONNECTED:
                 {
                     Log.e(TAG, "MESSAGE_BLUETOOTH_SERVICE_DISCONNECTED: " + msg.arg1);
+
+                    mIsBluetoothServiceConnected = false;
+
                     synchronized(mConnection) {
                         if (msg.arg1 == SERVICE_IBLUETOOTH) {
                             // if service is unbinded already, do nothing and return
@@ -909,6 +1007,9 @@
                         } else if (msg.arg1 == SERVICE_IBLUETOOTHGATT) {
                             mBluetoothGatt = null;
                             break;
+                        } else if (msg.arg1 == SERVICE_IBLUETOOTHQ) {
+                            mQBluetooth = null;
+                            break;
                         } else {
                             Log.e(TAG, "Bad msg.arg1: " + msg.arg1);
                             break;
@@ -926,6 +1027,7 @@
 
                     if (!mConnection.isGetNameAddressOnly()) {
                         sendBluetoothServiceDownCallback();
+                        sendQBluetoothServiceDownCallback();
 
                         // Send BT state broadcast to update
                         // the BT icon correctly
@@ -1012,6 +1114,7 @@
                         bluetoothStateChangeHandler(BluetoothAdapter.STATE_TURNING_OFF,
                                                     BluetoothAdapter.STATE_OFF);
                         sendBluetoothServiceDownCallback();
+                        sendQBluetoothServiceDownCallback();
                         synchronized (mConnection) {
                             if (mBluetooth != null) {
                                 mBluetooth = null;
@@ -1157,11 +1260,16 @@
                         Intent i = new Intent(IBluetoothGatt.class.getName());
                         doBind(i, mConnection, Context.BIND_AUTO_CREATE | Context.BIND_IMPORTANT,
                                 UserHandle.CURRENT);
+
+                        Intent iqc = new Intent(IQBluetooth.class.getName());
+                        doBind(iqc, mConnection, Context.BIND_AUTO_CREATE | Context.BIND_IMPORTANT,
+                                UserHandle.CURRENT);
                     }
                 } else {
                     //If Bluetooth is off, send service down event to proxy objects, and unbind
                     if (!isUp && canUnbindBluetoothService()) {
                         sendBluetoothServiceDownCallback();
+                        sendQBluetoothServiceDownCallback();
                         unbindAndFinish();
                     }
                 }
@@ -1261,9 +1369,12 @@
         waitForOnOff(false, true);
 
         sendBluetoothServiceDownCallback();
+        sendQBluetoothServiceDownCallback();
         synchronized (mConnection) {
             if (mBluetooth != null) {
                 mBluetooth = null;
+                if(mQBluetooth!=null)
+                    mQBluetooth=null;
                 //Unbind
                 mContext.unbindService(mConnection);
             }