1 /* 2 * Copyright (C) 2010 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17 package android.mtp; 18 19 import android.content.ContentProviderClient; 20 import android.database.Cursor; 21 import android.net.Uri; 22 import android.os.RemoteException; 23 import android.provider.MediaStore.Audio; 24 import android.provider.MediaStore.Files; 25 import android.provider.MediaStore.Images; 26 import android.util.Log; 27 28 import java.util.ArrayList; 29 30 /** 31 * MtpPropertyGroup represents a list of MTP properties. 32 * {@hide} 33 */ 34 class MtpPropertyGroup { 35 private static final String TAG = MtpPropertyGroup.class.getSimpleName(); 36 37 private class Property { 38 int code; 39 int type; 40 int column; 41 Property(int code, int type, int column)42 Property(int code, int type, int column) { 43 this.code = code; 44 this.type = type; 45 this.column = column; 46 } 47 } 48 49 // list of all properties in this group 50 private final Property[] mProperties; 51 52 // list of columns for database query 53 private String[] mColumns; 54 55 private static final String PATH_WHERE = Files.FileColumns.DATA + "=?"; 56 57 // constructs a property group for a list of properties MtpPropertyGroup(int[] properties)58 public MtpPropertyGroup(int[] properties) { 59 int count = properties.length; 60 ArrayList<String> columns = new ArrayList<>(count); 61 columns.add(Files.FileColumns._ID); 62 63 mProperties = new Property[count]; 64 for (int i = 0; i < count; i++) { 65 mProperties[i] = createProperty(properties[i], columns); 66 } 67 count = columns.size(); 68 mColumns = new String[count]; 69 for (int i = 0; i < count; i++) { 70 mColumns[i] = columns.get(i); 71 } 72 } 73 createProperty(int code, ArrayList<String> columns)74 private Property createProperty(int code, ArrayList<String> columns) { 75 String column = null; 76 int type; 77 78 switch (code) { 79 case MtpConstants.PROPERTY_STORAGE_ID: 80 type = MtpConstants.TYPE_UINT32; 81 break; 82 case MtpConstants.PROPERTY_OBJECT_FORMAT: 83 type = MtpConstants.TYPE_UINT16; 84 break; 85 case MtpConstants.PROPERTY_PROTECTION_STATUS: 86 type = MtpConstants.TYPE_UINT16; 87 break; 88 case MtpConstants.PROPERTY_OBJECT_SIZE: 89 type = MtpConstants.TYPE_UINT64; 90 break; 91 case MtpConstants.PROPERTY_OBJECT_FILE_NAME: 92 type = MtpConstants.TYPE_STR; 93 break; 94 case MtpConstants.PROPERTY_NAME: 95 type = MtpConstants.TYPE_STR; 96 break; 97 case MtpConstants.PROPERTY_DATE_MODIFIED: 98 type = MtpConstants.TYPE_STR; 99 break; 100 case MtpConstants.PROPERTY_DATE_ADDED: 101 type = MtpConstants.TYPE_STR; 102 break; 103 case MtpConstants.PROPERTY_ORIGINAL_RELEASE_DATE: 104 column = Audio.AudioColumns.YEAR; 105 type = MtpConstants.TYPE_STR; 106 break; 107 case MtpConstants.PROPERTY_PARENT_OBJECT: 108 type = MtpConstants.TYPE_UINT32; 109 break; 110 case MtpConstants.PROPERTY_PERSISTENT_UID: 111 type = MtpConstants.TYPE_UINT128; 112 break; 113 case MtpConstants.PROPERTY_DURATION: 114 column = Audio.AudioColumns.DURATION; 115 type = MtpConstants.TYPE_UINT32; 116 break; 117 case MtpConstants.PROPERTY_TRACK: 118 column = Audio.AudioColumns.TRACK; 119 type = MtpConstants.TYPE_UINT16; 120 break; 121 case MtpConstants.PROPERTY_DISPLAY_NAME: 122 type = MtpConstants.TYPE_STR; 123 break; 124 case MtpConstants.PROPERTY_ARTIST: 125 column = Audio.AudioColumns.ARTIST; 126 type = MtpConstants.TYPE_STR; 127 break; 128 case MtpConstants.PROPERTY_ALBUM_NAME: 129 column = Audio.AudioColumns.ALBUM; 130 type = MtpConstants.TYPE_STR; 131 break; 132 case MtpConstants.PROPERTY_ALBUM_ARTIST: 133 column = Audio.AudioColumns.ALBUM_ARTIST; 134 type = MtpConstants.TYPE_STR; 135 break; 136 case MtpConstants.PROPERTY_GENRE: 137 column = Audio.AudioColumns.GENRE; 138 type = MtpConstants.TYPE_STR; 139 break; 140 case MtpConstants.PROPERTY_COMPOSER: 141 column = Audio.AudioColumns.COMPOSER; 142 type = MtpConstants.TYPE_STR; 143 break; 144 case MtpConstants.PROPERTY_DESCRIPTION: 145 column = Images.ImageColumns.DESCRIPTION; 146 type = MtpConstants.TYPE_STR; 147 break; 148 case MtpConstants.PROPERTY_AUDIO_WAVE_CODEC: 149 case MtpConstants.PROPERTY_AUDIO_BITRATE: 150 case MtpConstants.PROPERTY_SAMPLE_RATE: 151 // these are special cased 152 type = MtpConstants.TYPE_UINT32; 153 break; 154 case MtpConstants.PROPERTY_BITRATE_TYPE: 155 case MtpConstants.PROPERTY_NUMBER_OF_CHANNELS: 156 // these are special cased 157 type = MtpConstants.TYPE_UINT16; 158 break; 159 default: 160 type = MtpConstants.TYPE_UNDEFINED; 161 Log.e(TAG, "unsupported property " + code); 162 break; 163 } 164 165 if (column != null) { 166 columns.add(column); 167 return new Property(code, type, columns.size() - 1); 168 } else { 169 return new Property(code, type, -1); 170 } 171 } 172 173 /** 174 * Gets the values of the properties represented by this property group for the given 175 * object and adds them to the given property list. 176 * @return Response_OK if the operation succeeded. 177 */ getPropertyList(ContentProviderClient provider, String volumeName, MtpStorageManager.MtpObject object, MtpPropertyList list)178 public int getPropertyList(ContentProviderClient provider, String volumeName, 179 MtpStorageManager.MtpObject object, MtpPropertyList list) { 180 Cursor c = null; 181 int id = object.getId(); 182 String path = object.getPath().toString(); 183 for (Property property : mProperties) { 184 if (property.column != -1 && c == null) { 185 try { 186 // Look up the entry in MediaProvider only if one of those properties is needed. 187 final Uri uri = MtpDatabase.getObjectPropertiesUri(object.getFormat(), 188 volumeName); 189 c = provider.query(uri, mColumns, 190 PATH_WHERE, new String[] {path}, null, null); 191 if (c != null && !c.moveToNext()) { 192 c.close(); 193 c = null; 194 } 195 } catch (IllegalArgumentException e) { 196 return MtpConstants.RESPONSE_INVALID_OBJECT_PROP_CODE; 197 } catch (RemoteException e) { 198 Log.e(TAG, "Mediaprovider lookup failed"); 199 } 200 } 201 switch (property.code) { 202 case MtpConstants.PROPERTY_PROTECTION_STATUS: 203 // protection status is always 0 204 list.append(id, property.code, property.type, 0); 205 break; 206 case MtpConstants.PROPERTY_NAME: 207 case MtpConstants.PROPERTY_OBJECT_FILE_NAME: 208 case MtpConstants.PROPERTY_DISPLAY_NAME: 209 list.append(id, property.code, object.getName()); 210 break; 211 case MtpConstants.PROPERTY_DATE_MODIFIED: 212 case MtpConstants.PROPERTY_DATE_ADDED: 213 // convert from seconds to DateTime 214 list.append(id, property.code, 215 format_date_time(object.getModifiedTime())); 216 break; 217 case MtpConstants.PROPERTY_STORAGE_ID: 218 list.append(id, property.code, property.type, object.getStorageId()); 219 break; 220 case MtpConstants.PROPERTY_OBJECT_FORMAT: 221 list.append(id, property.code, property.type, object.getFormat()); 222 break; 223 case MtpConstants.PROPERTY_OBJECT_SIZE: 224 list.append(id, property.code, property.type, object.getSize()); 225 break; 226 case MtpConstants.PROPERTY_PARENT_OBJECT: 227 list.append(id, property.code, property.type, 228 object.getParent().isRoot() ? 0 : object.getParent().getId()); 229 break; 230 case MtpConstants.PROPERTY_PERSISTENT_UID: 231 // The persistent uid must be unique and never reused among all objects, 232 // and remain the same between sessions. 233 long puid = (object.getPath().toString().hashCode() << 32) 234 + object.getModifiedTime(); 235 list.append(id, property.code, property.type, puid); 236 break; 237 case MtpConstants.PROPERTY_ORIGINAL_RELEASE_DATE: 238 // release date is stored internally as just the year 239 int year = 0; 240 if (c != null) 241 year = c.getInt(property.column); 242 String dateTime = Integer.toString(year) + "0101T000000"; 243 list.append(id, property.code, dateTime); 244 break; 245 case MtpConstants.PROPERTY_TRACK: 246 int track = 0; 247 if (c != null) 248 track = c.getInt(property.column); 249 list.append(id, property.code, MtpConstants.TYPE_UINT16, 250 track % 1000); 251 break; 252 case MtpConstants.PROPERTY_AUDIO_WAVE_CODEC: 253 case MtpConstants.PROPERTY_AUDIO_BITRATE: 254 case MtpConstants.PROPERTY_SAMPLE_RATE: 255 // we don't have these in our database, so return 0 256 list.append(id, property.code, MtpConstants.TYPE_UINT32, 0); 257 break; 258 case MtpConstants.PROPERTY_BITRATE_TYPE: 259 case MtpConstants.PROPERTY_NUMBER_OF_CHANNELS: 260 // we don't have these in our database, so return 0 261 list.append(id, property.code, MtpConstants.TYPE_UINT16, 0); 262 break; 263 default: 264 switch(property.type) { 265 case MtpConstants.TYPE_UNDEFINED: 266 list.append(id, property.code, property.type, 0); 267 break; 268 case MtpConstants.TYPE_STR: 269 String value = ""; 270 if (c != null) 271 value = c.getString(property.column); 272 list.append(id, property.code, value); 273 break; 274 default: 275 long longValue = 0L; 276 if (c != null) 277 longValue = c.getLong(property.column); 278 list.append(id, property.code, property.type, longValue); 279 } 280 } 281 } 282 if (c != null) 283 c.close(); 284 return MtpConstants.RESPONSE_OK; 285 } 286 format_date_time(long seconds)287 private native String format_date_time(long seconds); 288 } 289