Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 5ff9e2a1 authored by Mike Lockwood's avatar Mike Lockwood
Browse files

MidiManager: Add MIDI device status notifications

MidiManager clients can be notified of device status changes via a new MidiDeviceStatus object.
MidiDeviceStatus contains the busy status of the device's input ports and number of
connections to the output ports.
MidiDeviceService now has an optional callback for receiving notifications when its ports change as well.

Change-Id: I1600df4464d82724bc026c27b9633ae9c412d3f0
parent 46326e59
Loading
Loading
Loading
Loading
+2 −0
Original line number Diff line number Diff line
@@ -17,10 +17,12 @@
package android.media.midi;

import android.media.midi.MidiDeviceInfo;
import android.media.midi.MidiDeviceStatus;

/** @hide */
oneway interface IMidiDeviceListener
{
    void onDeviceAdded(in MidiDeviceInfo device);
    void onDeviceRemoved(in MidiDeviceInfo device);
    void onDeviceStatusChanged(in MidiDeviceStatus status);
}
+8 −0
Original line number Diff line number Diff line
@@ -19,6 +19,7 @@ package android.media.midi;
import android.media.midi.IMidiDeviceListener;
import android.media.midi.IMidiDeviceServer;
import android.media.midi.MidiDeviceInfo;
import android.media.midi.MidiDeviceStatus;
import android.os.Bundle;
import android.os.IBinder;

@@ -44,4 +45,11 @@ interface IMidiManager
    // used by MidiDeviceService to access the MidiDeviceInfo that was created based on its
    // manifest's meta-data
    MidiDeviceInfo getServiceDeviceInfo(String packageName, String className);

    // used for client's to retrieve a device's MidiDeviceStatus
    MidiDeviceStatus getDeviceStatus(in MidiDeviceInfo deviceInfo);

    // used by MIDI devices to report their status
    // the token is used by MidiService for death notification
    void setDeviceStatus(IBinder token, in MidiDeviceStatus status);
}
+67 −5
Original line number Diff line number Diff line
@@ -16,8 +16,8 @@

package android.media.midi;

import android.os.IBinder;
import android.os.Binder;
import android.os.IBinder;
import android.os.ParcelFileDescriptor;
import android.os.Process;
import android.os.RemoteException;
@@ -61,7 +61,25 @@ public final class MidiDeviceServer implements Closeable {
    private final CopyOnWriteArrayList<MidiInputPort> mInputPorts
            = new CopyOnWriteArrayList<MidiInputPort>();


    // for reporting device status
    private final IBinder mDeviceStatusToken = new Binder();
    private final boolean[] mInputPortBusy;
    private final int[] mOutputPortOpenCount;

    private final CloseGuard mGuard = CloseGuard.get();
    private boolean mIsClosed;

    private final Callback mCallback;

    public interface Callback {
        /**
         * Called to notify when an our device status has changed
         * @param server the {@link MidiDeviceServer} that changed
         * @param status the {@link MidiDeviceStatus} for the device
         */
        public void onDeviceStatusChanged(MidiDeviceServer server, MidiDeviceStatus status);
    }

    abstract private class PortClient implements IBinder.DeathRecipient {
        final IBinder mToken;
@@ -96,7 +114,10 @@ public final class MidiDeviceServer implements Closeable {
        void close() {
            mToken.unlinkToDeath(this, 0);
            synchronized (mInputPortOutputPorts) {
                mInputPortOutputPorts[mOutputPort.getPortNumber()] = null;
                int portNumber = mOutputPort.getPortNumber();
                mInputPortOutputPorts[portNumber] = null;
                mInputPortBusy[portNumber] = false;
                updateDeviceStatus();
            }
            IoUtils.closeQuietly(mOutputPort);
        }
@@ -113,7 +134,15 @@ public final class MidiDeviceServer implements Closeable {
        @Override
        void close() {
            mToken.unlinkToDeath(this, 0);
            mOutputPortDispatchers[mInputPort.getPortNumber()].getSender().disconnect(mInputPort);
            int portNumber = mInputPort.getPortNumber();
            MidiDispatcher dispatcher = mOutputPortDispatchers[portNumber];
            synchronized (dispatcher) {
                dispatcher.getSender().disconnect(mInputPort);
                int openCount = dispatcher.getReceiverCount();
                mOutputPortOpenCount[portNumber] = openCount;
                updateDeviceStatus();
           }

            mInputPorts.remove(mInputPort);
            IoUtils.closeQuietly(mInputPort);
        }
@@ -153,6 +182,8 @@ public final class MidiDeviceServer implements Closeable {
                    synchronized (mPortClients) {
                        mPortClients.put(token, client);
                    }
                    mInputPortBusy[portNumber] = true;
                    updateDeviceStatus();
                    return pair[1];
                } catch (IOException e) {
                    Log.e(TAG, "unable to create ParcelFileDescriptors in openInputPort");
@@ -178,7 +209,14 @@ public final class MidiDeviceServer implements Closeable {
                ParcelFileDescriptor[] pair = ParcelFileDescriptor.createSocketPair(
                                                    OsConstants.SOCK_SEQPACKET);
                MidiInputPort inputPort = new MidiInputPort(pair[0], portNumber);
                mOutputPortDispatchers[portNumber].getSender().connect(inputPort);
                MidiDispatcher dispatcher = mOutputPortDispatchers[portNumber];
                synchronized (dispatcher) {
                    dispatcher.getSender().connect(inputPort);
                    int openCount = dispatcher.getReceiverCount();
                    mOutputPortOpenCount[portNumber] = openCount;
                    updateDeviceStatus();
                }

                mInputPorts.add(inputPort);
                OutputPortClient client = new OutputPortClient(token, inputPort);
                synchronized (mPortClients) {
@@ -215,11 +253,12 @@ public final class MidiDeviceServer implements Closeable {
    };

    /* package */ MidiDeviceServer(IMidiManager midiManager, MidiReceiver[] inputPortReceivers,
            int numOutputPorts) {
            int numOutputPorts, Callback callback) {
        mMidiManager = midiManager;
        mInputPortReceivers = inputPortReceivers;
        mInputPortCount = inputPortReceivers.length;
        mOutputPortCount = numOutputPorts;
        mCallback = callback;

        mInputPortOutputPorts = new MidiOutputPort[mInputPortCount];

@@ -228,6 +267,9 @@ public final class MidiDeviceServer implements Closeable {
            mOutputPortDispatchers[i] = new MidiDispatcher();
        }

        mInputPortBusy = new boolean[mInputPortCount];
        mOutputPortOpenCount = new int[numOutputPorts];

        mGuard.open("close");
    }

@@ -242,9 +284,28 @@ public final class MidiDeviceServer implements Closeable {
        mDeviceInfo = deviceInfo;
    }

    private void updateDeviceStatus() {
        // clear calling identity, since we may be in a Binder call from one of our clients
        long identityToken = Binder.clearCallingIdentity();

        MidiDeviceStatus status = new MidiDeviceStatus(mDeviceInfo, mInputPortBusy,
                mOutputPortOpenCount);
        if (mCallback != null) {
            mCallback.onDeviceStatusChanged(this, status);
        }
        try {
            mMidiManager.setDeviceStatus(mDeviceStatusToken, status);
        } catch (RemoteException e) {
            Log.e(TAG, "RemoteException in updateDeviceStatus");
        } finally {
            Binder.restoreCallingIdentity(identityToken);
        }
    }

    @Override
    public void close() throws IOException {
        synchronized (mGuard) {
            if (mIsClosed) return;
            mGuard.close();

            for (int i = 0; i < mInputPortCount; i++) {
@@ -263,6 +324,7 @@ public final class MidiDeviceServer implements Closeable {
            } catch (RemoteException e) {
                Log.e(TAG, "RemoteException in unregisterDeviceServer");
            }
            mIsClosed = true;
        }
    }

+15 −1
Original line number Diff line number Diff line
@@ -57,6 +57,13 @@ abstract public class MidiDeviceService extends Service {
    private MidiDeviceServer mServer;
    private MidiDeviceInfo mDeviceInfo;

    private final MidiDeviceServer.Callback mCallback = new MidiDeviceServer.Callback() {
        @Override
        public void onDeviceStatusChanged(MidiDeviceServer server, MidiDeviceStatus status) {
            MidiDeviceService.this.onDeviceStatusChanged(status);
        }
    };

    @Override
    public void onCreate() {
        mMidiManager = IMidiManager.Stub.asInterface(
@@ -75,7 +82,7 @@ abstract public class MidiDeviceService extends Service {
                inputPortReceivers = new MidiReceiver[0];
            }
            server = new MidiDeviceServer(mMidiManager, inputPortReceivers,
                    deviceInfo.getOutputPortCount());
                    deviceInfo.getOutputPortCount(), mCallback);
            server.setDeviceInfo(deviceInfo);
        } catch (RemoteException e) {
            Log.e(TAG, "RemoteException in IMidiManager.getServiceDeviceInfo");
@@ -114,6 +121,13 @@ abstract public class MidiDeviceService extends Service {
        return mDeviceInfo;
    }

    /**
     * Called to notify when an our {@link MidiDeviceStatus} has changed
     * @param status the number of the port that was opened
     */
    public void onDeviceStatusChanged(MidiDeviceStatus status) {
    }

    @Override
    public IBinder onBind(Intent intent) {
        if (SERVICE_INTERFACE.equals(intent.getAction()) && mServer != null) {
+19 −0
Original line number Diff line number Diff line
/*
 * Copyright (C) 2015, The Android Open Source Project
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package android.media.midi;

parcelable MidiDeviceStatus;
Loading