Android BLE开发之操作IOS ANCS

前言

之前写过两篇有关于ANCS的文章,最近一段时间老是有人问关于得到ANCS服务的问题,因为IOS ANCS不同于其他的Peripheral一样对周边所有的蓝牙设备广播自己,而是仅有连接上配对并连接上IOS设备的可见,我想这对于Android、IOS、嵌入式等的开发都是一样的。

现在将以前写的Android ble操作ANCS的demo修改了一下,并集中对于ANCS的相关问题进行说明。

发现ANCS

熟悉IOS的都知道,IOS设备上的蓝牙是有很大限制的,只能连接手表、耳机等周边设备,甚至同样是IOS平台的设备都不能进行互联。但是BLE的出现给了我们使用蓝牙技术进行通信的可能。

这里我们用到的是IOS系统提供的ANCS服务获取IOS分发的通知,包括消息、来电、计划等,但是这个服务对于我们是不可见的,他并不主动进行广播,我们使用BLE scan 并不能扫描到ANCS这个服务。

** 那么是不是意味着我们就无法找到这个ANCS服务了呢? **
答案是否定的,经过调查我们发现ANCS是基于GATT做的封装,也就是他是一个BLE的gatt server,只是对通信过程加入了自定义的协议,他跟其他的Ble service是同等的,比如常见的Heart Rate。因此我们考虑通过其他的service连接上这个GATT server,然后在获取ANCS服务的思路。

how_to_find_ancs.png
发现设备

想要连接蓝牙设备我们首先要知道他的设备地址,但是IOS设备的蓝牙是不主动广播的,但是我们知道IOS是支持BLE广播的。

这里我们就可以借由这个功能让我们得到IOS设备的目标地址,当然你可以自己实现一个简单的APP去startLeAdvertisment,因为我并不会IOS开发所以这里借助了一个第三方APP(LightBlue)来虚拟一个peripherial,例如Heart Rate.

然后在我们的Android APP中进行扫描,就能扫描到这个名称为Heart Rate的周边设备。

@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;
    }
}
连接设备

使用我们上一步得到的BluetoothDevice对象或者设备地址链接到Gatt操作。
安卓代码示例如下:

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

并在连接回调中,获得连接的状态:

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

}
发现ANCS

当连接上GATT后,我们就可以调用discover函数去发现所有的服务。

gatt.discoverServices();

然后在发现服务的回调中就可以根据UUID获得ANCS服务。

@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后,我们也可以通过UUID获得ANCS的三个characteristic.

绑定设备

当我们进行ANCS操作的时候,就会弹出配对的请求的对话框要求我们来完成配对,如果我们不进行配对的话,就无法对ANCS进行操作,同时GATT连接也会经常自动断开连接。

因此我们一般是在扫描到设备后就与设备进行配对,完成配对后再与设备进行连接。

先判断是否已经在配对列表中,是,则进行连接,不是,则进行配对:

 //已经绑定,该设备在绑定的设备名单里面
 if (mBluetoothAdapter.getBondedDevices().contains(device)) {

     device.connectGatt(getApplicationContext(), false, mGattCallback);
     mBluetoothLeScanner.stopScan(mScanCallback);
 } else {//未绑定的设备
     device.createBond();
 }

通过接受系统的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服务下的三个Characteristic,其操作也无非是BLE characteristic的三种操作:

  • 读取(read)
  • 写入(write)
  • 通知(setNotification)

详细可见最后一章参考文章《Android BLE开发之玩转小米手环》。

因为需要通过对Notification Source、Data Source、Control Point进行读写通知操作完成所有的功能,因此对操作的流程、通知数据包的格式、命令的格式进行了规定,相当于应用层的协议,具体的可以参考ANCS分析的两篇文章。

  • Notification Source(setNotification):获取通知基本信息
  • Data Source(setNotification):获取通知的详细信息
  • Control Point (write): 写入通知控制命令

接下来我们主要从代码上来说明如何操作。

获取通知
  1. Data Source通知开启
 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开启后,Notification Source通知开启。
  2. Notification Source的回调中获取通知基本信息。
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中写入获取更多通知信息的命令。
 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的回调中获取更多信息。
    具体解析参见相关阅读中ANCS分析的两篇文章。
执行相应动作
  1. 解析eventFlags中的通知动作。
    public int getAction() {

        action = 0;
        //positive标志位为1
        if ((eventFlags & 0x08) > 0) {
            action = action + 1;
        }
        //negative标志位为1
        if ((eventFlags & 0x10) > 0) {
            action = action + 2;
        }
        return action;
    }
  1. 写入动作命令到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

  • 接收系统通知基本的信息,包括标题、类型(消息、来电等)、状态(产生、修改、删除)
  • 接收通知的详细信息,包括内容、应用、时间等各种信息。
  • 对通知采取相应的操作

相关阅读

** Android BLE开发相关知识 **
Android BLE开发之初识GATT
Android BLE开发之玩转小米手环

** ANCS相关知识 **
苹果通知中心服务ANCS协议分析
苹果通知中心服务ANCS协议分析二

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 206,126评论 6 481
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 88,254评论 2 382
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 152,445评论 0 341
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 55,185评论 1 278
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 64,178评论 5 371
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,970评论 1 284
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,276评论 3 399
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,927评论 0 259
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 43,400评论 1 300
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,883评论 2 323
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,997评论 1 333
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,646评论 4 322
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,213评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,204评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,423评论 1 260
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 45,423评论 2 352
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,722评论 2 345

推荐阅读更多精彩内容