Merge "MIDI: Add MidiUmpDeviceService" into main
This commit is contained in:
@@ -26391,6 +26391,17 @@ package android.media.midi {
|
||||
method public abstract void onDisconnect(android.media.midi.MidiReceiver);
|
||||
}
|
||||
|
||||
public abstract class MidiUmpDeviceService extends android.app.Service {
|
||||
ctor public MidiUmpDeviceService();
|
||||
method @Nullable public final android.media.midi.MidiDeviceInfo getDeviceInfo();
|
||||
method @NonNull public final java.util.List<android.media.midi.MidiReceiver> getOutputPortReceivers();
|
||||
method @Nullable public android.os.IBinder onBind(@NonNull android.content.Intent);
|
||||
method public void onClose();
|
||||
method public void onDeviceStatusChanged(@Nullable android.media.midi.MidiDeviceStatus);
|
||||
method @NonNull public abstract java.util.List<android.media.midi.MidiReceiver> onGetInputPortReceivers();
|
||||
field public static final String SERVICE_INTERFACE = "android.media.midi.MidiUmpDeviceService";
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
package android.media.projection {
|
||||
|
||||
@@ -25,6 +25,7 @@ import android.system.Os;
|
||||
import android.system.OsConstants;
|
||||
import android.util.Log;
|
||||
|
||||
import com.android.internal.annotations.GuardedBy;
|
||||
import com.android.internal.midi.MidiDispatcher;
|
||||
|
||||
import dalvik.system.CloseGuard;
|
||||
@@ -34,6 +35,7 @@ import libcore.io.IoUtils;
|
||||
import java.io.Closeable;
|
||||
import java.io.FileDescriptor;
|
||||
import java.io.IOException;
|
||||
import java.util.Arrays;
|
||||
import java.util.HashMap;
|
||||
import java.util.concurrent.CopyOnWriteArrayList;
|
||||
import java.util.concurrent.atomic.AtomicInteger;
|
||||
@@ -83,6 +85,14 @@ public final class MidiDeviceServer implements Closeable {
|
||||
private AtomicInteger mTotalInputBytes = new AtomicInteger();
|
||||
private AtomicInteger mTotalOutputBytes = new AtomicInteger();
|
||||
|
||||
private static final int UNUSED_UID = -1;
|
||||
|
||||
private final Object mUmpUidLock = new Object();
|
||||
@GuardedBy("mUmpUidLock")
|
||||
private final int[] mUmpInputPortUids;
|
||||
@GuardedBy("mUmpUidLock")
|
||||
private final int[] mUmpOutputPortUids;
|
||||
|
||||
public interface Callback {
|
||||
/**
|
||||
* Called to notify when an our device status has changed
|
||||
@@ -137,6 +147,9 @@ public final class MidiDeviceServer implements Closeable {
|
||||
int portNumber = mOutputPort.getPortNumber();
|
||||
mInputPortOutputPorts[portNumber] = null;
|
||||
mInputPortOpen[portNumber] = false;
|
||||
synchronized (mUmpUidLock) {
|
||||
mUmpInputPortUids[portNumber] = UNUSED_UID;
|
||||
}
|
||||
mTotalOutputBytes.addAndGet(mOutputPort.pullTotalBytesCount());
|
||||
updateTotalBytes();
|
||||
updateDeviceStatus();
|
||||
@@ -162,6 +175,9 @@ public final class MidiDeviceServer implements Closeable {
|
||||
dispatcher.getSender().disconnect(mInputPort);
|
||||
int openCount = dispatcher.getReceiverCount();
|
||||
mOutputPortOpenCount[portNumber] = openCount;
|
||||
synchronized (mUmpUidLock) {
|
||||
mUmpOutputPortUids[portNumber] = UNUSED_UID;
|
||||
}
|
||||
mTotalInputBytes.addAndGet(mInputPort.pullTotalBytesCount());
|
||||
updateTotalBytes();
|
||||
updateDeviceStatus();
|
||||
@@ -210,6 +226,25 @@ public final class MidiDeviceServer implements Closeable {
|
||||
return null;
|
||||
}
|
||||
|
||||
if (isUmpDevice()) {
|
||||
if (portNumber >= mOutputPortCount) {
|
||||
Log.e(TAG, "out portNumber out of range in openInputPort: " + portNumber);
|
||||
return null;
|
||||
}
|
||||
synchronized (mUmpUidLock) {
|
||||
if (mUmpInputPortUids[portNumber] != UNUSED_UID) {
|
||||
Log.e(TAG, "input port already open in openInputPort: " + portNumber);
|
||||
return null;
|
||||
}
|
||||
if ((mUmpOutputPortUids[portNumber] != UNUSED_UID)
|
||||
&& (Binder.getCallingUid() != mUmpOutputPortUids[portNumber])) {
|
||||
Log.e(TAG, "different uid for output in openInputPort: " + portNumber);
|
||||
return null;
|
||||
}
|
||||
mUmpInputPortUids[portNumber] = Binder.getCallingUid();
|
||||
}
|
||||
}
|
||||
|
||||
try {
|
||||
FileDescriptor[] pair = createSeqPacketSocketPair();
|
||||
MidiOutputPort outputPort = new MidiOutputPort(pair[0], portNumber);
|
||||
@@ -242,6 +277,25 @@ public final class MidiDeviceServer implements Closeable {
|
||||
return null;
|
||||
}
|
||||
|
||||
if (isUmpDevice()) {
|
||||
if (portNumber >= mInputPortCount) {
|
||||
Log.e(TAG, "in portNumber out of range in openOutputPort: " + portNumber);
|
||||
return null;
|
||||
}
|
||||
synchronized (mUmpUidLock) {
|
||||
if (mUmpOutputPortUids[portNumber] != UNUSED_UID) {
|
||||
Log.e(TAG, "output port already open in openOutputPort: " + portNumber);
|
||||
return null;
|
||||
}
|
||||
if ((mUmpInputPortUids[portNumber] != UNUSED_UID)
|
||||
&& (Binder.getCallingUid() != mUmpInputPortUids[portNumber])) {
|
||||
Log.e(TAG, "different uid for input in openOutputPort: " + portNumber);
|
||||
return null;
|
||||
}
|
||||
mUmpOutputPortUids[portNumber] = Binder.getCallingUid();
|
||||
}
|
||||
}
|
||||
|
||||
try {
|
||||
FileDescriptor[] pair = createSeqPacketSocketPair();
|
||||
MidiInputPort inputPort = new MidiInputPort(pair[0], portNumber);
|
||||
@@ -358,6 +412,13 @@ public final class MidiDeviceServer implements Closeable {
|
||||
mInputPortOpen = new boolean[mInputPortCount];
|
||||
mOutputPortOpenCount = new int[numOutputPorts];
|
||||
|
||||
synchronized (mUmpUidLock) {
|
||||
mUmpInputPortUids = new int[mInputPortCount];
|
||||
mUmpOutputPortUids = new int[mOutputPortCount];
|
||||
Arrays.fill(mUmpInputPortUids, UNUSED_UID);
|
||||
Arrays.fill(mUmpOutputPortUids, UNUSED_UID);
|
||||
}
|
||||
|
||||
mGuard.open("close");
|
||||
}
|
||||
|
||||
@@ -467,4 +528,8 @@ public final class MidiDeviceServer implements Closeable {
|
||||
Log.e(TAG, "RemoteException in updateTotalBytes");
|
||||
}
|
||||
}
|
||||
|
||||
private boolean isUmpDevice() {
|
||||
return mDeviceInfo.getDefaultProtocol() != MidiDeviceInfo.PROTOCOL_UNKNOWN;
|
||||
}
|
||||
}
|
||||
|
||||
161
media/java/android/media/midi/MidiUmpDeviceService.java
Normal file
161
media/java/android/media/midi/MidiUmpDeviceService.java
Normal file
@@ -0,0 +1,161 @@
|
||||
/*
|
||||
* Copyright (C) 2023 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.
|
||||
*/
|
||||
|
||||
package android.media.midi;
|
||||
|
||||
import android.annotation.NonNull;
|
||||
import android.annotation.Nullable;
|
||||
import android.app.Service;
|
||||
import android.content.Context;
|
||||
import android.content.Intent;
|
||||
import android.os.IBinder;
|
||||
import android.os.RemoteException;
|
||||
import android.os.ServiceManager;
|
||||
import android.util.Log;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* A service that implements a virtual MIDI device for Universal MIDI Packets (UMP).
|
||||
* Subclasses must implement the {@link #onGetInputPortReceivers} method to provide a
|
||||
* list of {@link MidiReceiver}s to receive data sent to the device's input ports.
|
||||
* Similarly, subclasses can call {@link #getOutputPortReceivers} to fetch a list
|
||||
* of {@link MidiReceiver}s for sending data out the output ports.
|
||||
*
|
||||
* Unlike traditional MIDI byte streams, only complete UMPs should be sent.
|
||||
* Unlike with {@link #MidiDeviceService}, the number of input and output ports must be equal.
|
||||
*
|
||||
* <p>To extend this class, you must declare the service in your manifest file with
|
||||
* an intent filter with the {@link #SERVICE_INTERFACE} action
|
||||
* and meta-data to describe the virtual device.
|
||||
* For example:</p>
|
||||
* <pre>
|
||||
* <service android:name=".VirtualDeviceService"
|
||||
* android:label="@string/service_name">
|
||||
* <intent-filter>
|
||||
* <action android:name="android.media.midi.MidiUmpDeviceService" />
|
||||
* </intent-filter>
|
||||
* <property android:name="android.media.midi.MidiUmpDeviceService"
|
||||
* android:resource="@xml/device_info" />
|
||||
* </service></pre>
|
||||
*/
|
||||
public abstract class MidiUmpDeviceService extends Service {
|
||||
private static final String TAG = "MidiUmpDeviceService";
|
||||
|
||||
public static final String SERVICE_INTERFACE = "android.media.midi.MidiUmpDeviceService";
|
||||
|
||||
private IMidiManager mMidiManager;
|
||||
private MidiDeviceServer mServer;
|
||||
private MidiDeviceInfo mDeviceInfo;
|
||||
|
||||
private final MidiDeviceServer.Callback mCallback = new MidiDeviceServer.Callback() {
|
||||
@Override
|
||||
public void onDeviceStatusChanged(MidiDeviceServer server, MidiDeviceStatus status) {
|
||||
MidiUmpDeviceService.this.onDeviceStatusChanged(status);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onClose() {
|
||||
MidiUmpDeviceService.this.onClose();
|
||||
}
|
||||
};
|
||||
|
||||
@Override
|
||||
public void onCreate() {
|
||||
mMidiManager = IMidiManager.Stub.asInterface(
|
||||
ServiceManager.getService(Context.MIDI_SERVICE));
|
||||
MidiDeviceServer server;
|
||||
try {
|
||||
MidiDeviceInfo deviceInfo = mMidiManager.getServiceDeviceInfo(getPackageName(),
|
||||
this.getClass().getName());
|
||||
if (deviceInfo == null) {
|
||||
Log.e(TAG, "Could not find MidiDeviceInfo for MidiUmpDeviceService " + this);
|
||||
return;
|
||||
}
|
||||
mDeviceInfo = deviceInfo;
|
||||
|
||||
List<MidiReceiver> inputPortReceivers = onGetInputPortReceivers();
|
||||
if (inputPortReceivers == null) {
|
||||
Log.e(TAG, "Could not get input port receivers for MidiUmpDeviceService " + this);
|
||||
return;
|
||||
}
|
||||
MidiReceiver[] inputPortReceiversArr = new MidiReceiver[inputPortReceivers.size()];
|
||||
inputPortReceivers.toArray(inputPortReceiversArr);
|
||||
server = new MidiDeviceServer(mMidiManager, inputPortReceiversArr, deviceInfo,
|
||||
mCallback);
|
||||
} catch (RemoteException e) {
|
||||
Log.e(TAG, "RemoteException in IMidiManager.getServiceDeviceInfo");
|
||||
server = null;
|
||||
}
|
||||
mServer = server;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a list of {@link MidiReceiver} for the device's input ports.
|
||||
* Subclasses must override this to provide the receivers which will receive
|
||||
* data sent to the device's input ports.
|
||||
* The number of input and output ports must be equal and non-zero.
|
||||
* @return list of MidiReceivers
|
||||
*/
|
||||
public abstract @NonNull List<MidiReceiver> onGetInputPortReceivers();
|
||||
|
||||
/**
|
||||
* Returns a list of {@link MidiReceiver} for the device's output ports.
|
||||
* These can be used to send data out the device's output ports.
|
||||
* The number of input and output ports must be equal and non-zero.
|
||||
* @return the list of MidiReceivers
|
||||
*/
|
||||
public final @NonNull List<MidiReceiver> getOutputPortReceivers() {
|
||||
if (mServer == null) {
|
||||
return new ArrayList<MidiReceiver>();
|
||||
} else {
|
||||
return Arrays.asList(mServer.getOutputPortReceivers());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the {@link MidiDeviceInfo} instance for this service
|
||||
* @return the MidiDeviceInfo of the virtual MIDI device
|
||||
*/
|
||||
public final @Nullable MidiDeviceInfo getDeviceInfo() {
|
||||
return mDeviceInfo;
|
||||
}
|
||||
|
||||
/**
|
||||
* Called to notify when the {@link MidiDeviceStatus} has changed
|
||||
* @param status the current status of the MIDI device
|
||||
*/
|
||||
public void onDeviceStatusChanged(@Nullable MidiDeviceStatus status) {
|
||||
}
|
||||
|
||||
/**
|
||||
* Called to notify when the virtual MIDI device running in this service has been closed by
|
||||
* all its clients
|
||||
*/
|
||||
public void onClose() {
|
||||
}
|
||||
|
||||
@Override
|
||||
public @Nullable IBinder onBind(@NonNull Intent intent) {
|
||||
if (SERVICE_INTERFACE.equals(intent.getAction()) && mServer != null) {
|
||||
return mServer.getBinderInterface().asBinder();
|
||||
} else {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -31,6 +31,7 @@ import android.content.pm.PackageInfo;
|
||||
import android.content.pm.PackageManager;
|
||||
import android.content.pm.ResolveInfo;
|
||||
import android.content.pm.ServiceInfo;
|
||||
import android.content.res.Resources;
|
||||
import android.content.res.XmlResourceParser;
|
||||
import android.media.MediaMetrics;
|
||||
import android.media.midi.IBluetoothMidiService;
|
||||
@@ -43,6 +44,7 @@ import android.media.midi.MidiDeviceInfo;
|
||||
import android.media.midi.MidiDeviceService;
|
||||
import android.media.midi.MidiDeviceStatus;
|
||||
import android.media.midi.MidiManager;
|
||||
import android.media.midi.MidiUmpDeviceService;
|
||||
import android.os.Binder;
|
||||
import android.os.Bundle;
|
||||
import android.os.IBinder;
|
||||
@@ -580,10 +582,14 @@ public class MidiService extends IMidiManager.Stub {
|
||||
intent.setComponent(new ComponentName(
|
||||
MidiManager.BLUETOOTH_MIDI_SERVICE_PACKAGE,
|
||||
MidiManager.BLUETOOTH_MIDI_SERVICE_CLASS));
|
||||
} else {
|
||||
} else if (!isUmpDevice(mDeviceInfo)) {
|
||||
intent = new Intent(MidiDeviceService.SERVICE_INTERFACE);
|
||||
intent.setComponent(
|
||||
new ComponentName(mServiceInfo.packageName, mServiceInfo.name));
|
||||
} else {
|
||||
intent = new Intent(MidiUmpDeviceService.SERVICE_INTERFACE);
|
||||
intent.setComponent(
|
||||
new ComponentName(mServiceInfo.packageName, mServiceInfo.name));
|
||||
}
|
||||
|
||||
if (!mContext.bindServiceAsUser(intent, mServiceConnection,
|
||||
@@ -696,8 +702,8 @@ public class MidiService extends IMidiManager.Stub {
|
||||
isDeviceDisconnected ? "true" : "false")
|
||||
.set(MediaMetrics.Property.IS_SHARED,
|
||||
!mDeviceInfo.isPrivate() ? "true" : "false")
|
||||
.set(MediaMetrics.Property.SUPPORTS_MIDI_UMP, mDeviceInfo.getDefaultProtocol()
|
||||
!= MidiDeviceInfo.PROTOCOL_UNKNOWN ? "true" : "false")
|
||||
.set(MediaMetrics.Property.SUPPORTS_MIDI_UMP,
|
||||
isUmpDevice(mDeviceInfo) ? "true" : "false")
|
||||
.set(MediaMetrics.Property.USING_ALSA, mDeviceInfo.getProperties().get(
|
||||
MidiDeviceInfo.PROPERTY_ALSA_CARD) != null ? "true" : "false")
|
||||
.set(MediaMetrics.Property.EVENT, "deviceClosed")
|
||||
@@ -971,19 +977,37 @@ public class MidiService extends IMidiManager.Stub {
|
||||
private void onStartOrUnlockUser(TargetUser user, boolean matchDirectBootUnaware) {
|
||||
Log.d(TAG, "onStartOrUnlockUser " + user.getUserIdentifier() + " matchDirectBootUnaware: "
|
||||
+ matchDirectBootUnaware);
|
||||
Intent intent = new Intent(MidiDeviceService.SERVICE_INTERFACE);
|
||||
int resolveFlags = PackageManager.GET_META_DATA;
|
||||
if (matchDirectBootUnaware) {
|
||||
resolveFlags |= PackageManager.MATCH_DIRECT_BOOT_UNAWARE;
|
||||
}
|
||||
List<ResolveInfo> resolveInfos = mPackageManager.queryIntentServicesAsUser(intent,
|
||||
resolveFlags, user.getUserIdentifier());
|
||||
if (resolveInfos != null) {
|
||||
int count = resolveInfos.size();
|
||||
for (int i = 0; i < count; i++) {
|
||||
ServiceInfo serviceInfo = resolveInfos.get(i).serviceInfo;
|
||||
if (serviceInfo != null) {
|
||||
addPackageDeviceServer(serviceInfo, user.getUserIdentifier());
|
||||
|
||||
{
|
||||
Intent intent = new Intent(MidiDeviceService.SERVICE_INTERFACE);
|
||||
List<ResolveInfo> resolveInfos = mPackageManager.queryIntentServicesAsUser(intent,
|
||||
resolveFlags, user.getUserIdentifier());
|
||||
if (resolveInfos != null) {
|
||||
int count = resolveInfos.size();
|
||||
for (int i = 0; i < count; i++) {
|
||||
ServiceInfo serviceInfo = resolveInfos.get(i).serviceInfo;
|
||||
if (serviceInfo != null) {
|
||||
addLegacyPackageDeviceServer(serviceInfo, user.getUserIdentifier());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
{
|
||||
Intent intent = new Intent(MidiUmpDeviceService.SERVICE_INTERFACE);
|
||||
List<ResolveInfo> resolveInfos = mPackageManager.queryIntentServicesAsUser(intent,
|
||||
resolveFlags, user.getUserIdentifier());
|
||||
if (resolveInfos != null) {
|
||||
int count = resolveInfos.size();
|
||||
for (int i = 0; i < count; i++) {
|
||||
ServiceInfo serviceInfo = resolveInfos.get(i).serviceInfo;
|
||||
if (serviceInfo != null) {
|
||||
addLegacyPackageDeviceServer(serviceInfo, user.getUserIdentifier());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1057,13 +1081,11 @@ public class MidiService extends IMidiManager.Stub {
|
||||
if (device.isUidAllowed(uid) && device.isUserIdAllowed(userId)) {
|
||||
// UMP devices have protocols that are not PROTOCOL_UNKNOWN
|
||||
if (transport == MidiManager.TRANSPORT_UNIVERSAL_MIDI_PACKETS) {
|
||||
if (device.getDeviceInfo().getDefaultProtocol()
|
||||
!= MidiDeviceInfo.PROTOCOL_UNKNOWN) {
|
||||
if (isUmpDevice(device.getDeviceInfo())) {
|
||||
deviceInfos.add(device.getDeviceInfo());
|
||||
}
|
||||
} else if (transport == MidiManager.TRANSPORT_MIDI_BYTE_STREAM) {
|
||||
if (device.getDeviceInfo().getDefaultProtocol()
|
||||
== MidiDeviceInfo.PROTOCOL_UNKNOWN) {
|
||||
if (!isUmpDevice(device.getDeviceInfo())) {
|
||||
deviceInfos.add(device.getDeviceInfo());
|
||||
}
|
||||
}
|
||||
@@ -1364,14 +1386,15 @@ public class MidiService extends IMidiManager.Stub {
|
||||
ServiceInfo[] services = info.services;
|
||||
if (services == null) return;
|
||||
for (int i = 0; i < services.length; i++) {
|
||||
addPackageDeviceServer(services[i], userId);
|
||||
addLegacyPackageDeviceServer(services[i], userId);
|
||||
addUmpPackageDeviceServer(services[i], userId);
|
||||
}
|
||||
}
|
||||
|
||||
private static final String[] EMPTY_STRING_ARRAY = new String[0];
|
||||
|
||||
private void addPackageDeviceServer(ServiceInfo serviceInfo, int userId) {
|
||||
Log.d(TAG, "addPackageDeviceServer()" + userId);
|
||||
private void addLegacyPackageDeviceServer(ServiceInfo serviceInfo, int userId) {
|
||||
Log.d(TAG, "addLegacyPackageDeviceServer()" + userId);
|
||||
XmlResourceParser parser = null;
|
||||
|
||||
try {
|
||||
@@ -1507,6 +1530,128 @@ public class MidiService extends IMidiManager.Stub {
|
||||
}
|
||||
}
|
||||
|
||||
@RequiresPermission(Manifest.permission.INTERACT_ACROSS_USERS)
|
||||
private void addUmpPackageDeviceServer(ServiceInfo serviceInfo, int userId) {
|
||||
Log.d(TAG, "addUmpPackageDeviceServer()" + userId);
|
||||
XmlResourceParser parser = null;
|
||||
|
||||
try {
|
||||
ComponentName componentName = new ComponentName(serviceInfo.packageName,
|
||||
serviceInfo.name);
|
||||
int resId = mPackageManager.getProperty(MidiUmpDeviceService.SERVICE_INTERFACE,
|
||||
componentName).getResourceId();
|
||||
Resources resources = mPackageManager.getResourcesForApplication(
|
||||
serviceInfo.packageName);
|
||||
parser = resources.getXml(resId);
|
||||
if (parser == null) return;
|
||||
|
||||
// ignore virtual device servers that do not require the correct permission
|
||||
if (!android.Manifest.permission.BIND_MIDI_DEVICE_SERVICE.equals(
|
||||
serviceInfo.permission)) {
|
||||
Log.w(TAG, "Skipping MIDI device service " + serviceInfo.packageName
|
||||
+ ": it does not require the permission "
|
||||
+ android.Manifest.permission.BIND_MIDI_DEVICE_SERVICE);
|
||||
return;
|
||||
}
|
||||
|
||||
Bundle properties = null;
|
||||
int numPorts = 0;
|
||||
boolean isPrivate = false;
|
||||
ArrayList<String> portNames = new ArrayList<String>();
|
||||
|
||||
while (true) {
|
||||
int eventType = parser.next();
|
||||
if (eventType == XmlPullParser.END_DOCUMENT) {
|
||||
break;
|
||||
} else if (eventType == XmlPullParser.START_TAG) {
|
||||
String tagName = parser.getName();
|
||||
if ("device".equals(tagName)) {
|
||||
if (properties != null) {
|
||||
Log.w(TAG, "nested <device> elements in metadata for "
|
||||
+ serviceInfo.packageName);
|
||||
continue;
|
||||
}
|
||||
properties = new Bundle();
|
||||
properties.putParcelable(MidiDeviceInfo.PROPERTY_SERVICE_INFO, serviceInfo);
|
||||
numPorts = 0;
|
||||
isPrivate = false;
|
||||
|
||||
int count = parser.getAttributeCount();
|
||||
for (int i = 0; i < count; i++) {
|
||||
String name = parser.getAttributeName(i);
|
||||
String value = parser.getAttributeValue(i);
|
||||
if ("private".equals(name)) {
|
||||
isPrivate = "true".equals(value);
|
||||
} else {
|
||||
properties.putString(name, value);
|
||||
}
|
||||
}
|
||||
} else if ("port".equals(tagName)) {
|
||||
if (properties == null) {
|
||||
Log.w(TAG, "<port> outside of <device> in metadata for "
|
||||
+ serviceInfo.packageName);
|
||||
continue;
|
||||
}
|
||||
numPorts++;
|
||||
|
||||
String portName = null;
|
||||
int count = parser.getAttributeCount();
|
||||
for (int i = 0; i < count; i++) {
|
||||
String name = parser.getAttributeName(i);
|
||||
String value = parser.getAttributeValue(i);
|
||||
if ("name".equals(name)) {
|
||||
portName = value;
|
||||
break;
|
||||
}
|
||||
}
|
||||
portNames.add(portName);
|
||||
}
|
||||
} else if (eventType == XmlPullParser.END_TAG) {
|
||||
String tagName = parser.getName();
|
||||
if ("device".equals(tagName)) {
|
||||
if (properties != null) {
|
||||
if (numPorts == 0) {
|
||||
Log.w(TAG, "<device> with no ports in metadata for "
|
||||
+ serviceInfo.packageName);
|
||||
continue;
|
||||
}
|
||||
|
||||
int uid;
|
||||
try {
|
||||
ApplicationInfo appInfo = mPackageManager.getApplicationInfoAsUser(
|
||||
serviceInfo.packageName, 0, userId);
|
||||
uid = appInfo.uid;
|
||||
} catch (PackageManager.NameNotFoundException e) {
|
||||
Log.e(TAG, "could not fetch ApplicationInfo for "
|
||||
+ serviceInfo.packageName);
|
||||
continue;
|
||||
}
|
||||
|
||||
synchronized (mDevicesByInfo) {
|
||||
addDeviceLocked(MidiDeviceInfo.TYPE_VIRTUAL,
|
||||
numPorts, numPorts,
|
||||
portNames.toArray(EMPTY_STRING_ARRAY),
|
||||
portNames.toArray(EMPTY_STRING_ARRAY),
|
||||
properties, null, serviceInfo, isPrivate, uid,
|
||||
MidiDeviceInfo.PROTOCOL_UMP_MIDI_2_0, userId);
|
||||
}
|
||||
// setting properties to null signals that we are no longer
|
||||
// processing a <device>
|
||||
properties = null;
|
||||
portNames.clear();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
} catch (PackageManager.NameNotFoundException e) {
|
||||
// No such property
|
||||
} catch (Exception e) {
|
||||
Log.w(TAG, "Unable to load component info " + serviceInfo.toString(), e);
|
||||
} finally {
|
||||
if (parser != null) parser.close();
|
||||
}
|
||||
}
|
||||
|
||||
private void removePackageDeviceServers(String packageName, int userId) {
|
||||
synchronized (mDevicesByInfo) {
|
||||
Iterator<Device> iterator = mDevicesByInfo.values().iterator();
|
||||
@@ -1649,4 +1794,8 @@ public class MidiService extends IMidiManager.Stub {
|
||||
private int getCallingUserId() {
|
||||
return UserHandle.getUserId(Binder.getCallingUid());
|
||||
}
|
||||
|
||||
private boolean isUmpDevice(MidiDeviceInfo deviceInfo) {
|
||||
return deviceInfo.getDefaultProtocol() != MidiDeviceInfo.PROTOCOL_UNKNOWN;
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user