Generate per-cache unique identifier.
PiperOrigin-RevId: 233030337
This commit is contained in:
parent
dec00997e3
commit
42e691519a
@ -22,6 +22,8 @@ import com.google.android.exoplayer2.C;
|
|||||||
import com.google.android.exoplayer2.util.Assertions;
|
import com.google.android.exoplayer2.util.Assertions;
|
||||||
import com.google.android.exoplayer2.util.Log;
|
import com.google.android.exoplayer2.util.Log;
|
||||||
import java.io.File;
|
import java.io.File;
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.security.SecureRandom;
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.HashMap;
|
import java.util.HashMap;
|
||||||
import java.util.HashSet;
|
import java.util.HashSet;
|
||||||
@ -46,6 +48,8 @@ public final class SimpleCache implements Cache {
|
|||||||
*/
|
*/
|
||||||
private static final int SUBDIRECTORY_COUNT = 10;
|
private static final int SUBDIRECTORY_COUNT = 10;
|
||||||
|
|
||||||
|
private static final String UID_FILE_SUFFIX = ".uid";
|
||||||
|
|
||||||
private static final HashSet<File> lockedCacheDirs = new HashSet<>();
|
private static final HashSet<File> lockedCacheDirs = new HashSet<>();
|
||||||
|
|
||||||
private static boolean cacheFolderLockingDisabled;
|
private static boolean cacheFolderLockingDisabled;
|
||||||
@ -411,16 +415,25 @@ public final class SimpleCache implements Cache {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
File[] files = cacheDir.listFiles();
|
||||||
|
|
||||||
|
long uid = 0;
|
||||||
|
try {
|
||||||
|
uid = loadUid(cacheDir, files);
|
||||||
|
} catch (IOException e) {
|
||||||
|
// TODO: Decide how to handle this.
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO: Pass the UID to the index, and use it.
|
||||||
contentIndex.load();
|
contentIndex.load();
|
||||||
if (fileIndex != null) {
|
if (fileIndex != null) {
|
||||||
Map<String, CacheFileMetadata> fileMetadata = fileIndex.getAll();
|
Map<String, CacheFileMetadata> fileMetadata = fileIndex.getAll();
|
||||||
loadDirectory(cacheDir, /* isRoot= */ true, fileMetadata);
|
loadDirectory(cacheDir, /* isRoot= */ true, files, fileMetadata);
|
||||||
fileIndex.removeAll(fileMetadata.keySet());
|
fileIndex.removeAll(fileMetadata.keySet());
|
||||||
} else {
|
} else {
|
||||||
loadDirectory(cacheDir, /* isRoot= */ true, /* fileMetadata= */ null);
|
loadDirectory(cacheDir, /* isRoot= */ true, files, /* fileMetadata= */ null);
|
||||||
}
|
}
|
||||||
contentIndex.removeEmpty();
|
contentIndex.removeEmpty();
|
||||||
|
|
||||||
try {
|
try {
|
||||||
contentIndex.store();
|
contentIndex.store();
|
||||||
} catch (CacheException e) {
|
} catch (CacheException e) {
|
||||||
@ -431,37 +444,40 @@ public final class SimpleCache implements Cache {
|
|||||||
/**
|
/**
|
||||||
* Loads a cache directory. If the root directory is passed, also loads any subdirectories.
|
* Loads a cache directory. If the root directory is passed, also loads any subdirectories.
|
||||||
*
|
*
|
||||||
* @param directory The directory to load.
|
* @param directory The directory.
|
||||||
* @param isRoot Whether the directory is the root directory.
|
* @param isRoot Whether the directory is the root directory.
|
||||||
|
* @param files The files belonging to the directory.
|
||||||
* @param fileMetadata A mutable map containing cache file metadata, keyed by file name. The map
|
* @param fileMetadata A mutable map containing cache file metadata, keyed by file name. The map
|
||||||
* is modified by removing entries for all loaded files. When the method call returns, the map
|
* is modified by removing entries for all loaded files. When the method call returns, the map
|
||||||
* will contain only metadata that was unused. May be null if no file metadata is available.
|
* will contain only metadata that was unused. May be null if no file metadata is available.
|
||||||
*/
|
*/
|
||||||
private void loadDirectory(
|
private void loadDirectory(
|
||||||
File directory, boolean isRoot, @Nullable Map<String, CacheFileMetadata> fileMetadata) {
|
File directory,
|
||||||
File[] files = directory.listFiles();
|
boolean isRoot,
|
||||||
if (files == null) {
|
File[] files,
|
||||||
// Not a directory.
|
@Nullable Map<String, CacheFileMetadata> fileMetadata) {
|
||||||
return;
|
if (files == null || files.length == 0) {
|
||||||
}
|
// Either (a) directory isn't really a directory (b) it's empty, or (c) listing files failed.
|
||||||
if (!isRoot && files.length == 0) {
|
if (!isRoot) {
|
||||||
// Empty non-root directory.
|
// For (a) and (b) deletion is the desired result. For (c) it will be a no-op if the
|
||||||
directory.delete();
|
// directory is non-empty, so there's no harm in trying.
|
||||||
|
directory.delete();
|
||||||
|
}
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
for (File file : files) {
|
for (File file : files) {
|
||||||
String fileName = file.getName();
|
String fileName = file.getName();
|
||||||
if (isRoot && fileName.indexOf('.') == -1) {
|
if (isRoot && fileName.indexOf('.') == -1) {
|
||||||
loadDirectory(file, /* isRoot= */ false, fileMetadata);
|
loadDirectory(file, /* isRoot= */ false, file.listFiles(), fileMetadata);
|
||||||
} else {
|
} else {
|
||||||
if (isRoot && CachedContentIndex.isIndexFile(fileName)) {
|
if (isRoot
|
||||||
// Skip the (expected) index files in the root directory.
|
&& (CachedContentIndex.isIndexFile(fileName) || fileName.endsWith(UID_FILE_SUFFIX))) {
|
||||||
|
// Skip expected UID and index files in the root directory.
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
CacheFileMetadata metadata =
|
|
||||||
fileMetadata != null ? fileMetadata.remove(file.getName()) : null;
|
|
||||||
long length = C.LENGTH_UNSET;
|
long length = C.LENGTH_UNSET;
|
||||||
long lastAccessTimestamp = C.TIME_UNSET;
|
long lastAccessTimestamp = C.TIME_UNSET;
|
||||||
|
CacheFileMetadata metadata = fileMetadata != null ? fileMetadata.remove(fileName) : null;
|
||||||
if (metadata != null) {
|
if (metadata != null) {
|
||||||
length = metadata.length;
|
length = metadata.length;
|
||||||
lastAccessTimestamp = metadata.lastAccessTimestamp;
|
lastAccessTimestamp = metadata.lastAccessTimestamp;
|
||||||
@ -549,6 +565,49 @@ public final class SimpleCache implements Cache {
|
|||||||
evictor.onSpanTouched(this, oldSpan, newSpan);
|
evictor.onSpanTouched(this, oldSpan, newSpan);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Loads the cache UID from the files belonging to the root directory, generating one if needed.
|
||||||
|
*
|
||||||
|
* @param directory The root directory.
|
||||||
|
* @param files The files belonging to the rood directory.
|
||||||
|
* @return The cache loaded UID.
|
||||||
|
* @throws IOException If there is an error loading or generating the UID.
|
||||||
|
*/
|
||||||
|
private static long loadUid(File directory, File[] files) throws IOException {
|
||||||
|
for (File file : files) {
|
||||||
|
String fileName = file.getName();
|
||||||
|
if (fileName.endsWith(UID_FILE_SUFFIX)) {
|
||||||
|
try {
|
||||||
|
return parseUid(fileName);
|
||||||
|
} catch (NumberFormatException e) {
|
||||||
|
// This should never happen, but if it does delete the malformed UID file and continue.
|
||||||
|
Log.e(TAG, "Malformed UID file: " + file);
|
||||||
|
file.delete();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return createUid(directory);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static long createUid(File directory) throws IOException {
|
||||||
|
SecureRandom random = new SecureRandom();
|
||||||
|
// Generate a non-negative UID.
|
||||||
|
long uid = random.nextLong();
|
||||||
|
uid = uid == Long.MIN_VALUE ? 0 : Math.abs(uid);
|
||||||
|
// Persist it as a file.
|
||||||
|
String hexUid = Long.toString(uid, /* radix= */ 16);
|
||||||
|
File hexUidFile = new File(directory, hexUid + UID_FILE_SUFFIX);
|
||||||
|
if (!hexUidFile.createNewFile()) {
|
||||||
|
// False means that the file already exists, so this should never happen.
|
||||||
|
throw new IOException("Failed to create UID file: " + hexUidFile);
|
||||||
|
}
|
||||||
|
return uid;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static long parseUid(String fileName) {
|
||||||
|
return Long.parseLong(fileName.substring(0, fileName.indexOf('.')), /* radix= */ 16);
|
||||||
|
}
|
||||||
|
|
||||||
private static synchronized boolean lockFolder(File cacheDir) {
|
private static synchronized boolean lockFolder(File cacheDir) {
|
||||||
if (cacheFolderLockingDisabled) {
|
if (cacheFolderLockingDisabled) {
|
||||||
return true;
|
return true;
|
||||||
|
Loading…
x
Reference in New Issue
Block a user