You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何在单台Android设备创建虚拟BLE GATT Server并实现数据本地存储?

单台Android设备实现BLE虚拟设备模拟与数据本地保存方案

核心思路

在单台Android设备上同时运行BLE GATT Server(模拟目标打印外设)和BLE GATT Client(作为中央设备连接自身模拟的外设),拦截并持久化目标APK发送给BLE设备的打印数据。

分步实现

1. 模拟匹配目标设备的BLE GATT Server

  • 先用nRF Connect等工具扫描原BLE打印设备,获取其服务UUID、特征UUID及设备名称,确保虚拟设备的参数与原设备完全一致,否则目标APK无法识别。
  • 实现GATT Server,注册对应服务和可写入特征,在特征写入回调中捕获数据并写入本地文件。

2. 本地BLE Client连接自身模拟的GATT Server

  • 启动BLE扫描,过滤自身模拟的设备名称或UUID,扫描到后发起连接并完成服务发现,让目标APK能正常连接到虚拟设备。

3. 关键代码示例

GATT Server初始化与数据捕获

private var gattServer: BluetoothGattServer? = null

private val gattServerCallback = object : GattServerCallback() {
    override fun onCharacteristicWriteRequest(
        device: BluetoothDevice?,
        requestId: Int,
        characteristic: BluetoothGattCharacteristic?,
        preparedWrite: Boolean,
        responseNeeded: Boolean,
        offset: Int,
        value: ByteArray?
    ) {
        super.onCharacteristicWriteRequest(device, requestId, characteristic, preparedWrite, responseNeeded, offset, value)
        // 捕获打印数据并写入本地文件
        value?.takeIf { it.isNotEmpty() }?.let { data ->
            val outputFile = File(getExternalFilesDir(null), "ble_print_data.txt")
            FileOutputStream(outputFile, true).use { fos ->
                fos.write(data)
                fos.write("\n--- 分割线 ---\n".toByteArray())
            }
        }
        // 向客户端发送成功响应
        gattServer?.sendResponse(device, requestId, BluetoothGatt.GATT_SUCCESS, 0, null)
    }
}

// 初始化GATT Server
fun initGattServer(context: Context) {
    val bluetoothManager = context.getSystemService(Context.BLUETOOTH_SERVICE) as BluetoothManager
    val bluetoothAdapter = bluetoothManager.adapter
    gattServer = bluetoothAdapter.openGattServer(context, gattServerCallback)
    
    // 替换为原BLE打印设备的服务和特征UUID
    val serviceUuid = UUID.fromString("0000ffe0-0000-1000-8000-00805f9b34fb")
    val charUuid = UUID.fromString("0000ffe1-0000-1000-8000-00805f9b34fb")
    
    val service = BluetoothGattService(serviceUuid, BluetoothGattService.SERVICE_TYPE_PRIMARY)
    val writeChar = BluetoothGattCharacteristic(
        charUuid,
        BluetoothGattCharacteristic.PROPERTY_WRITE or BluetoothGattCharacteristic.PROPERTY_WRITE_NO_RESPONSE,
        BluetoothGattCharacteristic.PERMISSION_WRITE
    )
    service.addCharacteristic(writeChar)
    gattServer?.addService(service)
}

本地BLE Client扫描与连接

private val scanCallback = object : ScanCallback() {
    override fun onScanResult(callbackType: Int, result: ScanResult?) {
        super.onScanResult(callbackType, result)
        result?.device?.takeIf { it.name == "VirtualBLEPrinter" }?.let { virtualDevice ->
            bluetoothAdapter.bluetoothLeScanner.stopScan(this)
            // 连接到自身模拟的GATT Server
            virtualDevice.connectGatt(context, false, object : BluetoothGattCallback() {
                override fun onConnectionStateChange(gatt: BluetoothGatt?, status: Int, newState: Int) {
                    super.onConnectionStateChange(gatt, status, newState)
                    if (newState == BluetoothProfile.STATE_CONNECTED) {
                        gatt?.discoverServices()
                    }
                }
            })
        }
    }
}

// 启动扫描
fun startLocalScan(bluetoothAdapter: BluetoothAdapter) {
    val scanFilters = listOf(ScanFilter.Builder().setDeviceName("VirtualBLEPrinter").build())
    val scanSettings = ScanSettings.Builder().build()
    bluetoothAdapter.bluetoothLeScanner.startScan(scanFilters, scanSettings, scanCallback)
}

4. 权限配置

在AndroidManifest.xml中声明必要权限,并在运行时动态申请:

<uses-permission android:name="android.permission.BLUETOOTH" />
<uses-permission android:name="android.permission.BLUETOOTH_ADMIN" />
<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
<!-- Android 12+ 新增权限 -->
<uses-permission android:name="android.permission.BLUETOOTH_SCAN" />
<uses-permission android:name="android.permission.BLUETOOTH_ADVERTISE" />
<uses-permission android:name="android.permission.BLUETOOTH_CONNECT" />

<uses-feature android:name="android.hardware.bluetooth_le" android:required="true" />

5. 注意事项

  • 部分Android设备存在自身BLE Client无法连接自身GATT Server的限制,可尝试修改虚拟设备名称或使用随机UUID测试。
  • 确保目标APK连接BLE设备的逻辑无特殊绑定(如MAC地址锁定),否则需额外处理。
  • 数据写入路径建议使用getExternalFilesDir(null),避免Android 10+的存储权限限制。

内容的提问来源于stack exchange,提问作者Eonassis Oliveira Santos

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.04 15:45:14