Index: chrome/android/java/src/org/chromium/chrome/browser/webapps/WebappDirectoryManager.java |
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/webapps/WebappDirectoryManager.java b/chrome/android/java/src/org/chromium/chrome/browser/webapps/WebappDirectoryManager.java |
new file mode 100644 |
index 0000000000000000000000000000000000000000..d60dd9efca515fe8b696db3541bef3faccc87207 |
--- /dev/null |
+++ b/chrome/android/java/src/org/chromium/chrome/browser/webapps/WebappDirectoryManager.java |
@@ -0,0 +1,167 @@ |
+// Copyright 2015 The Chromium Authors. All rights reserved. |
+// Use of this source code is governed by a BSD-style license that can be |
+// found in the LICENSE file. |
+ |
+package org.chromium.chrome.browser.webapps; |
+ |
+import android.annotation.TargetApi; |
+import android.app.ActivityManager; |
+import android.app.ActivityManager.AppTask; |
+import android.content.ComponentName; |
+import android.content.Context; |
+import android.content.Intent; |
+import android.net.Uri; |
+import android.os.AsyncTask; |
+import android.os.Build; |
+import android.text.TextUtils; |
+import android.util.Log; |
+ |
+import org.chromium.base.ApplicationStatus; |
+import org.chromium.chrome.browser.document.DocumentUtils; |
+ |
+import java.io.File; |
+import java.util.HashSet; |
+import java.util.concurrent.atomic.AtomicBoolean; |
+ |
+/** |
+ * Manages directories created to store data for webapps. |
+ * |
+ * Directories managed by this class are all subdirectories of the app_WebappActivity/ directory, |
+ * which each WebappActivity using a directory named either for its Webapp's ID in Document mode, |
+ * or the index of the WebappActivity if it is a subclass of the WebappManagedActivity class (which |
+ * are used in pre-L devices to allow multiple WebappActivities launching). |
+ */ |
+public class WebappDirectoryManager extends AsyncTask<Void, Void, Void> { |
+ private static final String TAG = "WebappDirectoryCleaner"; |
+ private static final String WEBAPP_DIRECTORY_NAME = "WebappActivity"; |
+ |
+ /** Whether or not the class has already started trying to clean up obsolete directories. */ |
+ private static final AtomicBoolean sMustCleanUpOldDirectories = new AtomicBoolean(true); |
+ |
+ /** Scheme used for Intents fired for WebappActivity instances. */ |
+ private final String mWebappScheme; |
+ |
+ /** Directories that will be deleted. */ |
+ private final HashSet<File> mDirectoriesToDelete; |
+ |
+ /** |
+ * Constructs a WebappDirectoryManager, which will manage the deletion of directories |
+ * corresponding to webapps that no longer need their data. |
+ * |
+ * Should be called by WebappActivities after they have restored all the data they need from |
+ * their directory. |
+ * |
+ * @param directory Directory that must be deleted. Corresponds to the current webapp. |
+ * @param webappScheme Scheme used for WebappActivities when building their Intent data URIs. |
+ * @param deleteOldDirectories Whether directories for old WebappActivities should be purged. |
+ */ |
+ public WebappDirectoryManager( |
+ final File directory, String webappScheme, boolean deleteOldDirectories) { |
+ mWebappScheme = webappScheme; |
+ |
+ mDirectoriesToDelete = new HashSet<File>(); |
+ mDirectoriesToDelete.add(directory); |
+ |
+ if (deleteOldDirectories && sMustCleanUpOldDirectories.getAndSet(false)) { |
+ assert Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP; |
+ Context context = ApplicationStatus.getApplicationContext(); |
+ cleanUpOldWebappDirectories( |
+ mDirectoriesToDelete, context.getApplicationInfo().dataDir); |
+ } |
+ } |
+ |
+ @Override |
+ protected Void doInBackground(Void... params) { |
+ for (File directory : mDirectoriesToDelete) { |
+ if (isCancelled()) return null; |
+ |
+ // Delete all the files in the directory. |
+ for (File file : directory.listFiles()) { |
+ if (!file.delete()) Log.e(TAG, "Failed to delete file: " + file.getPath()); |
+ } |
+ |
+ // Delete the directory itself. |
+ if (!directory.delete()) { |
+ Log.e(TAG, "Failed to delete directory: " + directory.getPath()); |
+ } |
+ } |
+ return null; |
+ } |
+ |
+ /** |
+ * Removes all directories using the old pre-K directory structure, which used directories named |
+ * app_WebappActivity*. Also deletes directories corresponding to WebappActivities that are no |
+ * longer listed in Android's recents, since these will be unable to restore their data, anyway. |
+ * @param directoriesToDelete Set to append directory names to. |
+ * @param baseDirectory Base directory of all of Chrome's persisted files. |
+ */ |
+ @TargetApi(Build.VERSION_CODES.LOLLIPOP) |
+ private void cleanUpOldWebappDirectories( |
+ HashSet<File> directoriesToDelete, String baseDirectory) { |
+ Context context = ApplicationStatus.getApplicationContext(); |
+ |
+ String webappDirectoryAppBaseName = |
+ context.getDir(WEBAPP_DIRECTORY_NAME, Context.MODE_PRIVATE).getName(); |
+ |
+ // Figure out what WebappActivities are still listed in Android's recents menu. |
+ HashSet<String> liveWebapps = new HashSet<String>(); |
+ ActivityManager manager = |
+ (ActivityManager) context.getSystemService(Context.ACTIVITY_SERVICE); |
+ for (AppTask task : manager.getAppTasks()) { |
+ Intent intent = DocumentUtils.getBaseIntentFromTask(task); |
+ if (intent == null) continue; |
+ |
+ Uri data = intent.getData(); |
+ if (data != null && TextUtils.equals(mWebappScheme, data.getScheme())) { |
+ liveWebapps.add(data.getHost()); |
+ } |
+ |
+ // WebappManagedActivities have titles from "WebappActivity0" through "WebappActivity9". |
+ ComponentName component = intent.getComponent(); |
+ if (component != null) { |
+ String fullClassName = component.getClassName(); |
+ int lastPeriodIndex = fullClassName.lastIndexOf("."); |
+ if (lastPeriodIndex != -1) { |
+ String className = fullClassName.substring(lastPeriodIndex + 1); |
+ if (className.startsWith(WEBAPP_DIRECTORY_NAME) |
+ && className.length() > WEBAPP_DIRECTORY_NAME.length()) { |
+ String activityIndex = className.substring(WEBAPP_DIRECTORY_NAME.length()); |
+ liveWebapps.add(activityIndex); |
+ } |
+ } |
+ } |
+ } |
+ |
+ // Delete all webapp directories in the main directory. |
+ File dataDirectory = new File(baseDirectory); |
+ for (File file : dataDirectory.listFiles()) { |
+ String filename = file.getName(); |
+ if (!filename.startsWith(webappDirectoryAppBaseName)) continue; |
+ if (filename.length() == webappDirectoryAppBaseName.length()) continue; |
+ directoriesToDelete.add(file); |
+ } |
+ |
+ // Clean out webapp directories no longer corresponding to tasks in Recents. |
+ File webappBaseDirectory = context.getDir(WEBAPP_DIRECTORY_NAME, Context.MODE_PRIVATE); |
+ if (webappBaseDirectory.exists()) { |
+ for (File file : webappBaseDirectory.listFiles()) { |
+ if (!liveWebapps.contains(file.getName())) directoriesToDelete.add(file); |
+ } |
+ } |
+ } |
+ |
+ /** |
+ * Returns the name of the directory for this webapp. |
+ * @param identifier ID for the webapp. Used as a subdirectory name. |
+ * @return File for storing information about the webapp. |
+ */ |
+ public static File getWebappDirectory(String identifier) { |
+ Context context = ApplicationStatus.getApplicationContext(); |
+ File baseDirectory = context.getDir(WEBAPP_DIRECTORY_NAME, Context.MODE_PRIVATE); |
+ File webappDirectory = new File(baseDirectory, identifier); |
+ if (!webappDirectory.exists() && !webappDirectory.mkdir()) { |
+ Log.e(TAG, "Failed to create webapp directory."); |
+ } |
+ return webappDirectory; |
+ } |
+} |