move clearOldFiles() to CleanCacheService to be used on other files

Now that there is the ability to remove files based on last access time, it
makes sense to use this on all cached files, including icons, etc.
This commit is contained in:
Hans-Christoph Steiner 2016-08-15 21:36:33 +02:00
parent 28dfe970da
commit fc98820c93
3 changed files with 54 additions and 54 deletions

View File

@ -1,12 +1,17 @@
package org.fdroid.fdroid; package org.fdroid.fdroid;
import android.annotation.TargetApi;
import android.app.AlarmManager; import android.app.AlarmManager;
import android.app.IntentService; import android.app.IntentService;
import android.app.PendingIntent; import android.app.PendingIntent;
import android.content.Context; import android.content.Context;
import android.content.Intent; import android.content.Intent;
import android.os.Build;
import android.os.Process; import android.os.Process;
import android.os.SystemClock; import android.os.SystemClock;
import android.system.ErrnoException;
import android.system.Os;
import android.system.StructStat;
import org.apache.commons.io.FileUtils; import org.apache.commons.io.FileUtils;
import org.fdroid.fdroid.installer.ApkCache; import org.fdroid.fdroid.installer.ApkCache;
@ -52,7 +57,7 @@ public class CleanCacheService extends IntentService {
return; return;
} }
Process.setThreadPriority(Process.THREAD_PRIORITY_LOWEST); Process.setThreadPriority(Process.THREAD_PRIORITY_LOWEST);
ApkCache.clearApkCache(this); clearOldFiles(ApkCache.getApkCacheDir(getBaseContext()), Preferences.get().getKeepCacheTime());
deleteStrayIndexFiles(); deleteStrayIndexFiles();
deleteOldInstallerFiles(); deleteOldInstallerFiles();
} }
@ -111,4 +116,45 @@ public class CleanCacheService extends IntentService {
} }
} }
} }
/**
* Recursively delete files in {@code dir} that were last used
* {@code secondsAgo} seconds ago. On {@code android-21} and newer, this
* is based on the last access of the file, on older Android versions, it is
* based on the last time the file was modified, e.g. downloaded.
*
* @param dir The directory to recurse in
* @param secondsAgo The number of seconds old that marks a file for deletion.
*/
@TargetApi(21)
public static void clearOldFiles(File dir, long secondsAgo) {
if (dir == null) {
return;
}
File[] files = dir.listFiles();
if (files == null) {
return;
}
long olderThan = System.currentTimeMillis() - (secondsAgo * 1000L);
for (File f : files) {
if (f.isDirectory()) {
clearOldFiles(f, olderThan);
f.delete();
}
if (Build.VERSION.SDK_INT < 21) {
if (FileUtils.isFileOlder(f, olderThan)) {
f.delete();
}
} else {
try {
StructStat stat = Os.lstat(f.getAbsolutePath());
if (stat.st_atime < olderThan) {
f.delete();
}
} catch (ErrnoException e) {
e.printStackTrace();
}
}
}
}
} }

View File

@ -26,7 +26,6 @@ import com.nostra13.universalimageloader.utils.StorageUtils;
import org.apache.commons.io.FileUtils; import org.apache.commons.io.FileUtils;
import org.fdroid.fdroid.Hasher; import org.fdroid.fdroid.Hasher;
import org.fdroid.fdroid.Preferences;
import org.fdroid.fdroid.data.Apk; import org.fdroid.fdroid.data.Apk;
import org.fdroid.fdroid.data.SanitizedFile; import org.fdroid.fdroid.data.SanitizedFile;
@ -117,19 +116,14 @@ public class ApkCache {
} }
} }
public static void clearApkCache(Context context) {
clearOldFiles(getApkCacheDir(context), Preferences.get().getKeepCacheTime());
}
/** /**
* This location is only for caching, do not install directly from this location * This location is only for caching, do not install directly from this location
* because if the file is on the External Storage, any other app could swap out * because if the file is on the External Storage, any other app could swap out
* the APK while the install was in process, allowing malware to install things. * the APK while the install was in process, allowing malware to install things.
* Using {@link Installer#installPackage(Uri localApkUri, Uri downloadUri, String packageName)} * Using {@link Installer#installPackage(Uri, Uri, Apk)}
* is fine since that does the right thing. * is fine since that does the right thing.
*/ */
private static File getApkCacheDir(Context context) { public static File getApkCacheDir(Context context) {
File apkCacheDir = new File(StorageUtils.getCacheDirectory(context, true), CACHE_DIR); File apkCacheDir = new File(StorageUtils.getCacheDirectory(context, true), CACHE_DIR);
if (apkCacheDir.isFile()) { if (apkCacheDir.isFile()) {
apkCacheDir.delete(); apkCacheDir.delete();
@ -139,45 +133,4 @@ public class ApkCache {
} }
return apkCacheDir; return apkCacheDir;
} }
/**
* Recursively delete files in {@code dir} that were last used
* {@code secondsAgo} seconds ago. On {@code android-21} and newer, this
* is based on the last access of the file, on older Android versions, it is
* based on the last time the file was modified, e.g. downloaded.
*
* @param dir The directory to recurse in
* @param secondsAgo The number of seconds old that marks a file for deletion.
*/
@TargetApi(21)
public static void clearOldFiles(File dir, long secondsAgo) {
if (dir == null) {
return;
}
File[] files = dir.listFiles();
if (files == null) {
return;
}
long olderThan = System.currentTimeMillis() - (secondsAgo * 1000L);
for (File f : files) {
if (f.isDirectory()) {
clearOldFiles(f, olderThan);
f.delete();
}
if (Build.VERSION.SDK_INT < 21) {
if (FileUtils.isFileOlder(f, olderThan)) {
f.delete();
}
} else {
try {
StructStat stat = Os.lstat(f.getAbsolutePath());
if (stat.st_atime < olderThan) {
f.delete();
}
} catch (ErrnoException e) {
e.printStackTrace();
}
}
}
}
} }

View File

@ -1,7 +1,8 @@
package org.fdroid.fdroid.installer; package org.fdroid.fdroid;
import org.apache.commons.io.FileUtils; import org.apache.commons.io.FileUtils;
import org.fdroid.fdroid.BuildConfig; import org.fdroid.fdroid.BuildConfig;
import org.fdroid.fdroid.CleanCacheService;
import org.junit.Test; import org.junit.Test;
import org.junit.runner.RunWith; import org.junit.runner.RunWith;
import org.robolectric.RobolectricGradleTestRunner; import org.robolectric.RobolectricGradleTestRunner;
@ -16,7 +17,7 @@ import static org.junit.Assert.assertTrue;
// TODO: Use sdk=24 when Robolectric supports this // TODO: Use sdk=24 when Robolectric supports this
@Config(constants = BuildConfig.class, sdk = 23) @Config(constants = BuildConfig.class, sdk = 23)
@RunWith(RobolectricGradleTestRunner.class) @RunWith(RobolectricGradleTestRunner.class)
public class ApkCacheTest { public class CleanCacheServiceTest {
@Test @Test
public void testClearOldFiles() throws IOException, InterruptedException { public void testClearOldFiles() throws IOException, InterruptedException {
@ -45,12 +46,12 @@ public class ApkCacheTest {
assertTrue(second.createNewFile()); assertTrue(second.createNewFile());
assertTrue(second.exists()); assertTrue(second.exists());
ApkCache.clearOldFiles(dir, 3); CleanCacheService.clearOldFiles(dir, 3);
assertFalse(first.exists()); assertFalse(first.exists());
assertTrue(second.exists()); assertTrue(second.exists());
Thread.sleep(7000); Thread.sleep(7000);
ApkCache.clearOldFiles(dir, 3); CleanCacheService.clearOldFiles(dir, 3);
assertFalse(first.exists()); assertFalse(first.exists());
assertFalse(second.exists()); assertFalse(second.exists());
} }