Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 6ddbb5e5 authored by Nitin Arora's avatar Nitin Arora Committed by Andre Eisenbach
Browse files

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
parent 104b0f13
Loading
Loading
Loading
Loading
+304 −8
Original line number Diff line number Diff line
@@ -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: ");
            }
    };

    /**
+42 −13
Original line number Diff line number Diff line
@@ -606,9 +606,11 @@ public final class BluetoothDevice implements Parcelable {
        public void onBluetoothServiceUp(IBluetooth bluetoothService)
                throws RemoteException {
            synchronized (BluetoothDevice.class) {
                if (sService == null) {
                    sService = bluetoothService;
                }
            }
        }

        public void onBluetoothServiceDown()
            throws RemoteException {
@@ -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);
    }

+2 −0
Original line number Diff line number Diff line
@@ -102,4 +102,6 @@ interface IBluetooth

    // for dumpsys support
    String dump();
    void onLeServiceUp();
    void onBrEdrDown();
}
+2 −0
Original line number Diff line number Diff line
@@ -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();
}
+2 −0
Original line number Diff line number Diff line
@@ -44,4 +44,6 @@ interface IBluetoothManager

    String getAddress();
    String getName();
    int updateBleAppCount(IBinder b, boolean enable);
    boolean isBleAppPresent();
}
Loading