Robert Wu a936a25198 Add MIDI 2.0 host mode support
Adding MIDI 2.0 host mode support to Android. MIDI 2.0 peripherals
create an alternative setting for Universal Midi Packets (UMP).
UMP packets can be passed directly through as the host has full
control. Instead of going through the ALSA, UMP packets can be
sent directly through a UsbDeviceConnection.

This CL also adds public apis to expose whether the pipe supports
UMP. This for MidiDeviceInfo in Java and AMidi in c++.

Bug: 201003646
Bug: 214447324
Test: Verified that the MIDI 1.0 path still works as before.
Test: Tested that MIDI 2.0 packets are passed through the pipe
with MIDIScope and MIDIKeyboard
Test: atest MidiSoloTest
Test: atest CtsMidiTestCases
Test: NativeMidiEchoTest

Change-Id: Idff8a3b9bdd05857239260fc3e6af7253f8f992f
2022-01-20 00:50:41 +00:00

142 lines
5.4 KiB
C++

/*
* Copyright (C) 2016 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#define LOG_TAG "MidiDeviceInfo"
#include <MidiDeviceInfo.h>
#include <binder/Parcel.h>
#include <log/log.h>
#include <utils/Errors.h>
#include <utils/String16.h>
namespace android {
namespace media {
namespace midi {
// The constant values need to be kept in sync with MidiDeviceInfo.java.
// static
const char* const MidiDeviceInfo::PROPERTY_NAME = "name";
const char* const MidiDeviceInfo::PROPERTY_MANUFACTURER = "manufacturer";
const char* const MidiDeviceInfo::PROPERTY_PRODUCT = "product";
const char* const MidiDeviceInfo::PROPERTY_VERSION = "version";
const char* const MidiDeviceInfo::PROPERTY_SERIAL_NUMBER = "serial_number";
const char* const MidiDeviceInfo::PROPERTY_ALSA_CARD = "alsa_card";
const char* const MidiDeviceInfo::PROPERTY_ALSA_DEVICE = "alsa_device";
String16 MidiDeviceInfo::getProperty(const char* propertyName) {
String16 value;
if (mProperties.getString(String16(propertyName), &value)) {
return value;
} else {
return String16();
}
}
#define RETURN_IF_FAILED(calledOnce) \
{ \
status_t returnStatus = calledOnce; \
if (returnStatus) { \
ALOGE("Failed at %s:%d (%s)", __FILE__, __LINE__, __func__); \
return returnStatus; \
} \
}
status_t MidiDeviceInfo::writeToParcel(Parcel* parcel) const {
// Needs to be kept in sync with code in MidiDeviceInfo.java
RETURN_IF_FAILED(parcel->writeInt32(mType));
RETURN_IF_FAILED(parcel->writeInt32(mId));
RETURN_IF_FAILED(parcel->writeInt32((int32_t)mInputPortNames.size()));
RETURN_IF_FAILED(parcel->writeInt32((int32_t)mOutputPortNames.size()));
RETURN_IF_FAILED(writeStringVector(parcel, mInputPortNames));
RETURN_IF_FAILED(writeStringVector(parcel, mOutputPortNames));
RETURN_IF_FAILED(parcel->writeInt32(mIsPrivate ? 1 : 0));
RETURN_IF_FAILED(parcel->writeInt32(mDefaultProtocol));
RETURN_IF_FAILED(mProperties.writeToParcel(parcel));
// This corresponds to "extra" properties written by Java code
RETURN_IF_FAILED(mProperties.writeToParcel(parcel));
return OK;
}
status_t MidiDeviceInfo::readFromParcel(const Parcel* parcel) {
// Needs to be kept in sync with code in MidiDeviceInfo.java
RETURN_IF_FAILED(parcel->readInt32(&mType));
RETURN_IF_FAILED(parcel->readInt32(&mId));
int32_t inputPortCount;
RETURN_IF_FAILED(parcel->readInt32(&inputPortCount));
int32_t outputPortCount;
RETURN_IF_FAILED(parcel->readInt32(&outputPortCount));
RETURN_IF_FAILED(readStringVector(parcel, &mInputPortNames, inputPortCount));
RETURN_IF_FAILED(readStringVector(parcel, &mOutputPortNames, outputPortCount));
int32_t isPrivate;
RETURN_IF_FAILED(parcel->readInt32(&isPrivate));
mIsPrivate = isPrivate == 1;
RETURN_IF_FAILED(parcel->readInt32(&mDefaultProtocol));
RETURN_IF_FAILED(mProperties.readFromParcel(parcel));
// Ignore "extra" properties as they may contain Java Parcelables
return OK;
}
status_t MidiDeviceInfo::readStringVector(
const Parcel* parcel, Vector<String16> *vectorPtr, size_t defaultLength) {
std::optional<std::vector<std::optional<String16>>> v;
status_t result = parcel->readString16Vector(&v);
if (result != OK) return result;
vectorPtr->clear();
if (v) {
for (const auto& iter : *v) {
if (iter) {
vectorPtr->push_back(*iter);
} else {
vectorPtr->push_back(String16());
}
}
} else {
vectorPtr->resize(defaultLength);
}
return OK;
}
status_t MidiDeviceInfo::writeStringVector(Parcel* parcel, const Vector<String16>& vector) const {
std::vector<String16> v;
for (size_t i = 0; i < vector.size(); ++i) {
v.push_back(vector[i]);
}
return parcel->writeString16Vector(v);
}
// Vector does not define operator==
static inline bool areVectorsEqual(const Vector<String16>& lhs, const Vector<String16>& rhs) {
if (lhs.size() != rhs.size()) return false;
for (size_t i = 0; i < lhs.size(); ++i) {
if (lhs[i] != rhs[i]) return false;
}
return true;
}
bool operator==(const MidiDeviceInfo& lhs, const MidiDeviceInfo& rhs) {
return (lhs.mType == rhs.mType && lhs.mId == rhs.mId &&
areVectorsEqual(lhs.mInputPortNames, rhs.mInputPortNames) &&
areVectorsEqual(lhs.mOutputPortNames, rhs.mOutputPortNames) &&
lhs.mProperties == rhs.mProperties &&
lhs.mIsPrivate == rhs.mIsPrivate &&
lhs.mDefaultProtocol == rhs.mDefaultProtocol);
}
} // namespace midi
} // namespace media
} // namespace android