Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit f578fa27 authored by Daichi Hirono's avatar Daichi Hirono
Browse files

Update object info when writing a file.

The MTP spec does not offer a way to update bytes of exisitng files, so
our provider implementation creates a new file with new bytes and
removes old one.

Previously the new file uses new document ID and the exising document ID
is expired. Also the provider does not update the metadata
database. Thus users see the old flie in DocumentsUI but actually the
files is not accessible.

The CL updates the database with exisitng document ID, so that we can
access the new file with exisiting document ID.

BUG=26549400

Change-Id: I629b707a2e662b34625e8b28857ef818d8933996
parent dd6e4c19
Loading
Loading
Loading
Loading
+17 −0
Original line number Diff line number Diff line
@@ -581,6 +581,23 @@ class MtpDatabase {
        }
    }

    void updateObject(String documentId, int deviceId, String parentId, MtpObjectInfo info) {
        final ContentValues values = new ContentValues();
        getObjectDocumentValues(values, deviceId, parentId, info);

        mDatabase.beginTransaction();
        try {
            mDatabase.update(
                    TABLE_DOCUMENTS,
                    values,
                    Document.COLUMN_DOCUMENT_ID + " = ?",
                    strings(documentId));
            mDatabase.setTransactionSuccessful();
        } finally {
            mDatabase.endTransaction();
        }
    }

    private static class OpenHelper extends SQLiteOpenHelper {
        public OpenHelper(Context context, int flags) {
            super(context,
+1 −1
Original line number Diff line number Diff line
@@ -486,7 +486,7 @@ public class MtpDocumentsProvider extends DocumentsProvider {
        public final DocumentLoader mDocumentLoader;

        public DeviceToolkit(MtpManager manager, ContentResolver resolver, MtpDatabase database) {
            mPipeManager = new PipeManager();
            mPipeManager = new PipeManager(database);
            mDocumentLoader = new DocumentLoader(manager, resolver, database);
        }
    }
+24 −9
Original line number Diff line number Diff line
@@ -29,12 +29,14 @@ import java.util.concurrent.Executors;

class PipeManager {
    final ExecutorService mExecutor;
    final MtpDatabase mDatabase;

    PipeManager() {
        this(Executors.newSingleThreadExecutor());
    PipeManager(MtpDatabase database) {
        this(database, Executors.newSingleThreadExecutor());
    }

    PipeManager(ExecutorService executor) {
    PipeManager(MtpDatabase database, ExecutorService executor) {
        this.mDatabase = database;
        this.mExecutor = executor;
    }

@@ -46,7 +48,7 @@ class PipeManager {

    ParcelFileDescriptor writeDocument(Context context, MtpManager model, Identifier identifier)
            throws IOException {
        final Task task = new WriteDocumentTask(context, model, identifier);
        final Task task = new WriteDocumentTask(context, model, identifier, mDatabase);
        mExecutor.execute(task);
        return task.getWritingFileDescriptor();
    }
@@ -100,11 +102,14 @@ class PipeManager {

    private static class WriteDocumentTask extends Task {
        private final Context mContext;
        private final MtpDatabase mDatabase;

        WriteDocumentTask(Context context, MtpManager model, Identifier identifier)
        WriteDocumentTask(
                Context context, MtpManager model, Identifier identifier, MtpDatabase database)
                throws IOException {
            super(model, identifier);
            mContext = context;
            mDatabase = database;
        }

        @Override
@@ -112,7 +117,7 @@ class PipeManager {
            File tempFile = null;
            try {
                // Obtain a temporary file and copy the data to it.
                tempFile = mContext.getCacheDir().createTempFile("mtp", "tmp");
                tempFile = File.createTempFile("mtp", "tmp", mContext.getCacheDir());
                try (
                    final FileOutputStream tempOutputStream =
                            new ParcelFileDescriptor.AutoCloseOutputStream(
@@ -140,12 +145,22 @@ class PipeManager {
                // Create the target object info with a correct file size and upload the file.
                final MtpObjectInfo targetObjectInfo =
                        new MtpObjectInfo.Builder(placeholderObjectInfo)
                                .setCompressedSize((int) tempFile.length())
                                .setCompressedSize(tempFile.length())
                                .build();
                final ParcelFileDescriptor tempInputDescriptor = ParcelFileDescriptor.open(
                        tempFile, ParcelFileDescriptor.MODE_READ_ONLY);
                mManager.createDocument(mIdentifier.mDeviceId,
                        targetObjectInfo, tempInputDescriptor);
                final int newObjectHandle = mManager.createDocument(
                        mIdentifier.mDeviceId, targetObjectInfo, tempInputDescriptor);

                final MtpObjectInfo newObjectInfo = mManager.getObjectInfo(
                        mIdentifier.mDeviceId, newObjectHandle);
                final Identifier parentIdentifier =
                        mDatabase.getParentIdentifier(mIdentifier.mDocumentId);
                mDatabase.updateObject(
                        mIdentifier.mDocumentId,
                        mIdentifier.mDeviceId,
                        parentIdentifier.mDocumentId,
                        newObjectInfo);
            } catch (IOException error) {
                Log.w(MtpDocumentsProvider.TAG,
                        "Failed to send a file because of: " + error.getMessage());
+2 −10
Original line number Diff line number Diff line
@@ -983,18 +983,10 @@ public class MtpDatabaseTest extends AndroidTestCase {
    }

    private void addTestDevice() throws FileNotFoundException {
        mDatabase.getMapper().startAddingDocuments(null);
        mDatabase.getMapper().putDeviceDocument(new MtpDeviceRecord(
                0, "Device", "device_key", /* opened is */ true, new MtpRoot[0], null,
                null));
        mDatabase.getMapper().stopAddingDocuments(null);
        TestUtil.addTestDevice(mDatabase);
    }

    private void addTestStorage(String parentId) throws FileNotFoundException {
        mDatabase.getMapper().startAddingDocuments(parentId);
        mDatabase.getMapper().putStorageDocuments(parentId, new MtpRoot[] {
                new MtpRoot(0, 100, "Storage", 1024, 1024, ""),
        });
        mDatabase.getMapper().stopAddingDocuments(parentId);
        TestUtil.addTestStorage(mDatabase, parentId);
    }
}
+30 −9
Original line number Diff line number Diff line
@@ -16,8 +16,10 @@

package com.android.mtp;

import android.database.Cursor;
import android.mtp.MtpObjectInfo;
import android.os.ParcelFileDescriptor;
import android.provider.DocumentsContract.Document;
import android.test.AndroidTestCase;
import android.test.suitebuilder.annotation.MediumTest;

@@ -33,12 +35,14 @@ public class PipeManagerTest extends AndroidTestCase {
    private TestMtpManager mtpManager;
    private ExecutorService mExecutor;
    private PipeManager mPipeManager;
    private MtpDatabase mDatabase;

    @Override
    public void setUp() {
        mtpManager = new TestMtpManager(getContext());
        mExecutor = Executors.newSingleThreadExecutor();
        mPipeManager = new PipeManager(mExecutor);
        mDatabase = new MtpDatabase(getContext(), MtpDatabaseConstants.FLAG_DATABASE_IN_MEMORY);
        mPipeManager = new PipeManager(mDatabase, mExecutor);
    }

    public void testReadDocument_basic() throws Exception {
@@ -57,25 +61,32 @@ public class PipeManagerTest extends AndroidTestCase {
    }

    public void testWriteDocument_basic() throws Exception {
        TestUtil.addTestDevice(mDatabase);
        TestUtil.addTestStorage(mDatabase, "1");

        final MtpObjectInfo info =
                new MtpObjectInfo.Builder().setObjectHandle(1).setName("note.txt").build();
        mDatabase.getMapper().startAddingDocuments("2");
        mDatabase.getMapper().putChildDocuments(0, "2", new MtpObjectInfo[] { info });
        mDatabase.getMapper().stopAddingDocuments("2");
        // Create a placeholder file which should be replaced by a real file later.
        mtpManager.setObjectInfo(0, new MtpObjectInfo.Builder()
                .setObjectHandle(1)
                .build());
        mtpManager.setObjectInfo(0, info);

        // Upload testing bytes.
        final ParcelFileDescriptor descriptor = mPipeManager.writeDocument(
                getContext(),
                mtpManager,
                new Identifier(0, 0, 1, null, MtpDatabaseConstants.DOCUMENT_TYPE_OBJECT));
                new Identifier(0, 0, 1, "2", MtpDatabaseConstants.DOCUMENT_TYPE_OBJECT));
        final ParcelFileDescriptor.AutoCloseOutputStream outputStream =
                new ParcelFileDescriptor.AutoCloseOutputStream(descriptor);
        outputStream.write(HELLO_BYTES, 0, HELLO_BYTES.length);
        outputStream.close();
        mExecutor.awaitTermination(1000, TimeUnit.MILLISECONDS);
        mExecutor.shutdown();
        assertTrue(mExecutor.awaitTermination(1000, TimeUnit.MILLISECONDS));

        // Check if the placeholder file is removed.
        try {
            final MtpObjectInfo placeholderDocument = mtpManager.getObjectInfo(0, 1);
            mtpManager.getObjectInfo(0, 1);
            fail();  // The placeholder file has not been deleted.
        } catch (IOException e) {
            // Expected error, as the file is gone.
@@ -86,6 +97,14 @@ public class PipeManagerTest extends AndroidTestCase {
                0, TestMtpManager.CREATED_DOCUMENT_HANDLE);
        assertTrue(targetDocument != null);

        // Confirm the object handle is updated.
        try (final Cursor cursor = mDatabase.queryDocument(
                "2", new String[] { MtpDatabaseConstants.COLUMN_OBJECT_HANDLE })) {
            assertEquals(1, cursor.getCount());
            cursor.moveToNext();
            assertEquals(TestMtpManager.CREATED_DOCUMENT_HANDLE, cursor.getInt(0));
        }

        // Verify uploaded bytes.
        final byte[] uploadedBytes = mtpManager.getImportFileBytes(
                0, TestMtpManager.CREATED_DOCUMENT_HANDLE);
@@ -112,7 +131,8 @@ public class PipeManagerTest extends AndroidTestCase {

    private void assertDescriptor(ParcelFileDescriptor descriptor, byte[] expectedBytes)
            throws IOException, InterruptedException {
        mExecutor.awaitTermination(1000, TimeUnit.MILLISECONDS);
        mExecutor.shutdown();
        assertTrue(mExecutor.awaitTermination(1000, TimeUnit.MILLISECONDS));
        try (final ParcelFileDescriptor.AutoCloseInputStream stream =
                new ParcelFileDescriptor.AutoCloseInputStream(descriptor)) {
            byte[] results = new byte[100];
@@ -125,7 +145,8 @@ public class PipeManagerTest extends AndroidTestCase {

    private void assertDescriptorError(ParcelFileDescriptor descriptor)
            throws InterruptedException {
        mExecutor.awaitTermination(1000, TimeUnit.MILLISECONDS);
        mExecutor.shutdown();
        assertTrue(mExecutor.awaitTermination(1000, TimeUnit.MILLISECONDS));
        try {
            descriptor.checkError();
            fail();
Loading