1/*
2 * Copyright (C) 2015 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 *      http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17package android.media.midi;
18
19import android.app.Service;
20import android.content.Context;
21import android.content.Intent;
22import android.os.IBinder;
23import android.os.RemoteException;
24import android.os.ServiceManager;
25import android.util.Log;
26
27/**
28 * A service that implements a virtual MIDI device.
29 * Subclasses must implement the {@link #onGetInputPortReceivers} method to provide a
30 * list of {@link MidiReceiver}s to receive data sent to the device's input ports.
31 * Similarly, subclasses can call {@link #getOutputPortReceivers} to fetch a list
32 * of {@link MidiReceiver}s for sending data out the output ports.
33 *
34 * <p>To extend this class, you must declare the service in your manifest file with
35 * an intent filter with the {@link #SERVICE_INTERFACE} action
36 * and meta-data to describe the virtual device.
37 For example:</p>
38 * <pre>
39 * &lt;service android:name=".VirtualDeviceService"
40 *          android:label="&#64;string/service_name">
41 *     &lt;intent-filter>
42 *         &lt;action android:name="android.media.midi.MidiDeviceService" />
43 *     &lt;/intent-filter>
44 *           &lt;meta-data android:name="android.media.midi.MidiDeviceService"
45                android:resource="@xml/device_info" />
46 * &lt;/service></pre>
47 */
48abstract public class MidiDeviceService extends Service {
49    private static final String TAG = "MidiDeviceService";
50
51    public static final String SERVICE_INTERFACE = "android.media.midi.MidiDeviceService";
52
53    private IMidiManager mMidiManager;
54    private MidiDeviceServer mServer;
55    private MidiDeviceInfo mDeviceInfo;
56
57    private final MidiDeviceServer.Callback mCallback = new MidiDeviceServer.Callback() {
58        @Override
59        public void onDeviceStatusChanged(MidiDeviceServer server, MidiDeviceStatus status) {
60            MidiDeviceService.this.onDeviceStatusChanged(status);
61        }
62
63        @Override
64        public void onClose() {
65            MidiDeviceService.this.onClose();
66        }
67    };
68
69    @Override
70    public void onCreate() {
71        mMidiManager = IMidiManager.Stub.asInterface(
72                    ServiceManager.getService(Context.MIDI_SERVICE));
73        MidiDeviceServer server;
74        try {
75            MidiDeviceInfo deviceInfo = mMidiManager.getServiceDeviceInfo(getPackageName(),
76                    this.getClass().getName());
77            if (deviceInfo == null) {
78                Log.e(TAG, "Could not find MidiDeviceInfo for MidiDeviceService " + this);
79                return;
80            }
81            mDeviceInfo = deviceInfo;
82            MidiReceiver[] inputPortReceivers = onGetInputPortReceivers();
83            if (inputPortReceivers == null) {
84                inputPortReceivers = new MidiReceiver[0];
85            }
86            server = new MidiDeviceServer(mMidiManager, inputPortReceivers, deviceInfo, mCallback);
87        } catch (RemoteException e) {
88            Log.e(TAG, "RemoteException in IMidiManager.getServiceDeviceInfo");
89            server = null;
90        }
91        mServer = server;
92   }
93
94    /**
95     * Returns an array of {@link MidiReceiver} for the device's input ports.
96     * Subclasses must override this to provide the receivers which will receive
97     * data sent to the device's input ports. An empty array should be returned if
98     * the device has no input ports.
99     * @return array of MidiReceivers
100     */
101    abstract public MidiReceiver[] onGetInputPortReceivers();
102
103    /**
104     * Returns an array of {@link MidiReceiver} for the device's output ports.
105     * These can be used to send data out the device's output ports.
106     * @return array of MidiReceivers
107     */
108    public final MidiReceiver[] getOutputPortReceivers() {
109        if (mServer == null) {
110            return null;
111        } else {
112            return mServer.getOutputPortReceivers();
113        }
114    }
115
116    /**
117     * returns the {@link MidiDeviceInfo} instance for this service
118     * @return our MidiDeviceInfo
119     */
120    public final MidiDeviceInfo getDeviceInfo() {
121        return mDeviceInfo;
122    }
123
124    /**
125     * Called to notify when an our {@link MidiDeviceStatus} has changed
126     * @param status the number of the port that was opened
127     */
128    public void onDeviceStatusChanged(MidiDeviceStatus status) {
129    }
130
131    /**
132     * Called to notify when our device has been closed by all its clients
133     */
134    public void onClose() {
135    }
136
137    @Override
138    public IBinder onBind(Intent intent) {
139        if (SERVICE_INTERFACE.equals(intent.getAction()) && mServer != null) {
140             return mServer.getBinderInterface().asBinder();
141        } else {
142             return null;
143       }
144    }
145}
146