diff options
Diffstat (limited to 'src/android/java/src/org/qtproject/qt5/android/bindings/QtLoader.java')
-rw-r--r-- | src/android/java/src/org/qtproject/qt5/android/bindings/QtLoader.java | 177 |
1 files changed, 135 insertions, 42 deletions
diff --git a/src/android/java/src/org/qtproject/qt5/android/bindings/QtLoader.java b/src/android/java/src/org/qtproject/qt5/android/bindings/QtLoader.java index 9d5578ed6d..45941e8ed8 100644 --- a/src/android/java/src/org/qtproject/qt5/android/bindings/QtLoader.java +++ b/src/android/java/src/org/qtproject/qt5/android/bindings/QtLoader.java @@ -1,5 +1,5 @@ /* - Copyright (c) 2016, BogDan Vatra <bogdan@kde.org> + Copyright (c) 2019, BogDan Vatra <bogdan@kde.org> Contact: http://www.qt.io/licensing/ Commercial License Usage @@ -63,9 +63,12 @@ import java.io.FileOutputStream; import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; +import java.lang.reflect.Array; import java.lang.reflect.Method; import java.util.ArrayList; import java.util.Arrays; +import java.util.HashMap; +import java.util.List; import dalvik.system.DexClassLoader; @@ -85,7 +88,6 @@ public abstract class QtLoader { public static final String ENVIRONMENT_VARIABLES_KEY = "environment.variables"; public static final String APPLICATION_PARAMETERS_KEY = "application.parameters"; public static final String BUNDLED_LIBRARIES_KEY = "bundled.libraries"; - public static final String QT_LIBS_RESOURCE_ID_KEY = "android.app.qt_libs_resource_id"; public static final String BUNDLED_IN_LIB_RESOURCE_ID_KEY = "android.app.bundled_in_lib_resource_id"; public static final String BUNDLED_IN_ASSETS_RESOURCE_ID_KEY = "android.app.bundled_in_assets_resource_id"; public static final String MAIN_LIBRARY_KEY = "main.library"; @@ -153,12 +155,15 @@ public abstract class QtLoader { // this repository is used to push a new release, and should be used to test your application. // * unstable - unstable repository, DO NOT use this repository in production, // this repository is used to push Qt snapshots. - public String[] m_qtLibs = null; // required qt libs + public ArrayList<String> m_qtLibs = null; // required qt libs public int m_displayDensity = -1; private ContextWrapper m_context; protected ComponentInfo m_contextInfo; private Class<?> m_delegateClass; + private static ArrayList<FileOutputStream> m_fileOutputStreams = new ArrayList<FileOutputStream>(); + // List of open file streams associated with files copied during installation. + QtLoader(ContextWrapper context, Class<?> clazz) { m_context = context; m_delegateClass = clazz; @@ -188,6 +193,36 @@ public abstract class QtLoader { } // Implement in subclass + private final List<String> supportedAbis = Arrays.asList(Build.SUPPORTED_ABIS); + private String preferredAbi = null; + + private ArrayList<String> prefferedAbiLibs(String []libs) + { + HashMap<String, ArrayList<String>> abisLibs = new HashMap<>(); + for (String lib : libs) { + String[] archLib = lib.split(";", 2); + if (preferredAbi != null && !archLib[0].equals(preferredAbi)) + continue; + if (!abisLibs.containsKey(archLib[0])) + abisLibs.put(archLib[0], new ArrayList<String>()); + abisLibs.get(archLib[0]).add(archLib[1]); + } + + if (preferredAbi != null) { + if (abisLibs.containsKey(preferredAbi)) { + return abisLibs.get(preferredAbi); + } + return new ArrayList<String>(); + } + + for (String abi: supportedAbis) { + if (abisLibs.containsKey(abi)) { + preferredAbi = abi; + return abisLibs.get(abi); + } + } + return new ArrayList<String>(); + } // this function is used to load and start the loader private void loadApplication(Bundle loaderParams) @@ -215,12 +250,14 @@ public abstract class QtLoader { // add all bundled Qt libs to loader params ArrayList<String> libs = new ArrayList<String>(); - if (m_contextInfo.metaData.containsKey("android.app.bundled_libs_resource_id")) - libs.addAll(Arrays.asList(m_context.getResources().getStringArray(m_contextInfo.metaData.getInt("android.app.bundled_libs_resource_id")))); + if (m_contextInfo.metaData.containsKey("android.app.bundled_libs_resource_id")) { + int resourceId = m_contextInfo.metaData.getInt("android.app.bundled_libs_resource_id"); + libs.addAll(prefferedAbiLibs(m_context.getResources().getStringArray(resourceId))); + } String libName = null; if (m_contextInfo.metaData.containsKey("android.app.lib_name")) { - libName = m_contextInfo.metaData.getString("android.app.lib_name"); + libName = m_contextInfo.metaData.getString("android.app.lib_name") + "_" + preferredAbi; loaderParams.putString(MAIN_LIBRARY_KEY, libName); //main library contains main() function } @@ -275,7 +312,7 @@ public abstract class QtLoader { try { if (m_service != null) { Bundle parameters = new Bundle(); - parameters.putStringArray(REQUIRED_MODULES_KEY, m_qtLibs); + parameters.putStringArray(REQUIRED_MODULES_KEY, (String[]) m_qtLibs.toArray()); parameters.putString(APPLICATION_TITLE_KEY, getTitle()); parameters.putInt(MINIMUM_MINISTRO_API_KEY, MINISTRO_API_LEVEL); parameters.putInt(MINIMUM_QT_VERSION_KEY, QT_VERSION); @@ -357,7 +394,7 @@ public abstract class QtLoader { AssetManager assetsManager = m_context.getAssets(); InputStream inputStream = null; - OutputStream outputStream = null; + FileOutputStream outputStream = null; try { inputStream = assetsManager.open(source); outputStream = new FileOutputStream(destinationFile); @@ -369,8 +406,12 @@ public abstract class QtLoader { inputStream.close(); if (outputStream != null) - outputStream.close(); + // Ensure that the buffered data is flushed to the OS for writing. + outputStream.flush(); } + // Mark the output stream as still needing to be written to physical disk. + // The output stream will be closed after this sync completes. + m_fileOutputStreams.add(outputStream); } private static void createBundledBinary(String source, String destination) @@ -388,7 +429,7 @@ public abstract class QtLoader { destinationFile.createNewFile(); InputStream inputStream = null; - OutputStream outputStream = null; + FileOutputStream outputStream = null; try { inputStream = new FileInputStream(source); outputStream = new FileOutputStream(destinationFile); @@ -400,8 +441,12 @@ public abstract class QtLoader { inputStream.close(); if (outputStream != null) - outputStream.close(); + // Ensure that the buffered data is flushed to the OS for writing. + outputStream.flush(); } + // Mark the output stream as still needing to be written to physical disk. + // The output stream will be closed after this sync completes. + m_fileOutputStreams.add(outputStream); } private boolean cleanCacheIfNecessary(String pluginsPrefix, long packageVersion) @@ -450,31 +495,11 @@ public abstract class QtLoader { return; { - File versionFile = new File(pluginsPrefix + "cache.version"); - - File parentDirectory = versionFile.getParentFile(); - if (!parentDirectory.exists()) - parentDirectory.mkdirs(); - - versionFile.createNewFile(); - - DataOutputStream outputStream = null; - try { - outputStream = new DataOutputStream(new FileOutputStream(versionFile)); - outputStream.writeLong(packageVersion); - } catch (Exception e) { - e.printStackTrace(); - } finally { - if (outputStream != null) - outputStream.close(); - } - } - - { // why can't we load the plugins directly from libs ?!?! String key = BUNDLED_IN_LIB_RESOURCE_ID_KEY; if (m_contextInfo.metaData.containsKey(key)) { - String[] list = m_context.getResources().getStringArray(m_contextInfo.metaData.getInt(key)); + int resourceId = m_contextInfo.metaData.getInt(key); + ArrayList<String> list = prefferedAbiLibs(m_context.getResources().getStringArray(resourceId)); for (String bundledImportBinary : list) { String[] split = bundledImportBinary.split(":"); @@ -499,6 +524,66 @@ public abstract class QtLoader { } } + + // The Java compiler must be assured that variables belonging to this parent thread will not + // go out of scope during the runtime of the spawned thread (since in general spawned + // threads can outlive their parent threads). Copy variables and declare as 'final' before + // passing into the spawned thread. + final String pluginsPrefixFinal = pluginsPrefix; + final long packageVersionFinal = packageVersion; + + // Spawn a worker thread to write all installed files to physical disk and indicate + // successful installation by creating the 'cache.version' file. + new Thread(new Runnable() { + @Override + public void run() { + try { + finalizeInstallation(pluginsPrefixFinal, packageVersionFinal); + } catch (Exception e) { + Log.e(QtApplication.QtTAG, e.getMessage()); + e.printStackTrace(); + return; + } + } + }).start(); + } + + private void finalizeInstallation(String pluginsPrefix, long packageVersion) + throws IOException + { + { + // Write all installed files to physical disk and close each output stream + for (FileOutputStream fileOutputStream : m_fileOutputStreams) { + fileOutputStream.getFD().sync(); + fileOutputStream.close(); + } + + m_fileOutputStreams.clear(); + } + + { + // Create 'cache.version' file + + File versionFile = new File(pluginsPrefix + "cache.version"); + + File parentDirectory = versionFile.getParentFile(); + if (!parentDirectory.exists()) + parentDirectory.mkdirs(); + + versionFile.createNewFile(); + + DataOutputStream outputStream = null; + try { + outputStream = new DataOutputStream(new FileOutputStream(versionFile)); + outputStream.writeLong(packageVersion); + } catch (Exception e) { + e.printStackTrace(); + } finally { + if (outputStream != null) + outputStream.close(); + } + } + } private void deleteRecursively(File directory) @@ -553,7 +638,7 @@ public abstract class QtLoader { if (m_contextInfo.metaData.containsKey("android.app.qt_libs_resource_id")) { int resourceId = m_contextInfo.metaData.getInt("android.app.qt_libs_resource_id"); - m_qtLibs = m_context.getResources().getStringArray(resourceId); + m_qtLibs = prefferedAbiLibs(m_context.getResources().getStringArray(resourceId)); } if (m_contextInfo.metaData.containsKey("android.app.use_local_qt_libs") @@ -567,6 +652,7 @@ public abstract class QtLoader { apkDeployFromSystem = true; String libsDir = null; + String bundledLibsDir = null; if (apkDeployFromSystem) { String systemLibsPrefix = SYSTEM_LIB_PATH; if (m_contextInfo.metaData.containsKey("android.app.system_libs_prefix")) { @@ -583,8 +669,11 @@ public abstract class QtLoader { } else { String nativeLibraryPrefix = m_context.getApplicationInfo().nativeLibraryDir + "/"; File nativeLibraryDir = new File(nativeLibraryPrefix); - if (nativeLibraryDir.exists() && nativeLibraryDir.isDirectory() && nativeLibraryDir.list().length > 0) + if (nativeLibraryDir.exists() && nativeLibraryDir.isDirectory() && nativeLibraryDir.list().length > 0) { libsDir = nativeLibraryPrefix; + bundledLibsDir = nativeLibraryPrefix; + } + } if (apkDeployFromSystem && libsDir == null) @@ -593,8 +682,8 @@ public abstract class QtLoader { if (m_qtLibs != null) { String libPrefix = libsDir + "lib"; - for (int i = 0; i < m_qtLibs.length; i++) - libraryList.add(libPrefix + m_qtLibs[i] + ".so"); + for (String lib : m_qtLibs) + libraryList.add(libPrefix + lib + ".so"); } if (m_contextInfo.metaData.containsKey("android.app.bundle_local_qt_libs") @@ -604,22 +693,26 @@ public abstract class QtLoader { String pluginsPrefix = dataPath + "qt-reserved-files/"; if (libsDir == null) - throw new Exception(""); + throw new Exception("Invalid libsDir"); cleanOldCacheIfNecessary(dataPath, pluginsPrefix); extractBundledPluginsAndImports(pluginsPrefix, libsDir); if (m_contextInfo.metaData.containsKey(BUNDLED_IN_LIB_RESOURCE_ID_KEY)) { - String[] extraLibs = m_contextInfo.metaData.getString("android.app.load_local_libs").split(":"); - for (String lib : extraLibs) { - if (!lib.isEmpty()) - libraryList.add(pluginsPrefix + lib); + int resourceId = m_contextInfo.metaData.getInt("android.app.load_local_libs_resource_id"); + for (String libs : prefferedAbiLibs(m_context.getResources().getStringArray(resourceId))) { + for (String lib : libs.split(":")) { + if (!lib.isEmpty()) + libraryList.add(libsDir + lib); + } } } ENVIRONMENT_VARIABLES += "\tQML2_IMPORT_PATH=" + pluginsPrefix + "/qml" + "\tQML_IMPORT_PATH=" + pluginsPrefix + "/imports" + "\tQT_PLUGIN_PATH=" + pluginsPrefix + "/plugins"; + if (bundledLibsDir != null) + ENVIRONMENT_VARIABLES += "\tQT_BUNDLED_LIBS_PATH=" + bundledLibsDir; } Bundle loaderParams = new Bundle(); |