Android: Allow deploying Qt apps as system apps
The main app shared library and shared dependencies are not automatically deployed when the apk is placed in /system/app or /system/priv-app. In such cases, we need to place these libraries in /system/lib and tell QtLoader to load them from here. It is possible to specify a custom library path in AndroidManifest.xml. [ChangeLog][Android][QtLoader] Enabled loading shared libraries from /system/lib or a custom path specified with the android.app.system_libs_prefix metadata variable in AndroidManifest.xml. This allows deploying Qt apps as Android system apps. Change-Id: I8388e91a53475b06a027467face45c08f096fbf8 Reviewed-by: Oswald Buddenhagen <oswald.buddenhagen@qt.io> Reviewed-by: Anton Kudryavtsev <antkudr@mail.ru> Reviewed-by: Eskil Abrahamsen Blomfeldt <eskil.abrahamsen-blomfeldt@qt.io>
This commit is contained in:
parent
9b54447e45
commit
57a77fe775
@ -49,6 +49,7 @@ import android.app.Service;
|
||||
import android.content.Context;
|
||||
import android.content.Intent;
|
||||
import android.content.pm.PackageManager;
|
||||
import android.content.pm.ActivityInfo;
|
||||
import android.net.Uri;
|
||||
import android.os.Handler;
|
||||
import android.os.Looper;
|
||||
@ -188,7 +189,21 @@ public class QtNative
|
||||
|
||||
for (String libName : libraries) {
|
||||
try {
|
||||
File f = new File(nativeLibraryDir+"lib"+libName+".so");
|
||||
String libNameTemplate = "lib" + libName + ".so";
|
||||
File f = new File(nativeLibraryDir + libNameTemplate);
|
||||
if (!f.exists()) {
|
||||
Log.i(QtTAG, "Can't find '" + f.getAbsolutePath());
|
||||
try {
|
||||
ActivityInfo info = m_activity.getPackageManager().getActivityInfo(m_activity.getComponentName(),
|
||||
PackageManager.GET_META_DATA);
|
||||
String systemLibraryDir = QtNativeLibrariesDir.systemLibrariesDir;
|
||||
if (info.metaData.containsKey("android.app.system_libs_prefix"))
|
||||
systemLibraryDir = info.metaData.getString("android.app.system_libs_prefix");
|
||||
f = new File(systemLibraryDir + libNameTemplate);
|
||||
} catch (Exception e) {
|
||||
|
||||
}
|
||||
}
|
||||
if (f.exists())
|
||||
System.load(f.getAbsolutePath());
|
||||
else
|
||||
@ -281,7 +296,20 @@ public class QtNative
|
||||
String mainLibrary,
|
||||
String nativeLibraryDir) throws Exception
|
||||
{
|
||||
File f = new File(nativeLibraryDir + "lib" + mainLibrary + ".so");
|
||||
String mainLibNameTemplate = "lib" + mainLibrary + ".so";
|
||||
File f = new File(nativeLibraryDir + mainLibNameTemplate);
|
||||
if (!f.exists()) {
|
||||
try {
|
||||
ActivityInfo info = m_activity.getPackageManager().getActivityInfo(m_activity.getComponentName(),
|
||||
PackageManager.GET_META_DATA);
|
||||
String systemLibraryDir = QtNativeLibrariesDir.systemLibrariesDir;
|
||||
if (info.metaData.containsKey("android.app.system_libs_prefix"))
|
||||
systemLibraryDir = info.metaData.getString("android.app.system_libs_prefix");
|
||||
f = new File(systemLibraryDir + mainLibNameTemplate);
|
||||
} catch (Exception e) {
|
||||
|
||||
}
|
||||
}
|
||||
if (!f.exists())
|
||||
throw new Exception("Can't find main library '" + mainLibrary + "'");
|
||||
|
||||
|
@ -45,6 +45,7 @@ import android.content.pm.ApplicationInfo;
|
||||
import android.content.pm.PackageManager.NameNotFoundException;
|
||||
|
||||
public class QtNativeLibrariesDir {
|
||||
public static final String systemLibrariesDir = "/system/lib/";
|
||||
public static String nativeLibrariesDir(Context context)
|
||||
{
|
||||
String m_nativeLibraryDir = null;
|
||||
|
@ -84,6 +84,7 @@ 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";
|
||||
@ -92,6 +93,10 @@ public abstract class QtLoader {
|
||||
public static final String EXTRACT_STYLE_KEY = "extract.android.style";
|
||||
private static final String EXTRACT_STYLE_MINIMAL_KEY = "extract.android.style.option";
|
||||
|
||||
// These parameters matter in case of deploying application as system (embedded into firmware)
|
||||
public static final String SYSTEM_LIB_PATH = "/system/lib/";
|
||||
public String[] SYSTEM_APP_PATHS = {"/system/priv-app/", "/system/app/"};
|
||||
|
||||
/// Ministro server parameter keys
|
||||
public static final String REQUIRED_MODULES_KEY = "required.modules";
|
||||
public static final String APPLICATION_TITLE_KEY = "application.title";
|
||||
@ -106,7 +111,6 @@ public abstract class QtLoader {
|
||||
public static final String REPOSITORY_KEY = "repository"; // use this key to overwrite the default ministro repsitory
|
||||
public static final String ANDROID_THEMES_KEY = "android.themes"; // themes that your application uses
|
||||
|
||||
|
||||
public String APPLICATION_PARAMETERS = null; // use this variable to pass any parameters to your application,
|
||||
// the parameters must not contain any white spaces
|
||||
// and must be separated with "\t"
|
||||
@ -210,11 +214,11 @@ 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") )
|
||||
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"))));
|
||||
|
||||
String libName = null;
|
||||
if ( m_contextInfo.metaData.containsKey("android.app.lib_name") ) {
|
||||
if (m_contextInfo.metaData.containsKey("android.app.lib_name")) {
|
||||
libName = m_contextInfo.metaData.getString("android.app.lib_name");
|
||||
loaderParams.putString(MAIN_LIBRARY_KEY, libName); //main library contains main() function
|
||||
}
|
||||
@ -408,13 +412,9 @@ public abstract class QtLoader {
|
||||
}
|
||||
}
|
||||
|
||||
private void extractBundledPluginsAndImports(String pluginsPrefix)
|
||||
private void extractBundledPluginsAndImports(String pluginsPrefix, String libsDir)
|
||||
throws IOException
|
||||
{
|
||||
ArrayList<String> libs = new ArrayList<String>();
|
||||
|
||||
String libsDir = m_context.getApplicationInfo().nativeLibraryDir + "/";
|
||||
|
||||
long packageVersion = -1;
|
||||
try {
|
||||
PackageInfo packageInfo = m_context.getPackageManager().getPackageInfo(m_context.getPackageName(), 0);
|
||||
@ -530,6 +530,35 @@ public abstract class QtLoader {
|
||||
&& m_contextInfo.metaData.getInt("android.app.use_local_qt_libs") == 1) {
|
||||
ArrayList<String> libraryList = new ArrayList<String>();
|
||||
|
||||
boolean apkDeployFromSystem = false;
|
||||
String apkPath = m_context.getApplicationInfo().publicSourceDir;
|
||||
File apkFile = new File(apkPath);
|
||||
if (apkFile.exists() && Arrays.asList(SYSTEM_APP_PATHS).contains(apkFile.getParentFile().getAbsolutePath() + "/"))
|
||||
apkDeployFromSystem = true;
|
||||
|
||||
String libsDir = null;
|
||||
if (apkDeployFromSystem) {
|
||||
String systemLibsPrefix = SYSTEM_LIB_PATH;
|
||||
if (m_contextInfo.metaData.containsKey("android.app.system_libs_prefix")) {
|
||||
systemLibsPrefix = m_contextInfo.metaData.getString("android.app.system_libs_prefix");
|
||||
} else {
|
||||
Log.e(QtApplication.QtTAG, "It looks like app deployed as system app. "
|
||||
+ "It may be necessary to specify path to system lib directory using "
|
||||
+ "android.app.system_libs_prefix metadata variable in your AndroidManifest.xml");
|
||||
Log.e(QtApplication.QtTAG, "Using " + SYSTEM_LIB_PATH + " as default path");
|
||||
}
|
||||
File systemLibraryDir = new File(systemLibsPrefix);
|
||||
if (systemLibraryDir.exists() && systemLibraryDir.isDirectory() && systemLibraryDir.list().length > 0)
|
||||
libsDir = systemLibsPrefix;
|
||||
} else {
|
||||
String nativeLibraryPrefix = m_context.getApplicationInfo().nativeLibraryDir + "/";
|
||||
File nativeLibraryDir = new File(nativeLibraryPrefix);
|
||||
if (nativeLibraryDir.exists() && nativeLibraryDir.isDirectory() && nativeLibraryDir.list().length > 0)
|
||||
libsDir = nativeLibraryPrefix;
|
||||
}
|
||||
|
||||
if (apkDeployFromSystem && libsDir == null)
|
||||
throw new Exception("");
|
||||
|
||||
String localPrefix = "/data/local/tmp/qt/";
|
||||
if (m_contextInfo.metaData.containsKey("android.app.libs_prefix"))
|
||||
@ -542,33 +571,30 @@ public abstract class QtLoader {
|
||||
&& m_contextInfo.metaData.getInt("android.app.bundle_local_qt_libs") == 1) {
|
||||
localPrefix = m_context.getApplicationInfo().dataDir + "/";
|
||||
pluginsPrefix = localPrefix + "qt-reserved-files/";
|
||||
|
||||
if (libsDir == null)
|
||||
throw new Exception("");
|
||||
|
||||
cleanOldCacheIfNecessary(localPrefix, pluginsPrefix);
|
||||
extractBundledPluginsAndImports(pluginsPrefix);
|
||||
extractBundledPluginsAndImports(pluginsPrefix, libsDir);
|
||||
|
||||
bundlingQtLibs = true;
|
||||
}
|
||||
|
||||
if (m_qtLibs != null) {
|
||||
for (int i=0;i<m_qtLibs.length;i++) {
|
||||
libraryList.add(localPrefix
|
||||
+ "lib/lib"
|
||||
+ m_qtLibs[i]
|
||||
+ ".so");
|
||||
}
|
||||
String libPrefix = apkDeployFromSystem ? libsDir + "lib" : localPrefix + "lib/lib";
|
||||
for (int i = 0; i < m_qtLibs.length; i++)
|
||||
libraryList.add(libPrefix + m_qtLibs[i] + ".so");
|
||||
}
|
||||
|
||||
if (m_contextInfo.metaData.containsKey("android.app.load_local_libs")) {
|
||||
String[] extraLibs = m_contextInfo.metaData.getString("android.app.load_local_libs").split(":");
|
||||
for (String lib : extraLibs) {
|
||||
if (lib.length() > 0) {
|
||||
if (lib.startsWith("lib/"))
|
||||
libraryList.add(localPrefix + lib);
|
||||
else
|
||||
libraryList.add(pluginsPrefix + lib);
|
||||
}
|
||||
}
|
||||
if (lib.length() > 0)
|
||||
libraryList.add((lib.startsWith("lib/") ? localPrefix : pluginsPrefix) + lib);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
String dexPaths = new String();
|
||||
String pathSeparator = System.getProperty("path.separator", ":");
|
||||
if (!bundlingQtLibs && m_contextInfo.metaData.containsKey("android.app.load_local_jars")) {
|
||||
|
@ -30,6 +30,8 @@
|
||||
<meta-data android:name="android.app.load_local_libs" android:value="-- %%INSERT_LOCAL_LIBS%% --"/>
|
||||
<meta-data android:name="android.app.load_local_jars" android:value="-- %%INSERT_LOCAL_JARS%% --"/>
|
||||
<meta-data android:name="android.app.static_init_classes" android:value="-- %%INSERT_INIT_CLASSES%% --"/>
|
||||
<!-- Used to specify custom system library path to run with local system libs -->
|
||||
<!-- <meta-data android:name="android.app.system_libs_prefix" android:value="/system/lib/"/> -->
|
||||
<!-- Messages maps -->
|
||||
<meta-data android:value="@string/ministro_not_found_msg" android:name="android.app.ministro_not_found_msg"/>
|
||||
<meta-data android:value="@string/ministro_needed_msg" android:name="android.app.ministro_needed_msg"/>
|
||||
|
Loading…
x
Reference in New Issue
Block a user