Dianne Hackborn 44bc17c6b5 Rework display size access.
Applications now get the display size from the window manager.  No
behavior should be changed yet, this is just prep for some real
changes.

Change-Id: I2958a6660895c1cba2b670509600014e55ee9273
2011-04-21 15:17:52 -07:00

823 lines
31 KiB
Java

/*
* 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.Context;
import android.content.ContentValues;
import android.content.IContentProvider;
import android.content.Intent;
import android.content.SharedPreferences;
import android.database.Cursor;
import android.database.sqlite.SQLiteDatabase;
import android.media.MediaScanner;
import android.net.Uri;
import android.os.Environment;
import android.os.RemoteException;
import android.provider.MediaStore;
import android.provider.MediaStore.Audio;
import android.provider.MediaStore.Files;
import android.provider.MediaStore.Images;
import android.provider.MediaStore.MediaColumns;
import android.util.Log;
import android.view.Display;
import android.view.WindowManager;
import java.io.File;
import java.util.HashMap;
/**
* {@hide}
*/
public class MtpDatabase {
private static final String TAG = "MtpDatabase";
private final Context mContext;
private final IContentProvider mMediaProvider;
private final String mVolumeName;
private final Uri mObjectsUri;
private final String mMediaStoragePath; // path to primary storage
private final HashMap<String, MtpStorage> mStorageMap = new HashMap<String, MtpStorage>();
// cached property groups for single properties
private final HashMap<Integer, MtpPropertyGroup> mPropertyGroupsByProperty
= new HashMap<Integer, MtpPropertyGroup>();
// cached property groups for all properties for a given format
private final HashMap<Integer, MtpPropertyGroup> mPropertyGroupsByFormat
= new HashMap<Integer, MtpPropertyGroup>();
// true if the database has been modified in the current MTP session
private boolean mDatabaseModified;
// SharedPreferences for writable MTP device properties
private SharedPreferences mDeviceProperties;
private static final int DEVICE_PROPERTIES_DATABASE_VERSION = 1;
private static final String[] ID_PROJECTION = new String[] {
Files.FileColumns._ID, // 0
};
private static final String[] PATH_PROJECTION = new String[] {
Files.FileColumns._ID, // 0
Files.FileColumns.DATA, // 1
};
private static final String[] PATH_SIZE_FORMAT_PROJECTION = new String[] {
Files.FileColumns._ID, // 0
Files.FileColumns.DATA, // 1
Files.FileColumns.SIZE, // 2
Files.FileColumns.FORMAT, // 3
};
private static final String[] OBJECT_INFO_PROJECTION = new String[] {
Files.FileColumns._ID, // 0
Files.FileColumns.STORAGE_ID, // 1
Files.FileColumns.FORMAT, // 2
Files.FileColumns.PARENT, // 3
Files.FileColumns.DATA, // 4
Files.FileColumns.SIZE, // 5
Files.FileColumns.DATE_MODIFIED, // 6
};
private static final String ID_WHERE = Files.FileColumns._ID + "=?";
private static final String PATH_WHERE = Files.FileColumns.DATA + "=?";
private static final String PARENT_WHERE = Files.FileColumns.PARENT + "=?";
private static final String PARENT_FORMAT_WHERE = PARENT_WHERE + " AND "
+ Files.FileColumns.FORMAT + "=?";
private static final String PARENT_STORAGE_WHERE = PARENT_WHERE + " AND "
+ Files.FileColumns.STORAGE_ID + "=?";
private static final String PARENT_STORAGE_FORMAT_WHERE = PARENT_STORAGE_WHERE + " AND "
+ Files.FileColumns.FORMAT + "=?";
private final MediaScanner mMediaScanner;
static {
System.loadLibrary("media_jni");
}
public MtpDatabase(Context context, String volumeName, String storagePath) {
native_setup();
mContext = context;
mMediaProvider = context.getContentResolver().acquireProvider("media");
mVolumeName = volumeName;
mMediaStoragePath = storagePath;
mObjectsUri = Files.getMtpObjectsUri(volumeName);
mMediaScanner = new MediaScanner(context);
initDeviceProperties(context);
}
@Override
protected void finalize() throws Throwable {
try {
native_finalize();
} finally {
super.finalize();
}
}
public void addStorage(MtpStorage storage) {
mStorageMap.put(storage.getPath(), storage);
}
public void removeStorage(MtpStorage storage) {
mStorageMap.remove(storage.getPath());
}
private void initDeviceProperties(Context context) {
final String devicePropertiesName = "device-properties";
mDeviceProperties = context.getSharedPreferences(devicePropertiesName, Context.MODE_PRIVATE);
File databaseFile = context.getDatabasePath(devicePropertiesName);
if (databaseFile.exists()) {
// for backward compatibility - read device properties from sqlite database
// and migrate them to shared prefs
SQLiteDatabase db = null;
Cursor c = null;
try {
db = context.openOrCreateDatabase("device-properties", Context.MODE_PRIVATE, null);
if (db != null) {
c = db.query("properties", new String[] { "_id", "code", "value" },
null, null, null, null, null);
if (c != null) {
SharedPreferences.Editor e = mDeviceProperties.edit();
while (c.moveToNext()) {
String name = c.getString(1);
String value = c.getString(2);
e.putString(name, value);
}
e.commit();
}
}
} catch (Exception e) {
Log.e(TAG, "failed to migrate device properties", e);
} finally {
if (c != null) c.close();
if (db != null) db.close();
}
databaseFile.delete();
}
}
private int beginSendObject(String path, int format, int parent,
int storageId, long size, long modified) {
// first make sure the object does not exist
if (path != null) {
Cursor c = null;
try {
c = mMediaProvider.query(mObjectsUri, ID_PROJECTION, PATH_WHERE,
new String[] { path }, null);
if (c != null && c.getCount() > 0) {
Log.w(TAG, "file already exists in beginSendObject: " + path);
return -1;
}
} catch (RemoteException e) {
Log.e(TAG, "RemoteException in beginSendObject", e);
} finally {
if (c != null) {
c.close();
}
}
}
mDatabaseModified = true;
ContentValues values = new ContentValues();
values.put(Files.FileColumns.DATA, path);
values.put(Files.FileColumns.FORMAT, format);
values.put(Files.FileColumns.PARENT, parent);
values.put(Files.FileColumns.STORAGE_ID, storageId);
values.put(Files.FileColumns.SIZE, size);
values.put(Files.FileColumns.DATE_MODIFIED, modified);
try {
Uri uri = mMediaProvider.insert(mObjectsUri, values);
if (uri != null) {
return Integer.parseInt(uri.getPathSegments().get(2));
} else {
return -1;
}
} catch (RemoteException e) {
Log.e(TAG, "RemoteException in beginSendObject", e);
return -1;
}
}
private void endSendObject(String path, int handle, int format, boolean succeeded) {
if (succeeded) {
// handle abstract playlists separately
// they do not exist in the file system so don't use the media scanner here
if (format == MtpConstants.FORMAT_ABSTRACT_AV_PLAYLIST) {
// extract name from path
String name = path;
int lastSlash = name.lastIndexOf('/');
if (lastSlash >= 0) {
name = name.substring(lastSlash + 1);
}
// strip trailing ".pla" from the name
if (name.endsWith(".pla")) {
name = name.substring(0, name.length() - 4);
}
ContentValues values = new ContentValues(1);
values.put(Audio.Playlists.DATA, path);
values.put(Audio.Playlists.NAME, name);
values.put(Files.FileColumns.FORMAT, format);
values.put(Files.FileColumns.DATE_MODIFIED, System.currentTimeMillis() / 1000);
values.put(MediaColumns.MEDIA_SCANNER_NEW_OBJECT_ID, handle);
try {
Uri uri = mMediaProvider.insert(Audio.Playlists.EXTERNAL_CONTENT_URI, values);
} catch (RemoteException e) {
Log.e(TAG, "RemoteException in endSendObject", e);
}
} else {
mMediaScanner.scanMtpFile(path, mVolumeName, handle, format);
}
} else {
deleteFile(handle);
}
}
private Cursor createObjectQuery(int storageID, int format, int parent) throws RemoteException {
if (storageID != 0) {
if (format != 0) {
return mMediaProvider.query(mObjectsUri, ID_PROJECTION,
PARENT_STORAGE_FORMAT_WHERE,
new String[] { Integer.toString(parent), Integer.toString(storageID),
Integer.toString(format) }, null);
} else {
return mMediaProvider.query(mObjectsUri, ID_PROJECTION,
PARENT_STORAGE_WHERE, new String[]
{ Integer.toString(parent), Integer.toString(storageID) }, null);
}
} else {
if (format != 0) {
return mMediaProvider.query(mObjectsUri, ID_PROJECTION,
PARENT_FORMAT_WHERE,
new String[] { Integer.toString(parent), Integer.toString(format) },
null);
} else {
return mMediaProvider.query(mObjectsUri, ID_PROJECTION,
PARENT_WHERE, new String[] { Integer.toString(parent) }, null);
}
}
}
private int[] getObjectList(int storageID, int format, int parent) {
Cursor c = null;
try {
c = createObjectQuery(storageID, format, parent);
if (c == null) {
return null;
}
int count = c.getCount();
if (count > 0) {
int[] result = new int[count];
for (int i = 0; i < count; i++) {
c.moveToNext();
result[i] = c.getInt(0);
}
return result;
}
} catch (RemoteException e) {
Log.e(TAG, "RemoteException in getObjectList", e);
} finally {
if (c != null) {
c.close();
}
}
return null;
}
private int getNumObjects(int storageID, int format, int parent) {
Cursor c = null;
try {
c = createObjectQuery(storageID, format, parent);
if (c != null) {
return c.getCount();
}
} catch (RemoteException e) {
Log.e(TAG, "RemoteException in getNumObjects", e);
} finally {
if (c != null) {
c.close();
}
}
return -1;
}
private int[] getSupportedPlaybackFormats() {
return new int[] {
// allow transfering arbitrary files
MtpConstants.FORMAT_UNDEFINED,
MtpConstants.FORMAT_ASSOCIATION,
MtpConstants.FORMAT_TEXT,
MtpConstants.FORMAT_HTML,
MtpConstants.FORMAT_WAV,
MtpConstants.FORMAT_MP3,
MtpConstants.FORMAT_MPEG,
MtpConstants.FORMAT_EXIF_JPEG,
MtpConstants.FORMAT_TIFF_EP,
MtpConstants.FORMAT_GIF,
MtpConstants.FORMAT_JFIF,
MtpConstants.FORMAT_PNG,
MtpConstants.FORMAT_TIFF,
MtpConstants.FORMAT_WMA,
MtpConstants.FORMAT_OGG,
MtpConstants.FORMAT_AAC,
MtpConstants.FORMAT_MP4_CONTAINER,
MtpConstants.FORMAT_MP2,
MtpConstants.FORMAT_3GP_CONTAINER,
MtpConstants.FORMAT_ABSTRACT_AV_PLAYLIST,
MtpConstants.FORMAT_WPL_PLAYLIST,
MtpConstants.FORMAT_M3U_PLAYLIST,
MtpConstants.FORMAT_PLS_PLAYLIST,
MtpConstants.FORMAT_XML_DOCUMENT,
MtpConstants.FORMAT_FLAC,
};
}
private int[] getSupportedCaptureFormats() {
// no capture formats yet
return null;
}
static final int[] FILE_PROPERTIES = {
// NOTE must match beginning of AUDIO_PROPERTIES, VIDEO_PROPERTIES
// and IMAGE_PROPERTIES below
MtpConstants.PROPERTY_STORAGE_ID,
MtpConstants.PROPERTY_OBJECT_FORMAT,
MtpConstants.PROPERTY_PROTECTION_STATUS,
MtpConstants.PROPERTY_OBJECT_SIZE,
MtpConstants.PROPERTY_OBJECT_FILE_NAME,
MtpConstants.PROPERTY_DATE_MODIFIED,
MtpConstants.PROPERTY_PARENT_OBJECT,
MtpConstants.PROPERTY_PERSISTENT_UID,
MtpConstants.PROPERTY_NAME,
MtpConstants.PROPERTY_DATE_ADDED,
};
static final int[] AUDIO_PROPERTIES = {
// NOTE must match FILE_PROPERTIES above
MtpConstants.PROPERTY_STORAGE_ID,
MtpConstants.PROPERTY_OBJECT_FORMAT,
MtpConstants.PROPERTY_PROTECTION_STATUS,
MtpConstants.PROPERTY_OBJECT_SIZE,
MtpConstants.PROPERTY_OBJECT_FILE_NAME,
MtpConstants.PROPERTY_DATE_MODIFIED,
MtpConstants.PROPERTY_PARENT_OBJECT,
MtpConstants.PROPERTY_PERSISTENT_UID,
MtpConstants.PROPERTY_NAME,
MtpConstants.PROPERTY_DISPLAY_NAME,
MtpConstants.PROPERTY_DATE_ADDED,
// audio specific properties
MtpConstants.PROPERTY_ARTIST,
MtpConstants.PROPERTY_ALBUM_NAME,
MtpConstants.PROPERTY_ALBUM_ARTIST,
MtpConstants.PROPERTY_TRACK,
MtpConstants.PROPERTY_ORIGINAL_RELEASE_DATE,
MtpConstants.PROPERTY_DURATION,
MtpConstants.PROPERTY_GENRE,
MtpConstants.PROPERTY_COMPOSER,
};
static final int[] VIDEO_PROPERTIES = {
// NOTE must match FILE_PROPERTIES above
MtpConstants.PROPERTY_STORAGE_ID,
MtpConstants.PROPERTY_OBJECT_FORMAT,
MtpConstants.PROPERTY_PROTECTION_STATUS,
MtpConstants.PROPERTY_OBJECT_SIZE,
MtpConstants.PROPERTY_OBJECT_FILE_NAME,
MtpConstants.PROPERTY_DATE_MODIFIED,
MtpConstants.PROPERTY_PARENT_OBJECT,
MtpConstants.PROPERTY_PERSISTENT_UID,
MtpConstants.PROPERTY_NAME,
MtpConstants.PROPERTY_DISPLAY_NAME,
MtpConstants.PROPERTY_DATE_ADDED,
// video specific properties
MtpConstants.PROPERTY_ARTIST,
MtpConstants.PROPERTY_ALBUM_NAME,
MtpConstants.PROPERTY_DURATION,
MtpConstants.PROPERTY_DESCRIPTION,
};
static final int[] IMAGE_PROPERTIES = {
// NOTE must match FILE_PROPERTIES above
MtpConstants.PROPERTY_STORAGE_ID,
MtpConstants.PROPERTY_OBJECT_FORMAT,
MtpConstants.PROPERTY_PROTECTION_STATUS,
MtpConstants.PROPERTY_OBJECT_SIZE,
MtpConstants.PROPERTY_OBJECT_FILE_NAME,
MtpConstants.PROPERTY_DATE_MODIFIED,
MtpConstants.PROPERTY_PARENT_OBJECT,
MtpConstants.PROPERTY_PERSISTENT_UID,
MtpConstants.PROPERTY_NAME,
MtpConstants.PROPERTY_DISPLAY_NAME,
MtpConstants.PROPERTY_DATE_ADDED,
// image specific properties
MtpConstants.PROPERTY_DESCRIPTION,
};
static final int[] ALL_PROPERTIES = {
// NOTE must match FILE_PROPERTIES above
MtpConstants.PROPERTY_STORAGE_ID,
MtpConstants.PROPERTY_OBJECT_FORMAT,
MtpConstants.PROPERTY_PROTECTION_STATUS,
MtpConstants.PROPERTY_OBJECT_SIZE,
MtpConstants.PROPERTY_OBJECT_FILE_NAME,
MtpConstants.PROPERTY_DATE_MODIFIED,
MtpConstants.PROPERTY_PARENT_OBJECT,
MtpConstants.PROPERTY_PERSISTENT_UID,
MtpConstants.PROPERTY_NAME,
MtpConstants.PROPERTY_DISPLAY_NAME,
MtpConstants.PROPERTY_DATE_ADDED,
// image specific properties
MtpConstants.PROPERTY_DESCRIPTION,
// audio specific properties
MtpConstants.PROPERTY_ARTIST,
MtpConstants.PROPERTY_ALBUM_NAME,
MtpConstants.PROPERTY_ALBUM_ARTIST,
MtpConstants.PROPERTY_TRACK,
MtpConstants.PROPERTY_ORIGINAL_RELEASE_DATE,
MtpConstants.PROPERTY_DURATION,
MtpConstants.PROPERTY_GENRE,
MtpConstants.PROPERTY_COMPOSER,
// video specific properties
MtpConstants.PROPERTY_ARTIST,
MtpConstants.PROPERTY_ALBUM_NAME,
MtpConstants.PROPERTY_DURATION,
MtpConstants.PROPERTY_DESCRIPTION,
// image specific properties
MtpConstants.PROPERTY_DESCRIPTION,
};
private int[] getSupportedObjectProperties(int format) {
switch (format) {
case MtpConstants.FORMAT_MP3:
case MtpConstants.FORMAT_WAV:
case MtpConstants.FORMAT_WMA:
case MtpConstants.FORMAT_OGG:
case MtpConstants.FORMAT_AAC:
return AUDIO_PROPERTIES;
case MtpConstants.FORMAT_MPEG:
case MtpConstants.FORMAT_3GP_CONTAINER:
case MtpConstants.FORMAT_WMV:
return VIDEO_PROPERTIES;
case MtpConstants.FORMAT_EXIF_JPEG:
case MtpConstants.FORMAT_GIF:
case MtpConstants.FORMAT_PNG:
case MtpConstants.FORMAT_BMP:
return IMAGE_PROPERTIES;
case 0:
return ALL_PROPERTIES;
default:
return FILE_PROPERTIES;
}
}
private int[] getSupportedDeviceProperties() {
return new int[] {
MtpConstants.DEVICE_PROPERTY_SYNCHRONIZATION_PARTNER,
MtpConstants.DEVICE_PROPERTY_DEVICE_FRIENDLY_NAME,
MtpConstants.DEVICE_PROPERTY_IMAGE_SIZE,
};
}
private MtpPropertyList getObjectPropertyList(long handle, int format, long property,
int groupCode, int depth) {
// FIXME - implement group support
if (groupCode != 0) {
return new MtpPropertyList(0, MtpConstants.RESPONSE_SPECIFICATION_BY_GROUP_UNSUPPORTED);
}
MtpPropertyGroup propertyGroup;
if (property == 0xFFFFFFFFL) {
propertyGroup = mPropertyGroupsByFormat.get(format);
if (propertyGroup == null) {
int[] propertyList = getSupportedObjectProperties(format);
propertyGroup = new MtpPropertyGroup(this, mMediaProvider, mVolumeName, propertyList);
mPropertyGroupsByFormat.put(new Integer(format), propertyGroup);
}
} else {
propertyGroup = mPropertyGroupsByProperty.get(property);
if (propertyGroup == null) {
int[] propertyList = new int[] { (int)property };
propertyGroup = new MtpPropertyGroup(this, mMediaProvider, mVolumeName, propertyList);
mPropertyGroupsByProperty.put(new Integer((int)property), propertyGroup);
}
}
return propertyGroup.getPropertyList((int)handle, format, depth);
}
private int renameFile(int handle, String newName) {
Cursor c = null;
// first compute current path
String path = null;
String[] whereArgs = new String[] { Integer.toString(handle) };
try {
c = mMediaProvider.query(mObjectsUri, PATH_PROJECTION, ID_WHERE, whereArgs, null);
if (c != null && c.moveToNext()) {
path = c.getString(1);
}
} catch (RemoteException e) {
Log.e(TAG, "RemoteException in getObjectFilePath", e);
return MtpConstants.RESPONSE_GENERAL_ERROR;
} finally {
if (c != null) {
c.close();
}
}
if (path == null) {
return MtpConstants.RESPONSE_INVALID_OBJECT_HANDLE;
}
// now rename the file. make sure this succeeds before updating database
File oldFile = new File(path);
int lastSlash = path.lastIndexOf('/');
if (lastSlash <= 1) {
return MtpConstants.RESPONSE_GENERAL_ERROR;
}
String newPath = path.substring(0, lastSlash + 1) + newName;
File newFile = new File(newPath);
boolean success = oldFile.renameTo(newFile);
if (!success) {
Log.w(TAG, "renaming "+ path + " to " + newPath + " failed");
return MtpConstants.RESPONSE_GENERAL_ERROR;
}
// finally update database
ContentValues values = new ContentValues();
values.put(Files.FileColumns.DATA, newPath);
int updated = 0;
try {
// note - we are relying on a special case in MediaProvider.update() to update
// the paths for all children in the case where this is a directory.
updated = mMediaProvider.update(mObjectsUri, values, ID_WHERE, whereArgs);
} catch (RemoteException e) {
Log.e(TAG, "RemoteException in mMediaProvider.update", e);
}
if (updated == 0) {
Log.e(TAG, "Unable to update path for " + path + " to " + newPath);
// this shouldn't happen, but if it does we need to rename the file to its original name
newFile.renameTo(oldFile);
return MtpConstants.RESPONSE_GENERAL_ERROR;
}
return MtpConstants.RESPONSE_OK;
}
private int setObjectProperty(int handle, int property,
long intValue, String stringValue) {
switch (property) {
case MtpConstants.PROPERTY_OBJECT_FILE_NAME:
return renameFile(handle, stringValue);
default:
return MtpConstants.RESPONSE_OBJECT_PROP_NOT_SUPPORTED;
}
}
private int getDeviceProperty(int property, long[] outIntValue, char[] outStringValue) {
switch (property) {
case MtpConstants.DEVICE_PROPERTY_SYNCHRONIZATION_PARTNER:
case MtpConstants.DEVICE_PROPERTY_DEVICE_FRIENDLY_NAME:
// writable string properties kept in shared preferences
String value = mDeviceProperties.getString(Integer.toString(property), "");
int length = value.length();
if (length > 255) {
length = 255;
}
value.getChars(0, length, outStringValue, 0);
outStringValue[length] = 0;
return MtpConstants.RESPONSE_OK;
case MtpConstants.DEVICE_PROPERTY_IMAGE_SIZE:
// use screen size as max image size
Display display = ((WindowManager)mContext.getSystemService(
Context.WINDOW_SERVICE)).getDefaultDisplay();
int width = display.getMaximumSizeDimension();
int height = display.getMaximumSizeDimension();
String imageSize = Integer.toString(width) + "x" + Integer.toString(height);
imageSize.getChars(0, imageSize.length(), outStringValue, 0);
outStringValue[imageSize.length()] = 0;
return MtpConstants.RESPONSE_OK;
default:
return MtpConstants.RESPONSE_DEVICE_PROP_NOT_SUPPORTED;
}
}
private int setDeviceProperty(int property, long intValue, String stringValue) {
switch (property) {
case MtpConstants.DEVICE_PROPERTY_SYNCHRONIZATION_PARTNER:
case MtpConstants.DEVICE_PROPERTY_DEVICE_FRIENDLY_NAME:
// writable string properties kept in shared prefs
SharedPreferences.Editor e = mDeviceProperties.edit();
e.putString(Integer.toString(property), stringValue);
return (e.commit() ? MtpConstants.RESPONSE_OK
: MtpConstants.RESPONSE_GENERAL_ERROR);
}
return MtpConstants.RESPONSE_DEVICE_PROP_NOT_SUPPORTED;
}
private boolean getObjectInfo(int handle, int[] outStorageFormatParent,
char[] outName, long[] outSizeModified) {
Cursor c = null;
try {
c = mMediaProvider.query(mObjectsUri, OBJECT_INFO_PROJECTION,
ID_WHERE, new String[] { Integer.toString(handle) }, null);
if (c != null && c.moveToNext()) {
outStorageFormatParent[0] = c.getInt(1);
outStorageFormatParent[1] = c.getInt(2);
outStorageFormatParent[2] = c.getInt(3);
// extract name from path
String path = c.getString(4);
int lastSlash = path.lastIndexOf('/');
int start = (lastSlash >= 0 ? lastSlash + 1 : 0);
int end = path.length();
if (end - start > 255) {
end = start + 255;
}
path.getChars(start, end, outName, 0);
outName[end - start] = 0;
outSizeModified[0] = c.getLong(5);
outSizeModified[1] = c.getLong(6);
return true;
}
} catch (RemoteException e) {
Log.e(TAG, "RemoteException in getObjectInfo", e);
} finally {
if (c != null) {
c.close();
}
}
return false;
}
private int getObjectFilePath(int handle, char[] outFilePath, long[] outFileLengthFormat) {
if (handle == 0) {
// special case root directory
mMediaStoragePath.getChars(0, mMediaStoragePath.length(), outFilePath, 0);
outFilePath[mMediaStoragePath.length()] = 0;
outFileLengthFormat[0] = 0;
outFileLengthFormat[1] = MtpConstants.FORMAT_ASSOCIATION;
return MtpConstants.RESPONSE_OK;
}
Cursor c = null;
try {
c = mMediaProvider.query(mObjectsUri, PATH_SIZE_FORMAT_PROJECTION,
ID_WHERE, new String[] { Integer.toString(handle) }, null);
if (c != null && c.moveToNext()) {
String path = c.getString(1);
path.getChars(0, path.length(), outFilePath, 0);
outFilePath[path.length()] = 0;
outFileLengthFormat[0] = c.getLong(2);
outFileLengthFormat[1] = c.getLong(3);
return MtpConstants.RESPONSE_OK;
} else {
return MtpConstants.RESPONSE_INVALID_OBJECT_HANDLE;
}
} catch (RemoteException e) {
Log.e(TAG, "RemoteException in getObjectFilePath", e);
return MtpConstants.RESPONSE_GENERAL_ERROR;
} finally {
if (c != null) {
c.close();
}
}
}
private int deleteFile(int handle) {
mDatabaseModified = true;
String path = null;
int format = 0;
Cursor c = null;
try {
c = mMediaProvider.query(mObjectsUri, PATH_SIZE_FORMAT_PROJECTION,
ID_WHERE, new String[] { Integer.toString(handle) }, null);
if (c != null && c.moveToNext()) {
// don't convert to media path here, since we will be matching
// against paths in the database matching /data/media
path = c.getString(1);
format = c.getInt(3);
} else {
return MtpConstants.RESPONSE_INVALID_OBJECT_HANDLE;
}
if (path == null || format == 0) {
return MtpConstants.RESPONSE_GENERAL_ERROR;
}
if (format == MtpConstants.FORMAT_ASSOCIATION) {
// recursive case - delete all children first
Uri uri = Files.getMtpObjectsUri(mVolumeName);
int count = mMediaProvider.delete(uri, "_data LIKE ?",
new String[] { path + "/%"});
}
Uri uri = Files.getMtpObjectsUri(mVolumeName, handle);
if (mMediaProvider.delete(uri, null, null) > 0) {
return MtpConstants.RESPONSE_OK;
} else {
return MtpConstants.RESPONSE_INVALID_OBJECT_HANDLE;
}
} catch (RemoteException e) {
Log.e(TAG, "RemoteException in deleteFile", e);
return MtpConstants.RESPONSE_GENERAL_ERROR;
} finally {
if (c != null) {
c.close();
}
}
}
private int[] getObjectReferences(int handle) {
Uri uri = Files.getMtpReferencesUri(mVolumeName, handle);
Cursor c = null;
try {
c = mMediaProvider.query(uri, ID_PROJECTION, null, null, null);
if (c == null) {
return null;
}
int count = c.getCount();
if (count > 0) {
int[] result = new int[count];
for (int i = 0; i < count; i++) {
c.moveToNext();
result[i] = c.getInt(0);
}
return result;
}
} catch (RemoteException e) {
Log.e(TAG, "RemoteException in getObjectList", e);
} finally {
if (c != null) {
c.close();
}
}
return null;
}
private int setObjectReferences(int handle, int[] references) {
mDatabaseModified = true;
Uri uri = Files.getMtpReferencesUri(mVolumeName, handle);
int count = references.length;
ContentValues[] valuesList = new ContentValues[count];
for (int i = 0; i < count; i++) {
ContentValues values = new ContentValues();
values.put(Files.FileColumns._ID, references[i]);
valuesList[i] = values;
}
try {
if (mMediaProvider.bulkInsert(uri, valuesList) > 0) {
return MtpConstants.RESPONSE_OK;
}
} catch (RemoteException e) {
Log.e(TAG, "RemoteException in setObjectReferences", e);
}
return MtpConstants.RESPONSE_GENERAL_ERROR;
}
private void sessionStarted() {
mDatabaseModified = false;
}
private void sessionEnded() {
if (mDatabaseModified) {
mContext.sendBroadcast(new Intent(MediaStore.ACTION_MTP_SESSION_END));
mDatabaseModified = false;
}
}
// used by the JNI code
private int mNativeContext;
private native final void native_setup();
private native final void native_finalize();
}