summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorMike Lockwood <lockwood@google.com>2015-06-04 13:43:56 -0700
committerMike Lockwood <lockwood@google.com>2015-06-04 19:16:23 -0700
commite0a6ca64fac5bd4f10139321604031816e90adb4 (patch)
treeaf84954409f063e07e3845c5b8a8fe703106da7a
parentec7100d0114d50f12c4ad95eaeac61e93a6d5a96 (diff)
downloadframeworks_base-e0a6ca64fac5bd4f10139321604031816e90adb4.zip
frameworks_base-e0a6ca64fac5bd4f10139321604031816e90adb4.tar.gz
frameworks_base-e0a6ca64fac5bd4f10139321604031816e90adb4.tar.bz2
MidiManager: proxy all requests to open devices through MidiService
Previously when a MidiManager client opened a virtual or Bluetooth device, the client bound directly to the virtual device's MidiDeviceService or BluetoothMidiDevice's IMidiDeviceServer for the given BluetoothDevice. Only USB devices were opened in MidiService. Now opening any type of MIDI device is done via IMidiManager.openDevice() or IMidiManager.openBluetoothDevice(). MidiService tracks all connnections between clients and devices. Services that implement virtual devices must now require android.permission.BIND_MIDI_DEVICE_SERVICE so only MidiService can bind to these services. Bug: 21044677 Change-Id: I7172f7b1e0cbfe4a2a87dff376c32dc9b41aa563
-rw-r--r--Android.mk1
-rw-r--r--api/current.txt2
-rw-r--r--api/system-current.txt2
-rw-r--r--core/java/android/app/SystemServiceRegistry.java2
-rw-r--r--core/res/AndroidManifest.xml5
-rw-r--r--media/java/android/media/midi/IMidiDeviceOpenCallback.aidl25
-rw-r--r--media/java/android/media/midi/IMidiDeviceServer.aidl1
-rw-r--r--media/java/android/media/midi/IMidiManager.aidl15
-rw-r--r--media/java/android/media/midi/MidiDevice.java27
-rw-r--r--media/java/android/media/midi/MidiDeviceServer.java16
-rw-r--r--media/java/android/media/midi/MidiDeviceService.java11
-rw-r--r--media/java/android/media/midi/MidiManager.java118
-rw-r--r--media/java/android/media/midi/package.html6
-rw-r--r--media/packages/BluetoothMidiService/AndroidManifest.xml3
-rw-r--r--media/packages/BluetoothMidiService/src/com/android/bluetoothmidiservice/BluetoothMidiDevice.java26
-rw-r--r--services/midi/java/com/android/server/midi/MidiService.java434
16 files changed, 520 insertions, 174 deletions
diff --git a/Android.mk b/Android.mk
index d6dac53..047254f 100644
--- a/Android.mk
+++ b/Android.mk
@@ -344,6 +344,7 @@ LOCAL_SRC_FILES += \
media/java/android/media/IVolumeController.aidl \
media/java/android/media/audiopolicy/IAudioPolicyCallback.aidl \
media/java/android/media/midi/IMidiDeviceListener.aidl \
+ media/java/android/media/midi/IMidiDeviceOpenCallback.aidl \
media/java/android/media/midi/IMidiDeviceServer.aidl \
media/java/android/media/midi/IMidiManager.aidl \
media/java/android/media/projection/IMediaProjection.aidl \
diff --git a/api/current.txt b/api/current.txt
index 31f4cff..4de7ed1 100644
--- a/api/current.txt
+++ b/api/current.txt
@@ -27,6 +27,7 @@ package android {
field public static final java.lang.String BIND_DREAM_SERVICE = "android.permission.BIND_DREAM_SERVICE";
field public static final java.lang.String BIND_INCALL_SERVICE = "android.permission.BIND_INCALL_SERVICE";
field public static final java.lang.String BIND_INPUT_METHOD = "android.permission.BIND_INPUT_METHOD";
+ field public static final java.lang.String BIND_MIDI_DEVICE_SERVICE = "android.permission.BIND_MIDI_DEVICE_SERVICE";
field public static final java.lang.String BIND_NFC_SERVICE = "android.permission.BIND_NFC_SERVICE";
field public static final java.lang.String BIND_NOTIFICATION_LISTENER_SERVICE = "android.permission.BIND_NOTIFICATION_LISTENER_SERVICE";
field public static final java.lang.String BIND_PRINT_SERVICE = "android.permission.BIND_PRINT_SERVICE";
@@ -17312,6 +17313,7 @@ package android.media.midi {
method public final android.media.midi.MidiDeviceInfo getDeviceInfo();
method public final android.media.midi.MidiReceiver[] getOutputPortReceivers();
method public android.os.IBinder onBind(android.content.Intent);
+ method public void onClose();
method public void onDeviceStatusChanged(android.media.midi.MidiDeviceStatus);
method public abstract android.media.midi.MidiReceiver[] onGetInputPortReceivers();
field public static final java.lang.String SERVICE_INTERFACE = "android.media.midi.MidiDeviceService";
diff --git a/api/system-current.txt b/api/system-current.txt
index 187edc0..38d4c08 100644
--- a/api/system-current.txt
+++ b/api/system-current.txt
@@ -39,6 +39,7 @@ package android {
field public static final java.lang.String BIND_INCALL_SERVICE = "android.permission.BIND_INCALL_SERVICE";
field public static final java.lang.String BIND_INPUT_METHOD = "android.permission.BIND_INPUT_METHOD";
field public static final java.lang.String BIND_KEYGUARD_APPWIDGET = "android.permission.BIND_KEYGUARD_APPWIDGET";
+ field public static final java.lang.String BIND_MIDI_DEVICE_SERVICE = "android.permission.BIND_MIDI_DEVICE_SERVICE";
field public static final java.lang.String BIND_NFC_SERVICE = "android.permission.BIND_NFC_SERVICE";
field public static final java.lang.String BIND_NOTIFICATION_LISTENER_SERVICE = "android.permission.BIND_NOTIFICATION_LISTENER_SERVICE";
field public static final java.lang.String BIND_PRINT_SERVICE = "android.permission.BIND_PRINT_SERVICE";
@@ -18618,6 +18619,7 @@ package android.media.midi {
method public final android.media.midi.MidiDeviceInfo getDeviceInfo();
method public final android.media.midi.MidiReceiver[] getOutputPortReceivers();
method public android.os.IBinder onBind(android.content.Intent);
+ method public void onClose();
method public void onDeviceStatusChanged(android.media.midi.MidiDeviceStatus);
method public abstract android.media.midi.MidiReceiver[] onGetInputPortReceivers();
field public static final java.lang.String SERVICE_INTERFACE = "android.media.midi.MidiDeviceService";
diff --git a/core/java/android/app/SystemServiceRegistry.java b/core/java/android/app/SystemServiceRegistry.java
index 0d00908..10e8a53 100644
--- a/core/java/android/app/SystemServiceRegistry.java
+++ b/core/java/android/app/SystemServiceRegistry.java
@@ -691,7 +691,7 @@ final class SystemServiceRegistry {
@Override
public MidiManager createService(ContextImpl ctx) {
IBinder b = ServiceManager.getService(Context.MIDI_SERVICE);
- return new MidiManager(ctx, IMidiManager.Stub.asInterface(b));
+ return new MidiManager(IMidiManager.Stub.asInterface(b));
}});
registerService(Context.RADIO_SERVICE, RadioManager.class,
diff --git a/core/res/AndroidManifest.xml b/core/res/AndroidManifest.xml
index ba91bce..3bced5a 100644
--- a/core/res/AndroidManifest.xml
+++ b/core/res/AndroidManifest.xml
@@ -1828,6 +1828,11 @@
<permission android:name="android.permission.BIND_INPUT_METHOD"
android:protectionLevel="signature" />
+ <!-- Must be required by an {@link android.media.midi.MidiDeviceService},
+ to ensure that only the system can bind to it. -->
+ <permission android:name="android.permission.BIND_MIDI_DEVICE_SERVICE"
+ android:protectionLevel="signature" />
+
<!-- Must be required by an {@link android.accessibilityservice.AccessibilityService},
to ensure that only the system can bind to it. -->
<permission android:name="android.permission.BIND_ACCESSIBILITY_SERVICE"
diff --git a/media/java/android/media/midi/IMidiDeviceOpenCallback.aidl b/media/java/android/media/midi/IMidiDeviceOpenCallback.aidl
new file mode 100644
index 0000000..6e3dbbf
--- /dev/null
+++ b/media/java/android/media/midi/IMidiDeviceOpenCallback.aidl
@@ -0,0 +1,25 @@
+/*
+ * Copyright (C) 2015 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.media.midi.IMidiDeviceServer;
+
+/** @hide */
+oneway interface IMidiDeviceOpenCallback
+{
+ void onDeviceOpened(in IMidiDeviceServer server, IBinder token);
+}
diff --git a/media/java/android/media/midi/IMidiDeviceServer.aidl b/media/java/android/media/midi/IMidiDeviceServer.aidl
index 96d12fd..e30796d 100644
--- a/media/java/android/media/midi/IMidiDeviceServer.aidl
+++ b/media/java/android/media/midi/IMidiDeviceServer.aidl
@@ -25,6 +25,7 @@ interface IMidiDeviceServer
ParcelFileDescriptor openInputPort(IBinder token, int portNumber);
ParcelFileDescriptor openOutputPort(IBinder token, int portNumber);
void closePort(IBinder token);
+ void closeDevice();
// connects the input port pfd to the specified output port
void connectPorts(IBinder token, in ParcelFileDescriptor pfd, int outputPortNumber);
diff --git a/media/java/android/media/midi/IMidiManager.aidl b/media/java/android/media/midi/IMidiManager.aidl
index fcd4aff..d3c8e0a 100644
--- a/media/java/android/media/midi/IMidiManager.aidl
+++ b/media/java/android/media/midi/IMidiManager.aidl
@@ -16,7 +16,10 @@
package android.media.midi;
+import android.bluetooth.BluetoothDevice;
import android.media.midi.IMidiDeviceListener;
+import android.media.midi.IMidiDeviceOpenCallback;
+import android.media.midi.IMidiDeviceServer;
import android.media.midi.IMidiDeviceServer;
import android.media.midi.MidiDeviceInfo;
import android.media.midi.MidiDeviceStatus;
@@ -29,11 +32,13 @@ interface IMidiManager
MidiDeviceInfo[] getDevices();
// for device creation & removal notifications
- void registerListener(IBinder token, in IMidiDeviceListener listener);
- void unregisterListener(IBinder token, in IMidiDeviceListener listener);
+ void registerListener(IBinder clientToken, in IMidiDeviceListener listener);
+ void unregisterListener(IBinder clientToken, in IMidiDeviceListener listener);
- // for opening built-in MIDI devices
- IMidiDeviceServer openDevice(IBinder token, in MidiDeviceInfo device);
+ void openDevice(IBinder clientToken, in MidiDeviceInfo device, in IMidiDeviceOpenCallback callback);
+ void openBluetoothDevice(IBinder clientToken, in BluetoothDevice bluetoothDevice,
+ in IMidiDeviceOpenCallback callback);
+ void closeDevice(IBinder clientToken, IBinder deviceToken);
// for registering built-in MIDI devices
MidiDeviceInfo registerDeviceServer(in IMidiDeviceServer server, int numInputPorts,
@@ -52,5 +57,5 @@ interface IMidiManager
// used by MIDI devices to report their status
// the token is used by MidiService for death notification
- void setDeviceStatus(IBinder token, in MidiDeviceStatus status);
+ void setDeviceStatus(in IMidiDeviceServer server, in MidiDeviceStatus status);
}
diff --git a/media/java/android/media/midi/MidiDevice.java b/media/java/android/media/midi/MidiDevice.java
index 6526adc..7998a92 100644
--- a/media/java/android/media/midi/MidiDevice.java
+++ b/media/java/android/media/midi/MidiDevice.java
@@ -16,8 +16,6 @@
package android.media.midi;
-import android.content.Context;
-import android.content.ServiceConnection;
import android.os.Binder;
import android.os.IBinder;
import android.os.ParcelFileDescriptor;
@@ -40,9 +38,9 @@ public final class MidiDevice implements Closeable {
private final MidiDeviceInfo mDeviceInfo;
private final IMidiDeviceServer mDeviceServer;
- private Context mContext;
- private ServiceConnection mServiceConnection;
-
+ private final IMidiManager mMidiManager;
+ private final IBinder mClientToken;
+ private final IBinder mDeviceToken;
private final CloseGuard mGuard = CloseGuard.get();
@@ -71,16 +69,13 @@ public final class MidiDevice implements Closeable {
}
}
- /* package */ MidiDevice(MidiDeviceInfo deviceInfo, IMidiDeviceServer server) {
- this(deviceInfo, server, null, null);
- }
-
/* package */ MidiDevice(MidiDeviceInfo deviceInfo, IMidiDeviceServer server,
- Context context, ServiceConnection serviceConnection) {
+ IMidiManager midiManager, IBinder clientToken, IBinder deviceToken) {
mDeviceInfo = deviceInfo;
mDeviceServer = server;
- mContext = context;
- mServiceConnection = serviceConnection;
+ mMidiManager = midiManager;
+ mClientToken = clientToken;
+ mDeviceToken = deviceToken;
mGuard.open("close");
}
@@ -170,10 +165,10 @@ public final class MidiDevice implements Closeable {
public void close() throws IOException {
synchronized (mGuard) {
mGuard.close();
- if (mContext != null && mServiceConnection != null) {
- mContext.unbindService(mServiceConnection);
- mContext = null;
- mServiceConnection = null;
+ try {
+ mMidiManager.closeDevice(mClientToken, mDeviceToken);
+ } catch (RemoteException e) {
+ Log.e(TAG, "RemoteException in closeDevice");
}
}
}
diff --git a/media/java/android/media/midi/MidiDeviceServer.java b/media/java/android/media/midi/MidiDeviceServer.java
index a316a44..1b56e1c 100644
--- a/media/java/android/media/midi/MidiDeviceServer.java
+++ b/media/java/android/media/midi/MidiDeviceServer.java
@@ -65,7 +65,6 @@ public final class MidiDeviceServer implements Closeable {
// for reporting device status
- private final IBinder mDeviceStatusToken = new Binder();
private final boolean[] mInputPortOpen;
private final int[] mOutputPortOpenCount;
@@ -81,6 +80,11 @@ public final class MidiDeviceServer implements Closeable {
* @param status the {@link MidiDeviceStatus} for the device
*/
public void onDeviceStatusChanged(MidiDeviceServer server, MidiDeviceStatus status);
+
+ /**
+ * Called to notify when the device is closed
+ */
+ public void onClose();
}
abstract private class PortClient implements IBinder.DeathRecipient {
@@ -242,6 +246,14 @@ public final class MidiDeviceServer implements Closeable {
}
@Override
+ public void closeDevice() {
+ if (mCallback != null) {
+ mCallback.onClose();
+ }
+ IoUtils.closeQuietly(MidiDeviceServer.this);
+ }
+
+ @Override
public void connectPorts(IBinder token, ParcelFileDescriptor pfd,
int outputPortNumber) {
MidiInputPort inputPort = new MidiInputPort(pfd, outputPortNumber);
@@ -305,7 +317,7 @@ public final class MidiDeviceServer implements Closeable {
mCallback.onDeviceStatusChanged(this, status);
}
try {
- mMidiManager.setDeviceStatus(mDeviceStatusToken, status);
+ mMidiManager.setDeviceStatus(mServer, status);
} catch (RemoteException e) {
Log.e(TAG, "RemoteException in updateDeviceStatus");
} finally {
diff --git a/media/java/android/media/midi/MidiDeviceService.java b/media/java/android/media/midi/MidiDeviceService.java
index ce12a4f..d897ad2 100644
--- a/media/java/android/media/midi/MidiDeviceService.java
+++ b/media/java/android/media/midi/MidiDeviceService.java
@@ -59,6 +59,11 @@ abstract public class MidiDeviceService extends Service {
public void onDeviceStatusChanged(MidiDeviceServer server, MidiDeviceStatus status) {
MidiDeviceService.this.onDeviceStatusChanged(status);
}
+
+ @Override
+ public void onClose() {
+ MidiDeviceService.this.onClose();
+ }
};
@Override
@@ -125,6 +130,12 @@ abstract public class MidiDeviceService extends Service {
public void onDeviceStatusChanged(MidiDeviceStatus status) {
}
+ /**
+ * Called to notify when our device has been closed by all its clients
+ */
+ public void onClose() {
+ }
+
@Override
public IBinder onBind(Intent intent) {
if (SERVICE_INTERFACE.equals(intent.getAction()) && mServer != null) {
diff --git a/media/java/android/media/midi/MidiManager.java b/media/java/android/media/midi/MidiManager.java
index d19cf36..0beb9a4 100644
--- a/media/java/android/media/midi/MidiManager.java
+++ b/media/java/android/media/midi/MidiManager.java
@@ -17,11 +17,6 @@
package android.media.midi;
import android.bluetooth.BluetoothDevice;
-import android.content.ComponentName;
-import android.content.Context;
-import android.content.Intent;
-import android.content.ServiceConnection;
-import android.content.pm.ServiceInfo;
import android.os.Binder;
import android.os.IBinder;
import android.os.Bundle;
@@ -52,16 +47,17 @@ public final class MidiManager {
/**
* BluetoothMidiService package name
+ * @hide
*/
- private static final String BLUETOOTH_MIDI_SERVICE_PACKAGE = "com.android.bluetoothmidiservice";
+ public static final String BLUETOOTH_MIDI_SERVICE_PACKAGE = "com.android.bluetoothmidiservice";
/**
* BluetoothMidiService class name
+ * @hide
*/
- private static final String BLUETOOTH_MIDI_SERVICE_CLASS =
+ public static final String BLUETOOTH_MIDI_SERVICE_CLASS =
"com.android.bluetoothmidiservice.BluetoothMidiService";
- private final Context mContext;
private final IMidiManager mService;
private final IBinder mToken = new Binder();
@@ -166,8 +162,7 @@ public final class MidiManager {
/**
* @hide
*/
- public MidiManager(Context context, IMidiManager service) {
- mContext = context;
+ public MidiManager(IMidiManager service) {
mService = service;
}
@@ -237,7 +232,7 @@ public final class MidiManager {
* Opens a MIDI device for reading and writing.
*
* @param deviceInfo a {@link android.media.midi.MidiDeviceInfo} to open
- * @param listener a {@link MidiManager.OnDeviceOpenedListener} to be called
+ * @param listener a {@link MidiManager.OnDeviceOpenedListener} to be called
* to receive the result
* @param handler the {@link android.os.Handler Handler} that will be used for delivering
* the result. If handler is null, then the thread used for the
@@ -245,52 +240,28 @@ public final class MidiManager {
*/
public void openDevice(MidiDeviceInfo deviceInfo, OnDeviceOpenedListener listener,
Handler handler) {
- MidiDevice device = null;
- try {
- IMidiDeviceServer server = mService.openDevice(mToken, deviceInfo);
- if (server == null) {
- ServiceInfo serviceInfo = (ServiceInfo)deviceInfo.getProperties().getParcelable(
- MidiDeviceInfo.PROPERTY_SERVICE_INFO);
- if (serviceInfo == null) {
- Log.e(TAG, "no ServiceInfo for " + deviceInfo);
- } else {
- Intent intent = new Intent(MidiDeviceService.SERVICE_INTERFACE);
- intent.setComponent(new ComponentName(serviceInfo.packageName,
- serviceInfo.name));
- final MidiDeviceInfo deviceInfoF = deviceInfo;
- final OnDeviceOpenedListener listenerF = listener;
- final Handler handlerF = handler;
- if (mContext.bindService(intent,
- new ServiceConnection() {
- @Override
- public void onServiceConnected(ComponentName name, IBinder binder) {
- IMidiDeviceServer server =
- IMidiDeviceServer.Stub.asInterface(binder);
- MidiDevice device = new MidiDevice(deviceInfoF, server, mContext,
- this);
- sendOpenDeviceResponse(device, listenerF, handlerF);
- }
+ final MidiDeviceInfo deviceInfoF = deviceInfo;
+ final OnDeviceOpenedListener listenerF = listener;
+ final Handler handlerF = handler;
- @Override
- public void onServiceDisconnected(ComponentName name) {
- // FIXME - anything to do here?
- }
- },
- Context.BIND_AUTO_CREATE))
- {
- // return immediately to avoid calling sendOpenDeviceResponse below
- return;
- } else {
- Log.e(TAG, "Unable to bind service: " + intent);
- }
+ IMidiDeviceOpenCallback callback = new IMidiDeviceOpenCallback.Stub() {
+ @Override
+ public void onDeviceOpened(IMidiDeviceServer server, IBinder deviceToken) {
+ MidiDevice device;
+ if (server != null) {
+ device = new MidiDevice(deviceInfoF, server, mService, mToken, deviceToken);
+ } else {
+ device = null;
}
- } else {
- device = new MidiDevice(deviceInfo, server);
+ sendOpenDeviceResponse(device, listenerF, handlerF);
}
+ };
+
+ try {
+ mService.openDevice(mToken, deviceInfo, callback);
} catch (RemoteException e) {
Log.e(TAG, "RemoteException in openDevice");
}
- sendOpenDeviceResponse(device, listener, handler);
}
/**
@@ -303,38 +274,33 @@ public final class MidiManager {
* the result. If handler is null, then the thread used for the
* listener is unspecified.
*/
- public void openBluetoothDevice(final BluetoothDevice bluetoothDevice,
- final OnDeviceOpenedListener listener, final Handler handler) {
- Intent intent = new Intent(BLUETOOTH_MIDI_SERVICE_INTENT);
- intent.setComponent(new ComponentName(BLUETOOTH_MIDI_SERVICE_PACKAGE,
- BLUETOOTH_MIDI_SERVICE_CLASS));
- intent.putExtra("device", bluetoothDevice);
- if (!mContext.bindService(intent,
- new ServiceConnection() {
- @Override
- public void onServiceConnected(ComponentName name, IBinder binder) {
- IMidiDeviceServer server =
- IMidiDeviceServer.Stub.asInterface(binder);
+ public void openBluetoothDevice(BluetoothDevice bluetoothDevice,
+ OnDeviceOpenedListener listener, Handler handler) {
+ final OnDeviceOpenedListener listenerF = listener;
+ final Handler handlerF = handler;
+
+ IMidiDeviceOpenCallback callback = new IMidiDeviceOpenCallback.Stub() {
+ @Override
+ public void onDeviceOpened(IMidiDeviceServer server, IBinder deviceToken) {
+ MidiDevice device = null;
+ if (server != null) {
try {
// fetch MidiDeviceInfo from the server
MidiDeviceInfo deviceInfo = server.getDeviceInfo();
- MidiDevice device = new MidiDevice(deviceInfo, server, mContext, this);
- sendOpenDeviceResponse(device, listener, handler);
+ device = new MidiDevice(deviceInfo, server, mService, mToken, deviceToken);
+ sendOpenDeviceResponse(device, listenerF, handlerF);
} catch (RemoteException e) {
- Log.e(TAG, "remote exception in onServiceConnected");
- sendOpenDeviceResponse(null, listener, handler);
+ Log.e(TAG, "remote exception in getDeviceInfo()");
}
}
+ sendOpenDeviceResponse(device, listenerF, handlerF);
+ }
+ };
- @Override
- public void onServiceDisconnected(ComponentName name) {
- // FIXME - anything to do here?
- }
- },
- Context.BIND_AUTO_CREATE))
- {
- Log.e(TAG, "Unable to bind service: " + intent);
- sendOpenDeviceResponse(null, listener, handler);
+ try {
+ mService.openBluetoothDevice(mToken, bluetoothDevice, callback);
+ } catch (RemoteException e) {
+ Log.e(TAG, "RemoteException in openDevice");
}
}
diff --git a/media/java/android/media/midi/package.html b/media/java/android/media/midi/package.html
index bd589a9..8b2bd16 100644
--- a/media/java/android/media/midi/package.html
+++ b/media/java/android/media/midi/package.html
@@ -241,7 +241,8 @@ messages.</p>
<p>An app can provide a MIDI Service that can be used by other apps. For example,
-an app can provide a custom synthesizer that other apps can send messages to. </p>
+an app can provide a custom synthesizer that other apps can send messages to.
+The service must be guarded with permission &quot;android.permission.BIND_MIDI_DEVICE_SERVICE&quot;.</p>
<h2 id=manifest_files>Manifest Files</h2>
@@ -250,7 +251,8 @@ an app can provide a custom synthesizer that other apps can send messages to. </
AndroidManifest.xml file.</p>
<pre class=prettyprint>
-&lt;service android:name="<strong>MySynthDeviceService</strong>">
+&lt;service android:name="<strong>MySynthDeviceService</strong>"
+ android:permission="android.permission.BIND_MIDI_DEVICE_SERVICE">
&lt;intent-filter>
&lt;action android:name="android.media.midi.MidiDeviceService" />
&lt;/intent-filter>
diff --git a/media/packages/BluetoothMidiService/AndroidManifest.xml b/media/packages/BluetoothMidiService/AndroidManifest.xml
index 15aa581..b0b389a 100644
--- a/media/packages/BluetoothMidiService/AndroidManifest.xml
+++ b/media/packages/BluetoothMidiService/AndroidManifest.xml
@@ -8,7 +8,8 @@
<application
android:label="@string/app_name">
- <service android:name="BluetoothMidiService">
+ <service android:name="BluetoothMidiService"
+ android:permission="android.permission.BIND_MIDI_DEVICE_SERVICE">
<intent-filter>
<action android:name="android.media.midi.BluetoothMidiService" />
</intent-filter>
diff --git a/media/packages/BluetoothMidiService/src/com/android/bluetoothmidiservice/BluetoothMidiDevice.java b/media/packages/BluetoothMidiService/src/com/android/bluetoothmidiservice/BluetoothMidiDevice.java
index 60c6570..e6d59e4 100644
--- a/media/packages/BluetoothMidiService/src/com/android/bluetoothmidiservice/BluetoothMidiDevice.java
+++ b/media/packages/BluetoothMidiService/src/com/android/bluetoothmidiservice/BluetoothMidiDevice.java
@@ -24,10 +24,11 @@ import android.bluetooth.BluetoothGattDescriptor;
import android.bluetooth.BluetoothGattService;
import android.bluetooth.BluetoothProfile;
import android.content.Context;
-import android.media.midi.MidiReceiver;
-import android.media.midi.MidiManager;
-import android.media.midi.MidiDeviceServer;
import android.media.midi.MidiDeviceInfo;
+import android.media.midi.MidiDeviceServer;
+import android.media.midi.MidiDeviceStatus;
+import android.media.midi.MidiManager;
+import android.media.midi.MidiReceiver;
import android.os.Bundle;
import android.os.IBinder;
import android.util.Log;
@@ -81,6 +82,18 @@ public final class BluetoothMidiDevice {
private final BluetoothPacketDecoder mPacketDecoder
= new BluetoothPacketDecoder(MAX_PACKET_SIZE);
+ private final MidiDeviceServer.Callback mDeviceServerCallback
+ = new MidiDeviceServer.Callback() {
+ @Override
+ public void onDeviceStatusChanged(MidiDeviceServer server, MidiDeviceStatus status) {
+ }
+
+ @Override
+ public void onClose() {
+ close();
+ }
+ };
+
private final BluetoothGattCallback mGattCallback = new BluetoothGattCallback() {
@Override
public void onConnectionStateChange(BluetoothGatt gatt, int status,
@@ -213,7 +226,7 @@ public final class BluetoothMidiDevice {
inputPortReceivers[0] = mEventScheduler.getReceiver();
mDeviceServer = mMidiManager.createDeviceServer(inputPortReceivers, 1,
- null, null, properties, MidiDeviceInfo.TYPE_BLUETOOTH, null);
+ null, null, properties, MidiDeviceInfo.TYPE_BLUETOOTH, mDeviceServerCallback);
mOutputReceiver = mDeviceServer.getOutputPortReceivers()[0];
@@ -248,11 +261,12 @@ public final class BluetoothMidiDevice {
private void close() {
synchronized (mBluetoothDevice) {
- mEventScheduler.close();
+ mEventScheduler.close();
+ mService.deviceClosed(mBluetoothDevice);
+
if (mDeviceServer != null) {
IoUtils.closeQuietly(mDeviceServer);
mDeviceServer = null;
- mService.deviceClosed(mBluetoothDevice);
}
if (mBluetoothGatt != null) {
mBluetoothGatt.close();
diff --git a/services/midi/java/com/android/server/midi/MidiService.java b/services/midi/java/com/android/server/midi/MidiService.java
index 370f125..d1bbbfc 100644
--- a/services/midi/java/com/android/server/midi/MidiService.java
+++ b/services/midi/java/com/android/server/midi/MidiService.java
@@ -16,8 +16,11 @@
package com.android.server.midi;
+import android.bluetooth.BluetoothDevice;
+import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
+import android.content.ServiceConnection;
import android.content.pm.ApplicationInfo;
import android.content.pm.PackageInfo;
import android.content.pm.PackageManager;
@@ -25,11 +28,13 @@ import android.content.pm.ResolveInfo;
import android.content.pm.ServiceInfo;
import android.content.res.XmlResourceParser;
import android.media.midi.IMidiDeviceListener;
+import android.media.midi.IMidiDeviceOpenCallback;
import android.media.midi.IMidiDeviceServer;
import android.media.midi.IMidiManager;
import android.media.midi.MidiDeviceInfo;
import android.media.midi.MidiDeviceService;
import android.media.midi.MidiDeviceStatus;
+import android.media.midi.MidiManager;
import android.os.Binder;
import android.os.Bundle;
import android.os.IBinder;
@@ -78,6 +83,10 @@ public class MidiService extends IMidiManager.Stub {
private final HashMap<MidiDeviceInfo, Device> mDevicesByInfo
= new HashMap<MidiDeviceInfo, Device>();
+ // list of all Bluetooth devices, keyed by BluetoothDevice
+ private final HashMap<BluetoothDevice, Device> mBluetoothDevices
+ = new HashMap<BluetoothDevice, Device>();
+
// list of all devices, keyed by IMidiDeviceServer
private final HashMap<IBinder, Device> mDevicesByServer = new HashMap<IBinder, Device>();
@@ -86,6 +95,9 @@ public class MidiService extends IMidiManager.Stub {
private final PackageManager mPackageManager;
+ // UID of BluetoothMidiService
+ private final int mBluetoothServiceUid;
+
// PackageMonitor for listening to package changes
private final PackageMonitor mPackageMonitor = new PackageMonitor() {
@Override
@@ -115,6 +127,9 @@ public class MidiService extends IMidiManager.Stub {
// List of all receivers for this client
private final ArrayList<IMidiDeviceListener> mListeners
= new ArrayList<IMidiDeviceListener>();
+ // List of all device connections for this client
+ private final HashMap<IBinder, DeviceConnection> mDeviceConnections
+ = new HashMap<IBinder, DeviceConnection>();
public Client(IBinder token) {
mToken = token;
@@ -132,8 +147,33 @@ public class MidiService extends IMidiManager.Stub {
public void removeListener(IMidiDeviceListener listener) {
mListeners.remove(listener);
- if (mListeners.size() == 0) {
- removeClient(mToken);
+ if (mListeners.size() == 0 && mDeviceConnections.size() == 0) {
+ close();
+ }
+ }
+
+ public void addDeviceConnection(Device device, IMidiDeviceOpenCallback callback) {
+ DeviceConnection connection = new DeviceConnection(device, this, callback);
+ mDeviceConnections.put(connection.getToken(), connection);
+ device.addDeviceConnection(connection);
+ }
+
+ // called from MidiService.closeDevice()
+ public void removeDeviceConnection(IBinder token) {
+ DeviceConnection connection = mDeviceConnections.remove(token);
+ if (connection != null) {
+ connection.getDevice().removeDeviceConnection(connection);
+ }
+ if (mListeners.size() == 0 && mDeviceConnections.size() == 0) {
+ close();
+ }
+ }
+
+ // called from Device.close()
+ public void removeDeviceConnection(DeviceConnection connection) {
+ mDeviceConnections.remove(connection.getToken());
+ if (mListeners.size() == 0 && mDeviceConnections.size() == 0) {
+ close();
}
}
@@ -178,8 +218,21 @@ public class MidiService extends IMidiManager.Stub {
}
}
+ private void close() {
+ synchronized (mClients) {
+ mClients.remove(mToken);
+ mToken.unlinkToDeath(this, 0);
+ }
+
+ for (DeviceConnection connection : mDeviceConnections.values()) {
+ connection.getDevice().removeDeviceConnection(connection);
+ }
+ }
+
+ @Override
public void binderDied() {
- removeClient(mToken);
+ Log.d(TAG, "Client died: " + this);
+ close();
}
@Override
@@ -190,6 +243,12 @@ public class MidiService extends IMidiManager.Stub {
sb.append(mPid);
sb.append(" listener count: ");
sb.append(mListeners.size());
+ sb.append(" Device Connections:");
+ for (DeviceConnection connection : mDeviceConnections.values()) {
+ sb.append(" <device ");
+ sb.append(connection.getDevice().getDeviceInfo().getId());
+ sb.append(">");
+ }
return sb.toString();
}
}
@@ -211,57 +270,96 @@ public class MidiService extends IMidiManager.Stub {
}
}
- private void removeClient(IBinder token) {
- mClients.remove(token);
- }
-
private final class Device implements IBinder.DeathRecipient {
- private final IMidiDeviceServer mServer;
- private final MidiDeviceInfo mDeviceInfo;
+ private IMidiDeviceServer mServer;
+ private MidiDeviceInfo mDeviceInfo;
+ private final BluetoothDevice mBluetoothDevice;
private MidiDeviceStatus mDeviceStatus;
- private IBinder mDeviceStatusToken;
+
// ServiceInfo for the device's MidiDeviceServer implementation (virtual devices only)
private final ServiceInfo mServiceInfo;
// UID of device implementation
private final int mUid;
+ // ServiceConnection for implementing Service (virtual devices only)
+ // mServiceConnection is non-null when connected or attempting to connect to the service
+ private ServiceConnection mServiceConnection;
+
+ // List of all device connections for this device
+ private final ArrayList<DeviceConnection> mDeviceConnections
+ = new ArrayList<DeviceConnection>();
+
public Device(IMidiDeviceServer server, MidiDeviceInfo deviceInfo,
ServiceInfo serviceInfo, int uid) {
- mServer = server;
mDeviceInfo = deviceInfo;
mServiceInfo = serviceInfo;
mUid = uid;
+ mBluetoothDevice = (BluetoothDevice)deviceInfo.getProperties().getParcelable(
+ MidiDeviceInfo.PROPERTY_BLUETOOTH_DEVICE);;
+ setDeviceServer(server);
+ }
+
+ public Device(BluetoothDevice bluetoothDevice) {
+ mBluetoothDevice = bluetoothDevice;
+ mServiceInfo = null;
+ mUid = mBluetoothServiceUid;
+ }
+
+ private void setDeviceServer(IMidiDeviceServer server) {
+ if (server != null) {
+ if (mServer != null) {
+ Log.e(TAG, "mServer already set in setDeviceServer");
+ return;
+ }
+ IBinder binder = server.asBinder();
+ try {
+ if (mDeviceInfo == null) {
+ mDeviceInfo = server.getDeviceInfo();
+ }
+ binder.linkToDeath(this, 0);
+ mServer = server;
+ } catch (RemoteException e) {
+ mServer = null;
+ return;
+ }
+ mDevicesByServer.put(binder, this);
+ } else if (mServer != null) {
+ server = mServer;
+ mServer = null;
+
+ IBinder binder = server.asBinder();
+ mDevicesByServer.remove(binder);
+
+ try {
+ server.closeDevice();
+ binder.unlinkToDeath(this, 0);
+ } catch (RemoteException e) {
+ // nothing to do here
+ }
+ }
+
+ if (mDeviceConnections != null) {
+ for (DeviceConnection connection : mDeviceConnections) {
+ connection.notifyClient(server);
+ }
+ }
}
public MidiDeviceInfo getDeviceInfo() {
return mDeviceInfo;
}
+ // only used for bluetooth devices, which are created before we have a MidiDeviceInfo
+ public void setDeviceInfo(MidiDeviceInfo deviceInfo) {
+ mDeviceInfo = deviceInfo;
+ }
+
public MidiDeviceStatus getDeviceStatus() {
return mDeviceStatus;
}
- public void setDeviceStatus(IBinder token, MidiDeviceStatus status) {
+ public void setDeviceStatus(MidiDeviceStatus status) {
mDeviceStatus = status;
-
- if (mDeviceStatusToken == null && token != null) {
- // register a death recipient so we can clear the status when the device dies
- try {
- token.linkToDeath(new IBinder.DeathRecipient() {
- @Override
- public void binderDied() {
- // reset to default status and clear the token
- mDeviceStatus = new MidiDeviceStatus(mDeviceInfo);
- mDeviceStatusToken = null;
- notifyDeviceStatusChanged(Device.this, mDeviceStatus);
- }
- }, 0);
- mDeviceStatusToken = token;
- } catch (RemoteException e) {
- // reset to default status
- mDeviceStatus = new MidiDeviceStatus(mDeviceInfo);
- }
- }
}
public IMidiDeviceServer getDeviceServer() {
@@ -284,11 +382,102 @@ public class MidiService extends IMidiManager.Stub {
return (!mDeviceInfo.isPrivate() || mUid == uid);
}
+ public void addDeviceConnection(DeviceConnection connection) {
+ synchronized (mDeviceConnections) {
+ if (mServer != null) {
+ mDeviceConnections.add(connection);
+ connection.notifyClient(mServer);
+ } else if (mServiceConnection == null &&
+ (mServiceInfo != null || mBluetoothDevice != null)) {
+ mDeviceConnections.add(connection);
+
+ mServiceConnection = new ServiceConnection() {
+ @Override
+ public void onServiceConnected(ComponentName name, IBinder service) {
+ IMidiDeviceServer server = IMidiDeviceServer.Stub.asInterface(service);
+ setDeviceServer(server);
+ }
+
+ @Override
+ public void onServiceDisconnected(ComponentName name) {
+ setDeviceServer(null);
+ mServiceConnection = null;
+ }
+ };
+
+ Intent intent;
+ if (mBluetoothDevice != null) {
+ intent = new Intent(MidiManager.BLUETOOTH_MIDI_SERVICE_INTENT);
+ intent.setComponent(new ComponentName(
+ MidiManager.BLUETOOTH_MIDI_SERVICE_PACKAGE,
+ MidiManager.BLUETOOTH_MIDI_SERVICE_CLASS));
+ intent.putExtra("device", mBluetoothDevice);
+ } else {
+ intent = new Intent(MidiDeviceService.SERVICE_INTERFACE);
+ intent.setComponent(
+ new ComponentName(mServiceInfo.packageName, mServiceInfo.name));
+ }
+
+ if (!mContext.bindService(intent, mServiceConnection,
+ Context.BIND_AUTO_CREATE)) {
+ Log.e(TAG, "Unable to bind service: " + intent);
+ setDeviceServer(null);
+ mServiceConnection = null;
+ }
+ } else {
+ Log.e(TAG, "No way to connect to device in addDeviceConnection");
+ connection.notifyClient(null);
+ }
+ }
+ }
+
+ public void removeDeviceConnection(DeviceConnection connection) {
+ synchronized (mDeviceConnections) {
+ mDeviceConnections.remove(connection);
+
+ if (mDeviceConnections.size() == 0 && mServiceConnection != null) {
+ mContext.unbindService(mServiceConnection);
+ mServiceConnection = null;
+ if (mBluetoothDevice != null) {
+ // Bluetooth devices are ephemeral - remove when no clients exist
+ synchronized (mDevicesByInfo) {
+ closeLocked();
+ }
+ } else {
+ setDeviceServer(null);
+ }
+ }
+ }
+ }
+
+ // synchronize on mDevicesByInfo
+ public void closeLocked() {
+ synchronized (mDeviceConnections) {
+ for (DeviceConnection connection : mDeviceConnections) {
+ connection.getClient().removeDeviceConnection(connection);
+ }
+ mDeviceConnections.clear();
+ }
+ setDeviceServer(null);
+
+ // closed virtual devices should not be removed from mDevicesByInfo
+ // since they can be restarted on demand
+ if (mServiceInfo == null) {
+ removeDeviceLocked(this);
+ } else {
+ mDeviceStatus = new MidiDeviceStatus(mDeviceInfo);
+ }
+
+ if (mBluetoothDevice != null) {
+ mBluetoothDevices.remove(mBluetoothDevice);
+ }
+ }
+
+ @Override
public void binderDied() {
+ Log.d(TAG, "Device died: " + this);
synchronized (mDevicesByInfo) {
- if (mDevicesByInfo.remove(mDeviceInfo) != null) {
- removeDeviceLocked(this);
- }
+ closeLocked();
}
}
@@ -300,10 +489,56 @@ public class MidiService extends IMidiManager.Stub {
sb.append(mDeviceStatus);
sb.append(" UID: ");
sb.append(mUid);
+ sb.append(" DeviceConnection count: ");
+ sb.append(mDeviceConnections.size());
+ sb.append(" mServiceConnection: ");
+ sb.append(mServiceConnection);
return sb.toString();
}
}
+ // Represents a connection between a client and a device
+ private final class DeviceConnection {
+ private final IBinder mToken = new Binder();
+ private final Device mDevice;
+ private final Client mClient;
+ private IMidiDeviceOpenCallback mCallback;
+
+ public DeviceConnection(Device device, Client client, IMidiDeviceOpenCallback callback) {
+ mDevice = device;
+ mClient = client;
+ mCallback = callback;
+ }
+
+ public Device getDevice() {
+ return mDevice;
+ }
+
+ public Client getClient() {
+ return mClient;
+ }
+
+ public IBinder getToken() {
+ return mToken;
+ }
+
+ public void notifyClient(IMidiDeviceServer deviceServer) {
+ if (mCallback != null) {
+ try {
+ mCallback.onDeviceOpened(deviceServer, (deviceServer == null ? null : mToken));
+ } catch (RemoteException e) {
+ // Client binderDied() method will do necessary cleanup, so nothing to do here
+ }
+ mCallback = null;
+ }
+ }
+
+ @Override
+ public String toString() {
+ return "DeviceConnection Device ID: " + mDevice.getDeviceInfo().getId();
+ }
+ }
+
public MidiService(Context context) {
mContext = context;
mPackageManager = context.getPackageManager();
@@ -321,6 +556,18 @@ public class MidiService extends IMidiManager.Stub {
}
}
}
+
+ PackageInfo info;
+ try {
+ info = mPackageManager.getPackageInfo(MidiManager.BLUETOOTH_MIDI_SERVICE_PACKAGE, 0);
+ } catch (PackageManager.NameNotFoundException e) {
+ info = null;
+ }
+ if (info != null && info.applicationInfo != null) {
+ mBluetoothServiceUid = info.applicationInfo.uid;
+ } else {
+ mBluetoothServiceUid = -1;
+ }
}
@Override
@@ -355,18 +602,61 @@ public class MidiService extends IMidiManager.Stub {
}
@Override
- public IMidiDeviceServer openDevice(IBinder token, MidiDeviceInfo deviceInfo) {
- Device device = mDevicesByInfo.get(deviceInfo);
- if (device == null) {
- Log.e(TAG, "device not found in openDevice: " + deviceInfo);
- return null;
+ public void openDevice(IBinder token, MidiDeviceInfo deviceInfo,
+ IMidiDeviceOpenCallback callback) {
+ Client client = getClient(token);
+ if (client == null) return;
+
+ Device device;
+ synchronized (mDevicesByInfo) {
+ device = mDevicesByInfo.get(deviceInfo);
+ if (device == null) {
+ throw new IllegalArgumentException("device does not exist: " + deviceInfo);
+ }
+ if (!device.isUidAllowed(Binder.getCallingUid())) {
+ throw new SecurityException("Attempt to open private device with wrong UID");
+ }
+ }
+
+ // clear calling identity so bindService does not fail
+ long identity = Binder.clearCallingIdentity();
+ try {
+ client.addDeviceConnection(device, callback);
+ } finally {
+ Binder.restoreCallingIdentity(identity);
+ }
+ }
+
+ @Override
+ public void openBluetoothDevice(IBinder token, BluetoothDevice bluetoothDevice,
+ IMidiDeviceOpenCallback callback) {
+ Client client = getClient(token);
+ if (client == null) return;
+
+ // Bluetooth devices are created on demand
+ Device device;
+ synchronized (mDevicesByInfo) {
+ device = mBluetoothDevices.get(bluetoothDevice);
+ if (device == null) {
+ device = new Device(bluetoothDevice);
+ mBluetoothDevices.put(bluetoothDevice, device);
+ }
}
- if (!device.isUidAllowed(Binder.getCallingUid())) {
- throw new SecurityException("Attempt to open private device with wrong UID");
+ // clear calling identity so bindService does not fail
+ long identity = Binder.clearCallingIdentity();
+ try {
+ client.addDeviceConnection(device, callback);
+ } finally {
+ Binder.restoreCallingIdentity(identity);
}
+ }
- return device.getDeviceServer();
+ @Override
+ public void closeDevice(IBinder clientToken, IBinder deviceToken) {
+ Client client = getClient(clientToken);
+ if (client == null) return;
+ client.removeDeviceConnection(deviceToken);
}
@Override
@@ -376,6 +666,8 @@ public class MidiService extends IMidiManager.Stub {
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");
}
synchronized (mDevicesByInfo) {
@@ -389,8 +681,7 @@ public class MidiService extends IMidiManager.Stub {
synchronized (mDevicesByInfo) {
Device device = mDevicesByServer.get(server.asBinder());
if (device != null) {
- mDevicesByInfo.remove(device.getDeviceInfo());
- removeDeviceLocked(device);
+ device.closeLocked();
}
}
}
@@ -420,19 +711,16 @@ public class MidiService extends IMidiManager.Stub {
}
@Override
- public void setDeviceStatus(IBinder token, MidiDeviceStatus status) {
- MidiDeviceInfo deviceInfo = status.getDeviceInfo();
- Device device = mDevicesByInfo.get(deviceInfo);
- if (device == null) {
- // Just return quietly here if device no longer exists
- return;
- }
- if (Binder.getCallingUid() != device.getUid()) {
- throw new SecurityException("setDeviceStatus() caller UID " + Binder.getCallingUid()
- + " does not match device's UID " + device.getUid());
+ public void setDeviceStatus(IMidiDeviceServer server, MidiDeviceStatus status) {
+ Device device = mDevicesByServer.get(server.asBinder());
+ if (device != null) {
+ if (Binder.getCallingUid() != device.getUid()) {
+ throw new SecurityException("setDeviceStatus() caller UID " + Binder.getCallingUid()
+ + " does not match device's UID " + device.getUid());
+ }
+ device.setDeviceStatus(status);
+ notifyDeviceStatusChanged(device, status);
}
- device.setDeviceStatus(token, status);
- notifyDeviceStatusChanged(device, status);
}
private void notifyDeviceStatusChanged(Device device, MidiDeviceStatus status) {
@@ -452,18 +740,24 @@ public class MidiService extends IMidiManager.Stub {
int id = mNextDeviceId++;
MidiDeviceInfo deviceInfo = new MidiDeviceInfo(type, id, numInputPorts, numOutputPorts,
inputPortNames, outputPortNames, properties, isPrivate);
- Device device = new Device(server, deviceInfo, serviceInfo, uid);
- if (server != null) {
- IBinder binder = server.asBinder();
- try {
- binder.linkToDeath(device, 0);
- } catch (RemoteException e) {
- return null;
+ Device device = null;
+ BluetoothDevice bluetoothDevice = null;
+ if (type == MidiDeviceInfo.TYPE_BLUETOOTH) {
+ bluetoothDevice = (BluetoothDevice)properties.getParcelable(
+ MidiDeviceInfo.PROPERTY_BLUETOOTH_DEVICE);
+ device = mBluetoothDevices.get(bluetoothDevice);
+ if (device != null) {
+ device.setDeviceInfo(deviceInfo);
}
- mDevicesByServer.put(binder, device);
+ }
+ if (device == null) {
+ device = new Device(server, deviceInfo, serviceInfo, uid);
}
mDevicesByInfo.put(deviceInfo, device);
+ if (bluetoothDevice != null) {
+ mBluetoothDevices.put(bluetoothDevice, device);
+ }
synchronized (mClients) {
for (Client c : mClients.values()) {
@@ -478,8 +772,9 @@ public class MidiService extends IMidiManager.Stub {
private void removeDeviceLocked(Device device) {
IMidiDeviceServer server = device.getDeviceServer();
if (server != null) {
- mDevicesByServer.remove(server);
+ mDevicesByServer.remove(server.asBinder());
}
+ mDevicesByInfo.remove(device.getDeviceInfo());
synchronized (mClients) {
for (Client c : mClients.values()) {
@@ -516,6 +811,15 @@ public class MidiService extends IMidiManager.Stub {
MidiDeviceService.SERVICE_INTERFACE);
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 numInputPorts = 0;
int numOutputPorts = 0;