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