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

Commit 35e9e93f authored by Arthur Hung's avatar Arthur Hung Committed by Android (Google) Code Review
Browse files

Merge "Fix crash for uncaught exception in MetadataLoader"

parents 491bdb40 0b8b22e8
Loading
Loading
Loading
Loading
+1 −1
Original line number Diff line number Diff line
@@ -47,7 +47,7 @@ final class MetadataLoader extends AsyncTaskLoader<Bundle> {
    public Bundle loadInBackground() {
        try {
            return DocumentsContract.getDocumentMetadata(mContext.getContentResolver(), mUri);
        } catch (FileNotFoundException e) {
        } catch (FileNotFoundException | RuntimeException e) {
            Log.e(TAG, "Failed to load metadata for doc: " + mUri, e);
        }

+32 −1
Original line number Diff line number Diff line
@@ -15,12 +15,16 @@
 */
package com.android.documentsui;

import static android.provider.DocumentsContract.Document.FLAG_SUPPORTS_METADATA;
import static android.provider.DocumentsContract.Document.FLAG_SUPPORTS_SETTINGS;

import android.database.Cursor;
import android.database.MatrixCursor;
import android.database.MatrixCursor.RowBuilder;
import android.media.ExifInterface;
import android.os.Bundle;
import android.provider.DocumentsContract.Document;

import java.io.FileNotFoundException;

/**
@@ -32,6 +36,8 @@ import java.io.FileNotFoundException;
 *         openInProvider    Dummy1 50B      Dummy11 50B     Dummy22 150B
 *         test.txt          Dummy2 150B     Dummy12 150B    Dummy23 100B
 *         update.txt        Dummy3 100B     Dummy13 100B
 *         test.jpg
 *         invalid.jpg
 */
public class InspectorProvider extends TestRootProvider {

@@ -39,6 +45,15 @@ public class InspectorProvider extends TestRootProvider {
    public static final String OPEN_IN_PROVIDER_TEST = "OpenInProviderTest";
    public static final String ROOT_ID = "inspector-root";

    // Virtual jpeg files for test metadata loading from provider.
    // TEST_JPEG is a normal jpeg file with legal exif data.
    // INVALID_JPEG is a invalid jpeg file with broken exif data.
    // TEST_JPEG_WIDTH, TEST_JPEG_HEIGHT are exif information for TEST_JPEG.
    public static final String TEST_JPEG = "test.jpg";
    public static final String INVALID_JPEG = "invalid.jpg";
    public static final int TEST_JPEG_WIDTH = 3840;
    public static final int TEST_JPEG_HEIGHT = 2160;

    private static final String ROOT_DOC_ID = "root0";
    private static final int ROOT_FLAGS = 0;

@@ -94,6 +109,8 @@ public class InspectorProvider extends TestRootProvider {
            addFile(c, OPEN_IN_PROVIDER_TEST, FLAG_SUPPORTS_SETTINGS);
            addFile(c, "test.txt");
            addFile(c, "update.txt");
            addFile(c, TEST_JPEG, FLAG_SUPPORTS_METADATA);
            addFile(c, INVALID_JPEG, FLAG_SUPPORTS_METADATA);
            return c;
        }
    }
@@ -108,6 +125,20 @@ public class InspectorProvider extends TestRootProvider {
        row.add(Document.COLUMN_LAST_MODIFIED, System.currentTimeMillis());
    }

    @Override
    public Bundle getDocumentMetadata(String documentId) throws FileNotFoundException {
        if (TEST_JPEG.contentEquals(documentId)) {
            Bundle metaData = new Bundle();
            metaData.putInt(ExifInterface.TAG_IMAGE_WIDTH, TEST_JPEG_WIDTH);
            metaData.putInt(ExifInterface.TAG_IMAGE_LENGTH, TEST_JPEG_HEIGHT);
            return metaData;
        } else if (INVALID_JPEG.contentEquals(documentId)) {
            // Suppose there are some errors occurs.
            // Return null makes DocumentsContract throw a RemoteExcpetion,
            // and rethrow a RemoteException when using ContentResolver.
            return null;
        }


        return super.getDocumentMetadata(documentId);
    }
}
+41 −0
Original line number Diff line number Diff line
package com.android.documentsui.testing;

import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertTrue;

import java.util.concurrent.CountDownLatch;
import java.util.concurrent.TimeUnit;
import java.util.function.Consumer;

/**
 * Helper class for testing async processes.
 */
public class LatchedConsumer<T> implements Consumer<T> {

    private T value;
    private CountDownLatch latch;

    public LatchedConsumer(int expectedCount) {
        latch = new CountDownLatch(expectedCount);
    }

    public CountDownLatch getLatch() { return latch; }
    public T getValue() { return value; }


    @Override
    public void accept(T value) {
        this.value = value;
        latch.countDown();
    }

    public void assertNotCalled(long timeout, TimeUnit unit)
            throws InterruptedException {
        assertFalse(latch.await(timeout, unit));
    }

    public void assertCalled(long timeout, TimeUnit unit)
            throws InterruptedException {
        assertTrue(latch.await(timeout, unit));
    }
}
+45 −47
Original line number Diff line number Diff line
@@ -17,23 +17,27 @@ package com.android.documentsui.inspector;

import android.content.ContentResolver;
import android.content.Context;
import android.media.ExifInterface;
import android.net.Uri;
import android.os.Bundle;
import android.os.Looper;
import android.provider.DocumentsContract;
import androidx.annotation.Nullable;
import android.support.test.InstrumentationRegistry;
import com.android.documentsui.InspectorProvider;
import android.test.suitebuilder.annotation.MediumTest;

import com.android.documentsui.InspectorProvider;
import com.android.documentsui.base.DocumentInfo;
import com.android.documentsui.inspector.InspectorController.DataSupplier;
import com.android.documentsui.testing.LatchedConsumer;
import com.android.documentsui.testing.TestLoaderManager;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.TimeUnit;
import java.util.function.Consumer;

import junit.framework.TestCase;

import org.junit.Before;
import org.junit.Test;

import java.util.concurrent.TimeUnit;

/**
 * This test relies the inspector providers test.txt file in inspector root.
 */
@@ -72,17 +76,17 @@ public class DocumentLoaderTest extends TestCase {
    public void testLoadsDocument() throws Exception {
        Uri validUri = DocumentsContract.buildDocumentUri(
                InspectorProvider.AUTHORITY, TEST_DOC_NAME);
        TestDocConsumer consumer = new TestDocConsumer(1);
        LatchedConsumer<DocumentInfo> consumer = new LatchedConsumer<>(1);
        mLoader.loadDocInfo(validUri, consumer);

        // this is a test double that requires explicitly loading. @see TestLoaderManager
        mLoaderManager.getLoader(0).startLoading();

        consumer.latch.await(1000, TimeUnit.MILLISECONDS);
        consumer.assertCalled(1000, TimeUnit.MILLISECONDS);

        assertNotNull(consumer.info);
        assertEquals(consumer.info.displayName, TEST_DOC_NAME);
        assertEquals(consumer.info.size, 0);
        assertNotNull(consumer.getValue());
        assertEquals(consumer.getValue().displayName, TEST_DOC_NAME);
        assertEquals(consumer.getValue().size, 0);
    }

    /**
@@ -93,21 +97,21 @@ public class DocumentLoaderTest extends TestCase {
    @Test
    public void testInvalidInput() throws Exception {
        Uri invalidUri = Uri.parse("content://poodles/chuckleberry/ham");
        TestDocConsumer consumer = new TestDocConsumer(1);
        LatchedConsumer<DocumentInfo> consumer = new LatchedConsumer<>(1);
        mLoader.loadDocInfo(invalidUri, consumer);

        // this is a test double that requires explicitly loading. @see TestLoaderManager
        mLoaderManager.getLoader(0).startLoading();

        consumer.latch.await(1000, TimeUnit.MILLISECONDS);
        assertNull(consumer.info);
        consumer.assertCalled(1000, TimeUnit.MILLISECONDS);
        assertNull(consumer.getValue());
    }

    @Test
    public void testNonContentUri() {

        Uri invalidUri = Uri.parse("http://poodles/chuckleberry/ham");
        TestDocConsumer consumer = new TestDocConsumer(1);
        LatchedConsumer<DocumentInfo> consumer = new LatchedConsumer<>(1);

        try {
            mLoader.loadDocInfo(invalidUri, consumer);
@@ -125,12 +129,12 @@ public class DocumentLoaderTest extends TestCase {

        DocumentInfo info = DocumentInfo.fromUri(mResolver, dirUri);

        TestDirConsumer consumer = new TestDirConsumer(1);
        LatchedConsumer<Integer> consumer = new LatchedConsumer<>(1);
        mLoader.loadDirCount(info, consumer);
        mLoaderManager.getLoader(0).startLoading();

        consumer.latch.await(1000, TimeUnit.MILLISECONDS);
        assertEquals(consumer.childCount, 4);
        consumer.assertCalled(1000, TimeUnit.MILLISECONDS);
        assertEquals(consumer.getValue().intValue(), 4);
    }

    @Test
@@ -139,7 +143,7 @@ public class DocumentLoaderTest extends TestCase {
            InspectorProvider.AUTHORITY, NOT_DIRECTORY);

        DocumentInfo info = DocumentInfo.fromUri(mResolver, uri);
        TestDirConsumer consumer = new TestDirConsumer(1);
        LatchedConsumer<Integer> consumer = new LatchedConsumer<>(1);

        try {
            mLoader.loadDirCount(info, consumer);
@@ -148,39 +152,33 @@ public class DocumentLoaderTest extends TestCase {
        } catch (Exception expected) {}
    }

    /**
     * Helper function for testing async processes.
     */
    private static class TestDocConsumer implements Consumer<DocumentInfo> {

        private DocumentInfo info;
        private CountDownLatch latch;
    @Test
    public void testLoadMetadata() throws Exception  {
        Uri uri = DocumentsContract.buildDocumentUri(
                InspectorProvider.AUTHORITY, InspectorProvider.TEST_JPEG);
        LatchedConsumer<Bundle> consumer = new LatchedConsumer<>(1);

        public TestDocConsumer(int expectedCount) {
            latch = new CountDownLatch(expectedCount);
        }
        mLoader.getDocumentMetadata(uri, consumer);
        mLoaderManager.getLoader(0).startLoading();

        @Nullable
        @Override
        public void accept(DocumentInfo documentInfo) {
            info = documentInfo;
            latch.countDown();
        }
        consumer.assertCalled(100, TimeUnit.MILLISECONDS);
        assertNotNull(consumer.getValue());
        assertEquals(consumer.getValue().getInt(ExifInterface.TAG_IMAGE_WIDTH),
                InspectorProvider.TEST_JPEG_WIDTH);
        assertEquals(consumer.getValue().getInt(ExifInterface.TAG_IMAGE_LENGTH),
                InspectorProvider.TEST_JPEG_HEIGHT);
    }

    private static class TestDirConsumer implements Consumer<Integer> {

        private int childCount;
        private CountDownLatch latch;
    @Test
    public void testLoadMetadata_Unsupported() throws Exception  {
        Uri uri = DocumentsContract.buildDocumentUri(
                InspectorProvider.AUTHORITY, InspectorProvider.INVALID_JPEG);
        LatchedConsumer<Bundle> consumer = new LatchedConsumer<>(1);

        public TestDirConsumer(int expectedCount) {
            latch = new CountDownLatch(expectedCount);
        }
        mLoader.getDocumentMetadata(uri, consumer);
        mLoaderManager.getLoader(0).startLoading();

        @Override
        public void accept(Integer integer) {
            childCount = integer;
            latch.countDown();
        }
        consumer.assertCalled(100, TimeUnit.MILLISECONDS);
        assertNull(consumer.getValue());
    }
}