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 type = MtpConstants.TYPE_STR; 126 break; 127 case MtpConstants.PROPERTY_ALBUM_NAME: 128 type = MtpConstants.TYPE_STR; 129 break; 130 case MtpConstants.PROPERTY_ALBUM_ARTIST: 131 column = Audio.AudioColumns.ALBUM_ARTIST; 132 type = MtpConstants.TYPE_STR; 133 break; 134 case MtpConstants.PROPERTY_COMPOSER: 135 column = Audio.AudioColumns.COMPOSER; 136 type = MtpConstants.TYPE_STR; 137 break; 138 case MtpConstants.PROPERTY_DESCRIPTION: 139 column = Images.ImageColumns.DESCRIPTION; 140 type = MtpConstants.TYPE_STR; 141 break; 142 case MtpConstants.PROPERTY_AUDIO_WAVE_CODEC: 143 case MtpConstants.PROPERTY_AUDIO_BITRATE: 144 case MtpConstants.PROPERTY_SAMPLE_RATE: 145 // these are special cased 146 type = MtpConstants.TYPE_UINT32; 147 break; 148 case MtpConstants.PROPERTY_BITRATE_TYPE: 149 case MtpConstants.PROPERTY_NUMBER_OF_CHANNELS: 150 // these are special cased 151 type = MtpConstants.TYPE_UINT16; 152 break; 153 default: 154 type = MtpConstants.TYPE_UNDEFINED; 155 Log.e(TAG, "unsupported property " + code); 156 break; 157 } 158 159 if (column != null) { 160 columns.add(column); 161 return new Property(code, type, columns.size() - 1); 162 } else { 163 return new Property(code, type, -1); 164 } 165 } 166 167 /** 168 * Gets the values of the properties represented by this property group for the given 169 * object and adds them to the given property list. 170 * @return Response_OK if the operation succeeded. 171 */ getPropertyList(ContentProviderClient provider, String volumeName, MtpStorageManager.MtpObject object, MtpPropertyList list)172 public int getPropertyList(ContentProviderClient provider, String volumeName, 173 MtpStorageManager.MtpObject object, MtpPropertyList list) { 174 Cursor c = null; 175 int id = object.getId(); 176 String path = object.getPath().toString(); 177 for (Property property : mProperties) { 178 if (property.column != -1 && c == null) { 179 try { 180 // Look up the entry in MediaProvider only if one of those properties is needed. 181 final Uri uri = MtpDatabase.getObjectPropertiesUri(object.getFormat(), 182 volumeName); 183 c = provider.query(uri, mColumns, 184 PATH_WHERE, new String[] {path}, null, null); 185 if (c != null && !c.moveToNext()) { 186 c.close(); 187 c = null; 188 } 189 } catch (IllegalArgumentException e) { 190 return MtpConstants.RESPONSE_INVALID_OBJECT_PROP_CODE; 191 } catch (RemoteException e) { 192 Log.e(TAG, "Mediaprovider lookup failed"); 193 } 194 } 195 switch (property.code) { 196 case MtpConstants.PROPERTY_PROTECTION_STATUS: 197 // protection status is always 0 198 list.append(id, property.code, property.type, 0); 199 break; 200 case MtpConstants.PROPERTY_NAME: 201 case MtpConstants.PROPERTY_OBJECT_FILE_NAME: 202 case MtpConstants.PROPERTY_DISPLAY_NAME: 203 list.append(id, property.code, object.getName()); 204 break; 205 case MtpConstants.PROPERTY_DATE_MODIFIED: 206 case MtpConstants.PROPERTY_DATE_ADDED: 207 // convert from seconds to DateTime 208 list.append(id, property.code, 209 format_date_time(object.getModifiedTime())); 210 break; 211 case MtpConstants.PROPERTY_STORAGE_ID: 212 list.append(id, property.code, property.type, object.getStorageId()); 213 break; 214 case MtpConstants.PROPERTY_OBJECT_FORMAT: 215 list.append(id, property.code, property.type, object.getFormat()); 216 break; 217 case MtpConstants.PROPERTY_OBJECT_SIZE: 218 list.append(id, property.code, property.type, object.getSize()); 219 break; 220 case MtpConstants.PROPERTY_PARENT_OBJECT: 221 list.append(id, property.code, property.type, 222 object.getParent().isRoot() ? 0 : object.getParent().getId()); 223 break; 224 case MtpConstants.PROPERTY_PERSISTENT_UID: 225 // The persistent uid must be unique and never reused among all objects, 226 // and remain the same between sessions. 227 long puid = (object.getPath().toString().hashCode() << 32) 228 + object.getModifiedTime(); 229 list.append(id, property.code, property.type, puid); 230 break; 231 case MtpConstants.PROPERTY_ORIGINAL_RELEASE_DATE: 232 // release date is stored internally as just the year 233 int year = 0; 234 if (c != null) 235 year = c.getInt(property.column); 236 String dateTime = Integer.toString(year) + "0101T000000"; 237 list.append(id, property.code, dateTime); 238 break; 239 case MtpConstants.PROPERTY_TRACK: 240 int track = 0; 241 if (c != null) 242 track = c.getInt(property.column); 243 list.append(id, property.code, MtpConstants.TYPE_UINT16, 244 track % 1000); 245 break; 246 case MtpConstants.PROPERTY_AUDIO_WAVE_CODEC: 247 case MtpConstants.PROPERTY_AUDIO_BITRATE: 248 case MtpConstants.PROPERTY_SAMPLE_RATE: 249 // we don't have these in our database, so return 0 250 list.append(id, property.code, MtpConstants.TYPE_UINT32, 0); 251 break; 252 case MtpConstants.PROPERTY_BITRATE_TYPE: 253 case MtpConstants.PROPERTY_NUMBER_OF_CHANNELS: 254 // we don't have these in our database, so return 0 255 list.append(id, property.code, MtpConstants.TYPE_UINT16, 0); 256 break; 257 default: 258 switch(property.type) { 259 case MtpConstants.TYPE_UNDEFINED: 260 list.append(id, property.code, property.type, 0); 261 break; 262 case MtpConstants.TYPE_STR: 263 String value = ""; 264 if (c != null) 265 value = c.getString(property.column); 266 list.append(id, property.code, value); 267 break; 268 default: 269 long longValue = 0L; 270 if (c != null) 271 longValue = c.getLong(property.column); 272 list.append(id, property.code, property.type, longValue); 273 } 274 } 275 } 276 if (c != null) 277 c.close(); 278 return MtpConstants.RESPONSE_OK; 279 } 280 format_date_time(long seconds)281 private native String format_date_time(long seconds); 282 } 283