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