summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorDipankar Bhardwaj <dipankarb@google.com>2023-02-16 11:24:00 +0000
committerDipankar Bhardwaj <dipankarb@google.com>2023-02-28 11:43:50 +0000
commit3579ba628ab50265578cf1ff50398f0a49184659 (patch)
treefb24bd50878aba92b89540947cd5cb48b23a479e
parentded1883f0c2c1b7aa7fab1fdd4f66b41d1c8504e (diff)
downloadMediaProvider-3579ba628ab50265578cf1ff50398f0a49184659.tar.gz
Refactor database recovery code
Moved recovery code from DatabaseHelper to DatabaseBackupAndRecovery class. Test: atest StableUriIdleMaintenanceServiceTest.java Bug: 259259604 Change-Id: I1405aaccc124783349050b0ffd293aac2ed8e910
-rw-r--r--src/com/android/providers/media/DatabaseBackupAndRecovery.java86
-rw-r--r--src/com/android/providers/media/DatabaseHelper.java100
-rw-r--r--src/com/android/providers/media/MediaProvider.java2
3 files changed, 93 insertions, 95 deletions
diff --git a/src/com/android/providers/media/DatabaseBackupAndRecovery.java b/src/com/android/providers/media/DatabaseBackupAndRecovery.java
index b7afca866..d861e5b88 100644
--- a/src/com/android/providers/media/DatabaseBackupAndRecovery.java
+++ b/src/com/android/providers/media/DatabaseBackupAndRecovery.java
@@ -17,6 +17,9 @@
package com.android.providers.media;
import static com.android.providers.media.DatabaseHelper.INTERNAL_DATABASE_NAME;
+import static com.android.providers.media.MediaProviderStatsLog.MEDIA_PROVIDER_VOLUME_RECOVERY_REPORTED__VOLUME__EXTERNAL_PRIMARY;
+import static com.android.providers.media.MediaProviderStatsLog.MEDIA_PROVIDER_VOLUME_RECOVERY_REPORTED__VOLUME__INTERNAL;
+import static com.android.providers.media.MediaProviderStatsLog.MEDIA_PROVIDER_VOLUME_RECOVERY_REPORTED__VOLUME__PUBLIC;
import static com.android.providers.media.util.Logging.TAG;
import android.content.ContentValues;
@@ -24,6 +27,7 @@ import android.database.Cursor;
import android.database.sqlite.SQLiteDatabase;
import android.os.CancellationSignal;
import android.os.ParcelFileDescriptor;
+import android.os.SystemClock;
import android.os.SystemProperties;
import android.os.UserHandle;
import android.provider.MediaStore;
@@ -31,6 +35,7 @@ import android.system.Os;
import android.util.Log;
import android.util.Pair;
+import androidx.annotation.GuardedBy;
import androidx.annotation.NonNull;
import com.android.providers.media.dao.FileRow;
@@ -88,6 +93,11 @@ public class DatabaseBackupAndRecovery {
"/storage/emulated/" + UserHandle.myUserId();
/**
+ * Number of records to read from leveldb in a JNI call.
+ */
+ protected static final int LEVEL_DB_READ_LIMIT = 1000;
+
+ /**
* Stores cached value of next owner id. This helps in improving performance by backing up next
* row id less frequently in the external storage.
*/
@@ -605,6 +615,82 @@ public class DatabaseBackupAndRecovery {
}
}
+ protected void recoverData(SQLiteDatabase db, String volumeName) {
+ final long startTime = SystemClock.elapsedRealtime();
+ int i = 0;
+ final String fuseFilePath = getFuseFilePathFromVolumeName(volumeName);
+ // Wait for external primary to be attached as we use same thread for internal volume.
+ // Maximum wait for 10s
+ while (!isFuseDaemonReadyForFilePath(fuseFilePath) && i < 1000) {
+ Log.d(TAG, "Waiting for fuse daemon to be ready.");
+ // Poll after every 10ms
+ SystemClock.sleep(10);
+ i++;
+ }
+ if (!isFuseDaemonReadyForFilePath(fuseFilePath)) {
+ Log.e(TAG, "Could not recover data as fuse daemon could not serve requests.");
+ return;
+ }
+
+ long rowsRecovered = 0;
+ long dirtyRowsCount = 0;
+ String[] backedUpFilePaths;
+ String lastReadValue = "";
+
+ while (true) {
+ backedUpFilePaths = readBackedUpFilePaths(volumeName, lastReadValue,
+ LEVEL_DB_READ_LIMIT);
+ if (backedUpFilePaths.length <= 0) {
+ break;
+ }
+
+ for (String filePath : backedUpFilePaths) {
+ Optional<BackupIdRow> fileRow = readDataFromBackup(volumeName, filePath);
+ if (fileRow.isPresent()) {
+ if (fileRow.get().getIsDirty()) {
+ dirtyRowsCount++;
+ continue;
+ }
+
+ insertDataInDatabase(db, fileRow.get(), filePath, volumeName);
+ rowsRecovered++;
+ }
+ }
+
+ // Read less rows than expected
+ if (backedUpFilePaths.length < LEVEL_DB_READ_LIMIT) {
+ break;
+ }
+ lastReadValue = backedUpFilePaths[backedUpFilePaths.length - 1];
+ }
+ long recoveryTime = SystemClock.elapsedRealtime() - startTime;
+ MediaProviderStatsLog.write(MediaProviderStatsLog.MEDIA_PROVIDER_VOLUME_RECOVERY_REPORTED,
+ getVolumeName(volumeName), recoveryTime, rowsRecovered, dirtyRowsCount);
+ Log.i(TAG, String.format(Locale.ROOT, "%d rows recovered for volume:%s.", rowsRecovered,
+ volumeName));
+ Log.i(TAG, String.format(Locale.ROOT, "Recovery time: %d ms", recoveryTime));
+ }
+
+ private int getVolumeName(String volumeName) {
+ if (volumeName.equalsIgnoreCase(MediaStore.VOLUME_INTERNAL)) {
+ return MEDIA_PROVIDER_VOLUME_RECOVERY_REPORTED__VOLUME__INTERNAL;
+ } else if (volumeName.equalsIgnoreCase(MediaStore.VOLUME_EXTERNAL_PRIMARY)) {
+ return MEDIA_PROVIDER_VOLUME_RECOVERY_REPORTED__VOLUME__EXTERNAL_PRIMARY;
+ }
+
+ return MEDIA_PROVIDER_VOLUME_RECOVERY_REPORTED__VOLUME__PUBLIC;
+ }
+
+ private static String getFuseFilePathFromVolumeName(String volumeName) {
+ switch (volumeName) {
+ case MediaStore.VOLUME_INTERNAL:
+ case MediaStore.VOLUME_EXTERNAL_PRIMARY:
+ return EXTERNAL_PRIMARY_ROOT_PATH;
+ default:
+ return "/storage/" + volumeName;
+ }
+ }
+
/**
* Returns list of backed up files from external storage.
*/
diff --git a/src/com/android/providers/media/DatabaseHelper.java b/src/com/android/providers/media/DatabaseHelper.java
index afec29d2d..aac3fcbfa 100644
--- a/src/com/android/providers/media/DatabaseHelper.java
+++ b/src/com/android/providers/media/DatabaseHelper.java
@@ -18,9 +18,6 @@ package com.android.providers.media;
import static com.android.providers.media.DatabaseBackupAndRecovery.getXattr;
import static com.android.providers.media.DatabaseBackupAndRecovery.setXattr;
-import static com.android.providers.media.MediaProviderStatsLog.MEDIA_PROVIDER_VOLUME_RECOVERY_REPORTED__VOLUME__EXTERNAL_PRIMARY;
-import static com.android.providers.media.MediaProviderStatsLog.MEDIA_PROVIDER_VOLUME_RECOVERY_REPORTED__VOLUME__INTERNAL;
-import static com.android.providers.media.MediaProviderStatsLog.MEDIA_PROVIDER_VOLUME_RECOVERY_REPORTED__VOLUME__PUBLIC;
import static com.android.providers.media.util.DatabaseUtils.bindList;
import static com.android.providers.media.util.Logging.LOGV;
import static com.android.providers.media.util.Logging.TAG;
@@ -71,7 +68,6 @@ import androidx.annotation.VisibleForTesting;
import com.android.modules.utils.BackgroundThread;
import com.android.providers.media.dao.FileRow;
import com.android.providers.media.playlist.Playlist;
-import com.android.providers.media.stableuris.dao.BackupIdRow;
import com.android.providers.media.util.DatabaseUtils;
import com.android.providers.media.util.FileUtils;
import com.android.providers.media.util.ForegroundThread;
@@ -154,8 +150,6 @@ public class DatabaseHelper extends SQLiteOpenHelper implements AutoCloseable {
*/
private static final String DATA_MEDIA_XATTR_DIRECTORY_PATH = "/data/media/0";
- protected static final int LEVEL_DB_READ_LIMIT = 1000;
-
static final String INTERNAL_DATABASE_NAME = "internal.db";
static final String EXTERNAL_DATABASE_NAME = "external.db";
@@ -557,10 +551,11 @@ public class DatabaseHelper extends SQLiteOpenHelper implements AutoCloseable {
} catch (Exception e) {
throw new RuntimeException("Could not retrieve local content provider", e);
}
+ DatabaseBackupAndRecovery databaseBackupAndRecovery =
+ mediaProvider.getDatabaseBackupAndRecovery();
String volumeName =
isInternal() ? MediaStore.VOLUME_INTERNAL : MediaStore.VOLUME_EXTERNAL;
- if (!isInternal()
- || !mediaProvider.getDatabaseBackupAndRecovery().isStableUrisEnabled(volumeName)) {
+ if (!isInternal() || !databaseBackupAndRecovery.isStableUrisEnabled(volumeName)) {
return;
}
@@ -574,9 +569,8 @@ public class DatabaseHelper extends SQLiteOpenHelper implements AutoCloseable {
// StableUrisIdleMaintenanceService will be attempted to run only once in 7days.
// Any rollback before that will not recover DB rows.
BackgroundThread.getExecutor().execute(
- () -> mediaProvider.getDatabaseBackupAndRecovery()
- .backupInternalDatabase(null));
- // Set next row id in External Storage to handle rollback in future.
+ () -> databaseBackupAndRecovery.backupInternalDatabase(null));
+ // Set next row id in External Storage to handle rollback in the future.
backupNextRowId(NEXT_ROW_ID_DEFAULT_BILLION_VALUE);
updateSessionIdInDatabaseAndExternalStorage(db);
return;
@@ -599,7 +593,7 @@ public class DatabaseHelper extends SQLiteOpenHelper implements AutoCloseable {
// Recover data from backup
// Ensure we do not back up in case of recovery.
mIsRecovering.set(true);
- recoverData(mediaProvider, db, volumeName);
+ databaseBackupAndRecovery.recoverData(db, volumeName);
updateNextRowIdInDatabaseAndExternalStorage(db);
mIsRecovering.set(false);
updateSessionIdInDatabaseAndExternalStorage(db);
@@ -621,88 +615,6 @@ public class DatabaseHelper extends SQLiteOpenHelper implements AutoCloseable {
}
}
- @GuardedBy("sRecoveryLock")
- private void recoverData(MediaProvider mediaProvider, SQLiteDatabase db, String volumeName) {
- final long startTime = SystemClock.elapsedRealtime();
- int i = 0;
- final String fuseFilePath = getFuseFilePathFromVolumeName(volumeName);
- // Wait for external primary to be attached as we use same thread for internal volume.
- // Maximum wait for 10s
- DatabaseBackupAndRecovery dbBackupAndRecovery =
- mediaProvider.getDatabaseBackupAndRecovery();
- while (!dbBackupAndRecovery.isFuseDaemonReadyForFilePath(fuseFilePath)
- && i < 1000) {
- Log.d(TAG, "Waiting for fuse daemon to be ready.");
- // Poll after every 10ms
- SystemClock.sleep(10);
- i++;
- }
- if (!dbBackupAndRecovery.isFuseDaemonReadyForFilePath(fuseFilePath)) {
- Log.e(TAG, "Could not recover data as fuse daemon could not serve requests.");
- return;
- }
-
- long rowsRecovered = 0;
- long dirtyRowsCount = 0;
- String[] backedUpFilePaths;
- String lastReadValue = "";
-
- while (true) {
- backedUpFilePaths = mediaProvider.getDatabaseBackupAndRecovery()
- .readBackedUpFilePaths(volumeName, lastReadValue, LEVEL_DB_READ_LIMIT);
- if (backedUpFilePaths.length <= 0) {
- break;
- }
-
- for (String filePath : backedUpFilePaths) {
- Optional<BackupIdRow> fileRow = mediaProvider.getDatabaseBackupAndRecovery()
- .readDataFromBackup(volumeName, filePath);
- if (fileRow.isPresent()) {
- if (fileRow.get().getIsDirty()) {
- dirtyRowsCount++;
- continue;
- }
-
- dbBackupAndRecovery.insertDataInDatabase(db, fileRow.get(), filePath,
- volumeName);
- rowsRecovered++;
- }
- }
-
- // Read less rows than expected
- if (backedUpFilePaths.length < LEVEL_DB_READ_LIMIT) {
- break;
- }
- lastReadValue = backedUpFilePaths[backedUpFilePaths.length - 1];
- }
- long recoveryTime = SystemClock.elapsedRealtime() - startTime;
- MediaProviderStatsLog.write(MediaProviderStatsLog.MEDIA_PROVIDER_VOLUME_RECOVERY_REPORTED,
- getVolumeName(volumeName), recoveryTime, rowsRecovered, dirtyRowsCount);
- Log.i(TAG, String.format(Locale.ROOT, "%d rows recovered for volume:%s.", rowsRecovered,
- volumeName));
- Log.i(TAG, String.format(Locale.ROOT, "Recovery time: %d ms", recoveryTime));
- }
-
- private int getVolumeName(String volumeName) {
- if (volumeName.equalsIgnoreCase(MediaStore.VOLUME_INTERNAL)) {
- return MEDIA_PROVIDER_VOLUME_RECOVERY_REPORTED__VOLUME__INTERNAL;
- } else if (volumeName.equalsIgnoreCase(MediaStore.VOLUME_EXTERNAL_PRIMARY)) {
- return MEDIA_PROVIDER_VOLUME_RECOVERY_REPORTED__VOLUME__EXTERNAL_PRIMARY;
- }
-
- return MEDIA_PROVIDER_VOLUME_RECOVERY_REPORTED__VOLUME__PUBLIC;
- }
-
- private static String getFuseFilePathFromVolumeName(String volumeName) {
- switch (volumeName) {
- case MediaStore.VOLUME_INTERNAL:
- case MediaStore.VOLUME_EXTERNAL_PRIMARY:
- return "/storage/emulated/" + UserHandle.myUserId();
- default:
- return "/storage/" + volumeName;
- }
- }
-
private void tryRecoverRowIdSequence(SQLiteDatabase db) {
if (isInternal()) {
// Database row id recovery for internal is handled in tryRecoverDatabase()
diff --git a/src/com/android/providers/media/MediaProvider.java b/src/com/android/providers/media/MediaProvider.java
index 402dba1c6..ee8275260 100644
--- a/src/com/android/providers/media/MediaProvider.java
+++ b/src/com/android/providers/media/MediaProvider.java
@@ -54,9 +54,9 @@ import static com.android.providers.media.AccessChecker.getWhereForOwnerPackageM
import static com.android.providers.media.AccessChecker.getWhereForUserSelectedAccess;
import static com.android.providers.media.AccessChecker.hasAccessToCollection;
import static com.android.providers.media.AccessChecker.hasUserSelectedAccess;
+import static com.android.providers.media.DatabaseBackupAndRecovery.LEVEL_DB_READ_LIMIT;
import static com.android.providers.media.DatabaseHelper.EXTERNAL_DATABASE_NAME;
import static com.android.providers.media.DatabaseHelper.INTERNAL_DATABASE_NAME;
-import static com.android.providers.media.DatabaseHelper.LEVEL_DB_READ_LIMIT;
import static com.android.providers.media.LocalCallingIdentity.APPOP_REQUEST_INSTALL_PACKAGES_FOR_SHARED_UID;
import static com.android.providers.media.LocalCallingIdentity.PERMISSION_ACCESS_MTP;
import static com.android.providers.media.LocalCallingIdentity.PERMISSION_INSTALL_PACKAGES;