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);
}