Loading media/java/android/mtp/MtpDatabase.java +9 −4 Original line number Diff line number Diff line Loading @@ -46,6 +46,7 @@ import android.view.Display; import android.view.WindowManager; import com.android.internal.annotations.VisibleForNative; import com.android.internal.annotations.VisibleForTesting; import dalvik.system.CloseGuard; Loading Loading @@ -408,7 +409,8 @@ public class MtpDatabase implements AutoCloseable { } @VisibleForNative private int beginSendObject(String path, int format, int parent, int storageId) { @VisibleForTesting public int beginSendObject(String path, int format, int parent, int storageId) { MtpStorageManager.MtpObject parentObj = parent == 0 ? mManager.getStorageRoot(storageId) : mManager.getObject(parent); if (parentObj == null) { Loading Loading @@ -452,7 +454,8 @@ public class MtpDatabase implements AutoCloseable { } @VisibleForNative private int getNumObjects(int storageID, int format, int parent) { @VisibleForTesting public int getNumObjects(int storageID, int format, int parent) { List<MtpStorageManager.MtpObject> objs = mManager.getObjects(parent, format, storageID); if (objs == null) { Loading Loading @@ -830,7 +833,8 @@ public class MtpDatabase implements AutoCloseable { } @VisibleForNative private boolean getThumbnailInfo(int handle, long[] outLongs) { @VisibleForTesting public boolean getThumbnailInfo(int handle, long[] outLongs) { MtpStorageManager.MtpObject obj = mManager.getObject(handle); if (obj == null) { return false; Loading Loading @@ -866,7 +870,8 @@ public class MtpDatabase implements AutoCloseable { } @VisibleForNative private byte[] getThumbnailData(int handle) { @VisibleForTesting public byte[] getThumbnailData(int handle) { MtpStorageManager.MtpObject obj = mManager.getObject(handle); if (obj == null) { return null; Loading media/java/android/mtp/MtpStorage.java +1 −1 Original line number Diff line number Diff line Loading @@ -36,7 +36,7 @@ public class MtpStorage { public MtpStorage(StorageVolume volume, int storageId) { mStorageId = storageId; mPath = volume.getInternalPath(); mPath = volume.getPath(); mDescription = volume.getDescription(null); mRemovable = volume.isRemovable(); mMaxFileSize = volume.getMaxFileSize(); Loading media/tests/MtpTests/res/raw/test_bad_thumb.jpg 0 → 100644 +0 −0 Empty file added. media/tests/MtpTests/src/android/mtp/MtpDatabaseTest.java 0 → 100644 +316 −0 Original line number Diff line number Diff line /* * Copyright (C) 2020 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.annotation.NonNull; import android.content.Context; import android.graphics.Bitmap; import android.graphics.BitmapFactory; import android.os.Build; import android.os.FileUtils; import android.os.UserHandle; import android.os.storage.StorageManager; import android.os.storage.StorageVolume; import android.util.Log; import androidx.test.InstrumentationRegistry; import androidx.test.filters.SmallTest; import androidx.test.runner.AndroidJUnit4; import com.android.internal.util.Preconditions; import org.junit.After; import org.junit.Assert; import org.junit.Assume; import org.junit.Before; import org.junit.Test; import org.junit.runner.RunWith; import java.io.ByteArrayOutputStream; import java.io.File; import java.io.FileOutputStream; import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; /** * Tests for MtpDatabase functionality. */ @RunWith(AndroidJUnit4.class) public class MtpDatabaseTest { private static final String TAG = MtpDatabaseTest.class.getSimpleName(); private final Context mContext = InstrumentationRegistry.getContext(); private static final File mBaseDir = InstrumentationRegistry.getContext().getExternalCacheDir(); private static final String MAIN_STORAGE_DIR = mBaseDir.getPath() + "/" + TAG + "/"; private static final String TEST_DIRNAME = "/TestIs"; private static final int MAIN_STORAGE_ID = 0x10001; private static final int SCND_STORAGE_ID = 0x20001; private static final String MAIN_STORAGE_ID_STR = Integer.toHexString(MAIN_STORAGE_ID); private static final String SCND_STORAGE_ID_STR = Integer.toHexString(SCND_STORAGE_ID); private static final File mMainStorageDir = new File(MAIN_STORAGE_DIR); private static ServerHolder mServerHolder; private MtpDatabase mMtpDatabase; private static void logMethodName() { Log.d(TAG, Thread.currentThread().getStackTrace()[3].getMethodName()); } private static File createNewDir(File parent, String name) { File ret = new File(parent, name); if (!ret.mkdir()) throw new AssertionError( "Failed to create file: name=" + name + ", " + parent.getPath()); return ret; } private static void writeNewFile(File newFile) { try { new FileOutputStream(newFile).write(new byte[] {0, 0, 0}); } catch (IOException e) { Assert.fail(); } } private static void writeNewFileFromByte(File newFile, byte[] byteData) { try { new FileOutputStream(newFile).write(byteData); } catch (IOException e) { Assert.fail(); } } private static class ServerHolder { @NonNull final MtpServer server; @NonNull final MtpDatabase database; ServerHolder(@NonNull MtpServer server, @NonNull MtpDatabase database) { Preconditions.checkNotNull(server); Preconditions.checkNotNull(database); this.server = server; this.database = database; } void close() { this.database.setServer(null); } } private class OnServerTerminated implements Runnable { @Override public void run() { if (mServerHolder == null) { Log.e(TAG, "mServerHolder is unexpectedly null."); return; } mServerHolder.close(); mServerHolder = null; } } @Before public void setUp() { FileUtils.deleteContentsAndDir(mMainStorageDir); Assert.assertTrue(mMainStorageDir.mkdir()); StorageVolume mainStorage = new StorageVolume(MAIN_STORAGE_ID_STR, mMainStorageDir, mMainStorageDir, "Primary Storage", true, false, true, false, -1, UserHandle.CURRENT, "", ""); final StorageVolume primary = mainStorage; mMtpDatabase = new MtpDatabase(mContext, null); final MtpServer server = new MtpServer(mMtpDatabase, null, false, new OnServerTerminated(), Build.MANUFACTURER, Build.MODEL, "1.0"); mMtpDatabase.setServer(server); mServerHolder = new ServerHolder(server, mMtpDatabase); mMtpDatabase.addStorage(mainStorage); } @After public void tearDown() { FileUtils.deleteContentsAndDir(mMainStorageDir); } private File stageFile(int resId, File file) throws IOException { try (InputStream source = mContext.getResources().openRawResource(resId); OutputStream target = new FileOutputStream(file)) { android.os.FileUtils.copy(source, target); } return file; } /** * Refer to BitmapUtilTests, but keep here, * so as to be aware of the behavior or interface change there */ private void assertBitmapSize(int expectedWidth, int expectedHeight, Bitmap bitmap) { Assert.assertTrue( "Abnormal bitmap.width: " + bitmap.getWidth(), bitmap.getWidth() >= expectedWidth); Assert.assertTrue( "Abnormal bitmap.height: " + bitmap.getHeight(), bitmap.getHeight() >= expectedHeight); } private byte[] createJpegRawData(int sourceWidth, int sourceHeight) throws IOException { return createRawData(Bitmap.CompressFormat.JPEG, sourceWidth, sourceHeight); } private byte[] createPngRawData(int sourceWidth, int sourceHeight) throws IOException { return createRawData(Bitmap.CompressFormat.PNG, sourceWidth, sourceHeight); } private byte[] createRawData(Bitmap.CompressFormat format, int sourceWidth, int sourceHeight) throws IOException { // Create a temp bitmap as our source Bitmap b = Bitmap.createBitmap(sourceWidth, sourceHeight, Bitmap.Config.ARGB_8888); ByteArrayOutputStream outputStream = new ByteArrayOutputStream(); b.compress(format, 50, outputStream); final byte[] data = outputStream.toByteArray(); outputStream.close(); return data; } /** * Decodes the bitmap with the given sample size */ public static Bitmap decodeBitmapFromBytes(byte[] bytes, int sampleSize) { final BitmapFactory.Options options; if (sampleSize <= 1) { options = null; } else { options = new BitmapFactory.Options(); options.inSampleSize = sampleSize; } return BitmapFactory.decodeByteArray(bytes, 0, bytes.length, options); } private void testThumbnail(int fileHandle, File imgFile, boolean isGoodThumb) throws IOException { boolean isValidThumb; byte[] byteArray; long[] outLongs = new long[3]; isValidThumb = mMtpDatabase.getThumbnailInfo(fileHandle, outLongs); Assert.assertTrue(isValidThumb); byteArray = mMtpDatabase.getThumbnailData(fileHandle); if (isGoodThumb) { Assert.assertNotNull("Fail to generate thumbnail:" + imgFile.getPath(), byteArray); Bitmap testBitmap = decodeBitmapFromBytes(byteArray, 4); assertBitmapSize(32, 16, testBitmap); } else Assert.assertNull("Bad image should return null:" + imgFile.getPath(), byteArray); } @Test @SmallTest public void testMtpDatabaseThumbnail() throws IOException { int baseHandle; int handleJpgBadThumb, handleJpgNoThumb, handleJpgBad; int handlePng1, handlePngBad; final String baseTestDirStr = mMainStorageDir.getPath() + TEST_DIRNAME; logMethodName(); Log.d(TAG, "testMtpDatabaseThumbnail: Generate and insert tested files."); baseHandle = mMtpDatabase.beginSendObject(baseTestDirStr, MtpConstants.FORMAT_ASSOCIATION, 0, MAIN_STORAGE_ID); File baseDir = new File(baseTestDirStr); baseDir.mkdirs(); final File jpgfileBadThumb = new File(baseDir, "jpgfileBadThumb.jpg"); final File jpgFileNoThumb = new File(baseDir, "jpgFileNoThumb.jpg"); final File jpgfileBad = new File(baseDir, "jpgfileBad.jpg"); final File pngFile1 = new File(baseDir, "pngFile1.png"); final File pngFileBad = new File(baseDir, "pngFileBad.png"); handleJpgBadThumb = mMtpDatabase.beginSendObject(jpgfileBadThumb.getPath(), MtpConstants.FORMAT_EXIF_JPEG, baseHandle, MAIN_STORAGE_ID); stageFile(R.raw.test_bad_thumb, jpgfileBadThumb); handleJpgNoThumb = mMtpDatabase.beginSendObject(jpgFileNoThumb.getPath(), MtpConstants.FORMAT_EXIF_JPEG, baseHandle, MAIN_STORAGE_ID); writeNewFileFromByte(jpgFileNoThumb, createJpegRawData(128, 64)); handleJpgBad = mMtpDatabase.beginSendObject(jpgfileBad.getPath(), MtpConstants.FORMAT_EXIF_JPEG, baseHandle, MAIN_STORAGE_ID); writeNewFile(jpgfileBad); handlePng1 = mMtpDatabase.beginSendObject(pngFile1.getPath(), MtpConstants.FORMAT_PNG, baseHandle, MAIN_STORAGE_ID); writeNewFileFromByte(pngFile1, createPngRawData(128, 64)); handlePngBad = mMtpDatabase.beginSendObject(pngFileBad.getPath(), MtpConstants.FORMAT_PNG, baseHandle, MAIN_STORAGE_ID); writeNewFile(pngFileBad); Log.d(TAG, "testMtpDatabaseThumbnail: Test bad JPG"); testThumbnail(handleJpgBadThumb, jpgfileBadThumb, false); testThumbnail(handleJpgNoThumb, jpgFileNoThumb, false); testThumbnail(handleJpgBad, jpgfileBad, false); Log.d(TAG, "testMtpDatabaseThumbnail: Test PNG"); testThumbnail(handlePng1, pngFile1, true); Log.d(TAG, "testMtpDatabaseThumbnail: Test bad PNG"); testThumbnail(handlePngBad, pngFileBad, false); } @Test @SmallTest public void testMtpDatabaseExtStorage() throws IOException { int numObj; StorageVolume[] mVolumes; logMethodName(); mVolumes = StorageManager.getVolumeList(UserHandle.myUserId(), 0); // Currently it may need manual setup for 2nd storage on virtual device testing. // Thus only run test when 2nd storage exists. Assume.assumeTrue( "Skip when 2nd storage not available, volume numbers = " + mVolumes.length, mVolumes.length >= 2); for (int ii = 0; ii < mVolumes.length; ii++) { StorageVolume volume = mVolumes[ii]; // Skip Actual Main storage (Internal Storage), // since we use manipulated path as testing Main storage if (ii > 0) mMtpDatabase.addStorage(volume); } numObj = mMtpDatabase.getNumObjects(SCND_STORAGE_ID, 0, 0xFFFFFFFF); Assert.assertTrue( "Fail to get objects in 2nd storage, object numbers = " + numObj, numObj >= 0); } } Loading
media/java/android/mtp/MtpDatabase.java +9 −4 Original line number Diff line number Diff line Loading @@ -46,6 +46,7 @@ import android.view.Display; import android.view.WindowManager; import com.android.internal.annotations.VisibleForNative; import com.android.internal.annotations.VisibleForTesting; import dalvik.system.CloseGuard; Loading Loading @@ -408,7 +409,8 @@ public class MtpDatabase implements AutoCloseable { } @VisibleForNative private int beginSendObject(String path, int format, int parent, int storageId) { @VisibleForTesting public int beginSendObject(String path, int format, int parent, int storageId) { MtpStorageManager.MtpObject parentObj = parent == 0 ? mManager.getStorageRoot(storageId) : mManager.getObject(parent); if (parentObj == null) { Loading Loading @@ -452,7 +454,8 @@ public class MtpDatabase implements AutoCloseable { } @VisibleForNative private int getNumObjects(int storageID, int format, int parent) { @VisibleForTesting public int getNumObjects(int storageID, int format, int parent) { List<MtpStorageManager.MtpObject> objs = mManager.getObjects(parent, format, storageID); if (objs == null) { Loading Loading @@ -830,7 +833,8 @@ public class MtpDatabase implements AutoCloseable { } @VisibleForNative private boolean getThumbnailInfo(int handle, long[] outLongs) { @VisibleForTesting public boolean getThumbnailInfo(int handle, long[] outLongs) { MtpStorageManager.MtpObject obj = mManager.getObject(handle); if (obj == null) { return false; Loading Loading @@ -866,7 +870,8 @@ public class MtpDatabase implements AutoCloseable { } @VisibleForNative private byte[] getThumbnailData(int handle) { @VisibleForTesting public byte[] getThumbnailData(int handle) { MtpStorageManager.MtpObject obj = mManager.getObject(handle); if (obj == null) { return null; Loading
media/java/android/mtp/MtpStorage.java +1 −1 Original line number Diff line number Diff line Loading @@ -36,7 +36,7 @@ public class MtpStorage { public MtpStorage(StorageVolume volume, int storageId) { mStorageId = storageId; mPath = volume.getInternalPath(); mPath = volume.getPath(); mDescription = volume.getDescription(null); mRemovable = volume.isRemovable(); mMaxFileSize = volume.getMaxFileSize(); Loading
media/tests/MtpTests/src/android/mtp/MtpDatabaseTest.java 0 → 100644 +316 −0 Original line number Diff line number Diff line /* * Copyright (C) 2020 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.annotation.NonNull; import android.content.Context; import android.graphics.Bitmap; import android.graphics.BitmapFactory; import android.os.Build; import android.os.FileUtils; import android.os.UserHandle; import android.os.storage.StorageManager; import android.os.storage.StorageVolume; import android.util.Log; import androidx.test.InstrumentationRegistry; import androidx.test.filters.SmallTest; import androidx.test.runner.AndroidJUnit4; import com.android.internal.util.Preconditions; import org.junit.After; import org.junit.Assert; import org.junit.Assume; import org.junit.Before; import org.junit.Test; import org.junit.runner.RunWith; import java.io.ByteArrayOutputStream; import java.io.File; import java.io.FileOutputStream; import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; /** * Tests for MtpDatabase functionality. */ @RunWith(AndroidJUnit4.class) public class MtpDatabaseTest { private static final String TAG = MtpDatabaseTest.class.getSimpleName(); private final Context mContext = InstrumentationRegistry.getContext(); private static final File mBaseDir = InstrumentationRegistry.getContext().getExternalCacheDir(); private static final String MAIN_STORAGE_DIR = mBaseDir.getPath() + "/" + TAG + "/"; private static final String TEST_DIRNAME = "/TestIs"; private static final int MAIN_STORAGE_ID = 0x10001; private static final int SCND_STORAGE_ID = 0x20001; private static final String MAIN_STORAGE_ID_STR = Integer.toHexString(MAIN_STORAGE_ID); private static final String SCND_STORAGE_ID_STR = Integer.toHexString(SCND_STORAGE_ID); private static final File mMainStorageDir = new File(MAIN_STORAGE_DIR); private static ServerHolder mServerHolder; private MtpDatabase mMtpDatabase; private static void logMethodName() { Log.d(TAG, Thread.currentThread().getStackTrace()[3].getMethodName()); } private static File createNewDir(File parent, String name) { File ret = new File(parent, name); if (!ret.mkdir()) throw new AssertionError( "Failed to create file: name=" + name + ", " + parent.getPath()); return ret; } private static void writeNewFile(File newFile) { try { new FileOutputStream(newFile).write(new byte[] {0, 0, 0}); } catch (IOException e) { Assert.fail(); } } private static void writeNewFileFromByte(File newFile, byte[] byteData) { try { new FileOutputStream(newFile).write(byteData); } catch (IOException e) { Assert.fail(); } } private static class ServerHolder { @NonNull final MtpServer server; @NonNull final MtpDatabase database; ServerHolder(@NonNull MtpServer server, @NonNull MtpDatabase database) { Preconditions.checkNotNull(server); Preconditions.checkNotNull(database); this.server = server; this.database = database; } void close() { this.database.setServer(null); } } private class OnServerTerminated implements Runnable { @Override public void run() { if (mServerHolder == null) { Log.e(TAG, "mServerHolder is unexpectedly null."); return; } mServerHolder.close(); mServerHolder = null; } } @Before public void setUp() { FileUtils.deleteContentsAndDir(mMainStorageDir); Assert.assertTrue(mMainStorageDir.mkdir()); StorageVolume mainStorage = new StorageVolume(MAIN_STORAGE_ID_STR, mMainStorageDir, mMainStorageDir, "Primary Storage", true, false, true, false, -1, UserHandle.CURRENT, "", ""); final StorageVolume primary = mainStorage; mMtpDatabase = new MtpDatabase(mContext, null); final MtpServer server = new MtpServer(mMtpDatabase, null, false, new OnServerTerminated(), Build.MANUFACTURER, Build.MODEL, "1.0"); mMtpDatabase.setServer(server); mServerHolder = new ServerHolder(server, mMtpDatabase); mMtpDatabase.addStorage(mainStorage); } @After public void tearDown() { FileUtils.deleteContentsAndDir(mMainStorageDir); } private File stageFile(int resId, File file) throws IOException { try (InputStream source = mContext.getResources().openRawResource(resId); OutputStream target = new FileOutputStream(file)) { android.os.FileUtils.copy(source, target); } return file; } /** * Refer to BitmapUtilTests, but keep here, * so as to be aware of the behavior or interface change there */ private void assertBitmapSize(int expectedWidth, int expectedHeight, Bitmap bitmap) { Assert.assertTrue( "Abnormal bitmap.width: " + bitmap.getWidth(), bitmap.getWidth() >= expectedWidth); Assert.assertTrue( "Abnormal bitmap.height: " + bitmap.getHeight(), bitmap.getHeight() >= expectedHeight); } private byte[] createJpegRawData(int sourceWidth, int sourceHeight) throws IOException { return createRawData(Bitmap.CompressFormat.JPEG, sourceWidth, sourceHeight); } private byte[] createPngRawData(int sourceWidth, int sourceHeight) throws IOException { return createRawData(Bitmap.CompressFormat.PNG, sourceWidth, sourceHeight); } private byte[] createRawData(Bitmap.CompressFormat format, int sourceWidth, int sourceHeight) throws IOException { // Create a temp bitmap as our source Bitmap b = Bitmap.createBitmap(sourceWidth, sourceHeight, Bitmap.Config.ARGB_8888); ByteArrayOutputStream outputStream = new ByteArrayOutputStream(); b.compress(format, 50, outputStream); final byte[] data = outputStream.toByteArray(); outputStream.close(); return data; } /** * Decodes the bitmap with the given sample size */ public static Bitmap decodeBitmapFromBytes(byte[] bytes, int sampleSize) { final BitmapFactory.Options options; if (sampleSize <= 1) { options = null; } else { options = new BitmapFactory.Options(); options.inSampleSize = sampleSize; } return BitmapFactory.decodeByteArray(bytes, 0, bytes.length, options); } private void testThumbnail(int fileHandle, File imgFile, boolean isGoodThumb) throws IOException { boolean isValidThumb; byte[] byteArray; long[] outLongs = new long[3]; isValidThumb = mMtpDatabase.getThumbnailInfo(fileHandle, outLongs); Assert.assertTrue(isValidThumb); byteArray = mMtpDatabase.getThumbnailData(fileHandle); if (isGoodThumb) { Assert.assertNotNull("Fail to generate thumbnail:" + imgFile.getPath(), byteArray); Bitmap testBitmap = decodeBitmapFromBytes(byteArray, 4); assertBitmapSize(32, 16, testBitmap); } else Assert.assertNull("Bad image should return null:" + imgFile.getPath(), byteArray); } @Test @SmallTest public void testMtpDatabaseThumbnail() throws IOException { int baseHandle; int handleJpgBadThumb, handleJpgNoThumb, handleJpgBad; int handlePng1, handlePngBad; final String baseTestDirStr = mMainStorageDir.getPath() + TEST_DIRNAME; logMethodName(); Log.d(TAG, "testMtpDatabaseThumbnail: Generate and insert tested files."); baseHandle = mMtpDatabase.beginSendObject(baseTestDirStr, MtpConstants.FORMAT_ASSOCIATION, 0, MAIN_STORAGE_ID); File baseDir = new File(baseTestDirStr); baseDir.mkdirs(); final File jpgfileBadThumb = new File(baseDir, "jpgfileBadThumb.jpg"); final File jpgFileNoThumb = new File(baseDir, "jpgFileNoThumb.jpg"); final File jpgfileBad = new File(baseDir, "jpgfileBad.jpg"); final File pngFile1 = new File(baseDir, "pngFile1.png"); final File pngFileBad = new File(baseDir, "pngFileBad.png"); handleJpgBadThumb = mMtpDatabase.beginSendObject(jpgfileBadThumb.getPath(), MtpConstants.FORMAT_EXIF_JPEG, baseHandle, MAIN_STORAGE_ID); stageFile(R.raw.test_bad_thumb, jpgfileBadThumb); handleJpgNoThumb = mMtpDatabase.beginSendObject(jpgFileNoThumb.getPath(), MtpConstants.FORMAT_EXIF_JPEG, baseHandle, MAIN_STORAGE_ID); writeNewFileFromByte(jpgFileNoThumb, createJpegRawData(128, 64)); handleJpgBad = mMtpDatabase.beginSendObject(jpgfileBad.getPath(), MtpConstants.FORMAT_EXIF_JPEG, baseHandle, MAIN_STORAGE_ID); writeNewFile(jpgfileBad); handlePng1 = mMtpDatabase.beginSendObject(pngFile1.getPath(), MtpConstants.FORMAT_PNG, baseHandle, MAIN_STORAGE_ID); writeNewFileFromByte(pngFile1, createPngRawData(128, 64)); handlePngBad = mMtpDatabase.beginSendObject(pngFileBad.getPath(), MtpConstants.FORMAT_PNG, baseHandle, MAIN_STORAGE_ID); writeNewFile(pngFileBad); Log.d(TAG, "testMtpDatabaseThumbnail: Test bad JPG"); testThumbnail(handleJpgBadThumb, jpgfileBadThumb, false); testThumbnail(handleJpgNoThumb, jpgFileNoThumb, false); testThumbnail(handleJpgBad, jpgfileBad, false); Log.d(TAG, "testMtpDatabaseThumbnail: Test PNG"); testThumbnail(handlePng1, pngFile1, true); Log.d(TAG, "testMtpDatabaseThumbnail: Test bad PNG"); testThumbnail(handlePngBad, pngFileBad, false); } @Test @SmallTest public void testMtpDatabaseExtStorage() throws IOException { int numObj; StorageVolume[] mVolumes; logMethodName(); mVolumes = StorageManager.getVolumeList(UserHandle.myUserId(), 0); // Currently it may need manual setup for 2nd storage on virtual device testing. // Thus only run test when 2nd storage exists. Assume.assumeTrue( "Skip when 2nd storage not available, volume numbers = " + mVolumes.length, mVolumes.length >= 2); for (int ii = 0; ii < mVolumes.length; ii++) { StorageVolume volume = mVolumes[ii]; // Skip Actual Main storage (Internal Storage), // since we use manipulated path as testing Main storage if (ii > 0) mMtpDatabase.addStorage(volume); } numObj = mMtpDatabase.getNumObjects(SCND_STORAGE_ID, 0, 0xFFFFFFFF); Assert.assertTrue( "Fail to get objects in 2nd storage, object numbers = " + numObj, numObj >= 0); } }