Loading framework/java/android/bluetooth/BluetoothAdapter.java +33 −73 Original line number Original line Diff line number Diff line Loading @@ -680,30 +680,7 @@ public final class BluetoothAdapter { } } /** /** * Performs action based on user action to turn BT ON * Turns off Bluetooth LE which was earlier turned on by calling enableBLE(). * or OFF if BT is in BLE_ON state */ private void notifyUserAction(boolean enable) { try { mServiceLock.readLock().lock(); if (mService == null) { Log.e(TAG, "mService is null"); return; } if (enable) { mService.onLeServiceUp(); //NA:TODO implementation pending } else { mService.onBrEdrDown(); //NA:TODO implementation pending } } catch (RemoteException e) { Log.e(TAG, "", e); } finally { mServiceLock.readLock().unlock(); } } /** * 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 * <p> If the internal Adapter state is STATE_BLE_ON, this would trigger the transition * to STATE_OFF and completely shut-down Bluetooth * to STATE_OFF and completely shut-down Bluetooth Loading Loading @@ -733,61 +710,50 @@ public final class BluetoothAdapter { if (!isBleScanAlwaysAvailable()) return false; if (!isBleScanAlwaysAvailable()) return false; int state = getLeState(); int state = getLeState(); if (state == BluetoothAdapter.STATE_ON) { if (state == BluetoothAdapter.STATE_ON || state == BluetoothAdapter.STATE_BLE_ON) { if (DBG) Log.d (TAG, "STATE_ON: shouldn't disable"); String packageName = ActivityThread.currentPackageName(); try { if (DBG) Log.d (TAG, "disableBLE(): de-registering " + packageName); 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 { try { bleAppCnt = mManagerService.updateBleAppCount(mToken, false); mManagerService.updateBleAppCount(mToken, false, packageName); } catch (RemoteException e) { } catch (RemoteException e) { Log.e(TAG, "", e); Log.e(TAG, "", e); } } if (bleAppCnt == 0) { // Disable only if there are no other clients notifyUserAction(false); } return true; return true; } } if (DBG) Log.d (TAG, "STATE_OFF: Already disabled"); if (DBG) Log.d (TAG, "disableBLE(): Already disabled"); return false; return false; } } /** /** * Special Applications who want to only turn on Bluetooth Low Energy (BLE) would * Applications who want to only use Bluetooth Low Energy (BLE) can call enableBLE. * 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 * enableBLE registers the existence of an app using only LE functions. * * enableBLE may enable Bluetooth to an LE only mode so that an app can use * LE related features (BluetoothGatt or BluetoothGattServer classes) * * If the user disables Bluetooth while an app is registered to use LE only features, * Bluetooth will remain on in LE only mode for the app. * * When Bluetooth is in LE only mode, it is not shown as ON to the UI. * * <p>This is an asynchronous call: it returns immediately, and * clients should listen for {@link #ACTION_BLE_STATE_CHANGED} * clients should listen for {@link #ACTION_BLE_STATE_CHANGED} * to be notified of subsequent adapter state changes. If this call returns * to be notified of adapter state changes. * true, then the adapter state will immediately transition from {@link * * #STATE_OFF} to {@link #STATE_BLE_TURNING_ON}, and some time * If this call returns * true, then the adapter state is either in a mode where * later transition to either {@link #STATE_OFF} or {@link * LE is available, or will transition from {@link #STATE_OFF} to {@link #STATE_BLE_TURNING_ON}, * #STATE_BLE_ON}. If this call returns false then there was an * and some time later transition to either {@link #STATE_OFF} or {@link #STATE_BLE_ON}. * immediate problem that will prevent the adapter from being turned on - * * such as Airplane mode, or the adapter is already turned on. * If this call returns false then there was an immediate problem that prevents the * (@link #ACTION_BLE_STATE_CHANGED) returns the Bluetooth Adapter's various * adapter from being turned on - such as Airplane mode. * * {@link #ACTION_BLE_STATE_CHANGED} returns the Bluetooth Adapter's various * states, It includes all the classic Bluetooth Adapter states along with * states, It includes all the classic Bluetooth Adapter states along with * internal BLE only states * internal BLE only states * * * @return true to indicate Bluetooth LE start-up has begun, or false on * @return true to indicate Bluetooth LE will be available, or false on * immediate error * immediate error * @hide * @hide */ */ Loading @@ -796,13 +762,14 @@ public final class BluetoothAdapter { if (!isBleScanAlwaysAvailable()) return false; if (!isBleScanAlwaysAvailable()) return false; try { try { mManagerService.updateBleAppCount(mToken, true); String packageName = ActivityThread.currentPackageName(); mManagerService.updateBleAppCount(mToken, true, packageName); if (isLeEnabled()) { if (isLeEnabled()) { if (DBG) Log.d(TAG, "enableBLE(): Bluetooth already enabled"); if (DBG) Log.d(TAG, "enableBLE(): Bluetooth already enabled"); return true; return true; } } if (DBG) Log.d(TAG, "enableBLE(): Calling enable"); if (DBG) Log.d(TAG, "enableBLE(): Calling enable"); return mManagerService.enable(ActivityThread.currentPackageName()); return mManagerService.enable(packageName); } catch (RemoteException e) { } catch (RemoteException e) { Log.e(TAG, "", e); Log.e(TAG, "", e); } } Loading Loading @@ -1940,9 +1907,6 @@ public final class BluetoothAdapter { } else if (profile == BluetoothProfile.MAP_CLIENT) { } else if (profile == BluetoothProfile.MAP_CLIENT) { BluetoothMapClient mapClient = new BluetoothMapClient(context, listener); BluetoothMapClient mapClient = new BluetoothMapClient(context, listener); return true; return true; } else if (profile == BluetoothProfile.INPUT_HOST) { BluetoothInputHost iHost = new BluetoothInputHost(context, listener); return true; } else { } else { return false; return false; } } Loading Loading @@ -2019,10 +1983,6 @@ public final class BluetoothAdapter { BluetoothMapClient mapClient = (BluetoothMapClient)proxy; BluetoothMapClient mapClient = (BluetoothMapClient)proxy; mapClient.close(); mapClient.close(); break; break; case BluetoothProfile.INPUT_HOST: BluetoothInputHost iHost = (BluetoothInputHost) proxy; iHost.close(); break; } } } } Loading Loading @@ -2094,7 +2054,7 @@ public final class BluetoothAdapter { return true; return true; } } try { try { return mManagerService.enableNoAutoConnect(); return mManagerService.enableNoAutoConnect(ActivityThread.currentPackageName()); } catch (RemoteException e) {Log.e(TAG, "", e);} } catch (RemoteException e) {Log.e(TAG, "", e);} return false; return false; } } Loading framework/java/android/bluetooth/IBluetoothManager.aidl +3 −3 Original line number Original line Diff line number Diff line Loading @@ -35,7 +35,7 @@ interface IBluetoothManager void unregisterStateChangeCallback(in IBluetoothStateChangeCallback callback); void unregisterStateChangeCallback(in IBluetoothStateChangeCallback callback); boolean isEnabled(); boolean isEnabled(); boolean enable(String packageName); boolean enable(String packageName); boolean enableNoAutoConnect(); boolean enableNoAutoConnect(String packageName); boolean disable(String packageName, boolean persist); boolean disable(String packageName, boolean persist); int getState(); int getState(); IBluetoothGatt getBluetoothGatt(); IBluetoothGatt getBluetoothGatt(); Loading @@ -47,7 +47,7 @@ interface IBluetoothManager String getName(); String getName(); boolean isBleScanAlwaysAvailable(); boolean isBleScanAlwaysAvailable(); int updateBleAppCount(IBinder b, boolean enable); int updateBleAppCount(IBinder b, boolean enable, String packageName); boolean isBleAppPresent(); boolean isBleAppPresent(); } } service/java/com/android/server/bluetooth/BluetoothManagerService.java +125 −49 Original line number Original line Diff line number Diff line Loading @@ -58,14 +58,16 @@ import android.os.UserManagerInternal.UserRestrictionsListener; import android.provider.Settings; import android.provider.Settings; import android.provider.Settings.SettingNotFoundException; import android.provider.Settings.SettingNotFoundException; import android.util.Slog; import android.util.Slog; import java.util.concurrent.locks.ReentrantReadWriteLock; import java.io.FileDescriptor; import java.io.FileDescriptor; import java.io.PrintWriter; import java.io.PrintWriter; import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.locks.ReentrantReadWriteLock; import java.util.HashMap; import java.util.HashMap; import java.util.LinkedList; import java.util.Map; import java.util.Map; class BluetoothManagerService extends IBluetoothManager.Stub { class BluetoothManagerService extends IBluetoothManager.Stub { private static final String TAG = "BluetoothManagerService"; private static final String TAG = "BluetoothManagerService"; private static final boolean DBG = true; private static final boolean DBG = true; Loading Loading @@ -138,16 +140,46 @@ class BluetoothManagerService extends IBluetoothManager.Stub { new ReentrantReadWriteLock(); new ReentrantReadWriteLock(); private boolean mBinding; private boolean mBinding; private boolean mUnbinding; private boolean mUnbinding; // used inside handler thread // used inside handler thread private boolean mQuietEnable = false; private boolean mQuietEnable = false; // configuarion from external IBinder call which is used to private boolean mEnable; /** * Used for tracking apps that enabled / disabled Bluetooth. */ private class ActiveLog { private String mPackageName; private boolean mEnable; private long mTimestamp; public ActiveLog(String packageName, boolean enable, long timestamp) { mPackageName = packageName; mEnable = enable; mTimestamp = timestamp; } public long getTime() { return mTimestamp; } public String toString() { return android.text.format.DateFormat.format("MM-dd hh:mm:ss ", mTimestamp) + (mEnable ? " Enabled " : " Disabled ") + " by " + mPackageName; } } private LinkedList<ActiveLog> mActiveLogs; // configuration from external IBinder call which is used to // synchronize with broadcast receiver. // synchronize with broadcast receiver. private boolean mQuietEnableExternal; private boolean mQuietEnableExternal; // configuarion from external IBinder call which is used to // synchronize with broadcast receiver. private boolean mEnableExternal; private boolean mEnableExternal; // used inside handler thread private boolean mEnable; // Map of apps registered to keep BLE scanning on. private Map<IBinder, ClientDeathRecipient> mBleApps = new ConcurrentHashMap<IBinder, ClientDeathRecipient>(); private int mState; private int mState; private final BluetoothHandler mHandler; private final BluetoothHandler mHandler; private int mErrorRecoveryRetryCounter; private int mErrorRecoveryRetryCounter; Loading Loading @@ -253,12 +285,12 @@ class BluetoothManagerService extends IBluetoothManager.Stub { } else if (st == BluetoothAdapter.STATE_ON){ } else if (st == BluetoothAdapter.STATE_ON){ // disable without persisting the setting // disable without persisting the setting Slog.d(TAG, "Calling disable"); Slog.d(TAG, "Calling disable"); sendDisableMsg(); sendDisableMsg("airplane mode"); } } } else if (mEnableExternal) { } else if (mEnableExternal) { // enable without persisting the setting // enable without persisting the setting Slog.d(TAG, "Calling enable"); Slog.d(TAG, "Calling enable"); sendEnableMsg(mQuietEnableExternal); sendEnableMsg(mQuietEnableExternal, "airplane mode"); } } } } } } Loading @@ -273,6 +305,7 @@ class BluetoothManagerService extends IBluetoothManager.Stub { mPermissionReviewRequired = context.getResources().getBoolean( mPermissionReviewRequired = context.getResources().getBoolean( com.android.internal.R.bool.config_permissionReviewRequired); com.android.internal.R.bool.config_permissionReviewRequired); mActiveLogs = new LinkedList<ActiveLog>(); mBluetooth = null; mBluetooth = null; mBluetoothBinder = null; mBluetoothBinder = null; mBluetoothGatt = null; mBluetoothGatt = null; Loading Loading @@ -300,15 +333,15 @@ class BluetoothManagerService extends IBluetoothManager.Stub { mEnableExternal = true; mEnableExternal = true; } } int sysUiUid = -1; int systemUiUid = -1; try { try { sysUiUid = mContext.getPackageManager().getPackageUidAsUser("com.android.systemui", systemUiUid = mContext.getPackageManager().getPackageUidAsUser("com.android.systemui", PackageManager.MATCH_SYSTEM_ONLY, UserHandle.USER_SYSTEM); PackageManager.MATCH_SYSTEM_ONLY, UserHandle.USER_SYSTEM); } catch (PackageManager.NameNotFoundException e) { } catch (PackageManager.NameNotFoundException e) { // Some platforms, such as wearables do not have a system ui. // Some platforms, such as wearables do not have a system ui. Slog.w(TAG, "Unable to resolve SystemUI's UID.", e); Slog.w(TAG, "Unable to resolve SystemUI's UID.", e); } } mSystemUiUid = sysUiUid; mSystemUiUid = systemUiUid; } } /** /** Loading Loading @@ -488,8 +521,14 @@ class BluetoothManagerService extends IBluetoothManager.Stub { } } class ClientDeathRecipient implements IBinder.DeathRecipient { class ClientDeathRecipient implements IBinder.DeathRecipient { private String mPackageName; public ClientDeathRecipient(String packageName) { mPackageName = packageName; } public void binderDied() { public void binderDied() { if (DBG) Slog.d(TAG, "Binder is dead - unregister Ble App"); if (DBG) Slog.d(TAG, "Binder is dead - unregister " + mPackageName); if (isBleAppPresent()) { if (isBleAppPresent()) { // Nothing to do, another app is here. // Nothing to do, another app is here. return; return; Loading @@ -508,10 +547,11 @@ class BluetoothManagerService extends IBluetoothManager.Stub { mBluetoothLock.readLock().unlock(); mBluetoothLock.readLock().unlock(); } } } } } /** Internal death rec list */ public String getPackageName() { Map<IBinder, ClientDeathRecipient> mBleApps = new ConcurrentHashMap<IBinder, ClientDeathRecipient>(); return mPackageName; } } @Override @Override public boolean isBleScanAlwaysAvailable() { public boolean isBleScanAlwaysAvailable() { Loading Loading @@ -569,28 +609,22 @@ class BluetoothManagerService extends IBluetoothManager.Stub { } } } } public int updateBleAppCount(IBinder token, boolean enable) { public int updateBleAppCount(IBinder token, boolean enable, String packageName) { if (enable) { ClientDeathRecipient r = mBleApps.get(token); ClientDeathRecipient r = mBleApps.get(token); if (r == null) { if (r == null && enable) { ClientDeathRecipient deathRec = new ClientDeathRecipient(); ClientDeathRecipient deathRec = new ClientDeathRecipient(packageName); try { try { token.linkToDeath(deathRec, 0); token.linkToDeath(deathRec, 0); } catch (RemoteException ex) { } catch (RemoteException ex) { throw new IllegalArgumentException("Wake lock is already dead."); throw new IllegalArgumentException("BLE app (" + packageName + ") already dead!"); } } mBleApps.put(token, deathRec); mBleApps.put(token, deathRec); if (DBG) Slog.d(TAG, "Registered for death Notification"); if (DBG) Slog.d(TAG, "Registered for death of " + packageName); } } else if (!enable && r != null) { } else { ClientDeathRecipient r = mBleApps.get(token); if (r != null) { // Unregister death recipient as the app goes away. // Unregister death recipient as the app goes away. token.unlinkToDeath(r, 0); token.unlinkToDeath(r, 0); mBleApps.remove(token); mBleApps.remove(token); if (DBG) Slog.d(TAG, "Unregistered for death Notification"); if (DBG) Slog.d(TAG, "Unregistered for death of " + packageName); } } } int appCount = mBleApps.size(); int appCount = mBleApps.size(); if (DBG) Slog.d(TAG, appCount + " registered Ble Apps"); if (DBG) Slog.d(TAG, appCount + " registered Ble Apps"); Loading Loading @@ -671,7 +705,7 @@ class BluetoothManagerService extends IBluetoothManager.Stub { } } } } public boolean enableNoAutoConnect() public boolean enableNoAutoConnect(String packageName) { { if (isBluetoothDisallowed()) { if (isBluetoothDisallowed()) { if (DBG) { if (DBG) { Loading @@ -696,7 +730,7 @@ class BluetoothManagerService extends IBluetoothManager.Stub { synchronized(mReceiver) { synchronized(mReceiver) { mQuietEnableExternal = true; mQuietEnableExternal = true; mEnableExternal = true; mEnableExternal = true; sendEnableMsg(true); sendEnableMsg(true, packageName); } } return true; return true; } } Loading Loading @@ -729,7 +763,7 @@ class BluetoothManagerService extends IBluetoothManager.Stub { } } if (DBG) { if (DBG) { Slog.d(TAG,"enable(): mBluetooth =" + mBluetooth + Slog.d(TAG,"enable(" + packageName + "): mBluetooth =" + mBluetooth + " mBinding = " + mBinding + " mState = " + " mBinding = " + mBinding + " mState = " + BluetoothAdapter.nameForState(mState)); BluetoothAdapter.nameForState(mState)); } } Loading @@ -738,7 +772,7 @@ class BluetoothManagerService extends IBluetoothManager.Stub { mQuietEnableExternal = false; mQuietEnableExternal = false; mEnableExternal = true; mEnableExternal = true; // waive WRITE_SECURE_SETTINGS permission check // waive WRITE_SECURE_SETTINGS permission check sendEnableMsg(false); sendEnableMsg(false, packageName); } } if (DBG) Slog.d(TAG, "enable returning"); if (DBG) Slog.d(TAG, "enable returning"); return true; return true; Loading Loading @@ -774,7 +808,7 @@ class BluetoothManagerService extends IBluetoothManager.Stub { persistBluetoothSetting(BLUETOOTH_OFF); persistBluetoothSetting(BLUETOOTH_OFF); } } mEnableExternal = false; mEnableExternal = false; sendDisableMsg(); sendDisableMsg(packageName); } } return true; return true; } } Loading Loading @@ -928,7 +962,7 @@ class BluetoothManagerService extends IBluetoothManager.Stub { } } if (mEnableExternal && isBluetoothPersistedStateOnBluetooth()) { if (mEnableExternal && isBluetoothPersistedStateOnBluetooth()) { if (DBG) Slog.d(TAG, "Auto-enabling Bluetooth."); if (DBG) Slog.d(TAG, "Auto-enabling Bluetooth."); sendEnableMsg(mQuietEnableExternal); sendEnableMsg(mQuietEnableExternal, "system boot"); } else if (!isNameAndAddressSet()) { } else if (!isNameAndAddressSet()) { if (DBG) Slog.d(TAG, "Getting adapter name and address"); if (DBG) Slog.d(TAG, "Getting adapter name and address"); Message getMsg = mHandler.obtainMessage(MESSAGE_GET_NAME_AND_ADDRESS); Message getMsg = mHandler.obtainMessage(MESSAGE_GET_NAME_AND_ADDRESS); Loading Loading @@ -1897,13 +1931,24 @@ class BluetoothManagerService extends IBluetoothManager.Stub { return false; return false; } } private void sendDisableMsg() { private void sendDisableMsg(String packageName) { mHandler.sendMessage(mHandler.obtainMessage(MESSAGE_DISABLE)); mHandler.sendMessage(mHandler.obtainMessage(MESSAGE_DISABLE)); addActiveLog(packageName, false); } } private void sendEnableMsg(boolean quietMode) { private void sendEnableMsg(boolean quietMode, String packageName) { mHandler.sendMessage(mHandler.obtainMessage(MESSAGE_ENABLE, mHandler.sendMessage(mHandler.obtainMessage(MESSAGE_ENABLE, quietMode ? 1 : 0, 0)); quietMode ? 1 : 0, 0)); addActiveLog(packageName, true); } private void addActiveLog(String packageName, boolean enable) { synchronized (mActiveLogs) { if (mActiveLogs.size() > 10) { mActiveLogs.remove(); } mActiveLogs.add(new ActiveLog(packageName, enable, System.currentTimeMillis())); } } } private void recoverBluetoothServiceFromError(boolean clearBle) { private void recoverBluetoothServiceFromError(boolean clearBle) { Loading Loading @@ -1974,19 +2019,50 @@ class BluetoothManagerService extends IBluetoothManager.Stub { public void dump(FileDescriptor fd, PrintWriter writer, String[] args) { public void dump(FileDescriptor fd, PrintWriter writer, String[] args) { mContext.enforceCallingOrSelfPermission(android.Manifest.permission.DUMP, TAG); mContext.enforceCallingOrSelfPermission(android.Manifest.permission.DUMP, TAG); String errorMsg = null; String errorMsg = null; boolean protoOut = (args.length > 0) && args[0].startsWith("--proto"); if (!protoOut) { writer.println("Bluetooth Status"); writer.println(" enabled: " + isEnabled()); writer.println(" state: " + BluetoothAdapter.nameForState(mState)); writer.println(" address: " + mAddress); writer.println(" name: " + mName); if (mEnable) { long onDuration = System.currentTimeMillis() - mActiveLogs.getLast().getTime(); String onDurationString = String.format("%02d:%02d:%02d.%03d", (int)(onDuration / (1000 * 60 * 60)), (int)((onDuration / (1000 * 60)) % 60), (int)((onDuration / 1000) % 60), (int)(onDuration % 1000)); writer.println(" time since enabled: " + onDurationString + "\n"); } writer.println("Enable log:"); for (ActiveLog log : mActiveLogs) { writer.println(log); } writer.println("\n" + mBleApps.size() + " BLE Apps registered:"); for (ClientDeathRecipient app : mBleApps.values()) { writer.println(app.getPackageName()); } writer.flush(); } if (mBluetoothBinder == null) { if (mBluetoothBinder == null) { errorMsg = "Bluetooth Service not connected"; errorMsg = "Bluetooth Service not connected"; } else { } else { try { try { mBluetoothBinder.dump(fd, args); mBluetoothBinder.dump(fd, args); } catch (RemoteException re) { } catch (RemoteException re) { errorMsg = "RemoteException while calling Bluetooth Service"; errorMsg = "RemoteException while dumping Bluetooth Service"; } } } } if (errorMsg != null) { if (errorMsg != null) { // Silently return if we are extracting metrics in Protobuf format // Silently return if we are extracting metrics in Protobuf format if ((args.length > 0) && args[0].startsWith("--proto")) if (protoOut) return; return; writer.println(errorMsg); writer.println(errorMsg); } } } } Loading Loading
framework/java/android/bluetooth/BluetoothAdapter.java +33 −73 Original line number Original line Diff line number Diff line Loading @@ -680,30 +680,7 @@ public final class BluetoothAdapter { } } /** /** * Performs action based on user action to turn BT ON * Turns off Bluetooth LE which was earlier turned on by calling enableBLE(). * or OFF if BT is in BLE_ON state */ private void notifyUserAction(boolean enable) { try { mServiceLock.readLock().lock(); if (mService == null) { Log.e(TAG, "mService is null"); return; } if (enable) { mService.onLeServiceUp(); //NA:TODO implementation pending } else { mService.onBrEdrDown(); //NA:TODO implementation pending } } catch (RemoteException e) { Log.e(TAG, "", e); } finally { mServiceLock.readLock().unlock(); } } /** * 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 * <p> If the internal Adapter state is STATE_BLE_ON, this would trigger the transition * to STATE_OFF and completely shut-down Bluetooth * to STATE_OFF and completely shut-down Bluetooth Loading Loading @@ -733,61 +710,50 @@ public final class BluetoothAdapter { if (!isBleScanAlwaysAvailable()) return false; if (!isBleScanAlwaysAvailable()) return false; int state = getLeState(); int state = getLeState(); if (state == BluetoothAdapter.STATE_ON) { if (state == BluetoothAdapter.STATE_ON || state == BluetoothAdapter.STATE_BLE_ON) { if (DBG) Log.d (TAG, "STATE_ON: shouldn't disable"); String packageName = ActivityThread.currentPackageName(); try { if (DBG) Log.d (TAG, "disableBLE(): de-registering " + packageName); 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 { try { bleAppCnt = mManagerService.updateBleAppCount(mToken, false); mManagerService.updateBleAppCount(mToken, false, packageName); } catch (RemoteException e) { } catch (RemoteException e) { Log.e(TAG, "", e); Log.e(TAG, "", e); } } if (bleAppCnt == 0) { // Disable only if there are no other clients notifyUserAction(false); } return true; return true; } } if (DBG) Log.d (TAG, "STATE_OFF: Already disabled"); if (DBG) Log.d (TAG, "disableBLE(): Already disabled"); return false; return false; } } /** /** * Special Applications who want to only turn on Bluetooth Low Energy (BLE) would * Applications who want to only use Bluetooth Low Energy (BLE) can call enableBLE. * 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 * enableBLE registers the existence of an app using only LE functions. * * enableBLE may enable Bluetooth to an LE only mode so that an app can use * LE related features (BluetoothGatt or BluetoothGattServer classes) * * If the user disables Bluetooth while an app is registered to use LE only features, * Bluetooth will remain on in LE only mode for the app. * * When Bluetooth is in LE only mode, it is not shown as ON to the UI. * * <p>This is an asynchronous call: it returns immediately, and * clients should listen for {@link #ACTION_BLE_STATE_CHANGED} * clients should listen for {@link #ACTION_BLE_STATE_CHANGED} * to be notified of subsequent adapter state changes. If this call returns * to be notified of adapter state changes. * true, then the adapter state will immediately transition from {@link * * #STATE_OFF} to {@link #STATE_BLE_TURNING_ON}, and some time * If this call returns * true, then the adapter state is either in a mode where * later transition to either {@link #STATE_OFF} or {@link * LE is available, or will transition from {@link #STATE_OFF} to {@link #STATE_BLE_TURNING_ON}, * #STATE_BLE_ON}. If this call returns false then there was an * and some time later transition to either {@link #STATE_OFF} or {@link #STATE_BLE_ON}. * immediate problem that will prevent the adapter from being turned on - * * such as Airplane mode, or the adapter is already turned on. * If this call returns false then there was an immediate problem that prevents the * (@link #ACTION_BLE_STATE_CHANGED) returns the Bluetooth Adapter's various * adapter from being turned on - such as Airplane mode. * * {@link #ACTION_BLE_STATE_CHANGED} returns the Bluetooth Adapter's various * states, It includes all the classic Bluetooth Adapter states along with * states, It includes all the classic Bluetooth Adapter states along with * internal BLE only states * internal BLE only states * * * @return true to indicate Bluetooth LE start-up has begun, or false on * @return true to indicate Bluetooth LE will be available, or false on * immediate error * immediate error * @hide * @hide */ */ Loading @@ -796,13 +762,14 @@ public final class BluetoothAdapter { if (!isBleScanAlwaysAvailable()) return false; if (!isBleScanAlwaysAvailable()) return false; try { try { mManagerService.updateBleAppCount(mToken, true); String packageName = ActivityThread.currentPackageName(); mManagerService.updateBleAppCount(mToken, true, packageName); if (isLeEnabled()) { if (isLeEnabled()) { if (DBG) Log.d(TAG, "enableBLE(): Bluetooth already enabled"); if (DBG) Log.d(TAG, "enableBLE(): Bluetooth already enabled"); return true; return true; } } if (DBG) Log.d(TAG, "enableBLE(): Calling enable"); if (DBG) Log.d(TAG, "enableBLE(): Calling enable"); return mManagerService.enable(ActivityThread.currentPackageName()); return mManagerService.enable(packageName); } catch (RemoteException e) { } catch (RemoteException e) { Log.e(TAG, "", e); Log.e(TAG, "", e); } } Loading Loading @@ -1940,9 +1907,6 @@ public final class BluetoothAdapter { } else if (profile == BluetoothProfile.MAP_CLIENT) { } else if (profile == BluetoothProfile.MAP_CLIENT) { BluetoothMapClient mapClient = new BluetoothMapClient(context, listener); BluetoothMapClient mapClient = new BluetoothMapClient(context, listener); return true; return true; } else if (profile == BluetoothProfile.INPUT_HOST) { BluetoothInputHost iHost = new BluetoothInputHost(context, listener); return true; } else { } else { return false; return false; } } Loading Loading @@ -2019,10 +1983,6 @@ public final class BluetoothAdapter { BluetoothMapClient mapClient = (BluetoothMapClient)proxy; BluetoothMapClient mapClient = (BluetoothMapClient)proxy; mapClient.close(); mapClient.close(); break; break; case BluetoothProfile.INPUT_HOST: BluetoothInputHost iHost = (BluetoothInputHost) proxy; iHost.close(); break; } } } } Loading Loading @@ -2094,7 +2054,7 @@ public final class BluetoothAdapter { return true; return true; } } try { try { return mManagerService.enableNoAutoConnect(); return mManagerService.enableNoAutoConnect(ActivityThread.currentPackageName()); } catch (RemoteException e) {Log.e(TAG, "", e);} } catch (RemoteException e) {Log.e(TAG, "", e);} return false; return false; } } Loading
framework/java/android/bluetooth/IBluetoothManager.aidl +3 −3 Original line number Original line Diff line number Diff line Loading @@ -35,7 +35,7 @@ interface IBluetoothManager void unregisterStateChangeCallback(in IBluetoothStateChangeCallback callback); void unregisterStateChangeCallback(in IBluetoothStateChangeCallback callback); boolean isEnabled(); boolean isEnabled(); boolean enable(String packageName); boolean enable(String packageName); boolean enableNoAutoConnect(); boolean enableNoAutoConnect(String packageName); boolean disable(String packageName, boolean persist); boolean disable(String packageName, boolean persist); int getState(); int getState(); IBluetoothGatt getBluetoothGatt(); IBluetoothGatt getBluetoothGatt(); Loading @@ -47,7 +47,7 @@ interface IBluetoothManager String getName(); String getName(); boolean isBleScanAlwaysAvailable(); boolean isBleScanAlwaysAvailable(); int updateBleAppCount(IBinder b, boolean enable); int updateBleAppCount(IBinder b, boolean enable, String packageName); boolean isBleAppPresent(); boolean isBleAppPresent(); } }
service/java/com/android/server/bluetooth/BluetoothManagerService.java +125 −49 Original line number Original line Diff line number Diff line Loading @@ -58,14 +58,16 @@ import android.os.UserManagerInternal.UserRestrictionsListener; import android.provider.Settings; import android.provider.Settings; import android.provider.Settings.SettingNotFoundException; import android.provider.Settings.SettingNotFoundException; import android.util.Slog; import android.util.Slog; import java.util.concurrent.locks.ReentrantReadWriteLock; import java.io.FileDescriptor; import java.io.FileDescriptor; import java.io.PrintWriter; import java.io.PrintWriter; import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.locks.ReentrantReadWriteLock; import java.util.HashMap; import java.util.HashMap; import java.util.LinkedList; import java.util.Map; import java.util.Map; class BluetoothManagerService extends IBluetoothManager.Stub { class BluetoothManagerService extends IBluetoothManager.Stub { private static final String TAG = "BluetoothManagerService"; private static final String TAG = "BluetoothManagerService"; private static final boolean DBG = true; private static final boolean DBG = true; Loading Loading @@ -138,16 +140,46 @@ class BluetoothManagerService extends IBluetoothManager.Stub { new ReentrantReadWriteLock(); new ReentrantReadWriteLock(); private boolean mBinding; private boolean mBinding; private boolean mUnbinding; private boolean mUnbinding; // used inside handler thread // used inside handler thread private boolean mQuietEnable = false; private boolean mQuietEnable = false; // configuarion from external IBinder call which is used to private boolean mEnable; /** * Used for tracking apps that enabled / disabled Bluetooth. */ private class ActiveLog { private String mPackageName; private boolean mEnable; private long mTimestamp; public ActiveLog(String packageName, boolean enable, long timestamp) { mPackageName = packageName; mEnable = enable; mTimestamp = timestamp; } public long getTime() { return mTimestamp; } public String toString() { return android.text.format.DateFormat.format("MM-dd hh:mm:ss ", mTimestamp) + (mEnable ? " Enabled " : " Disabled ") + " by " + mPackageName; } } private LinkedList<ActiveLog> mActiveLogs; // configuration from external IBinder call which is used to // synchronize with broadcast receiver. // synchronize with broadcast receiver. private boolean mQuietEnableExternal; private boolean mQuietEnableExternal; // configuarion from external IBinder call which is used to // synchronize with broadcast receiver. private boolean mEnableExternal; private boolean mEnableExternal; // used inside handler thread private boolean mEnable; // Map of apps registered to keep BLE scanning on. private Map<IBinder, ClientDeathRecipient> mBleApps = new ConcurrentHashMap<IBinder, ClientDeathRecipient>(); private int mState; private int mState; private final BluetoothHandler mHandler; private final BluetoothHandler mHandler; private int mErrorRecoveryRetryCounter; private int mErrorRecoveryRetryCounter; Loading Loading @@ -253,12 +285,12 @@ class BluetoothManagerService extends IBluetoothManager.Stub { } else if (st == BluetoothAdapter.STATE_ON){ } else if (st == BluetoothAdapter.STATE_ON){ // disable without persisting the setting // disable without persisting the setting Slog.d(TAG, "Calling disable"); Slog.d(TAG, "Calling disable"); sendDisableMsg(); sendDisableMsg("airplane mode"); } } } else if (mEnableExternal) { } else if (mEnableExternal) { // enable without persisting the setting // enable without persisting the setting Slog.d(TAG, "Calling enable"); Slog.d(TAG, "Calling enable"); sendEnableMsg(mQuietEnableExternal); sendEnableMsg(mQuietEnableExternal, "airplane mode"); } } } } } } Loading @@ -273,6 +305,7 @@ class BluetoothManagerService extends IBluetoothManager.Stub { mPermissionReviewRequired = context.getResources().getBoolean( mPermissionReviewRequired = context.getResources().getBoolean( com.android.internal.R.bool.config_permissionReviewRequired); com.android.internal.R.bool.config_permissionReviewRequired); mActiveLogs = new LinkedList<ActiveLog>(); mBluetooth = null; mBluetooth = null; mBluetoothBinder = null; mBluetoothBinder = null; mBluetoothGatt = null; mBluetoothGatt = null; Loading Loading @@ -300,15 +333,15 @@ class BluetoothManagerService extends IBluetoothManager.Stub { mEnableExternal = true; mEnableExternal = true; } } int sysUiUid = -1; int systemUiUid = -1; try { try { sysUiUid = mContext.getPackageManager().getPackageUidAsUser("com.android.systemui", systemUiUid = mContext.getPackageManager().getPackageUidAsUser("com.android.systemui", PackageManager.MATCH_SYSTEM_ONLY, UserHandle.USER_SYSTEM); PackageManager.MATCH_SYSTEM_ONLY, UserHandle.USER_SYSTEM); } catch (PackageManager.NameNotFoundException e) { } catch (PackageManager.NameNotFoundException e) { // Some platforms, such as wearables do not have a system ui. // Some platforms, such as wearables do not have a system ui. Slog.w(TAG, "Unable to resolve SystemUI's UID.", e); Slog.w(TAG, "Unable to resolve SystemUI's UID.", e); } } mSystemUiUid = sysUiUid; mSystemUiUid = systemUiUid; } } /** /** Loading Loading @@ -488,8 +521,14 @@ class BluetoothManagerService extends IBluetoothManager.Stub { } } class ClientDeathRecipient implements IBinder.DeathRecipient { class ClientDeathRecipient implements IBinder.DeathRecipient { private String mPackageName; public ClientDeathRecipient(String packageName) { mPackageName = packageName; } public void binderDied() { public void binderDied() { if (DBG) Slog.d(TAG, "Binder is dead - unregister Ble App"); if (DBG) Slog.d(TAG, "Binder is dead - unregister " + mPackageName); if (isBleAppPresent()) { if (isBleAppPresent()) { // Nothing to do, another app is here. // Nothing to do, another app is here. return; return; Loading @@ -508,10 +547,11 @@ class BluetoothManagerService extends IBluetoothManager.Stub { mBluetoothLock.readLock().unlock(); mBluetoothLock.readLock().unlock(); } } } } } /** Internal death rec list */ public String getPackageName() { Map<IBinder, ClientDeathRecipient> mBleApps = new ConcurrentHashMap<IBinder, ClientDeathRecipient>(); return mPackageName; } } @Override @Override public boolean isBleScanAlwaysAvailable() { public boolean isBleScanAlwaysAvailable() { Loading Loading @@ -569,28 +609,22 @@ class BluetoothManagerService extends IBluetoothManager.Stub { } } } } public int updateBleAppCount(IBinder token, boolean enable) { public int updateBleAppCount(IBinder token, boolean enable, String packageName) { if (enable) { ClientDeathRecipient r = mBleApps.get(token); ClientDeathRecipient r = mBleApps.get(token); if (r == null) { if (r == null && enable) { ClientDeathRecipient deathRec = new ClientDeathRecipient(); ClientDeathRecipient deathRec = new ClientDeathRecipient(packageName); try { try { token.linkToDeath(deathRec, 0); token.linkToDeath(deathRec, 0); } catch (RemoteException ex) { } catch (RemoteException ex) { throw new IllegalArgumentException("Wake lock is already dead."); throw new IllegalArgumentException("BLE app (" + packageName + ") already dead!"); } } mBleApps.put(token, deathRec); mBleApps.put(token, deathRec); if (DBG) Slog.d(TAG, "Registered for death Notification"); if (DBG) Slog.d(TAG, "Registered for death of " + packageName); } } else if (!enable && r != null) { } else { ClientDeathRecipient r = mBleApps.get(token); if (r != null) { // Unregister death recipient as the app goes away. // Unregister death recipient as the app goes away. token.unlinkToDeath(r, 0); token.unlinkToDeath(r, 0); mBleApps.remove(token); mBleApps.remove(token); if (DBG) Slog.d(TAG, "Unregistered for death Notification"); if (DBG) Slog.d(TAG, "Unregistered for death of " + packageName); } } } int appCount = mBleApps.size(); int appCount = mBleApps.size(); if (DBG) Slog.d(TAG, appCount + " registered Ble Apps"); if (DBG) Slog.d(TAG, appCount + " registered Ble Apps"); Loading Loading @@ -671,7 +705,7 @@ class BluetoothManagerService extends IBluetoothManager.Stub { } } } } public boolean enableNoAutoConnect() public boolean enableNoAutoConnect(String packageName) { { if (isBluetoothDisallowed()) { if (isBluetoothDisallowed()) { if (DBG) { if (DBG) { Loading @@ -696,7 +730,7 @@ class BluetoothManagerService extends IBluetoothManager.Stub { synchronized(mReceiver) { synchronized(mReceiver) { mQuietEnableExternal = true; mQuietEnableExternal = true; mEnableExternal = true; mEnableExternal = true; sendEnableMsg(true); sendEnableMsg(true, packageName); } } return true; return true; } } Loading Loading @@ -729,7 +763,7 @@ class BluetoothManagerService extends IBluetoothManager.Stub { } } if (DBG) { if (DBG) { Slog.d(TAG,"enable(): mBluetooth =" + mBluetooth + Slog.d(TAG,"enable(" + packageName + "): mBluetooth =" + mBluetooth + " mBinding = " + mBinding + " mState = " + " mBinding = " + mBinding + " mState = " + BluetoothAdapter.nameForState(mState)); BluetoothAdapter.nameForState(mState)); } } Loading @@ -738,7 +772,7 @@ class BluetoothManagerService extends IBluetoothManager.Stub { mQuietEnableExternal = false; mQuietEnableExternal = false; mEnableExternal = true; mEnableExternal = true; // waive WRITE_SECURE_SETTINGS permission check // waive WRITE_SECURE_SETTINGS permission check sendEnableMsg(false); sendEnableMsg(false, packageName); } } if (DBG) Slog.d(TAG, "enable returning"); if (DBG) Slog.d(TAG, "enable returning"); return true; return true; Loading Loading @@ -774,7 +808,7 @@ class BluetoothManagerService extends IBluetoothManager.Stub { persistBluetoothSetting(BLUETOOTH_OFF); persistBluetoothSetting(BLUETOOTH_OFF); } } mEnableExternal = false; mEnableExternal = false; sendDisableMsg(); sendDisableMsg(packageName); } } return true; return true; } } Loading Loading @@ -928,7 +962,7 @@ class BluetoothManagerService extends IBluetoothManager.Stub { } } if (mEnableExternal && isBluetoothPersistedStateOnBluetooth()) { if (mEnableExternal && isBluetoothPersistedStateOnBluetooth()) { if (DBG) Slog.d(TAG, "Auto-enabling Bluetooth."); if (DBG) Slog.d(TAG, "Auto-enabling Bluetooth."); sendEnableMsg(mQuietEnableExternal); sendEnableMsg(mQuietEnableExternal, "system boot"); } else if (!isNameAndAddressSet()) { } else if (!isNameAndAddressSet()) { if (DBG) Slog.d(TAG, "Getting adapter name and address"); if (DBG) Slog.d(TAG, "Getting adapter name and address"); Message getMsg = mHandler.obtainMessage(MESSAGE_GET_NAME_AND_ADDRESS); Message getMsg = mHandler.obtainMessage(MESSAGE_GET_NAME_AND_ADDRESS); Loading Loading @@ -1897,13 +1931,24 @@ class BluetoothManagerService extends IBluetoothManager.Stub { return false; return false; } } private void sendDisableMsg() { private void sendDisableMsg(String packageName) { mHandler.sendMessage(mHandler.obtainMessage(MESSAGE_DISABLE)); mHandler.sendMessage(mHandler.obtainMessage(MESSAGE_DISABLE)); addActiveLog(packageName, false); } } private void sendEnableMsg(boolean quietMode) { private void sendEnableMsg(boolean quietMode, String packageName) { mHandler.sendMessage(mHandler.obtainMessage(MESSAGE_ENABLE, mHandler.sendMessage(mHandler.obtainMessage(MESSAGE_ENABLE, quietMode ? 1 : 0, 0)); quietMode ? 1 : 0, 0)); addActiveLog(packageName, true); } private void addActiveLog(String packageName, boolean enable) { synchronized (mActiveLogs) { if (mActiveLogs.size() > 10) { mActiveLogs.remove(); } mActiveLogs.add(new ActiveLog(packageName, enable, System.currentTimeMillis())); } } } private void recoverBluetoothServiceFromError(boolean clearBle) { private void recoverBluetoothServiceFromError(boolean clearBle) { Loading Loading @@ -1974,19 +2019,50 @@ class BluetoothManagerService extends IBluetoothManager.Stub { public void dump(FileDescriptor fd, PrintWriter writer, String[] args) { public void dump(FileDescriptor fd, PrintWriter writer, String[] args) { mContext.enforceCallingOrSelfPermission(android.Manifest.permission.DUMP, TAG); mContext.enforceCallingOrSelfPermission(android.Manifest.permission.DUMP, TAG); String errorMsg = null; String errorMsg = null; boolean protoOut = (args.length > 0) && args[0].startsWith("--proto"); if (!protoOut) { writer.println("Bluetooth Status"); writer.println(" enabled: " + isEnabled()); writer.println(" state: " + BluetoothAdapter.nameForState(mState)); writer.println(" address: " + mAddress); writer.println(" name: " + mName); if (mEnable) { long onDuration = System.currentTimeMillis() - mActiveLogs.getLast().getTime(); String onDurationString = String.format("%02d:%02d:%02d.%03d", (int)(onDuration / (1000 * 60 * 60)), (int)((onDuration / (1000 * 60)) % 60), (int)((onDuration / 1000) % 60), (int)(onDuration % 1000)); writer.println(" time since enabled: " + onDurationString + "\n"); } writer.println("Enable log:"); for (ActiveLog log : mActiveLogs) { writer.println(log); } writer.println("\n" + mBleApps.size() + " BLE Apps registered:"); for (ClientDeathRecipient app : mBleApps.values()) { writer.println(app.getPackageName()); } writer.flush(); } if (mBluetoothBinder == null) { if (mBluetoothBinder == null) { errorMsg = "Bluetooth Service not connected"; errorMsg = "Bluetooth Service not connected"; } else { } else { try { try { mBluetoothBinder.dump(fd, args); mBluetoothBinder.dump(fd, args); } catch (RemoteException re) { } catch (RemoteException re) { errorMsg = "RemoteException while calling Bluetooth Service"; errorMsg = "RemoteException while dumping Bluetooth Service"; } } } } if (errorMsg != null) { if (errorMsg != null) { // Silently return if we are extracting metrics in Protobuf format // Silently return if we are extracting metrics in Protobuf format if ((args.length > 0) && args[0].startsWith("--proto")) if (protoOut) return; return; writer.println(errorMsg); writer.println(errorMsg); } } } } Loading