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 
17 package android.media.midi;
18 
19 import android.app.Service;
20 import android.content.Context;
21 import android.content.Intent;
22 import android.os.IBinder;
23 import android.os.RemoteException;
24 import android.os.ServiceManager;
25 import 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  */
48 abstract 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
onCreate()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      */
onGetInputPortReceivers()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      */
getOutputPortReceivers()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      */
getDeviceInfo()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      */
onDeviceStatusChanged(MidiDeviceStatus status)128     public void onDeviceStatusChanged(MidiDeviceStatus status) {
129     }
130 
131     /**
132      * Called to notify when our device has been closed by all its clients
133      */
onClose()134     public void onClose() {
135     }
136 
137     @Override
onBind(Intent intent)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