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:
@@ -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 \
|
||||||
|
|||||||
@@ -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";
|
||||||
|
|||||||
@@ -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";
|
||||||
|
|||||||
@@ -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,
|
||||||
|
|||||||
@@ -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"
|
||||||
|
|||||||
25
media/java/android/media/midi/IMidiDeviceOpenCallback.aidl
Normal file
25
media/java/android/media/midi/IMidiDeviceOpenCallback.aidl
Normal 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);
|
||||||
|
}
|
||||||
@@ -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);
|
||||||
|
|||||||
@@ -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);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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 {
|
||||||
|
|||||||
@@ -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) {
|
||||||
|
|||||||
@@ -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);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -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 "android.permission.BIND_MIDI_DEVICE_SERVICE".</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>
|
||||||
<service android:name="<strong>MySynthDeviceService</strong>">
|
<service android:name="<strong>MySynthDeviceService</strong>"
|
||||||
|
android:permission="android.permission.BIND_MIDI_DEVICE_SERVICE">
|
||||||
<intent-filter>
|
<intent-filter>
|
||||||
<action android:name="android.media.midi.MidiDeviceService" />
|
<action android:name="android.media.midi.MidiDeviceService" />
|
||||||
</intent-filter>
|
</intent-filter>
|
||||||
|
|||||||
@@ -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>
|
||||||
|
|||||||
@@ -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();
|
||||||
|
|||||||
@@ -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;
|
||||||
|
|||||||
Reference in New Issue
Block a user