From 58326e8585ea9fbd168321b5fa3f7d7aec93c924 Mon Sep 17 00:00:00 2001 From: Eskil Abrahamsen Blomfeldt Date: Wed, 27 Nov 2013 14:38:57 +0100 Subject: Android: Overwrite plugin cache when APK is updated To allow updating APKs with new versions of Qt, we need to delete the old cache when the APK is updated. This patch does two things: 1. Move the plugins (and imports/qml) into a directory called qt-reserved-files/ to better separate the cache from the rest of the application. The first time the files are put here, we will delete the old cache in /plugins, /imports and /qml if they exist to avoid leaving old files around forever. 2. Add versioning to the cache and flush it every time the APK is reinstalled. Potentially, the libraries in the APK can change for every reinstall, so this is the safest approach. Task-number: QTBUG-35129 [ChangeLog][Android][QTBUG-35129] Update deployed plugins and imports when APK is updated on the device. Change-Id: Ie38b639db2cfba8a521acc875c4afd5e07df3efd Reviewed-by: Paul Olav Tvete --- .../qtproject/qt5/android/bindings/QtActivity.java | 126 +++++++++++++++++++-- 1 file changed, 116 insertions(+), 10 deletions(-) (limited to 'src/android') diff --git a/src/android/java/src/org/qtproject/qt5/android/bindings/QtActivity.java b/src/android/java/src/org/qtproject/qt5/android/bindings/QtActivity.java index a10e58bb87..6762717d91 100644 --- a/src/android/java/src/org/qtproject/qt5/android/bindings/QtActivity.java +++ b/src/android/java/src/org/qtproject/qt5/android/bindings/QtActivity.java @@ -32,6 +32,8 @@ import java.io.OutputStream; import java.io.InputStream; import java.io.FileOutputStream; import java.io.FileInputStream; +import java.io.DataOutputStream; +import java.io.DataInputStream; import java.lang.reflect.Method; import java.util.ArrayList; import java.util.Arrays; @@ -50,6 +52,7 @@ import android.content.ServiceConnection; import android.content.pm.ActivityInfo; import android.content.pm.PackageManager; import android.content.pm.PackageManager.NameNotFoundException; +import android.content.pm.PackageInfo; import android.content.res.Configuration; import android.content.res.Resources.Theme; import android.content.res.AssetManager; @@ -382,6 +385,9 @@ public class QtActivity extends Activity InputStream inputStream = assetsManager.open(source); OutputStream outputStream = new FileOutputStream(destinationFile); copyFile(inputStream, outputStream); + + inputStream.close(); + outputStream.close(); } private static void createBundledBinary(String source, String destination) @@ -401,13 +407,66 @@ public class QtActivity extends Activity InputStream inputStream = new FileInputStream(source); OutputStream outputStream = new FileOutputStream(destinationFile); copyFile(inputStream, outputStream); + + inputStream.close(); + outputStream.close(); + } + + private boolean cleanCacheIfNecessary(String pluginsPrefix, long packageVersion) + { + File versionFile = new File(pluginsPrefix + "cache.version"); + + long cacheVersion = 0; + if (versionFile.exists() && versionFile.canRead()) { + try { + DataInputStream inputStream = new DataInputStream(new FileInputStream(versionFile)); + cacheVersion = inputStream.readLong(); + inputStream.close(); + } catch (Exception e) { + e.printStackTrace(); + } + } + + if (cacheVersion != packageVersion) { + deleteRecursively(new File(pluginsPrefix)); + return true; + } else { + return false; + } } - private void extractBundledPluginsAndImports(String localPrefix) + private void extractBundledPluginsAndImports(String pluginsPrefix) throws IOException { ArrayList libs = new ArrayList(); + String dataDir = getApplicationInfo().dataDir + "/"; + + long packageVersion = -1; + try { + PackageInfo packageInfo = getPackageManager().getPackageInfo(getPackageName(), 0); + packageVersion = packageInfo.lastUpdateTime; + } catch (Exception e) { + e.printStackTrace(); + } + + if (!cleanCacheIfNecessary(pluginsPrefix, packageVersion)) + return; + + { + File versionFile = new File(pluginsPrefix + "cache.version"); + + File parentDirectory = versionFile.getParentFile(); + if (!parentDirectory.exists()) + parentDirectory.mkdirs(); + + versionFile.createNewFile(); + + DataOutputStream outputStream = new DataOutputStream(new FileOutputStream(versionFile)); + outputStream.writeLong(packageVersion); + outputStream.close(); + } + { String key = BUNDLED_IN_LIB_RESOURCE_ID_KEY; java.util.Set keys = m_activityInfo.metaData.keySet(); @@ -416,8 +475,8 @@ public class QtActivity extends Activity for (String bundledImportBinary : list) { String[] split = bundledImportBinary.split(":"); - String sourceFileName = localPrefix + "lib/" + split[0]; - String destinationFileName = localPrefix + split[1]; + String sourceFileName = dataDir + "lib/" + split[0]; + String destinationFileName = pluginsPrefix + split[1]; createBundledBinary(sourceFileName, destinationFileName); } } @@ -431,7 +490,7 @@ public class QtActivity extends Activity for (String fileName : list) { String[] split = fileName.split(":"); String sourceFileName = split[0]; - String destinationFileName = localPrefix + split[1]; + String destinationFileName = pluginsPrefix + split[1]; copyAsset(sourceFileName, destinationFileName); } } @@ -439,6 +498,45 @@ public class QtActivity extends Activity } } + private void deleteRecursively(File directory) + { + File[] files = directory.listFiles(); + if (files != null) { + for (File file : files) { + if (file.isDirectory()) + deleteRecursively(file); + else + file.delete(); + } + + directory.delete(); + } + } + + private void cleanOldCacheIfNecessary(String oldLocalPrefix, String localPrefix) + { + File newCache = new File(localPrefix); + if (!newCache.exists()) { + { + File oldPluginsCache = new File(oldLocalPrefix + "plugins/"); + if (oldPluginsCache.exists() && oldPluginsCache.isDirectory()) + deleteRecursively(oldPluginsCache); + } + + { + File oldImportsCache = new File(oldLocalPrefix + "imports/"); + if (oldImportsCache.exists() && oldImportsCache.isDirectory()) + deleteRecursively(oldImportsCache); + } + + { + File oldQmlCache = new File(oldLocalPrefix + "qml/"); + if (oldQmlCache.exists() && oldQmlCache.isDirectory()) + deleteRecursively(oldQmlCache); + } + } + } + private void startApp(final boolean firstStart) { try { @@ -464,11 +562,15 @@ public class QtActivity extends Activity if (m_activityInfo.metaData.containsKey("android.app.libs_prefix")) localPrefix = m_activityInfo.metaData.getString("android.app.libs_prefix"); + String pluginsPrefix = localPrefix; + boolean bundlingQtLibs = false; if (m_activityInfo.metaData.containsKey("android.app.bundle_local_qt_libs") && m_activityInfo.metaData.getInt("android.app.bundle_local_qt_libs") == 1) { localPrefix = getApplicationInfo().dataDir + "/"; - extractBundledPluginsAndImports(localPrefix); + pluginsPrefix = localPrefix + "qt-reserved-files/"; + cleanOldCacheIfNecessary(localPrefix, pluginsPrefix); + extractBundledPluginsAndImports(pluginsPrefix); bundlingQtLibs = true; } @@ -484,8 +586,12 @@ public class QtActivity extends Activity if (m_activityInfo.metaData.containsKey("android.app.load_local_libs")) { String[] extraLibs = m_activityInfo.metaData.getString("android.app.load_local_libs").split(":"); for (String lib : extraLibs) { - if (lib.length() > 0) - libraryList.add(localPrefix + lib); + if (lib.length() > 0) { + if (lib.startsWith("lib/")) + libraryList.add(localPrefix + lib); + else + libraryList.add(pluginsPrefix + lib); + } } } @@ -513,9 +619,9 @@ public class QtActivity extends Activity } loaderParams.putStringArrayList(NATIVE_LIBRARIES_KEY, libraryList); loaderParams.putString(ENVIRONMENT_VARIABLES_KEY, ENVIRONMENT_VARIABLES - + "\tQML2_IMPORT_PATH=" + localPrefix + "/qml" - + "\tQML_IMPORT_PATH=" + localPrefix + "/imports" - + "\tQT_PLUGIN_PATH=" + localPrefix + "/plugins"); + + "\tQML2_IMPORT_PATH=" + pluginsPrefix + "/qml" + + "\tQML_IMPORT_PATH=" + pluginsPrefix + "/imports" + + "\tQT_PLUGIN_PATH=" + pluginsPrefix + "/plugins"); Intent intent = getIntent(); if (intent != null) { -- cgit v1.2.3