/media/packages/BluetoothMidiService/src/com/android/bluetoothmidiservice/BluetoothMidiDevice.java
Java | 300 lines | 223 code | 43 blank | 34 comment | 30 complexity | ad0e8a9cb20e2810d82afe142189d1be MD5 | raw file
- /*
- * Copyright (C) 2015 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
- package com.android.bluetoothmidiservice;
- import android.bluetooth.BluetoothDevice;
- import android.bluetooth.BluetoothGatt;
- import android.bluetooth.BluetoothGattCallback;
- import android.bluetooth.BluetoothGattCharacteristic;
- import android.bluetooth.BluetoothGattDescriptor;
- import android.bluetooth.BluetoothGattService;
- import android.bluetooth.BluetoothProfile;
- import android.content.Context;
- import android.media.midi.MidiDeviceInfo;
- import android.media.midi.MidiDeviceServer;
- import android.media.midi.MidiDeviceStatus;
- import android.media.midi.MidiManager;
- import android.media.midi.MidiReceiver;
- import android.os.Bundle;
- import android.os.IBinder;
- import android.util.Log;
- import com.android.internal.midi.MidiEventScheduler;
- import com.android.internal.midi.MidiEventScheduler.MidiEvent;
- import libcore.io.IoUtils;
- import java.io.IOException;
- import java.util.List;
- import java.util.UUID;
- /**
- * Class used to implement a Bluetooth MIDI device.
- */
- public final class BluetoothMidiDevice {
- private static final String TAG = "BluetoothMidiDevice";
- private static final boolean DEBUG = false;
- private static final int MAX_PACKET_SIZE = 20;
- // Bluetooth MIDI Gatt service UUID
- private static final UUID MIDI_SERVICE = UUID.fromString(
- "03B80E5A-EDE8-4B33-A751-6CE34EC4C700");
- // Bluetooth MIDI Gatt characteristic UUID
- private static final UUID MIDI_CHARACTERISTIC = UUID.fromString(
- "7772E5DB-3868-4112-A1A9-F2669D106BF3");
- // Descriptor UUID for enabling characteristic changed notifications
- private static final UUID CLIENT_CHARACTERISTIC_CONFIG = UUID.fromString(
- "00002902-0000-1000-8000-00805f9b34fb");
- private final BluetoothDevice mBluetoothDevice;
- private final BluetoothMidiService mService;
- private final MidiManager mMidiManager;
- private MidiReceiver mOutputReceiver;
- private final MidiEventScheduler mEventScheduler = new MidiEventScheduler();
- private MidiDeviceServer mDeviceServer;
- private BluetoothGatt mBluetoothGatt;
- private BluetoothGattCharacteristic mCharacteristic;
- // PacketReceiver for receiving formatted packets from our BluetoothPacketEncoder
- private final PacketReceiver mPacketReceiver = new PacketReceiver();
- private final BluetoothPacketEncoder mPacketEncoder
- = new BluetoothPacketEncoder(mPacketReceiver, MAX_PACKET_SIZE);
- private final BluetoothPacketDecoder mPacketDecoder
- = new BluetoothPacketDecoder(MAX_PACKET_SIZE);
- private final MidiDeviceServer.Callback mDeviceServerCallback
- = new MidiDeviceServer.Callback() {
- @Override
- public void onDeviceStatusChanged(MidiDeviceServer server, MidiDeviceStatus status) {
- }
- @Override
- public void onClose() {
- close();
- }
- };
- private final BluetoothGattCallback mGattCallback = new BluetoothGattCallback() {
- @Override
- public void onConnectionStateChange(BluetoothGatt gatt, int status,
- int newState) {
- String intentAction;
- if (newState == BluetoothProfile.STATE_CONNECTED) {
- Log.i(TAG, "Connected to GATT server.");
- Log.i(TAG, "Attempting to start service discovery:" +
- mBluetoothGatt.discoverServices());
- } else if (newState == BluetoothProfile.STATE_DISCONNECTED) {
- Log.i(TAG, "Disconnected from GATT server.");
- close();
- }
- }
- @Override
- public void onServicesDiscovered(BluetoothGatt gatt, int status) {
- if (status == BluetoothGatt.GATT_SUCCESS) {
- List<BluetoothGattService> services = mBluetoothGatt.getServices();
- for (BluetoothGattService service : services) {
- if (MIDI_SERVICE.equals(service.getUuid())) {
- Log.d(TAG, "found MIDI_SERVICE");
- List<BluetoothGattCharacteristic> characteristics
- = service.getCharacteristics();
- for (BluetoothGattCharacteristic characteristic : characteristics) {
- if (MIDI_CHARACTERISTIC.equals(characteristic.getUuid())) {
- Log.d(TAG, "found MIDI_CHARACTERISTIC");
- mCharacteristic = characteristic;
- // Specification says to read the characteristic first and then
- // switch to receiving notifications
- mBluetoothGatt.readCharacteristic(characteristic);
- break;
- }
- }
- break;
- }
- }
- } else {
- Log.e(TAG, "onServicesDiscovered received: " + status);
- close();
- }
- }
- @Override
- public void onCharacteristicRead(BluetoothGatt gatt,
- BluetoothGattCharacteristic characteristic,
- int status) {
- Log.d(TAG, "onCharacteristicRead " + status);
- // switch to receiving notifications after initial characteristic read
- mBluetoothGatt.setCharacteristicNotification(characteristic, true);
- // Use writeType that requests acknowledgement.
- // This improves compatibility with various BLE-MIDI devices.
- int originalWriteType = characteristic.getWriteType();
- characteristic.setWriteType(BluetoothGattCharacteristic.WRITE_TYPE_DEFAULT);
- BluetoothGattDescriptor descriptor = characteristic.getDescriptor(
- CLIENT_CHARACTERISTIC_CONFIG);
- if (descriptor != null) {
- descriptor.setValue(BluetoothGattDescriptor.ENABLE_NOTIFICATION_VALUE);
- boolean result = mBluetoothGatt.writeDescriptor(descriptor);
- Log.d(TAG, "writeDescriptor returned " + result);
- } else {
- Log.e(TAG, "No CLIENT_CHARACTERISTIC_CONFIG for device " + mBluetoothDevice);
- }
- characteristic.setWriteType(originalWriteType);
- }
- @Override
- public void onCharacteristicWrite(BluetoothGatt gatt,
- BluetoothGattCharacteristic characteristic,
- int status) {
- Log.d(TAG, "onCharacteristicWrite " + status);
- mPacketEncoder.writeComplete();
- }
- @Override
- public void onCharacteristicChanged(BluetoothGatt gatt,
- BluetoothGattCharacteristic characteristic) {
- if (DEBUG) {
- logByteArray("Received ", characteristic.getValue(), 0,
- characteristic.getValue().length);
- }
- mPacketDecoder.decodePacket(characteristic.getValue(), mOutputReceiver);
- }
- };
- // This receives MIDI data that has already been passed through our MidiEventScheduler
- // and has been normalized by our MidiFramer.
- private class PacketReceiver implements PacketEncoder.PacketReceiver {
- // buffers of every possible packet size
- private final byte[][] mWriteBuffers;
- public PacketReceiver() {
- // Create buffers of every possible packet size
- mWriteBuffers = new byte[MAX_PACKET_SIZE + 1][];
- for (int i = 0; i <= MAX_PACKET_SIZE; i++) {
- mWriteBuffers[i] = new byte[i];
- }
- }
- @Override
- public void writePacket(byte[] buffer, int count) {
- if (mCharacteristic == null) {
- Log.w(TAG, "not ready to send packet yet");
- return;
- }
- byte[] writeBuffer = mWriteBuffers[count];
- System.arraycopy(buffer, 0, writeBuffer, 0, count);
- mCharacteristic.setValue(writeBuffer);
- if (DEBUG) {
- logByteArray("Sent ", mCharacteristic.getValue(), 0,
- mCharacteristic.getValue().length);
- }
- mBluetoothGatt.writeCharacteristic(mCharacteristic);
- }
- }
- public BluetoothMidiDevice(Context context, BluetoothDevice device,
- BluetoothMidiService service) {
- mBluetoothDevice = device;
- mService = service;
- mBluetoothGatt = mBluetoothDevice.connectGatt(context, false, mGattCallback);
- mMidiManager = (MidiManager)context.getSystemService(Context.MIDI_SERVICE);
- Bundle properties = new Bundle();
- properties.putString(MidiDeviceInfo.PROPERTY_NAME, mBluetoothGatt.getDevice().getName());
- properties.putParcelable(MidiDeviceInfo.PROPERTY_BLUETOOTH_DEVICE,
- mBluetoothGatt.getDevice());
- MidiReceiver[] inputPortReceivers = new MidiReceiver[1];
- inputPortReceivers[0] = mEventScheduler.getReceiver();
- mDeviceServer = mMidiManager.createDeviceServer(inputPortReceivers, 1,
- null, null, properties, MidiDeviceInfo.TYPE_BLUETOOTH, mDeviceServerCallback);
- mOutputReceiver = mDeviceServer.getOutputPortReceivers()[0];
- // This thread waits for outgoing messages from our MidiEventScheduler
- // And forwards them to our MidiFramer to be prepared to send via Bluetooth.
- new Thread("BluetoothMidiDevice " + mBluetoothDevice) {
- @Override
- public void run() {
- while (true) {
- MidiEvent event;
- try {
- event = (MidiEvent)mEventScheduler.waitNextEvent();
- } catch (InterruptedException e) {
- // try again
- continue;
- }
- if (event == null) {
- break;
- }
- try {
- mPacketEncoder.send(event.data, 0, event.count,
- event.getTimestamp());
- } catch (IOException e) {
- Log.e(TAG, "mPacketAccumulator.send failed", e);
- }
- mEventScheduler.addEventToPool(event);
- }
- Log.d(TAG, "BluetoothMidiDevice thread exit");
- }
- }.start();
- }
- private void close() {
- synchronized (mBluetoothDevice) {
- mEventScheduler.close();
- mService.deviceClosed(mBluetoothDevice);
- if (mDeviceServer != null) {
- IoUtils.closeQuietly(mDeviceServer);
- mDeviceServer = null;
- }
- if (mBluetoothGatt != null) {
- mBluetoothGatt.close();
- mBluetoothGatt = null;
- }
- }
- }
- public IBinder getBinder() {
- return mDeviceServer.asBinder();
- }
- private static void logByteArray(String prefix, byte[] value, int offset, int count) {
- StringBuilder builder = new StringBuilder(prefix);
- for (int i = offset; i < count; i++) {
- builder.append(String.format("0x%02X", value[i]));
- if (i != value.length - 1) {
- builder.append(", ");
- }
- }
- Log.d(TAG, builder.toString());
- }
- }