為了能讓其它設備可以發現其設備,先啟動特定廣播。看自己需要什么廣播格式。
對於廣播可見的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
1 public void startAdvertising(MockServerCallBack callBack) { 2 //獲取BluetoothLeAdvertiser,BLE發送BLE廣播用的一個API 3 if (mBluetoothAdvertiser == null) { 4 mBluetoothAdvertiser = mBluetoothAdapter.getBluetoothLeAdvertiser(); 5 } 6 //創建BluetoothGattServerCallback, 7 // MockServerCallBack這個類繼承自BluetoothGattServerCallback 8 // BluetoothGattServerCallback這個回調類主要是一些BLE讀寫的接口 9 // 關於BLE讀寫的操作都在這個Callback中完成 10 if (mBluetoothAdvertiser != null) { 11 mMockServerCallBack = callBack; 12 //打開BluetoothGattServer 13 mGattServer = mBluetoothManager.openGattServer(mActivity, mMockServerCallBack); 14 if (mGattServer == null) { 15 Log.e(TAG, "gatt is null"); 16 } 17 try { 18 mMockServerCallBack.setupServices(mActivity, mGattServer); 20 mBluetoothAdvertiser.startAdvertising(createAdvSettings(true, 0) 21 , createAdvertiseData(BluetoothUUID.bleServerUUID), mAdvCallback); 22 } catch (Exception e) { 23 Log.v(TAG, "Fail to setup BleService"); 24 } 25 } 26 27 isAdvertising = true; 28 }
創建廣播,添加serviceUuid,廣播內容,自行決定。
addServiceUuid(ParcelUuid serviceUuid) 的作用。
廣播數據加入serviceUuid , 當其它設備掃描時,就可以根據此serviceUuid進行特定掃描.
掃描BLE設備的時候:啟動掃描時,有可選參數,傳入uuid數組。
BluetoothAdapter
public boolean startLeScan(final UUID[] serviceUuids, final LeScanCallback callback);
使用該函數啟動掃描的,會根據所傳入的uuid,去掃描過濾,只返回符合條件的設備。
注意:部分手機可能設置uuid后,掃描不到設備,可能底層掃描問題。
1 public static AdvertiseData createAdvertiseData(UUID proximityUuid) { 2 AdvertiseData.Builder builder = new AdvertiseData.Builder(); 4 builder.addManufacturerData(0x0301, new byte[]{0x01, 0x03}); 5 builder.addServiceUuid(ParcelUuid.fromString(BluetoothUUID.bleServerUUID.toString()) 7 AdvertiseData adv = builder.build(); 10 return adv; 11 }
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
1 bluetoothGattServer.sendResponse(device, requestId, BluetoothGatt.GATT_SUCCESS, offset, value); 2 // 執行 sendResponse后,會觸發回調方法 onCharacteristicWriteRequest
(3) 在 onCharacteristicWriteRequest方法中:
1 public void onCharacteristicWriteRequest(BluetoothDevice device, int requestId 2 , BluetoothGattCharacteristic characteristic, boolean preparedWrite, boolean responseNeeded, int offset, byte[] requestBytes) { 3 // 這個里可以獲得 來自客戶端發來的數據 requestBytes 4 }
(4) 處理響應內容
BluetoothGattServerCallback :
1 public class MockServerCallBack extends BluetoothGattServerCallback { 2 3 public void setupServices(Context context, BluetoothGattServer gattServer) throws InterruptedException { 4 if (gattServer == null) { 5 throw new IllegalArgumentException("gattServer is null"); 6 } 7 mGattServer = gattServer; 8 // 設置一個GattService以及BluetoothGattCharacteristic 9 BluetoothGattService service = new BluetoothGattService(BluetoothUUID.bleServerUUID, 10 BluetoothGattService.SERVICE_TYPE_PRIMARY); 11 12 BluetoothGattService service2 = new BluetoothGattService(BluetoothUUID.bleServerUUID2, 13 BluetoothGattService.SERVICE_TYPE_PRIMARY); 14 15 //add a read characteristic. 16 // 當是ios設備連接過來時,需添加BluetoothGattCharacteristic.PROPERTY_INDICATE或者notify進行兼容。 17 mCharacteristicRead = new BluetoothGattCharacteristic(BluetoothUUID.readDataUUID 18 , BluetoothGattCharacteristic.PROPERTY_READ | BluetoothGattCharacteristic.PROPERTY_INDICATE 19 , BluetoothGattCharacteristic.PERMISSION_READ); 20 //add a descriptor 21 BluetoothGattDescriptor descriptor = new BluetoothGattDescriptor(BluetoothUUID.CLIENT_CHARACTERISTIC_CONFIG 22 , BluetoothGattCharacteristic.PERMISSION_WRITE); 23 mCharacteristicRead.addDescriptor(descriptor); 24 service.addCharacteristic(mCharacteristicRead); 25 26 BluetoothGattCharacteristic write = new BluetoothGattCharacteristic( 27 BluetoothUUID.writeDataUUID, 28 BluetoothGattCharacteristic.PROPERTY_WRITE | BluetoothGattCharacteristic.PROPERTY_READ | BluetoothGattCharacteristic.PROPERTY_INDICATE, 29 BluetoothGattCharacteristic.PERMISSION_WRITE); 30 31 service.addCharacteristic(write); 32 33 if (mGattServer != null && service != null) { 34 mGattServer.addService(service); 35 mGattServer.addService(service2); 36 } 37 38 } 39 40 //當添加一個GattService成功后會回調改接口。 41 public void onServiceAdded(int status, BluetoothGattService service) { 42 super.onServiceAdded(status, service); 43 if (status == BluetoothGatt.GATT_SUCCESS) { 44 Log.d(TAG, "onServiceAdded status=GATT_SUCCESS service=" + service.getUuid().toString()); 45 } else { 46 Log.d(TAG, "onServiceAdded status!=GATT_SUCCESS"); 47 } 48 } 49 50 //BLE設備連接狀態發生改變后回調的接口 51 public void onConnectionStateChange(android.bluetooth.BluetoothDevice device, int status, 52 int newState) { 53 super.onConnectionStateChange(device, status, newState); 54 Log.e(TAG, String.format("1.onConnectionStateChange:device name = %s, address = %s" 55 , device.getName(), device.getAddress())); 56 Log.d(TAG, "onConnectionStateChange status=" + status + "->" + newState); 57 if (newState == BluetoothProfile.STATE_DISCONNECTED) { 58 btClient = null; // 移除客戶端連接設備 59 } 60 } 61 62 //當有客戶端來讀數據時回調的接口 64 /** 65 * 特征被讀取。當回復響應成功后,客戶端會讀取然后觸發本方法, 66 */ 67 public void onCharacteristicReadRequest(android.bluetooth.BluetoothDevice device, 68 int requestId, int offset, BluetoothGattCharacteristic characteristic) { 69 super.onCharacteristicReadRequest(device, requestId, offset, characteristic); 70 characteristic.setValue(new byte[]{0x03, 0x01}); 71 Log.e(TAG, String.format("1.onCharacteristicReadRequest:device name = %s, address = %s" 72 , device.getName(), device.getAddress())); 73 Log.e(TAG, String.format("onCharacteristicReadRequest:requestId = %s, offset = %s", requestId, offset)); 74 boolean result = mGattServer.sendResponse(device, requestId, BluetoothGatt.GATT_SUCCESS, offset, characteristic.getValue()); 75 76 Log.e(TAG, "read request send response:" + result); 77 } 78 79 //當有客戶端來寫數據時回調的接口 81 /** 82 * 接受具體數據字節
*/ 86 @Override 87 public void onCharacteristicWriteRequest(android.bluetooth.BluetoothDevice device, 88 int requestId, BluetoothGattCharacteristic characteristic, boolean preparedWrite, 89 boolean responseNeeded, int offset, byte[] value) { 90 super.onCharacteristicWriteRequest(device, requestId, characteristic, preparedWrite, responseNeeded, offset, value); 91 // 需調用 sendResponse 來響應,為了保持連接。 92 mGattServer.sendResponse(device, requestId, BluetoothGatt.GATT_SUCCESS, offset, null); 93 94 // 處理其它設備寫進來的數據 95 value. // 處理數據 byte[] value,記住連接設備 96 97 } 98
// 當有客戶端來寫Descriptor 時回調的接口 99 /** 描述被寫入時,在這里執行bluetoothGattServer.sendResponse(device, requestId, BluetoothGatt.GATT_SUCCESS...) 時
* 觸發onCharacteristicWriteRequest **/ 100 @Override 101 public void onDescriptorWriteRequest(BluetoothDevice device, int requestId, BluetoothGattDescriptor descriptor, boolean preparedWrite, boolean responseNeeded, int offset, byte[] value) { 102 super.onDescriptorWriteRequest(device, requestId, descriptor, preparedWrite, responseNeeded, offset, value); 103 104 Log.d(TAG, "onDescriptorWriteRequest:" + Arrays.toString(value)); 105 // now tell the connected device that this was all successfull 106 mGattServer.sendResponse(device, requestId, BluetoothGatt.GATT_SUCCESS, offset, value); 107 } 108 109 }
通過adb 獲取 藍牙 mac address:
1 adb shell settings get secure bluetooth_address
或者
1 // for Android 4.4.4 2 adb shell service call bluetooth_manager 10 3 // for Android 5.0+ 4 adb shell service call bluetooth_manager 12
編程獲取bluetooth mac address :
1 String macAddress = android.provider.Settings.Secure.getString(context.getContentResolver(), "bluetooth_address");
推薦文章: