a936a25198
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
142 lines
5.4 KiB
C++
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
|