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:
Nitin Arora
2015-03-02 15:03:51 -08:00
committed by Andre Eisenbach
parent 104b0f1353
commit 6ddbb5e55f
8 changed files with 601 additions and 59 deletions

View File

@@ -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&mdash;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: ");
}
};
/**

View File

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

View File

@@ -102,4 +102,6 @@ interface IBluetooth
// for dumpsys support
String dump();
void onLeServiceUp();
void onBrEdrDown();
}

View File

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

View File

@@ -44,4 +44,6 @@ interface IBluetoothManager
String getAddress();
String getName();
int updateBleAppCount(IBinder b, boolean enable);
boolean isBleAppPresent();
}

View File

@@ -26,4 +26,5 @@ import android.bluetooth.IBluetooth;
interface IBluetoothManagerCallback {
void onBluetoothServiceUp(in IBluetooth bluetoothService);
void onBluetoothServiceDown();
}
void onBrEdrDown();
}

View File

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

View File

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