content: /*
   * Copyright (C) 2014 The Android Open Source Project
   *
   * Licensed under the Apache License, Version 2.0 (the "License");
   * you may not use this file except in compliance with the License.
   * You may obtain a copy of the License at
   *
   *      http://www.apache.org/licenses/LICENSE-2.0
   *
   * Unless required by applicable law or agreed to in writing, software
   * distributed under the License is distributed on an "AS IS" BASIS,
   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
   * See the License for the specific language governing permissions and
   * limitations under the License.
   */
  
  package android.media.midi;
  
  import android.os.Bundle;
  import android.os.Parcel;
  import android.os.Parcelable;
  
  import android.util.Log;
  
  /**
   * This class contains information to describe a MIDI device.
   * For now we only have information that can be retrieved easily for USB devices,
   * but we will probably expand this in the future.
   *
   * This class is just an immutable object to encapsulate the MIDI device description.
   * Use the MidiDevice class to actually communicate with devices.
   */
  public final class MidiDeviceInfo implements Parcelable {
  
      private static final String TAG = "MidiDeviceInfo";
  
      /*
       * Please note that constants and (un)marshalling code need to be kept in sync
       * with the native implementation (MidiDeviceInfo.h|cpp)
       */
  
      /**
       * Constant representing USB MIDI devices for {@link #getType}
       */
      public static final int TYPE_USB = 1;
  
      /**
       * Constant representing virtual (software based) MIDI devices for {@link #getType}
       */
      public static final int TYPE_VIRTUAL = 2;
  
      /**
       * Constant representing Bluetooth MIDI devices for {@link #getType}
       */
      public static final int TYPE_BLUETOOTH = 3;
  
      /**
       * Bundle key for the device's user visible name property.
       * The value for this property is of type {@link java.lang.String}.
       * Used with the {@link android.os.Bundle} returned by {@link #getProperties}.
       * For USB devices, this is a concatenation of the manufacturer and product names.
       */
      public static final String PROPERTY_NAME = "name";
  
      /**
       * Bundle key for the device's manufacturer name property.
       * The value for this property is of type {@link java.lang.String}.
       * Used with the {@link android.os.Bundle} returned by {@link #getProperties}.
       * Matches the USB device manufacturer name string for USB MIDI devices.
       */
      public static final String PROPERTY_MANUFACTURER = "manufacturer";
  
      /**
       * Bundle key for the device's product name property.
       * The value for this property is of type {@link java.lang.String}.
       * Used with the {@link android.os.Bundle} returned by {@link #getProperties}
       * Matches the USB device product name string for USB MIDI devices.
       */
      public static final String PROPERTY_PRODUCT = "product";
  
      /**
       * Bundle key for the device's version property.
       * The value for this property is of type {@link java.lang.String}.
       * Used with the {@link android.os.Bundle} returned by {@link #getProperties}
       * Matches the USB device version number for USB MIDI devices.
       */
      public static final String PROPERTY_VERSION = "version";
  
      /**
       * Bundle key for the device's serial number property.
       * The value for this property is of type {@link java.lang.String}.
       * Used with the {@link android.os.Bundle} returned by {@link #getProperties}
       * Matches the USB device serial number for USB MIDI devices.
       */
      public static final String PROPERTY_SERIAL_NUMBER = "serial_number";
  
      /**
       * Bundle key for the device's corresponding USB device.
       * The value for this property is of type {@link android.hardware.usb.UsbDevice}.
       * Only set for USB MIDI devices.
       * Used with the {@link android.os.Bundle} returned by {@link #getProperties}
       */
      public static final String PROPERTY_USB_DEVICE = "usb_device";
  
      /**
       * Bundle key for the device's corresponding Bluetooth device.
       * The value for this property is of type {@link android.bluetooth.BluetoothDevice}.
       * Only set for Bluetooth MIDI devices.
       * Used with the {@link android.os.Bundle} returned by {@link #getProperties}
       */
      public static final String PROPERTY_BLUETOOTH_DEVICE = "bluetooth_device";
  
      /**
       * Bundle key for the device's ALSA card number.
       * The value for this property is an integer.
       * Only set for USB MIDI devices.
       * Used with the {@link android.os.Bundle} returned by {@link #getProperties}
       *
       * @hide
       */
      public static final String PROPERTY_ALSA_CARD = "alsa_card";
  
      /**
       * Bundle key for the device's ALSA device number.
       * The value for this property is an integer.
       * Only set for USB MIDI devices.
       * Used with the {@link android.os.Bundle} returned by {@link #getProperties}
       *
       * @hide
       */
      public static final String PROPERTY_ALSA_DEVICE = "alsa_device";
  
      /**
       * ServiceInfo for the service hosting the device implementation.
       * The value for this property is of type {@link android.content.pm.ServiceInfo}.
       * Only set for Virtual MIDI devices.
       * Used with the {@link android.os.Bundle} returned by {@link #getProperties}
       *
       * @hide
       */
      public static final String PROPERTY_SERVICE_INFO = "service_info";
  
      /**
       * Contains information about an input or output port.
       */
      public static final class PortInfo {
          /**
           * Port type for input ports
           */
          public static final int TYPE_INPUT = 1;
  
          /**
           * Port type for output ports
           */
          public static final int TYPE_OUTPUT = 2;
  
          private final int mPortType;
          private final int mPortNumber;
          private final String mName;
  
          PortInfo(int type, int portNumber, String name) {
              mPortType = type;
              mPortNumber = portNumber;
              mName = (name == null ? "" : name);
          }
  
          /**
           * Returns the port type of the port (either {@link #TYPE_INPUT} or {@link #TYPE_OUTPUT})
           * @return the port type
           */
          public int getType() {
              return mPortType;
          }
  
          /**
           * Returns the port number of the port
           * @return the port number
           */
          public int getPortNumber() {
              return mPortNumber;
          }
  
          /**
           * Returns the name of the port, or empty string if the port has no name
           * @return the port name
           */
          public String getName() {
              return mName;
          }
      }
  
      private final int mType;    // USB or virtual
      private final int mId;      // unique ID generated by MidiService. Accessed from native code.
      private final int mInputPortCount;
      private final int mOutputPortCount;
      private final String[] mInputPortNames;
      private final String[] mOutputPortNames;
      private final Bundle mProperties;
      private final boolean mIsPrivate;
  
      /**
       * MidiDeviceInfo should only be instantiated by MidiService implementation
       * @hide
       */
      public MidiDeviceInfo(int type, int id, int numInputPorts, int numOutputPorts,
              String[] inputPortNames, String[] outputPortNames, Bundle properties,
              boolean isPrivate) {
          mType = type;
          mId = id;
          mInputPortCount = numInputPorts;
          mOutputPortCount = numOutputPorts;
          if (inputPortNames == null) {
              mInputPortNames = new String[numInputPorts];
          } else {
              mInputPortNames = inputPortNames;
          }
          if (outputPortNames == null) {
              mOutputPortNames = new String[numOutputPorts];
          } else {
              mOutputPortNames = outputPortNames;
          }
          mProperties = properties;
          mIsPrivate = isPrivate;
      }
  
      /**
       * Returns the type of the device.
       *
       * @return the device's type
       */
      public int getType() {
          return mType;
      }
  
      /**
       * Returns the ID of the device.
       * This ID is generated by the MIDI service and is not persistent across device unplugs.
       *
       * @return the device's ID
       */
      public int getId() {
          return mId;
      }
  
      /**
       * Returns the device's number of input ports.
       *
       * @return the number of input ports
       */
      public int getInputPortCount() {
          return mInputPortCount;
      }
  
      /**
       * Returns the device's number of output ports.
       *
       * @return the number of output ports
       */
      public int getOutputPortCount() {
          return mOutputPortCount;
      }
  
      /**
       * Returns information about the device's ports.
       * The ports are in unspecified order.
       *
       * @return array of {@link PortInfo}
       */
      public PortInfo[] getPorts() {
          PortInfo[] ports = new PortInfo[mInputPortCount + mOutputPortCount];
  
          int index = 0;
          for (int i = 0; i < mInputPortCount; i++) {
              ports[index++] = new PortInfo(PortInfo.TYPE_INPUT, i, mInputPortNames[i]);
          }
          for (int i = 0; i < mOutputPortCount; i++) {
              ports[index++] = new PortInfo(PortInfo.TYPE_OUTPUT, i, mOutputPortNames[i]);
          }
  
          return ports;
      }
  
      /**
       * Returns the {@link android.os.Bundle} containing the device's properties.
       *
       * @return the device's properties
       */
      public Bundle getProperties() {
          return mProperties;
      }
  
      /**
       * Returns true if the device is private.  Private devices are only visible and accessible
       * to clients with the same UID as the application that is hosting the device.
       *
       * @return true if the device is private
       */
      public boolean isPrivate() {
          return mIsPrivate;
      }
  
      @Override
      public boolean equals(Object o) {
          if (o instanceof MidiDeviceInfo) {
              return (((MidiDeviceInfo)o).mId == mId);
          } else {
              return false;
          }
      }
  
      @Override
      public int hashCode() {
          return mId;
      }
  
      @Override
      public String toString() {
          // This is a hack to force the mProperties Bundle to unparcel so we can
          // print all the names and values.
          mProperties.getString(PROPERTY_NAME);
          return ("MidiDeviceInfo[mType=" + mType +
                  ",mInputPortCount=" + mInputPortCount +
                  ",mOutputPortCount=" + mOutputPortCount +
                  ",mProperties=" + mProperties +
                  ",mIsPrivate=" + mIsPrivate);
      }
  
      public static final @android.annotation.NonNull Parcelable.Creator<MidiDeviceInfo> CREATOR =
          new Parcelable.Creator<MidiDeviceInfo>() {
          public MidiDeviceInfo createFromParcel(Parcel in) {
              // Needs to be kept in sync with code in MidiDeviceInfo.cpp
              int type = in.readInt();
              int id = in.readInt();
              int inputPortCount = in.readInt();
              int outputPortCount = in.readInt();
              String[] inputPortNames = in.createStringArray();
              String[] outputPortNames = in.createStringArray();
              boolean isPrivate = (in.readInt() == 1);
              Bundle basicPropertiesIgnored = in.readBundle();
              Bundle properties = in.readBundle();
              return new MidiDeviceInfo(type, id, inputPortCount, outputPortCount,
                      inputPortNames, outputPortNames, properties, isPrivate);
          }
  
          public MidiDeviceInfo[] newArray(int size) {
              return new MidiDeviceInfo[size];
          }
      };
  
      public int describeContents() {
          return 0;
      }
  
      private Bundle getBasicProperties(String[] keys) {
          Bundle basicProperties = new Bundle();
          for (String key : keys) {
              Object val = mProperties.get(key);
              if (val != null) {
                  if (val instanceof String) {
                      basicProperties.putString(key, (String) val);
                  } else if (val instanceof Integer) {
                      basicProperties.putInt(key, (Integer) val);
                  } else {
                      Log.w(TAG, "Unsupported property type: " + val.getClass().getName());
                  }
              }
          }
          return basicProperties;
      }
  
      public void writeToParcel(Parcel parcel, int flags) {
          // Needs to be kept in sync with code in MidiDeviceInfo.cpp
          parcel.writeInt(mType);
          parcel.writeInt(mId);
          parcel.writeInt(mInputPortCount);
          parcel.writeInt(mOutputPortCount);
          parcel.writeStringArray(mInputPortNames);
          parcel.writeStringArray(mOutputPortNames);
          parcel.writeInt(mIsPrivate ? 1 : 0);
          // "Basic" properties only contain properties of primitive types
          // and thus can be read back by native code. "Extra" properties is
          // a superset that contains all properties.
          parcel.writeBundle(getBasicProperties(new String[] {
              PROPERTY_NAME, PROPERTY_MANUFACTURER, PROPERTY_PRODUCT, PROPERTY_VERSION,
              PROPERTY_SERIAL_NUMBER, PROPERTY_ALSA_CARD, PROPERTY_ALSA_DEVICE
          }));
          // Must be serialized last so native code can safely ignore it.
          parcel.writeBundle(mProperties);
     }
  }
  