Android应用开发:修改iBeacon Flags值为0x020106求助
Hey there, let's figure out how to modify the BLE advertising Flags field in your Android app from 0x02011A to 0x020106. First, let's clarify what these values represent to make sure we're on the same page:
- The Flags field in BLE advertising follows the structure:
[Length (1 byte)] [Type (0x01 for Flags)] [Value (1 byte)] 0x02011Ameans: Length = 0x02, Type = 0x01, Value = 0x1A (LE General Discoverable Mode + BR/EDR Not Supported)0x020106means: Length = 0x02, Type = 0x01, Value = 0x06 (LE General Discoverable Mode + BR/EDR Controller Supported but Host Not Supported)
Prerequisites
First, make sure you have the necessary permissions in your AndroidManifest.xml:
<!-- Basic BLE permissions --> <uses-permission android:name="android.permission.BLUETOOTH" android:maxSdkVersion="30" /> <uses-permission android:name="android.permission.BLUETOOTH_ADMIN" android:maxSdkVersion="30" /> <!-- For Android 12+ --> <uses-permission android:name="android.permission.BLUETOOTH_ADVERTISE" /> <uses-permission android:name="android.permission.BLUETOOTH_CONNECT" /> <!-- Location permission required for BLE operations on Android 6.0+ --> <uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" /> <uses-feature android:name="android.hardware.bluetooth_le" android:required="true" />
Don't forget to request runtime permissions for ACCESS_FINE_LOCATION (Android 6.0+) and BLUETOOTH_ADVERTISE (Android 12+).
Implementation Examples
1. Android 8.0 (API 26) and Above
Starting from API 26, Android uses BluetoothLeAdvertiser for more control over BLE advertising. Since the official AdvertiseData.Builder doesn't expose a direct method to set Flags, we'll use reflection to add raw advertising data:
import android.Manifest import android.bluetooth.BluetoothManager import android.bluetooth.le.AdvertiseCallback import android.bluetooth.le.AdvertiseData import android.bluetooth.le.AdvertiseSettings import android.content.Context import android.content.pm.PackageManager import android.os.Build import android.util.Log import androidx.appcompat.app.AppCompatActivity import android.os.Bundle class MainActivity : AppCompatActivity() { private val PERMISSION_REQUEST_CODE = 1001 override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) setContentView(R.layout.activity_main) // Check permissions first if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) { val requiredPermissions = mutableListOf(Manifest.permission.ACCESS_FINE_LOCATION) if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) { requiredPermissions.add(Manifest.permission.BLUETOOTH_ADVERTISE) requiredPermissions.add(Manifest.permission.BLUETOOTH_CONNECT) } if (requiredPermissions.any { checkSelfPermission(it) != PackageManager.PERMISSION_GRANTED }) { requestPermissions(requiredPermissions.toTypedArray(), PERMISSION_REQUEST_CODE) return } } // Initialize BLE adapter val bluetoothManager = getSystemService(Context.BLUETOOTH_SERVICE) as BluetoothManager val bluetoothAdapter = bluetoothManager.adapter // Ensure Bluetooth is enabled if (!bluetoothAdapter.isEnabled) { Log.e("BLE_ADVERT", "Bluetooth is not enabled") // You can request to enable Bluetooth here if needed return } // Build custom advertising data with Flags set to 0x06 val advertiseDataBuilder = AdvertiseData.Builder() .setIncludeDeviceName(false) // Optional: Disable device name to save space .setIncludeTxPowerLevel(false) // Optional: Disable TX power level // Add custom Flags field using reflection try { // Raw Flags data: [0x02 (length), 0x01 (type), 0x06 (value)] val flagsRawData = byteArrayOf(0x02.toByte(), 0x01.toByte(), 0x06.toByte()) val addRawDataMethod = AdvertiseData.Builder::class.java.getDeclaredMethod( "addRawData", ByteArray::class.java ) addRawDataMethod.isAccessible = true addRawDataMethod.invoke(advertiseDataBuilder, flagsRawData) } catch (e: Exception) { Log.e("BLE_ADVERT", "Failed to add custom Flags: ${e.message}") } val advertiseData = advertiseDataBuilder.build() // Build advertising settings val advertiseSettings = AdvertiseSettings.Builder() .setAdvertiseMode(AdvertiseSettings.ADVERTISE_MODE_LOW_POWER) .setTxPowerLevel(AdvertiseSettings.ADVERTISE_TX_POWER_MEDIUM) .setConnectable(true) .build() // Start advertising val advertiser = bluetoothAdapter.bluetoothLeAdvertiser advertiser.startAdvertising(advertiseSettings, advertiseData, object : AdvertiseCallback() { override fun onStartSuccess(settingsInEffect: AdvertiseSettings?) { super.onStartSuccess(settingsInEffect) Log.d("BLE_ADVERT", "Advertising started successfully! Flags set to 0x06") } override fun onStartFailure(errorCode: Int) { super.onStartFailure(errorCode) val errorMsg = when(errorCode) { ADVERTISE_FAILED_ALREADY_STARTED -> "Already advertising" ADVERTISE_FAILED_DATA_TOO_LARGE -> "Advertising data too long (max 31 bytes)" ADVERTISE_FAILED_FEATURE_UNSUPPORTED -> "BLE advertising not supported on this device" ADVERTISE_FAILED_INTERNAL_ERROR -> "Internal error" ADVERTISE_FAILED_TOO_MANY_ADVERTISERS -> "Too many advertisers" else -> "Unknown error: $errorCode" } Log.e("BLE_ADVERT", "Advertising failed: $errorMsg") } }) } override fun onRequestPermissionsResult(requestCode: Int, permissions: Array<out String>, grantResults: IntArray) { super.onRequestPermissionsResult(requestCode, permissions, grantResults) if (requestCode == PERMISSION_REQUEST_CODE) { if (grantResults.all { it == PackageManager.PERMISSION_GRANTED }) { // Permissions granted, restart advertising setup recreate() } else { Log.e("BLE_ADVERT", "Required permissions not granted") } } } }
2. Android 8.0 (API 25) and Below
For older versions, we use BluetoothAdapter.startLeAdvertising() directly, which allows passing raw advertising data:
import android.Manifest; import android.bluetooth.BluetoothAdapter; import android.bluetooth.BluetoothManager; import android.bluetooth.le.AdvertiseCallback; import android.bluetooth.le.AdvertiseSettings; import android.content.Context; import android.content.pm.PackageManager; import android.os.Build; import android.util.Log; import androidx.appcompat.app.AppCompatActivity; import android.os.Bundle; public class MainActivity extends AppCompatActivity { private static final int PERMISSION_REQUEST_CODE = 1001; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); // Check permissions if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) { if (checkSelfPermission(Manifest.permission.ACCESS_FINE_LOCATION) != PackageManager.PERMISSION_GRANTED) { requestPermissions(new String[]{Manifest.permission.ACCESS_FINE_LOCATION}, PERMISSION_REQUEST_CODE); return; } } // Initialize BLE adapter BluetoothManager bluetoothManager = (BluetoothManager) getSystemService(Context.BLUETOOTH_SERVICE); BluetoothAdapter bluetoothAdapter = bluetoothManager.getAdapter(); if (!bluetoothAdapter.isEnabled()) { Log.e("BLE_ADVERT", "Bluetooth is disabled"); return; } // Raw advertising data with Flags set to 0x06 byte[] advertiseData = new byte[]{0x02, 0x01, 0x06}; // Start advertising bluetoothAdapter.startLeAdvertising(null, advertiseData, null, new AdvertiseCallback() { @Override public void onStartSuccess(AdvertiseSettings settingsInEffect) { super.onStartSuccess(settingsInEffect); Log.d("BLE_ADVERT", "Advertising started with Flags 0x06"); } @Override public void onStartFailure(int errorCode) { super.onStartFailure(errorCode); Log.e("BLE_ADVERT", "Advertising failed with error code: " + errorCode); } }); } @Override public void onRequestPermissionsResult(int requestCode, String[] permissions, int[] grantResults) { super.onRequestPermissionsResult(requestCode, permissions, grantResults); if (requestCode == PERMISSION_REQUEST_CODE) { if (grantResults.length > 0 && grantResults[0] == PackageManager.PERMISSION_GRANTED) { recreate(); } else { Log.e("BLE_ADVERT", "Location permission required for BLE advertising"); } } } }
Key Notes
- Data Length Limit: BLE advertising data can't exceed 31 bytes. If you add other fields (like device name or service UUIDs), make sure the total length stays under this limit.
- System Overrides: Some devices might override your custom Flags if you enable certain settings (like
setConnectable(true)). If this happens, try disabling auto-included fields (like device name/TX power) or adjust the reflection approach. - Testing: Use nRF Connect to scan for your device and verify the Flags field shows
0x020106.
内容的提问来源于stack exchange,提问作者user9738849

