aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorRaphaël Cotty <raphael.cotty@gmail.com>2021-01-19 22:40:39 +0100
committerRaphaël Cotty <raphael.cotty@gmail.com>2021-04-01 15:42:44 +0000
commitfd1a0ce5b0bd23bb2121e896c1c2732c3d41884a (patch)
treec13a5464d0af3046dcd535a3a61e55f726a60452
parent517a121ac97032942220acae60f7b93d49d930f9 (diff)
Android: Code signing module
Android.sdk was already signing the apk package but it was using hardcoded debug key and could only be used to run the application locally. The signing part of the android apk/aab package generation is now in the new codesign module. By default the same debug key is used. But it is now also possible to configure a different key. Task-number: QBS-899 Change-Id: I49c54a4d55578c48363805e927392b3a468805f0 Reviewed-by: Ivan Komissarov <ABBAPOH@gmail.com> Reviewed-by: Christian Kandeler <christian.kandeler@qt.io>
-rw-r--r--doc/config/macros.qdocconf1
-rw-r--r--doc/reference/modules/codesign-module.qdoc65
-rwxr-xr-xscripts/test-qt-for-android.sh1
-rw-r--r--share/qbs/modules/Android/sdk/sdk.qbs61
-rw-r--r--share/qbs/modules/Android/sdk/utils.js64
-rw-r--r--share/qbs/modules/codesign/android.qbs116
-rw-r--r--share/qbs/modules/codesign/codesign.js72
-rw-r--r--tests/auto/blackbox/testdata-android/qt-app/test.keystorebin0 -> 1284 bytes
-rw-r--r--tests/auto/blackbox/tst_blackboxandroid.cpp108
9 files changed, 370 insertions, 118 deletions
diff --git a/doc/config/macros.qdocconf b/doc/config/macros.qdocconf
index a8abe25e5..6811215c0 100644
--- a/doc/config/macros.qdocconf
+++ b/doc/config/macros.qdocconf
@@ -4,6 +4,7 @@ macro.qbsversion = $QBS_VERSION
macro.defaultvalue = "Default:"
macro.nodefaultvalue = "Default: Undefined"
macro.appleproperty = "This property is specific to Apple platforms."
+macro.androidproperty = "This property is specific to Android platforms."
macro.unixproperty = "This property is specific to Unix platforms."
macro.windowsproperty = "This property is specific to Windows."
macro.baremetalproperty = "This property is specific to bare-metal platforms."
diff --git a/doc/reference/modules/codesign-module.qdoc b/doc/reference/modules/codesign-module.qdoc
index ab95798a7..70a37477b 100644
--- a/doc/reference/modules/codesign-module.qdoc
+++ b/doc/reference/modules/codesign-module.qdoc
@@ -33,7 +33,8 @@
\brief Provides code signing support.
- The \c codesign module contains properties and rules for code signing on Apple platforms.
+ The \c codesign module contains properties and rules for code signing on Apple and Android
+ platforms.
\section2 Relevant File Tags
@@ -244,3 +245,65 @@
\appleproperty
*/
+
+/*!
+ \qmlproperty bool codesign::useApksigner
+
+ If \c true, the package is signed using apksignerFilePath binary.
+ Set this property to \c false to use the jarsignerFilePath one.
+ Set by the Android.sdk module.
+
+ \since Qbs 1.19
+
+ \defaultvalue \c true
+
+ \androidproperty
+*/
+
+/*!
+ \qmlproperty string codesign::keystorePath
+
+ The absolute path to the keystore file.
+
+ \since Qbs 1.19
+
+ \defaultvalue \c "${HOME}/.android/debug.keystore"
+
+ \androidproperty
+*/
+
+/*!
+ \qmlproperty string codesign::keystorePassword
+
+ The keystore password.
+
+ \since Qbs 1.19
+
+ \defaultvalue \c "android"
+
+ \androidproperty
+*/
+
+/*!
+ \qmlproperty string codesign::keyPassword
+
+ The key password.
+
+ \since Qbs 1.19
+
+ \defaultvalue \c "android"
+
+ \androidproperty
+*/
+
+/*!
+ \qmlproperty string codesign::keyAlias
+
+ The key alias.
+
+ \since Qbs 1.19
+
+ \defaultvalue \c "androiddebugkey"
+
+ \androidproperty
+*/
diff --git a/scripts/test-qt-for-android.sh b/scripts/test-qt-for-android.sh
index 4d476624b..55ef991d4 100755
--- a/scripts/test-qt-for-android.sh
+++ b/scripts/test-qt-for-android.sh
@@ -62,6 +62,7 @@ qbs setup-android --ndk-dir ${ANDROID_HOME}/ndk-bundle --sdk-dir ${ANDROID_HOME}
export QBS_AUTOTEST_PROFILE=qbs_autotests-android
export QBS_AUTOTEST_ALWAYS_LOG_STDERR=true
+export QTEST_FUNCTION_TIMEOUT=9000000
if [ ! "${QT_VERSION}" \< "5.14.0" ] && [ "${QT_VERSION}" \< "6.0.0" ]; then
echo "Using multi-arch data sets for qml tests (only for qt version >= 5.14 and < 6.0.0) with all architectures"
diff --git a/share/qbs/modules/Android/sdk/sdk.qbs b/share/qbs/modules/Android/sdk/sdk.qbs
index 0e40d059b..cae5fc2e5 100644
--- a/share/qbs/modules/Android/sdk/sdk.qbs
+++ b/share/qbs/modules/Android/sdk/sdk.qbs
@@ -178,20 +178,18 @@ Module {
property path compiledResourcesDir: FileInfo.joinPaths(product.buildDirectory,
"compiled_resources")
property string packageContentsDir: FileInfo.joinPaths(product.buildDirectory, packageType)
- property string debugKeyStorePath: FileInfo.joinPaths(
- Environment.getEnv(qbs.hostOS.contains("windows")
- ? "USERPROFILE" : "HOME"),
- ".android", "debug.keystore")
- property bool useApksigner: buildToolsVersion && Utilities.versionCompare(
- buildToolsVersion, "24.0.3") >= 0
property stringList aidlSearchPaths
Depends { name: "java"; condition: _enableRules }
+ Depends { name: "codesign"; condition: _enableRules }
Properties {
condition: _enableRules
java.languageVersion: platformJavaVersion
java.runtimeVersion: platformJavaVersion
java.bootClassPaths: androidJarFilePath
+ codesign.apksignerFilePath: Android.sdk.apksignerFilePath
+ codesign._packageName: Android.sdk.apkBaseName + (_generateAab ? ".aab" : ".apk")
+ codesign.useApksigner: !_generateAab
}
validate: {
@@ -211,6 +209,11 @@ Module {
+ "Android bundletool can be downloaded from "
+ "https://github.com/google/bundletool");
}
+ if (Utilities.versionCompare(buildToolsVersion, "24.0.3") < 0) {
+ throw ModUtils.ModuleError("Version of Android SDK build tools too old. This version "
+ + "is " + buildToolsVersion + " and minimum version is "
+ + "24.0.3. Please update the Android SDK.")
+ }
}
FileTagger {
@@ -223,36 +226,6 @@ Module {
fileTags: ["android.aidl"]
}
- FileTagger {
- patterns: ["*.keystore"]
- fileTags: ["android.keystore"]
- }
-
- // Typically there is a debug keystore in ~/.android/debug.keystore which gets created
- // by the native build tools the first time a build is done. However, we don't want to create it
- // ourselves, because writing to a location outside the qbs build directory is both polluting
- // and has the potential for race conditions. So we'll instruct the user what to do.
- Group {
- name: "Android debug keystore"
- files: {
- if (!File.exists(Android.sdk.debugKeyStorePath)) {
- throw ModUtils.ModuleError("Could not find an Android debug keystore at " +
- Android.sdk.debugKeyStorePath + ". " +
- "If you are developing for Android on this machine for the first time and " +
- "have never built an application using the native Gradle / Android Studio " +
- "tooling, this is normal. You must create the debug keystore now using the " +
- "following command, in order to continue:\n\n" +
- SdkUtils.createDebugKeyStoreCommandString(java.keytoolFilePath,
- Android.sdk.debugKeyStorePath) +
- "\n\n" +
- "See the following URL for more information: " +
- "https://developer.android.com/studio/publish/app-signing.html#debug-mode");
- }
- return [Android.sdk.debugKeyStorePath];
- }
- fileTags: ["android.keystore"]
- }
-
Parameter {
property bool embedJar: true
}
@@ -525,11 +498,11 @@ Module {
inputs: [
"android.resources", "android.assets", "android.manifest_final",
"android.dex", "android.stl_deployed",
- "android.nativelibrary_deployed", "android.keystore"
+ "android.nativelibrary_deployed"
]
Artifact {
- filePath: product.Android.sdk.apkBaseName + ".apk"
- fileTags: "android.package"
+ filePath: product.Android.sdk.apkBaseName + ".apk_unsigned"
+ fileTags: "android.package_unsigned"
}
prepare: SdkUtils.prepareAaptPackage.apply(SdkUtils, arguments)
}
@@ -540,11 +513,11 @@ Module {
inputs: [
"android.apk_resources", "android.manifest_final",
"android.dex", "android.stl_deployed",
- "android.nativelibrary_deployed", "android.keystore"
+ "android.nativelibrary_deployed"
]
Artifact {
- filePath: product.Android.sdk.apkBaseName + ".apk"
- fileTags: "android.package"
+ filePath: product.Android.sdk.apkBaseName + ".apk_unsigned"
+ fileTags: "android.package_unsigned"
}
prepare: SdkUtils.prepareApkPackage.apply(SdkUtils, arguments)
}
@@ -558,8 +531,8 @@ Module {
"android.nativelibrary_deployed"
]
Artifact {
- filePath: product.Android.sdk.apkBaseName + ".aab"
- fileTags: "android.package"
+ filePath: product.Android.sdk.apkBaseName + ".aab_unsigned"
+ fileTags: "android.package_unsigned"
}
prepare: SdkUtils.prepareBundletoolPackage.apply(SdkUtils, arguments)
}
diff --git a/share/qbs/modules/Android/sdk/utils.js b/share/qbs/modules/Android/sdk/utils.js
index 6fa200822..9511ae9de 100644
--- a/share/qbs/modules/Android/sdk/utils.js
+++ b/share/qbs/modules/Android/sdk/utils.js
@@ -242,7 +242,7 @@ function prepareAaptGenerate(project, product, inputs, outputs, input, output,
function prepareAaptPackage(project, product, inputs, outputs, input, output, explicitlyDependsOn) {
var cmds = [];
- var apkOutput = outputs["android.package"][0];
+ var apkOutput = outputs["android.package_unsigned"][0];
var args = commonAaptPackageArgs.apply(this, arguments);
args.push("-F", apkOutput.filePath + ".unaligned");
args.push(product.Android.sdk.packageContentsDir);
@@ -250,17 +250,6 @@ function prepareAaptPackage(project, product, inputs, outputs, input, output, ex
cmd.description = "generating " + apkOutput.fileName;
cmds.push(cmd);
- if (!product.Android.sdk.useApksigner) {
- args = ["-sigalg", "SHA1withRSA", "-digestalg", "SHA1",
- "-keystore", inputs["android.keystore"][0].filePath,
- "-storepass", "android",
- apkOutput.filePath + ".unaligned",
- "androiddebugkey"];
- cmd = new Command(product.java.jarsignerFilePath, args);
- cmd.description = "signing " + apkOutput.fileName;
- cmds.push(cmd);
- }
-
cmd = new Command(product.Android.sdk.zipalignFilePath,
["-f", "4", apkOutput.filePath + ".unaligned", apkOutput.filePath]);
cmd.silent = true;
@@ -271,26 +260,14 @@ function prepareAaptPackage(project, product, inputs, outputs, input, output, ex
cmd.unalignedApk = apkOutput.filePath + ".unaligned";
cmd.sourceCode = function() { File.remove(unalignedApk); };
cmds.push(cmd);
-
- if (product.Android.sdk.useApksigner) {
- // TODO: Implement full signing support, not just using the debug keystore
- args = ["sign",
- "--ks", inputs["android.keystore"][0].filePath,
- "--ks-pass", "pass:android",
- apkOutput.filePath];
- cmd = new Command(product.Android.sdk.apksignerFilePath, args);
- cmd.description = "signing " + apkOutput.fileName;
- cmds.push(cmd);
- }
-
return cmds;
}
function prepareApkPackage(project, product, inputs, outputs, input, output, explicitlyDependsOn) {
var cmds = [];
var apkInputFilePath = inputs["android.apk_resources"][0].filePath;
- var apkOutput = outputs["android.package"][0];
- var apkOutputFilePathUnaligned = outputs["android.package"][0].filePath + ".unaligned";
+ var apkOutput = outputs["android.package_unsigned"][0];
+ var apkOutputFilePathUnaligned = apkOutput.filePath + ".unaligned";
var dexFilePath = inputs["android.dex"][0].filePath;
var copyCmd = new JavaScriptCommand();
@@ -309,17 +286,6 @@ function prepareApkPackage(project, product, inputs, outputs, input, output, exp
jarCmd.workingDirectory = libPath;
cmds.push(jarCmd);
- if (!product.Android.sdk.useApksigner) {
- args = ["-sigalg", "SHA1withRSA", "-digestalg", "SHA1",
- "-keystore", inputs["android.keystore"][0].filePath,
- "-storepass", "android",
- apkOutputFilePathUnaligned,
- "androiddebugkey"];
- cmd = new Command(product.java.jarsignerFilePath, args);
- cmd.description = "signing " + apkOutput.fileName;
- cmds.push(cmd);
- }
-
cmd = new Command(product.Android.sdk.zipalignFilePath,
["-f", "4", apkOutputFilePathUnaligned, apkOutput.filePath]);
cmd.silent = true;
@@ -330,18 +296,6 @@ function prepareApkPackage(project, product, inputs, outputs, input, output, exp
cmd.unalignedApk = apkOutputFilePathUnaligned;
cmd.sourceCode = function() { File.remove(unalignedApk); };
cmds.push(cmd);
-
- if (product.Android.sdk.useApksigner) {
- // TODO: Implement full signing support, not just using the debug keystore
- args = ["sign",
- "--ks", inputs["android.keystore"][0].filePath,
- "--ks-pass", "pass:android",
- apkOutput.filePath];
- cmd = new Command(product.Android.sdk.apksignerFilePath, args);
- cmd.description = "signing " + apkOutput.fileName;
- cmds.push(cmd);
- }
-
return cmds;
}
@@ -377,7 +331,7 @@ function prepareBundletoolPackage(project, product, inputs, outputs, input, outp
jarBaseCmd.workingDirectory = baseModuleDir;
cmds.push(jarBaseCmd);
- var aabFilePath = outputs["android.package"][0].filePath;
+ var aabFilePath = outputs["android.package_unsigned"][0].filePath;
var removeCmd = new JavaScriptCommand();
removeCmd.description = "removing previous aab";
removeCmd.filePath = aabFilePath;
@@ -391,20 +345,12 @@ function prepareBundletoolPackage(project, product, inputs, outputs, input, outp
args.push("--modules=" + baseFilePath);
args.push("--output=" + aabFilePath);
var cmd = new Command(product.java.interpreterFilePath, args);
- cmd.description = "generating " + outputs["android.package"][0].fileName;
+ cmd.description = "generating " + aabFilePath.fileName;
cmds.push(cmd);
return cmds;
}
-function createDebugKeyStoreCommandString(keytoolFilePath, keystoreFilePath) {
- var args = ["-genkey", "-keystore", keystoreFilePath, "-alias", "androiddebugkey",
- "-storepass", "android", "-keypass", "android", "-keyalg", "RSA",
- "-keysize", "2048", "-validity", "10000", "-dname",
- "CN=Android Debug,O=Android,C=US"];
- return Process.shellQuote(keytoolFilePath, args);
-}
-
function stlDeploymentData(product, inputs, type)
{
var data = { uniqueInputs: [], outputFilePaths: []};
diff --git a/share/qbs/modules/codesign/android.qbs b/share/qbs/modules/codesign/android.qbs
new file mode 100644
index 000000000..be96d42de
--- /dev/null
+++ b/share/qbs/modules/codesign/android.qbs
@@ -0,0 +1,116 @@
+/****************************************************************************
+**
+** Copyright (C) 2021 Raphaël Cotty <raphael.cotty@gmail.com>
+** Contact: http://www.qt.io/licensing
+**
+** This file is part of the Qbs.
+**
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms and
+** conditions see http://www.qt.io/terms-conditions. For further information
+** use the contact form at http://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 2.1 or version 3 as published by the Free
+** Software Foundation and appearing in the file LICENSE.LGPLv21 and
+** LICENSE.LGPLv3 included in the packaging of this file. Please review the
+** following information to ensure the GNU Lesser General Public License
+** requirements will be met: https://www.gnu.org/licenses/lgpl.html and
+** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, The Qt Company gives you certain additional
+** rights. These rights are described in The Qt Company LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+****************************************************************************/
+
+import qbs
+import qbs.Environment
+import qbs.File
+import qbs.FileInfo
+import qbs.ModUtils
+import qbs.Probes
+import "codesign.js" as CodeSign
+
+CodeSignModule {
+ condition: qbs.targetOS.contains("android")
+ priority: 1
+ enableCodeSigning: true
+
+ property bool useApksigner: true
+ property path apksignerFilePath
+
+ Probes.JdkProbe {
+ id: jdk
+ environmentPaths: (jdkPath ? [jdkPath] : []).concat(base)
+ }
+ property string jdkPath: jdk.path
+ property string jarsignerFilePath: FileInfo.joinPaths(jdkPath, "bin", jarsignerName)
+ property string jarsignerName: "jarsigner"
+ property string keytoolFilePath: FileInfo.joinPaths(jdkPath, "bin", keytoolName)
+ property string keytoolName: "keytool"
+
+ property string debugKeystorePath: FileInfo.joinPaths(
+ Environment.getEnv(qbs.hostOS.contains("windows")
+ ? "USERPROFILE" : "HOME"),
+ ".android", "debug.keystore")
+ readonly property string debugKeystorePassword: "android"
+ readonly property string debugPassword: "android"
+ readonly property string debugKeyAlias: "androiddebugkey"
+
+ property string keystorePath: debugKeystorePath
+ property string keystorePassword: debugKeystorePassword
+ property string keyPassword: debugPassword
+ property string keyAlias: debugKeyAlias
+
+ // Private property set by the Android.sdk module
+ property string _packageName
+
+ Rule {
+ condition: useApksigner
+ inputs: ["android.package_unsigned"]
+ Artifact {
+ filePath: product.codesign._packageName
+ fileTags: "android.package"
+ }
+ prepare: CodeSign.signApkPackage.apply(this, arguments)
+ }
+
+ Rule {
+ condition: !useApksigner
+ inputs: ["android.package_unsigned"]
+ Artifact {
+ filePath: product.codesign._packageName
+ fileTags: "android.package"
+ }
+ prepare: CodeSign.signAabPackage.apply(this, arguments)
+ }
+
+ validate: {
+ // Typically there is a debug keystore in ~/.android/debug.keystore which gets created
+ // by the native build tools the first time a build is done. However, we don't want to
+ // create it ourselves, because writing to a location outside the qbs build directory is
+ // both polluting and has the potential for race conditions. So we'll instruct the user what
+ // to do.
+ if (keystorePath === debugKeystorePath && !File.exists(debugKeystorePath)) {
+ throw ModUtils.ModuleError("Could not find an Android debug keystore at " +
+ codesign.debugKeystorePath + ". " +
+ "If you are developing for Android on this machine for the first time and " +
+ "have never built an application using the native Gradle / Android Studio " +
+ "tooling, this is normal. You must create the debug keystore now using the " +
+ "following command, in order to continue:\n\n" +
+ CodeSign.createDebugKeyStoreCommandString(codesign.keytoolFilePath,
+ codesign.debugKeystorePath,
+ codesign.debugKeystorePassword,
+ codesign.debugPassword,
+ codesign.debugKeyAlias) +
+ "\n\n" +
+ "See the following URL for more information: " +
+ "https://developer.android.com/studio/publish/app-signing.html#debug-mode");
+ }
+ }
+}
diff --git a/share/qbs/modules/codesign/codesign.js b/share/qbs/modules/codesign/codesign.js
index 1d039e7ca..bf7e95224 100644
--- a/share/qbs/modules/codesign/codesign.js
+++ b/share/qbs/modules/codesign/codesign.js
@@ -32,6 +32,7 @@
var File = require("qbs.File");
var FileInfo = require("qbs.FileInfo");
var PathTools = require("qbs.PathTools");
+var Process = require("qbs.Process");
var PropertyList = require("qbs.PropertyList");
var Utilities = require("qbs.Utilities");
@@ -277,3 +278,74 @@ function prepareSign(project, product, inputs, outputs, input, output) {
return cmds;
}
+
+function signApkPackage(project, product, inputs, outputs, input, output, explicitlyDependsOn) {
+ var apkInput = inputs["android.package_unsigned"][0];
+ var apkOutput = outputs["android.package"][0];
+ var cmd;
+ if (product.codesign.enableCodeSigning) {
+ var args = ["sign",
+ "--ks", product.codesign.keystorePath,
+ "--ks-pass", "pass:" + product.codesign.keystorePassword,
+ "--ks-key-alias", product.codesign.keyAlias,
+ "--key-pass", "pass:" + product.codesign.keyPassword,
+ "--out", apkOutput.filePath,
+ apkInput.filePath];
+ cmd = new Command(product.codesign.apksignerFilePath, args);
+ cmd.description = "signing " + apkOutput.fileName;
+ } else {
+ cmd = new JavaScriptCommand();
+ cmd.description = "copying without signing " + apkOutput.fileName;
+ cmd.source = apkInput.filePath;
+ cmd.target = apkOutput.filePath;
+ cmd.silent = true;
+ cmd.sourceCode = function() {
+ // If enableCodeSigning is changed to false without any change to unsigned package then
+ // the copy won't happen because of timestamps. So the target file needs file needs to
+ // be removed to avoid it.
+ File.remove(target);
+ File.copy(source, target);
+ }
+ }
+ return cmd;
+}
+
+function signAabPackage(project, product, inputs, outputs, input, output, explicitlyDependsOn) {
+ var aabInput = inputs["android.package_unsigned"][0];
+ var aabOutput = outputs["android.package"][0];
+ var cmd;
+ if (product.codesign.enableCodeSigning) {
+ args = ["-sigalg", "SHA1withRSA", "-digestalg", "SHA1",
+ "-keystore", product.codesign.keystorePath,
+ "-storepass", product.codesign.keystorePassword,
+ "-keypass", product.codesign.keyPassword,
+ "-signedjar", aabOutput.filePath,
+ aabInput.filePath,
+ product.codesign.keyAlias];
+ cmd = new Command(product.codesign.jarsignerFilePath, args);
+ cmd.description = "signing " + aabOutput.fileName;
+ } else {
+ cmd = new JavaScriptCommand();
+ cmd.description = "copying without signing " + aabOutput.fileName;
+ cmd.source = aabInput.filePath;
+ cmd.target = aabOutput.filePath;
+ cmd.silent = true;
+ cmd.sourceCode = function() {
+ // If enableCodeSigning is changed to false without any change to unsigned package then
+ // the copy won't happen because of timestamps. So the target file needs file needs to
+ // be removed to avoid it.
+ File.remove(target);
+ File.copy(source, target);
+ }
+ }
+ return cmd;
+}
+
+function createDebugKeyStoreCommandString(keytoolFilePath, keystoreFilePath, keystorePassword,
+ keyPassword, keyAlias) {
+ var args = ["-genkey", "-keystore", keystoreFilePath, "-alias", keyAlias,
+ "-storepass", keystorePassword, "-keypass", keyPassword, "-keyalg", "RSA",
+ "-keysize", "2048", "-validity", "10000", "-dname",
+ "CN=Android Debug,O=Android,C=US"];
+ return Process.shellQuote(keytoolFilePath, args);
+}
diff --git a/tests/auto/blackbox/testdata-android/qt-app/test.keystore b/tests/auto/blackbox/testdata-android/qt-app/test.keystore
new file mode 100644
index 000000000..5713d10d2
--- /dev/null
+++ b/tests/auto/blackbox/testdata-android/qt-app/test.keystore
Binary files differ
diff --git a/tests/auto/blackbox/tst_blackboxandroid.cpp b/tests/auto/blackbox/tst_blackboxandroid.cpp
index a03c9e318..f8ed8a0b9 100644
--- a/tests/auto/blackbox/tst_blackboxandroid.cpp
+++ b/tests/auto/blackbox/tst_blackboxandroid.cpp
@@ -268,14 +268,19 @@ void TestBlackboxAndroid::android_data()
return result;
};
- auto commonFiles = [](bool generateAab) {
+ auto commonFiles = [](bool generateAab, bool codeSign = true,
+ QString keyAlias="androiddebugkey") {
+ QByteArrayList files;
if (generateAab)
- return (QByteArrayList()
- << "base/manifest/AndroidManifest.xml" << "base/dex/classes.dex"
- << "BundleConfig.pb");
- return (QByteArrayList()
- << "AndroidManifest.xml" << "META-INF/ANDROIDD.RSA" << "META-INF/ANDROIDD.SF"
- << "META-INF/MANIFEST.MF" << "classes.dex");
+ files << "base/manifest/AndroidManifest.xml" << "base/dex/classes.dex"
+ << "BundleConfig.pb";
+ else
+ files << "AndroidManifest.xml" << "classes.dex";
+ if (codeSign)
+ files << QByteArray("META-INF/" + keyAlias.toUpper().left(8).toUtf8() + ".RSA")
+ << QByteArray("META-INF/" + keyAlias.toUpper().left(8).toUtf8() + ".SF")
+ << "META-INF/MANIFEST.MF";
+ return files;
};
QTest::addColumn<QString>("projectDir");
@@ -296,10 +301,11 @@ void TestBlackboxAndroid::android_data()
bool generateAab = false;
bool isIncrementalBuild = false;
- auto qtAppExpectedFiles = [&](bool generateAab, bool enableAapt2) {
+ auto qtAppExpectedFiles = [&](bool generateAab, bool enableAapt2, bool codeSign = true,
+ QString keyAlias="androiddebugkey") {
QByteArrayList expectedFile;
if (singleArchQt) {
- expectedFile << commonFiles(generateAab) + expandArchs(ndkArchsForQt, {
+ expectedFile << commonFiles(generateAab, codeSign, keyAlias) + expandArchs(ndkArchsForQt, {
cxxLibPath("libgnustl_shared.so", true),
"assets/--Added-by-androiddeployqt--/qt_cache_pregenerated_file_list",
"lib/${ARCH}/libplugins_imageformats_libqgif.so",
@@ -317,7 +323,7 @@ void TestBlackboxAndroid::android_data()
"lib/${ARCH}/libQt5Widgets.so",
"lib/${ARCH}/libqt-app.so"}, generateAab);
} else {
- expectedFile << commonFiles(generateAab) + expandArchs(ndkArchsForQt, {
+ expectedFile << commonFiles(generateAab, codeSign, keyAlias) + expandArchs(ndkArchsForQt, {
cxxLibPath("libgnustl_shared.so", true),
"lib/${ARCH}/libplugins_imageformats_qgif_${ARCH}.so",
"lib/${ARCH}/libplugins_imageformats_qico_${ARCH}.so",
@@ -329,7 +335,7 @@ void TestBlackboxAndroid::android_data()
"lib/${ARCH}/libqt-app_${ARCH}.so"}, generateAab);
}
if (generateAab)
- expectedFile << "base/resources.pb" << "base/assets.pb" << "base/native.pb";
+ expectedFile << "base/resources.pb" << "base/native.pb";
else
expectedFile << "resources.arsc";
if (version >= qbs::Version(5, 14))
@@ -346,11 +352,82 @@ void TestBlackboxAndroid::android_data()
expectedFile << "res/layout/splash.xml";
return expectedFile;
};
+ auto codeSignProperties = [&](bool codeSign, QString keyStorePath, QString keystorePassword,
+ QString keyPassword, QString keyAlias) {
+ if (!codeSign)
+ return QStringList{"modules.codesign.enableCodeSigning:false"};
+ return QStringList{
+ "modules.codesign.enableCodeSigning:true",
+ "modules.codesign.keystorePath:" + keyStorePath,
+ "modules.codesign.keystorePassword:" + keystorePassword,
+ "modules.codesign.keyPassword:" + keyPassword,
+ "modules.codesign.keyAlias:" + keyAlias,
+ };
+ };
+ bool codeSign = true;
+ QString keyStorePath(testDataDir + "/qt-app/test.keystore");
+ QString keystorePassword("qbsKeystoreTest");
+ QString keyPassword("qbsKeyTest");
+ QString keyAlias("qbsTest");
QTest::newRow("qt app")
<< "qt-app" << QStringList("qt-app")
<< (QList<QByteArrayList>() << (QByteArrayList() << qtAppExpectedFiles(generateAab,
- enableAapt2)))
- << QStringList{aaptVersion(enableAapt2), packageType(generateAab)}
+ enableAapt2,
+ codeSign,
+ keyAlias)))
+ << (QStringList() << codeSignProperties(codeSign, keyStorePath, keystorePassword,
+ keyPassword, keyAlias)
+ << aaptVersion(enableAapt2)
+ << packageType(generateAab))
+ << enableAapt2 << generateAab << isIncrementalBuild;
+ codeSign = false;
+ QTest::newRow("qt app no signing")
+ << "qt-app" << QStringList("qt-app")
+ << (QList<QByteArrayList>() << (QByteArrayList() << qtAppExpectedFiles(generateAab,
+ enableAapt2,
+ codeSign,
+ keyAlias)))
+ << (QStringList() << codeSignProperties(codeSign, keyStorePath, keystorePassword,
+ keyPassword, keyAlias)
+ << aaptVersion(enableAapt2)
+ << packageType(generateAab))
+ << enableAapt2 << generateAab << isIncrementalBuild;
+ enableAapt2 = true;
+ codeSign = true;
+ QTest::newRow("qt app aapt2")
+ << "qt-app" << QStringList("qt-app")
+ << (QList<QByteArrayList>() << (QByteArrayList() << qtAppExpectedFiles(generateAab,
+ enableAapt2,
+ codeSign,
+ keyAlias)))
+ << (QStringList() << codeSignProperties(codeSign, keyStorePath, keystorePassword,
+ keyPassword, keyAlias)
+ << aaptVersion(enableAapt2)
+ << packageType(generateAab))
+ << enableAapt2 << generateAab << isIncrementalBuild;
+ generateAab = true;
+ QTest::newRow("qt app aab")
+ << "qt-app" << QStringList("qt-app")
+ << (QList<QByteArrayList>() << (QByteArrayList() << qtAppExpectedFiles(generateAab,
+ enableAapt2,
+ codeSign,
+ keyAlias)))
+ << (QStringList() << codeSignProperties(codeSign, keyStorePath, keystorePassword,
+ keyPassword, keyAlias)
+ << aaptVersion(enableAapt2)
+ << packageType(generateAab))
+ << enableAapt2 << generateAab << isIncrementalBuild;
+ codeSign = false;
+ QTest::newRow("qt app aab no signing")
+ << "qt-app" << QStringList("qt-app")
+ << (QList<QByteArrayList>() << (QByteArrayList() << qtAppExpectedFiles(generateAab,
+ enableAapt2,
+ codeSign,
+ keyAlias)))
+ << (QStringList() << codeSignProperties(codeSign, keyStorePath, keystorePassword,
+ keyPassword, keyAlias)
+ << aaptVersion(enableAapt2)
+ << packageType(generateAab))
<< enableAapt2 << generateAab << isIncrementalBuild;
const QByteArrayList ndkArchsForQtSave = ndkArchsForQt;
@@ -383,6 +460,8 @@ void TestBlackboxAndroid::android_data()
return expectedFile;
};
+ generateAab = false;
+ enableAapt2 = false;
QTest::newRow("teapot")
<< "teapot" << QStringList("TeapotNativeActivity")
<< (QList<QByteArrayList>() << teaPotAppExpectedFiles(archs, generateAab))
@@ -437,6 +516,7 @@ void TestBlackboxAndroid::android_data()
"modules.qbs.architecture:" + archsStringList.first(),
aaptVersion(enableAapt2), packageType(generateAab)}
<< enableAapt2 << generateAab << isIncrementalBuild;
+
auto qmlAppExpectedFiles = [&](bool generateAab, bool enableAapt2) {
QByteArrayList expectedFile;
if (singleArchQt) {
@@ -730,7 +810,6 @@ void TestBlackboxAndroid::android_data()
} else {
qmlAppCustomProperties = QStringList{"modules.Android.sdk.automaticSources:false"};
}
-
// aapt tool for the resources works with a directory option pointing to the parent directory
// of the resources (res).
// The Qt.android_support module adds res/values/libs.xml (from Qt install dir). So the res from
@@ -851,6 +930,7 @@ void TestBlackboxAndroid::android_data()
expectedFile << "resources.arsc";
return expectedFile;
};
+
QTest::newRow("no native")
<< "no-native"
<< QStringList("com.example.android.basicmediadecoder")