| /* | |
| * 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: | |
| column = Audio.AudioColumns.ARTIST; | |
| type = MtpConstants.TYPE_STR; | |
| break; | |
| case MtpConstants.PROPERTY_ALBUM_NAME: | |
| column = Audio.AudioColumns.ALBUM; | |
| type = MtpConstants.TYPE_STR; | |
| break; | |
| case MtpConstants.PROPERTY_ALBUM_ARTIST: | |
| column = Audio.AudioColumns.ALBUM_ARTIST; | |
| type = MtpConstants.TYPE_STR; | |
| break; | |
| case MtpConstants.PROPERTY_GENRE: | |
| column = Audio.AudioColumns.GENRE; | |
| 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 = (((long) 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); | |
| } |