0%

Midi框架.md

1. 开启Midi function

在Usb模式中切换为midi模式, 跟切换其他模式一样, 如切换mtp, ptp等
sys.usb.config = midi

收到底层的uevent, 在MSG_UPDATE_STATE中进行处理
updateUsbFunctions扫描/sys/class/android_usb/android0/f_midi/alsa下文件, 调用UsbAlsaManager的setPeripheralMidiState接口添加音频设备.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
        private void updateMidiFunction() {
boolean enabled = (mCurrentFunctions & UsbManager.FUNCTION_MIDI) != 0;
if (enabled != mMidiEnabled) {
// 打开midi function
if (enabled) {
Scanner scanner = null;
try {
// 扫描"/sys/class/android_usb/android0/f_midi/alsa"
scanner = new Scanner(new File(MIDI_ALSA_PATH));
// 打开midi模式后, 会生成alsa的声卡, 添加card device号
mMidiCard = scanner.nextInt();
mMidiDevice = scanner.nextInt();
}
finally {
if (scanner != null) {
scanner.close();
}
}
}
mMidiEnabled = enabled;
}
1. mUsbAlsaManager.setPeripheralMidiState(
mMidiEnabled && mConfigured, mMidiCard, mMidiDevice);
}


/* package */ void setPeripheralMidiState(boolean enabled, int card, int device) {
if (!mHasMidiFeature) {
return;
}

if (enabled && mPeripheralMidiDevice == null) {
Bundle properties = new Bundle();
Resources r = mContext.getResources();
properties.putString(MidiDeviceInfo.PROPERTY_NAME, r.getString(
com.android.internal.R.string.usb_midi_peripheral_name));
properties.putString(MidiDeviceInfo.PROPERTY_MANUFACTURER, r.getString(
com.android.internal.R.string.usb_midi_peripheral_manufacturer_name));
properties.putString(MidiDeviceInfo.PROPERTY_PRODUCT, r.getString(
com.android.internal.R.string.usb_midi_peripheral_product_name));
properties.putInt(MidiDeviceInfo.PROPERTY_ALSA_CARD, card);
properties.putInt(MidiDeviceInfo.PROPERTY_ALSA_DEVICE, device);
1.->// 创建UsbMidiDevice
mPeripheralMidiDevice = UsbMidiDevice.create(mContext, properties, card, device);
}
}

public static UsbMidiDevice create(Context context, Bundle properties, int card, int device) {
// FIXME - support devices with different number of input and output ports
int subDeviceCount = nativeGetSubdeviceCount(card, device);
1.1 // 构造UsbMidiDevice
UsbMidiDevice midiDevice = new UsbMidiDevice(card, device, subDeviceCount);
1.2 // 注册midi服务
if (!midiDevice.register(context, properties)) {
}
return midiDevice;
}

1.1->// 创建 InputReceiverProxy mInputPortReceivers 以
snprintf(path, sizeof(path), "/dev/snd/controlC%d", card);
// 下的端口inputPort状态创建, 通信端

private UsbMidiDevice(int card, int device, int subdeviceCount) {
mAlsaCard = card;
mAlsaDevice = device;
mSubdeviceCount = subdeviceCount;

// FIXME - support devices with different number of input and output ports
int inputPortCount = subdeviceCount;

mInputPortReceivers = new InputReceiverProxy[inputPortCount];
for (int port = 0; port < inputPortCount; port++) {
mInputPortReceivers[port] = new InputReceiverProxy();
}
}
1.2 ->// 启动MidiDeviceServer

private boolean register(Context context, Bundle properties) {
MidiManager midiManager = (MidiManager)context.getSystemService(Context.MIDI_SERVICE);
if (midiManager == null) {
Log.e(TAG, "No MidiManager in UsbMidiDevice.create()");
return false;
}
1.2.1 // 构造MidiDeviceServer
mServer = midiManager.createDeviceServer(mInputPortReceivers, mSubdeviceCount,
null, null, properties, MidiDeviceInfo.TYPE_USB, mCallback);
if (mServer == null) {
return false;
}

return true;
}


public MidiDeviceServer createDeviceServer(MidiReceiver[] inputPortReceivers,
int numOutputPorts, String[] inputPortNames, String[] outputPortNames,
Bundle properties, int type, MidiDeviceServer.Callback callback) {
try {
1.2.1.1 // 创建MidiDeviceServer
MidiDeviceServer server = new MidiDeviceServer(mService, inputPortReceivers,
numOutputPorts, callback);
1.2.1.2 // 调用MidiService的registerDeviceServer, add device
MidiDeviceInfo deviceInfo = mService.registerDeviceServer(server.getBinderInterface(),
inputPortReceivers.length, numOutputPorts, inputPortNames, outputPortNames,
properties, type);
if (deviceInfo == null) {
Log.e(TAG, "registerVirtualDevice failed");
return null;
}
return server;
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
}

1.2.1.1 -> //初始化MidiDeviceServer, callback对象为MidiDeviceServer.Callback, 回调其onDeviceStatusChanged方法

// Constructor for MidiManager.createDeviceServer()
/* package */ MidiDeviceServer(IMidiManager midiManager, MidiReceiver[] inputPortReceivers,
int numOutputPorts, Callback callback) {
mMidiManager = midiManager;
mInputPortReceivers = inputPortReceivers;
mInputPortCount = inputPortReceivers.length;
mOutputPortCount = numOutputPorts;
mCallback = callback;

// 注意 mInputPortReceivers mInputPortOutputPorts mOutputPortDispatchers 这三个成员, 后面会用到数据传输
mInputPortOutputPorts = new MidiOutputPort[mInputPortCount];

mOutputPortDispatchers = new MidiDispatcher[numOutputPorts];
for (int i = 0; i < numOutputPorts; i++) {
mOutputPortDispatchers[i] = new MidiDispatcher(mInputPortFailureHandler);
}

mInputPortOpen = new boolean[mInputPortCount];
mOutputPortOpenCount = new int[numOutputPorts];

mGuard.open("close");
}

1.2.1.2-> // 注册DeviceServer,

public MidiDeviceInfo registerDeviceServer(IMidiDeviceServer server, int numInputPorts,
int numOutputPorts, String[] inputPortNames, String[] outputPortNames,
Bundle properties, int type) {
// 权限检查, 非系统用户不能添加
int uid = Binder.getCallingUid();
if (type == MidiDeviceInfo.TYPE_USB && uid != Process.SYSTEM_UID) {
throw new SecurityException("only system can create USB devices");
} else if (type == MidiDeviceInfo.TYPE_BLUETOOTH && uid != mBluetoothServiceUid) {
throw new SecurityException("only MidiBluetoothService can create Bluetooth devices");
}

//创建MidiDeviceInfo, 并设置到MidiDeviceServer中, 最后将device保存到MidiService的mDevicesByInfo中.
synchronized (mDevicesByInfo) {
return addDeviceLocked(type, numInputPorts, numOutputPorts, inputPortNames,
outputPortNames, properties, server, null, false, uid);
}
}

上述步骤为切换USb function为midi后, 进行的初始化步骤, 其中最重要的是创建了UsbMidiDevice, 启动了UsbMidiServer服务.关联的驱动节点为
alsa架构的节点/dev/snd/controlC%d

2. App使用MidiService服务

先来看一段demo code
服务启动后, app需要通过mMidiManager.registerDeviceCallback注册回调.
通过MidiManager的getDevices获取当前的UsbMidiDevice设备.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
public MidiPortSelector(MidiManager midiManager, Activity activity,
int spinnerId, int type) {
//客户端为MidiManager, MidiManager的对端为MidiService, MidiService为一开机就加载的服务
mMidiManager = midiManager;
mActivity = activity;
mMidiManager.registerDeviceCallback(this,
new Handler(Looper.getMainLooper()));
// getDevice
MidiDeviceInfo[] infos = mMidiManager.getDevices();
for (MidiDeviceInfo info : infos) {
// 查到后, 保存到app的内部结构中, (此处关联到ui中)
onDeviceAdded(info);
}
}

public void onDeviceAdded(final MidiDeviceInfo info) {
int portCount = getInfoPortCount(info);
for (int i = 0; i < portCount; ++i) {
MidiPortWrapper wrapper = new MidiPortWrapper(info, mType, i);
mAdapter.add(wrapper);
}
}

通过registerDeviceCallback注册后, 在MidiService中保存, 这一步主要用到的数据成员都在1.2.1.2步骤中. 注册后, 会紧接着调用updateStickyDeviceStatus通知到客户端
onDeviceStatusChanged

1
2
3
4
5
6
7
8
public void registerDeviceCallback(DeviceCallback callback, Handler handler) {
DeviceListener deviceListener = new DeviceListener(callback, handler);
try {
mService.registerListener(mToken, deviceListener);
}
// callback最终保存到mDeviceListeners中
mDeviceListeners.put(callback, deviceListener);
}

app端的回调, 更新ui等

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
public void onDeviceStatusChanged(final MidiDeviceStatus status) {
// If an input port becomes busy then remove it from the menu.
// If it becomes free then add it back to the menu.
if (mType == MidiDeviceInfo.PortInfo.TYPE_INPUT) {
MidiDeviceInfo info = status.getDeviceInfo();
Log.i(MidiConstants.TAG, "MidiPortSelector.onDeviceStatusChanged status = " + status
+ ", mType = " + mType
+ ", activity = " + mActivity.getPackageName()
+ ", info = " + info);
// Look for transitions from free to busy.
int portCount = info.getInputPortCount();
for (int i = 0; i < portCount; ++i) {
MidiPortWrapper wrapper = new MidiPortWrapper(info, mType, i);
if (!wrapper.equals(mCurrentWrapper)) {
if (status.isInputPortOpen(i)) { // busy?
if (!mBusyPorts.contains(wrapper)) {
// UI相关的变化
// was free, now busy
mBusyPorts.add(wrapper);
mAdapter.remove(wrapper);
mAdapter.notifyDataSetChanged();
}
} else {
if (mBusyPorts.remove(wrapper)) {
// was busy, now free
mAdapter.add(wrapper);
mAdapter.notifyDataSetChanged();
}
}
}
}
}
}

3. app打开midi的端口

这个地方有个疑问, 关于mDestinationDeviceInfo是什么, input端口可以确认是手机打开midi function后创建的声卡对应的端口

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
    private void setupMidi() {
// Setup MIDI
mMidiManager = (MidiManager) getSystemService(MIDI_SERVICE);

MidiDeviceInfo synthInfo = MidiTools.findDevice(mMidiManager, "AndroidTest",
"SynthExample");
int portIndex = 0;
// synthInfo作为mDestinationDeviceInfo
mPortSelector = new MidiOutputPortConnectionSelector(mMidiManager, this,
R.id.spinner_synth_sender, synthInfo, portIndex);
mPortSelector.setConnectedListener(new MyPortsConnectedListener());
}


@Override
public void onPortSelected(final MidiPortWrapper wrapper) {
close();
final MidiDeviceInfo info = wrapper.getDeviceInfo();
if (info != null) {
// 先调用openDevice 打开注册的端口, 打开后回调listener onDeviceOpened
// 在MidiService中添加DeviceConnection连接, 表示连接了client和device,保存到mDeviceConnections中
// 如果服务没有启动, device没有关联的UsbMidiServer, 需要先bindservice, 再关联到server上.
mMidiManager.openDevice(info, new MidiManager.OnDeviceOpenedListener() {
@Override
public void onDeviceOpened(MidiDevice device) {
if (device == null) {
Log.e(MidiConstants.TAG, "could not open " + info);
} else {
mOpenDevice = device;
3. // 打开端口, 这个地方才真正和设备的端口交互, 需要用到device关联的MidiDeviceServer
mInputPort = mOpenDevice.openInputPort(
wrapper.getPortIndex());
if (mInputPort == null) {
Log.e(MidiConstants.TAG, "could not open input port on " + info);
}
}
}
}, null);
// Don't run the callback on the UI thread because openInputPort might take a while.
}
}

4. MidiDeviceServer打开端口

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
3.-->// 打开端口
private final IMidiDeviceServer mServer = new IMidiDeviceServer.Stub() {

@Override
public FileDescriptor openInputPort(IBinder token, int portNumber) {
if (mDeviceInfo.isPrivate()) {
if (Binder.getCallingUid() != Process.myUid()) {
throw new SecurityException("Can't access private device from different UID");
}
}

if (portNumber < 0 || portNumber >= mInputPortCount) {
Log.e(TAG, "portNumber out of range in openInputPort: " + portNumber);
return null;
}

synchronized (mInputPortOutputPorts) {
if (mInputPortOutputPorts[portNumber] != null) {
Log.d(TAG, "port " + portNumber + " already open");
return null;
}

try {
FileDescriptor[] pair = createSeqPacketSocketPair();
MidiOutputPort outputPort = new MidiOutputPort(pair[0], portNumber);
mInputPortOutputPorts[portNumber] = outputPort;
// 加到MidiDispatcher.mReceivers中, 通过receiver处理数据
outputPort.connect(mInputPortReceivers[portNumber]);

InputPortClient client = new InputPortClient(token, outputPort);
synchronized (mPortClients) {
mPortClients.put(token, client);
}
// portOpen标记置位
mInputPortOpen[portNumber] = true;
3.1 // 更新所有server绑定的device状态
updateDeviceStatus();
return pair[1];
} catch (IOException e) {
Log.e(TAG, "unable to create FileDescriptors in openInputPort");
return null;
}
}
}

3.1-> // 更新所有server绑定的device状态
private void updateDeviceStatus() {
// clear calling identity, since we may be in a Binder call from one of our clients
long identityToken = Binder.clearCallingIdentity();

MidiDeviceStatus status = new MidiDeviceStatus(mDeviceInfo, mInputPortOpen,
mOutputPortOpenCount);
if (mCallback != null) {
// 在UsbMidiDevice中
3.2 mCallback.onDeviceStatusChanged(this, status);
}
try {
mMidiManager.setDeviceStatus(mServer, status);
} finally {
Binder.restoreCallingIdentity(identityToken);
}
}


3.2->// 需要关注下onDeviceStatusChanged,

private final MidiDeviceServer.Callback mCallback = new MidiDeviceServer.Callback() {

@Override
public void onDeviceStatusChanged(MidiDeviceServer server, MidiDeviceStatus status) {
MidiDeviceInfo deviceInfo = status.getDeviceInfo();
int inputPorts = deviceInfo.getInputPortCount();
int outputPorts = deviceInfo.getOutputPortCount();
boolean hasOpenPorts = false;

for (int i = 0; i < inputPorts; i++) {
// 已经在 3.1步骤前标记置为true了
if (status.isInputPortOpen(i)) {
hasOpenPorts = true;
break;
}
}

if (!hasOpenPorts) {
for (int i = 0; i < outputPorts; i++) {
if (status.getOutputPortOpenCount(i) > 0) {
hasOpenPorts = true;
break;
}
}
}

synchronized (mLock) {
if (hasOpenPorts && !mIsOpen) {
3.4 // 开port
openLocked();
} else if (!hasOpenPorts && mIsOpen) {
closeLocked();
}
}
}
};

5. 开启port

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
private boolean openLocked() {
// FIXME - support devices with different number of input and output ports
FileDescriptor[] fileDescriptors = nativeOpen(mAlsaCard, mAlsaDevice, mSubdeviceCount);
if (fileDescriptors == null) {
Log.e(TAG, "nativeOpen failed");
return false;
}

mFileDescriptors = fileDescriptors;
int inputStreamCount = fileDescriptors.length;
// last file descriptor returned from nativeOpen() is only used for unblocking Os.poll()
// in our input thread
int outputStreamCount = fileDescriptors.length - 1;

mPollFDs = new StructPollfd[inputStreamCount];
mInputStreams = new FileInputStream[inputStreamCount];
for (int i = 0; i < inputStreamCount; i++) {
FileDescriptor fd = fileDescriptors[i];
StructPollfd pollfd = new StructPollfd();
pollfd.fd = fd;
pollfd.events = (short)OsConstants.POLLIN;
mPollFDs[i] = pollfd;
mInputStreams[i] = new FileInputStream(fd);
}

mOutputStreams = new FileOutputStream[outputStreamCount];
mEventSchedulers = new MidiEventScheduler[outputStreamCount];
for (int i = 0; i < outputStreamCount; i++) {
mOutputStreams[i] = new FileOutputStream(fileDescriptors[i]);

MidiEventScheduler scheduler = new MidiEventScheduler();
mEventSchedulers[i] = scheduler;
mInputPortReceivers[i].setReceiver(scheduler.getReceiver());
}

final MidiReceiver[] outputReceivers = mServer.getOutputPortReceivers();

// Create input thread which will read from all output ports of the physical device
new Thread("UsbMidiDevice input thread") {
@Override
public void run() {
byte[] buffer = new byte[BUFFER_SIZE];
try {
while (true) {
// Record time of event immediately after waking.
long timestamp = System.nanoTime();
synchronized (mLock) {
if (!mIsOpen) break;

// look for a readable FileDescriptor
for (int index = 0; index < mPollFDs.length; index++) {
StructPollfd pfd = mPollFDs[index];
if ((pfd.revents & (OsConstants.POLLERR
| OsConstants.POLLHUP)) != 0) {
break;
} else if ((pfd.revents & OsConstants.POLLIN) != 0) {
// clear readable flag
pfd.revents = 0;

if (index == mInputStreams.length - 1) {
// last file descriptor is used only for unblocking Os.poll()
break;
}

int count = mInputStreams[index].read(buffer);
outputReceivers[index].send(buffer, 0, count, timestamp);
}
}
}

// wait until we have a readable port or we are signalled to close
Os.poll(mPollFDs, -1 /* infinite timeout */);
}
} catch (IOException e) {
Log.d(TAG, "reader thread exiting");
} catch (ErrnoException e) {
Log.d(TAG, "reader thread exiting");
}
Log.d(TAG, "input thread exit");
}
}.start();

// Create output thread for each input port of the physical device
for (int port = 0; port < outputStreamCount; port++) {
final MidiEventScheduler eventSchedulerF = mEventSchedulers[port];
final FileOutputStream outputStreamF = mOutputStreams[port];
final int portF = port;

new Thread("UsbMidiDevice output thread " + port) {
@Override
public void run() {
while (true) {
MidiEvent event;
try {
event = (MidiEvent)eventSchedulerF.waitNextEvent();
} catch (InterruptedException e) {
// try again
continue;
}
if (event == null) {
break;
}
try {
outputStreamF.write(event.data, 0, event.count);
} catch (IOException e) {
Log.e(TAG, "write failed for port " + portF);
}
eventSchedulerF.addEventToPool(event);
}
Log.d(TAG, "output thread exit");
}
}.start();
}

mIsOpen = true;
return true;
}

6. 图表类的关系图

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
@startuml
abstract class MidiReceiver {
+ void send(byte[] msg, int offset, int count)
+ void onSend(byte[] msg, int offset, int count, long timestamp)
}
abstract class MidiSender{
+ void onConnect(MidiReceiver receiver)
+ void onDisconnect(MidiReceiver receiver)
}

MidiSender --o MidiDispatcher
class MidiDispatcher extends MidiReceiver {
MidiSender mSender
MidiReceiverFailureHandler mFailureHandler
List<MidiReceiver> <b>mReceivers
+ MidiSender getSender()
+ void <b>onSend(byte[] msg, int offset, int count, long timestamp)
+int getReceiverCount()
}
MidiReceiver --* MidiDispatcher
MidiDispatcher --o MidiOutputPort


class MidiOutputPort <<<i><u>receiving data from a port on a MIDI device>> {
MidiDispatcher mDispatcher
+ void <b>onConnect(MidiReceiver receiver)
+ void onDisconnect(MidiReceiver receiver)
close()
}
MidiOutputPort -u|> MidiSender
MidiReceiverFailureHandler -d-o MidiDispatcher
interface MidiReceiverFailureHandler{
void onReceiverFailure(MidiReceiver receiver, IOException failure)
}


class MidiInputPort <<<i><u>for sending data to a port on a MIDI device>> {
FileDescriptor mFileDescriptor
+void onSend(byte[] msg, int offset, int count, long timestamp)
+void close()
}

MidiInputPort -|> MidiReceiver
abstract class PortClient {
void close()
}

class InputPortClient extends PortClient
class OutputPortClient extends PortClient {
MidiInputPort mInputPort
void close()
MidiInputPort getInputPort()
}

MidiInputPort -d-o OutputPortClient

class MidiDevice {
IMidiDeviceServer mDeviceServer
+MidiInputPort openInputPort(int portNumber)
+MidiOutputPort openOutputPort(int portNumber)
}



class MidiDeviceService extends service {
MidiDeviceServer mServer
<b>onBind();
}
MidiDeviceServer--o MidiDeviceService
class MidiDeviceServer {
IMidiDeviceServer mServer
MidiReceiver[] mInputPortReceivers
MidiDispatcher[] mOutputPortDispatchers
MidiOutputPort[] mInputPortOutputPorts
}
class MidiService {
MidiDeviceInfo[] getDevices
openDevice(IBinder token, MidiDeviceInfo deviceInfo,
IMidiDeviceOpenCallback callback)
notifyDeviceStatusChanged(Device device, MidiDeviceStatus status)
}


MidiService --* MidiManager
MidiService --* MidiDeviceService
class MidiManager {
penDevice(MidiDeviceInfo deviceInfo, OnDeviceOpenedListener listener,)
createDeviceServer(MidiReceiver[] inputPortReceivers,...)
}

MidiInputPort --o MidiDevice
MidiOutputPort --o MidiDevice
@enduml

调用关系:
在app端选择端口后, 会进行openDevice, MidiService会和MidiDeviceService进行bindService, bindService返回的binder对象为MidiDeviceService中实例化的MidiDeviceServer对象.
openDevice操作会导致bindService产生, 找到真正的管理类MidiDeviceServer对象. 该server对象会通过MidiDevice设备绑定.
openDevice成功后, 一般会调用MidiDevice的openInputPort打开相关的device端口.绑定类MidiInputPort, 关联的fd为MidiOutputPort的对端pair fd. 实际通信过程是MidiOutputPort通过connect过程与mInputPortReceivers进行直接通信的.关注MidiOutputPort的onConnect, 又经过类一层代理(mDispatcher),
MidiOutputPort类循环接收MidiInputPort的对端(app)发送过来的消息, 又将消息转发给其连接的所有mInputPortReceivers. 为 InputReceiverProxy 类.
这个类还是个代理类, 真实对象是通过类的setReceiver设置进去的,
在打开端口时, 端口状态变化导致回调onDeviceStatusChanged方法, 进而调用到openLocked函数. 而设置进去的receiver实际上是MidiEventScheduler的receiver.
MidiEventScheduler关联到”UsbMidiDevice output thread”线程上, 该线程在读取MidiEventScheduler上来的消息,再回写给device设备.
因此app端可以操作pair[1] fd, 经过层层消息转发, 最终写到物理device设备上(对应/dev/snd…设备).

而”UsbMidiDevice input thread”线程在持续读取真实设备上的消息, 转发给outputReceivers, 该receiver实际是MidiDeviceServer的mOutputPortDispatchers (MidiDispatcher)对象
该对象也是一个代理, 消息是通过其内部的receiver转发的, 是通过connect过程设置的receiver.

1
2
3
4
5
MidiInputPort inputPort = new MidiInputPort(pair[0], portNumber);
MidiDispatcher dispatcher = mOutputPortDispatchers[portNumber];
synchronized (dispatcher) {
dispatcher.getSender().connect(inputPort);
}

这个直接关联到pair[0], 对应的pair[1]为app持有, 向该fd发送消息会转发到pair[1]上.即转发给app. app通过openInputPort拿到的fd负责读写, 转发给真实的device设备上.

上述通信过程是基于openInputPort过程分析的, 不进行connectPort, 还有一个connectPort过程, 该过程允许指定特例化的UsbMidiService.
可以参考MidiSynthDeviceService 服务
该通信过程读取真实device设备上的信息, 往回传给app的过程跟上面是一致的.
不同的地方是app写数据, 把数据发给底层的过程.

1
2
3
4
@Override
public MidiReceiver[] onGetInputPortReceivers() {
return new MidiReceiver[]{mSynthEngine};
}

关键点在上面的函数, 返回了MidiReceiver的子类(SynthEngine)
在构造MidiDeviceServer时, 传入了inputPortReceivers参数, 在openInputPort时, 会传入onGetInputPortReceivers的结果