111fd96d6ff25bc1d710448eab545fe09da55a5f5Mike Lockwood/*
211fd96d6ff25bc1d710448eab545fe09da55a5f5Mike Lockwood * Copyright (C) 2015 The Android Open Source Project
311fd96d6ff25bc1d710448eab545fe09da55a5f5Mike Lockwood *
411fd96d6ff25bc1d710448eab545fe09da55a5f5Mike Lockwood * Licensed under the Apache License, Version 2.0 (the "License");
511fd96d6ff25bc1d710448eab545fe09da55a5f5Mike Lockwood * you may not use this file except in compliance with the License.
611fd96d6ff25bc1d710448eab545fe09da55a5f5Mike Lockwood * You may obtain a copy of the License at
711fd96d6ff25bc1d710448eab545fe09da55a5f5Mike Lockwood *
811fd96d6ff25bc1d710448eab545fe09da55a5f5Mike Lockwood *      http://www.apache.org/licenses/LICENSE-2.0
911fd96d6ff25bc1d710448eab545fe09da55a5f5Mike Lockwood *
1011fd96d6ff25bc1d710448eab545fe09da55a5f5Mike Lockwood * Unless required by applicable law or agreed to in writing, software
1111fd96d6ff25bc1d710448eab545fe09da55a5f5Mike Lockwood * distributed under the License is distributed on an "AS IS" BASIS,
1211fd96d6ff25bc1d710448eab545fe09da55a5f5Mike Lockwood * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
1311fd96d6ff25bc1d710448eab545fe09da55a5f5Mike Lockwood * See the License for the specific language governing permissions and
1411fd96d6ff25bc1d710448eab545fe09da55a5f5Mike Lockwood * limitations under the License.
1511fd96d6ff25bc1d710448eab545fe09da55a5f5Mike Lockwood */
1611fd96d6ff25bc1d710448eab545fe09da55a5f5Mike Lockwood
1711fd96d6ff25bc1d710448eab545fe09da55a5f5Mike Lockwoodpackage android.media.midi;
1811fd96d6ff25bc1d710448eab545fe09da55a5f5Mike Lockwood
1911fd96d6ff25bc1d710448eab545fe09da55a5f5Mike Lockwoodimport android.app.Service;
2011fd96d6ff25bc1d710448eab545fe09da55a5f5Mike Lockwoodimport android.content.Context;
2111fd96d6ff25bc1d710448eab545fe09da55a5f5Mike Lockwoodimport android.content.Intent;
2211fd96d6ff25bc1d710448eab545fe09da55a5f5Mike Lockwoodimport android.os.IBinder;
2311fd96d6ff25bc1d710448eab545fe09da55a5f5Mike Lockwoodimport android.os.RemoteException;
2411fd96d6ff25bc1d710448eab545fe09da55a5f5Mike Lockwoodimport android.os.ServiceManager;
2511fd96d6ff25bc1d710448eab545fe09da55a5f5Mike Lockwoodimport android.util.Log;
2611fd96d6ff25bc1d710448eab545fe09da55a5f5Mike Lockwood
2711fd96d6ff25bc1d710448eab545fe09da55a5f5Mike Lockwood/**
2811fd96d6ff25bc1d710448eab545fe09da55a5f5Mike Lockwood * A service that implements a virtual MIDI device.
2981b9f7d325a552c54e793b51f571ae3d65b26e94Mike Lockwood * Subclasses must implement the {@link #onGetInputPortReceivers} method to provide a
3011fd96d6ff25bc1d710448eab545fe09da55a5f5Mike Lockwood * list of {@link MidiReceiver}s to receive data sent to the device's input ports.
3111fd96d6ff25bc1d710448eab545fe09da55a5f5Mike Lockwood * Similarly, subclasses can call {@link #getOutputPortReceivers} to fetch a list
3211fd96d6ff25bc1d710448eab545fe09da55a5f5Mike Lockwood * of {@link MidiReceiver}s for sending data out the output ports.
3311fd96d6ff25bc1d710448eab545fe09da55a5f5Mike Lockwood *
3411fd96d6ff25bc1d710448eab545fe09da55a5f5Mike Lockwood * <p>To extend this class, you must declare the service in your manifest file with
3511fd96d6ff25bc1d710448eab545fe09da55a5f5Mike Lockwood * an intent filter with the {@link #SERVICE_INTERFACE} action
3611fd96d6ff25bc1d710448eab545fe09da55a5f5Mike Lockwood * and meta-data to describe the virtual device.
3711fd96d6ff25bc1d710448eab545fe09da55a5f5Mike Lockwood For example:</p>
3811fd96d6ff25bc1d710448eab545fe09da55a5f5Mike Lockwood * <pre>
3911fd96d6ff25bc1d710448eab545fe09da55a5f5Mike Lockwood * &lt;service android:name=".VirtualDeviceService"
4011fd96d6ff25bc1d710448eab545fe09da55a5f5Mike Lockwood *          android:label="&#64;string/service_name">
4111fd96d6ff25bc1d710448eab545fe09da55a5f5Mike Lockwood *     &lt;intent-filter>
4211fd96d6ff25bc1d710448eab545fe09da55a5f5Mike Lockwood *         &lt;action android:name="android.media.midi.MidiDeviceService" />
4311fd96d6ff25bc1d710448eab545fe09da55a5f5Mike Lockwood *     &lt;/intent-filter>
4411fd96d6ff25bc1d710448eab545fe09da55a5f5Mike Lockwood *           &lt;meta-data android:name="android.media.midi.MidiDeviceService"
4511fd96d6ff25bc1d710448eab545fe09da55a5f5Mike Lockwood                android:resource="@xml/device_info" />
4611fd96d6ff25bc1d710448eab545fe09da55a5f5Mike Lockwood * &lt;/service></pre>
4711fd96d6ff25bc1d710448eab545fe09da55a5f5Mike Lockwood */
4811fd96d6ff25bc1d710448eab545fe09da55a5f5Mike Lockwoodabstract public class MidiDeviceService extends Service {
4911fd96d6ff25bc1d710448eab545fe09da55a5f5Mike Lockwood    private static final String TAG = "MidiDeviceService";
5011fd96d6ff25bc1d710448eab545fe09da55a5f5Mike Lockwood
5111fd96d6ff25bc1d710448eab545fe09da55a5f5Mike Lockwood    public static final String SERVICE_INTERFACE = "android.media.midi.MidiDeviceService";
5211fd96d6ff25bc1d710448eab545fe09da55a5f5Mike Lockwood
5311fd96d6ff25bc1d710448eab545fe09da55a5f5Mike Lockwood    private IMidiManager mMidiManager;
5411fd96d6ff25bc1d710448eab545fe09da55a5f5Mike Lockwood    private MidiDeviceServer mServer;
55eebc98ff18c1ee92dff3fcd505158ea161d552beMike Lockwood    private MidiDeviceInfo mDeviceInfo;
5611fd96d6ff25bc1d710448eab545fe09da55a5f5Mike Lockwood
575ff9e2a1719f78cddc7a23d6572ab15ab595dafdMike Lockwood    private final MidiDeviceServer.Callback mCallback = new MidiDeviceServer.Callback() {
585ff9e2a1719f78cddc7a23d6572ab15ab595dafdMike Lockwood        @Override
595ff9e2a1719f78cddc7a23d6572ab15ab595dafdMike Lockwood        public void onDeviceStatusChanged(MidiDeviceServer server, MidiDeviceStatus status) {
605ff9e2a1719f78cddc7a23d6572ab15ab595dafdMike Lockwood            MidiDeviceService.this.onDeviceStatusChanged(status);
615ff9e2a1719f78cddc7a23d6572ab15ab595dafdMike Lockwood        }
62e0a6ca64fac5bd4f10139321604031816e90adb4Mike Lockwood
63e0a6ca64fac5bd4f10139321604031816e90adb4Mike Lockwood        @Override
64e0a6ca64fac5bd4f10139321604031816e90adb4Mike Lockwood        public void onClose() {
65e0a6ca64fac5bd4f10139321604031816e90adb4Mike Lockwood            MidiDeviceService.this.onClose();
66e0a6ca64fac5bd4f10139321604031816e90adb4Mike Lockwood        }
675ff9e2a1719f78cddc7a23d6572ab15ab595dafdMike Lockwood    };
685ff9e2a1719f78cddc7a23d6572ab15ab595dafdMike Lockwood
6911fd96d6ff25bc1d710448eab545fe09da55a5f5Mike Lockwood    @Override
7011fd96d6ff25bc1d710448eab545fe09da55a5f5Mike Lockwood    public void onCreate() {
7111fd96d6ff25bc1d710448eab545fe09da55a5f5Mike Lockwood        mMidiManager = IMidiManager.Stub.asInterface(
7211fd96d6ff25bc1d710448eab545fe09da55a5f5Mike Lockwood                    ServiceManager.getService(Context.MIDI_SERVICE));
7311fd96d6ff25bc1d710448eab545fe09da55a5f5Mike Lockwood        MidiDeviceServer server;
7411fd96d6ff25bc1d710448eab545fe09da55a5f5Mike Lockwood        try {
7511fd96d6ff25bc1d710448eab545fe09da55a5f5Mike Lockwood            MidiDeviceInfo deviceInfo = mMidiManager.getServiceDeviceInfo(getPackageName(),
7611fd96d6ff25bc1d710448eab545fe09da55a5f5Mike Lockwood                    this.getClass().getName());
77eebc98ff18c1ee92dff3fcd505158ea161d552beMike Lockwood            if (deviceInfo == null) {
78eebc98ff18c1ee92dff3fcd505158ea161d552beMike Lockwood                Log.e(TAG, "Could not find MidiDeviceInfo for MidiDeviceService " + this);
79eebc98ff18c1ee92dff3fcd505158ea161d552beMike Lockwood                return;
80eebc98ff18c1ee92dff3fcd505158ea161d552beMike Lockwood            }
81eebc98ff18c1ee92dff3fcd505158ea161d552beMike Lockwood            mDeviceInfo = deviceInfo;
8281b9f7d325a552c54e793b51f571ae3d65b26e94Mike Lockwood            MidiReceiver[] inputPortReceivers = onGetInputPortReceivers();
8311fd96d6ff25bc1d710448eab545fe09da55a5f5Mike Lockwood            if (inputPortReceivers == null) {
8411fd96d6ff25bc1d710448eab545fe09da55a5f5Mike Lockwood                inputPortReceivers = new MidiReceiver[0];
8511fd96d6ff25bc1d710448eab545fe09da55a5f5Mike Lockwood            }
86acd4321872387212bbd956e134faf22eee4bbadcMike Lockwood            server = new MidiDeviceServer(mMidiManager, inputPortReceivers, deviceInfo, mCallback);
8711fd96d6ff25bc1d710448eab545fe09da55a5f5Mike Lockwood        } catch (RemoteException e) {
8811fd96d6ff25bc1d710448eab545fe09da55a5f5Mike Lockwood            Log.e(TAG, "RemoteException in IMidiManager.getServiceDeviceInfo");
8911fd96d6ff25bc1d710448eab545fe09da55a5f5Mike Lockwood            server = null;
9011fd96d6ff25bc1d710448eab545fe09da55a5f5Mike Lockwood        }
9111fd96d6ff25bc1d710448eab545fe09da55a5f5Mike Lockwood        mServer = server;
9211fd96d6ff25bc1d710448eab545fe09da55a5f5Mike Lockwood   }
9311fd96d6ff25bc1d710448eab545fe09da55a5f5Mike Lockwood
9411fd96d6ff25bc1d710448eab545fe09da55a5f5Mike Lockwood    /**
9511fd96d6ff25bc1d710448eab545fe09da55a5f5Mike Lockwood     * Returns an array of {@link MidiReceiver} for the device's input ports.
9611fd96d6ff25bc1d710448eab545fe09da55a5f5Mike Lockwood     * Subclasses must override this to provide the receivers which will receive
979189f5f0252e1ef3e7d00978ef12e964565dd6f1Mike Lockwood     * data sent to the device's input ports. An empty array should be returned if
9811fd96d6ff25bc1d710448eab545fe09da55a5f5Mike Lockwood     * the device has no input ports.
9911fd96d6ff25bc1d710448eab545fe09da55a5f5Mike Lockwood     * @return array of MidiReceivers
10011fd96d6ff25bc1d710448eab545fe09da55a5f5Mike Lockwood     */
10181b9f7d325a552c54e793b51f571ae3d65b26e94Mike Lockwood    abstract public MidiReceiver[] onGetInputPortReceivers();
10211fd96d6ff25bc1d710448eab545fe09da55a5f5Mike Lockwood
10311fd96d6ff25bc1d710448eab545fe09da55a5f5Mike Lockwood    /**
10411fd96d6ff25bc1d710448eab545fe09da55a5f5Mike Lockwood     * Returns an array of {@link MidiReceiver} for the device's output ports.
10511fd96d6ff25bc1d710448eab545fe09da55a5f5Mike Lockwood     * These can be used to send data out the device's output ports.
10611fd96d6ff25bc1d710448eab545fe09da55a5f5Mike Lockwood     * @return array of MidiReceivers
10711fd96d6ff25bc1d710448eab545fe09da55a5f5Mike Lockwood     */
10881b9f7d325a552c54e793b51f571ae3d65b26e94Mike Lockwood    public final MidiReceiver[] getOutputPortReceivers() {
10911fd96d6ff25bc1d710448eab545fe09da55a5f5Mike Lockwood        if (mServer == null) {
11011fd96d6ff25bc1d710448eab545fe09da55a5f5Mike Lockwood            return null;
11111fd96d6ff25bc1d710448eab545fe09da55a5f5Mike Lockwood        } else {
11211fd96d6ff25bc1d710448eab545fe09da55a5f5Mike Lockwood            return mServer.getOutputPortReceivers();
11311fd96d6ff25bc1d710448eab545fe09da55a5f5Mike Lockwood        }
11411fd96d6ff25bc1d710448eab545fe09da55a5f5Mike Lockwood    }
11511fd96d6ff25bc1d710448eab545fe09da55a5f5Mike Lockwood
116eebc98ff18c1ee92dff3fcd505158ea161d552beMike Lockwood    /**
117eebc98ff18c1ee92dff3fcd505158ea161d552beMike Lockwood     * returns the {@link MidiDeviceInfo} instance for this service
118eebc98ff18c1ee92dff3fcd505158ea161d552beMike Lockwood     * @return our MidiDeviceInfo
119eebc98ff18c1ee92dff3fcd505158ea161d552beMike Lockwood     */
12081b9f7d325a552c54e793b51f571ae3d65b26e94Mike Lockwood    public final MidiDeviceInfo getDeviceInfo() {
121eebc98ff18c1ee92dff3fcd505158ea161d552beMike Lockwood        return mDeviceInfo;
122eebc98ff18c1ee92dff3fcd505158ea161d552beMike Lockwood    }
123eebc98ff18c1ee92dff3fcd505158ea161d552beMike Lockwood
1245ff9e2a1719f78cddc7a23d6572ab15ab595dafdMike Lockwood    /**
1255ff9e2a1719f78cddc7a23d6572ab15ab595dafdMike Lockwood     * Called to notify when an our {@link MidiDeviceStatus} has changed
1265ff9e2a1719f78cddc7a23d6572ab15ab595dafdMike Lockwood     * @param status the number of the port that was opened
1275ff9e2a1719f78cddc7a23d6572ab15ab595dafdMike Lockwood     */
1285ff9e2a1719f78cddc7a23d6572ab15ab595dafdMike Lockwood    public void onDeviceStatusChanged(MidiDeviceStatus status) {
1295ff9e2a1719f78cddc7a23d6572ab15ab595dafdMike Lockwood    }
1305ff9e2a1719f78cddc7a23d6572ab15ab595dafdMike Lockwood
131e0a6ca64fac5bd4f10139321604031816e90adb4Mike Lockwood    /**
132e0a6ca64fac5bd4f10139321604031816e90adb4Mike Lockwood     * Called to notify when our device has been closed by all its clients
133e0a6ca64fac5bd4f10139321604031816e90adb4Mike Lockwood     */
134e0a6ca64fac5bd4f10139321604031816e90adb4Mike Lockwood    public void onClose() {
135e0a6ca64fac5bd4f10139321604031816e90adb4Mike Lockwood    }
136e0a6ca64fac5bd4f10139321604031816e90adb4Mike Lockwood
13711fd96d6ff25bc1d710448eab545fe09da55a5f5Mike Lockwood    @Override
13811fd96d6ff25bc1d710448eab545fe09da55a5f5Mike Lockwood    public IBinder onBind(Intent intent) {
13911fd96d6ff25bc1d710448eab545fe09da55a5f5Mike Lockwood        if (SERVICE_INTERFACE.equals(intent.getAction()) && mServer != null) {
14011fd96d6ff25bc1d710448eab545fe09da55a5f5Mike Lockwood             return mServer.getBinderInterface().asBinder();
14111fd96d6ff25bc1d710448eab545fe09da55a5f5Mike Lockwood        } else {
14211fd96d6ff25bc1d710448eab545fe09da55a5f5Mike Lockwood             return null;
14311fd96d6ff25bc1d710448eab545fe09da55a5f5Mike Lockwood       }
14411fd96d6ff25bc1d710448eab545fe09da55a5f5Mike Lockwood    }
14511fd96d6ff25bc1d710448eab545fe09da55a5f5Mike Lockwood}
146