Android源碼個個擊破之應用藍牙掃描界面

全面的瞭解藍牙協議棧架構:https://www.cnblogs.com/blogs-of-lxl/p/7010061.html html

藍牙技術電子書:https://www.crifan.com/files/doc/docbook/bluetooth_intro/release/html/bluetooth_intro.htmljava

藍牙4.0 BLE 廣播包解析:https://blog.csdn.net/qq576494799/article/details/52102642 android




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


HAL:http://www.javashuo.com/article/p-xzmpkbtl-mo.html 安全

      

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






Android 做爲設備端app

        開源庫:http://www.jcodecraeer.com/a/anzhuokaifa/androidkaifa/2018/0403/9551.html less

      

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

                   

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



      1.廣播包:http://www.javashuo.com/article/p-viwozoro-md.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類裏被調用


繼續搜索getRomoteService方法

1)在主設備BluetoothGattServer裏會被調用到

 在其mBluetoothGattServerCallback裏會多處用到,用來建立一個BluetoothDevice對象,這個監聽是監遵從設備的。

注意:!!!!這些方法裏的address是從設備的地址,並非咱們想要的主設備的地址!!!



 因此仍是得從從設備的代碼出發尋找mac是從哪裏獲取的。

 

 從設備裏調用:



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

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

在網站源碼搜索registerScanner:


 

 registScanner方法

 

經過ScanManager的方法調用底層的ScanNative類註冊掃描器的方法:

image.png


最終找到JNI源碼調用的地方:/packages/apps/Bluetooth/jni/com_android_bluetooth_gatt.cpp

image.png

 注意這裏的 CallVoidMethod 方法,調用的是java的方法:https://blog.csdn.net/lyh1299259684/article/details/79438802

        image.png


onScanResult方法 (沒有找到這個方法在哪裏被調用了???)


        其實吧,上面說了這麼多,仍是說從設備如何獲取mac的,並無說明主設備的mac究竟是什麼,怎麼暴露給從設備的呢?

        

        後來才發現,客戶掃描的這個mac,其實是設備在開啓ble服務的時候經過廣播發出去的。設備本身定義的mac。而後前面這個推斷是錯誤的,這個mac地址仍是android系統本身生成的。

        

https://blog.csdn.net/shuijianbaozi/article/details/75219530   (關於廣播的mac爲何與本地的藍牙mac不一致的問題)


            

看看設備發廣播的源碼 (8.0源碼)

        image.png

 image.png

         

        這裏的mBluetoothManager其實是BluetoothManagerService.java這個類

        image.png

        經過aidl和binder機制獲取IBluetoothGatt對象

        image.png

       這裏的binder對應的對象,咱們使用的是BLE,因此應該是GattService對象。

       image.png

        因此看看GattService的startAdvertisingSet方法,注意這些方法都會有兩個:

       binder的方法


      image.png

        

     binder再會去調用GattService的方法

      image.png

      GattService再去調用AdvertiseManager的方法
        

      image.png

        注意上面,將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的位置

            image.png

        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):

         在上面源碼的閱讀過程當中,我發現有這樣的一個函數:

         image.png

        裏面有一個address,我猜測這應該就是我一直苦苦尋求的mac。那麼這個mac會回調到上層的某個回調裏嗎?事實證實前面的這個想法也是錯誤的,這裏的地址仍是客戶端本身的地址。

        

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

                可是並無找到vendor_open方法調用的地方。


        藍牙初始化

             1)獲取藍牙地址

        image.png

        

        此次這個地址是隨機的,有多是我想要的mac。

       image.png     

        


        enable

        image.png    

        




        下面的這個博客,看完一張圖就能找到verdor_open的地方 

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


根據上面的博客分析源碼:

  

      藍牙Enable過程追蹤(Android 6.0源碼,由於當前咱們的系統就是6.0):

        根據上面的博客分析源碼:

    image.png


        image.png


            image.png

                 module_start_up:開啓了兩個module

                

               一個module是hci_layer.c,在它的start_up方法裏調用了vendor->open方法。也就是前面我提到的博客裏的這個方法。

                

                

                image.png

                

                可是在vendor.c裏,發現這個local_bdaddr仍然是本機信息裏的那個固定的mac地址。

                image.png

       

            image.png



            BTU_StartUp方法

            image.png

        

            

            image.png


            》》》》BTU相關的源碼開始

            image.png

            

            SMP_Init()方法

            image.png

            

            image.png

                



 L2CA_RegisterFixedChannel 這個方法貌似有mac的蹤跡

    image.png



     btm_ble_init()方法

     這個地方貌似也有mac的蹤跡這不正是開啓廣播start adv那個方法嗎?):

     image.png

         給address賦值,注意在賦值以後調用了 btm_ble_start_adv方法 

        image.png

        

image.png

            BTE_InitStack()方法

            

        

           》》》》BTU相關的源碼結束



2018.9.14日,繼續探索mac:

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

        http://www.javashuo.com/article/p-ronyydad-me.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

         看這個類的註釋:

        image.png

         對ble地址的管理,哈哈,是否是有戲?


         看了一下它的各個函數,基本與上面博客提到的幾種地址匹配上了。因爲咱們的設備是廣播一旦開啓,mac地址就會隨機的變化。因此我猜測這個類裏被調用的方法應該是Resolvable private address

         image.png

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

            image.png

            這個方法, 和btsnd_hcic_ble_rand方法是關聯的,主要是判斷btsnd_hcic_ble_rand方法有沒有執行成功。因此主要看btsnd_hcic_ble_rand方法。


           


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

           |

            image.png

                說實話,看到這兒,我只看到給變量p申請了個內存空間,pp與p創建的關聯,可是是哪裏賦值的呢?

                看看這個方法:

                image.png

                      



            |

           image.png 

            |

            

              image.png

            |

           image.png     

            |

        image.png               

         到這裏,看得我一愣一愣的。  所幸我搜索android fixed_queue.c,找到下面這樣一篇博客,說明了藍牙協議棧通信的前因後果。

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

android bluedroid 協議棧裏面的各個組件之間的消息處理機制

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

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

        只能向前追溯了:
        image.png

           經過搜索MSG_STACK_TO_HC_HCI_CMD找到對應的處理位置:

           image.png

             根據case猜測到應該是btsnoop.c處理這個事件

             image.png

                看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);
    }

         追蹤,image.png

         image.png

        這不正是把數據發送給client_socket嗎?難道客戶端與設備端的藍牙通信,底層是走了socket通信?     

        那麼,設備端與客戶端的socket究竟是怎麼一回事,這個send方法又作了哪些事情。客戶端又是怎麼解析這些數據的呢?種種疑問立馬在我腦海裏浮現。

        繼續追溯send的調用棧:

        image.png

            

2018.09.17,繼續找mac:

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

          image.png

             

             image.png

           上面的這些看似很像的方法,沒有被調用 。

        

        

 繼續持之以恆的找mac

       image.png

            這個local_rpa能夠打印看一下,是不是mac。

         

        |

        追溯這個方法:

        》》相關資料:

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


        image.png

       》》

        image.png

         |

           image.png

                |

             

            image.png

            |

             image.png

            |

            image.png

            

                image.png

               這不是前面說到的btu與hci的通信嗎,經過任務隊列。

              image.png

                     

    2018.9.18日,搜索mac旋轉找到一篇博客,最後有人提出一個解決辦法 :    

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

             將這個常量的true改爲false  (此方法親測可用,就是不知道有什麼安全隱患。)

            image.png   

            

    而這篇中文博客: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)

image.png

這裏就是對隨機仍是固定的mac做了區分的地方。



2)

image.png




                   

image.png

相關文章
相關標籤/搜索