MidiDeviceServer.java revision 5ff9e2a1719f78cddc7a23d6572ab15ab595dafd
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 27be215dd57282888b05b234c39bba44cc0a864b8aMike Lockwoodimport dalvik.system.CloseGuard; 28be215dd57282888b05b234c39bba44cc0a864b8aMike Lockwood 2911fd96d6ff25bc1d710448eab545fe09da55a5f5Mike Lockwoodimport libcore.io.IoUtils; 3011fd96d6ff25bc1d710448eab545fe09da55a5f5Mike Lockwood 3110024b3dc12a8552c1547b67810c77b865045cc8Mike Lockwoodimport java.io.Closeable; 3210024b3dc12a8552c1547b67810c77b865045cc8Mike Lockwoodimport java.io.IOException; 334a3d7ed45d98ad2fe900221755845b87f26b554aMike Lockwoodimport java.util.HashMap; 34be215dd57282888b05b234c39bba44cc0a864b8aMike Lockwoodimport java.util.concurrent.CopyOnWriteArrayList; 3510024b3dc12a8552c1547b67810c77b865045cc8Mike Lockwood 36a7e348eb4d2ef1632f2ebe3a742743607ccfd82bMike Lockwood/** 3711fd96d6ff25bc1d710448eab545fe09da55a5f5Mike Lockwood * Internal class used for providing an implementation for a MIDI device. 38a7e348eb4d2ef1632f2ebe3a742743607ccfd82bMike Lockwood * 39a7e348eb4d2ef1632f2ebe3a742743607ccfd82bMike Lockwood * @hide 40a7e348eb4d2ef1632f2ebe3a742743607ccfd82bMike Lockwood */ 4110024b3dc12a8552c1547b67810c77b865045cc8Mike Lockwoodpublic final class MidiDeviceServer implements Closeable { 4210024b3dc12a8552c1547b67810c77b865045cc8Mike Lockwood private static final String TAG = "MidiDeviceServer"; 4310024b3dc12a8552c1547b67810c77b865045cc8Mike Lockwood 4410024b3dc12a8552c1547b67810c77b865045cc8Mike Lockwood private final IMidiManager mMidiManager; 4510024b3dc12a8552c1547b67810c77b865045cc8Mike Lockwood 4610024b3dc12a8552c1547b67810c77b865045cc8Mike Lockwood // MidiDeviceInfo for the device implemented by this server 4710024b3dc12a8552c1547b67810c77b865045cc8Mike Lockwood private MidiDeviceInfo mDeviceInfo; 4811fd96d6ff25bc1d710448eab545fe09da55a5f5Mike Lockwood private final int mInputPortCount; 4911fd96d6ff25bc1d710448eab545fe09da55a5f5Mike Lockwood private final int mOutputPortCount; 5010024b3dc12a8552c1547b67810c77b865045cc8Mike Lockwood 5111fd96d6ff25bc1d710448eab545fe09da55a5f5Mike Lockwood // MidiReceivers for receiving data on our input ports 5211fd96d6ff25bc1d710448eab545fe09da55a5f5Mike Lockwood private final MidiReceiver[] mInputPortReceivers; 5310024b3dc12a8552c1547b67810c77b865045cc8Mike Lockwood 5411fd96d6ff25bc1d710448eab545fe09da55a5f5Mike Lockwood // MidiDispatchers for sending data on our output ports 5511fd96d6ff25bc1d710448eab545fe09da55a5f5Mike Lockwood private MidiDispatcher[] mOutputPortDispatchers; 5610024b3dc12a8552c1547b67810c77b865045cc8Mike Lockwood 5711fd96d6ff25bc1d710448eab545fe09da55a5f5Mike Lockwood // MidiOutputPorts for clients connected to our input ports 5811fd96d6ff25bc1d710448eab545fe09da55a5f5Mike Lockwood private final MidiOutputPort[] mInputPortOutputPorts; 5910024b3dc12a8552c1547b67810c77b865045cc8Mike Lockwood 60be215dd57282888b05b234c39bba44cc0a864b8aMike Lockwood // List of all MidiInputPorts we created 61be215dd57282888b05b234c39bba44cc0a864b8aMike Lockwood private final CopyOnWriteArrayList<MidiInputPort> mInputPorts 62be215dd57282888b05b234c39bba44cc0a864b8aMike Lockwood = new CopyOnWriteArrayList<MidiInputPort>(); 63be215dd57282888b05b234c39bba44cc0a864b8aMike Lockwood 645ff9e2a1719f78cddc7a23d6572ab15ab595dafdMike Lockwood 655ff9e2a1719f78cddc7a23d6572ab15ab595dafdMike Lockwood // for reporting device status 665ff9e2a1719f78cddc7a23d6572ab15ab595dafdMike Lockwood private final IBinder mDeviceStatusToken = new Binder(); 675ff9e2a1719f78cddc7a23d6572ab15ab595dafdMike Lockwood private final boolean[] mInputPortBusy; 685ff9e2a1719f78cddc7a23d6572ab15ab595dafdMike Lockwood private final int[] mOutputPortOpenCount; 695ff9e2a1719f78cddc7a23d6572ab15ab595dafdMike Lockwood 70be215dd57282888b05b234c39bba44cc0a864b8aMike Lockwood private final CloseGuard mGuard = CloseGuard.get(); 715ff9e2a1719f78cddc7a23d6572ab15ab595dafdMike Lockwood private boolean mIsClosed; 725ff9e2a1719f78cddc7a23d6572ab15ab595dafdMike Lockwood 735ff9e2a1719f78cddc7a23d6572ab15ab595dafdMike Lockwood private final Callback mCallback; 745ff9e2a1719f78cddc7a23d6572ab15ab595dafdMike Lockwood 755ff9e2a1719f78cddc7a23d6572ab15ab595dafdMike Lockwood public interface Callback { 765ff9e2a1719f78cddc7a23d6572ab15ab595dafdMike Lockwood /** 775ff9e2a1719f78cddc7a23d6572ab15ab595dafdMike Lockwood * Called to notify when an our device status has changed 785ff9e2a1719f78cddc7a23d6572ab15ab595dafdMike Lockwood * @param server the {@link MidiDeviceServer} that changed 795ff9e2a1719f78cddc7a23d6572ab15ab595dafdMike Lockwood * @param status the {@link MidiDeviceStatus} for the device 805ff9e2a1719f78cddc7a23d6572ab15ab595dafdMike Lockwood */ 815ff9e2a1719f78cddc7a23d6572ab15ab595dafdMike Lockwood public void onDeviceStatusChanged(MidiDeviceServer server, MidiDeviceStatus status); 825ff9e2a1719f78cddc7a23d6572ab15ab595dafdMike Lockwood } 83be215dd57282888b05b234c39bba44cc0a864b8aMike Lockwood 844a3d7ed45d98ad2fe900221755845b87f26b554aMike Lockwood abstract private class PortClient implements IBinder.DeathRecipient { 854a3d7ed45d98ad2fe900221755845b87f26b554aMike Lockwood final IBinder mToken; 864a3d7ed45d98ad2fe900221755845b87f26b554aMike Lockwood 874a3d7ed45d98ad2fe900221755845b87f26b554aMike Lockwood PortClient(IBinder token) { 884a3d7ed45d98ad2fe900221755845b87f26b554aMike Lockwood mToken = token; 894a3d7ed45d98ad2fe900221755845b87f26b554aMike Lockwood 904a3d7ed45d98ad2fe900221755845b87f26b554aMike Lockwood try { 914a3d7ed45d98ad2fe900221755845b87f26b554aMike Lockwood token.linkToDeath(this, 0); 924a3d7ed45d98ad2fe900221755845b87f26b554aMike Lockwood } catch (RemoteException e) { 934a3d7ed45d98ad2fe900221755845b87f26b554aMike Lockwood close(); 944a3d7ed45d98ad2fe900221755845b87f26b554aMike Lockwood } 954a3d7ed45d98ad2fe900221755845b87f26b554aMike Lockwood } 964a3d7ed45d98ad2fe900221755845b87f26b554aMike Lockwood 974a3d7ed45d98ad2fe900221755845b87f26b554aMike Lockwood abstract void close(); 984a3d7ed45d98ad2fe900221755845b87f26b554aMike Lockwood 994a3d7ed45d98ad2fe900221755845b87f26b554aMike Lockwood @Override 1004a3d7ed45d98ad2fe900221755845b87f26b554aMike Lockwood public void binderDied() { 1014a3d7ed45d98ad2fe900221755845b87f26b554aMike Lockwood close(); 1024a3d7ed45d98ad2fe900221755845b87f26b554aMike Lockwood } 1034a3d7ed45d98ad2fe900221755845b87f26b554aMike Lockwood } 1044a3d7ed45d98ad2fe900221755845b87f26b554aMike Lockwood 1054a3d7ed45d98ad2fe900221755845b87f26b554aMike Lockwood private class InputPortClient extends PortClient { 1064a3d7ed45d98ad2fe900221755845b87f26b554aMike Lockwood private final MidiOutputPort mOutputPort; 1074a3d7ed45d98ad2fe900221755845b87f26b554aMike Lockwood 1084a3d7ed45d98ad2fe900221755845b87f26b554aMike Lockwood InputPortClient(IBinder token, MidiOutputPort outputPort) { 1094a3d7ed45d98ad2fe900221755845b87f26b554aMike Lockwood super(token); 1104a3d7ed45d98ad2fe900221755845b87f26b554aMike Lockwood mOutputPort = outputPort; 1114a3d7ed45d98ad2fe900221755845b87f26b554aMike Lockwood } 1124a3d7ed45d98ad2fe900221755845b87f26b554aMike Lockwood 1134a3d7ed45d98ad2fe900221755845b87f26b554aMike Lockwood @Override 1144a3d7ed45d98ad2fe900221755845b87f26b554aMike Lockwood void close() { 1154a3d7ed45d98ad2fe900221755845b87f26b554aMike Lockwood mToken.unlinkToDeath(this, 0); 1164a3d7ed45d98ad2fe900221755845b87f26b554aMike Lockwood synchronized (mInputPortOutputPorts) { 1175ff9e2a1719f78cddc7a23d6572ab15ab595dafdMike Lockwood int portNumber = mOutputPort.getPortNumber(); 1185ff9e2a1719f78cddc7a23d6572ab15ab595dafdMike Lockwood mInputPortOutputPorts[portNumber] = null; 1195ff9e2a1719f78cddc7a23d6572ab15ab595dafdMike Lockwood mInputPortBusy[portNumber] = false; 1205ff9e2a1719f78cddc7a23d6572ab15ab595dafdMike Lockwood updateDeviceStatus(); 1214a3d7ed45d98ad2fe900221755845b87f26b554aMike Lockwood } 1224a3d7ed45d98ad2fe900221755845b87f26b554aMike Lockwood IoUtils.closeQuietly(mOutputPort); 1234a3d7ed45d98ad2fe900221755845b87f26b554aMike Lockwood } 1244a3d7ed45d98ad2fe900221755845b87f26b554aMike Lockwood } 1254a3d7ed45d98ad2fe900221755845b87f26b554aMike Lockwood 1264a3d7ed45d98ad2fe900221755845b87f26b554aMike Lockwood private class OutputPortClient extends PortClient { 1274a3d7ed45d98ad2fe900221755845b87f26b554aMike Lockwood private final MidiInputPort mInputPort; 1284a3d7ed45d98ad2fe900221755845b87f26b554aMike Lockwood 1294a3d7ed45d98ad2fe900221755845b87f26b554aMike Lockwood OutputPortClient(IBinder token, MidiInputPort inputPort) { 1304a3d7ed45d98ad2fe900221755845b87f26b554aMike Lockwood super(token); 1314a3d7ed45d98ad2fe900221755845b87f26b554aMike Lockwood mInputPort = inputPort; 1324a3d7ed45d98ad2fe900221755845b87f26b554aMike Lockwood } 1334a3d7ed45d98ad2fe900221755845b87f26b554aMike Lockwood 1344a3d7ed45d98ad2fe900221755845b87f26b554aMike Lockwood @Override 1354a3d7ed45d98ad2fe900221755845b87f26b554aMike Lockwood void close() { 1364a3d7ed45d98ad2fe900221755845b87f26b554aMike Lockwood mToken.unlinkToDeath(this, 0); 1375ff9e2a1719f78cddc7a23d6572ab15ab595dafdMike Lockwood int portNumber = mInputPort.getPortNumber(); 1385ff9e2a1719f78cddc7a23d6572ab15ab595dafdMike Lockwood MidiDispatcher dispatcher = mOutputPortDispatchers[portNumber]; 1395ff9e2a1719f78cddc7a23d6572ab15ab595dafdMike Lockwood synchronized (dispatcher) { 1405ff9e2a1719f78cddc7a23d6572ab15ab595dafdMike Lockwood dispatcher.getSender().disconnect(mInputPort); 1415ff9e2a1719f78cddc7a23d6572ab15ab595dafdMike Lockwood int openCount = dispatcher.getReceiverCount(); 1425ff9e2a1719f78cddc7a23d6572ab15ab595dafdMike Lockwood mOutputPortOpenCount[portNumber] = openCount; 1435ff9e2a1719f78cddc7a23d6572ab15ab595dafdMike Lockwood updateDeviceStatus(); 1445ff9e2a1719f78cddc7a23d6572ab15ab595dafdMike Lockwood } 1455ff9e2a1719f78cddc7a23d6572ab15ab595dafdMike Lockwood 146be215dd57282888b05b234c39bba44cc0a864b8aMike Lockwood mInputPorts.remove(mInputPort); 1474a3d7ed45d98ad2fe900221755845b87f26b554aMike Lockwood IoUtils.closeQuietly(mInputPort); 1484a3d7ed45d98ad2fe900221755845b87f26b554aMike Lockwood } 1494a3d7ed45d98ad2fe900221755845b87f26b554aMike Lockwood } 1504a3d7ed45d98ad2fe900221755845b87f26b554aMike Lockwood 1514a3d7ed45d98ad2fe900221755845b87f26b554aMike Lockwood private final HashMap<IBinder, PortClient> mPortClients = new HashMap<IBinder, PortClient>(); 1524a3d7ed45d98ad2fe900221755845b87f26b554aMike Lockwood 15310024b3dc12a8552c1547b67810c77b865045cc8Mike Lockwood // Binder interface stub for receiving connection requests from clients 15410024b3dc12a8552c1547b67810c77b865045cc8Mike Lockwood private final IMidiDeviceServer mServer = new IMidiDeviceServer.Stub() { 15510024b3dc12a8552c1547b67810c77b865045cc8Mike Lockwood 15610024b3dc12a8552c1547b67810c77b865045cc8Mike Lockwood @Override 1574a3d7ed45d98ad2fe900221755845b87f26b554aMike Lockwood public ParcelFileDescriptor openInputPort(IBinder token, int portNumber) { 15811fd96d6ff25bc1d710448eab545fe09da55a5f5Mike Lockwood if (mDeviceInfo.isPrivate()) { 15911fd96d6ff25bc1d710448eab545fe09da55a5f5Mike Lockwood if (Binder.getCallingUid() != Process.myUid()) { 16011fd96d6ff25bc1d710448eab545fe09da55a5f5Mike Lockwood throw new SecurityException("Can't access private device from different UID"); 16111fd96d6ff25bc1d710448eab545fe09da55a5f5Mike Lockwood } 16211fd96d6ff25bc1d710448eab545fe09da55a5f5Mike Lockwood } 16311fd96d6ff25bc1d710448eab545fe09da55a5f5Mike Lockwood 16410024b3dc12a8552c1547b67810c77b865045cc8Mike Lockwood if (portNumber < 0 || portNumber >= mInputPortCount) { 16510024b3dc12a8552c1547b67810c77b865045cc8Mike Lockwood Log.e(TAG, "portNumber out of range in openInputPort: " + portNumber); 16610024b3dc12a8552c1547b67810c77b865045cc8Mike Lockwood return null; 16710024b3dc12a8552c1547b67810c77b865045cc8Mike Lockwood } 16810024b3dc12a8552c1547b67810c77b865045cc8Mike Lockwood 16911fd96d6ff25bc1d710448eab545fe09da55a5f5Mike Lockwood synchronized (mInputPortOutputPorts) { 17011fd96d6ff25bc1d710448eab545fe09da55a5f5Mike Lockwood if (mInputPortOutputPorts[portNumber] != null) { 17110024b3dc12a8552c1547b67810c77b865045cc8Mike Lockwood Log.d(TAG, "port " + portNumber + " already open"); 17210024b3dc12a8552c1547b67810c77b865045cc8Mike Lockwood return null; 17310024b3dc12a8552c1547b67810c77b865045cc8Mike Lockwood } 17410024b3dc12a8552c1547b67810c77b865045cc8Mike Lockwood 17510024b3dc12a8552c1547b67810c77b865045cc8Mike Lockwood try { 17610024b3dc12a8552c1547b67810c77b865045cc8Mike Lockwood ParcelFileDescriptor[] pair = ParcelFileDescriptor.createSocketPair( 17710024b3dc12a8552c1547b67810c77b865045cc8Mike Lockwood OsConstants.SOCK_SEQPACKET); 1784a3d7ed45d98ad2fe900221755845b87f26b554aMike Lockwood MidiOutputPort outputPort = new MidiOutputPort(pair[0], portNumber); 17911fd96d6ff25bc1d710448eab545fe09da55a5f5Mike Lockwood mInputPortOutputPorts[portNumber] = outputPort; 1804a3d7ed45d98ad2fe900221755845b87f26b554aMike Lockwood outputPort.connect(mInputPortReceivers[portNumber]); 1814a3d7ed45d98ad2fe900221755845b87f26b554aMike Lockwood InputPortClient client = new InputPortClient(token, outputPort); 1824a3d7ed45d98ad2fe900221755845b87f26b554aMike Lockwood synchronized (mPortClients) { 1834a3d7ed45d98ad2fe900221755845b87f26b554aMike Lockwood mPortClients.put(token, client); 1844a3d7ed45d98ad2fe900221755845b87f26b554aMike Lockwood } 1855ff9e2a1719f78cddc7a23d6572ab15ab595dafdMike Lockwood mInputPortBusy[portNumber] = true; 1865ff9e2a1719f78cddc7a23d6572ab15ab595dafdMike Lockwood updateDeviceStatus(); 18711fd96d6ff25bc1d710448eab545fe09da55a5f5Mike Lockwood return pair[1]; 18898cc8e5a6473b3a5802d97cc81020ec4e3cd23f3Mike Lockwood } catch (IOException e) { 18998cc8e5a6473b3a5802d97cc81020ec4e3cd23f3Mike Lockwood Log.e(TAG, "unable to create ParcelFileDescriptors in openInputPort"); 19098cc8e5a6473b3a5802d97cc81020ec4e3cd23f3Mike Lockwood return null; 19110024b3dc12a8552c1547b67810c77b865045cc8Mike Lockwood } 19210024b3dc12a8552c1547b67810c77b865045cc8Mike Lockwood } 19310024b3dc12a8552c1547b67810c77b865045cc8Mike Lockwood } 19410024b3dc12a8552c1547b67810c77b865045cc8Mike Lockwood 19510024b3dc12a8552c1547b67810c77b865045cc8Mike Lockwood @Override 1964a3d7ed45d98ad2fe900221755845b87f26b554aMike Lockwood public ParcelFileDescriptor openOutputPort(IBinder token, int portNumber) { 19711fd96d6ff25bc1d710448eab545fe09da55a5f5Mike Lockwood if (mDeviceInfo.isPrivate()) { 19811fd96d6ff25bc1d710448eab545fe09da55a5f5Mike Lockwood if (Binder.getCallingUid() != Process.myUid()) { 19911fd96d6ff25bc1d710448eab545fe09da55a5f5Mike Lockwood throw new SecurityException("Can't access private device from different UID"); 20011fd96d6ff25bc1d710448eab545fe09da55a5f5Mike Lockwood } 20111fd96d6ff25bc1d710448eab545fe09da55a5f5Mike Lockwood } 20211fd96d6ff25bc1d710448eab545fe09da55a5f5Mike Lockwood 20310024b3dc12a8552c1547b67810c77b865045cc8Mike Lockwood if (portNumber < 0 || portNumber >= mOutputPortCount) { 20410024b3dc12a8552c1547b67810c77b865045cc8Mike Lockwood Log.e(TAG, "portNumber out of range in openOutputPort: " + portNumber); 20510024b3dc12a8552c1547b67810c77b865045cc8Mike Lockwood return null; 20610024b3dc12a8552c1547b67810c77b865045cc8Mike Lockwood } 20711fd96d6ff25bc1d710448eab545fe09da55a5f5Mike Lockwood 20811fd96d6ff25bc1d710448eab545fe09da55a5f5Mike Lockwood try { 20911fd96d6ff25bc1d710448eab545fe09da55a5f5Mike Lockwood ParcelFileDescriptor[] pair = ParcelFileDescriptor.createSocketPair( 21011fd96d6ff25bc1d710448eab545fe09da55a5f5Mike Lockwood OsConstants.SOCK_SEQPACKET); 2114a3d7ed45d98ad2fe900221755845b87f26b554aMike Lockwood MidiInputPort inputPort = new MidiInputPort(pair[0], portNumber); 2125ff9e2a1719f78cddc7a23d6572ab15ab595dafdMike Lockwood MidiDispatcher dispatcher = mOutputPortDispatchers[portNumber]; 2135ff9e2a1719f78cddc7a23d6572ab15ab595dafdMike Lockwood synchronized (dispatcher) { 2145ff9e2a1719f78cddc7a23d6572ab15ab595dafdMike Lockwood dispatcher.getSender().connect(inputPort); 2155ff9e2a1719f78cddc7a23d6572ab15ab595dafdMike Lockwood int openCount = dispatcher.getReceiverCount(); 2165ff9e2a1719f78cddc7a23d6572ab15ab595dafdMike Lockwood mOutputPortOpenCount[portNumber] = openCount; 2175ff9e2a1719f78cddc7a23d6572ab15ab595dafdMike Lockwood updateDeviceStatus(); 2185ff9e2a1719f78cddc7a23d6572ab15ab595dafdMike Lockwood } 2195ff9e2a1719f78cddc7a23d6572ab15ab595dafdMike Lockwood 220be215dd57282888b05b234c39bba44cc0a864b8aMike Lockwood mInputPorts.add(inputPort); 2214a3d7ed45d98ad2fe900221755845b87f26b554aMike Lockwood OutputPortClient client = new OutputPortClient(token, inputPort); 2224a3d7ed45d98ad2fe900221755845b87f26b554aMike Lockwood synchronized (mPortClients) { 2234a3d7ed45d98ad2fe900221755845b87f26b554aMike Lockwood mPortClients.put(token, client); 2244a3d7ed45d98ad2fe900221755845b87f26b554aMike Lockwood } 22511fd96d6ff25bc1d710448eab545fe09da55a5f5Mike Lockwood return pair[1]; 22611fd96d6ff25bc1d710448eab545fe09da55a5f5Mike Lockwood } catch (IOException e) { 22711fd96d6ff25bc1d710448eab545fe09da55a5f5Mike Lockwood Log.e(TAG, "unable to create ParcelFileDescriptors in openOutputPort"); 22811fd96d6ff25bc1d710448eab545fe09da55a5f5Mike Lockwood return null; 22910024b3dc12a8552c1547b67810c77b865045cc8Mike Lockwood } 23010024b3dc12a8552c1547b67810c77b865045cc8Mike Lockwood } 2314a3d7ed45d98ad2fe900221755845b87f26b554aMike Lockwood 2324a3d7ed45d98ad2fe900221755845b87f26b554aMike Lockwood @Override 2334a3d7ed45d98ad2fe900221755845b87f26b554aMike Lockwood public void closePort(IBinder token) { 2344a3d7ed45d98ad2fe900221755845b87f26b554aMike Lockwood synchronized (mPortClients) { 2354a3d7ed45d98ad2fe900221755845b87f26b554aMike Lockwood PortClient client = mPortClients.remove(token); 2364a3d7ed45d98ad2fe900221755845b87f26b554aMike Lockwood if (client != null) { 2374a3d7ed45d98ad2fe900221755845b87f26b554aMike Lockwood client.close(); 2384a3d7ed45d98ad2fe900221755845b87f26b554aMike Lockwood } 2394a3d7ed45d98ad2fe900221755845b87f26b554aMike Lockwood } 2404a3d7ed45d98ad2fe900221755845b87f26b554aMike Lockwood } 24146326e59a0a19367d4158c027d56d4b8440e8d3dMike Lockwood 24246326e59a0a19367d4158c027d56d4b8440e8d3dMike Lockwood @Override 24346326e59a0a19367d4158c027d56d4b8440e8d3dMike Lockwood public void connectPorts(IBinder token, ParcelFileDescriptor pfd, 24446326e59a0a19367d4158c027d56d4b8440e8d3dMike Lockwood int outputPortNumber) { 24546326e59a0a19367d4158c027d56d4b8440e8d3dMike Lockwood MidiInputPort inputPort = new MidiInputPort(pfd, outputPortNumber); 24646326e59a0a19367d4158c027d56d4b8440e8d3dMike Lockwood mOutputPortDispatchers[outputPortNumber].getSender().connect(inputPort); 24746326e59a0a19367d4158c027d56d4b8440e8d3dMike Lockwood mInputPorts.add(inputPort); 24846326e59a0a19367d4158c027d56d4b8440e8d3dMike Lockwood OutputPortClient client = new OutputPortClient(token, inputPort); 24946326e59a0a19367d4158c027d56d4b8440e8d3dMike Lockwood synchronized (mPortClients) { 25046326e59a0a19367d4158c027d56d4b8440e8d3dMike Lockwood mPortClients.put(token, client); 25146326e59a0a19367d4158c027d56d4b8440e8d3dMike Lockwood } 25246326e59a0a19367d4158c027d56d4b8440e8d3dMike Lockwood } 25310024b3dc12a8552c1547b67810c77b865045cc8Mike Lockwood }; 25410024b3dc12a8552c1547b67810c77b865045cc8Mike Lockwood 25511fd96d6ff25bc1d710448eab545fe09da55a5f5Mike Lockwood /* package */ MidiDeviceServer(IMidiManager midiManager, MidiReceiver[] inputPortReceivers, 2565ff9e2a1719f78cddc7a23d6572ab15ab595dafdMike Lockwood int numOutputPorts, Callback callback) { 25710024b3dc12a8552c1547b67810c77b865045cc8Mike Lockwood mMidiManager = midiManager; 25811fd96d6ff25bc1d710448eab545fe09da55a5f5Mike Lockwood mInputPortReceivers = inputPortReceivers; 25911fd96d6ff25bc1d710448eab545fe09da55a5f5Mike Lockwood mInputPortCount = inputPortReceivers.length; 26011fd96d6ff25bc1d710448eab545fe09da55a5f5Mike Lockwood mOutputPortCount = numOutputPorts; 2615ff9e2a1719f78cddc7a23d6572ab15ab595dafdMike Lockwood mCallback = callback; 26211fd96d6ff25bc1d710448eab545fe09da55a5f5Mike Lockwood 26311fd96d6ff25bc1d710448eab545fe09da55a5f5Mike Lockwood mInputPortOutputPorts = new MidiOutputPort[mInputPortCount]; 26411fd96d6ff25bc1d710448eab545fe09da55a5f5Mike Lockwood 26511fd96d6ff25bc1d710448eab545fe09da55a5f5Mike Lockwood mOutputPortDispatchers = new MidiDispatcher[numOutputPorts]; 26611fd96d6ff25bc1d710448eab545fe09da55a5f5Mike Lockwood for (int i = 0; i < numOutputPorts; i++) { 26711fd96d6ff25bc1d710448eab545fe09da55a5f5Mike Lockwood mOutputPortDispatchers[i] = new MidiDispatcher(); 26811fd96d6ff25bc1d710448eab545fe09da55a5f5Mike Lockwood } 269be215dd57282888b05b234c39bba44cc0a864b8aMike Lockwood 2705ff9e2a1719f78cddc7a23d6572ab15ab595dafdMike Lockwood mInputPortBusy = new boolean[mInputPortCount]; 2715ff9e2a1719f78cddc7a23d6572ab15ab595dafdMike Lockwood mOutputPortOpenCount = new int[numOutputPorts]; 2725ff9e2a1719f78cddc7a23d6572ab15ab595dafdMike Lockwood 273be215dd57282888b05b234c39bba44cc0a864b8aMike Lockwood mGuard.open("close"); 27410024b3dc12a8552c1547b67810c77b865045cc8Mike Lockwood } 27510024b3dc12a8552c1547b67810c77b865045cc8Mike Lockwood 27610024b3dc12a8552c1547b67810c77b865045cc8Mike Lockwood /* package */ IMidiDeviceServer getBinderInterface() { 27710024b3dc12a8552c1547b67810c77b865045cc8Mike Lockwood return mServer; 27810024b3dc12a8552c1547b67810c77b865045cc8Mike Lockwood } 27910024b3dc12a8552c1547b67810c77b865045cc8Mike Lockwood 28010024b3dc12a8552c1547b67810c77b865045cc8Mike Lockwood /* package */ void setDeviceInfo(MidiDeviceInfo deviceInfo) { 28110024b3dc12a8552c1547b67810c77b865045cc8Mike Lockwood if (mDeviceInfo != null) { 28210024b3dc12a8552c1547b67810c77b865045cc8Mike Lockwood throw new IllegalStateException("setDeviceInfo should only be called once"); 28310024b3dc12a8552c1547b67810c77b865045cc8Mike Lockwood } 28410024b3dc12a8552c1547b67810c77b865045cc8Mike Lockwood mDeviceInfo = deviceInfo; 28510024b3dc12a8552c1547b67810c77b865045cc8Mike Lockwood } 28610024b3dc12a8552c1547b67810c77b865045cc8Mike Lockwood 2875ff9e2a1719f78cddc7a23d6572ab15ab595dafdMike Lockwood private void updateDeviceStatus() { 2885ff9e2a1719f78cddc7a23d6572ab15ab595dafdMike Lockwood // clear calling identity, since we may be in a Binder call from one of our clients 2895ff9e2a1719f78cddc7a23d6572ab15ab595dafdMike Lockwood long identityToken = Binder.clearCallingIdentity(); 2905ff9e2a1719f78cddc7a23d6572ab15ab595dafdMike Lockwood 2915ff9e2a1719f78cddc7a23d6572ab15ab595dafdMike Lockwood MidiDeviceStatus status = new MidiDeviceStatus(mDeviceInfo, mInputPortBusy, 2925ff9e2a1719f78cddc7a23d6572ab15ab595dafdMike Lockwood mOutputPortOpenCount); 2935ff9e2a1719f78cddc7a23d6572ab15ab595dafdMike Lockwood if (mCallback != null) { 2945ff9e2a1719f78cddc7a23d6572ab15ab595dafdMike Lockwood mCallback.onDeviceStatusChanged(this, status); 2955ff9e2a1719f78cddc7a23d6572ab15ab595dafdMike Lockwood } 2965ff9e2a1719f78cddc7a23d6572ab15ab595dafdMike Lockwood try { 2975ff9e2a1719f78cddc7a23d6572ab15ab595dafdMike Lockwood mMidiManager.setDeviceStatus(mDeviceStatusToken, status); 2985ff9e2a1719f78cddc7a23d6572ab15ab595dafdMike Lockwood } catch (RemoteException e) { 2995ff9e2a1719f78cddc7a23d6572ab15ab595dafdMike Lockwood Log.e(TAG, "RemoteException in updateDeviceStatus"); 3005ff9e2a1719f78cddc7a23d6572ab15ab595dafdMike Lockwood } finally { 3015ff9e2a1719f78cddc7a23d6572ab15ab595dafdMike Lockwood Binder.restoreCallingIdentity(identityToken); 3025ff9e2a1719f78cddc7a23d6572ab15ab595dafdMike Lockwood } 3035ff9e2a1719f78cddc7a23d6572ab15ab595dafdMike Lockwood } 3045ff9e2a1719f78cddc7a23d6572ab15ab595dafdMike Lockwood 30510024b3dc12a8552c1547b67810c77b865045cc8Mike Lockwood @Override 30610024b3dc12a8552c1547b67810c77b865045cc8Mike Lockwood public void close() throws IOException { 307be215dd57282888b05b234c39bba44cc0a864b8aMike Lockwood synchronized (mGuard) { 3085ff9e2a1719f78cddc7a23d6572ab15ab595dafdMike Lockwood if (mIsClosed) return; 309be215dd57282888b05b234c39bba44cc0a864b8aMike Lockwood mGuard.close(); 310be215dd57282888b05b234c39bba44cc0a864b8aMike Lockwood 311be215dd57282888b05b234c39bba44cc0a864b8aMike Lockwood for (int i = 0; i < mInputPortCount; i++) { 312be215dd57282888b05b234c39bba44cc0a864b8aMike Lockwood MidiOutputPort outputPort = mInputPortOutputPorts[i]; 313be215dd57282888b05b234c39bba44cc0a864b8aMike Lockwood if (outputPort != null) { 314be215dd57282888b05b234c39bba44cc0a864b8aMike Lockwood IoUtils.closeQuietly(outputPort); 315be215dd57282888b05b234c39bba44cc0a864b8aMike Lockwood mInputPortOutputPorts[i] = null; 316be215dd57282888b05b234c39bba44cc0a864b8aMike Lockwood } 317be215dd57282888b05b234c39bba44cc0a864b8aMike Lockwood } 318be215dd57282888b05b234c39bba44cc0a864b8aMike Lockwood for (MidiInputPort inputPort : mInputPorts) { 319be215dd57282888b05b234c39bba44cc0a864b8aMike Lockwood IoUtils.closeQuietly(inputPort); 320be215dd57282888b05b234c39bba44cc0a864b8aMike Lockwood } 321be215dd57282888b05b234c39bba44cc0a864b8aMike Lockwood mInputPorts.clear(); 322be215dd57282888b05b234c39bba44cc0a864b8aMike Lockwood try { 323be215dd57282888b05b234c39bba44cc0a864b8aMike Lockwood mMidiManager.unregisterDeviceServer(mServer); 324be215dd57282888b05b234c39bba44cc0a864b8aMike Lockwood } catch (RemoteException e) { 325be215dd57282888b05b234c39bba44cc0a864b8aMike Lockwood Log.e(TAG, "RemoteException in unregisterDeviceServer"); 326be215dd57282888b05b234c39bba44cc0a864b8aMike Lockwood } 3275ff9e2a1719f78cddc7a23d6572ab15ab595dafdMike Lockwood mIsClosed = true; 328be215dd57282888b05b234c39bba44cc0a864b8aMike Lockwood } 329be215dd57282888b05b234c39bba44cc0a864b8aMike Lockwood } 330be215dd57282888b05b234c39bba44cc0a864b8aMike Lockwood 331be215dd57282888b05b234c39bba44cc0a864b8aMike Lockwood @Override 332be215dd57282888b05b234c39bba44cc0a864b8aMike Lockwood protected void finalize() throws Throwable { 33310024b3dc12a8552c1547b67810c77b865045cc8Mike Lockwood try { 334be215dd57282888b05b234c39bba44cc0a864b8aMike Lockwood mGuard.warnIfOpen(); 335be215dd57282888b05b234c39bba44cc0a864b8aMike Lockwood close(); 336be215dd57282888b05b234c39bba44cc0a864b8aMike Lockwood } finally { 337be215dd57282888b05b234c39bba44cc0a864b8aMike Lockwood super.finalize(); 33810024b3dc12a8552c1547b67810c77b865045cc8Mike Lockwood } 33910024b3dc12a8552c1547b67810c77b865045cc8Mike Lockwood } 34010024b3dc12a8552c1547b67810c77b865045cc8Mike Lockwood 34110024b3dc12a8552c1547b67810c77b865045cc8Mike Lockwood /** 34211fd96d6ff25bc1d710448eab545fe09da55a5f5Mike Lockwood * Returns an array of {@link MidiReceiver} for the device's output ports. 34311fd96d6ff25bc1d710448eab545fe09da55a5f5Mike Lockwood * Clients can use these receivers to send data out the device's output ports. 34411fd96d6ff25bc1d710448eab545fe09da55a5f5Mike Lockwood * @return array of MidiReceivers 34510024b3dc12a8552c1547b67810c77b865045cc8Mike Lockwood */ 34611fd96d6ff25bc1d710448eab545fe09da55a5f5Mike Lockwood public MidiReceiver[] getOutputPortReceivers() { 34711fd96d6ff25bc1d710448eab545fe09da55a5f5Mike Lockwood MidiReceiver[] receivers = new MidiReceiver[mOutputPortCount]; 34811fd96d6ff25bc1d710448eab545fe09da55a5f5Mike Lockwood System.arraycopy(mOutputPortDispatchers, 0, receivers, 0, mOutputPortCount); 34911fd96d6ff25bc1d710448eab545fe09da55a5f5Mike Lockwood return receivers; 35010024b3dc12a8552c1547b67810c77b865045cc8Mike Lockwood } 35110024b3dc12a8552c1547b67810c77b865045cc8Mike Lockwood} 352