Android Package

android.bluetooth.le

Manifest

1
2
3
4
5
6
7
8
9
10
11
12
13
14
<manifest>
<uses-feature
android:name="android.hardware.bluetooth_le"
android:required="true" />

<uses-permission android:name="android.permission.BLUETOOTH" android:maxSdkVersion="30" />
<uses-permission android:name="android.permission.BLUETOOTH_ADMIN" android:maxSdkVersion="30" />
<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" android:maxSdkVersion="30"/>

<!-- Request Target API >= 31, Bluetooth permissions. -->
<uses-permission android:name="android.permission.BLUETOOTH_SCAN" android:usesPermissionFlags = "neverForLocation"/>
<uses-permission android:name="android.permission.BLUETOOTH_CONNECT" />
...
</manifest>

Phone Bluetooth State

  • BluetoothAdapter
    Pop up warning message if bluetooth setting is turned off.
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    fun checkBLESetting(): Boolean{
    //Check if phone support bluetooth and bluetooth is on
    val bluetoothAdapter: BluetoothAdapter? = BluetoothAdapter.getDefaultAdapter()
    if (!bluetoothAdapter.isEnabled){

    mAlertDlg = AlertDialog.Builder(this)
    .setMessage("Please turn on Bluetooth setting!")
    .setTitle("")
    .setPositiveButton("OK") { _, _ ->
    startActivity(Intent(Settings.ACTION_BLUETOOTH_SETTINGS))
    mAlertDlg = null
    }
    .setNeutralButton("Cancel") { _, _ ->
    mAlertDlg = null
    }
    .create()

    mAlertDlg?.show()
    mAlertDlg?.getButton(AlertDialog.BUTTON_NEUTRAL)?.setTextColor(ContextCompat.getColor(this, R.color.blue));
    mAlertDlg?.getButton(AlertDialog.BUTTON_POSITIVE)?.setTextColor(ContextCompat.getColor(this, R.color.blue));

    return false
    }
    return true
    }

Check & request for Permission

  • Request for BLE permission

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    fun requestPermissions(requestCode: Int) {
    if (Build.VERSION.SDK_INT > 30) {
    requirePermission(
    arrayOf(
    Manifest.permission.BLUETOOTH_CONNECT,
    Manifest.permission.BLUETOOTH_SCAN,
    ), requestCode = requestCode
    )
    } else {
    requirePermission(
    arrayOf(
    Manifest.permission.BLUETOOTH,
    Manifest.permission.BLUETOOTH_ADMIN,
    Manifest.permission.ACCESS_FINE_LOCATION
    ), requestCode = requestCode
    )
    }
    }
  • Check for BLE permission

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    fun checkBLEPermission(): Boolean{

    //Check permissions for ACCESS_FINE_LOCATION if API < 30
    //Check permissions for BLUETOOTH_SCAN if API > 30
    if (Build.VERSION.SDK_INT > 30){

    when (PermissionChecker.checkSelfPermission(
    this,
    Manifest.permission.BLUETOOTH_SCAN
    )) {
    PackageManager.PERMISSION_GRANTED -> {
    return true
    }
    else -> {
    if (shouldShowRequestPermissionRationale(Manifest.permission.BLUETOOTH_SCAN)) {
    // ask user to grant the permission and redirect to the app setting
    return false
    }
    requestPermissions(1)
    }
    }

    }else {
    when (PermissionChecker.checkSelfPermission(
    this,
    Manifest.permission.ACCESS_FINE_LOCATION
    )) {
    PackageManager.PERMISSION_GRANTED -> {
    return true
    }
    else -> {
    if (shouldShowRequestPermissionRationale(Manifest.permission.ACCESS_FINE_LOCATION)) {
    // ask user to grant the permission and redirect to the app setting
    return false
    }
    requestPermissions(1)
    }
    }
    }
    return false
    }

Start & Stop Scan

  • BluetoothLeScanner

    1
    2
    3
    4
    5
    6
    private val mBluetoothLeScanner: BluetoothLeScanner
    get() {
    val bluetoothManager = applicationContext.getSystemService(Context.BLUETOOTH_SERVICE) as BluetoothManager
    val bluetoothAdapter = bluetoothManager.adapter
    return bluetoothAdapter.bluetoothLeScanner
    }
  • ScanCallback

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    private val mScanCallback = object : ScanCallback() {
    override fun onScanResult(callbackType: Int, result: ScanResult?) {
    super.onScanResult(callbackType, result)

    val device = result?.device
    val deviceName = result?.device?.name

    if (deviceName != null && deviceName != ""){
    Log.d(
    "ScanDeviceActivity",
    "onScanResult(): ${result?.device?.address} - ${result?.device?.name} ${result.rssi}"
    )
    }
    ...
    }

    override fun onScanFailed(errorCode: Int) {
    super.onScanFailed(errorCode)
    Log.d("ttt", "onScanFailed: $errorCode")
    }
    }
  • Start scan

    1
    mBluetoothLeScanner.startScan(mScanCallback)
  • Stop scan

    1
    mBluetoothLeScanner.stopScan(mScanCallback)

Connect & Get servers & characteristics

  • BluetoothGattServerCallback

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    66
    67
    68
    private val gattCallback = object : BluetoothGattCallback() {

    override fun onConnectionStateChange(
    gatt: BluetoothGatt,
    status: Int,
    newState: Int
    ) {

    val intentAction: String
    when (newState) {

    BluetoothProfile.STATE_CONNECTED -> {
    gatt.discoverServices()
    }

    BluetoothProfile.STATE_DISCONNECTED -> {

    }
    }
    }

    // New services discovered
    override fun onServicesDiscovered(gatt: BluetoothGatt, status: Int) {
    when (status) {
    BluetoothGatt.GATT_SUCCESS -> {
    val gattServices = gatt.services
    gattServices.forEach { gattService ->

    val uuid = gattService.uuid.toString()
    val gattCharacteristics = gattService.characteristics

    gattCharacteristics.forEach { gattCharacteristic ->
    uuid = gattCharacteristic.uuid.toString()
    }

    }
    }
    }
    }

    // Result of a characteristic read operation
    override fun onCharacteristicRead(
    gatt: BluetoothGatt,
    characteristic: BluetoothGattCharacteristic,
    status: Int
    ) {
    when (status) {
    BluetoothGatt.GATT_SUCCESS -> {

    }
    }
    }

    override fun onCharacteristicRead(BluetoothGatt gatt,
    BluetoothGattCharacteristic characteristic, byte[] value, int status){

    }

    override fun onCharacteristicChanged(BluetoothGatt gatt,
    BluetoothGattCharacteristic characteristic, byte[] value){

    }

    override fun onCharacteristicWrite(BluetoothGatt gatt,
    BluetoothGattCharacteristic characteristic, int status){

    }
    }
  • Connect

    1
    val bluetoothGatt = device.connectGatt(this, false, gattCallback)
  • Reconnect

    1
    2
    3
    bluetoothGatt?.let { gatt ->
    gatt.connect()
    }
  • Disconnect

    1
    2
    3
    bluetoothGatt?.let { gatt ->
    gatt.disconnect()
    }
  • Close

    1
    2
    3
    4
    bluetoothGatt?.let { gatt ->
    gatt.close()
    bluetoothGatt = null
    }

Read & Write operations

  • Read

    1
    2
    3
    bluetoothGatt?.let{ gatt ->
    gatt.readCharacteristic(rxChar)
    }
  • Write

    1
    2
    3
    bluetoothGatt?.let{ gatt ->
    gatt.writeCharacteristic(txChar, value, WRITE_TYPE_DEFAULT)
    }
  • Set notification

    1
    2
    3
    bluetoothGatt?.let{ gatt ->
    gatt.setCharacteristicNotification(rxChar, true)
    }

References

https://developer.android.com/reference/android/bluetooth/le/package-summary

https://developer.android.com/reference/android/bluetooth/BluetoothGatt

https://developer.android.com/reference/android/bluetooth/BluetoothGattCallback

https://developer.android.com/reference/android/bluetooth/le/BluetoothLeScanner

https://developer.android.com/reference/android/bluetooth/le/ScanCallback

https://developer.android.com/reference/android/bluetooth/BluetoothManager

https://developer.android.com/reference/android/bluetooth/BluetoothGattServerCallback