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