/* * 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 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 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); }