110024b3dc12a8552c1547b67810c77b865045cc8Mike Lockwood/* 210024b3dc12a8552c1547b67810c77b865045cc8Mike Lockwood * Copyright (C) 2014 The Android Open Source Project 310024b3dc12a8552c1547b67810c77b865045cc8Mike Lockwood * 410024b3dc12a8552c1547b67810c77b865045cc8Mike Lockwood * Licensed under the Apache License, Version 2.0 (the "License"); 510024b3dc12a8552c1547b67810c77b865045cc8Mike Lockwood * you may not use this file except in compliance with the License. 610024b3dc12a8552c1547b67810c77b865045cc8Mike Lockwood * You may obtain a copy of the License at 710024b3dc12a8552c1547b67810c77b865045cc8Mike Lockwood * 810024b3dc12a8552c1547b67810c77b865045cc8Mike Lockwood * http://www.apache.org/licenses/LICENSE-2.0 910024b3dc12a8552c1547b67810c77b865045cc8Mike Lockwood * 1010024b3dc12a8552c1547b67810c77b865045cc8Mike Lockwood * Unless required by applicable law or agreed to in writing, software 1110024b3dc12a8552c1547b67810c77b865045cc8Mike Lockwood * distributed under the License is distributed on an "AS IS" BASIS, 1210024b3dc12a8552c1547b67810c77b865045cc8Mike Lockwood * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 1310024b3dc12a8552c1547b67810c77b865045cc8Mike Lockwood * See the License for the specific language governing permissions and 1410024b3dc12a8552c1547b67810c77b865045cc8Mike Lockwood * limitations under the License. 1510024b3dc12a8552c1547b67810c77b865045cc8Mike Lockwood */ 1610024b3dc12a8552c1547b67810c77b865045cc8Mike Lockwood 17b673770f7172d4fca9bc05de1f36bc53e93eb247Mike Lockwoodpackage android.media.midi; 1810024b3dc12a8552c1547b67810c77b865045cc8Mike Lockwood 1911fd96d6ff25bc1d710448eab545fe09da55a5f5Mike Lockwoodimport android.os.Binder; 205ff9e2a1719f78cddc7a23d6572ab15ab595dafdMike Lockwoodimport android.os.IBinder; 2110024b3dc12a8552c1547b67810c77b865045cc8Mike Lockwoodimport android.os.ParcelFileDescriptor; 2211fd96d6ff25bc1d710448eab545fe09da55a5f5Mike Lockwoodimport android.os.Process; 2310024b3dc12a8552c1547b67810c77b865045cc8Mike Lockwoodimport android.os.RemoteException; 2410024b3dc12a8552c1547b67810c77b865045cc8Mike Lockwoodimport android.system.OsConstants; 2510024b3dc12a8552c1547b67810c77b865045cc8Mike Lockwoodimport android.util.Log; 2610024b3dc12a8552c1547b67810c77b865045cc8Mike Lockwood 27d1b16fe2fb7527eee214898263ec4d6dabbfb0b4Mike Lockwoodimport com.android.internal.midi.MidiDispatcher; 28d1b16fe2fb7527eee214898263ec4d6dabbfb0b4Mike Lockwood 29be215dd57282888b05b234c39bba44cc0a864b8aMike Lockwoodimport dalvik.system.CloseGuard; 30be215dd57282888b05b234c39bba44cc0a864b8aMike Lockwood 3111fd96d6ff25bc1d710448eab545fe09da55a5f5Mike Lockwoodimport libcore.io.IoUtils; 3211fd96d6ff25bc1d710448eab545fe09da55a5f5Mike Lockwood 3310024b3dc12a8552c1547b67810c77b865045cc8Mike Lockwoodimport java.io.Closeable; 3410024b3dc12a8552c1547b67810c77b865045cc8Mike Lockwoodimport java.io.IOException; 354a3d7ed45d98ad2fe900221755845b87f26b554aMike Lockwoodimport java.util.HashMap; 36be215dd57282888b05b234c39bba44cc0a864b8aMike Lockwoodimport java.util.concurrent.CopyOnWriteArrayList; 3710024b3dc12a8552c1547b67810c77b865045cc8Mike Lockwood 38a7e348eb4d2ef1632f2ebe3a742743607ccfd82bMike Lockwood/** 3911fd96d6ff25bc1d710448eab545fe09da55a5f5Mike Lockwood * Internal class used for providing an implementation for a MIDI device. 40a7e348eb4d2ef1632f2ebe3a742743607ccfd82bMike Lockwood * 41a7e348eb4d2ef1632f2ebe3a742743607ccfd82bMike Lockwood * @hide 42a7e348eb4d2ef1632f2ebe3a742743607ccfd82bMike Lockwood */ 4310024b3dc12a8552c1547b67810c77b865045cc8Mike Lockwoodpublic final class MidiDeviceServer implements Closeable { 4410024b3dc12a8552c1547b67810c77b865045cc8Mike Lockwood private static final String TAG = "MidiDeviceServer"; 4510024b3dc12a8552c1547b67810c77b865045cc8Mike Lockwood 4610024b3dc12a8552c1547b67810c77b865045cc8Mike Lockwood private final IMidiManager mMidiManager; 4710024b3dc12a8552c1547b67810c77b865045cc8Mike Lockwood 4810024b3dc12a8552c1547b67810c77b865045cc8Mike Lockwood // MidiDeviceInfo for the device implemented by this server 4910024b3dc12a8552c1547b67810c77b865045cc8Mike Lockwood private MidiDeviceInfo mDeviceInfo; 5011fd96d6ff25bc1d710448eab545fe09da55a5f5Mike Lockwood private final int mInputPortCount; 5111fd96d6ff25bc1d710448eab545fe09da55a5f5Mike Lockwood private final int mOutputPortCount; 5210024b3dc12a8552c1547b67810c77b865045cc8Mike Lockwood 5311fd96d6ff25bc1d710448eab545fe09da55a5f5Mike Lockwood // MidiReceivers for receiving data on our input ports 5411fd96d6ff25bc1d710448eab545fe09da55a5f5Mike Lockwood private final MidiReceiver[] mInputPortReceivers; 5510024b3dc12a8552c1547b67810c77b865045cc8Mike Lockwood 5611fd96d6ff25bc1d710448eab545fe09da55a5f5Mike Lockwood // MidiDispatchers for sending data on our output ports 5711fd96d6ff25bc1d710448eab545fe09da55a5f5Mike Lockwood private MidiDispatcher[] mOutputPortDispatchers; 5810024b3dc12a8552c1547b67810c77b865045cc8Mike Lockwood 5911fd96d6ff25bc1d710448eab545fe09da55a5f5Mike Lockwood // MidiOutputPorts for clients connected to our input ports 6011fd96d6ff25bc1d710448eab545fe09da55a5f5Mike Lockwood private final MidiOutputPort[] mInputPortOutputPorts; 6110024b3dc12a8552c1547b67810c77b865045cc8Mike Lockwood 62be215dd57282888b05b234c39bba44cc0a864b8aMike Lockwood // List of all MidiInputPorts we created 63be215dd57282888b05b234c39bba44cc0a864b8aMike Lockwood private final CopyOnWriteArrayList<MidiInputPort> mInputPorts 64be215dd57282888b05b234c39bba44cc0a864b8aMike Lockwood = new CopyOnWriteArrayList<MidiInputPort>(); 65be215dd57282888b05b234c39bba44cc0a864b8aMike Lockwood 665ff9e2a1719f78cddc7a23d6572ab15ab595dafdMike Lockwood 675ff9e2a1719f78cddc7a23d6572ab15ab595dafdMike Lockwood // for reporting device status 6881b9f7d325a552c54e793b51f571ae3d65b26e94Mike Lockwood private final boolean[] mInputPortOpen; 695ff9e2a1719f78cddc7a23d6572ab15ab595dafdMike Lockwood private final int[] mOutputPortOpenCount; 705ff9e2a1719f78cddc7a23d6572ab15ab595dafdMike Lockwood 71be215dd57282888b05b234c39bba44cc0a864b8aMike Lockwood private final CloseGuard mGuard = CloseGuard.get(); 725ff9e2a1719f78cddc7a23d6572ab15ab595dafdMike Lockwood private boolean mIsClosed; 735ff9e2a1719f78cddc7a23d6572ab15ab595dafdMike Lockwood 745ff9e2a1719f78cddc7a23d6572ab15ab595dafdMike Lockwood private final Callback mCallback; 755ff9e2a1719f78cddc7a23d6572ab15ab595dafdMike Lockwood 765ff9e2a1719f78cddc7a23d6572ab15ab595dafdMike Lockwood public interface Callback { 775ff9e2a1719f78cddc7a23d6572ab15ab595dafdMike Lockwood /** 785ff9e2a1719f78cddc7a23d6572ab15ab595dafdMike Lockwood * Called to notify when an our device status has changed 795ff9e2a1719f78cddc7a23d6572ab15ab595dafdMike Lockwood * @param server the {@link MidiDeviceServer} that changed 805ff9e2a1719f78cddc7a23d6572ab15ab595dafdMike Lockwood * @param status the {@link MidiDeviceStatus} for the device 815ff9e2a1719f78cddc7a23d6572ab15ab595dafdMike Lockwood */ 825ff9e2a1719f78cddc7a23d6572ab15ab595dafdMike Lockwood public void onDeviceStatusChanged(MidiDeviceServer server, MidiDeviceStatus status); 83e0a6ca64fac5bd4f10139321604031816e90adb4Mike Lockwood 84e0a6ca64fac5bd4f10139321604031816e90adb4Mike Lockwood /** 85e0a6ca64fac5bd4f10139321604031816e90adb4Mike Lockwood * Called to notify when the device is closed 86e0a6ca64fac5bd4f10139321604031816e90adb4Mike Lockwood */ 87e0a6ca64fac5bd4f10139321604031816e90adb4Mike Lockwood public void onClose(); 885ff9e2a1719f78cddc7a23d6572ab15ab595dafdMike Lockwood } 89be215dd57282888b05b234c39bba44cc0a864b8aMike Lockwood 904a3d7ed45d98ad2fe900221755845b87f26b554aMike Lockwood abstract private class PortClient implements IBinder.DeathRecipient { 914a3d7ed45d98ad2fe900221755845b87f26b554aMike Lockwood final IBinder mToken; 924a3d7ed45d98ad2fe900221755845b87f26b554aMike Lockwood 934a3d7ed45d98ad2fe900221755845b87f26b554aMike Lockwood PortClient(IBinder token) { 944a3d7ed45d98ad2fe900221755845b87f26b554aMike Lockwood mToken = token; 954a3d7ed45d98ad2fe900221755845b87f26b554aMike Lockwood 964a3d7ed45d98ad2fe900221755845b87f26b554aMike Lockwood try { 974a3d7ed45d98ad2fe900221755845b87f26b554aMike Lockwood token.linkToDeath(this, 0); 984a3d7ed45d98ad2fe900221755845b87f26b554aMike Lockwood } catch (RemoteException e) { 994a3d7ed45d98ad2fe900221755845b87f26b554aMike Lockwood close(); 1004a3d7ed45d98ad2fe900221755845b87f26b554aMike Lockwood } 1014a3d7ed45d98ad2fe900221755845b87f26b554aMike Lockwood } 1024a3d7ed45d98ad2fe900221755845b87f26b554aMike Lockwood 1034a3d7ed45d98ad2fe900221755845b87f26b554aMike Lockwood abstract void close(); 1044a3d7ed45d98ad2fe900221755845b87f26b554aMike Lockwood 1054a3d7ed45d98ad2fe900221755845b87f26b554aMike Lockwood @Override 1064a3d7ed45d98ad2fe900221755845b87f26b554aMike Lockwood public void binderDied() { 1074a3d7ed45d98ad2fe900221755845b87f26b554aMike Lockwood close(); 1084a3d7ed45d98ad2fe900221755845b87f26b554aMike Lockwood } 1094a3d7ed45d98ad2fe900221755845b87f26b554aMike Lockwood } 1104a3d7ed45d98ad2fe900221755845b87f26b554aMike Lockwood 1114a3d7ed45d98ad2fe900221755845b87f26b554aMike Lockwood private class InputPortClient extends PortClient { 1124a3d7ed45d98ad2fe900221755845b87f26b554aMike Lockwood private final MidiOutputPort mOutputPort; 1134a3d7ed45d98ad2fe900221755845b87f26b554aMike Lockwood 1144a3d7ed45d98ad2fe900221755845b87f26b554aMike Lockwood InputPortClient(IBinder token, MidiOutputPort outputPort) { 1154a3d7ed45d98ad2fe900221755845b87f26b554aMike Lockwood super(token); 1164a3d7ed45d98ad2fe900221755845b87f26b554aMike Lockwood mOutputPort = outputPort; 1174a3d7ed45d98ad2fe900221755845b87f26b554aMike Lockwood } 1184a3d7ed45d98ad2fe900221755845b87f26b554aMike Lockwood 1194a3d7ed45d98ad2fe900221755845b87f26b554aMike Lockwood @Override 1204a3d7ed45d98ad2fe900221755845b87f26b554aMike Lockwood void close() { 1214a3d7ed45d98ad2fe900221755845b87f26b554aMike Lockwood mToken.unlinkToDeath(this, 0); 1224a3d7ed45d98ad2fe900221755845b87f26b554aMike Lockwood synchronized (mInputPortOutputPorts) { 1235ff9e2a1719f78cddc7a23d6572ab15ab595dafdMike Lockwood int portNumber = mOutputPort.getPortNumber(); 1245ff9e2a1719f78cddc7a23d6572ab15ab595dafdMike Lockwood mInputPortOutputPorts[portNumber] = null; 12581b9f7d325a552c54e793b51f571ae3d65b26e94Mike Lockwood mInputPortOpen[portNumber] = false; 1265ff9e2a1719f78cddc7a23d6572ab15ab595dafdMike Lockwood updateDeviceStatus(); 1274a3d7ed45d98ad2fe900221755845b87f26b554aMike Lockwood } 1284a3d7ed45d98ad2fe900221755845b87f26b554aMike Lockwood IoUtils.closeQuietly(mOutputPort); 1294a3d7ed45d98ad2fe900221755845b87f26b554aMike Lockwood } 1304a3d7ed45d98ad2fe900221755845b87f26b554aMike Lockwood } 1314a3d7ed45d98ad2fe900221755845b87f26b554aMike Lockwood 1324a3d7ed45d98ad2fe900221755845b87f26b554aMike Lockwood private class OutputPortClient extends PortClient { 1334a3d7ed45d98ad2fe900221755845b87f26b554aMike Lockwood private final MidiInputPort mInputPort; 1344a3d7ed45d98ad2fe900221755845b87f26b554aMike Lockwood 1354a3d7ed45d98ad2fe900221755845b87f26b554aMike Lockwood OutputPortClient(IBinder token, MidiInputPort inputPort) { 1364a3d7ed45d98ad2fe900221755845b87f26b554aMike Lockwood super(token); 1374a3d7ed45d98ad2fe900221755845b87f26b554aMike Lockwood mInputPort = inputPort; 1384a3d7ed45d98ad2fe900221755845b87f26b554aMike Lockwood } 1394a3d7ed45d98ad2fe900221755845b87f26b554aMike Lockwood 1404a3d7ed45d98ad2fe900221755845b87f26b554aMike Lockwood @Override 1414a3d7ed45d98ad2fe900221755845b87f26b554aMike Lockwood void close() { 1424a3d7ed45d98ad2fe900221755845b87f26b554aMike Lockwood mToken.unlinkToDeath(this, 0); 1435ff9e2a1719f78cddc7a23d6572ab15ab595dafdMike Lockwood int portNumber = mInputPort.getPortNumber(); 1445ff9e2a1719f78cddc7a23d6572ab15ab595dafdMike Lockwood MidiDispatcher dispatcher = mOutputPortDispatchers[portNumber]; 1455ff9e2a1719f78cddc7a23d6572ab15ab595dafdMike Lockwood synchronized (dispatcher) { 1465ff9e2a1719f78cddc7a23d6572ab15ab595dafdMike Lockwood dispatcher.getSender().disconnect(mInputPort); 1475ff9e2a1719f78cddc7a23d6572ab15ab595dafdMike Lockwood int openCount = dispatcher.getReceiverCount(); 1485ff9e2a1719f78cddc7a23d6572ab15ab595dafdMike Lockwood mOutputPortOpenCount[portNumber] = openCount; 1495ff9e2a1719f78cddc7a23d6572ab15ab595dafdMike Lockwood updateDeviceStatus(); 1505ff9e2a1719f78cddc7a23d6572ab15ab595dafdMike Lockwood } 1515ff9e2a1719f78cddc7a23d6572ab15ab595dafdMike Lockwood 152be215dd57282888b05b234c39bba44cc0a864b8aMike Lockwood mInputPorts.remove(mInputPort); 1534a3d7ed45d98ad2fe900221755845b87f26b554aMike Lockwood IoUtils.closeQuietly(mInputPort); 1544a3d7ed45d98ad2fe900221755845b87f26b554aMike Lockwood } 1554a3d7ed45d98ad2fe900221755845b87f26b554aMike Lockwood } 1564a3d7ed45d98ad2fe900221755845b87f26b554aMike Lockwood 1574a3d7ed45d98ad2fe900221755845b87f26b554aMike Lockwood private final HashMap<IBinder, PortClient> mPortClients = new HashMap<IBinder, PortClient>(); 1584a3d7ed45d98ad2fe900221755845b87f26b554aMike Lockwood 15910024b3dc12a8552c1547b67810c77b865045cc8Mike Lockwood // Binder interface stub for receiving connection requests from clients 16010024b3dc12a8552c1547b67810c77b865045cc8Mike Lockwood private final IMidiDeviceServer mServer = new IMidiDeviceServer.Stub() { 16110024b3dc12a8552c1547b67810c77b865045cc8Mike Lockwood 16210024b3dc12a8552c1547b67810c77b865045cc8Mike Lockwood @Override 1634a3d7ed45d98ad2fe900221755845b87f26b554aMike Lockwood public ParcelFileDescriptor openInputPort(IBinder token, int portNumber) { 16411fd96d6ff25bc1d710448eab545fe09da55a5f5Mike Lockwood if (mDeviceInfo.isPrivate()) { 16511fd96d6ff25bc1d710448eab545fe09da55a5f5Mike Lockwood if (Binder.getCallingUid() != Process.myUid()) { 16611fd96d6ff25bc1d710448eab545fe09da55a5f5Mike Lockwood throw new SecurityException("Can't access private device from different UID"); 16711fd96d6ff25bc1d710448eab545fe09da55a5f5Mike Lockwood } 16811fd96d6ff25bc1d710448eab545fe09da55a5f5Mike Lockwood } 16911fd96d6ff25bc1d710448eab545fe09da55a5f5Mike Lockwood 17010024b3dc12a8552c1547b67810c77b865045cc8Mike Lockwood if (portNumber < 0 || portNumber >= mInputPortCount) { 17110024b3dc12a8552c1547b67810c77b865045cc8Mike Lockwood Log.e(TAG, "portNumber out of range in openInputPort: " + portNumber); 17210024b3dc12a8552c1547b67810c77b865045cc8Mike Lockwood return null; 17310024b3dc12a8552c1547b67810c77b865045cc8Mike Lockwood } 17410024b3dc12a8552c1547b67810c77b865045cc8Mike Lockwood 17511fd96d6ff25bc1d710448eab545fe09da55a5f5Mike Lockwood synchronized (mInputPortOutputPorts) { 17611fd96d6ff25bc1d710448eab545fe09da55a5f5Mike Lockwood if (mInputPortOutputPorts[portNumber] != null) { 17710024b3dc12a8552c1547b67810c77b865045cc8Mike Lockwood Log.d(TAG, "port " + portNumber + " already open"); 17810024b3dc12a8552c1547b67810c77b865045cc8Mike Lockwood return null; 17910024b3dc12a8552c1547b67810c77b865045cc8Mike Lockwood } 18010024b3dc12a8552c1547b67810c77b865045cc8Mike Lockwood 18110024b3dc12a8552c1547b67810c77b865045cc8Mike Lockwood try { 18210024b3dc12a8552c1547b67810c77b865045cc8Mike Lockwood ParcelFileDescriptor[] pair = ParcelFileDescriptor.createSocketPair( 18310024b3dc12a8552c1547b67810c77b865045cc8Mike Lockwood OsConstants.SOCK_SEQPACKET); 1844a3d7ed45d98ad2fe900221755845b87f26b554aMike Lockwood MidiOutputPort outputPort = new MidiOutputPort(pair[0], portNumber); 18511fd96d6ff25bc1d710448eab545fe09da55a5f5Mike Lockwood mInputPortOutputPorts[portNumber] = outputPort; 1864a3d7ed45d98ad2fe900221755845b87f26b554aMike Lockwood outputPort.connect(mInputPortReceivers[portNumber]); 1874a3d7ed45d98ad2fe900221755845b87f26b554aMike Lockwood InputPortClient client = new InputPortClient(token, outputPort); 1884a3d7ed45d98ad2fe900221755845b87f26b554aMike Lockwood synchronized (mPortClients) { 1894a3d7ed45d98ad2fe900221755845b87f26b554aMike Lockwood mPortClients.put(token, client); 1904a3d7ed45d98ad2fe900221755845b87f26b554aMike Lockwood } 19181b9f7d325a552c54e793b51f571ae3d65b26e94Mike Lockwood mInputPortOpen[portNumber] = true; 1925ff9e2a1719f78cddc7a23d6572ab15ab595dafdMike Lockwood updateDeviceStatus(); 19311fd96d6ff25bc1d710448eab545fe09da55a5f5Mike Lockwood return pair[1]; 19498cc8e5a6473b3a5802d97cc81020ec4e3cd23f3Mike Lockwood } catch (IOException e) { 19598cc8e5a6473b3a5802d97cc81020ec4e3cd23f3Mike Lockwood Log.e(TAG, "unable to create ParcelFileDescriptors in openInputPort"); 19698cc8e5a6473b3a5802d97cc81020ec4e3cd23f3Mike Lockwood return null; 19710024b3dc12a8552c1547b67810c77b865045cc8Mike Lockwood } 19810024b3dc12a8552c1547b67810c77b865045cc8Mike Lockwood } 19910024b3dc12a8552c1547b67810c77b865045cc8Mike Lockwood } 20010024b3dc12a8552c1547b67810c77b865045cc8Mike Lockwood 20110024b3dc12a8552c1547b67810c77b865045cc8Mike Lockwood @Override 2024a3d7ed45d98ad2fe900221755845b87f26b554aMike Lockwood public ParcelFileDescriptor openOutputPort(IBinder token, int portNumber) { 20311fd96d6ff25bc1d710448eab545fe09da55a5f5Mike Lockwood if (mDeviceInfo.isPrivate()) { 20411fd96d6ff25bc1d710448eab545fe09da55a5f5Mike Lockwood if (Binder.getCallingUid() != Process.myUid()) { 20511fd96d6ff25bc1d710448eab545fe09da55a5f5Mike Lockwood throw new SecurityException("Can't access private device from different UID"); 20611fd96d6ff25bc1d710448eab545fe09da55a5f5Mike Lockwood } 20711fd96d6ff25bc1d710448eab545fe09da55a5f5Mike Lockwood } 20811fd96d6ff25bc1d710448eab545fe09da55a5f5Mike Lockwood 20910024b3dc12a8552c1547b67810c77b865045cc8Mike Lockwood if (portNumber < 0 || portNumber >= mOutputPortCount) { 21010024b3dc12a8552c1547b67810c77b865045cc8Mike Lockwood Log.e(TAG, "portNumber out of range in openOutputPort: " + portNumber); 21110024b3dc12a8552c1547b67810c77b865045cc8Mike Lockwood return null; 21210024b3dc12a8552c1547b67810c77b865045cc8Mike Lockwood } 21311fd96d6ff25bc1d710448eab545fe09da55a5f5Mike Lockwood 21411fd96d6ff25bc1d710448eab545fe09da55a5f5Mike Lockwood try { 21511fd96d6ff25bc1d710448eab545fe09da55a5f5Mike Lockwood ParcelFileDescriptor[] pair = ParcelFileDescriptor.createSocketPair( 21611fd96d6ff25bc1d710448eab545fe09da55a5f5Mike Lockwood OsConstants.SOCK_SEQPACKET); 2174a3d7ed45d98ad2fe900221755845b87f26b554aMike Lockwood MidiInputPort inputPort = new MidiInputPort(pair[0], portNumber); 2185ff9e2a1719f78cddc7a23d6572ab15ab595dafdMike Lockwood MidiDispatcher dispatcher = mOutputPortDispatchers[portNumber]; 2195ff9e2a1719f78cddc7a23d6572ab15ab595dafdMike Lockwood synchronized (dispatcher) { 2205ff9e2a1719f78cddc7a23d6572ab15ab595dafdMike Lockwood dispatcher.getSender().connect(inputPort); 2215ff9e2a1719f78cddc7a23d6572ab15ab595dafdMike Lockwood int openCount = dispatcher.getReceiverCount(); 2225ff9e2a1719f78cddc7a23d6572ab15ab595dafdMike Lockwood mOutputPortOpenCount[portNumber] = openCount; 2235ff9e2a1719f78cddc7a23d6572ab15ab595dafdMike Lockwood updateDeviceStatus(); 2245ff9e2a1719f78cddc7a23d6572ab15ab595dafdMike Lockwood } 2255ff9e2a1719f78cddc7a23d6572ab15ab595dafdMike Lockwood 226be215dd57282888b05b234c39bba44cc0a864b8aMike Lockwood mInputPorts.add(inputPort); 2274a3d7ed45d98ad2fe900221755845b87f26b554aMike Lockwood OutputPortClient client = new OutputPortClient(token, inputPort); 2284a3d7ed45d98ad2fe900221755845b87f26b554aMike Lockwood synchronized (mPortClients) { 2294a3d7ed45d98ad2fe900221755845b87f26b554aMike Lockwood mPortClients.put(token, client); 2304a3d7ed45d98ad2fe900221755845b87f26b554aMike Lockwood } 23111fd96d6ff25bc1d710448eab545fe09da55a5f5Mike Lockwood return pair[1]; 23211fd96d6ff25bc1d710448eab545fe09da55a5f5Mike Lockwood } catch (IOException e) { 23311fd96d6ff25bc1d710448eab545fe09da55a5f5Mike Lockwood Log.e(TAG, "unable to create ParcelFileDescriptors in openOutputPort"); 23411fd96d6ff25bc1d710448eab545fe09da55a5f5Mike Lockwood return null; 23510024b3dc12a8552c1547b67810c77b865045cc8Mike Lockwood } 23610024b3dc12a8552c1547b67810c77b865045cc8Mike Lockwood } 2374a3d7ed45d98ad2fe900221755845b87f26b554aMike Lockwood 2384a3d7ed45d98ad2fe900221755845b87f26b554aMike Lockwood @Override 2394a3d7ed45d98ad2fe900221755845b87f26b554aMike Lockwood public void closePort(IBinder token) { 2404a3d7ed45d98ad2fe900221755845b87f26b554aMike Lockwood synchronized (mPortClients) { 2414a3d7ed45d98ad2fe900221755845b87f26b554aMike Lockwood PortClient client = mPortClients.remove(token); 2424a3d7ed45d98ad2fe900221755845b87f26b554aMike Lockwood if (client != null) { 2434a3d7ed45d98ad2fe900221755845b87f26b554aMike Lockwood client.close(); 2444a3d7ed45d98ad2fe900221755845b87f26b554aMike Lockwood } 2454a3d7ed45d98ad2fe900221755845b87f26b554aMike Lockwood } 2464a3d7ed45d98ad2fe900221755845b87f26b554aMike Lockwood } 24746326e59a0a19367d4158c027d56d4b8440e8d3dMike Lockwood 24846326e59a0a19367d4158c027d56d4b8440e8d3dMike Lockwood @Override 249e0a6ca64fac5bd4f10139321604031816e90adb4Mike Lockwood public void closeDevice() { 250e0a6ca64fac5bd4f10139321604031816e90adb4Mike Lockwood if (mCallback != null) { 251e0a6ca64fac5bd4f10139321604031816e90adb4Mike Lockwood mCallback.onClose(); 252e0a6ca64fac5bd4f10139321604031816e90adb4Mike Lockwood } 253e0a6ca64fac5bd4f10139321604031816e90adb4Mike Lockwood IoUtils.closeQuietly(MidiDeviceServer.this); 254e0a6ca64fac5bd4f10139321604031816e90adb4Mike Lockwood } 255e0a6ca64fac5bd4f10139321604031816e90adb4Mike Lockwood 256e0a6ca64fac5bd4f10139321604031816e90adb4Mike Lockwood @Override 25780299748cc7ed5f59cb65122459b036b74150d4aPhil Burk public int connectPorts(IBinder token, ParcelFileDescriptor pfd, 25846326e59a0a19367d4158c027d56d4b8440e8d3dMike Lockwood int outputPortNumber) { 25946326e59a0a19367d4158c027d56d4b8440e8d3dMike Lockwood MidiInputPort inputPort = new MidiInputPort(pfd, outputPortNumber); 2602aef7e3559e9e2c78287a00b3f693b6dc19e56f0Mike Lockwood MidiDispatcher dispatcher = mOutputPortDispatchers[outputPortNumber]; 2612aef7e3559e9e2c78287a00b3f693b6dc19e56f0Mike Lockwood synchronized (dispatcher) { 2622aef7e3559e9e2c78287a00b3f693b6dc19e56f0Mike Lockwood dispatcher.getSender().connect(inputPort); 2632aef7e3559e9e2c78287a00b3f693b6dc19e56f0Mike Lockwood int openCount = dispatcher.getReceiverCount(); 2642aef7e3559e9e2c78287a00b3f693b6dc19e56f0Mike Lockwood mOutputPortOpenCount[outputPortNumber] = openCount; 2652aef7e3559e9e2c78287a00b3f693b6dc19e56f0Mike Lockwood updateDeviceStatus(); 2662aef7e3559e9e2c78287a00b3f693b6dc19e56f0Mike Lockwood } 2672aef7e3559e9e2c78287a00b3f693b6dc19e56f0Mike Lockwood 26846326e59a0a19367d4158c027d56d4b8440e8d3dMike Lockwood mInputPorts.add(inputPort); 26946326e59a0a19367d4158c027d56d4b8440e8d3dMike Lockwood OutputPortClient client = new OutputPortClient(token, inputPort); 27046326e59a0a19367d4158c027d56d4b8440e8d3dMike Lockwood synchronized (mPortClients) { 27146326e59a0a19367d4158c027d56d4b8440e8d3dMike Lockwood mPortClients.put(token, client); 27246326e59a0a19367d4158c027d56d4b8440e8d3dMike Lockwood } 27380299748cc7ed5f59cb65122459b036b74150d4aPhil Burk return Process.myPid(); // for caller to detect same process ID 27446326e59a0a19367d4158c027d56d4b8440e8d3dMike Lockwood } 275f0a41d1c591193fbe02c9ddbaf24c79af4da9972Mike Lockwood 276f0a41d1c591193fbe02c9ddbaf24c79af4da9972Mike Lockwood @Override 277f0a41d1c591193fbe02c9ddbaf24c79af4da9972Mike Lockwood public MidiDeviceInfo getDeviceInfo() { 278f0a41d1c591193fbe02c9ddbaf24c79af4da9972Mike Lockwood return mDeviceInfo; 279f0a41d1c591193fbe02c9ddbaf24c79af4da9972Mike Lockwood } 280acd4321872387212bbd956e134faf22eee4bbadcMike Lockwood 281acd4321872387212bbd956e134faf22eee4bbadcMike Lockwood @Override 282acd4321872387212bbd956e134faf22eee4bbadcMike Lockwood public void setDeviceInfo(MidiDeviceInfo deviceInfo) { 283acd4321872387212bbd956e134faf22eee4bbadcMike Lockwood if (Binder.getCallingUid() != Process.SYSTEM_UID) { 284acd4321872387212bbd956e134faf22eee4bbadcMike Lockwood throw new SecurityException("setDeviceInfo should only be called by MidiService"); 285acd4321872387212bbd956e134faf22eee4bbadcMike Lockwood } 286acd4321872387212bbd956e134faf22eee4bbadcMike Lockwood if (mDeviceInfo != null) { 287acd4321872387212bbd956e134faf22eee4bbadcMike Lockwood throw new IllegalStateException("setDeviceInfo should only be called once"); 288acd4321872387212bbd956e134faf22eee4bbadcMike Lockwood } 289acd4321872387212bbd956e134faf22eee4bbadcMike Lockwood mDeviceInfo = deviceInfo; 290acd4321872387212bbd956e134faf22eee4bbadcMike Lockwood } 29110024b3dc12a8552c1547b67810c77b865045cc8Mike Lockwood }; 29210024b3dc12a8552c1547b67810c77b865045cc8Mike Lockwood 293acd4321872387212bbd956e134faf22eee4bbadcMike Lockwood // Constructor for MidiManager.createDeviceServer() 29411fd96d6ff25bc1d710448eab545fe09da55a5f5Mike Lockwood /* package */ MidiDeviceServer(IMidiManager midiManager, MidiReceiver[] inputPortReceivers, 2955ff9e2a1719f78cddc7a23d6572ab15ab595dafdMike Lockwood int numOutputPorts, Callback callback) { 29610024b3dc12a8552c1547b67810c77b865045cc8Mike Lockwood mMidiManager = midiManager; 29711fd96d6ff25bc1d710448eab545fe09da55a5f5Mike Lockwood mInputPortReceivers = inputPortReceivers; 29811fd96d6ff25bc1d710448eab545fe09da55a5f5Mike Lockwood mInputPortCount = inputPortReceivers.length; 29911fd96d6ff25bc1d710448eab545fe09da55a5f5Mike Lockwood mOutputPortCount = numOutputPorts; 3005ff9e2a1719f78cddc7a23d6572ab15ab595dafdMike Lockwood mCallback = callback; 30111fd96d6ff25bc1d710448eab545fe09da55a5f5Mike Lockwood 30211fd96d6ff25bc1d710448eab545fe09da55a5f5Mike Lockwood mInputPortOutputPorts = new MidiOutputPort[mInputPortCount]; 30311fd96d6ff25bc1d710448eab545fe09da55a5f5Mike Lockwood 30411fd96d6ff25bc1d710448eab545fe09da55a5f5Mike Lockwood mOutputPortDispatchers = new MidiDispatcher[numOutputPorts]; 30511fd96d6ff25bc1d710448eab545fe09da55a5f5Mike Lockwood for (int i = 0; i < numOutputPorts; i++) { 30611fd96d6ff25bc1d710448eab545fe09da55a5f5Mike Lockwood mOutputPortDispatchers[i] = new MidiDispatcher(); 30711fd96d6ff25bc1d710448eab545fe09da55a5f5Mike Lockwood } 308be215dd57282888b05b234c39bba44cc0a864b8aMike Lockwood 30981b9f7d325a552c54e793b51f571ae3d65b26e94Mike Lockwood mInputPortOpen = new boolean[mInputPortCount]; 3105ff9e2a1719f78cddc7a23d6572ab15ab595dafdMike Lockwood mOutputPortOpenCount = new int[numOutputPorts]; 3115ff9e2a1719f78cddc7a23d6572ab15ab595dafdMike Lockwood 312be215dd57282888b05b234c39bba44cc0a864b8aMike Lockwood mGuard.open("close"); 31310024b3dc12a8552c1547b67810c77b865045cc8Mike Lockwood } 31410024b3dc12a8552c1547b67810c77b865045cc8Mike Lockwood 315acd4321872387212bbd956e134faf22eee4bbadcMike Lockwood // Constructor for MidiDeviceService.onCreate() 316acd4321872387212bbd956e134faf22eee4bbadcMike Lockwood /* package */ MidiDeviceServer(IMidiManager midiManager, MidiReceiver[] inputPortReceivers, 317acd4321872387212bbd956e134faf22eee4bbadcMike Lockwood MidiDeviceInfo deviceInfo, Callback callback) { 318acd4321872387212bbd956e134faf22eee4bbadcMike Lockwood this(midiManager, inputPortReceivers, deviceInfo.getOutputPortCount(), callback); 319acd4321872387212bbd956e134faf22eee4bbadcMike Lockwood mDeviceInfo = deviceInfo; 320acd4321872387212bbd956e134faf22eee4bbadcMike Lockwood } 321acd4321872387212bbd956e134faf22eee4bbadcMike Lockwood 32210024b3dc12a8552c1547b67810c77b865045cc8Mike Lockwood /* package */ IMidiDeviceServer getBinderInterface() { 32310024b3dc12a8552c1547b67810c77b865045cc8Mike Lockwood return mServer; 32410024b3dc12a8552c1547b67810c77b865045cc8Mike Lockwood } 32510024b3dc12a8552c1547b67810c77b865045cc8Mike Lockwood 326f0a41d1c591193fbe02c9ddbaf24c79af4da9972Mike Lockwood public IBinder asBinder() { 327f0a41d1c591193fbe02c9ddbaf24c79af4da9972Mike Lockwood return mServer.asBinder(); 328f0a41d1c591193fbe02c9ddbaf24c79af4da9972Mike Lockwood } 329f0a41d1c591193fbe02c9ddbaf24c79af4da9972Mike Lockwood 3305ff9e2a1719f78cddc7a23d6572ab15ab595dafdMike Lockwood private void updateDeviceStatus() { 3315ff9e2a1719f78cddc7a23d6572ab15ab595dafdMike Lockwood // clear calling identity, since we may be in a Binder call from one of our clients 3325ff9e2a1719f78cddc7a23d6572ab15ab595dafdMike Lockwood long identityToken = Binder.clearCallingIdentity(); 3335ff9e2a1719f78cddc7a23d6572ab15ab595dafdMike Lockwood 33481b9f7d325a552c54e793b51f571ae3d65b26e94Mike Lockwood MidiDeviceStatus status = new MidiDeviceStatus(mDeviceInfo, mInputPortOpen, 3355ff9e2a1719f78cddc7a23d6572ab15ab595dafdMike Lockwood mOutputPortOpenCount); 3365ff9e2a1719f78cddc7a23d6572ab15ab595dafdMike Lockwood if (mCallback != null) { 3375ff9e2a1719f78cddc7a23d6572ab15ab595dafdMike Lockwood mCallback.onDeviceStatusChanged(this, status); 3385ff9e2a1719f78cddc7a23d6572ab15ab595dafdMike Lockwood } 3395ff9e2a1719f78cddc7a23d6572ab15ab595dafdMike Lockwood try { 340e0a6ca64fac5bd4f10139321604031816e90adb4Mike Lockwood mMidiManager.setDeviceStatus(mServer, status); 3415ff9e2a1719f78cddc7a23d6572ab15ab595dafdMike Lockwood } catch (RemoteException e) { 3425ff9e2a1719f78cddc7a23d6572ab15ab595dafdMike Lockwood Log.e(TAG, "RemoteException in updateDeviceStatus"); 3435ff9e2a1719f78cddc7a23d6572ab15ab595dafdMike Lockwood } finally { 3445ff9e2a1719f78cddc7a23d6572ab15ab595dafdMike Lockwood Binder.restoreCallingIdentity(identityToken); 3455ff9e2a1719f78cddc7a23d6572ab15ab595dafdMike Lockwood } 3465ff9e2a1719f78cddc7a23d6572ab15ab595dafdMike Lockwood } 3475ff9e2a1719f78cddc7a23d6572ab15ab595dafdMike Lockwood 34810024b3dc12a8552c1547b67810c77b865045cc8Mike Lockwood @Override 34910024b3dc12a8552c1547b67810c77b865045cc8Mike Lockwood public void close() throws IOException { 350be215dd57282888b05b234c39bba44cc0a864b8aMike Lockwood synchronized (mGuard) { 3515ff9e2a1719f78cddc7a23d6572ab15ab595dafdMike Lockwood if (mIsClosed) return; 352be215dd57282888b05b234c39bba44cc0a864b8aMike Lockwood mGuard.close(); 353be215dd57282888b05b234c39bba44cc0a864b8aMike Lockwood 354be215dd57282888b05b234c39bba44cc0a864b8aMike Lockwood for (int i = 0; i < mInputPortCount; i++) { 355be215dd57282888b05b234c39bba44cc0a864b8aMike Lockwood MidiOutputPort outputPort = mInputPortOutputPorts[i]; 356be215dd57282888b05b234c39bba44cc0a864b8aMike Lockwood if (outputPort != null) { 357be215dd57282888b05b234c39bba44cc0a864b8aMike Lockwood IoUtils.closeQuietly(outputPort); 358be215dd57282888b05b234c39bba44cc0a864b8aMike Lockwood mInputPortOutputPorts[i] = null; 359be215dd57282888b05b234c39bba44cc0a864b8aMike Lockwood } 360be215dd57282888b05b234c39bba44cc0a864b8aMike Lockwood } 361be215dd57282888b05b234c39bba44cc0a864b8aMike Lockwood for (MidiInputPort inputPort : mInputPorts) { 362be215dd57282888b05b234c39bba44cc0a864b8aMike Lockwood IoUtils.closeQuietly(inputPort); 363be215dd57282888b05b234c39bba44cc0a864b8aMike Lockwood } 364be215dd57282888b05b234c39bba44cc0a864b8aMike Lockwood mInputPorts.clear(); 365be215dd57282888b05b234c39bba44cc0a864b8aMike Lockwood try { 366be215dd57282888b05b234c39bba44cc0a864b8aMike Lockwood mMidiManager.unregisterDeviceServer(mServer); 367be215dd57282888b05b234c39bba44cc0a864b8aMike Lockwood } catch (RemoteException e) { 368be215dd57282888b05b234c39bba44cc0a864b8aMike Lockwood Log.e(TAG, "RemoteException in unregisterDeviceServer"); 369be215dd57282888b05b234c39bba44cc0a864b8aMike Lockwood } 3705ff9e2a1719f78cddc7a23d6572ab15ab595dafdMike Lockwood mIsClosed = true; 371be215dd57282888b05b234c39bba44cc0a864b8aMike Lockwood } 372be215dd57282888b05b234c39bba44cc0a864b8aMike Lockwood } 373be215dd57282888b05b234c39bba44cc0a864b8aMike Lockwood 374be215dd57282888b05b234c39bba44cc0a864b8aMike Lockwood @Override 375be215dd57282888b05b234c39bba44cc0a864b8aMike Lockwood protected void finalize() throws Throwable { 37610024b3dc12a8552c1547b67810c77b865045cc8Mike Lockwood try { 377be215dd57282888b05b234c39bba44cc0a864b8aMike Lockwood mGuard.warnIfOpen(); 378be215dd57282888b05b234c39bba44cc0a864b8aMike Lockwood close(); 379be215dd57282888b05b234c39bba44cc0a864b8aMike Lockwood } finally { 380be215dd57282888b05b234c39bba44cc0a864b8aMike Lockwood super.finalize(); 38110024b3dc12a8552c1547b67810c77b865045cc8Mike Lockwood } 38210024b3dc12a8552c1547b67810c77b865045cc8Mike Lockwood } 38310024b3dc12a8552c1547b67810c77b865045cc8Mike Lockwood 38410024b3dc12a8552c1547b67810c77b865045cc8Mike Lockwood /** 38511fd96d6ff25bc1d710448eab545fe09da55a5f5Mike Lockwood * Returns an array of {@link MidiReceiver} for the device's output ports. 38611fd96d6ff25bc1d710448eab545fe09da55a5f5Mike Lockwood * Clients can use these receivers to send data out the device's output ports. 38711fd96d6ff25bc1d710448eab545fe09da55a5f5Mike Lockwood * @return array of MidiReceivers 38810024b3dc12a8552c1547b67810c77b865045cc8Mike Lockwood */ 38911fd96d6ff25bc1d710448eab545fe09da55a5f5Mike Lockwood public MidiReceiver[] getOutputPortReceivers() { 39011fd96d6ff25bc1d710448eab545fe09da55a5f5Mike Lockwood MidiReceiver[] receivers = new MidiReceiver[mOutputPortCount]; 39111fd96d6ff25bc1d710448eab545fe09da55a5f5Mike Lockwood System.arraycopy(mOutputPortDispatchers, 0, receivers, 0, mOutputPortCount); 39211fd96d6ff25bc1d710448eab545fe09da55a5f5Mike Lockwood return receivers; 39310024b3dc12a8552c1547b67810c77b865045cc8Mike Lockwood } 39410024b3dc12a8552c1547b67810c77b865045cc8Mike Lockwood} 395