Android BLE開(kāi)發(fā)之操作IOS ANCS

前言

之前寫(xiě)過(guò)兩篇有關(guān)于ANCS的文章,最近一段時(shí)間老是有人問(wèn)關(guān)于得到ANCS服務(wù)的問(wèn)題,因?yàn)镮OS ANCS不同于其他的Peripheral一樣對(duì)周邊所有的藍(lán)牙設(shè)備廣播自己,而是僅有連接上配對(duì)并連接上IOS設(shè)備的可見(jiàn),我想這對(duì)于Android、IOS、嵌入式等的開(kāi)發(fā)都是一樣的。

現(xiàn)在將以前寫(xiě)的Android ble操作ANCS的demo修改了一下,并集中對(duì)于ANCS的相關(guān)問(wèn)題進(jìn)行說(shuō)明。

發(fā)現(xiàn)ANCS

熟悉IOS的都知道,IOS設(shè)備上的藍(lán)牙是有很大限制的,只能連接手表、耳機(jī)等周邊設(shè)備,甚至同樣是IOS平臺(tái)的設(shè)備都不能進(jìn)行互聯(lián)。但是BLE的出現(xiàn)給了我們使用藍(lán)牙技術(shù)進(jìn)行通信的可能。

這里我們用到的是IOS系統(tǒng)提供的ANCS服務(wù)獲取IOS分發(fā)的通知,包括消息、來(lái)電、計(jì)劃等,但是這個(gè)服務(wù)對(duì)于我們是不可見(jiàn)的,他并不主動(dòng)進(jìn)行廣播,我們使用BLE scan 并不能掃描到ANCS這個(gè)服務(wù)。

** 那么是不是意味著我們就無(wú)法找到這個(gè)ANCS服務(wù)了呢? **
答案是否定的,經(jīng)過(guò)調(diào)查我們發(fā)現(xiàn)ANCS是基于GATT做的封裝,也就是他是一個(gè)BLE的gatt server,只是對(duì)通信過(guò)程加入了自定義的協(xié)議,他跟其他的Ble service是同等的,比如常見(jiàn)的Heart Rate。因此我們考慮通過(guò)其他的service連接上這個(gè)GATT server,然后在獲取ANCS服務(wù)的思路。

how_to_find_ancs.png
發(fā)現(xiàn)設(shè)備

想要連接藍(lán)牙設(shè)備我們首先要知道他的設(shè)備地址,但是IOS設(shè)備的藍(lán)牙是不主動(dòng)廣播的,但是我們知道IOS是支持BLE廣播的。

這里我們就可以借由這個(gè)功能讓我們得到IOS設(shè)備的目標(biāo)地址,當(dāng)然你可以自己實(shí)現(xiàn)一個(gè)簡(jiǎn)單的APP去startLeAdvertisment,因?yàn)槲也⒉粫?huì)IOS開(kāi)發(fā)所以這里借助了一個(gè)第三方APP(LightBlue)來(lái)虛擬一個(gè)peripherial,例如Heart Rate.

然后在我們的Android APP中進(jìn)行掃描,就能掃描到這個(gè)名稱為Heart Rate的周邊設(shè)備。

@Override
public void onScanResult(int callbackType, ScanResult result) {
    Log.d(TAG, "onScanResult Device Address :" + result.getDevice());

    BluetoothDevice device = result.getDevice();

    if (device.getName() != null && device.getName().equals("Heart Rate")) {
         mTargetDevice = device;
    }
}
連接設(shè)備

使用我們上一步得到的BluetoothDevice對(duì)象或者設(shè)備地址鏈接到Gatt操作。
安卓代碼示例如下:

device.connectGatt(getApplicationContext(), false, mGattCallback);

并在連接回調(diào)中,獲得連接的狀態(tài):

private class LocalBluetoothGattCallback extends BluetoothGattCallback {

   @Override
   public void onConnectionStateChange(BluetoothGatt gatt, int status, int newState) {
       if (newState == BluetoothProfile.STATE_CONNECTED) {
           Log.d(TAG, "connected");

           mConnectedGatt = gatt;
       }
       if (newState == BluetoothProfile.STATE_DISCONNECTED) {
           Log.d(TAG, "disconnected");
           mConnectedGatt = null;
       }
   }

}
發(fā)現(xiàn)ANCS

當(dāng)連接上GATT后,我們就可以調(diào)用discover函數(shù)去發(fā)現(xiàn)所有的服務(wù)。

gatt.discoverServices();

然后在發(fā)現(xiàn)服務(wù)的回調(diào)中就可以根據(jù)UUID獲得ANCS服務(wù)。

@Override
public void onServicesDiscovered(BluetoothGatt gatt, int status) {
    if (status == BluetoothGatt.GATT_SUCCESS) {
        BluetoothGattService ancsService = gatt.getService(UUID.fromString(Constants.service_ancs));
        if (ancsService == null) {
            Log.d(TAG, "ANCS cannot find");
        } else {
            Log.d(TAG, "ANCS find");

            mANCSService = ancsService;
            mDataSourceChar = ancsService.getCharacteristic(UUID.fromString(Constants.characteristics_data_source));
            mPointControlChar = ancsService.getCharacteristic(UUID.fromString(Constants.characteristics_control_point));
            mNotificationSourceChar = ancsService.getCharacteristic(UUID.fromString(Constants.characteristics_notification_source));


        }
    }
}

獲得ANCS后,我們也可以通過(guò)UUID獲得ANCS的三個(gè)characteristic.

綁定設(shè)備

當(dāng)我們進(jìn)行ANCS操作的時(shí)候,就會(huì)彈出配對(duì)的請(qǐng)求的對(duì)話框要求我們來(lái)完成配對(duì),如果我們不進(jìn)行配對(duì)的話,就無(wú)法對(duì)ANCS進(jìn)行操作,同時(shí)GATT連接也會(huì)經(jīng)常自動(dòng)斷開(kāi)連接。

因此我們一般是在掃描到設(shè)備后就與設(shè)備進(jìn)行配對(duì),完成配對(duì)后再與設(shè)備進(jìn)行連接。

先判斷是否已經(jīng)在配對(duì)列表中,是,則進(jìn)行連接,不是,則進(jìn)行配對(duì):

 //已經(jīng)綁定,該設(shè)備在綁定的設(shè)備名單里面
 if (mBluetoothAdapter.getBondedDevices().contains(device)) {

     device.connectGatt(getApplicationContext(), false, mGattCallback);
     mBluetoothLeScanner.stopScan(mScanCallback);
 } else {//未綁定的設(shè)備
     device.createBond();
 }

通過(guò)接受系統(tǒng)的BondStateChanged廣播接受綁定成功的消息:

if (BluetoothDevice.ACTION_BOND_STATE_CHANGED.equals(action)) {
    if (intent.getIntExtra(BluetoothDevice.EXTRA_BOND_STATE, -1) == BluetoothDevice.BOND_BONDED) {
        showMessage("Bluetooth bond success!");
    }
}

操作ANCS

操作ANCS就是操作ANCS服務(wù)下的三個(gè)Characteristic,其操作也無(wú)非是BLE characteristic的三種操作:

  • 讀取(read)
  • 寫(xiě)入(write)
  • 通知(setNotification)

詳細(xì)可見(jiàn)最后一章參考文章《Android BLE開(kāi)發(fā)之玩轉(zhuǎn)小米手環(huán)》。

因?yàn)樾枰ㄟ^(guò)對(duì)Notification Source、Data Source、Control Point進(jìn)行讀寫(xiě)通知操作完成所有的功能,因此對(duì)操作的流程、通知數(shù)據(jù)包的格式、命令的格式進(jìn)行了規(guī)定,相當(dāng)于應(yīng)用層的協(xié)議,具體的可以參考ANCS分析的兩篇文章。

  • Notification Source(setNotification):獲取通知基本信息
  • Data Source(setNotification):獲取通知的詳細(xì)信息
  • Control Point (write): 寫(xiě)入通知控制命令

接下來(lái)我們主要從代碼上來(lái)說(shuō)明如何操作。

獲取通知
  1. Data Source通知開(kāi)啟
 private void setNotificationEnabled(BluetoothGattCharacteristic characteristic) {
    mConnectedGatt.setCharacteristicNotification(characteristic, true);
    BluetoothGattDescriptor descriptor = characteristic.getDescriptor(UUID.fromString(Constants.descriptor_config));
    if (descriptor != null) {
        descriptor.setValue(BluetoothGattDescriptor.ENABLE_NOTIFICATION_VALUE);
        mConnectedGatt.writeDescriptor(descriptor);
    }
}
  1. Data Source開(kāi)啟后,Notification Source通知開(kāi)啟。
  2. Notification Source的回調(diào)中獲取通知基本信息。
System.out.println(
    "EventId:" + String.format("%d", nsData[0]) + "\n" +
    "EventFlags:" + String.format("%02x", nsData[1]) + "\n" +
    "Category id:" + String.format("%d", nsData[2]) + "\n" +
    "Category Count:" + String.format("%d", nsData[3]) + "\n" +
    "NotificationUId:" + String.format("%02X", nsData[4]) + String.format("%02X", nsData[5])+ String.format("%02X", nsData[6]) + String.format("%02X", nsData[7]) + "\n"
);
  1. 往Control Point中寫(xiě)入獲取更多通知信息的命令。
 private void getMoreAboutNotification(byte[] nsData) {
        byte[] getNotificationAttribute = {
        (byte) 0x00,
        //UID
        nsData[4], nsData[5], nsData[6], nsData[7],
        //app id
        (byte) 0x00,
        //title
        (byte) 0x01, (byte) 0xff, (byte) 0xff,
        //message
        (byte) 0x03, (byte) 0xff, (byte) 0xff
    };

    if (mConnectedGatt != null) {
        mPointControlChar.setValue(getNotificationAttribute);
        mConnectedGatt.writeCharacteristic(mPointControlChar);
    }
}
  1. Data Source的回調(diào)中獲取更多信息。
    具體解析參見(jiàn)相關(guān)閱讀中ANCS分析的兩篇文章。
執(zhí)行相應(yīng)動(dòng)作
  1. 解析eventFlags中的通知?jiǎng)幼鳌?/li>
    public int getAction() {

        action = 0;
        //positive標(biāo)志位為1
        if ((eventFlags & 0x08) > 0) {
            action = action + 1;
        }
        //negative標(biāo)志位為1
        if ((eventFlags & 0x10) > 0) {
            action = action + 2;
        }
        return action;
    }
  1. 寫(xiě)入動(dòng)作命令到Control Point中。
byte[] action = {
        (byte) 0x02,
        //UID
        nid[0], nid[1], nid[2], nid[3],
        //positive action id(二選一)
        (byte) 0x00,
        //negative action id(二選一)
        (byte)0x01,
};

Demo

Github地址:ANCSReader

  • 接收系統(tǒng)通知基本的信息,包括標(biāo)題、類型(消息、來(lái)電等)、狀態(tài)(產(chǎn)生、修改、刪除)
  • 接收通知的詳細(xì)信息,包括內(nèi)容、應(yīng)用、時(shí)間等各種信息。
  • 對(duì)通知采取相應(yīng)的操作

相關(guān)閱讀

** Android BLE開(kāi)發(fā)相關(guān)知識(shí) **
Android BLE開(kāi)發(fā)之初識(shí)GATT
Android BLE開(kāi)發(fā)之玩轉(zhuǎn)小米手環(huán)

** ANCS相關(guān)知識(shí) **
蘋果通知中心服務(wù)ANCS協(xié)議分析
蘋果通知中心服務(wù)ANCS協(xié)議分析二

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書(shū)系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

推薦閱讀更多精彩內(nèi)容