Android源碼個個擊破之應(yīng)用藍(lán)牙掃描界面

全面的了解藍(lán)牙協(xié)議棧架構(gòu):https://www.cnblogs.com/blogs-of-lxl/p/7010061.html

創(chuàng)新互聯(lián)公司專注于本溪網(wǎng)站建設(shè)服務(wù)及定制,我們擁有豐富的企業(yè)做網(wǎng)站經(jīng)驗。 熱誠為您提供本溪營銷型網(wǎng)站建設(shè),本溪網(wǎng)站制作、本溪網(wǎng)頁設(shè)計、本溪網(wǎng)站官網(wǎng)定制、小程序開發(fā)服務(wù),打造本溪網(wǎng)絡(luò)公司原創(chuàng)品牌,更為您提供本溪網(wǎng)站排名全網(wǎng)營銷落地服務(wù)。

藍(lán)牙技術(shù)電子書:https://www.crifan.com/files/doc/docbook/bluetooth_intro/release/html/bluetooth_intro.html

藍(lán)牙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 作為設(shè)備端

        開源庫: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

        

最近在做一個藍(lán)牙開關(guān)的功能,發(fā)現(xiàn)一個很奇怪的現(xiàn)象:

1.打開藍(lán)牙,藍(lán)牙圖標(biāo)亮了,但是藍(lán)牙不能被外界搜索到。只有從設(shè)置-藍(lán)牙進(jìn)入藍(lán)牙掃描界面,此時藍(lán)牙才能被外界搜索到。所以準(zhǔn)備一探源碼,看能否找到解決辦法。

藍(lán)牙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  (超級詳細(xì) 

藍(lán)牙掃描

https://www.cnblogs.com/libs-liu/p/9166075.html   (藍(lán)牙掃描)

從設(shè)備掃描主設(shè)備的藍(lán)牙,藍(lán)牙的地址,竟然不是主設(shè)備主機(jī)信息里顯示的藍(lán)牙地址,只有從源碼

分析這個地址了:

1.首先這個地址是BluetoothDevice這個類里的mAddress變量,這個變量是從BluetoothDevice的構(gòu)造方法傳入的,而BluetoothDevice的構(gòu)造方法在BluetoothAdapter類里被調(diào)用

Android源碼個個擊破之應(yīng)用藍(lán)牙掃描界面

繼續(xù)搜索getRomoteService方法

Android源碼個個擊破之應(yīng)用藍(lán)牙掃描界面

1)在主設(shè)備BluetoothGattServer里會被調(diào)用到

 在其mBluetoothGattServerCallback里會多處用到,用來創(chuàng)建一個BluetoothDevice對象,這個監(jiān)聽是監(jiān)聽從設(shè)備的。

注意:?。。?!這些方法里的address是從設(shè)備的地址,并不是我們想要的主設(shè)備的地址?。?!

 所以還是得從從設(shè)備的代碼出發(fā)尋找mac是從哪里獲取的。

 

 從設(shè)備里調(diào)用:

Android源碼個個擊破之應(yīng)用藍(lán)牙掃描界面

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

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

在網(wǎng)站源碼搜索registerScanner:

Android源碼個個擊破之應(yīng)用藍(lán)牙掃描界面

 

 registScanner方法

 

Android源碼個個擊破之應(yīng)用藍(lán)牙掃描界面

通過ScanManager的方法調(diào)用底層的ScanNative類注冊掃描器的方法:

Android源碼個個擊破之應(yīng)用藍(lán)牙掃描界面

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

Android源碼個個擊破之應(yīng)用藍(lán)牙掃描界面

 注意這里的 CallVoidMethod 方法,調(diào)用的是java的方法:https://blog.csdn.net/lyh2299259684/article/details/79438802

        Android源碼個個擊破之應(yīng)用藍(lán)牙掃描界面

onScanResult方法 (沒有找到這個方法在哪里被調(diào)用了???)

Android源碼個個擊破之應(yīng)用藍(lán)牙掃描界面

        其實吧,上面說了這么多,還是說從設(shè)備如何獲取mac的,并沒有說明主設(shè)備的mac到底是什么,怎么暴露給從設(shè)備的呢?

        

        后來才發(fā)現(xiàn),客戶掃描的這個mac,實際上是設(shè)備在開啟ble服務(wù)的時候通過廣播發(fā)出去的。設(shè)備自己定義的mac。然后前面這個推斷是錯誤的,這個mac地址還是android系統(tǒng)自己生成的。

        

https://blog.csdn.net/shuijianbaozi/article/details/75219530   (關(guān)于廣播的mac為什么與本地的藍(lán)牙m(xù)ac不一致的問題)

            

看看設(shè)備發(fā)廣播的源碼 (8.0源碼)

        Android源碼個個擊破之應(yīng)用藍(lán)牙掃描界面

 Android源碼個個擊破之應(yīng)用藍(lán)牙掃描界面

         

        這里的mBluetoothManager實際上是BluetoothManagerService.java這個類

        Android源碼個個擊破之應(yīng)用藍(lán)牙掃描界面

        通過aidl和binder機(jī)制獲取IBluetoothGatt對象

        Android源碼個個擊破之應(yīng)用藍(lán)牙掃描界面

       這里的binder對應(yīng)的對象,我們使用的是BLE,所以應(yīng)該是GattService對象。

       Android源碼個個擊破之應(yīng)用藍(lán)牙掃描界面

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

       binder的方法

      Android源碼個個擊破之應(yīng)用藍(lán)牙掃描界面

        

     binder再會去調(diào)用GattService的方法

      Android源碼個個擊破之應(yīng)用藍(lán)牙掃描界面

      GattService再去調(diào)用AdvertiseManager的方法
        

      Android源碼個個擊破之應(yīng)用藍(lán)牙掃描界面

        注意上面,將advertiseData對象解析成了byte[]數(shù)據(jù),也就是組織廣播包的數(shù)據(jù)??纯催@個方法是如何解析的:

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 發(fā)起廣播的源碼解析:https://blog.csdn.net/lansefeiyang08/article/details/46545215

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

        我對上面的博客源碼作一下補(bǔ)充:

        1)結(jié)構(gòu)體btgatt_interface_t的位置

            Android源碼個個擊破之應(yīng)用藍(lán)牙掃描界面

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

        3)constbtgatt_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這一層的跳轉(zhuǎn)順序:bt_gatt_client.h 》btif_gatt_client.c 》bta_gattc_api.c

        

            

   繼續(xù)找mac(Android 6.0源碼,因為當(dāng)前我們的系統(tǒng)就是6.0):

         在上面源碼的閱讀過程中,我發(fā)現(xiàn)有這樣的一個函數(shù):

         Android源碼個個擊破之應(yīng)用藍(lán)牙掃描界面

        里面有一個address,我猜想這應(yīng)該就是我一直苦苦尋求的mac。那么這個mac會回調(diào)到上層的某個回調(diào)里嗎?事實證明前面的這個想法也是錯誤的,這里的地址還是客戶端自己的地址。

        

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

                但是并沒有找到vendor_open方法調(diào)用的地方。

        藍(lán)牙初始化

             1)獲取藍(lán)牙地址

        Android源碼個個擊破之應(yīng)用藍(lán)牙掃描界面

        

        這次這個地址是隨機(jī)的,有可能是我想要的mac。

       Android源碼個個擊破之應(yīng)用藍(lán)牙掃描界面     

        

        enable

        Android源碼個個擊破之應(yīng)用藍(lán)牙掃描界面    

        

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

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

根據(jù)上面的博客分析源碼:

  

      藍(lán)牙Enable過程追蹤(Android 6.0源碼,因為當(dāng)前我們的系統(tǒng)就是6.0):

        根據(jù)上面的博客分析源碼:

    Android源碼個個擊破之應(yīng)用藍(lán)牙掃描界面


        Android源碼個個擊破之應(yīng)用藍(lán)牙掃描界面

            Android源碼個個擊破之應(yīng)用藍(lán)牙掃描界面

                 module_start_up:開啟了兩個module

                

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

                

                

                Android源碼個個擊破之應(yīng)用藍(lán)牙掃描界面

                

                但是在vendor.c里,發(fā)現(xiàn)這個local_bdaddr仍然是本機(jī)信息里的那個固定的mac地址。

                Android源碼個個擊破之應(yīng)用藍(lán)牙掃描界面

       

            Android源碼個個擊破之應(yīng)用藍(lán)牙掃描界面

            BTU_StartUp方法

            Android源碼個個擊破之應(yīng)用藍(lán)牙掃描界面

        

            

            Android源碼個個擊破之應(yīng)用藍(lán)牙掃描界面

            》》》》BTU相關(guān)的源碼開始

            Android源碼個個擊破之應(yīng)用藍(lán)牙掃描界面

            

            SMP_Init()方法

            Android源碼個個擊破之應(yīng)用藍(lán)牙掃描界面

            

            Android源碼個個擊破之應(yīng)用藍(lán)牙掃描界面

                

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

    Android源碼個個擊破之應(yīng)用藍(lán)牙掃描界面

     btm_ble_init()方法

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

     Android源碼個個擊破之應(yīng)用藍(lán)牙掃描界面

         給address賦值,注意在賦值之后調(diào)用了 btm_ble_start_adv方法 

        Android源碼個個擊破之應(yīng)用藍(lán)牙掃描界面

        

Android源碼個個擊破之應(yīng)用藍(lán)牙掃描界面

            BTE_InitStack()方法

            

        

           》》》》BTU相關(guān)的源碼結(jié)束

2018.9.14日,繼續(xù)探索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網(wǎng)站上搜索rpa,找到了一些蛛絲馬跡:

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

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

         看這個類的注釋:

        Android源碼個個擊破之應(yīng)用藍(lán)牙掃描界面

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

         看了一下它的各個函數(shù),基本與上面博客提到的幾種地址匹配上了。由于我們的設(shè)備是廣播一旦開啟,mac地址就會隨機(jī)的變化。所以我猜想這個類里被調(diào)用的方法應(yīng)該是Resolvable private address:

         Android源碼個個擊破之應(yīng)用藍(lán)牙掃描界面

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

            Android源碼個個擊破之應(yīng)用藍(lán)牙掃描界面

            這個方法, 和btsnd_hcic_ble_rand方法是關(guān)聯(lián)的,主要是判斷btsnd_hcic_ble_rand方法有沒有執(zhí)行成功。所以主要看btsnd_hcic_ble_rand方法。

           

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

           |

            Android源碼個個擊破之應(yīng)用藍(lán)牙掃描界面

                說實話,看到這兒,我只看到給變量p申請了個內(nèi)存空間,pp與p建立的關(guān)聯(lián),但是是哪里賦值的呢?

                看看這個方法:

                Android源碼個個擊破之應(yīng)用藍(lán)牙掃描界面

                      

            |

           Android源碼個個擊破之應(yīng)用藍(lán)牙掃描界面 

            |

            

              Android源碼個個擊破之應(yīng)用藍(lán)牙掃描界面

            |

           Android源碼個個擊破之應(yīng)用藍(lán)牙掃描界面     

            |

        Android源碼個個擊破之應(yīng)用藍(lán)牙掃描界面               

         到這里,看得我一愣一愣的。  所幸我搜索android fixed_queue.c,找到下面這樣一篇博客,說明了藍(lán)牙協(xié)議棧通訊的來龍去脈。

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

android bluedroid 協(xié)議棧里面的各個組件之間的消息處理機(jī)制

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

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

        只能向前追溯了:
        Android源碼個個擊破之應(yīng)用藍(lán)牙掃描界面

           通過搜索MSG_STACK_TO_HC_HCI_CMD找到對應(yīng)的處理位置:

           Android源碼個個擊破之應(yīng)用藍(lán)牙掃描界面

             根據(jù)case猜想到應(yīng)該是btsnoop.c處理這個事件

             Android源碼個個擊破之應(yīng)用藍(lán)牙掃描界面

                看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源碼個個擊破之應(yīng)用藍(lán)牙掃描界面

         Android源碼個個擊破之應(yīng)用藍(lán)牙掃描界面

        這不正是把數(shù)據(jù)發(fā)送給client_socket嗎?難道客戶端與設(shè)備端的藍(lán)牙通訊,底層是走了socket通訊?     

        那么,設(shè)備端與客戶端的socket到底是怎么一回事,這個send方法又做了哪些事情??蛻舳擞质窃趺唇馕鲞@些數(shù)據(jù)的呢?種種疑問立馬在我腦海里浮現(xiàn)。

        繼續(xù)追溯send的調(diào)用棧:

        Android源碼個個擊破之應(yīng)用藍(lán)牙掃描界面

            

2018.09.17,繼續(xù)找mac:

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

          Android源碼個個擊破之應(yīng)用藍(lán)牙掃描界面

             

             Android源碼個個擊破之應(yīng)用藍(lán)牙掃描界面

           上面的這些看似很像的方法,沒有被調(diào)用 。

        

        

 繼續(xù)鍥而不舍的找mac

       Android源碼個個擊破之應(yīng)用藍(lán)牙掃描界面

            這個local_rpa可以打印看一下,是否是mac。

         

        |

        追溯這個方法:

        》》相關(guān)資料:

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

        Android源碼個個擊破之應(yīng)用藍(lán)牙掃描界面

       》》

        Android源碼個個擊破之應(yīng)用藍(lán)牙掃描界面

         |

           Android源碼個個擊破之應(yīng)用藍(lán)牙掃描界面

                |

             

            Android源碼個個擊破之應(yīng)用藍(lán)牙掃描界面

            |

             Android源碼個個擊破之應(yīng)用藍(lán)牙掃描界面

            |

            Android源碼個個擊破之應(yīng)用藍(lán)牙掃描界面

            

                Android源碼個個擊破之應(yīng)用藍(lán)牙掃描界面

               這不是前面說到的btu與hci的通訊嗎,通過任務(wù)隊列。

              Android源碼個個擊破之應(yīng)用藍(lán)牙掃描界面

                     

    2018.9.18日,搜索mac旋轉(zhuǎn)找到一篇博客,最后有人提出一個解決辦法 :    

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

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

            Android源碼個個擊破之應(yīng)用藍(lán)牙掃描界面   

            

    而這篇中文博客:https://blog.csdn.net/shuijianbaozi/article/details/75219530    ,只說了有地址旋轉(zhuǎn)這回事兒,但是沒有提出解決辦法。還是google找老外靠譜呢。

   

百度的一些修改藍(lán)牙m(xù)ac的方法,但是不適用于我的6.0的設(shè)備。

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源碼個個擊破之應(yīng)用藍(lán)牙掃描界面

這里就是對隨機(jī)還是固定的mac作了區(qū)分的地方。

2)

Android源碼個個擊破之應(yīng)用藍(lán)牙掃描界面

                   

Android源碼個個擊破之應(yīng)用藍(lán)牙掃描界面

網(wǎng)站標(biāo)題:Android源碼個個擊破之應(yīng)用藍(lán)牙掃描界面
瀏覽路徑:http://muchs.cn/article48/jojsep.html

成都網(wǎng)站建設(shè)公司_創(chuàng)新互聯(lián),為您提供網(wǎng)站制作、營銷型網(wǎng)站建設(shè)商城網(wǎng)站、App設(shè)計、虛擬主機(jī)全網(wǎng)營銷推廣

廣告

聲明:本網(wǎng)站發(fā)布的內(nèi)容(圖片、視頻和文字)以用戶投稿、用戶轉(zhuǎn)載內(nèi)容為主,如果涉及侵權(quán)請盡快告知,我們將會在第一時間刪除。文章觀點不代表本網(wǎng)站立場,如需處理請聯(lián)系客服。電話:028-86922220;郵箱:631063699@qq.com。內(nèi)容未經(jīng)允許不得轉(zhuǎn)載,或轉(zhuǎn)載時需注明來源: 創(chuàng)新互聯(lián)

成都網(wǎng)站建設(shè)