Advanced Tappy Management - TapTrack/TappyBLE GitHub Wiki

This tutorial covers direct use of the TappyBleCommunicationsService. This method of working with the TappyBLE SDK affords the greatest flexibility, but is fairly complicated to implement. As such, it is suggested to check to make sure that you cannot modify the Simple Tappy Manager to fit your needs before continuing. This tutorial assumes you are familiar with Services, multi-threaded programming, and interprocess communication via AIDL, so it is advisable to familiarize yourself with those topics before proceeding.

Gradle Dependencies

compile "com.taptrack.tcmptappy:tappyble-service:${latestVersion}"

Setup

<service
    android:name="com.taptrack.tcmptappy.tappy.ble.service.TappyBleCommunicationsService"
    android:process=":tcmpcomm"
    android:exported="false" />

Note that the communication service is designed to run in a separate process from the application. If you wish to run it in the same process, you can do that as well, although it may cause some UI jank.

Communicating with the Service

First it is important to remember that communications received over AIDL will come in on arbitrary threads. Therefore, it is important to either use a Handler to move all of your communications onto a common thread (often the main thread) or make sure all execution kicked off by data coming in over AIDL is thread-safe. There are three important .aidl files defined in the project:

IBLETappyMessageCallback.aidl

// IBleTappyMessageCallback.aidl
package com.taptrack.tcmptappy.tappy.ble.service;

import com.taptrack.tcmptappy.tappy.ble.ParcelableTappyBleDeviceDefinition;

interface IBleTappyMessageCallback {
    oneway void onMessageReceived(in ParcelableTappyBleDeviceDefinition device, in byte[] message);
}

IBleTappyStatusCallback.aidl

// IBleTappyStatusCallback.aidl
package com.taptrack.tcmptappy.tappy.ble.service;

import com.taptrack.tcmptappy.tappy.ble.ParcelableTappyBleDeviceDefinition;

interface IBleTappyStatusCallback {
    oneway void onTappyBleStatus(in ParcelableTappyBleDeviceDefinition device, int status);
}

IBleTappyServiceIPC.aidl

// IBleTappyServiceIPC.aidl
package com.taptrack.tcmptappy.tappy.ble.service;

import com.taptrack.tcmptappy.tappy.ble.ParcelableTappyBleDeviceDefinition;
import com.taptrack.tcmptappy.tappy.ble.service.IBleTappyMessageCallback;
import com.taptrack.tcmptappy.tappy.ble.service.IBleTappyStatusCallback;

interface IBleTappyServiceIPC {
    oneway void sendMessage(in ParcelableTappyBleDeviceDefinition definition, in byte[] message);
    oneway void broadcastMessage(in byte[] message);
    oneway void setMessageReceivedCallback(IBleTappyMessageCallback callback);
    oneway void unregisterMessageReceivedCallback();
    oneway void setUnparsableMessageReceivedCallback(IBleTappyMessageCallback callback);
    oneway void unregisterUnparsableMessageReceivedCallback();

    //using this name due to aidl limitations on overloading
    int getTappyStatusSync(in ParcelableTappyBleDeviceDefinition definition);
    oneway void getTappyStatus(in ParcelableTappyBleDeviceDefinition definition, IBleTappyStatusCallback cb);
    oneway void setOnTappyStatusChangedListener(IBleTappyStatusCallback cb);
    oneway void unregisterOnTappyStatusChangedListener();

    oneway void setConnectedDevices(in List<ParcelableTappyBleDeviceDefinition> devices);

    oneway void connectDevice(in ParcelableTappyBleDeviceDefinition definition);
    oneway void disconnectDevice(in ParcelableTappyBleDeviceDefinition definition);
    oneway void disconnectAll();

    oneway void close();
}

Note that with the exception of getTappyStatusSync, most of the methods are marked as oneway as this interface is intended to be used asynchronously. In general you will want to define both a IBleTappyMessageCallback and IBleStatusCallback. The TappyBleCommunicationService only supports one message callback and one status callback at any given time and you should structure your code accordingly.

Managing Device Connections

Once you have bound to the TappyBleCommunicationService and initialized your callbacks if desired, you need to provide the service with a Tappy to connect to. There are two ways to do this setConnectedDevices and connectDevice. If you call connectDevice, the service will attempt to initiate a connection to the Tappy specified. If the Tappy is currently READY or in the process of connecting (CONNECTING or CONNECTED), the service will ignore your request. If you use setConnectedDevices, the service will first disconnect from all devices it is connected to which are not specified in the list followed by initiating a new connection will all devices in the list (assuming they are not already READY, CONNECTING, or CONNECTED).

Communicating with Devices

The byte arrays used for all of the communication methods are TCMPMessage packets (ie the result of tcmpMessage.toByteArray()). To send a message to all connected (or connecting) devices, use sendBroadcast. If a device is in the process of connecting, the message will be queued for transmission upon the device entering the READY state. In order to message a specific device, you must provide a ParcelableTappyBleDeviceDefinition corresponding to the TappyBLE you wish to send the message to. A ParcelableTappyBleDeviceDefinition can be acquired from any TappyBleDeviceDefinition by using ParcelableTappyBleDeviceDefinition's single-argument constructor.

In order to parse responses you should generate a RawTCMPMessage from the packet and pass it to a CommandFamilyResolver (see TCMP Basics) for resolution to the appropriate response, if relevant.

Multi-Tappy Support

The SDK itself places no limit on the number of Tappies that can be connected. However, the Android SDK puts limits on the number of BLE devices that can be connected (7 for API 19+, 4 for API 18). Additionally, the phone hardware has to support the number of connected devices. Parts of the Android BLE stack appear to be blocking, so multiple connections can occasionally experience reduced performance. In particular, if you attempt to connect to several devices simultaneously, the OS will attempt the connections sequentially. If one of the connections takes longer than usual, it will block subsequent connections from starting until it either times out or succeeds.