PageRenderTime 3784ms CodeModel.GetById 23ms RepoModel.GetById 1ms app.codeStats 0ms

/media/packages/BluetoothMidiService/src/com/android/bluetoothmidiservice/BluetoothMidiDevice.java

https://gitlab.com/amardeep434/nitro_base
Java | 300 lines | 223 code | 43 blank | 34 comment | 30 complexity | ad0e8a9cb20e2810d82afe142189d1be MD5 | raw file
  1. /*
  2. * Copyright (C) 2015 The Android Open Source Project
  3. *
  4. * Licensed under the Apache License, Version 2.0 (the "License");
  5. * you may not use this file except in compliance with the License.
  6. * You may obtain a copy of the License at
  7. *
  8. * http://www.apache.org/licenses/LICENSE-2.0
  9. *
  10. * Unless required by applicable law or agreed to in writing, software
  11. * distributed under the License is distributed on an "AS IS" BASIS,
  12. * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  13. * See the License for the specific language governing permissions and
  14. * limitations under the License.
  15. */
  16. package com.android.bluetoothmidiservice;
  17. import android.bluetooth.BluetoothDevice;
  18. import android.bluetooth.BluetoothGatt;
  19. import android.bluetooth.BluetoothGattCallback;
  20. import android.bluetooth.BluetoothGattCharacteristic;
  21. import android.bluetooth.BluetoothGattDescriptor;
  22. import android.bluetooth.BluetoothGattService;
  23. import android.bluetooth.BluetoothProfile;
  24. import android.content.Context;
  25. import android.media.midi.MidiDeviceInfo;
  26. import android.media.midi.MidiDeviceServer;
  27. import android.media.midi.MidiDeviceStatus;
  28. import android.media.midi.MidiManager;
  29. import android.media.midi.MidiReceiver;
  30. import android.os.Bundle;
  31. import android.os.IBinder;
  32. import android.util.Log;
  33. import com.android.internal.midi.MidiEventScheduler;
  34. import com.android.internal.midi.MidiEventScheduler.MidiEvent;
  35. import libcore.io.IoUtils;
  36. import java.io.IOException;
  37. import java.util.List;
  38. import java.util.UUID;
  39. /**
  40. * Class used to implement a Bluetooth MIDI device.
  41. */
  42. public final class BluetoothMidiDevice {
  43. private static final String TAG = "BluetoothMidiDevice";
  44. private static final boolean DEBUG = false;
  45. private static final int MAX_PACKET_SIZE = 20;
  46. // Bluetooth MIDI Gatt service UUID
  47. private static final UUID MIDI_SERVICE = UUID.fromString(
  48. "03B80E5A-EDE8-4B33-A751-6CE34EC4C700");
  49. // Bluetooth MIDI Gatt characteristic UUID
  50. private static final UUID MIDI_CHARACTERISTIC = UUID.fromString(
  51. "7772E5DB-3868-4112-A1A9-F2669D106BF3");
  52. // Descriptor UUID for enabling characteristic changed notifications
  53. private static final UUID CLIENT_CHARACTERISTIC_CONFIG = UUID.fromString(
  54. "00002902-0000-1000-8000-00805f9b34fb");
  55. private final BluetoothDevice mBluetoothDevice;
  56. private final BluetoothMidiService mService;
  57. private final MidiManager mMidiManager;
  58. private MidiReceiver mOutputReceiver;
  59. private final MidiEventScheduler mEventScheduler = new MidiEventScheduler();
  60. private MidiDeviceServer mDeviceServer;
  61. private BluetoothGatt mBluetoothGatt;
  62. private BluetoothGattCharacteristic mCharacteristic;
  63. // PacketReceiver for receiving formatted packets from our BluetoothPacketEncoder
  64. private final PacketReceiver mPacketReceiver = new PacketReceiver();
  65. private final BluetoothPacketEncoder mPacketEncoder
  66. = new BluetoothPacketEncoder(mPacketReceiver, MAX_PACKET_SIZE);
  67. private final BluetoothPacketDecoder mPacketDecoder
  68. = new BluetoothPacketDecoder(MAX_PACKET_SIZE);
  69. private final MidiDeviceServer.Callback mDeviceServerCallback
  70. = new MidiDeviceServer.Callback() {
  71. @Override
  72. public void onDeviceStatusChanged(MidiDeviceServer server, MidiDeviceStatus status) {
  73. }
  74. @Override
  75. public void onClose() {
  76. close();
  77. }
  78. };
  79. private final BluetoothGattCallback mGattCallback = new BluetoothGattCallback() {
  80. @Override
  81. public void onConnectionStateChange(BluetoothGatt gatt, int status,
  82. int newState) {
  83. String intentAction;
  84. if (newState == BluetoothProfile.STATE_CONNECTED) {
  85. Log.i(TAG, "Connected to GATT server.");
  86. Log.i(TAG, "Attempting to start service discovery:" +
  87. mBluetoothGatt.discoverServices());
  88. } else if (newState == BluetoothProfile.STATE_DISCONNECTED) {
  89. Log.i(TAG, "Disconnected from GATT server.");
  90. close();
  91. }
  92. }
  93. @Override
  94. public void onServicesDiscovered(BluetoothGatt gatt, int status) {
  95. if (status == BluetoothGatt.GATT_SUCCESS) {
  96. List<BluetoothGattService> services = mBluetoothGatt.getServices();
  97. for (BluetoothGattService service : services) {
  98. if (MIDI_SERVICE.equals(service.getUuid())) {
  99. Log.d(TAG, "found MIDI_SERVICE");
  100. List<BluetoothGattCharacteristic> characteristics
  101. = service.getCharacteristics();
  102. for (BluetoothGattCharacteristic characteristic : characteristics) {
  103. if (MIDI_CHARACTERISTIC.equals(characteristic.getUuid())) {
  104. Log.d(TAG, "found MIDI_CHARACTERISTIC");
  105. mCharacteristic = characteristic;
  106. // Specification says to read the characteristic first and then
  107. // switch to receiving notifications
  108. mBluetoothGatt.readCharacteristic(characteristic);
  109. break;
  110. }
  111. }
  112. break;
  113. }
  114. }
  115. } else {
  116. Log.e(TAG, "onServicesDiscovered received: " + status);
  117. close();
  118. }
  119. }
  120. @Override
  121. public void onCharacteristicRead(BluetoothGatt gatt,
  122. BluetoothGattCharacteristic characteristic,
  123. int status) {
  124. Log.d(TAG, "onCharacteristicRead " + status);
  125. // switch to receiving notifications after initial characteristic read
  126. mBluetoothGatt.setCharacteristicNotification(characteristic, true);
  127. // Use writeType that requests acknowledgement.
  128. // This improves compatibility with various BLE-MIDI devices.
  129. int originalWriteType = characteristic.getWriteType();
  130. characteristic.setWriteType(BluetoothGattCharacteristic.WRITE_TYPE_DEFAULT);
  131. BluetoothGattDescriptor descriptor = characteristic.getDescriptor(
  132. CLIENT_CHARACTERISTIC_CONFIG);
  133. if (descriptor != null) {
  134. descriptor.setValue(BluetoothGattDescriptor.ENABLE_NOTIFICATION_VALUE);
  135. boolean result = mBluetoothGatt.writeDescriptor(descriptor);
  136. Log.d(TAG, "writeDescriptor returned " + result);
  137. } else {
  138. Log.e(TAG, "No CLIENT_CHARACTERISTIC_CONFIG for device " + mBluetoothDevice);
  139. }
  140. characteristic.setWriteType(originalWriteType);
  141. }
  142. @Override
  143. public void onCharacteristicWrite(BluetoothGatt gatt,
  144. BluetoothGattCharacteristic characteristic,
  145. int status) {
  146. Log.d(TAG, "onCharacteristicWrite " + status);
  147. mPacketEncoder.writeComplete();
  148. }
  149. @Override
  150. public void onCharacteristicChanged(BluetoothGatt gatt,
  151. BluetoothGattCharacteristic characteristic) {
  152. if (DEBUG) {
  153. logByteArray("Received ", characteristic.getValue(), 0,
  154. characteristic.getValue().length);
  155. }
  156. mPacketDecoder.decodePacket(characteristic.getValue(), mOutputReceiver);
  157. }
  158. };
  159. // This receives MIDI data that has already been passed through our MidiEventScheduler
  160. // and has been normalized by our MidiFramer.
  161. private class PacketReceiver implements PacketEncoder.PacketReceiver {
  162. // buffers of every possible packet size
  163. private final byte[][] mWriteBuffers;
  164. public PacketReceiver() {
  165. // Create buffers of every possible packet size
  166. mWriteBuffers = new byte[MAX_PACKET_SIZE + 1][];
  167. for (int i = 0; i <= MAX_PACKET_SIZE; i++) {
  168. mWriteBuffers[i] = new byte[i];
  169. }
  170. }
  171. @Override
  172. public void writePacket(byte[] buffer, int count) {
  173. if (mCharacteristic == null) {
  174. Log.w(TAG, "not ready to send packet yet");
  175. return;
  176. }
  177. byte[] writeBuffer = mWriteBuffers[count];
  178. System.arraycopy(buffer, 0, writeBuffer, 0, count);
  179. mCharacteristic.setValue(writeBuffer);
  180. if (DEBUG) {
  181. logByteArray("Sent ", mCharacteristic.getValue(), 0,
  182. mCharacteristic.getValue().length);
  183. }
  184. mBluetoothGatt.writeCharacteristic(mCharacteristic);
  185. }
  186. }
  187. public BluetoothMidiDevice(Context context, BluetoothDevice device,
  188. BluetoothMidiService service) {
  189. mBluetoothDevice = device;
  190. mService = service;
  191. mBluetoothGatt = mBluetoothDevice.connectGatt(context, false, mGattCallback);
  192. mMidiManager = (MidiManager)context.getSystemService(Context.MIDI_SERVICE);
  193. Bundle properties = new Bundle();
  194. properties.putString(MidiDeviceInfo.PROPERTY_NAME, mBluetoothGatt.getDevice().getName());
  195. properties.putParcelable(MidiDeviceInfo.PROPERTY_BLUETOOTH_DEVICE,
  196. mBluetoothGatt.getDevice());
  197. MidiReceiver[] inputPortReceivers = new MidiReceiver[1];
  198. inputPortReceivers[0] = mEventScheduler.getReceiver();
  199. mDeviceServer = mMidiManager.createDeviceServer(inputPortReceivers, 1,
  200. null, null, properties, MidiDeviceInfo.TYPE_BLUETOOTH, mDeviceServerCallback);
  201. mOutputReceiver = mDeviceServer.getOutputPortReceivers()[0];
  202. // This thread waits for outgoing messages from our MidiEventScheduler
  203. // And forwards them to our MidiFramer to be prepared to send via Bluetooth.
  204. new Thread("BluetoothMidiDevice " + mBluetoothDevice) {
  205. @Override
  206. public void run() {
  207. while (true) {
  208. MidiEvent event;
  209. try {
  210. event = (MidiEvent)mEventScheduler.waitNextEvent();
  211. } catch (InterruptedException e) {
  212. // try again
  213. continue;
  214. }
  215. if (event == null) {
  216. break;
  217. }
  218. try {
  219. mPacketEncoder.send(event.data, 0, event.count,
  220. event.getTimestamp());
  221. } catch (IOException e) {
  222. Log.e(TAG, "mPacketAccumulator.send failed", e);
  223. }
  224. mEventScheduler.addEventToPool(event);
  225. }
  226. Log.d(TAG, "BluetoothMidiDevice thread exit");
  227. }
  228. }.start();
  229. }
  230. private void close() {
  231. synchronized (mBluetoothDevice) {
  232. mEventScheduler.close();
  233. mService.deviceClosed(mBluetoothDevice);
  234. if (mDeviceServer != null) {
  235. IoUtils.closeQuietly(mDeviceServer);
  236. mDeviceServer = null;
  237. }
  238. if (mBluetoothGatt != null) {
  239. mBluetoothGatt.close();
  240. mBluetoothGatt = null;
  241. }
  242. }
  243. }
  244. public IBinder getBinder() {
  245. return mDeviceServer.asBinder();
  246. }
  247. private static void logByteArray(String prefix, byte[] value, int offset, int count) {
  248. StringBuilder builder = new StringBuilder(prefix);
  249. for (int i = offset; i < count; i++) {
  250. builder.append(String.format("0x%02X", value[i]));
  251. if (i != value.length - 1) {
  252. builder.append(", ");
  253. }
  254. }
  255. Log.d(TAG, builder.toString());
  256. }
  257. }