blob: 41a47b95a6dd65eb949ff9dbed3138f24ec420d1 [file] [log] [blame]
/*
* Copyright (C) 2022 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.scopedstorage.cts.device;
import static android.app.AppOpsManager.permissionToOp;
import static android.os.SystemProperties.getBoolean;
import static android.scopedstorage.cts.lib.TestUtils.allowAppOpsToUid;
import static android.scopedstorage.cts.lib.TestUtils.getPicturesDir;
import static android.scopedstorage.cts.lib.TestUtils.readMaximumRowIdFromDatabaseAs;
import static android.scopedstorage.cts.lib.TestUtils.readMinimumRowIdFromDatabaseAs;
import static android.scopedstorage.cts.lib.TestUtils.waitForMountedAndIdleState;
import static com.google.common.truth.Truth.assertWithMessage;
import static org.junit.Assume.assumeFalse;
import static org.junit.Assume.assumeTrue;
import android.Manifest;
import android.app.Instrumentation;
import android.content.ContentResolver;
import android.content.Context;
import android.provider.MediaStore;
import android.scopedstorage.cts.lib.TestUtils;
import android.util.Log;
import androidx.test.core.app.ApplicationProvider;
import androidx.test.platform.app.InstrumentationRegistry;
import androidx.test.uiautomator.UiDevice;
import com.android.cts.install.lib.TestApp;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.junit.runners.Parameterized;
import org.junit.runners.Parameterized.Parameter;
import java.io.File;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
@RunWith(Parameterized.class)
public final class StableUrisTest extends ScopedStorageBaseDeviceTest {
private static final String TAG = "StableUrisTest";
// An app that has file manager (MANAGE_EXTERNAL_STORAGE) permission.
private static final TestApp APP_FM = new TestApp("TestAppFileManager",
"android.scopedstorage.cts.testapp.filemanager", 1, false,
"CtsScopedStorageTestAppFileManager.apk");
private static final String OPSTR_MANAGE_EXTERNAL_STORAGE =
permissionToOp(Manifest.permission.MANAGE_EXTERNAL_STORAGE);
private Context mContext;
private ContentResolver mContentResolver;
private UiDevice mDevice;
@Parameter()
public String mVolumeName;
/** Parameters data. */
@Parameterized.Parameters(name = "volume={0}")
public static Iterable<?> data() {
return Arrays.asList(MediaStore.VOLUME_EXTERNAL_PRIMARY);
}
@Before
public void setUp() throws Exception {
super.setupExternalStorage(mVolumeName);
Log.d(TAG, "Using volume : " + mVolumeName);
mContext = ApplicationProvider.getApplicationContext();
mContentResolver = mContext.getContentResolver();
final Instrumentation inst = InstrumentationRegistry.getInstrumentation();
mDevice = UiDevice.getInstance(inst);
}
@Test
public void testUrisMapToExistingIds_withoutNextRowIdBackup() throws Exception {
assumeFalse(getBoolean("persist.sys.fuse.backup.nextrowid_enabled", true));
testScenario(/* nextRowIdBackupEnabled */ false);
}
@Test
public void testUrisMapToNewIds_withNextRowIdBackup() throws Exception {
assumeTrue(getBoolean("persist.sys.fuse.backup.nextrowid_enabled", false));
testScenario(/* nextRowIdBackupEnabled */ true);
}
private void testScenario(boolean nextRowIdBackupEnabled) throws Exception {
List<File> files = new ArrayList<>();
try {
// Test App needs to be explicitly granted MES app op.
final int fmUid = mContext.getPackageManager().getPackageUid(APP_FM.getPackageName(),
0);
allowAppOpsToUid(fmUid, OPSTR_MANAGE_EXTERNAL_STORAGE);
files = createFilesAsTestApp(APP_FM, 5);
long maxRowIdOfInternalDbBeforeReset = readMaximumRowIdFromDatabaseAs(APP_FM,
MediaStore.Files.getContentUri(MediaStore.VOLUME_INTERNAL));
Log.d(TAG, "maxRowIdOfInternalDbBeforeReset:" + maxRowIdOfInternalDbBeforeReset);
long maxRowIdOfExternalDbBeforeReset = readMaximumRowIdFromDatabaseAs(APP_FM,
MediaStore.Files.getContentUri(MediaStore.VOLUME_EXTERNAL));
Log.d(TAG, "maxRowIdOfExternalDbBeforeReset:" + maxRowIdOfExternalDbBeforeReset);
// Clear MediaProvider package data to trigger DB recreation.
mDevice.executeShellCommand("pm clear com.google.android.providers.media.module");
waitForMountedAndIdleState(mContentResolver);
MediaStore.scanVolume(mContentResolver, mVolumeName);
long minRowIdOfInternalDbAfterReset = readMinimumRowIdFromDatabaseAs(APP_FM,
MediaStore.Files.getContentUri(MediaStore.VOLUME_INTERNAL));
Log.d(TAG, "minRowIdOfInternalDbAfterReset:" + minRowIdOfInternalDbAfterReset);
long minRowIdOfExternalDbAfterReset = readMinimumRowIdFromDatabaseAs(APP_FM,
MediaStore.Files.getContentUri(MediaStore.VOLUME_EXTERNAL));
Log.d(TAG, "minRowIdOfExternalDbAfterReset:" + minRowIdOfExternalDbAfterReset);
if (nextRowIdBackupEnabled) {
assertWithMessage(
"Expected minimum row id after internal database reset to be greater "
+ "than max row id before reset").that(
minRowIdOfInternalDbAfterReset > maxRowIdOfInternalDbBeforeReset).isTrue();
assertWithMessage(
"Expected minimum row id after external database reset to be greater "
+ "than max row id before reset").that(
minRowIdOfExternalDbAfterReset > maxRowIdOfExternalDbBeforeReset).isTrue();
} else {
assertWithMessage(
"Expected internal database row ids to be reused without next row id "
+ "backup").that(
minRowIdOfInternalDbAfterReset <= maxRowIdOfInternalDbBeforeReset).isTrue();
assertWithMessage(
"Expected external database row ids to be reused without next row id "
+ "backup").that(
minRowIdOfExternalDbAfterReset <= maxRowIdOfExternalDbBeforeReset).isTrue();
}
} finally {
for (File file : files) {
file.delete();
}
}
}
private List<File> createFilesAsTestApp(TestApp app, int count) throws Exception {
List<File> files = new ArrayList<>();
for (int i = 1; i <= count; i++) {
final File file = new File(getPicturesDir(),
"Cts_" + System.currentTimeMillis() + ".jpg");
TestUtils.createFileAs(app, file.getAbsolutePath());
MediaStore.scanFile(mContentResolver, file);
files.add(file);
}
return files;
}
}