1 /* 2 * Copyright (C) 2023 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 17 package com.android.server.usb; 18 19 import static org.junit.Assert.assertEquals; 20 21 import androidx.test.filters.SmallTest; 22 import androidx.test.runner.AndroidJUnit4; 23 24 import org.junit.Test; 25 import org.junit.runner.RunWith; 26 27 import java.util.Arrays; 28 import java.util.Random; 29 30 /** 31 * Unit tests for com.android.server.usb.UsbMidiPacketConverter. 32 */ 33 @SmallTest 34 @RunWith(AndroidJUnit4.class) 35 public class UsbMidiPacketConverterTest { generateRandomByteStream(Random rnd, int size)36 private byte[] generateRandomByteStream(Random rnd, int size) { 37 byte[] output = new byte[size]; 38 rnd.nextBytes(output); 39 return output; 40 } 41 compareByteArrays(byte[] expectedArray, byte[] outputArray)42 private void compareByteArrays(byte[] expectedArray, byte[] outputArray) { 43 assertEquals(expectedArray.length, outputArray.length); 44 for (int i = 0; i < outputArray.length; i++) { 45 assertEquals(expectedArray[i], outputArray[i]); 46 } 47 } 48 49 @Test testDecoderSinglePacket()50 public void testDecoderSinglePacket() { 51 UsbMidiPacketConverter usbMidiPacketConverter = new UsbMidiPacketConverter(); 52 usbMidiPacketConverter.createDecoders(2); 53 byte[] input = new byte[] {0x19 /* Cable 1 Note-On */, (byte) 0x91, 0x33, 0x66}; 54 byte[] expectedOutputCable0 = new byte[] {}; 55 byte[] expectedOutputCable1 = new byte[] {(byte) 0x91, 0x33, 0x66}; 56 usbMidiPacketConverter.decodeMidiPackets(input, input.length); 57 byte[] actualOutputCable0 = usbMidiPacketConverter.pullDecodedMidiPackets(0); 58 byte[] actualOutputCable1 = usbMidiPacketConverter.pullDecodedMidiPackets(1); 59 compareByteArrays(expectedOutputCable0, actualOutputCable0); 60 compareByteArrays(expectedOutputCable1, actualOutputCable1); 61 } 62 63 @Test testDecoderMultiplePackets()64 public void testDecoderMultiplePackets() { 65 UsbMidiPacketConverter usbMidiPacketConverter = new UsbMidiPacketConverter(); 66 usbMidiPacketConverter.createDecoders(4); 67 byte[] input = new byte[] { 68 0x1B /* Cable 1 Control Change */, (byte) 0xB4, 0x55, 0x6E, 69 0x35 /* Cable 3 Single byte SysEx */, (byte) 0xF8, 0x00, 0x00, 70 0x02 /* Cable 0 Two byte System Common */, (byte) 0xF3, 0x12, 0x00}; 71 byte[] expectedOutputCable0 = new byte[] {(byte) 0xF3, 0x12}; 72 byte[] expectedOutputCable1 = new byte[] {(byte) 0xB4, 0x55, 0x6E}; 73 byte[] expectedOutputCable2 = new byte[] {}; 74 byte[] expectedOutputCable3 = new byte[] {(byte) 0xF8}; 75 usbMidiPacketConverter.decodeMidiPackets(input, input.length); 76 byte[] actualOutputCable0 = usbMidiPacketConverter.pullDecodedMidiPackets(0); 77 byte[] actualOutputCable1 = usbMidiPacketConverter.pullDecodedMidiPackets(1); 78 byte[] actualOutputCable2 = usbMidiPacketConverter.pullDecodedMidiPackets(2); 79 byte[] actualOutputCable3 = usbMidiPacketConverter.pullDecodedMidiPackets(3); 80 compareByteArrays(expectedOutputCable0, actualOutputCable0); 81 compareByteArrays(expectedOutputCable1, actualOutputCable1); 82 compareByteArrays(expectedOutputCable2, actualOutputCable2); 83 compareByteArrays(expectedOutputCable3, actualOutputCable3); 84 } 85 86 @Test testDecoderSysExEndFirstByte()87 public void testDecoderSysExEndFirstByte() { 88 UsbMidiPacketConverter usbMidiPacketConverter = new UsbMidiPacketConverter(); 89 usbMidiPacketConverter.createDecoders(2); 90 byte[] input = new byte[] { 91 0x14 /* Cable 1 SysEx Start */, (byte) 0xF0, 0x00, 0x01, 92 0x15 /* Cable 1 Single byte SysEx End */, (byte) 0xF7, 0x00, 0x00}; 93 byte[] expectedOutputCable0 = new byte[] {}; 94 byte[] expectedOutputCable1 = new byte[] { 95 (byte) 0xF0, 0x00, 0x01, 96 (byte) 0xF7}; 97 usbMidiPacketConverter.decodeMidiPackets(input, input.length); 98 byte[] actualOutputCable0 = usbMidiPacketConverter.pullDecodedMidiPackets(0); 99 byte[] actualOutputCable1 = usbMidiPacketConverter.pullDecodedMidiPackets(1); 100 compareByteArrays(expectedOutputCable0, actualOutputCable0); 101 compareByteArrays(expectedOutputCable1, actualOutputCable1); 102 } 103 104 @Test testDecoderSysExEndSecondByte()105 public void testDecoderSysExEndSecondByte() { 106 UsbMidiPacketConverter usbMidiPacketConverter = new UsbMidiPacketConverter(); 107 usbMidiPacketConverter.createDecoders(1); 108 byte[] input = new byte[] { 109 0x04 /* Cable 0 SysEx Start */, (byte) 0xF0, 0x00, 0x01, 110 0x06 /* Cable 0 Two byte SysEx End */, 0x02, (byte) 0xF7, 0x00}; 111 byte[] expectedOutputCable0 = new byte[] { 112 (byte) 0xF0, 0x00, 0x01, 113 0x02, (byte) 0xF7}; 114 usbMidiPacketConverter.decodeMidiPackets(input, input.length); 115 byte[] actualOutputCable0 = usbMidiPacketConverter.pullDecodedMidiPackets(0); 116 compareByteArrays(expectedOutputCable0, actualOutputCable0); 117 } 118 119 @Test testDecoderSysExEndThirdByte()120 public void testDecoderSysExEndThirdByte() { 121 UsbMidiPacketConverter usbMidiPacketConverter = new UsbMidiPacketConverter(); 122 byte[] input = new byte[] { 123 0x04 /* Cable 0 SysEx Start */, (byte) 0xF0, 0x00, 0x01, 124 0x07 /* Cable 0 Three byte SysEx End */, 0x02, 0x03, (byte) 0xF7}; 125 usbMidiPacketConverter.createDecoders(1); 126 byte[] expectedOutputCable0 = new byte[] { 127 (byte) 0xF0, 0x00, 0x01, 128 0x02, 0x03, (byte) 0xF7}; 129 usbMidiPacketConverter.decodeMidiPackets(input, input.length); 130 byte[] actualOutputCable0 = usbMidiPacketConverter.pullDecodedMidiPackets(0); 131 compareByteArrays(expectedOutputCable0, actualOutputCable0); 132 } 133 134 @Test testDecoderSysExStartEnd()135 public void testDecoderSysExStartEnd() { 136 UsbMidiPacketConverter usbMidiPacketConverter = new UsbMidiPacketConverter(); 137 byte[] input = new byte[] { 138 0x06 /* Cable 0 Two byte SysEx End */, (byte) 0xF0, (byte) 0xF7, 0x00}; 139 usbMidiPacketConverter.createDecoders(1); 140 byte[] expectedOutputCable0 = new byte[] { 141 (byte) 0xF0, (byte) 0xF7}; 142 usbMidiPacketConverter.decodeMidiPackets(input, input.length); 143 byte[] actualOutputCable0 = usbMidiPacketConverter.pullDecodedMidiPackets(0); 144 compareByteArrays(expectedOutputCable0, actualOutputCable0); 145 } 146 147 @Test testDecoderSysExStartByteEnd()148 public void testDecoderSysExStartByteEnd() { 149 UsbMidiPacketConverter usbMidiPacketConverter = new UsbMidiPacketConverter(); 150 byte[] input = new byte[] { 151 0x07 /* Cable 0 Three byte SysEx End */, (byte) 0xF0, 0x44, (byte) 0xF7}; 152 usbMidiPacketConverter.createDecoders(1); 153 byte[] expectedOutputCable0 = new byte[] { 154 (byte) 0xF0, 0x44, (byte) 0xF7}; 155 usbMidiPacketConverter.decodeMidiPackets(input, input.length); 156 byte[] actualOutputCable0 = usbMidiPacketConverter.pullDecodedMidiPackets(0); 157 compareByteArrays(expectedOutputCable0, actualOutputCable0); 158 } 159 160 @Test testDecoderDefaultToFirstCable()161 public void testDecoderDefaultToFirstCable() { 162 UsbMidiPacketConverter usbMidiPacketConverter = new UsbMidiPacketConverter(); 163 byte[] input = new byte[] {0x49 /* Cable 4 Note-On */, (byte) 0x91, 0x22, 0x33}; 164 usbMidiPacketConverter.createDecoders(1); 165 byte[] expectedOutputCable0 = new byte[] { 166 (byte) 0x91, 0x22, 0x33}; 167 usbMidiPacketConverter.decodeMidiPackets(input, input.length); 168 byte[] actualOutputCable0 = usbMidiPacketConverter.pullDecodedMidiPackets(0); 169 compareByteArrays(expectedOutputCable0, actualOutputCable0); 170 } 171 172 @Test testDecoderLargePacketDoesNotCrash()173 public void testDecoderLargePacketDoesNotCrash() { 174 for (long seed = 1001; seed < 5000; seed += 777) { 175 UsbMidiPacketConverter usbMidiPacketConverter = new UsbMidiPacketConverter(); 176 usbMidiPacketConverter.createDecoders(3); 177 Random rnd = new Random(seed); 178 byte[] input = generateRandomByteStream(rnd, 1003 /* arbitrary large size */); 179 usbMidiPacketConverter.decodeMidiPackets(input, input.length); 180 usbMidiPacketConverter.pullDecodedMidiPackets(0); 181 usbMidiPacketConverter.pullDecodedMidiPackets(1); 182 usbMidiPacketConverter.pullDecodedMidiPackets(2); 183 } 184 } 185 186 @Test testEncoderBasic()187 public void testEncoderBasic() { 188 UsbMidiPacketConverter usbMidiPacketConverter = new UsbMidiPacketConverter(); 189 usbMidiPacketConverter.createEncoders(1); 190 byte[] input = new byte[] {(byte) 0x91 /* Note-On */, 0x33, 0x66}; 191 byte[] expectedOutput = new byte[] { 192 0x09 /* Cable 0 Note-On */, (byte) 0x91, 0x33, 0x66}; 193 usbMidiPacketConverter.encodeMidiPackets(input, input.length, 0); 194 byte[] output = usbMidiPacketConverter.pullEncodedMidiPackets(); 195 compareByteArrays(expectedOutput, output); 196 } 197 198 @Test testEncoderMultiplePackets()199 public void testEncoderMultiplePackets() { 200 UsbMidiPacketConverter usbMidiPacketConverter = new UsbMidiPacketConverter(); 201 usbMidiPacketConverter.createEncoders(3); 202 byte[] inputCable2 = new byte[] { 203 (byte) 0xB4 /* Control Change */, 0x55, 0x6E}; 204 byte[] inputCable1 = new byte[] { 205 (byte) 0xF8 /* Timing Clock (Single Byte) */, 206 (byte) 0xF3 /* Song Select (Two Bytes) */, 0x12}; 207 byte[] expectedOutput = new byte[] { 208 0x2B /* Cable 2 Control Change */, (byte) 0xB4, 0x55, 0x6E, 209 0x15 /* Cable 1 Timing Clock */, (byte) 0xF8, 0x00, 0x00, 210 0x12 /* Cable 1 Two Byte System Common */, (byte) 0xF3, 0x12, 0x00}; 211 usbMidiPacketConverter.encodeMidiPackets(inputCable2, inputCable2.length, 2); 212 usbMidiPacketConverter.encodeMidiPackets(inputCable1, inputCable1.length, 1); 213 byte[] output = usbMidiPacketConverter.pullEncodedMidiPackets(); 214 compareByteArrays(expectedOutput, output); 215 } 216 217 @Test testEncoderWeavePackets()218 public void testEncoderWeavePackets() { 219 UsbMidiPacketConverter usbMidiPacketConverter = new UsbMidiPacketConverter(); 220 usbMidiPacketConverter.createEncoders(2); 221 byte[] inputCable1Msg1 = new byte[] { 222 (byte) 0x93 /* Note-On */, 0x23, 0x43}; 223 byte[] inputCable0Msg = new byte[] { 224 (byte) 0xB4 /* Control Change */, 0x65, 0x26}; 225 byte[] inputCable1Msg2 = new byte[] { 226 (byte) 0xA4 /* Poly-KeyPress */, 0x52, 0x76}; 227 byte[] expectedOutput = new byte[] { 228 0x19 /* Cable 1 Note-On */, (byte) 0x93, 0x23, 0x43, 229 0x0B /* Cable 0 Control Change */, (byte) 0xB4, 0x65, 0x26, 230 0x1A /* Cable 1 Poly-KeyPress */, (byte) 0xA4, 0x52, 0x76}; 231 usbMidiPacketConverter.encodeMidiPackets(inputCable1Msg1, inputCable1Msg1.length, 1); 232 usbMidiPacketConverter.encodeMidiPackets(inputCable0Msg, inputCable0Msg.length, 0); 233 usbMidiPacketConverter.encodeMidiPackets(inputCable1Msg2, inputCable1Msg2.length, 1); 234 byte[] output = usbMidiPacketConverter.pullEncodedMidiPackets(); 235 compareByteArrays(expectedOutput, output); 236 } 237 238 @Test testEncoderSysExEndFirstByte()239 public void testEncoderSysExEndFirstByte() { 240 UsbMidiPacketConverter usbMidiPacketConverter = new UsbMidiPacketConverter(); 241 usbMidiPacketConverter.createEncoders(1); 242 byte[] input = new byte[] { 243 (byte) 0xF0 /* SysEx Start */, 0x00, 0x01, 244 (byte) 0xF7 /* SysEx End */}; 245 byte[] expectedOutput = new byte[] { 246 0x04 /* Cable 0 Three Byte SysEx Start */, (byte) 0xF0, 0x00, 0x01, 247 0x05 /* Cable 0 One Byte SysEx End */, (byte) 0xF7, 0x00, 0x00}; 248 usbMidiPacketConverter.encodeMidiPackets(input, input.length, 0); 249 byte[] output = usbMidiPacketConverter.pullEncodedMidiPackets(); 250 compareByteArrays(expectedOutput, output); 251 } 252 253 @Test testEncoderSysExEndSecondByte()254 public void testEncoderSysExEndSecondByte() { 255 UsbMidiPacketConverter usbMidiPacketConverter = new UsbMidiPacketConverter(); 256 usbMidiPacketConverter.createEncoders(1); 257 byte[] input = new byte[] { 258 (byte) 0xF0 /* SysEx Start */, 0x00, 0x01, 259 0x02, (byte) 0xF7 /* SysEx End */}; 260 byte[] expectedOutput = new byte[] { 261 0x04 /* Cable 0 Three Byte SysEx Start */, (byte) 0xF0, 0x00, 0x01, 262 0x06 /* Cable 0 Two Byte SysEx End */, 0x02, (byte) 0xF7, 0x00}; 263 usbMidiPacketConverter.encodeMidiPackets(input, input.length, 0); 264 byte[] output = usbMidiPacketConverter.pullEncodedMidiPackets(); 265 compareByteArrays(expectedOutput, output); 266 } 267 268 @Test testEncoderSysExEndThirdByte()269 public void testEncoderSysExEndThirdByte() { 270 UsbMidiPacketConverter usbMidiPacketConverter = new UsbMidiPacketConverter(); 271 usbMidiPacketConverter.createEncoders(1); 272 byte[] input = new byte[] { 273 (byte) 0xF0 /* SysEx Start */, 0x00, 0x01, 274 0x02, 0x03, (byte) 0xF7 /* SysEx End */}; 275 byte[] expectedOutput = new byte[] { 276 0x04 /* Cable 0 Three Byte SysEx Start */, (byte) 0xF0, 0x00, 0x01, 277 0x07 /* Cable 0 Three Byte SysEx End */, 0x02, 0x03, (byte) 0xF7}; 278 usbMidiPacketConverter.encodeMidiPackets(input, input.length, 0); 279 byte[] output = usbMidiPacketConverter.pullEncodedMidiPackets(); 280 compareByteArrays(expectedOutput, output); 281 } 282 283 @Test testEncoderSysExStartEnd()284 public void testEncoderSysExStartEnd() { 285 UsbMidiPacketConverter usbMidiPacketConverter = new UsbMidiPacketConverter(); 286 usbMidiPacketConverter.createEncoders(1); 287 byte[] input = new byte[] { 288 (byte) 0xF0 /* SysEx Start */, (byte) 0xF7 /* SysEx End */}; 289 byte[] expectedOutput = new byte[] { 290 0x06 /* Cable 0 Two Byte SysEx End */, (byte) 0xF0, (byte) 0xF7, 0x00}; 291 usbMidiPacketConverter.encodeMidiPackets(input, input.length, 0); 292 byte[] output = usbMidiPacketConverter.pullEncodedMidiPackets(); 293 compareByteArrays(expectedOutput, output); 294 } 295 296 @Test testEncoderSysExStartByteEnd()297 public void testEncoderSysExStartByteEnd() { 298 UsbMidiPacketConverter usbMidiPacketConverter = new UsbMidiPacketConverter(); 299 usbMidiPacketConverter.createEncoders(1); 300 byte[] input = new byte[] { 301 (byte) 0xF0 /* SysEx Start */, 0x44, (byte) 0xF7 /* SysEx End */}; 302 byte[] expectedOutput = new byte[] { 303 0x07 /* Cable 0 Three Byte SysEx End */, (byte) 0xF0, 0x44, (byte) 0xF7}; 304 usbMidiPacketConverter.encodeMidiPackets(input, input.length, 0); 305 byte[] output = usbMidiPacketConverter.pullEncodedMidiPackets(); 306 compareByteArrays(expectedOutput, output); 307 } 308 309 @Test testEncoderMultiplePulls()310 public void testEncoderMultiplePulls() { 311 UsbMidiPacketConverter usbMidiPacketConverter = new UsbMidiPacketConverter(); 312 usbMidiPacketConverter.createEncoders(1); 313 314 byte[] input = new byte[] { 315 (byte) 0xF0 /* SysEx Start */, 0x44, 0x55, 316 0x66, 0x77}; // 0x66 and 0x77 will not be pulled the first time 317 byte[] expectedOutput = new byte[] { 318 0x04 /* SysEx Start */, (byte) 0xF0, 0x44, 0x55}; 319 usbMidiPacketConverter.encodeMidiPackets(input, input.length, 0); 320 byte[] output = usbMidiPacketConverter.pullEncodedMidiPackets(); 321 compareByteArrays(expectedOutput, output); 322 323 input = new byte[] { 324 0x11, // Combined with 0x66 and 0x77 above 325 0x22, (byte) 0xF7 /* SysEx End */}; 326 expectedOutput = new byte[] { 327 0x04 /* Cable 0 SysEx Continue */, 0x66, 0x77, 0x11, 328 0x06 /* Cable 0 Two Byte SysEx End */, 0x22, (byte) 0xF7, 0x00}; 329 usbMidiPacketConverter.encodeMidiPackets(input, input.length, 0); 330 output = usbMidiPacketConverter.pullEncodedMidiPackets(); 331 compareByteArrays(expectedOutput, output); 332 333 input = new byte[] { 334 (byte) 0xF0 /* SysEx Start */, (byte) 0xF7 /* SysEx End */}; 335 expectedOutput = new byte[] { 336 0x06 /* Cable 0 Two Byte SysEx End */, (byte) 0xF0, (byte) 0xF7, 0x00}; 337 usbMidiPacketConverter.encodeMidiPackets(input, input.length, 0); 338 output = usbMidiPacketConverter.pullEncodedMidiPackets(); 339 compareByteArrays(expectedOutput, output); 340 } 341 342 @Test testEncoderDefaultToFirstCable()343 public void testEncoderDefaultToFirstCable() { 344 UsbMidiPacketConverter usbMidiPacketConverter = new UsbMidiPacketConverter(); 345 usbMidiPacketConverter.createEncoders(2); 346 byte[] input = new byte[] {(byte) 0x91 /* Note-On */, 0x22, 0x33}; 347 byte[] expectedOutput = new byte[] { 348 0x09 /* Cable 0 Note-On */, (byte) 0x91, 0x22, 0x33}; 349 usbMidiPacketConverter.encodeMidiPackets(input, input.length, 4); 350 byte[] output = usbMidiPacketConverter.pullEncodedMidiPackets(); 351 compareByteArrays(expectedOutput, output); 352 } 353 354 @Test testEncoderLargePacketDoesNotCrash()355 public void testEncoderLargePacketDoesNotCrash() { 356 for (long seed = 234; seed < 4000; seed += 666) { 357 Random rnd = new Random(seed); 358 UsbMidiPacketConverter usbMidiPacketConverter = new UsbMidiPacketConverter(); 359 usbMidiPacketConverter.createEncoders(4); 360 for (int cableNumber = 0; cableNumber < 4; cableNumber++) { 361 byte[] input = generateRandomByteStream(rnd, 1003 /* arbitrary large size */); 362 usbMidiPacketConverter.encodeMidiPackets(input, input.length, cableNumber); 363 } 364 usbMidiPacketConverter.pullEncodedMidiPackets(); 365 } 366 } 367 368 @Test testEncodeDecode()369 public void testEncodeDecode() { 370 final int bufferSize = 30; 371 final int numCables = 16; 372 final int bytesToEncodePerEncoding = 10; 373 byte[][] rawMidi = new byte[numCables][bufferSize]; 374 for (long seed = 45; seed < 3000; seed += 300) { 375 Random rnd = new Random(seed); 376 for (int cableNumber = 0; cableNumber < numCables; cableNumber++) { 377 rawMidi[cableNumber] = generateRandomByteStream(rnd, bufferSize); 378 379 // Change the last byte to SysEx End. 380 // This way the encoder is guaranteed to flush all packets. 381 rawMidi[cableNumber][bufferSize - 1] = (byte) 0xF7; 382 } 383 UsbMidiPacketConverter usbMidiPacketConverter = new UsbMidiPacketConverter(); 384 usbMidiPacketConverter.createEncoders(numCables); 385 // Encode packets and interweave them 386 for (int startByte = 0; startByte < bufferSize; 387 startByte += bytesToEncodePerEncoding) { 388 for (int cableNumber = 0; cableNumber < numCables; cableNumber++) { 389 byte[] bytesToEncode = Arrays.copyOfRange(rawMidi[cableNumber], startByte, 390 startByte + bytesToEncodePerEncoding); 391 usbMidiPacketConverter.encodeMidiPackets(bytesToEncode, bytesToEncode.length, 392 cableNumber); 393 } 394 } 395 byte[] usbMidi = usbMidiPacketConverter.pullEncodedMidiPackets(); 396 397 usbMidiPacketConverter.createDecoders(numCables); 398 399 // Now decode the MIDI packets to check if they are the same as the original 400 usbMidiPacketConverter.decodeMidiPackets(usbMidi, usbMidi.length); 401 for (int cableNumber = 0; cableNumber < numCables; cableNumber++) { 402 byte[] decodedRawMidi = usbMidiPacketConverter.pullDecodedMidiPackets(cableNumber); 403 compareByteArrays(rawMidi[cableNumber], decodedRawMidi); 404 } 405 } 406 } 407 } 408