android BLE Peripheral 做外设模拟设备,供ios、android 连接通讯。
为了能让其它设备可以发现其设备,先启动特定广播。看自己需要什么广播格式。
对于广播可见的mac address:
在调用startAdvertising();时,mac address 就会改变。
并且跟mBluetoothAdapter.getAddress();获取到的蓝牙mac 地址不一样。
这是因为在android 5.0 之后,为了保护真正的mac address。
在广播出来的地址,是经过随机可解析隐秘转换的(Resolvable private address)。
所以要在广播数据中添加mac address 输出,就要用静态蓝牙地址。
BLE设备的地址类型:
一个BLE设备,可以使用两种类型的地址(一个BLE设备可同时具备两种地址):
1、 Public Device Address (公有地址)
该地址需要向IEEE申请(购买),IEEE保证地址的唯一性。
2、Random Device Address (随机地址)
设备地址不是固定分配的,而是在设备设备启动后随机生成的。
Random Device Address分为:
(1)Static Device Address (静态地址)
在一个上电周期内保持不变。地址随机生成。
(2)Private Device Address (私有地址)
通过定时更新和地址加密两种方法,提高蓝牙地址的可靠性和安全性。
Private Device Address分为
1) Non-resolvable Private Address (不可解析私有地址)
会定时更新。以T_GAP(private_addr_int)为周期,建议15分钟。
2) Resolvable Private Address (可解析私有地址)
以T_GAP(private_addr_int)为周期,定时更新。哪怕在广播、扫描、已连接等过程中,也可能改变。它通过一个随机数和一个称作identity resolving key (IRK) 的密码生成,因此只能被拥有相同IPK的设备扫描到,可以防止被未知设备扫描和追踪。
android 对外发出广播,都一样,只是更改其中的方法:
android BLE Peripheral 模拟 ibeacon 发出ble 广播
开始广播: 增加使用BluetoothGattServer
public void startAdvertising(MockServerCallBack callBack) {
//获取BluetoothLeAdvertiser,BLE发送BLE广播用的一个API
if (mBluetoothAdvertiser == null) {
mBluetoothAdvertiser = mBluetoothAdapter.getBluetoothLeAdvertiser();
}
//创建BluetoothGattServerCallback,
// MockServerCallBack这个类继承自BluetoothGattServerCallback
// BluetoothGattServerCallback这个回调类主要是一些BLE读写的接口
// 关于BLE读写的操作都在这个Callback中完成
if (mBluetoothAdvertiser != null) {
mMockServerCallBack = callBack;
//打开BluetoothGattServer
mGattServer = mBluetoothManager.openGattServer(mActivity, mMockServerCallBack);
if (mGattServer == null) {
Log.e(TAG, "gatt is null");
}
try {
mMockServerCallBack.setupServices(mActivity, mGattServer);
mBluetoothAdvertiser.startAdvertising(createAdvSettings(true, 0)
, createAdvertiseData(BluetoothUUID.bleServerUUID), mAdvCallback);
} catch (Exception e) {
Log.v(TAG, "Fail to setup BleService");
}
} isAdvertising = true;
}
创建广播,添加serviceUuid,广播内容,自行决定。
addServiceUuid(ParcelUuid serviceUuid) 的作用。
广播数据加入serviceUuid , 当其它设备扫描时,就可以根据此serviceUuid进行特定扫描.
扫描BLE设备的时候:启动扫描时,有可选参数,传入uuid数组。
BluetoothAdapter
public boolean startLeScan(final UUID[] serviceUuids, final LeScanCallback callback);
使用该函数启动扫描的,会根据所传入的uuid,去扫描过滤,只返回符合条件的设备。
注意:部分手机可能设置uuid后,扫描不到设备,可能底层扫描问题。
public static AdvertiseData createAdvertiseData(UUID proximityUuid) {
AdvertiseData.Builder builder = new AdvertiseData.Builder();
builder.addManufacturerData(0x0301, new byte[]{0x01, 0x03});
builder.addServiceUuid(ParcelUuid.fromString(BluetoothUUID.bleServerUUID.toString())
AdvertiseData adv = builder.build();
return adv;
}
BluetoothGattServerCallback:服务事件的回调
打开notification 对应的value为 BluetoothGattDescriptor.ENABLE_NOTIFICATION_VALUE
打开indication 对应的value为 BluetoothGattDescriptor.ENABLE_INDICATION_VALUE
关闭notification 对应的value均为BluetoothGattDescriptor.DISABLE_NOTIFICATION_VALUE
服务事件响应过程:
(1) 当客户端开始写入数据时: 触发回调方法 onDescriptorWriteRequest
(2) 在 onDescriptorWriteRequest 方法中,执行下面的方法表示 写入成功 BluetoothGatt.GATT_SUCCESS
bluetoothGattServer.sendResponse(device, requestId, BluetoothGatt.GATT_SUCCESS, offset, value);
2 // 执行 sendResponse后,会触发回调方法 onCharacteristicWriteRequest
(3) 在 onCharacteristicWriteRequest方法中:
public void onCharacteristicWriteRequest(BluetoothDevice device, int requestId
, BluetoothGattCharacteristic characteristic, boolean preparedWrite, boolean responseNeeded, int offset, byte[] requestBytes) {
// 这个里可以获得 来自客户端发来的数据 requestBytes
}
(4) 处理响应内容
BluetoothGattServerCallback :
public class MockServerCallBack extends BluetoothGattServerCallback { public void setupServices(Context context, BluetoothGattServer gattServer) throws InterruptedException {
if (gattServer == null) {
throw new IllegalArgumentException("gattServer is null");
}
mGattServer = gattServer;
// 设置一个GattService以及BluetoothGattCharacteristic
BluetoothGattService service = new BluetoothGattService(BluetoothUUID.bleServerUUID,
BluetoothGattService.SERVICE_TYPE_PRIMARY); BluetoothGattService service2 = new BluetoothGattService(BluetoothUUID.bleServerUUID2,
BluetoothGattService.SERVICE_TYPE_PRIMARY); //add a read characteristic.
// 当是ios设备连接过来时,需添加BluetoothGattCharacteristic.PROPERTY_INDICATE或者notify进行兼容。
mCharacteristicRead = new BluetoothGattCharacteristic(BluetoothUUID.readDataUUID
, BluetoothGattCharacteristic.PROPERTY_READ | BluetoothGattCharacteristic.PROPERTY_INDICATE
, BluetoothGattCharacteristic.PERMISSION_READ);
//add a descriptor
BluetoothGattDescriptor descriptor = new BluetoothGattDescriptor(BluetoothUUID.CLIENT_CHARACTERISTIC_CONFIG
, BluetoothGattCharacteristic.PERMISSION_WRITE);
mCharacteristicRead.addDescriptor(descriptor);
service.addCharacteristic(mCharacteristicRead); BluetoothGattCharacteristic write = new BluetoothGattCharacteristic(
BluetoothUUID.writeDataUUID,
BluetoothGattCharacteristic.PROPERTY_WRITE | BluetoothGattCharacteristic.PROPERTY_READ | BluetoothGattCharacteristic.PROPERTY_INDICATE,
BluetoothGattCharacteristic.PERMISSION_WRITE); service.addCharacteristic(write); if (mGattServer != null && service != null) {
mGattServer.addService(service);
mGattServer.addService(service2);
} } //当添加一个GattService成功后会回调改接口。
public void onServiceAdded(int status, BluetoothGattService service) {
super.onServiceAdded(status, service);
if (status == BluetoothGatt.GATT_SUCCESS) {
Log.d(TAG, "onServiceAdded status=GATT_SUCCESS service=" + service.getUuid().toString());
} else {
Log.d(TAG, "onServiceAdded status!=GATT_SUCCESS");
}
} //BLE设备连接状态发生改变后回调的接口
public void onConnectionStateChange(android.bluetooth.BluetoothDevice device, int status,
int newState) {
super.onConnectionStateChange(device, status, newState);
Log.e(TAG, String.format("1.onConnectionStateChange:device name = %s, address = %s"
, device.getName(), device.getAddress()));
Log.d(TAG, "onConnectionStateChange status=" + status + "->" + newState);
if (newState == BluetoothProfile.STATE_DISCONNECTED) {
btClient = null; // 移除客户端连接设备
}
} //当有客户端来读数据时回调的接口
/**
* 特征被读取。当回复响应成功后,客户端会读取然后触发本方法,
*/
public void onCharacteristicReadRequest(android.bluetooth.BluetoothDevice device,
int requestId, int offset, BluetoothGattCharacteristic characteristic) {
super.onCharacteristicReadRequest(device, requestId, offset, characteristic);
characteristic.setValue(new byte[]{0x03, 0x01});
Log.e(TAG, String.format("1.onCharacteristicReadRequest:device name = %s, address = %s"
, device.getName(), device.getAddress()));
Log.e(TAG, String.format("onCharacteristicReadRequest:requestId = %s, offset = %s", requestId, offset));
boolean result = mGattServer.sendResponse(device, requestId, BluetoothGatt.GATT_SUCCESS, offset, characteristic.getValue()); Log.e(TAG, "read request send response:" + result);
} //当有客户端来写数据时回调的接口
/**
* 接受具体数据字节
*/
@Override
public void onCharacteristicWriteRequest(android.bluetooth.BluetoothDevice device,
int requestId, BluetoothGattCharacteristic characteristic, boolean preparedWrite,
boolean responseNeeded, int offset, byte[] value) {
super.onCharacteristicWriteRequest(device, requestId, characteristic, preparedWrite, responseNeeded, offset, value);
// 需调用 sendResponse 来响应,为了保持连接。
mGattServer.sendResponse(device, requestId, BluetoothGatt.GATT_SUCCESS, offset, null); // 处理其它设备写进来的数据
value. // 处理数据 byte[] value,记住连接设备 }
// 当有客户端来写Descriptor 时回调的接口
/** 描述被写入时,在这里执行bluetoothGattServer.sendResponse(device, requestId, BluetoothGatt.GATT_SUCCESS...) 时
* 触发onCharacteristicWriteRequest **/
@Override
public void onDescriptorWriteRequest(BluetoothDevice device, int requestId, BluetoothGattDescriptor descriptor, boolean preparedWrite, boolean responseNeeded, int offset, byte[] value) {
super.onDescriptorWriteRequest(device, requestId, descriptor, preparedWrite, responseNeeded, offset, value); Log.d(TAG, "onDescriptorWriteRequest:" + Arrays.toString(value));
// now tell the connected device that this was all successfull
mGattServer.sendResponse(device, requestId, BluetoothGatt.GATT_SUCCESS, offset, value);
} }
通过adb 获取 蓝牙 mac address:
adb shell settings get secure bluetooth_address
或者
// for Android 4.4.4
adb shell service call bluetooth_manager 10
// for Android 5.0+
adb shell service call bluetooth_manager 12
编程获取bluetooth mac address :
String macAddress = android.provider.Settings.Secure.getString(context.getContentResolver(), "bluetooth_address");
推荐文章:
android BLE Peripheral 做外设模拟设备,供ios、android 连接通讯。的更多相关文章
- android BLE Peripheral 手机模拟设备发出BLE广播 BluetoothLeAdvertiser
android 从4.3系统开始可以连接BLE设备,这个大家都知道了.iOS是从7.0版本开始支持BLE. android 进入5.0时代时,开放了一个新功能,手机可以模拟设备发出BLE广播, 这个新 ...
- Android BLE与终端通信(二)——Android Bluetooth基础科普以及搜索蓝牙设备显示列表
Android BLE与终端通信(二)--Android Bluetooth基础搜索蓝牙设备显示列表 摘要 第一篇算是个热身,这一片开始来写些硬菜了,这篇就是实际和蓝牙打交道了,所以要用到真机调试哟, ...
- Android BLE与终端通信(一)——Android Bluetooth基础API以及简单使用获取本地蓝牙名称地址
Android BLE与终端通信(一)--Android Bluetooth基础API以及简单使用获取本地蓝牙名称地址 Hello,工作需要,也必须开始向BLE方向学习了,公司的核心技术就是BLE终端 ...
- android BLE Peripheral 模拟 ibeacon 发出ble 广播
Android对外模模式(peripheral)的支持: 从Android 5.0+开始才支持. api level >= 21 所以5.0 之前设备,是不能向外发送广播的. Android中心 ...
- Android BLE与终端通信(三)——客户端与服务端通信过程以及实现数据通信
Android BLE与终端通信(三)--客户端与服务端通信过程以及实现数据通信 前面的终究只是小知识点,上不了台面,也只能算是起到一个科普的作用,而同步到实际的开发上去,今天就来延续前两篇实现蓝牙主 ...
- Android BLE与终端通信(三)——client与服务端通信过程以及实现数据通信
Android BLE与终端通信(三)--client与服务端通信过程以及实现数据通信 前面的终究仅仅是小知识点.上不了台面,也仅仅能算是起到一个科普的作用.而同步到实际的开发上去,今天就来延续前两篇 ...
- Android与IOS的优缺点比较 对 Android 与 IOS 比较是个个人的问题。 就好比我来说,我两个都用。我深知这两个平台的优缺点。所以,我决定分享我关于这两个移动平台的观点。另外,然后谈谈我对新的 Ubuntu 移动平台的印象和它的优势。 IOS 的优点 虽然这些天我是个十足的 Android 用户,但我必须承认 IOS 在某些方面做的是不错。首先,苹果公司在他们的设备更新方面有更
Android与IOS的优缺点比较 对 Android 与 IOS 比较是个个人的问题. 就好比我来说,我两个都用.我深知这两个平台的优缺点.所以,我决定分享我关于这两个移动平台的观点.另外,然后谈谈 ...
- Android:BLE智能硬件开发详解
目录 前言 BLE是个什么鬼 BLE中的角色分工 主要的关键词和概念 GATT(Generic Attribute Profile ) Characteristic Service Android如何 ...
- BLE简介和Android BLE编程
一.BLE和BT区别 其实我知道许多程序员不太喜欢阅读除了代码以外的文档,因为有时这些过于冗长的文档对编程并没有更多的好处,有了协议,接口,demo差不多很多人就能写出很好质量的代码了.但其实更深入的 ...
随机推荐
- c语言int型和char型的自动类型转换
; //机器码为0xff unsigned ; //机器码0xfe if (a <= b){ printf("a <= b\n"); } else{ printf(&q ...
- Hyper-V安装虚拟机
1.进入控制面板-程序,选择“启用或关闭Windows功能” 2.找到Hyper-V,勾选,重启电脑,即可 3.若是没有找到Hyper-V,如此操作 1)新建txt文件,输入以下内容: pushd & ...
- laravel-mix的安装
Laravel-mix的安装 Laravel Mix 是一款前端任务自动化管理工具,使用了工作流的模式对制定好的任务依次执行.Mix 提供了简洁流畅的 API,让你能够为你的 Laravel 应用定义 ...
- [转]ANR问题分析指南
引言 每天收到无数的兄弟团队的同事向系统转ANR JIRA,有些一旦遇到App ANR就直接转到系统组,有些简单看一下就转到系统组帮忙看一下.如此浩瀚的JIRA,我们什么事不做也处理不过来,请每个Ap ...
- java学习笔记08-switch case语句
switch是一种选择语句,可以通过匹配某个条件,来执行某块代码 switch(expression){ case value: break;//可选 default://可选 //语句 } swit ...
- MailKit系列之附件分离
本文主要谈谈实现思路,不提供完整代码 一.分离基础 1.MIME邮件的multipart类型 引用文章:https://blog.csdn.net/wangyu13476969128/article/ ...
- WPF 10天修炼 第五天- 内容控件
WPF内容控件 在WPF中,所有呈现在用户界面上的对象都称为用户界面元素.但是只有派生自System.Windows.Controls.Control类的对象才称为控件.内容控件通常是指具有Conte ...
- tensorflow调试tfdbg
tensorflow调试工具:tfdbg 使用教程:https://www.cnblogs.com/hellcat/articles/7812119.html 遇到的错误信息及解决方案 ModuleN ...
- 团队软件的NABCD——星遇
日期:2019.4.17 博客期:053 星期三 我们项目是个面向希望有新奇体验的用户的社交软件,致力于打造不一样的有趣的社交. N:(Need,需求) 目前主流社交软件由于时间原因体量越来越大,各种 ...
- Scyther-Semantics and verification of Security Protocol
1 .本书前一节主要是介作者自己的生平经历(读完感觉作者是个神童),目标明确作者13岁代码已经写的很溜了.自己也开了网络公司,但是后面又专注于自己的计算机基础理论,修了哲学的博士学位(不得不说很多专业 ...