目录

一、蓝牙广播数据包定义与释义

二、解析广播数据

2.1、获取本地蓝牙名称

2.2、获取16位数据包

2.2.1、将字节转换为hex

2.3、32位于128位字节解析

一、蓝牙广播数据包定义与释义

public class ParseLeAdvDate{
    //   解析自定义ble适配器
  private final static String TAG = "";
    /**
     * 一、概述
     * 蓝牙工作在2.4GHz频段,频率范围为 2402MHz – 2480 MHz,
     * 每 2MHz 一个信道,共40个信道,其中为3个广播信道,剩余的37为个数据信道。
     * 蓝牙广播就是在这三个广播信道上,以一定的数据格式通过电磁波发射数据。
     *
     * 低功耗蓝牙:Bluetooth Low Energy 简称BLE
     * 规范:Bluetooth 蓝牙协议的名称
     *
     * 协议栈
     * 从上直下分别为 控制器、主机、应用层
     *
     * 控制器:协议栈的底层实现,直接和硬件相关,由芯片厂商实现,包括物理层(PHY)、链路层(LL)、主机控制接口(HCI)、
     * 主机(HOST):协议栈的上层实现是硬件的抽象,与具体的硬件厂商没有关系。
     * 应用层:使用主机层提供的 API 开发的应用。

     * 一、概念释意
     * 物理层(PHY):蓝牙是工作在 2.4GHz 附近,这是工业、科学、医疗 ISM 的频段,
     * 免许可证。WIFI 也是工作在同一个频段。蓝牙把频段切分为 40 个通道,
     * 其中 3 个广播通道,37 个数据通道,按照一个规律跳频通信。
     *
     * 链路层(Linker layer):用于控制射频状态,设备将处于五种状态之一:
     * 等待、广告、扫描、初始化、连接。
     * 广播设备不需要建立连接就可以发送数据,而扫描设备接受广播设备发送的数据
     * 发起连接的设备通过发送连接请求来回应广播设备
     * 如果广播设备接受请求,那么广播设备与发起连接的设备将会进入连接状态,
     * 发起连接的设备成为主机、接受连接的请求的设备为从机。
     *
     * HCI(主机控制器接口)主机和控制器就是通过这个接口来进行通讯的,通讯的介质就是 HCI 命令。
     * 这层在协议栈中是可选的,一些小型终端可能没有,但是 Android 设备上肯定有,
     * 这层是蓝牙上层和芯片的交互必经之路.
     *
     * HOST(主机):有链路控制器和适配器(L2CAP)、安全管理(SM)、属性协议(ATT)、
     * 通用属性配置文件(GATT)、通用访问配置文件(GAP)。重点为属性协议层,即ATT,它是整个BLE通信的基础。
     * ATT负责数据封装、向外暴露为“属性”,提供属性的为服务端,获取属性的客户端。ATT是专门为BLE低功耗而设计的传输协议,结构简单,传输数据短。
     *
     * GATT(通用属性配置文件):是基于ATT做的进一步逻辑封装,定义数据的交换方式和含义,APP开发就是用的这一层。
     * GATT定义了三个非常重要的概念,服务Service、特征Characteristic、描述Descripter、
     *
     *一个 Service 可以包含若干个 Characteristic,
     * 一个 Characteristic 可以包含属性(properties)和值(value),
     * 还可以包含多个 descripter 。Characteristic 实际上具有读、写、通知等权限。
     * 我们在对一个 BLE 设备发起连接成功以后,对他进行读写操作,其实就是对 Characteristic 的操作。
     *BLE 蓝牙使用 UUID 来区分 Service、Characteristic 、Descripter。
     *
     * GAP(Generic Access Profile):通用访问控制配置文件。定义了 BLE 整个通信过程中的流程,
     * 负责处理设备访问模式和程序,包括设备发现、建立连接、终止连接等等。GAP 层总是作为下面四种角色之一:
     * (1)广播者:不可连接的广播设备。(2)观察者:扫描设备,但不发起建立连接
     * (3)外部设备:可连接的广播设备,可以在单个链路层连接中作为从机。
     * (4)集中器:扫描广播设备并发起连接,可以在单链路层连接中作为主机。
     *
     *ble应用可以分为两类,基于连接和基于非连接。
     *
     * 基于非连接的ble:
     * 外设和周边设备不发生连接,主要靠扫描到的广播来获取信息,发送广播的一方叫做broadcaster,
     * 监听广播一方叫做Oberver在GAP层对应用的角色定义。
     * 特点:广播设备不断地向外广播(含有特定的信息),然后观察者接受到广播安装两者之间约定好的协议进行解析拿到有用的信息
     * 例如:IBeacon,通过这种设备可以实现室内定位
     *
     * 蓝牙广播数据包格式
     * 每个广播数据包由31bit组成,分为有效数据和无效数据两部分。
     * 无效数据部分:因为广播数据包的长度必须是31字节,如果数据包不够31字节,剩下的由0补齐,由0补齐的部分是无效的,即无效数据。
     *
     * 有效数据部分,包含若干个广播数据单元,成为AD Structure,每个AD Structure的组成格式是:
     * 第一个字节长度值Length,表示数据部分的长度,
     * 数据类别得第一个字节表示数据类型AD Type,剩下的Length-1为真正的数据
     *
     * 举列:扫描到的数据解析(ScaanDate:0201 1A05 FFAC 01345 6000.....[共31bit,无效数据用0表示] )。
     * 第一个字节02(此为16进制的数据,一个字节占bit)表示后面2位是数据。
     * 第二字字节01,表示数据类型。
     * 第三个字节为真正的数据。
     *
     * 蓝牙广播包的最大长度是37个字节,其中设备地址占用了6个字节,只有31个字节是可用的。
     * Length(1个字节),ADType(1个字节) AD Data(n 个字节)
     * length = AD Type长度+ADData长度
     *
     * 基于连接的ble
     * 指两个设备建立GATT连接,需要双方进通信,这里的两个角色是外设设备(Peripheral)和中心设备(一般指手机)Centeral。
     * 特点:一个中心设备可连接多个外设,但是一个外设只能连接一个中心(外设连接成功后就会停止对外广播,
     * 别人就发现不了它了)。其中一个中心设备的连接外设的数量也是有限的
     * 
     * 蓝牙模块
     * HC05和SKB369一样,是主从一体的蓝牙串口模块,简单的说,
     * 当蓝牙设备与蓝牙设备配对连接成功后,我们可以忽视蓝牙内部的通信协议,直接将将蓝牙当做串口用,串口支持数据透传。
     */

    //    BLE广播包数据类型
    /**AD Type是0x1,代表广播模式。
     * LElimited Discoverable(AD data是0x0)是有限时间广播,
     * General Discoverable(AD data是0x1)是无限广播,直到被主动停止广播。*/
   public static final short BLE_GAP_AD_TYPE_FLAGS = 0x01;

   /**
    * 代表服务的UUID,一般用16bit的UUID。如微信airsync协议的ServiceUUID是0xFEE7。
    * */
   //  不完整的16位服务UUIDs列表
    public static final short BLE_GAP_AD_TYPE_16bit_SERVICE_UUID_MORE_AVAILABLE = 0x02;

    //  完整的16位服务UUIDs列表
    public static final short BLE_GAP_AD_TYPE_16bit_SERVICE_UUID_COMPLETE = 0x03;

    //  不完整的32bit服务UUIDs列表
    public static final short BLE_GAP_AD_TYPE_32bit_SERVICE_UUID_MORE_AVAILABLE= 0x04;

    //  完整的32bit服务UUIDs列表
    public static final short BLE_GAP_AD_TYPE_32bit_SERVICE_UUID_COMPLETE = 0x05;

    //  不完整的128bit服务UUIDs列表  Partial list of128 bit service UUIDs
    public static final short BLE_GAP_AD_TYPE_128bit_SERVICE_UUID_MORE_AVAILABLE= 0x06;

    //  完整的128bit服务UUIDs列表 Complete list of 128 bit service UUIDs
    public static final short BLE_GAP_AD_TYPE_128bit_SERVICE_UUID_COMPLETE = 0x07;

    //  设备简称 short Local device name
    public static final short BLE_GAP_AD_TYPE_SHORT_LOCAL_NAME = 0x08;

    //  设备全名 Complete local device name
    public static final short BLE_GAP_AD_TYPE_COMPLETE_LOCAL_NAME=0x09;

    /**
     * 发射功率,主机根据它和RSSI就可以计算主机到从机的距离,ibeacon室内定位即是利用这点。m
     */

    //  Transmit power level  传输功率
    public static final short BLE_GAP_AD_TYPE_TX_POWER_LEVEL = 0x0A;

    //  Class for device 设备类别
    public static final short BLE_GAP_AD_TYPE_CLASS_OF_DEVICE = 0x0D;

    // Simple paring Hash C  设备配对的hash值 C
    public static final short BLE_GAP_AD_TYPE_SIMPLE_PAIRING_HASH_C = 0x0E;

    //  设备配对的随机值 R
    public static final short BLE_GAP_AD_TYPE_SIMPLE_PAIRING_RANDOMIZER_R = 0x0F;

    //  安全管理TK值
   public static final short BLE_GAP_AD_TYPE_SECURITY_MANGER_TK_VALUE = 0x10;

   //   安全管理范围标志
   public static final short BLE_GAP_AD_TYPE_SECURITY_MANAGER_OOB_FLAGS = 0x11;

   //  连接间隔范围
   public static final short BLE_GAP_AD_TYPE_SLAVE_CONNECTION_INTERVAL_RANGE = 0x12;

   //  16bit列表服务请求UUIDs
   public static final short BLE_GAP_AD_TYPE_SOLICITED_SERVICE_UUIDS_16BIT = 0x14;

   // 128bit列表服务请求UUIDs
  public static final short BLE_GAP_AD_TYPE_SOLICITED_SERVICE_UUIDS_128BIT = 0x15;

   //  服务数据
  public static final short BLE_GAP_AD_TYPE_SWRVICE_DATA = 0x16;

  //  公共的目标地址
  public static final short BLE_GAP_AD_TYPE_PUBLIC_TARGET_ADDRESS = 0x17;

 //  随机的目标地址
 public static final short BLE_GAP_AD_TYPE_RANDOM_TARGET_ADDRESS = 0x18;

  //  广播
  public static final short BLE_GAP_AD_TYPE_APPEARANCE = 0x19;

  /**
   * AD Type是0xff,代表厂商数据,自定义的数据可以写到这里。
   * 很多第三方场景应用都是利用这个字段来进一步定义应用协议格式,
   * 如微信的airsync协议和ibeacon协议。
  * */
 public static final short BLE_GAP_AD_TYPE_MANUFACTURER_SPECIFIC_DATA = 0xFF;

二、解析广播数据

/**
  *  解析广播数据
  *  @param type 类型:查看上面常量
  *  @param adv_data 解析包数据
  * * @return 指定类型的数据
  */
  public static byte[] adv_report_parse(short type,byte[] adv_data)
  {
   // byte即字节的意思,是java中的基本类型
   // byte,即字节,由8位的二进制组成。在Java中,byte类型的数据是8位带符号的二进制数。
   // 取值范围是[-128, 127],
   int length;
   int index=0;
   byte[] data;
   byte field_type = 0;
   byte field_length=0;
   length = adv_data.length;
  
   while(index < length)
   {
    /**exception 是程序应该捕获的异常,如果JVM产生了exception且没有捕获,
     * 那么程序将自动停止。try{ }中加入运行代码——可能会发生exception异常的的代码,
     * 如果这段代码存在异常,JVM将抛出这个异常,而catch( )将捕获抛出的异常,
     * 程序继续运行。换句话而言,try{}—catch()就是排除运行代码中的异常,
     * catch(exception e)将抛出的异常显示出来。*/
      try{
       field_length = adv_data[index];  //  adv_data字节数组的索引
       field_type = adv_data[index+1];  //  adv_data字节数组的长度
      }catch(Exception e){
        return null;
      }
      // 字节变量的值可以分配给短(short)变量,因为字节数据类型的范围落在短(short)数据类型的范围内。
      //  short 2字节 占16位 取值范围 -32768至32767(或-2^15至2^15-1)
      if(field_type==(byte)type){
      data = new byte[field_length-1];  //  创建一个字节类型的数组data 长度index-2
      byte i;
      for(i=0;i<field_length-1;i++)
      {
       data[i] = adv_data[index+2+i];
      }
      return  data;
      }
      index+=field_length+1;
      if(index >= adv_data.length){
       return  null;
      }
   }
   return  null;
  }

2.1、获取本地蓝牙名称

/**
     * 获得本地名称
     * @param adv_data 广播数据
     * @return
     */
    public static String getLocalName(byte[] adv_data){
        byte[] data = adv_report_parse(BLE_GAP_AD_TYPE_COMPLETE_LOCAL_NAME,adv_data);
        if(data != null){
            return byteArrayToGbkString(data,0,data.length);
        }
        return null;
    }



  /**
     * 
     * @param inarray  字节数组名称
     * @param offset   索引
     * @param len      数组长度
     * @return
     */
    private static String byteArrayToGbkString(byte[] inarray, int offset, int len) {

        String gbkstr = "";
        int idx = 0;
        if (inarray != null) {
            for (idx = 0; idx < len; idx++) {
                if (inarray[idx + offset] == 0x00) {
                    break;
                }
            }
            try {
                //  第一个参数:需要解码的字符
                //  第二个参数:要解码的第一个字节的索引
                //  需要解码的字节数
                //  所支持字符集的名称
                gbkstr = new String(inarray, offset, idx, "UTF-8");
            } catch (UnsupportedEncodingException e) {
            }
        }

        return gbkstr;
    }

2.2、获取16位数据包

/**
     * 获得16bit数据包
     * @param adv_data
     * @return
     */
    public static List<String> get16BitServiceUuids(byte[] adv_data){
        List<String> list = new ArrayList<String>();
        /**128bit解析方法*/
//		byte[] data = adv_report_parse(BLE_GAP_AD_TYPE_128BIT_SERVICE_UUID_COMPLETE,adv_data);
//		if(data != null){
//			int size = data.length / 16;
//			for (int i = 0; i < size; i++) {
//				UUID uuid = decodeUuid128(data,i * 16);
//				list.add(uuid.toString());
//			}
//			return list;
//		}
        byte[] data = adv_report_parse(BLE_GAP_AD_TYPE_16bit_SERVICE_UUID_COMPLETE,adv_data);
        if(data != null){
            for (int i = 0; i < data.length / 2; i++) {
                byte[] by = {data[i * 2 + 1],data[i * 2]};
                String str = bytesToHexString(by,true);
                list.add(str);
            }
            return list;
        }
        return list;
    }

2.2.1、将字节转换为hex

/**
     * byte to Hex String
     *字节换号为字符
     * @param barray
     *            byte array
     * @return hex string
     * 一、字节与字符的区别:
     * 字节:指一小组相邻的二进制数码,是计算机重要的数据单位。
     * 字符:表示数据和信息的字母、数字或其他符号。
     *
     * 字节:数据存储是以“字节”(Byte)为单位,数据传输大多是以“位”(bit,又名“比特”)为单位,
     * 一个位就代表一个0或1(即二进制),每8个位(bit,简写为b)组成一个字节(Byte,简写为B),是最小一级的信息单位。
     *
     * 字符:字符包括字母、数字、运算符号、标点符号和其他符号,以及一些功能性符号。
     * 字符在计算机内存放,应规定相应的代表字符的二进制代码。
     *
     *二、 hex与ASCLL:
     * Hex 全称 是Intel HEX。Hex文件是由一行行符合Intel HEX文件格式的文本所构成的ASCII文本文件。
     * 在Intel HEX文件中,每一行包含一个HEX记录。这些记录由对应机器语言码和/或常量数据的十六进制编码数字组成
     * 
     * ASCII(American Standard Code for Information Interchange,美国信息交换标准代码)是基于拉丁字母的一套电脑编码系统,
     * 主要用于显示现代英语和其他西欧语言。它是现今最通用的单字节编码系统,并等同于国际标准ISO/IEC 646。
     * 举列
     * 1,字符串“1234”表示ACSII码每个字符由一个字节表示, 为0x31 0x32 0x33 0x34
     * 2,HEX字符串的意思是将0x去掉变成字符串:31323334
     * 3,如果字符串“1234”为HEX字符串则表为两个字节0x12,0x34 根据与ASCII对应表中对应的字符为0x12:DC2 (device control 2)设备控制2 0x34
6
     *
     *
     */
    private static String bytesToHexString(byte[] barray, boolean flag) {
        if (barray == null) {
            return "";
        }
        // 构造一个字符串缓冲区,其中不包含字符,初始容量为16个字符。
        StringBuffer sb = new StringBuffer();
        String stemp;
        //  遍历字节数组
        for (int i = 0; i < barray.length; i++) {
            /**
             * 返回整数参数的字符串表示形式,为十进制16的无符号整数。
             *
             * 如果参数为负,则无符号整型值为参数加232;否则,它就等于参数。该值转换为不带前导0的16进制ASCII数字字符串。
             *
             * 如果需要大写字母,可以对结果调用String.toUpperCase()方法:Integer.toHexString (n) .toUpperCase ()
             */
            stemp = Integer.toHexString(0xFF & barray[i]);
            if (stemp.length() < 2) {
                sb.append(0);
            }
            // 转换为大写字符
            sb.append(stemp.toUpperCase());
            // sb.append("  ");
        }
        if(flag){
            //  拼接字符串
            return "0x" + sb.toString();
        }
        //  返回一个字符串
        return sb.toString();
    }

2.3、32位于128位字节解析

/**
     *  解析 128数据
     * @param adv_data
     * @param i
     * @return
     */
    public static UUID decodeUuid128(byte[] adv_data, int i){
        int j = decodeUuid32(adv_data, i + 12);
        int k = decodeUuid32(adv_data, i + 8);
        int l = decodeUuid32(adv_data, i + 4);
        int il = decodeUuid32(adv_data, i + 0);
        return new UUID(((long)j << 32) + (0xffffffffL & (long)k),((long)l << 32) + (0xffffffffL & (long)il));
    }

    public static int decodeUuid32(byte[] adv_data,int i){
        int j = 0xff & adv_data[i];
        int k = 0xff & adv_data[i + 1];
        int l = 0xff & adv_data[i + 2];
        return j | ((0xff & adv_data[i + 3]) << 24 | l << 16 | k << 8);
    }

三、扩展

3.1、蓝牙标准规定了五项基本安全服务:

  • 认证:根据蓝牙地址验证通信设备的身份。蓝牙不提供本机用户认证。
  • 机密性:通过确保只有授权的设备可以访问和查看传输的数据来防止窃听造成的信息泄密。
  • 授权:允许通过确保设备在允许使用服务之前授权使用服务来控制资源。
  • 消息完整性:验证在两个蓝牙设备之间发送的消息在传输过程中没有被更改。
  • 配对/绑定:创建一个或多个共享密钥和存储这些密钥以用于后续连接,以便形成可信设对。