1 /*
2  * Copyright (C) 2014 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.os.Bundle;
20 import android.os.Parcel;
21 import android.os.Parcelable;
22 
23 import android.util.Log;
24 
25 /**
26  * This class contains information to describe a MIDI device.
27  * For now we only have information that can be retrieved easily for USB devices,
28  * but we will probably expand this in the future.
29  *
30  * This class is just an immutable object to encapsulate the MIDI device description.
31  * Use the MidiDevice class to actually communicate with devices.
32  */
33 public final class MidiDeviceInfo implements Parcelable {
34 
35     private static final String TAG = "MidiDeviceInfo";
36 
37     /*
38      * Please note that constants and (un)marshalling code need to be kept in sync
39      * with the native implementation (MidiDeviceInfo.h|cpp)
40      */
41 
42     /**
43      * Constant representing USB MIDI devices for {@link #getType}
44      */
45     public static final int TYPE_USB = 1;
46 
47     /**
48      * Constant representing virtual (software based) MIDI devices for {@link #getType}
49      */
50     public static final int TYPE_VIRTUAL = 2;
51 
52     /**
53      * Constant representing Bluetooth MIDI devices for {@link #getType}
54      */
55     public static final int TYPE_BLUETOOTH = 3;
56 
57     /**
58      * Bundle key for the device's user visible name property.
59      * The value for this property is of type {@link java.lang.String}.
60      * Used with the {@link android.os.Bundle} returned by {@link #getProperties}.
61      * For USB devices, this is a concatenation of the manufacturer and product names.
62      */
63     public static final String PROPERTY_NAME = "name";
64 
65     /**
66      * Bundle key for the device's manufacturer name property.
67      * The value for this property is of type {@link java.lang.String}.
68      * Used with the {@link android.os.Bundle} returned by {@link #getProperties}.
69      * Matches the USB device manufacturer name string for USB MIDI devices.
70      */
71     public static final String PROPERTY_MANUFACTURER = "manufacturer";
72 
73     /**
74      * Bundle key for the device's product name property.
75      * The value for this property is of type {@link java.lang.String}.
76      * Used with the {@link android.os.Bundle} returned by {@link #getProperties}
77      * Matches the USB device product name string for USB MIDI devices.
78      */
79     public static final String PROPERTY_PRODUCT = "product";
80 
81     /**
82      * Bundle key for the device's version property.
83      * The value for this property is of type {@link java.lang.String}.
84      * Used with the {@link android.os.Bundle} returned by {@link #getProperties}
85      * Matches the USB device version number for USB MIDI devices.
86      */
87     public static final String PROPERTY_VERSION = "version";
88 
89     /**
90      * Bundle key for the device's serial number property.
91      * The value for this property is of type {@link java.lang.String}.
92      * Used with the {@link android.os.Bundle} returned by {@link #getProperties}
93      * Matches the USB device serial number for USB MIDI devices.
94      */
95     public static final String PROPERTY_SERIAL_NUMBER = "serial_number";
96 
97     /**
98      * Bundle key for the device's corresponding USB device.
99      * The value for this property is of type {@link android.hardware.usb.UsbDevice}.
100      * Only set for USB MIDI devices.
101      * Used with the {@link android.os.Bundle} returned by {@link #getProperties}
102      */
103     public static final String PROPERTY_USB_DEVICE = "usb_device";
104 
105     /**
106      * Bundle key for the device's corresponding Bluetooth device.
107      * The value for this property is of type {@link android.bluetooth.BluetoothDevice}.
108      * Only set for Bluetooth MIDI devices.
109      * Used with the {@link android.os.Bundle} returned by {@link #getProperties}
110      */
111     public static final String PROPERTY_BLUETOOTH_DEVICE = "bluetooth_device";
112 
113     /**
114      * Bundle key for the device's ALSA card number.
115      * The value for this property is an integer.
116      * Only set for USB MIDI devices.
117      * Used with the {@link android.os.Bundle} returned by {@link #getProperties}
118      *
119      * @hide
120      */
121     public static final String PROPERTY_ALSA_CARD = "alsa_card";
122 
123     /**
124      * Bundle key for the device's ALSA device number.
125      * The value for this property is an integer.
126      * Only set for USB MIDI devices.
127      * Used with the {@link android.os.Bundle} returned by {@link #getProperties}
128      *
129      * @hide
130      */
131     public static final String PROPERTY_ALSA_DEVICE = "alsa_device";
132 
133     /**
134      * ServiceInfo for the service hosting the device implementation.
135      * The value for this property is of type {@link android.content.pm.ServiceInfo}.
136      * Only set for Virtual MIDI devices.
137      * Used with the {@link android.os.Bundle} returned by {@link #getProperties}
138      *
139      * @hide
140      */
141     public static final String PROPERTY_SERVICE_INFO = "service_info";
142 
143     /**
144      * Contains information about an input or output port.
145      */
146     public static final class PortInfo {
147         /**
148          * Port type for input ports
149          */
150         public static final int TYPE_INPUT = 1;
151 
152         /**
153          * Port type for output ports
154          */
155         public static final int TYPE_OUTPUT = 2;
156 
157         private final int mPortType;
158         private final int mPortNumber;
159         private final String mName;
160 
PortInfo(int type, int portNumber, String name)161         PortInfo(int type, int portNumber, String name) {
162             mPortType = type;
163             mPortNumber = portNumber;
164             mName = (name == null ? "" : name);
165         }
166 
167         /**
168          * Returns the port type of the port (either {@link #TYPE_INPUT} or {@link #TYPE_OUTPUT})
169          * @return the port type
170          */
getType()171         public int getType() {
172             return mPortType;
173         }
174 
175         /**
176          * Returns the port number of the port
177          * @return the port number
178          */
getPortNumber()179         public int getPortNumber() {
180             return mPortNumber;
181         }
182 
183         /**
184          * Returns the name of the port, or empty string if the port has no name
185          * @return the port name
186          */
getName()187         public String getName() {
188             return mName;
189         }
190     }
191 
192     private final int mType;    // USB or virtual
193     private final int mId;      // unique ID generated by MidiService. Accessed from native code.
194     private final int mInputPortCount;
195     private final int mOutputPortCount;
196     private final String[] mInputPortNames;
197     private final String[] mOutputPortNames;
198     private final Bundle mProperties;
199     private final boolean mIsPrivate;
200 
201     /**
202      * MidiDeviceInfo should only be instantiated by MidiService implementation
203      * @hide
204      */
MidiDeviceInfo(int type, int id, int numInputPorts, int numOutputPorts, String[] inputPortNames, String[] outputPortNames, Bundle properties, boolean isPrivate)205     public MidiDeviceInfo(int type, int id, int numInputPorts, int numOutputPorts,
206             String[] inputPortNames, String[] outputPortNames, Bundle properties,
207             boolean isPrivate) {
208         mType = type;
209         mId = id;
210         mInputPortCount = numInputPorts;
211         mOutputPortCount = numOutputPorts;
212         if (inputPortNames == null) {
213             mInputPortNames = new String[numInputPorts];
214         } else {
215             mInputPortNames = inputPortNames;
216         }
217         if (outputPortNames == null) {
218             mOutputPortNames = new String[numOutputPorts];
219         } else {
220             mOutputPortNames = outputPortNames;
221         }
222         mProperties = properties;
223         mIsPrivate = isPrivate;
224     }
225 
226     /**
227      * Returns the type of the device.
228      *
229      * @return the device's type
230      */
getType()231     public int getType() {
232         return mType;
233     }
234 
235     /**
236      * Returns the ID of the device.
237      * This ID is generated by the MIDI service and is not persistent across device unplugs.
238      *
239      * @return the device's ID
240      */
getId()241     public int getId() {
242         return mId;
243     }
244 
245     /**
246      * Returns the device's number of input ports.
247      *
248      * @return the number of input ports
249      */
getInputPortCount()250     public int getInputPortCount() {
251         return mInputPortCount;
252     }
253 
254     /**
255      * Returns the device's number of output ports.
256      *
257      * @return the number of output ports
258      */
getOutputPortCount()259     public int getOutputPortCount() {
260         return mOutputPortCount;
261     }
262 
263     /**
264      * Returns information about the device's ports.
265      * The ports are in unspecified order.
266      *
267      * @return array of {@link PortInfo}
268      */
getPorts()269     public PortInfo[] getPorts() {
270         PortInfo[] ports = new PortInfo[mInputPortCount + mOutputPortCount];
271 
272         int index = 0;
273         for (int i = 0; i < mInputPortCount; i++) {
274             ports[index++] = new PortInfo(PortInfo.TYPE_INPUT, i, mInputPortNames[i]);
275         }
276         for (int i = 0; i < mOutputPortCount; i++) {
277             ports[index++] = new PortInfo(PortInfo.TYPE_OUTPUT, i, mOutputPortNames[i]);
278         }
279 
280         return ports;
281     }
282 
283     /**
284      * Returns the {@link android.os.Bundle} containing the device's properties.
285      *
286      * @return the device's properties
287      */
getProperties()288     public Bundle getProperties() {
289         return mProperties;
290     }
291 
292     /**
293      * Returns true if the device is private.  Private devices are only visible and accessible
294      * to clients with the same UID as the application that is hosting the device.
295      *
296      * @return true if the device is private
297      */
isPrivate()298     public boolean isPrivate() {
299         return mIsPrivate;
300     }
301 
302     @Override
equals(Object o)303     public boolean equals(Object o) {
304         if (o instanceof MidiDeviceInfo) {
305             return (((MidiDeviceInfo)o).mId == mId);
306         } else {
307             return false;
308         }
309     }
310 
311     @Override
hashCode()312     public int hashCode() {
313         return mId;
314     }
315 
316     @Override
toString()317     public String toString() {
318         // This is a hack to force the mProperties Bundle to unparcel so we can
319         // print all the names and values.
320         mProperties.getString(PROPERTY_NAME);
321         return ("MidiDeviceInfo[mType=" + mType +
322                 ",mInputPortCount=" + mInputPortCount +
323                 ",mOutputPortCount=" + mOutputPortCount +
324                 ",mProperties=" + mProperties +
325                 ",mIsPrivate=" + mIsPrivate);
326     }
327 
328     public static final @android.annotation.NonNull Parcelable.Creator<MidiDeviceInfo> CREATOR =
329         new Parcelable.Creator<MidiDeviceInfo>() {
330         public MidiDeviceInfo createFromParcel(Parcel in) {
331             // Needs to be kept in sync with code in MidiDeviceInfo.cpp
332             int type = in.readInt();
333             int id = in.readInt();
334             int inputPortCount = in.readInt();
335             int outputPortCount = in.readInt();
336             String[] inputPortNames = in.createStringArray();
337             String[] outputPortNames = in.createStringArray();
338             boolean isPrivate = (in.readInt() == 1);
339             Bundle basicPropertiesIgnored = in.readBundle();
340             Bundle properties = in.readBundle();
341             return new MidiDeviceInfo(type, id, inputPortCount, outputPortCount,
342                     inputPortNames, outputPortNames, properties, isPrivate);
343         }
344 
345         public MidiDeviceInfo[] newArray(int size) {
346             return new MidiDeviceInfo[size];
347         }
348     };
349 
describeContents()350     public int describeContents() {
351         return 0;
352     }
353 
getBasicProperties(String[] keys)354     private Bundle getBasicProperties(String[] keys) {
355         Bundle basicProperties = new Bundle();
356         for (String key : keys) {
357             Object val = mProperties.get(key);
358             if (val != null) {
359                 if (val instanceof String) {
360                     basicProperties.putString(key, (String) val);
361                 } else if (val instanceof Integer) {
362                     basicProperties.putInt(key, (Integer) val);
363                 } else {
364                     Log.w(TAG, "Unsupported property type: " + val.getClass().getName());
365                 }
366             }
367         }
368         return basicProperties;
369     }
370 
writeToParcel(Parcel parcel, int flags)371     public void writeToParcel(Parcel parcel, int flags) {
372         // Needs to be kept in sync with code in MidiDeviceInfo.cpp
373         parcel.writeInt(mType);
374         parcel.writeInt(mId);
375         parcel.writeInt(mInputPortCount);
376         parcel.writeInt(mOutputPortCount);
377         parcel.writeStringArray(mInputPortNames);
378         parcel.writeStringArray(mOutputPortNames);
379         parcel.writeInt(mIsPrivate ? 1 : 0);
380         // "Basic" properties only contain properties of primitive types
381         // and thus can be read back by native code. "Extra" properties is
382         // a superset that contains all properties.
383         parcel.writeBundle(getBasicProperties(new String[] {
384             PROPERTY_NAME, PROPERTY_MANUFACTURER, PROPERTY_PRODUCT, PROPERTY_VERSION,
385             PROPERTY_SERIAL_NUMBER, PROPERTY_ALSA_CARD, PROPERTY_ALSA_DEVICE
386         }));
387         // Must be serialized last so native code can safely ignore it.
388         parcel.writeBundle(mProperties);
389    }
390 }
391