am ac4c704b: am 383ce580: Merge "MidiManager: proxy all requests to open devices through MidiService" into mnc-dev

* commit 'ac4c704b4ef233f12c649f6e20794dd1aeb28a1d':
  MidiManager: proxy all requests to open devices through MidiService
This commit is contained in:
Mike Lockwood
2015-06-08 20:01:57 +00:00
committed by Android Git Automerger
16 changed files with 521 additions and 175 deletions

View File

@@ -343,6 +343,7 @@ LOCAL_SRC_FILES += \
media/java/android/media/IVolumeController.aidl \ media/java/android/media/IVolumeController.aidl \
media/java/android/media/audiopolicy/IAudioPolicyCallback.aidl \ media/java/android/media/audiopolicy/IAudioPolicyCallback.aidl \
media/java/android/media/midi/IMidiDeviceListener.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/IMidiDeviceServer.aidl \
media/java/android/media/midi/IMidiManager.aidl \ media/java/android/media/midi/IMidiManager.aidl \
media/java/android/media/projection/IMediaProjection.aidl \ media/java/android/media/projection/IMediaProjection.aidl \

View File

@@ -28,6 +28,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_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_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_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_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_NOTIFICATION_LISTENER_SERVICE = "android.permission.BIND_NOTIFICATION_LISTENER_SERVICE";
field public static final java.lang.String BIND_PRINT_SERVICE = "android.permission.BIND_PRINT_SERVICE"; field public static final java.lang.String BIND_PRINT_SERVICE = "android.permission.BIND_PRINT_SERVICE";
@@ -17360,6 +17361,7 @@ package android.media.midi {
method public final android.media.midi.MidiDeviceInfo getDeviceInfo(); method public final android.media.midi.MidiDeviceInfo getDeviceInfo();
method public final android.media.midi.MidiReceiver[] getOutputPortReceivers(); method public final android.media.midi.MidiReceiver[] getOutputPortReceivers();
method public android.os.IBinder onBind(android.content.Intent); method public android.os.IBinder onBind(android.content.Intent);
method public void onClose();
method public void onDeviceStatusChanged(android.media.midi.MidiDeviceStatus); method public void onDeviceStatusChanged(android.media.midi.MidiDeviceStatus);
method public abstract android.media.midi.MidiReceiver[] onGetInputPortReceivers(); method public abstract android.media.midi.MidiReceiver[] onGetInputPortReceivers();
field public static final java.lang.String SERVICE_INTERFACE = "android.media.midi.MidiDeviceService"; field public static final java.lang.String SERVICE_INTERFACE = "android.media.midi.MidiDeviceService";

View File

@@ -40,6 +40,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_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_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_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_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_NOTIFICATION_LISTENER_SERVICE = "android.permission.BIND_NOTIFICATION_LISTENER_SERVICE";
field public static final java.lang.String BIND_PRINT_SERVICE = "android.permission.BIND_PRINT_SERVICE"; field public static final java.lang.String BIND_PRINT_SERVICE = "android.permission.BIND_PRINT_SERVICE";
@@ -18672,6 +18673,7 @@ package android.media.midi {
method public final android.media.midi.MidiDeviceInfo getDeviceInfo(); method public final android.media.midi.MidiDeviceInfo getDeviceInfo();
method public final android.media.midi.MidiReceiver[] getOutputPortReceivers(); method public final android.media.midi.MidiReceiver[] getOutputPortReceivers();
method public android.os.IBinder onBind(android.content.Intent); method public android.os.IBinder onBind(android.content.Intent);
method public void onClose();
method public void onDeviceStatusChanged(android.media.midi.MidiDeviceStatus); method public void onDeviceStatusChanged(android.media.midi.MidiDeviceStatus);
method public abstract android.media.midi.MidiReceiver[] onGetInputPortReceivers(); method public abstract android.media.midi.MidiReceiver[] onGetInputPortReceivers();
field public static final java.lang.String SERVICE_INTERFACE = "android.media.midi.MidiDeviceService"; field public static final java.lang.String SERVICE_INTERFACE = "android.media.midi.MidiDeviceService";

View File

@@ -691,7 +691,7 @@ final class SystemServiceRegistry {
@Override @Override
public MidiManager createService(ContextImpl ctx) { public MidiManager createService(ContextImpl ctx) {
IBinder b = ServiceManager.getService(Context.MIDI_SERVICE); 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, registerService(Context.RADIO_SERVICE, RadioManager.class,

View File

@@ -1827,6 +1827,11 @@
<permission android:name="android.permission.BIND_INPUT_METHOD" <permission android:name="android.permission.BIND_INPUT_METHOD"
android:protectionLevel="signature" /> 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}, <!-- Must be required by an {@link android.accessibilityservice.AccessibilityService},
to ensure that only the system can bind to it. --> to ensure that only the system can bind to it. -->
<permission android:name="android.permission.BIND_ACCESSIBILITY_SERVICE" <permission android:name="android.permission.BIND_ACCESSIBILITY_SERVICE"

View File

@@ -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);
}

View File

@@ -25,6 +25,7 @@ interface IMidiDeviceServer
ParcelFileDescriptor openInputPort(IBinder token, int portNumber); ParcelFileDescriptor openInputPort(IBinder token, int portNumber);
ParcelFileDescriptor openOutputPort(IBinder token, int portNumber); ParcelFileDescriptor openOutputPort(IBinder token, int portNumber);
void closePort(IBinder token); void closePort(IBinder token);
void closeDevice();
// connects the input port pfd to the specified output port // connects the input port pfd to the specified output port
void connectPorts(IBinder token, in ParcelFileDescriptor pfd, int outputPortNumber); void connectPorts(IBinder token, in ParcelFileDescriptor pfd, int outputPortNumber);

View File

@@ -16,7 +16,10 @@
package android.media.midi; package android.media.midi;
import android.bluetooth.BluetoothDevice;
import android.media.midi.IMidiDeviceListener; import android.media.midi.IMidiDeviceListener;
import android.media.midi.IMidiDeviceOpenCallback;
import android.media.midi.IMidiDeviceServer;
import android.media.midi.IMidiDeviceServer; import android.media.midi.IMidiDeviceServer;
import android.media.midi.MidiDeviceInfo; import android.media.midi.MidiDeviceInfo;
import android.media.midi.MidiDeviceStatus; import android.media.midi.MidiDeviceStatus;
@@ -29,11 +32,13 @@ interface IMidiManager
MidiDeviceInfo[] getDevices(); MidiDeviceInfo[] getDevices();
// for device creation & removal notifications // for device creation & removal notifications
void registerListener(IBinder token, in IMidiDeviceListener listener); void registerListener(IBinder clientToken, in IMidiDeviceListener listener);
void unregisterListener(IBinder token, in IMidiDeviceListener listener); void unregisterListener(IBinder clientToken, in IMidiDeviceListener listener);
// for opening built-in MIDI devices void openDevice(IBinder clientToken, in MidiDeviceInfo device, in IMidiDeviceOpenCallback callback);
IMidiDeviceServer openDevice(IBinder token, in MidiDeviceInfo device); void openBluetoothDevice(IBinder clientToken, in BluetoothDevice bluetoothDevice,
in IMidiDeviceOpenCallback callback);
void closeDevice(IBinder clientToken, IBinder deviceToken);
// for registering built-in MIDI devices // for registering built-in MIDI devices
MidiDeviceInfo registerDeviceServer(in IMidiDeviceServer server, int numInputPorts, MidiDeviceInfo registerDeviceServer(in IMidiDeviceServer server, int numInputPorts,
@@ -52,5 +57,5 @@ interface IMidiManager
// used by MIDI devices to report their status // used by MIDI devices to report their status
// the token is used by MidiService for death notification // the token is used by MidiService for death notification
void setDeviceStatus(IBinder token, in MidiDeviceStatus status); void setDeviceStatus(in IMidiDeviceServer server, in MidiDeviceStatus status);
} }

View File

@@ -16,8 +16,6 @@
package android.media.midi; package android.media.midi;
import android.content.Context;
import android.content.ServiceConnection;
import android.os.Binder; import android.os.Binder;
import android.os.IBinder; import android.os.IBinder;
import android.os.ParcelFileDescriptor; import android.os.ParcelFileDescriptor;
@@ -40,9 +38,9 @@ public final class MidiDevice implements Closeable {
private final MidiDeviceInfo mDeviceInfo; private final MidiDeviceInfo mDeviceInfo;
private final IMidiDeviceServer mDeviceServer; private final IMidiDeviceServer mDeviceServer;
private Context mContext; private final IMidiManager mMidiManager;
private ServiceConnection mServiceConnection; private final IBinder mClientToken;
private final IBinder mDeviceToken;
private final CloseGuard mGuard = CloseGuard.get(); 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, /* package */ MidiDevice(MidiDeviceInfo deviceInfo, IMidiDeviceServer server,
Context context, ServiceConnection serviceConnection) { IMidiManager midiManager, IBinder clientToken, IBinder deviceToken) {
mDeviceInfo = deviceInfo; mDeviceInfo = deviceInfo;
mDeviceServer = server; mDeviceServer = server;
mContext = context; mMidiManager = midiManager;
mServiceConnection = serviceConnection; mClientToken = clientToken;
mDeviceToken = deviceToken;
mGuard.open("close"); mGuard.open("close");
} }
@@ -170,10 +165,10 @@ public final class MidiDevice implements Closeable {
public void close() throws IOException { public void close() throws IOException {
synchronized (mGuard) { synchronized (mGuard) {
mGuard.close(); mGuard.close();
if (mContext != null && mServiceConnection != null) { try {
mContext.unbindService(mServiceConnection); mMidiManager.closeDevice(mClientToken, mDeviceToken);
mContext = null; } catch (RemoteException e) {
mServiceConnection = null; Log.e(TAG, "RemoteException in closeDevice");
} }
} }
} }

View File

@@ -65,7 +65,6 @@ public final class MidiDeviceServer implements Closeable {
// for reporting device status // for reporting device status
private final IBinder mDeviceStatusToken = new Binder();
private final boolean[] mInputPortOpen; private final boolean[] mInputPortOpen;
private final int[] mOutputPortOpenCount; private final int[] mOutputPortOpenCount;
@@ -81,6 +80,11 @@ public final class MidiDeviceServer implements Closeable {
* @param status the {@link MidiDeviceStatus} for the device * @param status the {@link MidiDeviceStatus} for the device
*/ */
public void onDeviceStatusChanged(MidiDeviceServer server, MidiDeviceStatus status); 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 { abstract private class PortClient implements IBinder.DeathRecipient {
@@ -241,6 +245,14 @@ public final class MidiDeviceServer implements Closeable {
} }
} }
@Override
public void closeDevice() {
if (mCallback != null) {
mCallback.onClose();
}
IoUtils.closeQuietly(MidiDeviceServer.this);
}
@Override @Override
public void connectPorts(IBinder token, ParcelFileDescriptor pfd, public void connectPorts(IBinder token, ParcelFileDescriptor pfd,
int outputPortNumber) { int outputPortNumber) {
@@ -305,7 +317,7 @@ public final class MidiDeviceServer implements Closeable {
mCallback.onDeviceStatusChanged(this, status); mCallback.onDeviceStatusChanged(this, status);
} }
try { try {
mMidiManager.setDeviceStatus(mDeviceStatusToken, status); mMidiManager.setDeviceStatus(mServer, status);
} catch (RemoteException e) { } catch (RemoteException e) {
Log.e(TAG, "RemoteException in updateDeviceStatus"); Log.e(TAG, "RemoteException in updateDeviceStatus");
} finally { } finally {

View File

@@ -59,6 +59,11 @@ abstract public class MidiDeviceService extends Service {
public void onDeviceStatusChanged(MidiDeviceServer server, MidiDeviceStatus status) { public void onDeviceStatusChanged(MidiDeviceServer server, MidiDeviceStatus status) {
MidiDeviceService.this.onDeviceStatusChanged(status); MidiDeviceService.this.onDeviceStatusChanged(status);
} }
@Override
public void onClose() {
MidiDeviceService.this.onClose();
}
}; };
@Override @Override
@@ -125,6 +130,12 @@ abstract public class MidiDeviceService extends Service {
public void onDeviceStatusChanged(MidiDeviceStatus status) { public void onDeviceStatusChanged(MidiDeviceStatus status) {
} }
/**
* Called to notify when our device has been closed by all its clients
*/
public void onClose() {
}
@Override @Override
public IBinder onBind(Intent intent) { public IBinder onBind(Intent intent) {
if (SERVICE_INTERFACE.equals(intent.getAction()) && mServer != null) { if (SERVICE_INTERFACE.equals(intent.getAction()) && mServer != null) {

View File

@@ -17,11 +17,6 @@
package android.media.midi; package android.media.midi;
import android.bluetooth.BluetoothDevice; 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.Binder;
import android.os.IBinder; import android.os.IBinder;
import android.os.Bundle; import android.os.Bundle;
@@ -52,16 +47,17 @@ public final class MidiManager {
/** /**
* BluetoothMidiService package name * 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 * BluetoothMidiService class name
* @hide
*/ */
private static final String BLUETOOTH_MIDI_SERVICE_CLASS = public static final String BLUETOOTH_MIDI_SERVICE_CLASS =
"com.android.bluetoothmidiservice.BluetoothMidiService"; "com.android.bluetoothmidiservice.BluetoothMidiService";
private final Context mContext;
private final IMidiManager mService; private final IMidiManager mService;
private final IBinder mToken = new Binder(); private final IBinder mToken = new Binder();
@@ -166,8 +162,7 @@ public final class MidiManager {
/** /**
* @hide * @hide
*/ */
public MidiManager(Context context, IMidiManager service) { public MidiManager(IMidiManager service) {
mContext = context;
mService = service; mService = service;
} }
@@ -237,7 +232,7 @@ public final class MidiManager {
* Opens a MIDI device for reading and writing. * Opens a MIDI device for reading and writing.
* *
* @param deviceInfo a {@link android.media.midi.MidiDeviceInfo} to open * @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 * to receive the result
* @param handler the {@link android.os.Handler Handler} that will be used for delivering * @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 * 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, public void openDevice(MidiDeviceInfo deviceInfo, OnDeviceOpenedListener listener,
Handler handler) { Handler handler) {
MidiDevice device = null; final MidiDeviceInfo deviceInfoF = deviceInfo;
try { final OnDeviceOpenedListener listenerF = listener;
IMidiDeviceServer server = mService.openDevice(mToken, deviceInfo); final Handler handlerF = handler;
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);
}
@Override IMidiDeviceOpenCallback callback = new IMidiDeviceOpenCallback.Stub() {
public void onServiceDisconnected(ComponentName name) { @Override
// FIXME - anything to do here? public void onDeviceOpened(IMidiDeviceServer server, IBinder deviceToken) {
} MidiDevice device;
}, if (server != null) {
Context.BIND_AUTO_CREATE)) device = new MidiDevice(deviceInfoF, server, mService, mToken, deviceToken);
{ } else {
// return immediately to avoid calling sendOpenDeviceResponse below device = null;
return;
} else {
Log.e(TAG, "Unable to bind service: " + intent);
}
} }
} else { sendOpenDeviceResponse(device, listenerF, handlerF);
device = new MidiDevice(deviceInfo, server);
} }
};
try {
mService.openDevice(mToken, deviceInfo, callback);
} catch (RemoteException e) { } catch (RemoteException e) {
Log.e(TAG, "RemoteException in openDevice"); 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 * the result. If handler is null, then the thread used for the
* listener is unspecified. * listener is unspecified.
*/ */
public void openBluetoothDevice(final BluetoothDevice bluetoothDevice, public void openBluetoothDevice(BluetoothDevice bluetoothDevice,
final OnDeviceOpenedListener listener, final Handler handler) { OnDeviceOpenedListener listener, Handler handler) {
Intent intent = new Intent(BLUETOOTH_MIDI_SERVICE_INTENT); final OnDeviceOpenedListener listenerF = listener;
intent.setComponent(new ComponentName(BLUETOOTH_MIDI_SERVICE_PACKAGE, final Handler handlerF = handler;
BLUETOOTH_MIDI_SERVICE_CLASS));
intent.putExtra("device", bluetoothDevice); IMidiDeviceOpenCallback callback = new IMidiDeviceOpenCallback.Stub() {
if (!mContext.bindService(intent, @Override
new ServiceConnection() { public void onDeviceOpened(IMidiDeviceServer server, IBinder deviceToken) {
@Override MidiDevice device = null;
public void onServiceConnected(ComponentName name, IBinder binder) { if (server != null) {
IMidiDeviceServer server =
IMidiDeviceServer.Stub.asInterface(binder);
try { try {
// fetch MidiDeviceInfo from the server // fetch MidiDeviceInfo from the server
MidiDeviceInfo deviceInfo = server.getDeviceInfo(); MidiDeviceInfo deviceInfo = server.getDeviceInfo();
MidiDevice device = new MidiDevice(deviceInfo, server, mContext, this); device = new MidiDevice(deviceInfo, server, mService, mToken, deviceToken);
sendOpenDeviceResponse(device, listener, handler); sendOpenDeviceResponse(device, listenerF, handlerF);
} catch (RemoteException e) { } catch (RemoteException e) {
Log.e(TAG, "remote exception in onServiceConnected"); Log.e(TAG, "remote exception in getDeviceInfo()");
sendOpenDeviceResponse(null, listener, handler);
} }
} }
sendOpenDeviceResponse(device, listenerF, handlerF);
}
};
@Override try {
public void onServiceDisconnected(ComponentName name) { mService.openBluetoothDevice(mToken, bluetoothDevice, callback);
// FIXME - anything to do here? } catch (RemoteException e) {
} Log.e(TAG, "RemoteException in openDevice");
},
Context.BIND_AUTO_CREATE))
{
Log.e(TAG, "Unable to bind service: " + intent);
sendOpenDeviceResponse(null, listener, handler);
} }
} }

View File

@@ -241,7 +241,8 @@ messages.</p>
<p>An app can provide a MIDI Service that can be used by other apps. For example, <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> <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> AndroidManifest.xml file.</p>
<pre class=prettyprint> <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;intent-filter>
&lt;action android:name="android.media.midi.MidiDeviceService" /> &lt;action android:name="android.media.midi.MidiDeviceService" />
&lt;/intent-filter> &lt;/intent-filter>

View File

@@ -8,7 +8,8 @@
<application <application
android:label="@string/app_name"> android:label="@string/app_name">
<service android:name="BluetoothMidiService"> <service android:name="BluetoothMidiService"
android:permission="android.permission.BIND_MIDI_DEVICE_SERVICE">
<intent-filter> <intent-filter>
<action android:name="android.media.midi.BluetoothMidiService" /> <action android:name="android.media.midi.BluetoothMidiService" />
</intent-filter> </intent-filter>

View File

@@ -24,10 +24,11 @@ import android.bluetooth.BluetoothGattDescriptor;
import android.bluetooth.BluetoothGattService; import android.bluetooth.BluetoothGattService;
import android.bluetooth.BluetoothProfile; import android.bluetooth.BluetoothProfile;
import android.content.Context; 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.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.Bundle;
import android.os.IBinder; import android.os.IBinder;
import android.util.Log; import android.util.Log;
@@ -81,6 +82,18 @@ public final class BluetoothMidiDevice {
private final BluetoothPacketDecoder mPacketDecoder private final BluetoothPacketDecoder mPacketDecoder
= new BluetoothPacketDecoder(MAX_PACKET_SIZE); = 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() { private final BluetoothGattCallback mGattCallback = new BluetoothGattCallback() {
@Override @Override
public void onConnectionStateChange(BluetoothGatt gatt, int status, public void onConnectionStateChange(BluetoothGatt gatt, int status,
@@ -213,7 +226,7 @@ public final class BluetoothMidiDevice {
inputPortReceivers[0] = mEventScheduler.getReceiver(); inputPortReceivers[0] = mEventScheduler.getReceiver();
mDeviceServer = mMidiManager.createDeviceServer(inputPortReceivers, 1, mDeviceServer = mMidiManager.createDeviceServer(inputPortReceivers, 1,
null, null, properties, MidiDeviceInfo.TYPE_BLUETOOTH, null); null, null, properties, MidiDeviceInfo.TYPE_BLUETOOTH, mDeviceServerCallback);
mOutputReceiver = mDeviceServer.getOutputPortReceivers()[0]; mOutputReceiver = mDeviceServer.getOutputPortReceivers()[0];
@@ -248,11 +261,12 @@ public final class BluetoothMidiDevice {
private void close() { private void close() {
synchronized (mBluetoothDevice) { synchronized (mBluetoothDevice) {
mEventScheduler.close(); mEventScheduler.close();
mService.deviceClosed(mBluetoothDevice);
if (mDeviceServer != null) { if (mDeviceServer != null) {
IoUtils.closeQuietly(mDeviceServer); IoUtils.closeQuietly(mDeviceServer);
mDeviceServer = null; mDeviceServer = null;
mService.deviceClosed(mBluetoothDevice);
} }
if (mBluetoothGatt != null) { if (mBluetoothGatt != null) {
mBluetoothGatt.close(); mBluetoothGatt.close();

View File

@@ -16,8 +16,11 @@
package com.android.server.midi; package com.android.server.midi;
import android.bluetooth.BluetoothDevice;
import android.content.ComponentName;
import android.content.Context; import android.content.Context;
import android.content.Intent; import android.content.Intent;
import android.content.ServiceConnection;
import android.content.pm.ApplicationInfo; import android.content.pm.ApplicationInfo;
import android.content.pm.PackageInfo; import android.content.pm.PackageInfo;
import android.content.pm.PackageManager; import android.content.pm.PackageManager;
@@ -25,11 +28,13 @@ import android.content.pm.ResolveInfo;
import android.content.pm.ServiceInfo; import android.content.pm.ServiceInfo;
import android.content.res.XmlResourceParser; import android.content.res.XmlResourceParser;
import android.media.midi.IMidiDeviceListener; import android.media.midi.IMidiDeviceListener;
import android.media.midi.IMidiDeviceOpenCallback;
import android.media.midi.IMidiDeviceServer; import android.media.midi.IMidiDeviceServer;
import android.media.midi.IMidiManager; import android.media.midi.IMidiManager;
import android.media.midi.MidiDeviceInfo; import android.media.midi.MidiDeviceInfo;
import android.media.midi.MidiDeviceService; import android.media.midi.MidiDeviceService;
import android.media.midi.MidiDeviceStatus; import android.media.midi.MidiDeviceStatus;
import android.media.midi.MidiManager;
import android.os.Binder; import android.os.Binder;
import android.os.Bundle; import android.os.Bundle;
import android.os.IBinder; import android.os.IBinder;
@@ -78,6 +83,10 @@ public class MidiService extends IMidiManager.Stub {
private final HashMap<MidiDeviceInfo, Device> mDevicesByInfo private final HashMap<MidiDeviceInfo, Device> mDevicesByInfo
= new HashMap<MidiDeviceInfo, Device>(); = 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 // list of all devices, keyed by IMidiDeviceServer
private final HashMap<IBinder, Device> mDevicesByServer = new HashMap<IBinder, Device>(); private final HashMap<IBinder, Device> mDevicesByServer = new HashMap<IBinder, Device>();
@@ -86,6 +95,9 @@ public class MidiService extends IMidiManager.Stub {
private final PackageManager mPackageManager; private final PackageManager mPackageManager;
// UID of BluetoothMidiService
private final int mBluetoothServiceUid;
// PackageMonitor for listening to package changes // PackageMonitor for listening to package changes
private final PackageMonitor mPackageMonitor = new PackageMonitor() { private final PackageMonitor mPackageMonitor = new PackageMonitor() {
@Override @Override
@@ -115,6 +127,9 @@ public class MidiService extends IMidiManager.Stub {
// List of all receivers for this client // List of all receivers for this client
private final ArrayList<IMidiDeviceListener> mListeners private final ArrayList<IMidiDeviceListener> mListeners
= new ArrayList<IMidiDeviceListener>(); = 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) { public Client(IBinder token) {
mToken = token; mToken = token;
@@ -132,8 +147,33 @@ public class MidiService extends IMidiManager.Stub {
public void removeListener(IMidiDeviceListener listener) { public void removeListener(IMidiDeviceListener listener) {
mListeners.remove(listener); mListeners.remove(listener);
if (mListeners.size() == 0) { if (mListeners.size() == 0 && mDeviceConnections.size() == 0) {
removeClient(mToken); 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() { public void binderDied() {
removeClient(mToken); Log.d(TAG, "Client died: " + this);
close();
} }
@Override @Override
@@ -190,6 +243,12 @@ public class MidiService extends IMidiManager.Stub {
sb.append(mPid); sb.append(mPid);
sb.append(" listener count: "); sb.append(" listener count: ");
sb.append(mListeners.size()); 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(); 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 class Device implements IBinder.DeathRecipient {
private final IMidiDeviceServer mServer; private IMidiDeviceServer mServer;
private final MidiDeviceInfo mDeviceInfo; private MidiDeviceInfo mDeviceInfo;
private final BluetoothDevice mBluetoothDevice;
private MidiDeviceStatus mDeviceStatus; private MidiDeviceStatus mDeviceStatus;
private IBinder mDeviceStatusToken;
// ServiceInfo for the device's MidiDeviceServer implementation (virtual devices only) // ServiceInfo for the device's MidiDeviceServer implementation (virtual devices only)
private final ServiceInfo mServiceInfo; private final ServiceInfo mServiceInfo;
// UID of device implementation // UID of device implementation
private final int mUid; 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, public Device(IMidiDeviceServer server, MidiDeviceInfo deviceInfo,
ServiceInfo serviceInfo, int uid) { ServiceInfo serviceInfo, int uid) {
mServer = server;
mDeviceInfo = deviceInfo; mDeviceInfo = deviceInfo;
mServiceInfo = serviceInfo; mServiceInfo = serviceInfo;
mUid = uid; 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() { public MidiDeviceInfo getDeviceInfo() {
return mDeviceInfo; 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() { public MidiDeviceStatus getDeviceStatus() {
return mDeviceStatus; return mDeviceStatus;
} }
public void setDeviceStatus(IBinder token, MidiDeviceStatus status) { public void setDeviceStatus(MidiDeviceStatus status) {
mDeviceStatus = 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() { public IMidiDeviceServer getDeviceServer() {
@@ -284,14 +382,105 @@ public class MidiService extends IMidiManager.Stub {
return (!mDeviceInfo.isPrivate() || mUid == uid); return (!mDeviceInfo.isPrivate() || mUid == uid);
} }
public void binderDied() { public void addDeviceConnection(DeviceConnection connection) {
synchronized (mDevicesByInfo) { synchronized (mDeviceConnections) {
if (mDevicesByInfo.remove(mDeviceInfo) != null) { if (mServer != null) {
removeDeviceLocked(this); 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) {
closeLocked();
}
}
@Override @Override
public String toString() { public String toString() {
StringBuilder sb = new StringBuilder("Device Info: "); StringBuilder sb = new StringBuilder("Device Info: ");
@@ -300,10 +489,56 @@ public class MidiService extends IMidiManager.Stub {
sb.append(mDeviceStatus); sb.append(mDeviceStatus);
sb.append(" UID: "); sb.append(" UID: ");
sb.append(mUid); sb.append(mUid);
sb.append(" DeviceConnection count: ");
sb.append(mDeviceConnections.size());
sb.append(" mServiceConnection: ");
sb.append(mServiceConnection);
return sb.toString(); 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) { public MidiService(Context context) {
mContext = context; mContext = context;
mPackageManager = context.getPackageManager(); 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 @Override
@@ -355,18 +602,61 @@ public class MidiService extends IMidiManager.Stub {
} }
@Override @Override
public IMidiDeviceServer openDevice(IBinder token, MidiDeviceInfo deviceInfo) { public void openDevice(IBinder token, MidiDeviceInfo deviceInfo,
Device device = mDevicesByInfo.get(deviceInfo); IMidiDeviceOpenCallback callback) {
if (device == null) { Client client = getClient(token);
Log.e(TAG, "device not found in openDevice: " + deviceInfo); if (client == null) return;
return null;
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");
}
} }
if (!device.isUidAllowed(Binder.getCallingUid())) { // clear calling identity so bindService does not fail
throw new SecurityException("Attempt to open private device with wrong UID"); 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);
}
} }
return device.getDeviceServer(); // clear calling identity so bindService does not fail
long identity = Binder.clearCallingIdentity();
try {
client.addDeviceConnection(device, callback);
} finally {
Binder.restoreCallingIdentity(identity);
}
}
@Override
public void closeDevice(IBinder clientToken, IBinder deviceToken) {
Client client = getClient(clientToken);
if (client == null) return;
client.removeDeviceConnection(deviceToken);
} }
@Override @Override
@@ -376,6 +666,8 @@ public class MidiService extends IMidiManager.Stub {
int uid = Binder.getCallingUid(); int uid = Binder.getCallingUid();
if (type == MidiDeviceInfo.TYPE_USB && uid != Process.SYSTEM_UID) { if (type == MidiDeviceInfo.TYPE_USB && uid != Process.SYSTEM_UID) {
throw new SecurityException("only system can create USB devices"); 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) { synchronized (mDevicesByInfo) {
@@ -389,8 +681,7 @@ public class MidiService extends IMidiManager.Stub {
synchronized (mDevicesByInfo) { synchronized (mDevicesByInfo) {
Device device = mDevicesByServer.get(server.asBinder()); Device device = mDevicesByServer.get(server.asBinder());
if (device != null) { if (device != null) {
mDevicesByInfo.remove(device.getDeviceInfo()); device.closeLocked();
removeDeviceLocked(device);
} }
} }
} }
@@ -420,19 +711,16 @@ public class MidiService extends IMidiManager.Stub {
} }
@Override @Override
public void setDeviceStatus(IBinder token, MidiDeviceStatus status) { public void setDeviceStatus(IMidiDeviceServer server, MidiDeviceStatus status) {
MidiDeviceInfo deviceInfo = status.getDeviceInfo(); Device device = mDevicesByServer.get(server.asBinder());
Device device = mDevicesByInfo.get(deviceInfo); if (device != null) {
if (device == null) { if (Binder.getCallingUid() != device.getUid()) {
// Just return quietly here if device no longer exists throw new SecurityException("setDeviceStatus() caller UID " + Binder.getCallingUid()
return; + " does not match device's UID " + device.getUid());
}
device.setDeviceStatus(status);
notifyDeviceStatusChanged(device, status);
} }
if (Binder.getCallingUid() != device.getUid()) {
throw new SecurityException("setDeviceStatus() caller UID " + Binder.getCallingUid()
+ " does not match device's UID " + device.getUid());
}
device.setDeviceStatus(token, status);
notifyDeviceStatusChanged(device, status);
} }
private void notifyDeviceStatusChanged(Device device, MidiDeviceStatus status) { private void notifyDeviceStatusChanged(Device device, MidiDeviceStatus status) {
@@ -452,18 +740,24 @@ public class MidiService extends IMidiManager.Stub {
int id = mNextDeviceId++; int id = mNextDeviceId++;
MidiDeviceInfo deviceInfo = new MidiDeviceInfo(type, id, numInputPorts, numOutputPorts, MidiDeviceInfo deviceInfo = new MidiDeviceInfo(type, id, numInputPorts, numOutputPorts,
inputPortNames, outputPortNames, properties, isPrivate); inputPortNames, outputPortNames, properties, isPrivate);
Device device = new Device(server, deviceInfo, serviceInfo, uid);
if (server != null) { Device device = null;
IBinder binder = server.asBinder(); BluetoothDevice bluetoothDevice = null;
try { if (type == MidiDeviceInfo.TYPE_BLUETOOTH) {
binder.linkToDeath(device, 0); bluetoothDevice = (BluetoothDevice)properties.getParcelable(
} catch (RemoteException e) { MidiDeviceInfo.PROPERTY_BLUETOOTH_DEVICE);
return null; 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); mDevicesByInfo.put(deviceInfo, device);
if (bluetoothDevice != null) {
mBluetoothDevices.put(bluetoothDevice, device);
}
synchronized (mClients) { synchronized (mClients) {
for (Client c : mClients.values()) { for (Client c : mClients.values()) {
@@ -478,8 +772,9 @@ public class MidiService extends IMidiManager.Stub {
private void removeDeviceLocked(Device device) { private void removeDeviceLocked(Device device) {
IMidiDeviceServer server = device.getDeviceServer(); IMidiDeviceServer server = device.getDeviceServer();
if (server != null) { if (server != null) {
mDevicesByServer.remove(server); mDevicesByServer.remove(server.asBinder());
} }
mDevicesByInfo.remove(device.getDeviceInfo());
synchronized (mClients) { synchronized (mClients) {
for (Client c : mClients.values()) { for (Client c : mClients.values()) {
@@ -516,6 +811,15 @@ public class MidiService extends IMidiManager.Stub {
MidiDeviceService.SERVICE_INTERFACE); MidiDeviceService.SERVICE_INTERFACE);
if (parser == null) return; 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; Bundle properties = null;
int numInputPorts = 0; int numInputPorts = 0;
int numOutputPorts = 0; int numOutputPorts = 0;