diff --git a/app/src/main/java/org/fdroid/fdroid/CleanCacheService.java b/app/src/main/java/org/fdroid/fdroid/CleanCacheService.java index d51719917..b912992a9 100644 --- a/app/src/main/java/org/fdroid/fdroid/CleanCacheService.java +++ b/app/src/main/java/org/fdroid/fdroid/CleanCacheService.java @@ -1,12 +1,17 @@ package org.fdroid.fdroid; +import android.annotation.TargetApi; import android.app.AlarmManager; import android.app.IntentService; import android.app.PendingIntent; import android.content.Context; import android.content.Intent; +import android.os.Build; import android.os.Process; import android.os.SystemClock; +import android.system.ErrnoException; +import android.system.Os; +import android.system.StructStat; import org.apache.commons.io.FileUtils; import org.fdroid.fdroid.installer.ApkCache; @@ -52,7 +57,7 @@ public class CleanCacheService extends IntentService { return; } Process.setThreadPriority(Process.THREAD_PRIORITY_LOWEST); - ApkCache.clearApkCache(this); + clearOldFiles(ApkCache.getApkCacheDir(getBaseContext()), Preferences.get().getKeepCacheTime()); deleteStrayIndexFiles(); 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(); + } + } + } + } } diff --git a/app/src/main/java/org/fdroid/fdroid/installer/ApkCache.java b/app/src/main/java/org/fdroid/fdroid/installer/ApkCache.java index 587c9c87d..47f6fb32e 100644 --- a/app/src/main/java/org/fdroid/fdroid/installer/ApkCache.java +++ b/app/src/main/java/org/fdroid/fdroid/installer/ApkCache.java @@ -26,7 +26,6 @@ import com.nostra13.universalimageloader.utils.StorageUtils; import org.apache.commons.io.FileUtils; import org.fdroid.fdroid.Hasher; -import org.fdroid.fdroid.Preferences; import org.fdroid.fdroid.data.Apk; 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 * 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. - * 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. */ - private static File getApkCacheDir(Context context) { + public static File getApkCacheDir(Context context) { File apkCacheDir = new File(StorageUtils.getCacheDirectory(context, true), CACHE_DIR); if (apkCacheDir.isFile()) { apkCacheDir.delete(); @@ -139,45 +133,4 @@ public class ApkCache { } 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(); - } - } - } - } } diff --git a/app/src/test/java/org/fdroid/fdroid/installer/ApkCacheTest.java b/app/src/test/java/org/fdroid/fdroid/CleanCacheServiceTest.java similarity index 88% rename from app/src/test/java/org/fdroid/fdroid/installer/ApkCacheTest.java rename to app/src/test/java/org/fdroid/fdroid/CleanCacheServiceTest.java index 7d9d26620..8494e6891 100644 --- a/app/src/test/java/org/fdroid/fdroid/installer/ApkCacheTest.java +++ b/app/src/test/java/org/fdroid/fdroid/CleanCacheServiceTest.java @@ -1,7 +1,8 @@ -package org.fdroid.fdroid.installer; +package org.fdroid.fdroid; import org.apache.commons.io.FileUtils; import org.fdroid.fdroid.BuildConfig; +import org.fdroid.fdroid.CleanCacheService; import org.junit.Test; import org.junit.runner.RunWith; import org.robolectric.RobolectricGradleTestRunner; @@ -16,7 +17,7 @@ import static org.junit.Assert.assertTrue; // TODO: Use sdk=24 when Robolectric supports this @Config(constants = BuildConfig.class, sdk = 23) @RunWith(RobolectricGradleTestRunner.class) -public class ApkCacheTest { +public class CleanCacheServiceTest { @Test public void testClearOldFiles() throws IOException, InterruptedException { @@ -45,12 +46,12 @@ public class ApkCacheTest { assertTrue(second.createNewFile()); assertTrue(second.exists()); - ApkCache.clearOldFiles(dir, 3); + CleanCacheService.clearOldFiles(dir, 3); assertFalse(first.exists()); assertTrue(second.exists()); Thread.sleep(7000); - ApkCache.clearOldFiles(dir, 3); + CleanCacheService.clearOldFiles(dir, 3); assertFalse(first.exists()); assertFalse(second.exists()); }