content: /*
   * Copyright (C) 2010 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.mtp;
  
  import android.content.ContentProviderClient;
  import android.database.Cursor;
  import android.net.Uri;
  import android.os.RemoteException;
  import android.provider.MediaStore.Audio;
  import android.provider.MediaStore.Files;
  import android.provider.MediaStore.Images;
  import android.util.Log;
  
  import java.util.ArrayList;
  
  /**
   * MtpPropertyGroup represents a list of MTP properties.
   * {@hide}
   */
  class MtpPropertyGroup {
      private static final String TAG = MtpPropertyGroup.class.getSimpleName();
  
      private class Property {
          int code;
          int type;
          int column;
  
          Property(int code, int type, int column) {
              this.code = code;
              this.type = type;
              this.column = column;
          }
      }
  
      // list of all properties in this group
      private final Property[] mProperties;
  
      // list of columns for database query
      private String[] mColumns;
  
      private static final String PATH_WHERE = Files.FileColumns.DATA + "=?";
  
      // constructs a property group for a list of properties
      public MtpPropertyGroup(int[] properties) {
          int count = properties.length;
          ArrayList<String> columns = new ArrayList<>(count);
          columns.add(Files.FileColumns._ID);
  
          mProperties = new Property[count];
          for (int i = 0; i < count; i++) {
              mProperties[i] = createProperty(properties[i], columns);
          }
          count = columns.size();
          mColumns = new String[count];
          for (int i = 0; i < count; i++) {
              mColumns[i] = columns.get(i);
          }
      }
  
      private Property createProperty(int code, ArrayList<String> columns) {
          String column = null;
          int type;
  
          switch (code) {
              case MtpConstants.PROPERTY_STORAGE_ID:
                  type = MtpConstants.TYPE_UINT32;
                  break;
              case MtpConstants.PROPERTY_OBJECT_FORMAT:
                  type = MtpConstants.TYPE_UINT16;
                  break;
              case MtpConstants.PROPERTY_PROTECTION_STATUS:
                  type = MtpConstants.TYPE_UINT16;
                  break;
              case MtpConstants.PROPERTY_OBJECT_SIZE:
                  type = MtpConstants.TYPE_UINT64;
                  break;
              case MtpConstants.PROPERTY_OBJECT_FILE_NAME:
                  type = MtpConstants.TYPE_STR;
                  break;
              case MtpConstants.PROPERTY_NAME:
                  type = MtpConstants.TYPE_STR;
                  break;
              case MtpConstants.PROPERTY_DATE_MODIFIED:
                  type = MtpConstants.TYPE_STR;
                  break;
              case MtpConstants.PROPERTY_DATE_ADDED:
                  type = MtpConstants.TYPE_STR;
                  break;
              case MtpConstants.PROPERTY_ORIGINAL_RELEASE_DATE:
                  column = Audio.AudioColumns.YEAR;
                  type = MtpConstants.TYPE_STR;
                  break;
              case MtpConstants.PROPERTY_PARENT_OBJECT:
                  type = MtpConstants.TYPE_UINT32;
                  break;
              case MtpConstants.PROPERTY_PERSISTENT_UID:
                  type = MtpConstants.TYPE_UINT128;
                  break;
              case MtpConstants.PROPERTY_DURATION:
                  column = Audio.AudioColumns.DURATION;
                  type = MtpConstants.TYPE_UINT32;
                  break;
              case MtpConstants.PROPERTY_TRACK:
                  column = Audio.AudioColumns.TRACK;
                  type = MtpConstants.TYPE_UINT16;
                  break;
              case MtpConstants.PROPERTY_DISPLAY_NAME:
                  type = MtpConstants.TYPE_STR;
                  break;
              case MtpConstants.PROPERTY_ARTIST:
                  type = MtpConstants.TYPE_STR;
                  break;
              case MtpConstants.PROPERTY_ALBUM_NAME:
                  type = MtpConstants.TYPE_STR;
                  break;
              case MtpConstants.PROPERTY_ALBUM_ARTIST:
                  column = Audio.AudioColumns.ALBUM_ARTIST;
                  type = MtpConstants.TYPE_STR;
                  break;
              case MtpConstants.PROPERTY_COMPOSER:
                  column = Audio.AudioColumns.COMPOSER;
                  type = MtpConstants.TYPE_STR;
                  break;
              case MtpConstants.PROPERTY_DESCRIPTION:
                  column = Images.ImageColumns.DESCRIPTION;
                  type = MtpConstants.TYPE_STR;
                  break;
              case MtpConstants.PROPERTY_AUDIO_WAVE_CODEC:
              case MtpConstants.PROPERTY_AUDIO_BITRATE:
              case MtpConstants.PROPERTY_SAMPLE_RATE:
                  // these are special cased
                  type = MtpConstants.TYPE_UINT32;
                  break;
              case MtpConstants.PROPERTY_BITRATE_TYPE:
              case MtpConstants.PROPERTY_NUMBER_OF_CHANNELS:
                  // these are special cased
                  type = MtpConstants.TYPE_UINT16;
                  break;
              default:
                  type = MtpConstants.TYPE_UNDEFINED;
                  Log.e(TAG, "unsupported property " + code);
                  break;
          }
  
          if (column != null) {
              columns.add(column);
              return new Property(code, type, columns.size() - 1);
          } else {
              return new Property(code, type, -1);
          }
      }
  
      /**
       * Gets the values of the properties represented by this property group for the given
       * object and adds them to the given property list.
       * @return Response_OK if the operation succeeded.
       */
      public int getPropertyList(ContentProviderClient provider, String volumeName,
              MtpStorageManager.MtpObject object, MtpPropertyList list) {
          Cursor c = null;
          int id = object.getId();
          String path = object.getPath().toString();
          for (Property property : mProperties) {
              if (property.column != -1 && c == null) {
                  try {
                      // Look up the entry in MediaProvider only if one of those properties is needed.
                      final Uri uri = MtpDatabase.getObjectPropertiesUri(object.getFormat(),
                              volumeName);
                      c = provider.query(uri, mColumns,
                              PATH_WHERE, new String[] {path}, null, null);
                      if (c != null && !c.moveToNext()) {
                          c.close();
                          c = null;
                      }
                  } catch (IllegalArgumentException e) {
                      return MtpConstants.RESPONSE_INVALID_OBJECT_PROP_CODE;
                  } catch (RemoteException e) {
                      Log.e(TAG, "Mediaprovider lookup failed");
                  }
              }
              switch (property.code) {
                  case MtpConstants.PROPERTY_PROTECTION_STATUS:
                      // protection status is always 0
                      list.append(id, property.code, property.type, 0);
                      break;
                  case MtpConstants.PROPERTY_NAME:
                  case MtpConstants.PROPERTY_OBJECT_FILE_NAME:
                  case MtpConstants.PROPERTY_DISPLAY_NAME:
                      list.append(id, property.code, object.getName());
                      break;
                  case MtpConstants.PROPERTY_DATE_MODIFIED:
                  case MtpConstants.PROPERTY_DATE_ADDED:
                      // convert from seconds to DateTime
                      list.append(id, property.code,
                              format_date_time(object.getModifiedTime()));
                      break;
                  case MtpConstants.PROPERTY_STORAGE_ID:
                      list.append(id, property.code, property.type, object.getStorageId());
                      break;
                  case MtpConstants.PROPERTY_OBJECT_FORMAT:
                      list.append(id, property.code, property.type, object.getFormat());
                      break;
                  case MtpConstants.PROPERTY_OBJECT_SIZE:
                      list.append(id, property.code, property.type, object.getSize());
                      break;
                  case MtpConstants.PROPERTY_PARENT_OBJECT:
                      list.append(id, property.code, property.type,
                              object.getParent().isRoot() ? 0 : object.getParent().getId());
                      break;
                  case MtpConstants.PROPERTY_PERSISTENT_UID:
                      // The persistent uid must be unique and never reused among all objects,
                      // and remain the same between sessions.
                      long puid = (object.getPath().toString().hashCode() << 32)
                              + object.getModifiedTime();
                      list.append(id, property.code, property.type, puid);
                      break;
                  case MtpConstants.PROPERTY_ORIGINAL_RELEASE_DATE:
                      // release date is stored internally as just the year
                      int year = 0;
                      if (c != null)
                          year = c.getInt(property.column);
                      String dateTime = Integer.toString(year) + "0101T000000";
                      list.append(id, property.code, dateTime);
                      break;
                  case MtpConstants.PROPERTY_TRACK:
                      int track = 0;
                      if (c != null)
                          track = c.getInt(property.column);
                      list.append(id, property.code, MtpConstants.TYPE_UINT16,
                              track % 1000);
                      break;
                  case MtpConstants.PROPERTY_AUDIO_WAVE_CODEC:
                  case MtpConstants.PROPERTY_AUDIO_BITRATE:
                  case MtpConstants.PROPERTY_SAMPLE_RATE:
                      // we don't have these in our database, so return 0
                      list.append(id, property.code, MtpConstants.TYPE_UINT32, 0);
                      break;
                  case MtpConstants.PROPERTY_BITRATE_TYPE:
                  case MtpConstants.PROPERTY_NUMBER_OF_CHANNELS:
                      // we don't have these in our database, so return 0
                      list.append(id, property.code, MtpConstants.TYPE_UINT16, 0);
                      break;
                  default:
                      switch(property.type) {
                          case MtpConstants.TYPE_UNDEFINED:
                              list.append(id, property.code, property.type, 0);
                              break;
                          case MtpConstants.TYPE_STR:
                              String value = "";
                              if (c != null)
                                  value = c.getString(property.column);
                              list.append(id, property.code, value);
                              break;
                          default:
                              long longValue = 0L;
                              if (c != null)
                                  longValue = c.getLong(property.column);
                              list.append(id, property.code, property.type, longValue);
                      }
              }
          }
          if (c != null)
              c.close();
          return MtpConstants.RESPONSE_OK;
      }
  
      private native String format_date_time(long seconds);
  }
  