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