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