1 /* 2 * Copyright (C) 2018 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.midi; 18 19 import android.content.Context; 20 import android.content.Intent; 21 import android.media.midi.MidiDeviceInfo; 22 import android.media.midi.MidiDeviceService; 23 import android.media.midi.MidiDeviceStatus; 24 import android.media.midi.MidiManager; 25 import android.media.midi.MidiReceiver; 26 import android.os.Bundle; 27 import android.os.IBinder; 28 import android.util.Log; 29 30 import java.io.IOException; 31 import java.util.Collection; 32 33 /** 34 * Virtual MIDI Device that copies its input to its output. 35 * This is used for loop-back testing of MIDI I/O. 36 * 37 * Note: The application's AndroidManifest.xml should contain the following in 38 * its <application> section. 39 * 40 <service android:name="MidiEchoTestService" 41 android:permission="android.permission.BIND_MIDI_DEVICE_SERVICE"> 42 <intent-filter> 43 <action android:name="android.media.midi.MidiDeviceService" /> 44 </intent-filter> 45 <meta-data android:name="android.media.midi.MidiDeviceService" 46 android:resource="@xml/echo_device_info" /> 47 </service> 48 49 * also it must provide an xml reource file "echo_device_info.xml" containing: 50 <devices> 51 <device manufacturer="AndroidCTS" product="MidiEcho" tags="echo,test"> 52 <input-port name="input" /> 53 <output-port name="output" /> 54 </device> 55 </devices> 56 */ 57 58 public class MidiEchoTestService extends MidiDeviceService { 59 private static final String TAG = "MidiEchoTestService"; 60 private static final boolean DEBUG = false; 61 62 // Other apps will write to this port. 63 private MidiReceiver mInputReceiver = new MyReceiver(); 64 // This app will copy the data to this port. 65 private MidiReceiver mOutputReceiver; 66 private static MidiEchoTestService sInstance; 67 68 // These are public so we can easily read them from CTS test. 69 public int statusChangeCount; 70 public boolean inputOpened; 71 public int outputOpenCount; 72 73 public static final String TEST_MANUFACTURER = "AndroidCTS"; 74 public static final String ECHO_PRODUCT = "MidiEcho"; 75 76 /** 77 * Search through the available devices for the ECHO loop-back device. 78 */ findEchoDevice(Context context)79 public static MidiDeviceInfo findEchoDevice(Context context) { 80 MidiManager midiManager = 81 (MidiManager) context.getSystemService(Context.MIDI_SERVICE); 82 Collection<MidiDeviceInfo> infos = midiManager.getDevicesForTransport( 83 MidiManager.TRANSPORT_MIDI_BYTE_STREAM); 84 MidiDeviceInfo echoInfo = null; 85 for (MidiDeviceInfo info : infos) { 86 Bundle properties = info.getProperties(); 87 String manufacturer = properties.getString( 88 MidiDeviceInfo.PROPERTY_MANUFACTURER); 89 90 if (TEST_MANUFACTURER.equals(manufacturer)) { 91 String product = properties.getString( 92 MidiDeviceInfo.PROPERTY_PRODUCT); 93 if (ECHO_PRODUCT.equals(product)) { 94 echoInfo = info; 95 break; 96 } 97 } 98 } 99 if (DEBUG) { 100 Log.i(TAG, "MidiEchoService for " + ECHO_PRODUCT + ": " + echoInfo); 101 } 102 return echoInfo; 103 } 104 105 /** 106 * @return A textual name for this echo service. 107 */ getEchoServerName()108 public static String getEchoServerName() { 109 return ECHO_PRODUCT; 110 } 111 112 @Override onCreate()113 public void onCreate() { 114 super.onCreate(); 115 if (DEBUG) { 116 Log.i(TAG, "#### onCreate()"); 117 } 118 sInstance = this; 119 } 120 121 @Override onDestroy()122 public void onDestroy() { 123 super.onDestroy(); 124 if (DEBUG) { 125 Log.i(TAG, "#### onDestroy()"); 126 } 127 } 128 129 @Override onStartCommand(Intent intent, int flags, int startId)130 public int onStartCommand(Intent intent, int flags, int startId) { 131 if (DEBUG) { 132 Log.i(TAG, "#### onStartCommand()"); 133 } 134 return super.onStartCommand(intent, flags, startId); 135 } 136 137 @Override onBind(Intent intent)138 public IBinder onBind(Intent intent) { 139 if (DEBUG) { 140 Log.i(TAG, "#### onBind()"); 141 } 142 return super.onBind(intent); 143 } 144 145 // For CTS testing, so I can read test fields. getInstance()146 public static MidiEchoTestService getInstance() { 147 return sInstance; 148 } 149 150 @Override onGetInputPortReceivers()151 public MidiReceiver[] onGetInputPortReceivers() { 152 return new MidiReceiver[] { mInputReceiver }; 153 } 154 155 class MyReceiver extends MidiReceiver { 156 @Override onSend(byte[] data, int offset, int count, long timestamp)157 public void onSend(byte[] data, int offset, int count, long timestamp) 158 throws IOException { 159 if (mOutputReceiver == null) { 160 mOutputReceiver = getOutputPortReceivers()[0]; 161 } 162 // Copy input to output. 163 mOutputReceiver.send(data, offset, count, timestamp); 164 } 165 } 166 167 @Override onDeviceStatusChanged(MidiDeviceStatus status)168 public void onDeviceStatusChanged(MidiDeviceStatus status) { 169 statusChangeCount++; 170 inputOpened = status.isInputPortOpen(0); 171 outputOpenCount = status.getOutputPortOpenCount(0); 172 } 173 } 174