温馨提示×

温馨提示×

您好,登录后才能下订单哦!

密码登录×
登录注册×
其他方式登录
点击 登录注册 即表示同意《亿速云用户服务条款》

Android源码个个击破之应用蓝牙扫描界面

发布时间:2020-07-28 12:48:37 来源:网络 阅读:5857 作者:屠夫章哥 栏目:移动开发

全面的了解蓝牙协议栈架构:https://www.cnblogs.com/blogs-of-lxl/p/7010061.html

蓝牙技术电子书:https://www.crifan.com/files/doc/docbook/bluetooth_intro/release/html/bluetooth_intro.html

蓝牙4.0 BLE 广播包解析:https://blog.csdn.net/qq576494799/article/details/52102642




HCI:https://blog.csdn.net/u010657219/article/details/42192481


HAL:https://blog.csdn.net/luoshengyang/article/details/6567257

      

        https://blog.csdn.net/myarrow/article/details/7175204






Android 作为设备端

        开源库:http://www.jcodecraeer.com/a/anzhuokaifa/androidkaifa/2018/0403/9551.html

      

       demo : https://blog.csdn.net/z302766296/article/details/77816960

                   

                   https://www.jianshu.com/p/c4f84af432a1



      1.广播包:https://www.cnblogs.com/CharlesGrant/p/7155211.html

        








最近在做一个蓝牙开关的功能,发现一个很奇怪的现象:

1.打开蓝牙,蓝牙图标亮了,但是蓝牙不能被外界搜索到。只有从设置-蓝牙进入蓝牙扫描界面,此时蓝牙才能被外界搜索到。所以准备一探源码,看能否找到解决办法。




蓝牙enable源码分析

https://blog.csdn.net/ccc905341846/article/details/79009200 

https://blog.csdn.net/zrf1335348191/article/details/53215281

https://www.cnblogs.com/chenbin7/p/3334082.html  (超级详细 


蓝牙扫描


https://www.cnblogs.com/libs-liu/p/9166075.html   (蓝牙扫描)






从设备扫描主设备的蓝牙,蓝牙的地址,竟然不是主设备主机信息里显示的蓝牙地址,只有从源码

分析这个地址了:

1.首先这个地址是BluetoothDevice这个类里的mAddress变量,这个变量是从BluetoothDevice的构造方法传入的,而BluetoothDevice的构造方法在BluetoothAdapter类里被调用

Android源码个个击破之应用蓝牙扫描界面


继续搜索getRomoteService方法

Android源码个个击破之应用蓝牙扫描界面

1)在主设备BluetoothGattServer里会被调用到

 在其mBluetoothGattServerCallback里会多处用到,用来创建一个BluetoothDevice对象,这个监听是监听从设备的。

注意:!!!!这些方法里的address是从设备的地址,并不是我们想要的主设备的地址!!!



 所以还是得从从设备的代码出发寻找mac是从哪里获取的。

 

 从设备里调用:

Android源码个个击破之应用蓝牙扫描界面



---------------------------------------------------------------------------------------------------------------

---------------------------------------------------------------------------------------------------------------

在网站源码搜索registerScanner:


Android源码个个击破之应用蓝牙扫描界面

 

 registScanner方法

 

Android源码个个击破之应用蓝牙扫描界面

通过ScanManager的方法调用底层的ScanNative类注册扫描器的方法:

Android源码个个击破之应用蓝牙扫描界面


最终找到JNI源码调用的地方:/packages/apps/Bluetooth/jni/com_android_bluetooth_gatt.cpp

Android源码个个击破之应用蓝牙扫描界面

 注意这里的 CallVoidMethod 方法,调用的是java的方法:https://blog.csdn.net/lyh2299259684/article/details/79438802

        Android源码个个击破之应用蓝牙扫描界面


onScanResult方法 (没有找到这个方法在哪里被调用了???)


Android源码个个击破之应用蓝牙扫描界面

        其实吧,上面说了这么多,还是说从设备如何获取mac的,并没有说明主设备的mac到底是什么,怎么暴露给从设备的呢?

        

        后来才发现,客户扫描的这个mac,实际上是设备在开启ble服务的时候通过广播发出去的。设备自己定义的mac。然后前面这个推断是错误的,这个mac地址还是android系统自己生成的。

        

https://blog.csdn.net/shuijianbaozi/article/details/75219530   (关于广播的mac为什么与本地的蓝牙mac不一致的问题)


            

看看设备发广播的源码 (8.0源码)

        Android源码个个击破之应用蓝牙扫描界面

 Android源码个个击破之应用蓝牙扫描界面

         

        这里的mBluetoothManager实际上是BluetoothManagerService.java这个类

        Android源码个个击破之应用蓝牙扫描界面

        通过aidl和binder机制获取IBluetoothGatt对象

        Android源码个个击破之应用蓝牙扫描界面

       这里的binder对应的对象,我们使用的是BLE,所以应该是GattService对象。

       Android源码个个击破之应用蓝牙扫描界面

        所以看看GattService的startAdvertisingSet方法,注意这些方法都会有两个:

       binder的方法


      Android源码个个击破之应用蓝牙扫描界面

        

     binder再会去调用GattService的方法

      Android源码个个击破之应用蓝牙扫描界面

      GattService再去调用AdvertiseManager的方法
        

      Android源码个个击破之应用蓝牙扫描界面

        注意上面,将advertiseData对象解析成了byte[]数据,也就是组织广播包的数据。看看这个方法是如何解析的:

package com.android.bluetooth.gatt;

import android.bluetooth.BluetoothUuid;
import android.bluetooth.le.AdvertiseData;
import android.os.ParcelUuid;
import android.util.Log;

import com.android.bluetooth.Utils;

import java.io.ByteArrayOutputStream;

class AdvertiseHelper {

    private static final String TAG = "AdvertiseHelper";

    private static final int DEVICE_NAME_MAX = ;

    private static final int COMPLETE_LIST__BIT_SERVICE_UUIDS = 0X;
    private static final int COMPLETE_LIST__BIT_SERVICE_UUIDS = 0X;
    private static final int COMPLETE_LIST__BIT_SERVICE_UUIDS = 0X;
    private static final int SHORTENED_LOCAL_NAME = 0X;
    private static final int COMPLETE_LOCAL_NAME = 0X;
    private static final int TX_POWER_LEVEL = 0x0A;
    private static final int SERVICE_DATA__BIT_UUID = 0X;
    private static final int SERVICE_DATA__BIT_UUID = 0X;
    private static final int SERVICE_DATA__BIT_UUID = 0X;
    private static final int MANUFACTURER_SPECIFIC_DATA = 0XFF;

    public static byte[] advertiseDataToBytes(AdvertiseData data, String name) {

        if (data == null) return new byte[0];

        // Flags are added by lower layers of the stack, only if needed;
        // no need to add them here.

        ByteArrayOutputStream ret = new ByteArrayOutputStream();

        if (data.getIncludeDeviceName()) {
            try {
                byte[] nameBytes = name.getBytes("UTF-8");

                int nameLength = nameBytes.length;
                byte type;

                // TODO(jpawlowski) put a better limit on device name!
                if (nameLength > DEVICE_NAME_MAX) {
                    nameLength = DEVICE_NAME_MAX;
                    type = SHORTENED_LOCAL_NAME;
                } else {
                    type = COMPLETE_LOCAL_NAME;
                }

                ret.write(nameLength + 1);
                ret.write(type);
                ret.write(nameBytes, 0, nameLength);
            } catch (java.io.UnsupportedEncodingException e) {
                Log.e(TAG, "Can't include name - encoding error!", e);
            }
        }

        for (int i = 0; i < data.getManufacturerSpecificData().size(); i++) {
            int manufacturerId = data.getManufacturerSpecificData().keyAt(i);

            byte[] manufacturerData = data.getManufacturerSpecificData().get(manufacturerId);
            int dataLen = 2 + (manufacturerData == null ? 0 : manufacturerData.length);
            byte[] concated = new byte[dataLen];
            // First two bytes are manufacturer id in little-endian.
            concated[0] = (byte) (manufacturerId & 0xFF);
            concated[1] = (byte) ((manufacturerId >> 8) & 0xFF);
            if (manufacturerData != null) {
                System.arraycopy(manufacturerData, 0, concated, 2, manufacturerData.length);
            }

            ret.write(concated.length + 1);
            ret.write(MANUFACTURER_SPECIFIC_DATA);
            ret.write(concated, 0, concated.length);
        }

        if (data.getIncludeTxPowerLevel()) {
            ret.write(2 /* Length */);
            ret.write(TX_POWER_LEVEL);
            ret.write(0); // lower layers will fill this value.
        }

        if (data.getServiceUuids() != null) {
            ByteArrayOutputStream serviceUuids = new ByteArrayOutputStream();
            ByteArrayOutputStream serviceUuids = new ByteArrayOutputStream();
            ByteArrayOutputStream serviceUuids = new ByteArrayOutputStream();

            for (ParcelUuid parcelUuid : data.getServiceUuids()) {
                byte[] uuid = BluetoothUuid.uuidToBytes(parcelUuid);

                if (uuid.length == BluetoothUuid.UUID_BYTES__BIT) {
                    serviceUuids.write(uuid, 0, uuid.length);
                } else if (uuid.length == BluetoothUuid.UUID_BYTES__BIT) {
                    serviceUuids.write(uuid, 0, uuid.length);
                } else /*if (uuid.length == BluetoothUuid.UUID_BYTES__BIT)*/ {
                    serviceUuids.write(uuid, 0, uuid.length);
                }
            }

            if (serviceUuids.size() != 0) {
                ret.write(serviceUuids.size() + 1);
                ret.write(COMPLETE_LIST__BIT_SERVICE_UUIDS);
                ret.write(serviceUuids.toByteArray(), 0, serviceUuids.size());
            }

            if (serviceUuids.size() != 0) {
                ret.write(serviceUuids.size() + 1);
                ret.write(COMPLETE_LIST__BIT_SERVICE_UUIDS);
                ret.write(serviceUuids.toByteArray(), 0, serviceUuids.size());
            }

            if (serviceUuids.size() != 0) {
                ret.write(serviceUuids.size() + 1);
                ret.write(COMPLETE_LIST__BIT_SERVICE_UUIDS);
                ret.write(serviceUuids.toByteArray(), 0, serviceUuids.size());
            }
        }

        if (!data.getServiceData().isEmpty()) {
            for (ParcelUuid parcelUuid : data.getServiceData().keySet()) {
                byte[] serviceData = data.getServiceData().get(parcelUuid);

                byte[] uuid = BluetoothUuid.uuidToBytes(parcelUuid);
                int uuidLen = uuid.length;

                int dataLen = uuidLen + (serviceData == null ? 0 : serviceData.length);
                byte[] concated = new byte[dataLen];

                System.arraycopy(uuid, 0, concated, 0, uuidLen);

                if (serviceData != null) {
                    System.arraycopy(serviceData, 0, concated, uuidLen, serviceData.length);
                }

                if (uuid.length == BluetoothUuid.UUID_BYTES__BIT) {
                    ret.write(concated.length + 1);
                    ret.write(SERVICE_DATA__BIT_UUID);
                    ret.write(concated, 0, concated.length);
                } else if (uuid.length == BluetoothUuid.UUID_BYTES__BIT) {
                    ret.write(concated.length + 1);
                    ret.write(SERVICE_DATA__BIT_UUID);
                    ret.write(concated, 0, concated.length);
                } else /*if (uuid.length == BluetoothUuid.UUID_BYTES__BIT)*/ {
                    ret.write(concated.length + 1);
                    ret.write(SERVICE_DATA__BIT_UUID);
                    ret.write(concated, 0, concated.length);
                }
            }
        }

        return ret.toByteArray();
    }
}

       

     

Android 6.0 发起广播的源码解析:https://blog.csdn.net/lansefeiyang08/article/details/46545215


https://blog.csdn.net/lansefeiyang08/article/details/46505921

        我对上面的博客源码作一下补充:

        1)结构体btgatt_interface_t的位置

            Android源码个个击破之应用蓝牙扫描界面

        2)btgatt_client_interface_t所在的位置   /hardware/libhardware/include/hardware/bt_gatt_client.h

        3)const btgatt_client_interface_t btgattClientInterface 映射所在的目录:/system/bt/btif/src/btif_gatt_client.c

        4)BTA_GATTC_AppRegister方法所在目录:/system/bt/bta/gatt/bta_gattc_api.c

            注意C这一层的跳转顺序:bt_gatt_client.h 》btif_gatt_client.c 》bta_gattc_api.c

        

            

   继续找mac(Android 6.0源码,因为当前我们的系统就是6.0):

         在上面源码的阅读过程中,我发现有这样的一个函数:

         Android源码个个击破之应用蓝牙扫描界面

        里面有一个address,我猜想这应该就是我一直苦苦寻求的mac。那么这个mac会回调到上层的某个回调里吗?事实证明前面的这个想法也是错误的,这里的地址还是客户端自己的地址。

        

        设备如何设置的mac:https://blog.csdn.net/shichaog/article/details/52100954

                但是并没有找到vendor_open方法调用的地方。


        蓝牙初始化

             1)获取蓝牙地址

        Android源码个个击破之应用蓝牙扫描界面

        

        这次这个地址是随机的,有可能是我想要的mac。

       Android源码个个击破之应用蓝牙扫描界面     

        


        enable

        Android源码个个击破之应用蓝牙扫描界面    

        




        下面的这个博客,看完一张图就能找到verdor_open的地方 

        https://blog.csdn.net/shichaog/article/details/52728684


根据上面的博客分析源码:

  

      蓝牙Enable过程追踪(Android 6.0源码,因为当前我们的系统就是6.0):

        根据上面的博客分析源码:

    Android源码个个击破之应用蓝牙扫描界面


        Android源码个个击破之应用蓝牙扫描界面


            Android源码个个击破之应用蓝牙扫描界面

                 module_start_up:开启了两个module

                

               一个module是hci_layer.c,在它的start_up方法里调用了vendor->open方法。也就是前面我提到的博客里的这个方法。

                

                

                Android源码个个击破之应用蓝牙扫描界面

                

                但是在vendor.c里,发现这个local_bdaddr仍然是本机信息里的那个固定的mac地址。

                Android源码个个击破之应用蓝牙扫描界面

       

            Android源码个个击破之应用蓝牙扫描界面



            BTU_StartUp方法

            Android源码个个击破之应用蓝牙扫描界面

        

            

            Android源码个个击破之应用蓝牙扫描界面


            》》》》BTU相关的源码开始

            Android源码个个击破之应用蓝牙扫描界面

            

            SMP_Init()方法

            Android源码个个击破之应用蓝牙扫描界面

            

            Android源码个个击破之应用蓝牙扫描界面

                



 L2CA_RegisterFixedChannel 这个方法貌似有mac的踪影

    Android源码个个击破之应用蓝牙扫描界面



     btm_ble_init()方法

     这个地方貌似也有mac的踪影这不正是开启广播start adv那个方法吗?):

     Android源码个个击破之应用蓝牙扫描界面

         给address赋值,注意在赋值之后调用了 btm_ble_start_adv方法 

        Android源码个个击破之应用蓝牙扫描界面

        

Android源码个个击破之应用蓝牙扫描界面

            BTE_InitStack()方法

            

        

           》》》》BTU相关的源码结束



2018.9.14日,继续探索mac:

         https://e2echina.ti.com/question_answer/wireless_connectivity/bluetooth/f/103/t/136174

        https://www.cnblogs.com/CharlesGrant/p/7155812.html

        https://blog.csdn.net/android_jiangjun/article/details/77113883

        以上的博客,对ble广播的mac种类都做了一些说明,但是没有从源码里指明这个mac是在哪里如何生成的。

        看到有这样的一个博客:https://devzone.nordicsemi.com/f/nordic-q-a/16720/setting-resolvable-private-address

        通过在xref网站上搜索rpa,找到了一些蛛丝马迹:


           /system/bt/stack/btm/btm_ble_multi_adv.c


        /system/bt/stack/btm/btm_ble_addr.c

         看这个类的注释:

        Android源码个个击破之应用蓝牙扫描界面

         对ble地址的管理,哈哈,是不是有戏?


         看了一下它的各个函数,基本与上面博客提到的几种地址匹配上了。由于我们的设备是广播一旦开启,mac地址就会随机的变化。所以我猜想这个类里被调用的方法应该是Resolvable private address

         Android源码个个击破之应用蓝牙扫描界面

               》》》》》》》》》》看btm_gen_resolve_paddr_cmpl方法:

            Android源码个个击破之应用蓝牙扫描界面

            这个方法, 和btsnd_hcic_ble_rand方法是关联的,主要是判断btsnd_hcic_ble_rand方法有没有执行成功。所以主要看btsnd_hcic_ble_rand方法。


           


            》》》》》》》》》》看btsnd_hcic_ble_rand方法

           |

            Android源码个个击破之应用蓝牙扫描界面

                说实话,看到这儿,我只看到给变量p申请了个内存空间,pp与p建立的关联,但是是哪里赋值的呢?

                看看这个方法:

                Android源码个个击破之应用蓝牙扫描界面

                      



            |

           Android源码个个击破之应用蓝牙扫描界面 

            |

            

              Android源码个个击破之应用蓝牙扫描界面

            |

           Android源码个个击破之应用蓝牙扫描界面     

            |

        Android源码个个击破之应用蓝牙扫描界面               

         到这里,看得我一愣一愣的。  所幸我搜索android fixed_queue.c,找到下面这样一篇博客,说明了蓝牙协议栈通讯的来龙去脉。

############################################################################ 

android bluedroid 协议栈里面的各个组件之间的消息处理机制

            https://blog.csdn.net/yanli0084/article/details/51821064

############################################################################ 

        只能向前追溯了:
        Android源码个个击破之应用蓝牙扫描界面

           通过搜索MSG_STACK_TO_HC_HCI_CMD找到对应的处理位置:

           Android源码个个击破之应用蓝牙扫描界面

             根据case猜想到应该是btsnoop.c处理这个事件

             Android源码个个击破之应用蓝牙扫描界面

                看btsnoop_write_packet方法

static void btsnoop_write_packet(packet_type_t type, const uint8_t *packet, bool is_received) {
      int length_he = 0;
      int length;
      int flags;
      int drops = 0;
      switch (type) {
            case kCommandPacket:
                  length_he = packet[2] + 4;
                  flags = 2;
                  break;
            case kAclPacket:
                  length_he = (packet[3] << 8) + packet[2] + 5;
                  flags = is_received;
                  break;
            case kScoPacket:
                  length_he = packet[2] + 4;
                  flags = is_received;
                  break;
            case kEventPacket:
                  length_he = packet[1] + 3;
                  flags = 3;
                  break;
          }
    
      uint64_t timestamp = btsnoop_timestamp();
      uint32_t time_hi = timestamp >> 32;
      uint32_t time_lo = timestamp & 0xFFFFFFFF;
    
      length = htonl(length_he);
      flags = htonl(flags);
      drops = htonl(drops);
      time_hi = htonl(time_hi);
      time_lo = htonl(time_lo);
    
      btsnoop_write(&length, 4);
      btsnoop_write(&length, 4);
      btsnoop_write(&flags, 4);
      btsnoop_write(&drops, 4);
      btsnoop_write(&time_hi, 4);
      btsnoop_write(&time_lo, 4);
      btsnoop_write(&type, 1);
      btsnoop_write(packet, length_he - 1);
    }

         追踪,Android源码个个击破之应用蓝牙扫描界面

         Android源码个个击破之应用蓝牙扫描界面

        这不正是把数据发送给client_socket吗?难道客户端与设备端的蓝牙通讯,底层是走了socket通讯?     

        那么,设备端与客户端的socket到底是怎么一回事,这个send方法又做了哪些事情。客户端又是怎么解析这些数据的呢?种种疑问立马在我脑海里浮现。

        继续追溯send的调用栈:

        Android源码个个击破之应用蓝牙扫描界面

            

2018.09.17,继续找mac:

          /system/bt/stack/btm/btm_ble.c

          Android源码个个击破之应用蓝牙扫描界面

             

             Android源码个个击破之应用蓝牙扫描界面

           上面的这些看似很像的方法,没有被调用 。

        

        

 继续锲而不舍的找mac

       Android源码个个击破之应用蓝牙扫描界面

            这个local_rpa可以打印看一下,是否是mac。

         

        |

        追溯这个方法:

        》》相关资料:

            在HCI层ACL Connection的建立  https://blog.csdn.net/gjsisi/article/details/13021253


        Android源码个个击破之应用蓝牙扫描界面

       》》

        Android源码个个击破之应用蓝牙扫描界面

         |

           Android源码个个击破之应用蓝牙扫描界面

                |

             

            Android源码个个击破之应用蓝牙扫描界面

            |

             Android源码个个击破之应用蓝牙扫描界面

            |

            Android源码个个击破之应用蓝牙扫描界面

            

                Android源码个个击破之应用蓝牙扫描界面

               这不是前面说到的btu与hci的通讯吗,通过任务队列。

              Android源码个个击破之应用蓝牙扫描界面

                     

    2018.9.18日,搜索mac旋转找到一篇博客,最后有人提出一个解决办法 :    

            https://stackoverflow.com/questions/28602672/android-5-static-bluetooth-mac-address-for-ble-advertising

             将这个常量的true改成false  (此方法亲测可用,就是不知道有什么安全隐患。)

            Android源码个个击破之应用蓝牙扫描界面   

            

    而这篇中文博客:https://blog.csdn.net/shuijianbaozi/article/details/75219530    ,只说了有地址旋转这回事儿,但是没有提出解决办法。还是google找老外靠谱呢。



   


百度的一些修改蓝牙mac的方法,但是不适用于我的6.0的设备。

http://bbs.gfan.com/android-4369727-1-1.html 

                  


https://jingyan.baidu.com/article/17bd8e5250b6be85ab2bb8bf.html   (android hex editor修改不了文件)



全局搜索BLE_PRIVACY_SPT这个变量,找寻可能暴露mac的地方。


1)

Android源码个个击破之应用蓝牙扫描界面

这里就是对随机还是固定的mac作了区分的地方。



2)

Android源码个个击破之应用蓝牙扫描界面




                   

Android源码个个击破之应用蓝牙扫描界面


向AI问一下细节

免责声明:本站发布的内容(图片、视频和文字)以原创、转载和分享为主,文章观点不代表本网站立场,如果涉及侵权请联系站长邮箱:is@yisu.com进行举报,并提供相关证据,一经查实,将立刻删除涉嫌侵权内容。

AI