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

Commit 8e2ed8d5 authored by Dongwon Kang's avatar Dongwon Kang
Browse files

Refactor FileInserter in MediaScanner and adding unit tests for the newly added class.

Change-Id: Ia0e8c95239916fd4c21cb5bf10ac94201e6eb6f1
parent 041ed048
Loading
Loading
Loading
Loading
+74 −0
Original line number Diff line number Diff line
/*
 * Copyright (C) 2011 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.media;

import android.content.ContentValues;
import android.content.IContentProvider;
import android.net.Uri;
import android.os.RemoteException;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;

/**
 * A MediaScanner helper class which enables us to do lazy insertion on the
 * given provider. This class manages buffers internally and flushes when they
 * are full. Note that you should call flushAll() after using this class.
 * {@hide}
 */
public class MediaInserter {
    private HashMap<Uri, List<ContentValues>> mRowMap =
            new HashMap<Uri, List<ContentValues>>();

    private IContentProvider mProvider;
    private int mBufferSizePerUri;

    public MediaInserter(IContentProvider provider, int bufferSizePerUri) {
        mProvider = provider;
        mBufferSizePerUri = bufferSizePerUri;
    }

    public void insert(Uri tableUri, ContentValues values) throws RemoteException {
        List<ContentValues> list = mRowMap.get(tableUri);
        if (list == null) {
            list = new ArrayList<ContentValues>();
            mRowMap.put(tableUri, list);
        }
        list.add(new ContentValues(values));
        if (list.size() >= mBufferSizePerUri) {
            flush(tableUri);
        }
    }

    public void flushAll() throws RemoteException {
        for (Uri tableUri : mRowMap.keySet()){
            flush(tableUri);
        }
        mRowMap.clear();
    }

    private void flush(Uri tableUri) throws RemoteException {
        List<ContentValues> list = mRowMap.get(tableUri);
        if (!list.isEmpty()) {
            ContentValues[] valuesArray = new ContentValues[list.size()];
            valuesArray = list.toArray(valuesArray);
            mProvider.bulkInsert(tableUri, valuesArray);
            list.clear();
        }
    }
}
+7 −55
Original line number Diff line number Diff line
@@ -377,43 +377,7 @@ public class MediaScanner
        }
    }

    private class FileInserter {

        private final Uri mUri;
        private final ContentValues[] mValues;
        private int mIndex;

        public FileInserter(Uri uri, int count) {
            mUri = uri;
            mValues = new ContentValues[count];
        }

        public Uri insert(ContentValues values) {
            if (mIndex == mValues.length) {
                flush();
            }
            mValues[mIndex++] = values;
            // URI not needed when doing bulk inserts
            return null;
        }

        public void flush() {
            while (mIndex < mValues.length) {
                mValues[mIndex++] = null;
            }
            try {
                mMediaProvider.bulkInsert(mUri, mValues);
            } catch (RemoteException e) {
                Log.e(TAG, "RemoteException in FileInserter.flush()", e);
            }
            mIndex = 0;
        }
    }

    private FileInserter mAudioInserter;
    private FileInserter mVideoInserter;
    private FileInserter mImageInserter;
    private FileInserter mFileInserter;
    private MediaInserter mMediaInserter;

    // hashes file path to FileCacheEntry.
    // path should be lower case if mCaseInsensitivePaths is true
@@ -880,17 +844,14 @@ public class MediaScanner
            }

            Uri tableUri = mFilesUri;
            FileInserter inserter = mFileInserter;
            MediaInserter inserter = mMediaInserter;
            if (!mNoMedia) {
                if (MediaFile.isVideoFileType(mFileType)) {
                    tableUri = mVideoUri;
                    inserter = mVideoInserter;
                } else if (MediaFile.isImageFileType(mFileType)) {
                    tableUri = mImagesUri;
                    inserter = mImageInserter;
                } else if (MediaFile.isAudioFileType(mFileType)) {
                    tableUri = mAudioUri;
                    inserter = mAudioInserter;
                }
            }
            Uri result = null;
@@ -913,7 +874,7 @@ public class MediaScanner
                if (inserter == null || entry.mFormat == MtpConstants.FORMAT_ASSOCIATION) {
                    result = mMediaProvider.insert(tableUri, values);
                } else {
                    result = inserter.insert(values);
                    inserter.insert(tableUri, values);
                }

                if (result != null) {
@@ -1212,11 +1173,8 @@ public class MediaScanner
            long prescan = System.currentTimeMillis();

            if (ENABLE_BULK_INSERTS) {
                // create FileInserters for bulk inserts
                mAudioInserter = new FileInserter(mAudioUri, 500);
                mVideoInserter = new FileInserter(mVideoUri, 500);
                mImageInserter = new FileInserter(mImagesUri, 500);
                mFileInserter = new FileInserter(mFilesUri, 500);
                // create MediaInserter for bulk inserts
                mMediaInserter = new MediaInserter(mMediaProvider, 500);
            }

            for (int i = 0; i < directories.length; i++) {
@@ -1225,14 +1183,8 @@ public class MediaScanner

            if (ENABLE_BULK_INSERTS) {
                // flush remaining inserts
                mAudioInserter.flush();
                mVideoInserter.flush();
                mImageInserter.flush();
                mFileInserter.flush();
                mAudioInserter = null;
                mVideoInserter = null;
                mImageInserter = null;
                mFileInserter = null;
                mMediaInserter.flushAll();
                mMediaInserter = null;
            }

            long scan = System.currentTimeMillis();
+2 −0
Original line number Diff line number Diff line
@@ -7,6 +7,8 @@ LOCAL_SRC_FILES := $(call all-subdir-java-files)

LOCAL_JAVA_LIBRARIES := android.test.runner

LOCAL_STATIC_JAVA_LIBRARIES := easymocklib

LOCAL_PACKAGE_NAME := mediaframeworktest

include $(BUILD_PACKAGE)
+5 −0
Original line number Diff line number Diff line
@@ -47,6 +47,7 @@ public class MediaFrameworkUnitTestRunner extends InstrumentationTestRunner {
        addMediaMetadataRetrieverStateUnitTests(suite);
        addMediaRecorderStateUnitTests(suite);
        addMediaPlayerStateUnitTests(suite);
        addMediaScannerUnitTests(suite);
        return suite;
    }

@@ -89,4 +90,8 @@ public class MediaFrameworkUnitTestRunner extends InstrumentationTestRunner {
        suite.addTestSuite(MediaPlayerSetVolumeStateUnitTest.class);
        suite.addTestSuite(MediaPlayerMetadataParserTest.class);
    }

    private void addMediaScannerUnitTests(TestSuite suite) {
        suite.addTestSuite(MediaInserterTest.class);
    }
}
+246 −0
Original line number Diff line number Diff line
/*
 * Copyright (C) 2011 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 com.android.mediaframeworktest.unit;

import android.content.ContentValues;
import android.content.IContentProvider;
import android.media.MediaInserter;
import android.net.Uri;
import android.provider.MediaStore.Audio;
import android.provider.MediaStore.Files;
import android.provider.MediaStore.Images;
import android.provider.MediaStore.Video;
import android.test.InstrumentationTestCase;
import android.test.suitebuilder.annotation.SmallTest;

import dalvik.annotation.TestTargetClass;

import org.easymock.EasyMock;
import org.easymock.IArgumentMatcher;

@TestTargetClass(MediaInserter.class)
public class MediaInserterTest extends InstrumentationTestCase {

    private MediaInserter mMediaInserter;
    private static final int TEST_BUFFER_SIZE = 10;
    private IContentProvider mMockProvider;

    private int mFilesCounter;
    private int mAudioCounter;
    private int mVideoCounter;
    private int mImagesCounter;

    private static final String sVolumeName = "external";
    private static final Uri sAudioUri = Audio.Media.getContentUri(sVolumeName);
    private static final Uri sVideoUri = Video.Media.getContentUri(sVolumeName);
    private static final Uri sImagesUri = Images.Media.getContentUri(sVolumeName);
    private static final Uri sFilesUri = Files.getContentUri(sVolumeName);

    private static class MediaUriMatcher implements IArgumentMatcher {
        private Uri mUri;

        private MediaUriMatcher(Uri uri) {
            mUri = uri;
        }

        @Override
        public boolean matches(Object argument) {
            if (!(argument instanceof Uri)) {
                return false;
            }

            Uri actualUri = (Uri) argument;
            if (actualUri == mUri) return true;
            return false;
        }

        @Override
        public void appendTo(StringBuffer buffer) {
            buffer.append("expected a TableUri '").append(mUri).append("'");
        }

        private static Uri expectMediaUri(Uri in) {
            EasyMock.reportMatcher(new MediaUriMatcher(in));
            return null;
        }
    }

    @Override
    protected void setUp() throws Exception {
        super.setUp();
        mMockProvider = EasyMock.createMock(IContentProvider.class);
        mMediaInserter = new MediaInserter(mMockProvider, TEST_BUFFER_SIZE);
        mFilesCounter = 0;
        mAudioCounter = 0;
        mVideoCounter = 0;
        mImagesCounter = 0;
    }

    private ContentValues createFileContent() {
        ContentValues values = new ContentValues();
        values.put("_data", "/mnt/sdcard/file" + ++mFilesCounter);
        return values;
    }

    private ContentValues createAudioContent() {
        ContentValues values = new ContentValues();
        values.put("_data", "/mnt/sdcard/audio" + ++mAudioCounter);
        return values;
    }

    private ContentValues createVideoContent() {
        ContentValues values = new ContentValues();
        values.put("_data", "/mnt/sdcard/video" + ++mVideoCounter);
        return values;
    }

    private ContentValues createImageContent() {
        ContentValues values = new ContentValues();
        values.put("_data", "/mnt/sdcard/image" + ++mImagesCounter);
        return values;
    }

    private ContentValues createContent(Uri uri) {
        if (uri == sFilesUri) return createFileContent();
        else if (uri == sAudioUri) return createAudioContent();
        else if (uri == sVideoUri) return createVideoContent();
        else if (uri == sImagesUri) return createImageContent();
        else throw new IllegalArgumentException("Unknown URL: " + uri.toString());
    }

    private void fillBuffer(Uri uri, int numberOfFiles) throws Exception {
        ContentValues values;
        for (int i = 0; i < numberOfFiles; ++i) {
            values = createContent(uri);
            mMediaInserter.insert(uri, values);
        }
    }

    @SmallTest
    public void testInsertContentsLessThanBufferSize() throws Exception {
        EasyMock.replay(mMockProvider);

        fillBuffer(sFilesUri, TEST_BUFFER_SIZE - 4);
        fillBuffer(sAudioUri, TEST_BUFFER_SIZE - 3);
        fillBuffer(sVideoUri, TEST_BUFFER_SIZE - 2);
        fillBuffer(sImagesUri, TEST_BUFFER_SIZE - 1);

        EasyMock.verify(mMockProvider);
    }

    @SmallTest
    public void testInsertContentsEqualToBufferSize() throws Exception {
        EasyMock.expect(mMockProvider.bulkInsert(
                (Uri) EasyMock.anyObject(), (ContentValues[]) EasyMock.anyObject())).andReturn(1);
        EasyMock.expectLastCall().times(4);
        EasyMock.replay(mMockProvider);

        fillBuffer(sFilesUri, TEST_BUFFER_SIZE);
        fillBuffer(sAudioUri, TEST_BUFFER_SIZE);
        fillBuffer(sVideoUri, TEST_BUFFER_SIZE);
        fillBuffer(sImagesUri, TEST_BUFFER_SIZE);

        EasyMock.verify(mMockProvider);
    }

    @SmallTest
    public void testInsertContentsMoreThanBufferSize() throws Exception {
        EasyMock.expect(mMockProvider.bulkInsert(
                (Uri) EasyMock.anyObject(), (ContentValues[]) EasyMock.anyObject())).andReturn(1);
        EasyMock.expectLastCall().times(4);
        EasyMock.replay(mMockProvider);

        fillBuffer(sFilesUri, TEST_BUFFER_SIZE + 1);
        fillBuffer(sAudioUri, TEST_BUFFER_SIZE + 2);
        fillBuffer(sVideoUri, TEST_BUFFER_SIZE + 3);
        fillBuffer(sImagesUri, TEST_BUFFER_SIZE + 4);

        EasyMock.verify(mMockProvider);
    }

    @SmallTest
    public void testFlushAllWithEmptyContents() throws Exception {
        EasyMock.replay(mMockProvider);

        mMediaInserter.flushAll();

        EasyMock.verify(mMockProvider);
    }

    @SmallTest
    public void testFlushAllWithSomeContents() throws Exception {
        EasyMock.expect(mMockProvider.bulkInsert(
                (Uri) EasyMock.anyObject(), (ContentValues[]) EasyMock.anyObject())).andReturn(1);
        EasyMock.expectLastCall().times(4);
        EasyMock.replay(mMockProvider);

        fillBuffer(sFilesUri, TEST_BUFFER_SIZE - 4);
        fillBuffer(sAudioUri, TEST_BUFFER_SIZE - 3);
        fillBuffer(sVideoUri, TEST_BUFFER_SIZE - 2);
        fillBuffer(sImagesUri, TEST_BUFFER_SIZE - 1);
        mMediaInserter.flushAll();

        EasyMock.verify(mMockProvider);
    }

    @SmallTest
    public void testInsertContentsAfterFlushAll() throws Exception {
        EasyMock.expect(mMockProvider.bulkInsert(
                (Uri) EasyMock.anyObject(), (ContentValues[]) EasyMock.anyObject())).andReturn(1);
        EasyMock.expectLastCall().times(8);
        EasyMock.replay(mMockProvider);

        fillBuffer(sFilesUri, TEST_BUFFER_SIZE - 4);
        fillBuffer(sAudioUri, TEST_BUFFER_SIZE - 3);
        fillBuffer(sVideoUri, TEST_BUFFER_SIZE - 2);
        fillBuffer(sImagesUri, TEST_BUFFER_SIZE - 1);
        mMediaInserter.flushAll();

        fillBuffer(sFilesUri, TEST_BUFFER_SIZE + 1);
        fillBuffer(sAudioUri, TEST_BUFFER_SIZE + 2);
        fillBuffer(sVideoUri, TEST_BUFFER_SIZE + 3);
        fillBuffer(sImagesUri, TEST_BUFFER_SIZE + 4);

        EasyMock.verify(mMockProvider);
    }

    @SmallTest
    public void testInsertContentsWithDifferentSizePerContentType() throws Exception {
        EasyMock.expect(mMockProvider.bulkInsert(MediaUriMatcher.expectMediaUri(sFilesUri),
                (ContentValues[]) EasyMock.anyObject())).andReturn(1);
        EasyMock.expectLastCall().times(1);
        EasyMock.expect(mMockProvider.bulkInsert(MediaUriMatcher.expectMediaUri(sAudioUri),
                (ContentValues[]) EasyMock.anyObject())).andReturn(1);
        EasyMock.expectLastCall().times(2);
        EasyMock.expect(mMockProvider.bulkInsert(MediaUriMatcher.expectMediaUri(sVideoUri),
                (ContentValues[]) EasyMock.anyObject())).andReturn(1);
        EasyMock.expectLastCall().times(3);
        EasyMock.expect(mMockProvider.bulkInsert(MediaUriMatcher.expectMediaUri(sImagesUri),
                (ContentValues[]) EasyMock.anyObject())).andReturn(1);
        EasyMock.expectLastCall().times(4);
        EasyMock.replay(mMockProvider);

        for (int i = 0; i < TEST_BUFFER_SIZE; ++i) {
            fillBuffer(sFilesUri, 1);
            fillBuffer(sAudioUri, 2);
            fillBuffer(sVideoUri, 3);
            fillBuffer(sImagesUri, 4);
        }

        EasyMock.verify(mMockProvider);
    }
}