Support multiple instances of database features
- Use Cache UID for CacheContentIndex and CacheFileMetadataIndex. This enables SD card swapping for a single device. - I'm hopeful of finding a way to get the Cache UID to DefaultDownloadIndex so we can do the same there. PiperOrigin-RevId: 234662753
This commit is contained in:
parent
5b891a4c1f
commit
2685b8bd90
@ -31,7 +31,7 @@ import java.lang.annotation.RetentionPolicy;
|
||||
*/
|
||||
public final class VersionTable {
|
||||
|
||||
/** Returned by {@link #getVersion(SQLiteDatabase, int)} if the version is unset. */
|
||||
/** Returned by {@link #getVersion(SQLiteDatabase, int, String)} if the version is unset. */
|
||||
public static final int VERSION_UNSET = -1;
|
||||
/** Version of tables used for offline functionality. */
|
||||
public static final int FEATURE_OFFLINE = 0;
|
||||
@ -43,18 +43,26 @@ public final class VersionTable {
|
||||
private static final String TABLE_NAME = DatabaseProvider.TABLE_PREFIX + "Versions";
|
||||
|
||||
private static final String COLUMN_FEATURE = "feature";
|
||||
private static final String COLUMN_INSTANCE_UID = "instance_uid";
|
||||
private static final String COLUMN_VERSION = "version";
|
||||
|
||||
private static final String WHERE_FEATURE_EQUALS = COLUMN_FEATURE + " = ?";
|
||||
private static final String WHERE_FEATURE_AND_INSTANCE_UID_EQUALS =
|
||||
COLUMN_FEATURE + " = ? AND " + COLUMN_INSTANCE_UID + " = ?";
|
||||
|
||||
private static final String PRIMARY_KEY =
|
||||
"PRIMARY KEY (" + COLUMN_FEATURE + ", " + COLUMN_INSTANCE_UID + ")";
|
||||
private static final String SQL_CREATE_TABLE_IF_NOT_EXISTS =
|
||||
"CREATE TABLE IF NOT EXISTS "
|
||||
+ TABLE_NAME
|
||||
+ " ("
|
||||
+ COLUMN_FEATURE
|
||||
+ " INTEGER PRIMARY KEY NOT NULL,"
|
||||
+ " INTEGER NOT NULL,"
|
||||
+ COLUMN_INSTANCE_UID
|
||||
+ " TEXT NOT NULL,"
|
||||
+ COLUMN_VERSION
|
||||
+ " INTEGER NOT NULL)";
|
||||
+ " INTEGER NOT NULL,"
|
||||
+ PRIMARY_KEY
|
||||
+ ")";
|
||||
|
||||
@Documented
|
||||
@Retention(RetentionPolicy.SOURCE)
|
||||
@ -64,42 +72,51 @@ public final class VersionTable {
|
||||
private VersionTable() {}
|
||||
|
||||
/**
|
||||
* Sets the version of the specified feature.
|
||||
* Sets the version of a specified instance of a specified feature.
|
||||
*
|
||||
* @param writableDatabase The database to update.
|
||||
* @param feature The feature.
|
||||
* @param instanceUid The unique identifier of the instance of the feature.
|
||||
* @param version The version.
|
||||
*/
|
||||
public static void setVersion(
|
||||
SQLiteDatabase writableDatabase, @Feature int feature, int version) {
|
||||
SQLiteDatabase writableDatabase, @Feature int feature, String instanceUid, int version) {
|
||||
writableDatabase.execSQL(SQL_CREATE_TABLE_IF_NOT_EXISTS);
|
||||
ContentValues values = new ContentValues();
|
||||
values.put(COLUMN_FEATURE, feature);
|
||||
values.put(COLUMN_INSTANCE_UID, instanceUid);
|
||||
values.put(COLUMN_VERSION, version);
|
||||
writableDatabase.replace(TABLE_NAME, /* nullColumnHack= */ null, values);
|
||||
}
|
||||
|
||||
/**
|
||||
* Removes the version of the specified feature.
|
||||
* Removes the version of a specified instance of a feature.
|
||||
*
|
||||
* @param writableDatabase The database to update.
|
||||
* @param feature The feature.
|
||||
* @param instanceUid The unique identifier of the instance of the feature.
|
||||
*/
|
||||
public static void removeVersion(SQLiteDatabase writableDatabase, @Feature int feature) {
|
||||
public static void removeVersion(
|
||||
SQLiteDatabase writableDatabase, @Feature int feature, String instanceUid) {
|
||||
if (!tableExists(writableDatabase, TABLE_NAME)) {
|
||||
return;
|
||||
}
|
||||
writableDatabase.delete(TABLE_NAME, WHERE_FEATURE_EQUALS, featureArgument(feature));
|
||||
writableDatabase.delete(
|
||||
TABLE_NAME,
|
||||
WHERE_FEATURE_AND_INSTANCE_UID_EQUALS,
|
||||
featureAndInstanceUidArguments(feature, instanceUid));
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the version of the specified feature, or {@link #VERSION_UNSET} if no version
|
||||
* information is available.
|
||||
* Returns the version of a specified instance of a feature, or {@link #VERSION_UNSET} if no
|
||||
* version is set.
|
||||
*
|
||||
* @param database The database to query.
|
||||
* @param feature The feature.
|
||||
* @param instanceUid The unique identifier of the instance of the feature.
|
||||
* @return The version, or {@link #VERSION_UNSET} if no version is set.
|
||||
*/
|
||||
public static int getVersion(SQLiteDatabase database, @Feature int feature) {
|
||||
public static int getVersion(SQLiteDatabase database, @Feature int feature, String instanceUid) {
|
||||
if (!tableExists(database, TABLE_NAME)) {
|
||||
return VERSION_UNSET;
|
||||
}
|
||||
@ -107,8 +124,8 @@ public final class VersionTable {
|
||||
database.query(
|
||||
TABLE_NAME,
|
||||
new String[] {COLUMN_VERSION},
|
||||
WHERE_FEATURE_EQUALS,
|
||||
featureArgument(feature),
|
||||
WHERE_FEATURE_AND_INSTANCE_UID_EQUALS,
|
||||
featureAndInstanceUidArguments(feature, instanceUid),
|
||||
/* groupBy= */ null,
|
||||
/* having= */ null,
|
||||
/* orderBy= */ null)) {
|
||||
@ -128,7 +145,7 @@ public final class VersionTable {
|
||||
return count > 0;
|
||||
}
|
||||
|
||||
private static String[] featureArgument(int feature) {
|
||||
return new String[] {Integer.toString(feature)};
|
||||
private static String[] featureAndInstanceUidArguments(int feature, String instance) {
|
||||
return new String[] {Integer.toString(feature), instance};
|
||||
}
|
||||
}
|
||||
|
@ -37,6 +37,8 @@ public final class DefaultDownloadIndex implements DownloadIndex {
|
||||
@VisibleForTesting
|
||||
/* package */ static final String TABLE_NAME = DatabaseProvider.TABLE_PREFIX + "Downloads";
|
||||
|
||||
// TODO: Support multiple instances. Probably using the underlying cache UID.
|
||||
@VisibleForTesting /* package */ static final String INSTANCE_UID = "singleton";
|
||||
@VisibleForTesting /* package */ static final int TABLE_VERSION = 1;
|
||||
|
||||
private static final String COLUMN_ID = "id";
|
||||
@ -218,12 +220,14 @@ public final class DefaultDownloadIndex implements DownloadIndex {
|
||||
return;
|
||||
}
|
||||
SQLiteDatabase readableDatabase = databaseProvider.getReadableDatabase();
|
||||
int version = VersionTable.getVersion(readableDatabase, VersionTable.FEATURE_OFFLINE);
|
||||
int version =
|
||||
VersionTable.getVersion(readableDatabase, VersionTable.FEATURE_OFFLINE, INSTANCE_UID);
|
||||
if (version == VersionTable.VERSION_UNSET || version > TABLE_VERSION) {
|
||||
SQLiteDatabase writableDatabase = databaseProvider.getWritableDatabase();
|
||||
writableDatabase.beginTransaction();
|
||||
try {
|
||||
VersionTable.setVersion(writableDatabase, VersionTable.FEATURE_OFFLINE, TABLE_VERSION);
|
||||
VersionTable.setVersion(
|
||||
writableDatabase, VersionTable.FEATURE_OFFLINE, INSTANCE_UID, TABLE_VERSION);
|
||||
writableDatabase.execSQL(SQL_DROP_TABLE_IF_EXISTS);
|
||||
writableDatabase.execSQL(SQL_CREATE_TABLE);
|
||||
writableDatabase.setTransactionSuccessful();
|
||||
|
@ -20,14 +20,16 @@ import android.database.Cursor;
|
||||
import android.database.sqlite.SQLiteDatabase;
|
||||
import com.google.android.exoplayer2.database.DatabaseProvider;
|
||||
import com.google.android.exoplayer2.database.VersionTable;
|
||||
import com.google.android.exoplayer2.util.Assertions;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
import org.checkerframework.checker.nullness.qual.MonotonicNonNull;
|
||||
|
||||
/** Maintains an index of cache file metadata. */
|
||||
/* package */ final class CacheFileMetadataIndex {
|
||||
|
||||
private static final String TABLE_NAME = DatabaseProvider.TABLE_PREFIX + "CacheFileMetadata";
|
||||
private static final String TABLE_PREFIX = DatabaseProvider.TABLE_PREFIX + "CacheFileMetadata";
|
||||
private static final int TABLE_VERSION = 1;
|
||||
|
||||
private static final String COLUMN_NAME = "name";
|
||||
@ -44,12 +46,8 @@ import java.util.Set;
|
||||
new String[] {
|
||||
COLUMN_NAME, COLUMN_LENGTH, COLUMN_LAST_ACCESS_TIMESTAMP,
|
||||
};
|
||||
|
||||
private static final String SQL_DROP_TABLE_IF_EXISTS = "DROP TABLE IF EXISTS " + TABLE_NAME;
|
||||
private static final String SQL_CREATE_TABLE =
|
||||
"CREATE TABLE "
|
||||
+ TABLE_NAME
|
||||
+ " ("
|
||||
private static final String TABLE_SCHEMA =
|
||||
"("
|
||||
+ COLUMN_NAME
|
||||
+ " TEXT PRIMARY KEY NOT NULL,"
|
||||
+ COLUMN_LENGTH
|
||||
@ -59,18 +57,42 @@ import java.util.Set;
|
||||
|
||||
private final DatabaseProvider databaseProvider;
|
||||
|
||||
private boolean initialized;
|
||||
@MonotonicNonNull private String tableName;
|
||||
|
||||
public CacheFileMetadataIndex(DatabaseProvider databaseProvider) {
|
||||
this.databaseProvider = databaseProvider;
|
||||
}
|
||||
|
||||
/** Initializes the index for the given cache UID. */
|
||||
public void initialize(long uid) {
|
||||
String hexUid = Long.toHexString(uid);
|
||||
tableName = TABLE_PREFIX + hexUid;
|
||||
SQLiteDatabase readableDatabase = databaseProvider.getReadableDatabase();
|
||||
int version =
|
||||
VersionTable.getVersion(readableDatabase, VersionTable.FEATURE_CACHE_FILE_METADATA, hexUid);
|
||||
if (version == VersionTable.VERSION_UNSET || version > TABLE_VERSION) {
|
||||
SQLiteDatabase writableDatabase = databaseProvider.getWritableDatabase();
|
||||
writableDatabase.beginTransaction();
|
||||
try {
|
||||
VersionTable.setVersion(
|
||||
writableDatabase, VersionTable.FEATURE_CACHE_FILE_METADATA, hexUid, TABLE_VERSION);
|
||||
writableDatabase.execSQL("DROP TABLE IF EXISTS " + tableName);
|
||||
writableDatabase.execSQL("CREATE TABLE " + tableName + " " + TABLE_SCHEMA);
|
||||
writableDatabase.setTransactionSuccessful();
|
||||
} finally {
|
||||
writableDatabase.endTransaction();
|
||||
}
|
||||
} else if (version < TABLE_VERSION) {
|
||||
// There is no previous version currently.
|
||||
throw new IllegalStateException();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns all file metadata keyed by file name. The returned map is mutable and may be modified
|
||||
* by the caller.
|
||||
*/
|
||||
public Map<String, CacheFileMetadata> getAll() {
|
||||
ensureInitialized();
|
||||
try (Cursor cursor = getCursor()) {
|
||||
Map<String, CacheFileMetadata> fileMetadata = new HashMap<>(cursor.getCount());
|
||||
while (cursor.moveToNext()) {
|
||||
@ -91,13 +113,13 @@ import java.util.Set;
|
||||
* @param lastAccessTimestamp The file last access timestamp.
|
||||
*/
|
||||
public void set(String name, long length, long lastAccessTimestamp) {
|
||||
ensureInitialized();
|
||||
Assertions.checkNotNull(tableName);
|
||||
SQLiteDatabase writableDatabase = databaseProvider.getWritableDatabase();
|
||||
ContentValues values = new ContentValues();
|
||||
values.put(COLUMN_NAME, name);
|
||||
values.put(COLUMN_LENGTH, length);
|
||||
values.put(COLUMN_LAST_ACCESS_TIMESTAMP, lastAccessTimestamp);
|
||||
writableDatabase.replace(TABLE_NAME, /* nullColumnHack= */ null, values);
|
||||
writableDatabase.replace(tableName, /* nullColumnHack= */ null, values);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -106,9 +128,9 @@ import java.util.Set;
|
||||
* @param name The name of the file whose metadata is to be removed.
|
||||
*/
|
||||
public void remove(String name) {
|
||||
ensureInitialized();
|
||||
Assertions.checkNotNull(tableName);
|
||||
SQLiteDatabase writableDatabase = databaseProvider.getWritableDatabase();
|
||||
writableDatabase.delete(TABLE_NAME, WHERE_NAME_EQUALS, new String[] {name});
|
||||
writableDatabase.delete(tableName, WHERE_NAME_EQUALS, new String[] {name});
|
||||
}
|
||||
|
||||
/**
|
||||
@ -117,12 +139,12 @@ import java.util.Set;
|
||||
* @param names The names of the files whose metadata is to be removed.
|
||||
*/
|
||||
public void removeAll(Set<String> names) {
|
||||
ensureInitialized();
|
||||
Assertions.checkNotNull(tableName);
|
||||
SQLiteDatabase writableDatabase = databaseProvider.getWritableDatabase();
|
||||
writableDatabase.beginTransaction();
|
||||
try {
|
||||
for (String name : names) {
|
||||
writableDatabase.delete(TABLE_NAME, WHERE_NAME_EQUALS, new String[] {name});
|
||||
writableDatabase.delete(tableName, WHERE_NAME_EQUALS, new String[] {name});
|
||||
}
|
||||
writableDatabase.setTransactionSuccessful();
|
||||
} finally {
|
||||
@ -130,37 +152,12 @@ import java.util.Set;
|
||||
}
|
||||
}
|
||||
|
||||
private void ensureInitialized() {
|
||||
if (initialized) {
|
||||
return;
|
||||
}
|
||||
SQLiteDatabase readableDatabase = databaseProvider.getReadableDatabase();
|
||||
int version =
|
||||
VersionTable.getVersion(readableDatabase, VersionTable.FEATURE_CACHE_FILE_METADATA);
|
||||
if (version == VersionTable.VERSION_UNSET || version > TABLE_VERSION) {
|
||||
SQLiteDatabase writableDatabase = databaseProvider.getWritableDatabase();
|
||||
writableDatabase.beginTransaction();
|
||||
try {
|
||||
VersionTable.setVersion(
|
||||
writableDatabase, VersionTable.FEATURE_CACHE_FILE_METADATA, TABLE_VERSION);
|
||||
writableDatabase.execSQL(SQL_DROP_TABLE_IF_EXISTS);
|
||||
writableDatabase.execSQL(SQL_CREATE_TABLE);
|
||||
writableDatabase.setTransactionSuccessful();
|
||||
} finally {
|
||||
writableDatabase.endTransaction();
|
||||
}
|
||||
} else if (version < TABLE_VERSION) {
|
||||
// There is no previous version currently.
|
||||
throw new IllegalStateException();
|
||||
}
|
||||
initialized = true;
|
||||
}
|
||||
|
||||
private Cursor getCursor() {
|
||||
Assertions.checkNotNull(tableName);
|
||||
return databaseProvider
|
||||
.getReadableDatabase()
|
||||
.query(
|
||||
TABLE_NAME,
|
||||
tableName,
|
||||
COLUMNS,
|
||||
/* selection */ null,
|
||||
/* selectionArgs= */ null,
|
||||
|
@ -159,8 +159,16 @@ import org.checkerframework.checker.nullness.compatqual.NullableType;
|
||||
}
|
||||
}
|
||||
|
||||
/** Loads the index file. */
|
||||
public void load() {
|
||||
/**
|
||||
* Loads the index file for the given cache UID.
|
||||
*
|
||||
* @param uid The UID of the cache whose index is to be loaded.
|
||||
*/
|
||||
public void initialize(long uid) {
|
||||
storage.initialize(uid);
|
||||
if (previousStorage != null) {
|
||||
previousStorage.initialize(uid);
|
||||
}
|
||||
if (!storage.exists() && previousStorage != null && previousStorage.exists()) {
|
||||
// Copy from previous storage into current storage.
|
||||
loadFrom(previousStorage);
|
||||
@ -383,6 +391,9 @@ import org.checkerframework.checker.nullness.compatqual.NullableType;
|
||||
/** Interface for the persistent index. */
|
||||
private interface Storage {
|
||||
|
||||
/** Initializes the storage for the given cache UID. */
|
||||
void initialize(long uid);
|
||||
|
||||
/** Returns whether the persisted index exists. */
|
||||
boolean exists();
|
||||
|
||||
@ -409,9 +420,9 @@ import org.checkerframework.checker.nullness.compatqual.NullableType;
|
||||
void storeFully(HashMap<String, CachedContent> content) throws CacheException;
|
||||
|
||||
/**
|
||||
* Ensures incremental changes to the index since the last {@link #load()} or {@link
|
||||
* #storeFully(HashMap)} are persisted. The storage will have been notified of all such changes
|
||||
* via {@link #onUpdate(CachedContent)} and {@link #onRemove(CachedContent)}.
|
||||
* Ensures incremental changes to the index since the initial {@link #initialize(long)} or last
|
||||
* {@link #storeFully(HashMap)} are persisted. The storage will have been notified of all such
|
||||
* changes via {@link #onUpdate(CachedContent)} and {@link #onRemove(CachedContent)}.
|
||||
*
|
||||
* @param content The key to content map to persist.
|
||||
* @throws CacheException If an error occurs persisting the index.
|
||||
@ -457,7 +468,7 @@ import org.checkerframework.checker.nullness.compatqual.NullableType;
|
||||
throw new IllegalStateException(e); // Should never happen.
|
||||
}
|
||||
} else {
|
||||
Assertions.checkState(!encrypt);
|
||||
Assertions.checkArgument(!encrypt);
|
||||
}
|
||||
this.encrypt = encrypt;
|
||||
this.cipher = cipher;
|
||||
@ -466,6 +477,11 @@ import org.checkerframework.checker.nullness.compatqual.NullableType;
|
||||
atomicFile = new AtomicFile(file);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void initialize(long uid) {
|
||||
// Do nothing. Legacy storage uses a separate file for each cache.
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean exists() {
|
||||
return atomicFile.exists();
|
||||
@ -665,7 +681,7 @@ import org.checkerframework.checker.nullness.compatqual.NullableType;
|
||||
/** {@link Storage} implementation that uses an SQL database. */
|
||||
private static final class DatabaseStorage implements Storage {
|
||||
|
||||
private static final String TABLE_NAME = DatabaseProvider.TABLE_PREFIX + "CacheContentMetadata";
|
||||
private static final String TABLE_PREFIX = DatabaseProvider.TABLE_PREFIX + "CacheIndex";
|
||||
private static final int TABLE_VERSION = 1;
|
||||
|
||||
private static final String COLUMN_ID = "id";
|
||||
@ -679,12 +695,8 @@ import org.checkerframework.checker.nullness.compatqual.NullableType;
|
||||
private static final String WHERE_ID_EQUALS = COLUMN_ID + " = ?";
|
||||
|
||||
private static final String[] COLUMNS = new String[] {COLUMN_ID, COLUMN_KEY, COLUMN_METADATA};
|
||||
|
||||
private static final String SQL_DROP_TABLE_IF_EXISTS = "DROP TABLE IF EXISTS " + TABLE_NAME;
|
||||
private static final String SQL_CREATE_TABLE =
|
||||
"CREATE TABLE "
|
||||
+ TABLE_NAME
|
||||
+ " ("
|
||||
private static final String TABLE_SCHEMA =
|
||||
"("
|
||||
+ COLUMN_ID
|
||||
+ " INTEGER PRIMARY KEY NOT NULL,"
|
||||
+ COLUMN_KEY
|
||||
@ -695,15 +707,26 @@ import org.checkerframework.checker.nullness.compatqual.NullableType;
|
||||
private final DatabaseProvider databaseProvider;
|
||||
private final SparseArray<CachedContent> pendingUpdates;
|
||||
|
||||
private String hexUid;
|
||||
private String tableName;
|
||||
|
||||
public DatabaseStorage(DatabaseProvider databaseProvider) {
|
||||
this.databaseProvider = databaseProvider;
|
||||
pendingUpdates = new SparseArray<>();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void initialize(long uid) {
|
||||
hexUid = Long.toHexString(uid);
|
||||
tableName = TABLE_PREFIX + hexUid;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean exists() {
|
||||
return VersionTable.getVersion(
|
||||
databaseProvider.getReadableDatabase(), VersionTable.FEATURE_CACHE_CONTENT_METADATA)
|
||||
databaseProvider.getReadableDatabase(),
|
||||
VersionTable.FEATURE_CACHE_CONTENT_METADATA,
|
||||
hexUid)
|
||||
!= VersionTable.VERSION_UNSET;
|
||||
}
|
||||
|
||||
@ -712,8 +735,9 @@ import org.checkerframework.checker.nullness.compatqual.NullableType;
|
||||
SQLiteDatabase writableDatabase = databaseProvider.getWritableDatabase();
|
||||
writableDatabase.beginTransaction();
|
||||
try {
|
||||
VersionTable.removeVersion(writableDatabase, VersionTable.FEATURE_CACHE_CONTENT_METADATA);
|
||||
writableDatabase.execSQL(SQL_DROP_TABLE_IF_EXISTS);
|
||||
VersionTable.removeVersion(
|
||||
writableDatabase, VersionTable.FEATURE_CACHE_CONTENT_METADATA, hexUid);
|
||||
dropTable(writableDatabase);
|
||||
writableDatabase.setTransactionSuccessful();
|
||||
} finally {
|
||||
writableDatabase.endTransaction();
|
||||
@ -728,7 +752,8 @@ import org.checkerframework.checker.nullness.compatqual.NullableType;
|
||||
int version =
|
||||
VersionTable.getVersion(
|
||||
databaseProvider.getReadableDatabase(),
|
||||
VersionTable.FEATURE_CACHE_CONTENT_METADATA);
|
||||
VersionTable.FEATURE_CACHE_CONTENT_METADATA,
|
||||
hexUid);
|
||||
if (version == VersionTable.VERSION_UNSET || version > TABLE_VERSION) {
|
||||
SQLiteDatabase writableDatabase = databaseProvider.getWritableDatabase();
|
||||
writableDatabase.beginTransaction();
|
||||
@ -821,7 +846,7 @@ import org.checkerframework.checker.nullness.compatqual.NullableType;
|
||||
return databaseProvider
|
||||
.getReadableDatabase()
|
||||
.query(
|
||||
TABLE_NAME,
|
||||
tableName,
|
||||
COLUMNS,
|
||||
/* selection= */ null,
|
||||
/* selectionArgs= */ null,
|
||||
@ -832,13 +857,17 @@ import org.checkerframework.checker.nullness.compatqual.NullableType;
|
||||
|
||||
private void initializeTable(SQLiteDatabase writableDatabase) {
|
||||
VersionTable.setVersion(
|
||||
writableDatabase, VersionTable.FEATURE_CACHE_CONTENT_METADATA, TABLE_VERSION);
|
||||
writableDatabase.execSQL(SQL_DROP_TABLE_IF_EXISTS);
|
||||
writableDatabase.execSQL(SQL_CREATE_TABLE);
|
||||
writableDatabase, VersionTable.FEATURE_CACHE_CONTENT_METADATA, hexUid, TABLE_VERSION);
|
||||
dropTable(writableDatabase);
|
||||
writableDatabase.execSQL("CREATE TABLE " + tableName + " " + TABLE_SCHEMA);
|
||||
}
|
||||
|
||||
private void dropTable(SQLiteDatabase writableDatabase) {
|
||||
writableDatabase.execSQL("DROP TABLE IF EXISTS " + tableName);
|
||||
}
|
||||
|
||||
private void deleteRow(SQLiteDatabase writableDatabase, int key) {
|
||||
writableDatabase.delete(TABLE_NAME, WHERE_ID_EQUALS, new String[] {Integer.toString(key)});
|
||||
writableDatabase.delete(tableName, WHERE_ID_EQUALS, new String[] {Integer.toString(key)});
|
||||
}
|
||||
|
||||
private void addOrUpdateRow(SQLiteDatabase writableDatabase, CachedContent cachedContent)
|
||||
@ -851,7 +880,7 @@ import org.checkerframework.checker.nullness.compatqual.NullableType;
|
||||
values.put(COLUMN_ID, cachedContent.id);
|
||||
values.put(COLUMN_KEY, cachedContent.key);
|
||||
values.put(COLUMN_METADATA, data);
|
||||
writableDatabase.replace(TABLE_NAME, /* nullColumnHack= */ null, values);
|
||||
writableDatabase.replace(tableName, /* nullColumnHack= */ null, values);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -254,8 +254,8 @@ public final class SimpleCache implements Cache {
|
||||
}
|
||||
|
||||
@Override
|
||||
public synchronized @Nullable SimpleCacheSpan startReadWriteNonBlocking(String key, long position)
|
||||
throws CacheException {
|
||||
public synchronized @Nullable SimpleCacheSpan startReadWriteNonBlocking(
|
||||
String key, long position) {
|
||||
Assertions.checkState(!released);
|
||||
SimpleCacheSpan span = getSpan(key, position);
|
||||
|
||||
@ -290,7 +290,7 @@ public final class SimpleCache implements Cache {
|
||||
}
|
||||
|
||||
@Override
|
||||
public synchronized File startFile(String key, long position, long length) throws CacheException {
|
||||
public synchronized File startFile(String key, long position, long length) {
|
||||
Assertions.checkState(!released);
|
||||
CachedContent cachedContent = contentIndex.get(key);
|
||||
Assertions.checkNotNull(cachedContent);
|
||||
@ -399,7 +399,7 @@ public final class SimpleCache implements Cache {
|
||||
* @param position The position of the span being requested.
|
||||
* @return The corresponding cache {@link SimpleCacheSpan}.
|
||||
*/
|
||||
private SimpleCacheSpan getSpan(String key, long position) throws CacheException {
|
||||
private SimpleCacheSpan getSpan(String key, long position) {
|
||||
CachedContent cachedContent = contentIndex.get(key);
|
||||
if (cachedContent == null) {
|
||||
return SimpleCacheSpan.createOpenHole(key, position);
|
||||
@ -432,9 +432,9 @@ public final class SimpleCache implements Cache {
|
||||
// TODO: Decide how to handle this.
|
||||
}
|
||||
|
||||
// TODO: Pass the UID to the index, and use it.
|
||||
contentIndex.load();
|
||||
contentIndex.initialize(uid);
|
||||
if (fileIndex != null) {
|
||||
fileIndex.initialize(uid);
|
||||
Map<String, CacheFileMetadata> fileMetadata = fileIndex.getAll();
|
||||
loadDirectory(cacheDir, /* isRoot= */ true, files, fileMetadata);
|
||||
fileIndex.removeAll(fileMetadata.keySet());
|
||||
|
@ -15,77 +15,86 @@
|
||||
*/
|
||||
package com.google.android.exoplayer2.database;
|
||||
|
||||
import static com.google.android.exoplayer2.database.VersionTable.FEATURE_CACHE_CONTENT_METADATA;
|
||||
import static com.google.android.exoplayer2.database.VersionTable.FEATURE_OFFLINE;
|
||||
import static com.google.common.truth.Truth.assertThat;
|
||||
|
||||
import android.database.sqlite.SQLiteDatabase;
|
||||
import org.junit.After;
|
||||
import com.google.android.exoplayer2.testutil.TestUtil;
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
import org.robolectric.RobolectricTestRunner;
|
||||
import org.robolectric.RuntimeEnvironment;
|
||||
|
||||
/** Unit tests for {@link VersionTable}. */
|
||||
@RunWith(RobolectricTestRunner.class)
|
||||
public class VersionTableTest {
|
||||
|
||||
private ExoDatabaseProvider databaseProvider;
|
||||
private SQLiteDatabase readableDatabase;
|
||||
private SQLiteDatabase writableDatabase;
|
||||
private static final int FEATURE_1 = 1;
|
||||
private static final int FEATURE_2 = 2;
|
||||
private static final String INSTANCE_1 = "1";
|
||||
private static final String INSTANCE_2 = "2";
|
||||
|
||||
private DatabaseProvider databaseProvider;
|
||||
private SQLiteDatabase database;
|
||||
|
||||
@Before
|
||||
public void setUp() {
|
||||
databaseProvider = new ExoDatabaseProvider(RuntimeEnvironment.application);
|
||||
readableDatabase = databaseProvider.getReadableDatabase();
|
||||
writableDatabase = databaseProvider.getWritableDatabase();
|
||||
}
|
||||
|
||||
@After
|
||||
public void tearDown() {
|
||||
databaseProvider.close();
|
||||
databaseProvider = TestUtil.getTestDatabaseProvider();
|
||||
database = databaseProvider.getWritableDatabase();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void getVersion_nonExistingTable_returnsVersionUnset() {
|
||||
int version = VersionTable.getVersion(readableDatabase, FEATURE_OFFLINE);
|
||||
public void getVersion_unsetFeature_returnsVersionUnset() {
|
||||
int version = VersionTable.getVersion(database, FEATURE_1, INSTANCE_1);
|
||||
assertThat(version).isEqualTo(VersionTable.VERSION_UNSET);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void getVersion_unsetVersion_returnsVersionUnset() {
|
||||
VersionTable.setVersion(database, FEATURE_1, INSTANCE_1, 1);
|
||||
int version = VersionTable.getVersion(database, FEATURE_1, INSTANCE_2);
|
||||
assertThat(version).isEqualTo(VersionTable.VERSION_UNSET);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void getVersion_returnsSetVersion() {
|
||||
VersionTable.setVersion(writableDatabase, FEATURE_OFFLINE, 1);
|
||||
assertThat(VersionTable.getVersion(readableDatabase, FEATURE_OFFLINE)).isEqualTo(1);
|
||||
VersionTable.setVersion(database, FEATURE_1, INSTANCE_1, 1);
|
||||
assertThat(VersionTable.getVersion(database, FEATURE_1, INSTANCE_1)).isEqualTo(1);
|
||||
|
||||
VersionTable.setVersion(writableDatabase, FEATURE_OFFLINE, 10);
|
||||
assertThat(VersionTable.getVersion(readableDatabase, FEATURE_OFFLINE)).isEqualTo(10);
|
||||
VersionTable.setVersion(database, FEATURE_1, INSTANCE_1, 2);
|
||||
assertThat(VersionTable.getVersion(database, FEATURE_1, INSTANCE_1)).isEqualTo(2);
|
||||
|
||||
VersionTable.setVersion(writableDatabase, FEATURE_CACHE_CONTENT_METADATA, 5);
|
||||
assertThat(VersionTable.getVersion(readableDatabase, FEATURE_CACHE_CONTENT_METADATA))
|
||||
.isEqualTo(5);
|
||||
assertThat(VersionTable.getVersion(readableDatabase, FEATURE_OFFLINE)).isEqualTo(10);
|
||||
VersionTable.setVersion(database, FEATURE_2, INSTANCE_1, 3);
|
||||
assertThat(VersionTable.getVersion(database, FEATURE_2, INSTANCE_1)).isEqualTo(3);
|
||||
assertThat(VersionTable.getVersion(database, FEATURE_1, INSTANCE_1)).isEqualTo(2);
|
||||
|
||||
VersionTable.setVersion(database, FEATURE_2, INSTANCE_2, 4);
|
||||
assertThat(VersionTable.getVersion(database, FEATURE_2, INSTANCE_2)).isEqualTo(4);
|
||||
assertThat(VersionTable.getVersion(database, FEATURE_2, INSTANCE_1)).isEqualTo(3);
|
||||
assertThat(VersionTable.getVersion(database, FEATURE_1, INSTANCE_1)).isEqualTo(2);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void removeVersion_removesSetVersion() {
|
||||
VersionTable.setVersion(writableDatabase, FEATURE_OFFLINE, 1);
|
||||
assertThat(VersionTable.getVersion(readableDatabase, FEATURE_OFFLINE)).isEqualTo(1);
|
||||
VersionTable.setVersion(database, FEATURE_1, INSTANCE_1, 1);
|
||||
VersionTable.setVersion(database, FEATURE_1, INSTANCE_2, 2);
|
||||
assertThat(VersionTable.getVersion(database, FEATURE_1, INSTANCE_1)).isEqualTo(1);
|
||||
assertThat(VersionTable.getVersion(database, FEATURE_1, INSTANCE_2)).isEqualTo(2);
|
||||
|
||||
VersionTable.removeVersion(writableDatabase, FEATURE_OFFLINE);
|
||||
assertThat(VersionTable.getVersion(readableDatabase, FEATURE_OFFLINE))
|
||||
VersionTable.removeVersion(database, FEATURE_1, INSTANCE_1);
|
||||
assertThat(VersionTable.getVersion(database, FEATURE_1, INSTANCE_1))
|
||||
.isEqualTo(VersionTable.VERSION_UNSET);
|
||||
assertThat(VersionTable.getVersion(database, FEATURE_1, INSTANCE_2)).isEqualTo(2);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void doesTableExist_nonExistingTable_returnsFalse() {
|
||||
assertThat(VersionTable.tableExists(readableDatabase, "NonExistingTable")).isFalse();
|
||||
assertThat(VersionTable.tableExists(database, "NonExistingTable")).isFalse();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void doesTableExist_existingTable_returnsTrue() {
|
||||
String table = "TestTable";
|
||||
databaseProvider.getWritableDatabase().execSQL("CREATE TABLE " + table + " (dummy INTEGER)");
|
||||
assertThat(VersionTable.tableExists(readableDatabase, table)).isTrue();
|
||||
assertThat(VersionTable.tableExists(database, table)).isTrue();
|
||||
}
|
||||
}
|
||||
|
@ -15,6 +15,7 @@
|
||||
*/
|
||||
package com.google.android.exoplayer2.offline;
|
||||
|
||||
import static com.google.android.exoplayer2.offline.DefaultDownloadIndex.INSTANCE_UID;
|
||||
import static com.google.common.truth.Truth.assertThat;
|
||||
|
||||
import android.database.sqlite.SQLiteDatabase;
|
||||
@ -180,12 +181,14 @@ public class DefaultDownloadIndexTest {
|
||||
@Test
|
||||
public void putDownloadState_setsVersion() {
|
||||
SQLiteDatabase readableDatabase = databaseProvider.getReadableDatabase();
|
||||
assertThat(VersionTable.getVersion(readableDatabase, VersionTable.FEATURE_OFFLINE))
|
||||
assertThat(
|
||||
VersionTable.getVersion(readableDatabase, VersionTable.FEATURE_OFFLINE, INSTANCE_UID))
|
||||
.isEqualTo(VersionTable.VERSION_UNSET);
|
||||
|
||||
downloadIndex.putDownloadState(new DownloadStateBuilder("id1").build());
|
||||
|
||||
assertThat(VersionTable.getVersion(readableDatabase, VersionTable.FEATURE_OFFLINE))
|
||||
assertThat(
|
||||
VersionTable.getVersion(readableDatabase, VersionTable.FEATURE_OFFLINE, INSTANCE_UID))
|
||||
.isEqualTo(DefaultDownloadIndex.TABLE_VERSION);
|
||||
}
|
||||
|
||||
@ -198,14 +201,16 @@ public class DefaultDownloadIndexTest {
|
||||
cursor.close();
|
||||
|
||||
SQLiteDatabase writableDatabase = databaseProvider.getWritableDatabase();
|
||||
VersionTable.setVersion(writableDatabase, VersionTable.FEATURE_OFFLINE, Integer.MAX_VALUE);
|
||||
VersionTable.setVersion(
|
||||
writableDatabase, VersionTable.FEATURE_OFFLINE, INSTANCE_UID, Integer.MAX_VALUE);
|
||||
|
||||
downloadIndex = new DefaultDownloadIndex(databaseProvider);
|
||||
|
||||
cursor = downloadIndex.getDownloadStates();
|
||||
assertThat(cursor.getCount()).isEqualTo(0);
|
||||
cursor.close();
|
||||
assertThat(VersionTable.getVersion(writableDatabase, VersionTable.FEATURE_OFFLINE))
|
||||
assertThat(
|
||||
VersionTable.getVersion(writableDatabase, VersionTable.FEATURE_OFFLINE, INSTANCE_UID))
|
||||
.isEqualTo(DefaultDownloadIndex.TABLE_VERSION);
|
||||
}
|
||||
}
|
||||
|
@ -160,7 +160,7 @@ public class CachedContentIndexTest {
|
||||
fos.write(testIndexV1File);
|
||||
fos.close();
|
||||
|
||||
index.load();
|
||||
index.initialize(/* uid= */ 0);
|
||||
assertThat(index.getAll()).hasSize(2);
|
||||
|
||||
assertThat(index.assignIdForKey("ABCDE")).isEqualTo(5);
|
||||
@ -181,7 +181,7 @@ public class CachedContentIndexTest {
|
||||
fos.write(testIndexV2File);
|
||||
fos.close();
|
||||
|
||||
index.load();
|
||||
index.initialize(/* uid= */ 0);
|
||||
assertThat(index.getAll()).hasSize(2);
|
||||
|
||||
assertThat(index.assignIdForKey("ABCDE")).isEqualTo(5);
|
||||
@ -325,7 +325,7 @@ public class CachedContentIndexTest {
|
||||
index.getOrAdd("ABCDE").applyMetadataMutations(mutations2);
|
||||
index.store();
|
||||
|
||||
index2.load();
|
||||
index2.initialize(/* uid= */ 0);
|
||||
Set<String> keys = index.getKeys();
|
||||
Set<String> keys2 = index2.getKeys();
|
||||
assertThat(keys2).isEqualTo(keys);
|
||||
|
Loading…
x
Reference in New Issue
Block a user