Bluetooth LE background operation mode (2/2)
Changes include new framework APIs to enable and disable Bluetooth LE separately from Bluetooth Classic. Along with handling the new states in the Bluetooth manager service. Change-Id: Idf667981f48fcbcb6dfda1aa77ea8bab1b2361f0
This commit is contained in:
committed by
Andre Eisenbach
parent
104b0f1353
commit
6ddbb5e55f
@@ -34,6 +34,9 @@ import android.os.Looper;
|
||||
import android.os.ParcelUuid;
|
||||
import android.os.RemoteException;
|
||||
import android.os.ServiceManager;
|
||||
import android.app.ActivityThread;
|
||||
import android.os.SystemProperties;
|
||||
import android.os.Binder;
|
||||
import android.util.Log;
|
||||
import android.util.Pair;
|
||||
|
||||
@@ -121,6 +124,9 @@ public final class BluetoothAdapter {
|
||||
* {@link #STATE_TURNING_ON},
|
||||
* {@link #STATE_ON},
|
||||
* {@link #STATE_TURNING_OFF},
|
||||
* {@link #STATE_BLE_TURNING_ON},
|
||||
* {@link #STATE_BLE_ON},
|
||||
* {@link #STATE_BLE_TURNING_OFF},
|
||||
*/
|
||||
public static final String EXTRA_STATE =
|
||||
"android.bluetooth.adapter.extra.STATE";
|
||||
@@ -131,6 +137,9 @@ public final class BluetoothAdapter {
|
||||
* {@link #STATE_TURNING_ON},
|
||||
* {@link #STATE_ON},
|
||||
* {@link #STATE_TURNING_OFF},
|
||||
* {@link #STATE_BLE_TURNING_ON},
|
||||
* {@link #STATE_BLE_ON},
|
||||
* {@link #STATE_BLE_TURNING_OFF},
|
||||
*/
|
||||
public static final String EXTRA_PREVIOUS_STATE =
|
||||
"android.bluetooth.adapter.extra.PREVIOUS_STATE";
|
||||
@@ -155,6 +164,24 @@ public final class BluetoothAdapter {
|
||||
*/
|
||||
public static final int STATE_TURNING_OFF = 13;
|
||||
|
||||
/**
|
||||
* Indicates the local Bluetooth adapter is turning Bluetooth LE mode on.
|
||||
* @hide
|
||||
*/
|
||||
public static final int STATE_BLE_TURNING_ON = 14;
|
||||
|
||||
/**
|
||||
* Indicates the local Bluetooth adapter is in LE only mode.
|
||||
* @hide
|
||||
*/
|
||||
public static final int STATE_BLE_ON = 15;
|
||||
|
||||
/**
|
||||
* Indicates the local Bluetooth adapter is turning off LE only mode.
|
||||
* @hide
|
||||
*/
|
||||
public static final int STATE_BLE_TURNING_OFF = 16;
|
||||
|
||||
/**
|
||||
* Activity Action: Show a system activity that requests discoverable mode.
|
||||
* This activity will also request the user to turn on Bluetooth if it
|
||||
@@ -366,6 +393,39 @@ public final class BluetoothAdapter {
|
||||
public static final String EXTRA_PREVIOUS_CONNECTION_STATE =
|
||||
"android.bluetooth.adapter.extra.PREVIOUS_CONNECTION_STATE";
|
||||
|
||||
/**
|
||||
* Broadcast Action: The Bluetooth adapter state has changed in LE only mode.
|
||||
* @hide
|
||||
*/
|
||||
public static final String ACTION_BLE_STATE_CHANGED =
|
||||
"anrdoid.bluetooth.adapter.action.BLE_STATE_CHANGED";
|
||||
|
||||
/**
|
||||
* Broadcast Action: The notifys Bluetooth ACL connected event. This will be
|
||||
* by BLE Always on enabled application to know the ACL_CONNECTED event
|
||||
* when Bluetooth state in STATE_BLE_ON. This denotes GATT connection
|
||||
* as Bluetooth LE is the only feature available in STATE_BLE_ON
|
||||
*
|
||||
* This is counterpart of {@link BluetoothDevice#ACTION_ACL_CONNECTED} which
|
||||
* works in Bluetooth state STATE_ON
|
||||
* @hide
|
||||
*/
|
||||
public static final String ACTION_BLE_ACL_CONNECTED =
|
||||
"android.bluetooth.adapter.action.BLE_ACL_CONNECTED";
|
||||
|
||||
/**
|
||||
* Broadcast Action: The notifys Bluetooth ACL connected event. This will be
|
||||
* by BLE Always on enabled application to know the ACL_DISCONNECTED event
|
||||
* when Bluetooth state in STATE_BLE_ON. This denotes GATT disconnection as Bluetooth
|
||||
* LE is the only feature available in STATE_BLE_ON
|
||||
*
|
||||
* This is counterpart of {@link BluetoothDevice#ACTION_ACL_DISCONNECTED} which
|
||||
* works in Bluetooth state STATE_ON
|
||||
* @hide
|
||||
*/
|
||||
public static final String ACTION_BLE_ACL_DISCONNECTED =
|
||||
"android.bluetooth.adapter.action.BLE_ACL_DISCONNECTED";
|
||||
|
||||
/** The profile is in disconnected state */
|
||||
public static final int STATE_DISCONNECTED = 0;
|
||||
/** The profile is in connecting state */
|
||||
@@ -377,6 +437,7 @@ public final class BluetoothAdapter {
|
||||
|
||||
/** @hide */
|
||||
public static final String BLUETOOTH_MANAGER_SERVICE = "bluetooth_manager";
|
||||
private final IBinder mToken;
|
||||
|
||||
|
||||
/** When creating a ServerSocket using listenUsingRfcommOn() or
|
||||
@@ -447,6 +508,7 @@ public final class BluetoothAdapter {
|
||||
} catch (RemoteException e) {Log.e(TAG, "", e);}
|
||||
mManagerService = managerService;
|
||||
mLeScanClients = new HashMap<LeScanCallback, ScanCallback>();
|
||||
mToken = new Binder();
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -493,11 +555,9 @@ public final class BluetoothAdapter {
|
||||
* on this device before calling this method.
|
||||
*/
|
||||
public BluetoothLeAdvertiser getBluetoothLeAdvertiser() {
|
||||
if (getState() != STATE_ON) {
|
||||
return null;
|
||||
}
|
||||
if (!getLeAccess()) return null;
|
||||
if (!isMultipleAdvertisementSupported() && !isPeripheralModeSupported()) {
|
||||
Log.e(TAG, "bluetooth le advertising not supported");
|
||||
Log.e(TAG, "Bluetooth LE advertising not supported");
|
||||
return null;
|
||||
}
|
||||
synchronized(mLock) {
|
||||
@@ -512,9 +572,7 @@ public final class BluetoothAdapter {
|
||||
* Returns a {@link BluetoothLeScanner} object for Bluetooth LE scan operations.
|
||||
*/
|
||||
public BluetoothLeScanner getBluetoothLeScanner() {
|
||||
if (getState() != STATE_ON) {
|
||||
return null;
|
||||
}
|
||||
if (!getLeAccess()) return null;
|
||||
synchronized(mLock) {
|
||||
if (sBluetoothLeScanner == null) {
|
||||
sBluetoothLeScanner = new BluetoothLeScanner(mManagerService);
|
||||
@@ -532,7 +590,6 @@ public final class BluetoothAdapter {
|
||||
* @return true if the local adapter is turned on
|
||||
*/
|
||||
public boolean isEnabled() {
|
||||
|
||||
try {
|
||||
synchronized(mManagerCallback) {
|
||||
if (mService != null) return mService.isEnabled();
|
||||
@@ -541,6 +598,178 @@ public final class BluetoothAdapter {
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return true if Bluetooth LE(Always BLE On feature) is currently
|
||||
* enabled and ready for use
|
||||
* <p>This returns true if current state is either STATE_ON or STATE_BLE_ON
|
||||
*
|
||||
* @return true if the local Bluetooth LE adapter is turned on
|
||||
* @hide
|
||||
*/
|
||||
public boolean isLeEnabled() {
|
||||
final int state = getLeState();
|
||||
if (state == BluetoothAdapter.STATE_ON) {
|
||||
if (DBG) Log.d (TAG, "STATE_ON");
|
||||
} else if (state == BluetoothAdapter.STATE_BLE_ON) {
|
||||
if (DBG) Log.d (TAG, "STATE_BLE_ON");
|
||||
} else {
|
||||
if (DBG) Log.d (TAG, "STATE_OFF");
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Performs action based on user action to turn BT ON
|
||||
* or OFF if BT is in BLE_ON state
|
||||
*/
|
||||
private void notifyUserAction(boolean enable) {
|
||||
if (mService == null) {
|
||||
Log.e(TAG, "mService is null");
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
if (enable) {
|
||||
mService.onLeServiceUp(); //NA:TODO implementation pending
|
||||
} else {
|
||||
mService.onBrEdrDown(); //NA:TODO implementation pending
|
||||
}
|
||||
} catch (RemoteException e) {
|
||||
Log.e(TAG, "", e);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns true if LE only mode is enabled, that is apps
|
||||
* have authorization to turn only BT ON and the calling
|
||||
* app has privilage to do so
|
||||
*/
|
||||
private boolean isLEAlwaysOnEnabled() {
|
||||
boolean ret = false;
|
||||
if (SystemProperties.getBoolean("ro.bluetooth.blealwayson", true) == true) {
|
||||
Log.v(TAG, "LE always on mode is enabled");
|
||||
// TODO: System API authorization check
|
||||
ret = true;
|
||||
} else {
|
||||
Log.v(TAG, "LE always on mode is disabled");
|
||||
ret = false;
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
/**
|
||||
* Turns off Bluetooth LE which was earlier turned on by calling EnableBLE().
|
||||
*
|
||||
* <p> If the internal Adapter state is STATE_BLE_ON, this would trigger the transition
|
||||
* to STATE_OFF and completely shut-down Bluetooth
|
||||
*
|
||||
* <p> If the Adapter state is STATE_ON, This would unregister the existance of
|
||||
* special Bluetooth LE application and hence the further turning off of Bluetooth
|
||||
* from UI would ensure the complete turn-off of Bluetooth rather than staying back
|
||||
* BLE only state
|
||||
*
|
||||
* <p>This is an asynchronous call: it will return immediately, and
|
||||
* clients should listen for {@link #ACTION_BLE_STATE_CHANGED}
|
||||
* to be notified of subsequent adapter state changes If this call returns
|
||||
* true, then the adapter state will immediately transition from {@link
|
||||
* #STATE_ON} to {@link #STATE_TURNING_OFF}, and some time
|
||||
* later transition to either {@link #STATE_BLE_ON} or {@link
|
||||
* #STATE_OFF} based on the existance of the further Always BLE ON enabled applications
|
||||
* If this call returns false then there was an
|
||||
* immediate problem that will prevent the QAdapter from being turned off -
|
||||
* such as the QAadapter already being turned off.
|
||||
*
|
||||
* @return true to indicate success, or false on
|
||||
* immediate error
|
||||
* @hide
|
||||
*/
|
||||
public boolean disableBLE() {
|
||||
if (isLEAlwaysOnEnabled() != true) return false;
|
||||
|
||||
int state = getLeState();
|
||||
if (state == BluetoothAdapter.STATE_ON) {
|
||||
if (DBG) Log.d (TAG, "STATE_ON: shouldn't disable");
|
||||
try {
|
||||
mManagerService.updateBleAppCount(mToken, false);
|
||||
} catch (RemoteException e) {
|
||||
Log.e(TAG, "", e);
|
||||
}
|
||||
return true;
|
||||
|
||||
} else if (state == BluetoothAdapter.STATE_BLE_ON) {
|
||||
if (DBG) Log.d (TAG, "STATE_BLE_ON");
|
||||
int bleAppCnt = 0;
|
||||
try {
|
||||
bleAppCnt = mManagerService.updateBleAppCount(mToken, false);
|
||||
} catch (RemoteException e) {
|
||||
Log.e(TAG, "", e);
|
||||
}
|
||||
if (bleAppCnt == 0) {
|
||||
// Disable only if there are no other clients
|
||||
notifyUserAction(false);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
if (DBG) Log.d (TAG, "STATE_OFF: Already disabled");
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Special Applications who want to only turn on Bluetooth Low Energy (BLE) would
|
||||
* EnableBLE, EnableBLE brings-up Bluetooth so that application can access
|
||||
* only LE related feature (Bluetooth GATT layers interfaces using the respective class)
|
||||
* EnableBLE in turn registers the existance of a special App which wants to
|
||||
* turn on Bluetooth Low enrgy part without making it visible at the settings UI
|
||||
* as Bluetooth ON.
|
||||
* <p>Invoking EnableBLE when Bluetooth is already in ON state, would just registers
|
||||
* the existance of special Application and doesn't do anything to current BT state.
|
||||
* when user turn OFF Bluetooth from UI, if there is an existance of special app, Bluetooth
|
||||
* would stay in BLE_ON state so that LE features are still acessible to the special
|
||||
* Applications.
|
||||
*
|
||||
* <p>This is an asynchronous call: it will return immediately, and
|
||||
* clients should listen for {@link #ACTION_BLE_STATE_CHANGED}
|
||||
* to be notified of subsequent adapter state changes. If this call returns
|
||||
* true, then the adapter state will immediately transition from {@link
|
||||
* #STATE_OFF} to {@link #STATE_BLE_TURNING_ON}, and some time
|
||||
* later transition to either {@link #STATE_OFF} or {@link
|
||||
* #STATE_BLE_ON}. If this call returns false then there was an
|
||||
* immediate problem that will prevent the adapter from being turned on -
|
||||
* such as Airplane mode, or the adapter is already turned on.
|
||||
* (@link #ACTION_BLE_STATE_CHANGED) returns the Bluetooth Adapter's various
|
||||
* states, It includes all the classic Bluetooth Adapter states along with
|
||||
* internal BLE only states
|
||||
*
|
||||
* @return true to indicate Bluetooth LE start-up has begun, or false on
|
||||
* immediate error
|
||||
* @hide
|
||||
*/
|
||||
public boolean enableBLE() {
|
||||
if (isLEAlwaysOnEnabled() != true) return false;
|
||||
|
||||
if (isLeEnabled() == true) {
|
||||
if (DBG) Log.d(TAG, "enableBLE(): BT is already enabled..!");
|
||||
try {
|
||||
mManagerService.updateBleAppCount(mToken, true);
|
||||
} catch (RemoteException e) {
|
||||
Log.e(TAG, "", e);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
try {
|
||||
if (DBG) Log.d(TAG, "Calling enableBLE");
|
||||
mManagerService.updateBleAppCount(mToken, true);
|
||||
return mManagerService.enable();
|
||||
} catch (RemoteException e) {
|
||||
Log.e(TAG, "", e);
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the current state of the local Bluetooth adapter.
|
||||
* <p>Possible return values are
|
||||
@@ -559,6 +788,13 @@ public final class BluetoothAdapter {
|
||||
{
|
||||
int state= mService.getState();
|
||||
if (VDBG) Log.d(TAG, "" + hashCode() + ": getState(). Returning " + state);
|
||||
//consider all internal states as OFF
|
||||
if (state == BluetoothAdapter.STATE_BLE_ON
|
||||
|| state == BluetoothAdapter.STATE_BLE_TURNING_ON
|
||||
|| state == BluetoothAdapter.STATE_BLE_TURNING_OFF) {
|
||||
if (VDBG) Log.d(TAG, "Consider internal state as OFF");
|
||||
state = BluetoothAdapter.STATE_OFF;
|
||||
}
|
||||
return state;
|
||||
}
|
||||
// TODO(BT) there might be a small gap during STATE_TURNING_ON that
|
||||
@@ -569,6 +805,49 @@ public final class BluetoothAdapter {
|
||||
return STATE_OFF;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the current state of the local Bluetooth adapter
|
||||
* <p>This returns current internal state of Adapter including LE ON/OFF
|
||||
*
|
||||
* <p>Possible return values are
|
||||
* {@link #STATE_OFF},
|
||||
* {@link #STATE_BLE_TURNING_ON},
|
||||
* {@link #STATE_BLE_ON},
|
||||
* {@link #STATE_TURNING_ON},
|
||||
* {@link #STATE_ON},
|
||||
* {@link #STATE_TURNING_OFF},
|
||||
* {@link #STATE_BLE_TURNING_OFF}.
|
||||
* <p>Requires {@link android.Manifest.permission#BLUETOOTH}
|
||||
*
|
||||
* @return current state of Bluetooth adapter
|
||||
* @hide
|
||||
*/
|
||||
public int getLeState() {
|
||||
try {
|
||||
synchronized(mManagerCallback) {
|
||||
if (mService != null)
|
||||
{
|
||||
int state= mService.getState();
|
||||
if (VDBG) Log.d(TAG,"getLeState() returning " + state);
|
||||
return state;
|
||||
}
|
||||
}
|
||||
} catch (RemoteException e) {
|
||||
Log.e(TAG, "", e);
|
||||
}
|
||||
return BluetoothAdapter.STATE_OFF;
|
||||
}
|
||||
|
||||
boolean getLeAccess() {
|
||||
if(getLeState() == STATE_ON)
|
||||
return true;
|
||||
|
||||
else if (getLeState() == STATE_BLE_ON)
|
||||
return true; // TODO: FILTER SYSTEM APPS HERE <--
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Turn on the local Bluetooth adapter—do not use without explicit
|
||||
* user action to turn on Bluetooth.
|
||||
@@ -597,10 +876,23 @@ public final class BluetoothAdapter {
|
||||
* immediate error
|
||||
*/
|
||||
public boolean enable() {
|
||||
int state = STATE_OFF;
|
||||
if (isEnabled() == true){
|
||||
if (DBG) Log.d(TAG, "enable(): BT is already enabled..!");
|
||||
return true;
|
||||
}
|
||||
//Use service interface to get the exact state
|
||||
if (mService != null) {
|
||||
try {
|
||||
state = mService.getState();
|
||||
} catch (RemoteException e) {Log.e(TAG, "", e);}
|
||||
}
|
||||
|
||||
if (state == BluetoothAdapter.STATE_BLE_ON) {
|
||||
Log.e(TAG, "BT is in BLE_ON State");
|
||||
notifyUserAction(true);
|
||||
return true;
|
||||
}
|
||||
try {
|
||||
return mManagerService.enable();
|
||||
} catch (RemoteException e) {Log.e(TAG, "", e);}
|
||||
@@ -1561,6 +1853,10 @@ public final class BluetoothAdapter {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void onBrEdrDown() {
|
||||
if (VDBG) Log.i(TAG, "on QBrEdrDown: ");
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
|
||||
@@ -606,7 +606,9 @@ public final class BluetoothDevice implements Parcelable {
|
||||
public void onBluetoothServiceUp(IBluetooth bluetoothService)
|
||||
throws RemoteException {
|
||||
synchronized (BluetoothDevice.class) {
|
||||
sService = bluetoothService;
|
||||
if (sService == null) {
|
||||
sService = bluetoothService;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -616,6 +618,11 @@ public final class BluetoothDevice implements Parcelable {
|
||||
sService = null;
|
||||
}
|
||||
}
|
||||
|
||||
public void onBrEdrDown()
|
||||
{
|
||||
if (DBG) Log.d(TAG, "onBrEdrDown: reached BLE ON state");
|
||||
}
|
||||
};
|
||||
/**
|
||||
* Create a new BluetoothDevice
|
||||
@@ -1030,7 +1037,7 @@ public final class BluetoothDevice implements Parcelable {
|
||||
* or null on error
|
||||
*/
|
||||
public ParcelUuid[] getUuids() {
|
||||
if (sService == null) {
|
||||
if (sService == null || isBluetoothEnabled() == false) {
|
||||
Log.e(TAG, "BT not enabled. Cannot get remote device Uuids");
|
||||
return null;
|
||||
}
|
||||
@@ -1057,7 +1064,7 @@ public final class BluetoothDevice implements Parcelable {
|
||||
*/
|
||||
public boolean fetchUuidsWithSdp() {
|
||||
IBluetooth service = sService;
|
||||
if (service == null) {
|
||||
if (service == null || isBluetoothEnabled() == false) {
|
||||
Log.e(TAG, "BT not enabled. Cannot fetchUuidsWithSdp");
|
||||
return false;
|
||||
}
|
||||
@@ -1099,16 +1106,6 @@ public final class BluetoothDevice implements Parcelable {
|
||||
return false;
|
||||
}
|
||||
|
||||
/** @hide */
|
||||
public int getServiceChannel(ParcelUuid uuid) {
|
||||
//TODO(BT)
|
||||
/*
|
||||
try {
|
||||
return sService.getRemoteServiceChannel(this, uuid);
|
||||
} catch (RemoteException e) {Log.e(TAG, "", e);}*/
|
||||
return BluetoothDevice.ERROR;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the pin during pairing when the pairing method is {@link #PAIRING_VARIANT_PIN}
|
||||
* <p>Requires {@link android.Manifest.permission#BLUETOOTH_ADMIN}.
|
||||
@@ -1187,6 +1184,15 @@ public final class BluetoothDevice implements Parcelable {
|
||||
return false;
|
||||
}
|
||||
|
||||
boolean isBluetoothEnabled() {
|
||||
boolean ret = false;
|
||||
BluetoothAdapter adapter = BluetoothAdapter.getDefaultAdapter();
|
||||
if (adapter != null && adapter.isEnabled() == true) {
|
||||
ret = true;
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
/**
|
||||
* Requires {@link android.Manifest.permission#BLUETOOTH}.
|
||||
* @return Whether the phonebook access is allowed to this device. Can be
|
||||
@@ -1289,6 +1295,10 @@ public final class BluetoothDevice implements Parcelable {
|
||||
* @hide
|
||||
*/
|
||||
public BluetoothSocket createRfcommSocket(int channel) throws IOException {
|
||||
if (isBluetoothEnabled() == false) {
|
||||
Log.e(TAG, "Bluetooth is not enabled");
|
||||
throw new IOException();
|
||||
}
|
||||
return new BluetoothSocket(BluetoothSocket.TYPE_RFCOMM, -1, true, true, this, channel,
|
||||
null);
|
||||
}
|
||||
@@ -1355,6 +1365,11 @@ public final class BluetoothDevice implements Parcelable {
|
||||
* insufficient permissions
|
||||
*/
|
||||
public BluetoothSocket createRfcommSocketToServiceRecord(UUID uuid) throws IOException {
|
||||
if (isBluetoothEnabled() == false) {
|
||||
Log.e(TAG, "Bluetooth is not enabled");
|
||||
throw new IOException();
|
||||
}
|
||||
|
||||
return new BluetoothSocket(BluetoothSocket.TYPE_RFCOMM, -1, true, true, this, -1,
|
||||
new ParcelUuid(uuid));
|
||||
}
|
||||
@@ -1388,6 +1403,10 @@ public final class BluetoothDevice implements Parcelable {
|
||||
* insufficient permissions
|
||||
*/
|
||||
public BluetoothSocket createInsecureRfcommSocketToServiceRecord(UUID uuid) throws IOException {
|
||||
if (isBluetoothEnabled() == false) {
|
||||
Log.e(TAG, "Bluetooth is not enabled");
|
||||
throw new IOException();
|
||||
}
|
||||
return new BluetoothSocket(BluetoothSocket.TYPE_RFCOMM, -1, false, false, this, -1,
|
||||
new ParcelUuid(uuid));
|
||||
}
|
||||
@@ -1407,6 +1426,11 @@ public final class BluetoothDevice implements Parcelable {
|
||||
* @hide
|
||||
*/
|
||||
public BluetoothSocket createInsecureRfcommSocket(int port) throws IOException {
|
||||
|
||||
if (isBluetoothEnabled() == false) {
|
||||
Log.e(TAG, "Bluetooth is not enabled");
|
||||
throw new IOException();
|
||||
}
|
||||
return new BluetoothSocket(BluetoothSocket.TYPE_RFCOMM, -1, false, false, this, port,
|
||||
null);
|
||||
}
|
||||
@@ -1422,6 +1446,11 @@ public final class BluetoothDevice implements Parcelable {
|
||||
* @hide
|
||||
*/
|
||||
public BluetoothSocket createScoSocket() throws IOException {
|
||||
|
||||
if (isBluetoothEnabled() == false) {
|
||||
Log.e(TAG, "Bluetooth is not enabled");
|
||||
throw new IOException();
|
||||
}
|
||||
return new BluetoothSocket(BluetoothSocket.TYPE_SCO, -1, true, true, this, -1, null);
|
||||
}
|
||||
|
||||
|
||||
@@ -102,4 +102,6 @@ interface IBluetooth
|
||||
|
||||
// for dumpsys support
|
||||
String dump();
|
||||
void onLeServiceUp();
|
||||
void onBrEdrDown();
|
||||
}
|
||||
|
||||
@@ -101,4 +101,6 @@ interface IBluetoothGatt {
|
||||
in int srvcInstanceId, in ParcelUuid srvcId,
|
||||
in int charInstanceId, in ParcelUuid charId,
|
||||
in boolean confirm, in byte[] value);
|
||||
void disconnectAll();
|
||||
void unregAll();
|
||||
}
|
||||
|
||||
@@ -44,4 +44,6 @@ interface IBluetoothManager
|
||||
|
||||
String getAddress();
|
||||
String getName();
|
||||
int updateBleAppCount(IBinder b, boolean enable);
|
||||
boolean isBleAppPresent();
|
||||
}
|
||||
|
||||
@@ -26,4 +26,5 @@ import android.bluetooth.IBluetooth;
|
||||
interface IBluetoothManagerCallback {
|
||||
void onBluetoothServiceUp(in IBluetooth bluetoothService);
|
||||
void onBluetoothServiceDown();
|
||||
}
|
||||
void onBrEdrDown();
|
||||
}
|
||||
|
||||
@@ -132,7 +132,7 @@ public class BluetoothLeUtils {
|
||||
* {@link BluetoothAdapter#STATE_ON}.
|
||||
*/
|
||||
static void checkAdapterStateOn(BluetoothAdapter adapter) {
|
||||
if (adapter == null || adapter.getState() != BluetoothAdapter.STATE_ON) {
|
||||
if (adapter == null || !adapter.isLeEnabled()) {//adapter.getState() != BluetoothAdapter.STATE_ON) {
|
||||
throw new IllegalStateException("BT Adapter is not turned ON");
|
||||
}
|
||||
}
|
||||
|
||||
@@ -58,6 +58,7 @@ import java.util.Map;
|
||||
import java.util.List;
|
||||
import java.util.Vector;
|
||||
|
||||
import java.util.*;
|
||||
class BluetoothManagerService extends IBluetoothManager.Stub {
|
||||
private static final String TAG = "BluetoothManagerService";
|
||||
private static final boolean DBG = true;
|
||||
@@ -114,6 +115,7 @@ class BluetoothManagerService extends IBluetoothManager.Stub {
|
||||
private static final int SERVICE_IBLUETOOTHGATT = 2;
|
||||
|
||||
private final Context mContext;
|
||||
private static int mBleAppCount = 0;
|
||||
|
||||
// Locks are not provided for mName and mAddress.
|
||||
// They are accessed in handler or broadcast receiver, same thread context.
|
||||
@@ -186,11 +188,40 @@ class BluetoothManagerService extends IBluetoothManager.Stub {
|
||||
persistBluetoothSetting(BLUETOOTH_ON_BLUETOOTH);
|
||||
}
|
||||
}
|
||||
|
||||
int st = BluetoothAdapter.STATE_OFF;
|
||||
if (mBluetooth != null) {
|
||||
try {
|
||||
st = mBluetooth.getState();
|
||||
} catch (RemoteException e) {
|
||||
Log.e(TAG,"Unable to call getState", e);
|
||||
}
|
||||
}
|
||||
Log.d(TAG, "state" + st);
|
||||
|
||||
if (isAirplaneModeOn()) {
|
||||
// disable without persisting the setting
|
||||
sendDisableMsg();
|
||||
// Clear registered LE apps to force shut-off
|
||||
synchronized (this) {
|
||||
mBleAppCount = 0;
|
||||
}
|
||||
if (st == BluetoothAdapter.STATE_BLE_ON) {
|
||||
//if state is BLE_ON make sure you trigger disableBLE part
|
||||
try {
|
||||
if (mBluetooth != null) {
|
||||
mBluetooth.onBrEdrDown();
|
||||
mEnableExternal = false;
|
||||
}
|
||||
} catch(RemoteException e) {
|
||||
Log.e(TAG,"Unable to call onBrEdrDown", e);
|
||||
}
|
||||
} else if (st == BluetoothAdapter.STATE_ON){
|
||||
// disable without persisting the setting
|
||||
Log.d(TAG, "Calling disable");
|
||||
sendDisableMsg();
|
||||
}
|
||||
} else if (mEnableExternal) {
|
||||
// enable without persisting the setting
|
||||
Log.d(TAG, "Calling enable");
|
||||
sendEnableMsg(mQuietEnableExternal);
|
||||
}
|
||||
}
|
||||
@@ -205,12 +236,6 @@ class BluetoothManagerService extends IBluetoothManager.Stub {
|
||||
sendEnableMsg(mQuietEnableExternal);
|
||||
}
|
||||
}
|
||||
|
||||
if (!isNameAndAddressSet()) {
|
||||
//Sync the Bluetooth name and address from the Bluetooth Adapter
|
||||
if (DBG) Log.d(TAG,"Retrieving Bluetooth Adapter name and address...");
|
||||
getNameAndAddress();
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
@@ -220,6 +245,7 @@ class BluetoothManagerService extends IBluetoothManager.Stub {
|
||||
|
||||
mContext = context;
|
||||
mBluetooth = null;
|
||||
mBluetoothGatt = null;
|
||||
mBinding = false;
|
||||
mUnbinding = false;
|
||||
mEnable = false;
|
||||
@@ -398,6 +424,133 @@ class BluetoothManagerService extends IBluetoothManager.Stub {
|
||||
return false;
|
||||
}
|
||||
|
||||
class ClientDeathRecipient implements IBinder.DeathRecipient {
|
||||
public void binderDied() {
|
||||
if (DBG) Log.d(TAG, "Binder is dead - unregister Ble App");
|
||||
if (mBleAppCount > 0) --mBleAppCount;
|
||||
|
||||
if (mBleAppCount == 0) {
|
||||
if (DBG) Log.d(TAG, "Disabling LE only mode after application crash");
|
||||
try {
|
||||
if (mBluetooth != null) {
|
||||
mBluetooth.onBrEdrDown();
|
||||
}
|
||||
} catch(RemoteException e) {
|
||||
Log.e(TAG,"Unable to call onBrEdrDown", e);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/** Internal death rec list */
|
||||
Map<IBinder, ClientDeathRecipient> mBleApps = new HashMap<IBinder, ClientDeathRecipient>();
|
||||
|
||||
public int updateBleAppCount(IBinder token, boolean enable) {
|
||||
if (enable) {
|
||||
ClientDeathRecipient r = mBleApps.get(token);
|
||||
if (r == null) {
|
||||
ClientDeathRecipient deathRec = new ClientDeathRecipient();
|
||||
try {
|
||||
token.linkToDeath(deathRec, 0);
|
||||
} catch (RemoteException ex) {
|
||||
throw new IllegalArgumentException("Wake lock is already dead.");
|
||||
}
|
||||
mBleApps.put(token, deathRec);
|
||||
synchronized (this) {
|
||||
++mBleAppCount;
|
||||
}
|
||||
if (DBG) Log.d(TAG, "Registered for death Notification");
|
||||
}
|
||||
|
||||
} else {
|
||||
ClientDeathRecipient r = mBleApps.get(token);
|
||||
if (r != null) {
|
||||
try {
|
||||
token.linkToDeath(r, 0);
|
||||
} catch (RemoteException ex) {
|
||||
throw new IllegalArgumentException("Wake lock is already dead.");
|
||||
}
|
||||
mBleApps.remove(token);
|
||||
synchronized (this) {
|
||||
if (mBleAppCount > 0) --mBleAppCount;
|
||||
}
|
||||
if (DBG) Log.d(TAG, "Unregistered for death Notification");
|
||||
}
|
||||
}
|
||||
if (DBG) Log.d(TAG, "Updated BleAppCount" + mBleAppCount);
|
||||
if (mBleAppCount == 0 && mEnable) {
|
||||
try {
|
||||
if (mBluetooth != null && (mBluetooth.getState() != BluetoothAdapter.STATE_ON)) {
|
||||
if (DBG) Log.d(TAG, "Reseting the mEnable flag for clean disable");
|
||||
mEnable = false;
|
||||
}
|
||||
} catch (RemoteException e) {
|
||||
Log.e(TAG, "getState()", e);
|
||||
}
|
||||
}
|
||||
return mBleAppCount;
|
||||
}
|
||||
|
||||
/** @hide*/
|
||||
public boolean isBleAppPresent() {
|
||||
if (DBG) Log.d(TAG, "isBleAppPresent() count: " + mBleAppCount);
|
||||
return (mBleAppCount > 0);
|
||||
}
|
||||
|
||||
/**
|
||||
* Action taken when GattService is turned off
|
||||
*/
|
||||
private void onBluetoothGattServiceUp() {
|
||||
if (DBG) Log.d(TAG,"BluetoothGatt Service is Up");
|
||||
try{
|
||||
if (isBleAppPresent() == false && mBluetooth.getState() == BluetoothAdapter.STATE_BLE_ON) {
|
||||
mBluetooth.onLeServiceUp();
|
||||
|
||||
// waive WRITE_SECURE_SETTINGS permission check
|
||||
long callingIdentity = Binder.clearCallingIdentity();
|
||||
persistBluetoothSetting(BLUETOOTH_ON_BLUETOOTH);
|
||||
Binder.restoreCallingIdentity(callingIdentity);
|
||||
}
|
||||
} catch(RemoteException e) {
|
||||
Log.e(TAG,"Unable to call onServiceUp", e);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Inform BluetoothAdapter instances that BREDR part is down
|
||||
* and turn off all service and stack if no LE app needs it
|
||||
*/
|
||||
private void sendBrEdrDownCallback() {
|
||||
if (DBG) Log.d(TAG,"Calling sendBrEdrDownCallback callbacks");
|
||||
int n = mCallbacks.beginBroadcast();
|
||||
|
||||
if (isBleAppPresent() == false) {
|
||||
try {
|
||||
mBluetooth.onBrEdrDown();
|
||||
} catch(RemoteException e) {
|
||||
Log.e(TAG,"Unable to call onBrEdrDown", e);
|
||||
}
|
||||
}
|
||||
else{//need to stay at BLE ON. disconnect all Gatt connections
|
||||
try{
|
||||
mBluetoothGatt.unregAll();//disconnectAll();
|
||||
} catch(RemoteException e) {
|
||||
Log.e(TAG,"Unable to disconn all", e);
|
||||
}
|
||||
}
|
||||
|
||||
Log.d(TAG,"Broadcasting onBrEdrDown() to " + n + " receivers.");
|
||||
for (int i=0; i <n; i++) {
|
||||
try {
|
||||
mCallbacks.getBroadcastItem(i).onBrEdrDown();
|
||||
} catch (RemoteException e) {
|
||||
Log.e(TAG, "Unable to call sendBrEdrDownCallback() on callback #" + i, e);
|
||||
}
|
||||
}
|
||||
mCallbacks.finishBroadcast();
|
||||
}
|
||||
|
||||
/** @hide*/
|
||||
public void getNameAndAddress() {
|
||||
if (DBG) {
|
||||
Log.d(TAG,"getNameAndAddress(): mBluetooth = " + mBluetooth +
|
||||
@@ -447,11 +600,9 @@ class BluetoothManagerService extends IBluetoothManager.Stub {
|
||||
mQuietEnableExternal = false;
|
||||
mEnableExternal = true;
|
||||
// waive WRITE_SECURE_SETTINGS permission check
|
||||
long callingIdentity = Binder.clearCallingIdentity();
|
||||
persistBluetoothSetting(BLUETOOTH_ON_BLUETOOTH);
|
||||
Binder.restoreCallingIdentity(callingIdentity);
|
||||
sendEnableMsg(false);
|
||||
}
|
||||
if (DBG) Log.d(TAG, "enable returning");
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -510,6 +661,7 @@ class BluetoothManagerService extends IBluetoothManager.Stub {
|
||||
} else {
|
||||
mUnbinding=false;
|
||||
}
|
||||
mBluetoothGatt = null;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1036,6 +1188,7 @@ class BluetoothManagerService extends IBluetoothManager.Stub {
|
||||
synchronized(mConnection) {
|
||||
if (msg.arg1 == SERVICE_IBLUETOOTHGATT) {
|
||||
mBluetoothGatt = IBluetoothGatt.Stub.asInterface(service);
|
||||
onBluetoothGattServiceUp();
|
||||
break;
|
||||
} // else must be SERVICE_IBLUETOOTH
|
||||
|
||||
@@ -1113,12 +1266,18 @@ class BluetoothManagerService extends IBluetoothManager.Stub {
|
||||
bluetoothStateChangeHandler(prevState, newState);
|
||||
// handle error state transition case from TURNING_ON to OFF
|
||||
// unbind and rebind bluetooth service and enable bluetooth
|
||||
if ((prevState == BluetoothAdapter.STATE_TURNING_ON) &&
|
||||
if ((prevState == BluetoothAdapter.STATE_BLE_TURNING_ON) &&
|
||||
(newState == BluetoothAdapter.STATE_OFF) &&
|
||||
(mBluetooth != null) && mEnable) {
|
||||
recoverBluetoothServiceFromError();
|
||||
}
|
||||
if (newState == BluetoothAdapter.STATE_ON) {
|
||||
if ((prevState == BluetoothAdapter.STATE_TURNING_ON) &&
|
||||
(newState == BluetoothAdapter.STATE_BLE_ON) &&
|
||||
(mBluetooth != null) && mEnable) {
|
||||
recoverBluetoothServiceFromError();
|
||||
}
|
||||
if (newState == BluetoothAdapter.STATE_ON ||
|
||||
newState == BluetoothAdapter.STATE_BLE_ON) {
|
||||
// bluetooth is working, reset the counter
|
||||
if (mErrorRecoveryRetryCounter != 0) {
|
||||
Log.w(TAG, "bluetooth is recovered from error");
|
||||
@@ -1378,39 +1537,90 @@ class BluetoothManagerService extends IBluetoothManager.Stub {
|
||||
return valid;
|
||||
}
|
||||
|
||||
private void sendBleStateChanged(int prevState, int newState) {
|
||||
if (DBG) Log.d(TAG,"BLE State Change Intent: " + prevState + " -> " + newState);
|
||||
// Send broadcast message to everyone else
|
||||
Intent intent = new Intent(BluetoothAdapter.ACTION_BLE_STATE_CHANGED);
|
||||
intent.putExtra(BluetoothAdapter.EXTRA_PREVIOUS_STATE, prevState);
|
||||
intent.putExtra(BluetoothAdapter.EXTRA_STATE, newState);
|
||||
intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT);
|
||||
mContext.sendBroadcastAsUser(intent, UserHandle.ALL, BLUETOOTH_PERM);
|
||||
}
|
||||
|
||||
private void bluetoothStateChangeHandler(int prevState, int newState) {
|
||||
boolean isStandardBroadcast = true;
|
||||
if (prevState != newState) {
|
||||
//Notify all proxy objects first of adapter state change
|
||||
if (newState == BluetoothAdapter.STATE_ON || newState == BluetoothAdapter.STATE_OFF) {
|
||||
boolean isUp = (newState==BluetoothAdapter.STATE_ON);
|
||||
sendBluetoothStateCallback(isUp);
|
||||
if (newState == BluetoothAdapter.STATE_BLE_ON
|
||||
|| newState == BluetoothAdapter.STATE_OFF) {
|
||||
boolean intermediate_off = (prevState == BluetoothAdapter.STATE_TURNING_OFF
|
||||
&& newState == BluetoothAdapter.STATE_BLE_ON);
|
||||
|
||||
if (isUp) {
|
||||
// connect to GattService
|
||||
if (mContext.getPackageManager().hasSystemFeature(
|
||||
PackageManager.FEATURE_BLUETOOTH_LE)) {
|
||||
Intent i = new Intent(IBluetoothGatt.class.getName());
|
||||
doBind(i, 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()) {
|
||||
unbindAllBluetoothProfileServices();
|
||||
if (newState == BluetoothAdapter.STATE_OFF) {
|
||||
// If Bluetooth is off, send service down event to proxy objects, and unbind
|
||||
if (DBG) Log.d(TAG, "Bluetooth is complete turn off");
|
||||
if (canUnbindBluetoothService()) {
|
||||
if (DBG) Log.d(TAG, "Good to unbind!");
|
||||
sendBluetoothServiceDownCallback();
|
||||
unbindAndFinish();
|
||||
sendBleStateChanged(prevState, newState);
|
||||
// Don't broadcast as it has already been broadcast before
|
||||
isStandardBroadcast = false;
|
||||
}
|
||||
|
||||
} else if (!intermediate_off) {
|
||||
// connect to GattService
|
||||
if (DBG) Log.d(TAG, "Bluetooth is in LE only mode");
|
||||
if (mBluetoothGatt != null) {
|
||||
if (DBG) Log.d(TAG, "Calling BluetoothGattServiceUp");
|
||||
onBluetoothGattServiceUp();
|
||||
} else {
|
||||
if (DBG) Log.d(TAG, "Binding Bluetooth GATT service");
|
||||
if (mContext.getPackageManager().hasSystemFeature(
|
||||
PackageManager.FEATURE_BLUETOOTH_LE)) {
|
||||
Intent i = new Intent(IBluetoothGatt.class.getName());
|
||||
doBind(i, mConnection, Context.BIND_AUTO_CREATE | Context.BIND_IMPORTANT, UserHandle.CURRENT);
|
||||
}
|
||||
}
|
||||
sendBleStateChanged(prevState, newState);
|
||||
//Don't broadcase this as std intent
|
||||
isStandardBroadcast = false;
|
||||
|
||||
} else if (intermediate_off){
|
||||
if (DBG) Log.d(TAG, "Intermediate off, back to LE only mode");
|
||||
// For LE only mode, broadcast as is
|
||||
sendBleStateChanged(prevState, newState);
|
||||
sendBluetoothStateCallback(false); // BT is OFF for general users
|
||||
// Broadcast as STATE_OFF
|
||||
newState = BluetoothAdapter.STATE_OFF;
|
||||
sendBrEdrDownCallback();
|
||||
}
|
||||
} else if (newState == BluetoothAdapter.STATE_ON) {
|
||||
boolean isUp = (newState==BluetoothAdapter.STATE_ON);
|
||||
sendBluetoothStateCallback(isUp);
|
||||
sendBleStateChanged(prevState, newState);
|
||||
|
||||
} else if (newState == BluetoothAdapter.STATE_BLE_TURNING_ON
|
||||
|| newState == BluetoothAdapter.STATE_BLE_TURNING_OFF ) {
|
||||
sendBleStateChanged(prevState, newState);
|
||||
isStandardBroadcast = false;
|
||||
|
||||
} else if (newState == BluetoothAdapter.STATE_TURNING_ON
|
||||
|| newState == BluetoothAdapter.STATE_TURNING_OFF) {
|
||||
sendBleStateChanged(prevState, newState);
|
||||
}
|
||||
|
||||
//Send broadcast message to everyone else
|
||||
Intent intent = new Intent(BluetoothAdapter.ACTION_STATE_CHANGED);
|
||||
intent.putExtra(BluetoothAdapter.EXTRA_PREVIOUS_STATE, prevState);
|
||||
intent.putExtra(BluetoothAdapter.EXTRA_STATE, newState);
|
||||
intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT);
|
||||
if (DBG) Log.d(TAG,"Bluetooth State Change Intent: " + prevState + " -> " + newState);
|
||||
mContext.sendBroadcastAsUser(intent, UserHandle.ALL,
|
||||
BLUETOOTH_PERM);
|
||||
if (isStandardBroadcast) {
|
||||
if (prevState == BluetoothAdapter.STATE_BLE_ON) {
|
||||
// Show prevState of BLE_ON as OFF to standard users
|
||||
prevState = BluetoothAdapter.STATE_OFF;
|
||||
}
|
||||
Intent intent = new Intent(BluetoothAdapter.ACTION_STATE_CHANGED);
|
||||
intent.putExtra(BluetoothAdapter.EXTRA_PREVIOUS_STATE, prevState);
|
||||
intent.putExtra(BluetoothAdapter.EXTRA_STATE, newState);
|
||||
intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT);
|
||||
mContext.sendBroadcastAsUser(intent, UserHandle.ALL, BLUETOOTH_PERM);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user