From 48e11ea3f1bfd43917ce35e2f83b31e4dd29aab2 Mon Sep 17 00:00:00 2001 From: Hans-Christoph Steiner Date: Mon, 12 Oct 2020 17:39:40 +0200 Subject: [PATCH 1/9] run-tests: exit with error if no test APKs are found There must be at least one APK available for this test suite to work, for example, this test: grep -F ' repo/$apk" ln "$f" $1/repo/$apk || \ rsync -axv "$f" $1/repo/$apk # rsync if hard link is not possible + touch $1/.found-apks fi done + if [ ! -e $1/.found-apks ]; then + echo "ERROR: The dir APKDIR must have APKs in it! $APKDIR does not." + exit 1 + fi set -x } From b5cd850abee421ebe65128fac2169ae09edb67d1 Mon Sep 17 00:00:00 2001 From: Hans-Christoph Steiner Date: Wed, 14 Oct 2020 16:43:24 +0200 Subject: [PATCH 2/9] apksigner search should use MINIMUM_APKSIGNER_BUILD_TOOLS_VERSION --- fdroidserver/common.py | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/fdroidserver/common.py b/fdroidserver/common.py index ee093778..aefe50b0 100644 --- a/fdroidserver/common.py +++ b/fdroidserver/common.py @@ -423,7 +423,8 @@ def find_apksigner(): Returns the best version of apksigner following this algorithm: * use config['apksigner'] if set * try to find apksigner in path - * find apksigner in build-tools starting from newest installed going down to MINIMUM_APKSIGNER_BUILD_TOOLS_VERSION + * find apksigner in build-tools starting from newest installed + going down to MINIMUM_APKSIGNER_BUILD_TOOLS_VERSION :return: path to apksigner or None if no version is found """ if set_command_in_config('apksigner'): @@ -434,7 +435,7 @@ def find_apksigner(): for f in sorted(os.listdir(build_tools_path), reverse=True): if not os.path.isdir(os.path.join(build_tools_path, f)): continue - if LooseVersion(f) < LooseVersion(MINIMUM_AAPT_BUILD_TOOLS_VERSION): + if LooseVersion(f) < LooseVersion(MINIMUM_APKSIGNER_BUILD_TOOLS_VERSION): return None if os.path.exists(os.path.join(build_tools_path, f, 'apksigner')): apksigner = os.path.join(build_tools_path, f, 'apksigner') From 27b90a13bff71651e47d95cb35b772842a849889 Mon Sep 17 00:00:00 2001 From: Hans-Christoph Steiner Date: Wed, 14 Oct 2020 16:49:00 +0200 Subject: [PATCH 3/9] remove aapt version of common.is_apk_and_debuggable() --- fdroidserver/common.py | 32 +++++++------------------------- tests/common.TestCase | 23 ++--------------------- 2 files changed, 9 insertions(+), 46 deletions(-) diff --git a/fdroidserver/common.py b/fdroidserver/common.py index aefe50b0..32ca62bc 100644 --- a/fdroidserver/common.py +++ b/fdroidserver/common.py @@ -2389,19 +2389,15 @@ def ensure_final_value(packageName, arsc, value): return '' -def is_apk_and_debuggable_aapt(apkfile): - p = SdkToolsPopen(['aapt', 'dump', 'xmltree', apkfile, 'AndroidManifest.xml'], - output=False) - if p.returncode != 0: - raise FDroidException(_("Failed to get APK manifest information")) - for line in p.output.splitlines(): - if 'android:debuggable' in line and not line.endswith('0x0'): - return True - return False +def is_apk_and_debuggable(apkfile): + """Returns True if the given file is an APK and is debuggable + Parse only from the APK. -def is_apk_and_debuggable_androguard(apkfile): - """Parse only from the APK""" + :param apkfile: full path to the apk to check""" + + if get_file_extension(apkfile) != 'apk': + return False from androguard.core.bytecodes.axml import AXMLParser, format_value, START_TAG with ZipFile(apkfile) as apk: with apk.open('AndroidManifest.xml') as manifest: @@ -2423,20 +2419,6 @@ def is_apk_and_debuggable_androguard(apkfile): return False -def is_apk_and_debuggable(apkfile): - """Returns True if the given file is an APK and is debuggable - - :param apkfile: full path to the apk to check""" - - if get_file_extension(apkfile) != 'apk': - return False - - if use_androguard(): - return is_apk_and_debuggable_androguard(apkfile) - else: - return is_apk_and_debuggable_aapt(apkfile) - - def get_apk_id(apkfile): """Extract identification information from APK. diff --git a/tests/common.TestCase b/tests/common.TestCase index ca06de8b..99e10e08 100755 --- a/tests/common.TestCase +++ b/tests/common.TestCase @@ -148,11 +148,6 @@ class CommonTest(unittest.TestCase): config = dict() fdroidserver.common.fill_config_defaults(config) fdroidserver.common.config = config - self._set_build_tools() - try: - config['aapt'] = fdroidserver.common.find_sdk_tools_cmd('aapt') - except fdroidserver.exception.FDroidException: - pass # aapt is not required if androguard is present # these are set debuggable testfiles = [] @@ -160,15 +155,8 @@ class CommonTest(unittest.TestCase): testfiles.append(os.path.join(self.basedir, 'urzip-badsig.apk')) testfiles.append(os.path.join(self.basedir, 'urzip-badcert.apk')) for apkfile in testfiles: - debuggable = fdroidserver.common.is_apk_and_debuggable(apkfile) - self.assertTrue(debuggable, + self.assertTrue(fdroidserver.common.is_apk_and_debuggable(apkfile), "debuggable APK state was not properly parsed!") - if 'aapt' in config: - self.assertTrue(fdroidserver.common.is_apk_and_debuggable_aapt(apkfile), - 'aapt parsing missed !') - if fdroidserver.common.use_androguard(): - self.assertTrue(fdroidserver.common.is_apk_and_debuggable_androguard(apkfile), - 'androguard missed !') # these are set NOT debuggable testfiles = [] @@ -176,15 +164,8 @@ class CommonTest(unittest.TestCase): testfiles.append(os.path.join(self.basedir, 'urzip-release-unsigned.apk')) testfiles.append(os.path.join(self.basedir, 'v2.only.sig_2.apk')) for apkfile in testfiles: - debuggable = fdroidserver.common.is_apk_and_debuggable(apkfile) - self.assertFalse(debuggable, + self.assertFalse(fdroidserver.common.is_apk_and_debuggable(apkfile), "debuggable APK state was not properly parsed!") - if 'aapt' in config: - self.assertFalse(fdroidserver.common.is_apk_and_debuggable_aapt(apkfile), - 'aapt parsing missed !') - if fdroidserver.common.use_androguard(): - self.assertFalse(fdroidserver.common.is_apk_and_debuggable_androguard(apkfile), - 'androguard missed !') VALID_STRICT_PACKAGE_NAMES = [ "An.stop", From 8fd7dcd425465806f5f6ad42a4fb496baffe560b Mon Sep 17 00:00:00 2001 From: Hans-Christoph Steiner Date: Wed, 14 Oct 2020 16:53:59 +0200 Subject: [PATCH 4/9] always use androguard version of common.get_apk_id() first This removes the need for common.use_androguard() --- fdroidserver/common.py | 15 ++++++--------- 1 file changed, 6 insertions(+), 9 deletions(-) diff --git a/fdroidserver/common.py b/fdroidserver/common.py index 32ca62bc..ca88fee2 100644 --- a/fdroidserver/common.py +++ b/fdroidserver/common.py @@ -2431,15 +2431,12 @@ def get_apk_id(apkfile): :returns: triplet (appid, version code, version name) """ - if use_androguard(): - try: - return get_apk_id_androguard(apkfile) - except zipfile.BadZipFile as e: - logging.error(apkfile + ': ' + str(e)) - if 'aapt' in config: - return get_apk_id_aapt(apkfile) - else: - return get_apk_id_aapt(apkfile) + try: + return get_apk_id_androguard(apkfile) + except zipfile.BadZipFile as e: + logging.error(apkfile + ': ' + str(e)) + if 'aapt' in config: + return get_apk_id_aapt(apkfile) def get_apk_id_androguard(apkfile): From 08931f45240f519f525e299a4b9ac8610e0c8088 Mon Sep 17 00:00:00 2001 From: Hans-Christoph Steiner Date: Wed, 14 Oct 2020 17:04:26 +0200 Subject: [PATCH 5/9] purge update.scan_apk_aapt() androguard v3.3.3+ properly extracts the app name, so this adds the names to the tests. --- fdroidserver/update.py | 85 +---------- .../apk/info.guardianproject.urzip.yaml | 1 + tests/metadata/apk/org.dyndns.fules.ck.yaml | 1 + tests/update.TestCase | 142 +++++++----------- 4 files changed, 60 insertions(+), 169 deletions(-) diff --git a/fdroidserver/update.py b/fdroidserver/update.py index 9a2f80d4..a2512db6 100644 --- a/fdroidserver/update.py +++ b/fdroidserver/update.py @@ -49,7 +49,6 @@ from . import _ from . import common from . import index from . import metadata -from .common import SdkToolsPopen from .exception import BuildException, FDroidException from PIL import Image, PngImagePlugin @@ -1350,10 +1349,7 @@ def scan_apk(apk_file): 'antiFeatures': set(), } - if common.use_androguard(): - scan_apk_androguard(apk, apk_file) - else: - scan_apk_aapt(apk, apk_file) + scan_apk_androguard(apk, apk_file) if not common.is_valid_package_name(apk['packageName']): raise BuildException(_("{appid} from {path} is not a valid Java Package Name!") @@ -1412,83 +1408,6 @@ def _get_apk_icons_src(apkfile, icon_name): return icons_src -def scan_apk_aapt(apk, apkfile): - p = SdkToolsPopen(['aapt', 'dump', 'badging', apkfile], output=False) - if p.returncode != 0: - if options.delete_unknown: - if os.path.exists(apkfile): - logging.error(_("Failed to get apk information, deleting {path}").format(path=apkfile)) - os.remove(apkfile) - else: - logging.error("Could not find {0} to remove it".format(apkfile)) - else: - logging.error(_("Failed to get apk information, skipping {path}").format(path=apkfile)) - raise BuildException(_("Invalid APK")) - icon_name = None - for line in p.output.splitlines(): - if line.startswith("package:"): - try: - apk['packageName'] = re.match(APK_NAME_PAT, line).group(1) - apk['versionCode'] = int(re.match(APK_VERCODE_PAT, line).group(1)) - apk['versionName'] = re.match(APK_VERNAME_PAT, line).group(1) - except Exception as e: - raise FDroidException("Package matching failed: " + str(e) + "\nLine was: " + line) - elif line.startswith("application:"): - m = re.match(APK_LABEL_ICON_PAT, line) - if m: - apk['name'] = m.group(1) - icon_name = os.path.splitext(os.path.basename(m.group(2)))[0] - elif not apk.get('name') and line.startswith("launchable-activity:"): - # Only use launchable-activity as fallback to application - apk['name'] = re.match(APK_LABEL_ICON_PAT, line).group(1) - elif line.startswith("sdkVersion:"): - m = re.match(APK_SDK_VERSION_PAT, line) - if m is None: - logging.error(line.replace('sdkVersion:', '') - + ' is not a valid minSdkVersion!') - else: - apk['minSdkVersion'] = int(m.group(1)) - elif line.startswith("targetSdkVersion:"): - m = re.match(APK_SDK_VERSION_PAT, line) - if m is None: - logging.error(line.replace('targetSdkVersion:', '') - + ' is not a valid targetSdkVersion!') - else: - apk['targetSdkVersion'] = int(m.group(1)) - elif line.startswith("maxSdkVersion:"): - apk['maxSdkVersion'] = int(re.match(APK_SDK_VERSION_PAT, line).group(1)) - elif line.startswith("native-code:"): - apk['nativecode'] = [] - for arch in line[13:].split(' '): - apk['nativecode'].append(arch[1:-1]) - elif line.startswith('uses-permission:'): - perm_match = re.match(APK_PERMISSION_PAT, line).groups() - permission = UsesPermission( - perm_match[0], # name - None if perm_match[1] is None else int(perm_match[1]), # maxSdkVersion - ) - apk['uses-permission'].append(permission) - - elif line.startswith('uses-permission-sdk-23:'): - perm_match = re.match(APK_PERMISSION_PAT, line).groups() - permission_sdk_23 = UsesPermissionSdk23( - perm_match[0], # name - None if perm_match[1] is None else int(perm_match[1]), # maxSdkVersion - ) - apk['uses-permission-sdk-23'].append(permission_sdk_23) - - elif line.startswith('uses-feature:'): - feature = re.match(APK_FEATURE_PAT, line).group(1) - # Filter out this, it's only added with the latest SDK tools and - # causes problems for lots of apps. - if feature != "android.hardware.screen.portrait" \ - and feature != "android.hardware.screen.landscape": - if feature.startswith("android.feature."): - feature = feature[16:] - apk['features'].add(feature) - apk['icons_src'] = _get_apk_icons_src(apkfile, icon_name) - - def _sanitize_sdk_version(value): """Sanitize the raw values from androguard to handle bad values @@ -1529,8 +1448,6 @@ def scan_apk_androguard(apk, apkfile): logging.error(_("Failed to get apk information, skipping {path}") .format(path=apkfile)) raise BuildException(_("Invalid APK")) - except ImportError: - raise FDroidException("androguard library is not installed and aapt not present") except FileNotFoundError: logging.error(_("Could not open apk file for analysis")) raise BuildException(_("Invalid APK")) diff --git a/tests/metadata/apk/info.guardianproject.urzip.yaml b/tests/metadata/apk/info.guardianproject.urzip.yaml index f2e17651..4632c314 100644 --- a/tests/metadata/apk/info.guardianproject.urzip.yaml +++ b/tests/metadata/apk/info.guardianproject.urzip.yaml @@ -10,6 +10,7 @@ icons_src: '-1': res/drawable/ic_launcher.png '160': res/drawable/ic_launcher.png minSdkVersion: 4 +name: urzip packageName: info.guardianproject.urzip sig: e0ecb5fc2d63088e4a07ae410a127722 signer: 7eabd8c15de883d1e82b5df2fd4f7f769e498078e9ad6dc901f0e96db77ceac3 diff --git a/tests/metadata/apk/org.dyndns.fules.ck.yaml b/tests/metadata/apk/org.dyndns.fules.ck.yaml index 76f7cacb..23a0325f 100644 --- a/tests/metadata/apk/org.dyndns.fules.ck.yaml +++ b/tests/metadata/apk/org.dyndns.fules.ck.yaml @@ -14,6 +14,7 @@ icons_src: '160': res/drawable-mdpi-v4/icon_launcher.png '240': res/drawable-hdpi-v4/icon_launcher.png minSdkVersion: 7 +name: Compass Keyboard nativecode: - arm64-v8a - armeabi diff --git a/tests/update.TestCase b/tests/update.TestCase index 4f9c3f3f..30a93f1f 100755 --- a/tests/update.TestCase +++ b/tests/update.TestCase @@ -458,98 +458,72 @@ class UpdateTest(unittest.TestCase): fdroidserver.common.config = config fdroidserver.update.config = config os.chdir(os.path.join(localmodule, 'tests')) - try: - config['aapt'] = fdroidserver.common.find_sdk_tools_cmd('aapt') - except fdroidserver.exception.FDroidException: - pass # aapt is not required if androguard is present - for use_androguard in (True, False): - if use_androguard: - try: - import androguard - androguard - - def func(): - return True - fdroidserver.common.use_androguard = func - except ImportError: - continue - else: - if 'aapt' in config: - def func(): - return False - fdroidserver.common.use_androguard = func - else: - continue - - print('USE_ANDROGUARD', use_androguard) - - apksigner = fdroidserver.common.find_apksigner() - if apksigner: - if use_androguard: # v2 parsing needs both - config['apksigner'] = apksigner - apk_info = fdroidserver.update.scan_apk('v2.only.sig_2.apk') - self.assertIsNone(apk_info.get('maxSdkVersion')) - self.assertEqual(apk_info.get('versionName'), 'v2-only') - self.assertEqual(apk_info.get('versionCode'), 2) - else: - print('WARNING: skipping v2-only test since apksigner cannot be found') - apk_info = fdroidserver.update.scan_apk('repo/v1.v2.sig_1020.apk') + apksigner = fdroidserver.common.find_apksigner() + if apksigner: + config['apksigner'] = apksigner + apk_info = fdroidserver.update.scan_apk('v2.only.sig_2.apk') self.assertIsNone(apk_info.get('maxSdkVersion')) - self.assertEqual(apk_info.get('versionName'), 'v1+2') - self.assertEqual(apk_info.get('versionCode'), 1020) + self.assertEqual(apk_info.get('versionName'), 'v2-only') + self.assertEqual(apk_info.get('versionCode'), 2) + else: + print('WARNING: skipping v2-only test since apksigner cannot be found') + apk_info = fdroidserver.update.scan_apk('repo/v1.v2.sig_1020.apk') + self.assertIsNone(apk_info.get('maxSdkVersion')) + self.assertEqual(apk_info.get('versionName'), 'v1+2') + self.assertEqual(apk_info.get('versionCode'), 1020) - apk_info = fdroidserver.update.scan_apk('repo/souch.smsbypass_9.apk') - self.assertIsNone(apk_info.get('maxSdkVersion')) - self.assertEqual(apk_info.get('versionName'), '0.9') + apk_info = fdroidserver.update.scan_apk('repo/souch.smsbypass_9.apk') + self.assertIsNone(apk_info.get('maxSdkVersion')) + self.assertEqual(apk_info.get('versionName'), '0.9') - apk_info = fdroidserver.update.scan_apk('repo/duplicate.permisssions_9999999.apk') - self.assertEqual(apk_info.get('versionName'), '') - self.assertEqual(apk_info['icons_src'], {'160': 'res/drawable/ic_launcher.png', - '-1': 'res/drawable/ic_launcher.png'}) + apk_info = fdroidserver.update.scan_apk('repo/duplicate.permisssions_9999999.apk') + self.assertEqual(apk_info.get('versionName'), '') + self.assertEqual(apk_info['icons_src'], {'160': 'res/drawable/ic_launcher.png', + '-1': 'res/drawable/ic_launcher.png'}) - apk_info = fdroidserver.update.scan_apk('org.dyndns.fules.ck_20.apk') - self.assertEqual(apk_info['icons_src'], {'240': 'res/drawable-hdpi-v4/icon_launcher.png', - '120': 'res/drawable-ldpi-v4/icon_launcher.png', - '160': 'res/drawable-mdpi-v4/icon_launcher.png', - '-1': 'res/drawable-mdpi-v4/icon_launcher.png'}) - self.assertEqual(apk_info['icons'], {}) - self.assertEqual(apk_info['features'], []) - self.assertEqual(apk_info['antiFeatures'], set()) - self.assertEqual(apk_info['versionName'], 'v1.6pre2') - self.assertEqual(apk_info['hash'], - '897486e1f857c6c0ee32ccbad0e1b8cd82f6d0e65a44a23f13f852d2b63a18c8') - self.assertEqual(apk_info['packageName'], 'org.dyndns.fules.ck') - self.assertEqual(apk_info['versionCode'], 20) - self.assertEqual(apk_info['size'], 132453) - self.assertEqual(apk_info['nativecode'], - ['arm64-v8a', 'armeabi', 'armeabi-v7a', 'mips', 'mips64', 'x86', 'x86_64']) - self.assertEqual(apk_info['minSdkVersion'], 7) - self.assertEqual(apk_info['sig'], '9bf7a6a67f95688daec75eab4b1436ac') - self.assertEqual(apk_info['hashType'], 'sha256') - self.assertEqual(apk_info['targetSdkVersion'], 8) + apk_info = fdroidserver.update.scan_apk('org.dyndns.fules.ck_20.apk') + self.assertEqual(apk_info['icons_src'], {'240': 'res/drawable-hdpi-v4/icon_launcher.png', + '120': 'res/drawable-ldpi-v4/icon_launcher.png', + '160': 'res/drawable-mdpi-v4/icon_launcher.png', + '-1': 'res/drawable-mdpi-v4/icon_launcher.png'}) + self.assertEqual(apk_info['icons'], {}) + self.assertEqual(apk_info['features'], []) + self.assertEqual(apk_info['antiFeatures'], set()) + self.assertEqual(apk_info['versionName'], 'v1.6pre2') + self.assertEqual(apk_info['hash'], + '897486e1f857c6c0ee32ccbad0e1b8cd82f6d0e65a44a23f13f852d2b63a18c8') + self.assertEqual(apk_info['packageName'], 'org.dyndns.fules.ck') + self.assertEqual(apk_info['versionCode'], 20) + self.assertEqual(apk_info['size'], 132453) + self.assertEqual(apk_info['nativecode'], + ['arm64-v8a', 'armeabi', 'armeabi-v7a', 'mips', 'mips64', 'x86', 'x86_64']) + self.assertEqual(apk_info['minSdkVersion'], 7) + self.assertEqual(apk_info['sig'], '9bf7a6a67f95688daec75eab4b1436ac') + self.assertEqual(apk_info['hashType'], 'sha256') + self.assertEqual(apk_info['targetSdkVersion'], 8) - apk_info = fdroidserver.update.scan_apk('org.bitbucket.tickytacky.mirrormirror_4.apk') - self.assertEqual(apk_info.get('versionName'), '1.0.3') - self.assertEqual(apk_info['icons_src'], {'160': 'res/drawable-mdpi/mirror.png', - '-1': 'res/drawable-mdpi/mirror.png'}) + apk_info = fdroidserver.update.scan_apk('org.bitbucket.tickytacky.mirrormirror_4.apk') + self.assertEqual(apk_info.get('versionName'), '1.0.3') + self.assertEqual(apk_info['icons_src'], {'160': 'res/drawable-mdpi/mirror.png', + '-1': 'res/drawable-mdpi/mirror.png'}) - apk_info = fdroidserver.update.scan_apk('repo/info.zwanenburg.caffeinetile_4.apk') - self.assertEqual(apk_info.get('versionName'), '1.3') - self.assertEqual(apk_info['icons_src'], {'160': 'res/drawable/ic_coffee_on.xml', - '-1': 'res/drawable/ic_coffee_on.xml'}) + apk_info = fdroidserver.update.scan_apk('repo/info.zwanenburg.caffeinetile_4.apk') + self.assertEqual(apk_info.get('versionName'), '1.3') + self.assertEqual(apk_info['icons_src'], {'160': 'res/drawable/ic_coffee_on.xml', + '-1': 'res/drawable/ic_coffee_on.xml'}) - apk_info = fdroidserver.update.scan_apk('repo/com.politedroid_6.apk') - self.assertEqual(apk_info.get('versionName'), '1.5') - self.assertEqual(apk_info['icons_src'], {'120': 'res/drawable-ldpi-v4/icon.png', - '160': 'res/drawable-mdpi-v4/icon.png', - '240': 'res/drawable-hdpi-v4/icon.png', - '320': 'res/drawable-xhdpi-v4/icon.png', - '-1': 'res/drawable-mdpi-v4/icon.png'}) + apk_info = fdroidserver.update.scan_apk('repo/com.politedroid_6.apk') + self.assertEqual(apk_info.get('versionName'), '1.5') + self.assertEqual(apk_info['icons_src'], {'120': 'res/drawable-ldpi-v4/icon.png', + '160': 'res/drawable-mdpi-v4/icon.png', + '240': 'res/drawable-hdpi-v4/icon.png', + '320': 'res/drawable-xhdpi-v4/icon.png', + '-1': 'res/drawable-mdpi-v4/icon.png'}) - apk_info = fdroidserver.update.scan_apk('SpeedoMeterApp.main_1.apk') - self.assertEqual(apk_info.get('versionName'), '1.0') - self.assertEqual(apk_info['icons_src'], {}) + apk_info = fdroidserver.update.scan_apk('SpeedoMeterApp.main_1.apk') + self.assertEqual(apk_info.get('versionName'), '1.0') + self.assertEqual(apk_info['icons_src'], {}) def test_scan_apk_no_min_target(self): config = dict() @@ -627,8 +601,6 @@ class UpdateTest(unittest.TestCase): # Don't care about the date added to the repo and relative apkName del apk['added'] del apk['apkName'] - # avoid AAPT application name bug - del apk['name'] # ensure that icons have been extracted properly if apkName == '../urzip.apk': From 501a33f117fd1fbefc5280bc692d4b949cb418a0 Mon Sep 17 00:00:00 2001 From: Hans-Christoph Steiner Date: Wed, 14 Oct 2020 18:39:51 +0200 Subject: [PATCH 6/9] remove unused helper function --- tests/build.TestCase | 7 ------- 1 file changed, 7 deletions(-) diff --git a/tests/build.TestCase b/tests/build.TestCase index decae7ab..ea1af963 100755 --- a/tests/build.TestCase +++ b/tests/build.TestCase @@ -43,13 +43,6 @@ class BuildTest(unittest.TestCase): print('no build-tools found: ' + build_tools) return False - def _find_all(self): - for cmd in ('aapt', 'adb', 'android', 'zipalign'): - path = fdroidserver.common.find_sdk_tools_cmd(cmd) - if path is not None: - self.assertTrue(os.path.exists(path)) - self.assertTrue(os.path.isfile(path)) - def setUp(self): logging.basicConfig(level=logging.DEBUG) self.basedir = os.path.join(localmodule, 'tests') From 989159ef09a928b98613055627b752231f55f077 Mon Sep 17 00:00:00 2001 From: Hans-Christoph Steiner Date: Wed, 14 Oct 2020 18:44:25 +0200 Subject: [PATCH 7/9] require build-tools that fully supports apksigner --- fdroidserver/common.py | 3 ++- tests/common.TestCase | 2 +- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/fdroidserver/common.py b/fdroidserver/common.py index ca88fee2..df9ffe25 100644 --- a/fdroidserver/common.py +++ b/fdroidserver/common.py @@ -114,7 +114,7 @@ default_config = { 'r16b': None, }, 'cachedir': os.path.join(os.getenv('HOME'), '.cache', 'fdroidserver'), - 'build_tools': MINIMUM_AAPT_BUILD_TOOLS_VERSION, + 'build_tools': MINIMUM_APKSIGNER_BUILD_TOOLS_VERSION, 'java_paths': None, 'scan_binary': False, 'ant': "ant", @@ -508,6 +508,7 @@ def test_aapt_version(aapt): def test_sdk_exists(thisconfig): if 'sdk_path' not in thisconfig: + # TODO convert this to apksigner once it is required if 'aapt' in thisconfig and os.path.isfile(thisconfig['aapt']): test_aapt_version(thisconfig['aapt']) return True diff --git a/tests/common.TestCase b/tests/common.TestCase index 99e10e08..4e887d3c 100755 --- a/tests/common.TestCase +++ b/tests/common.TestCase @@ -78,7 +78,7 @@ class CommonTest(unittest.TestCase): for f in sorted(os.listdir(build_tools), reverse=True): versioned = os.path.join(build_tools, f) if os.path.isdir(versioned) \ - and os.path.isfile(os.path.join(versioned, 'aapt')): + and os.path.isfile(os.path.join(versioned, 'apksigner')): fdroidserver.common.config['build_tools'] = versioned break return True From aa80662642ce6fb71db1ea7369b50f6fe149ffde Mon Sep 17 00:00:00 2001 From: Hans-Christoph Steiner Date: Wed, 21 Oct 2020 15:16:12 +0200 Subject: [PATCH 8/9] init: enable apksigner by default so v2/v3 APK signatures validate Ultimately we want to get to using apksigner by default everywhere, this gets us closer to that by setting up all new repos to use apksigner by default in the config.py --- fdroidserver/init.py | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/fdroidserver/init.py b/fdroidserver/init.py index 20596130..64f63f73 100644 --- a/fdroidserver/init.py +++ b/fdroidserver/init.py @@ -162,6 +162,10 @@ def main(): # now that we have a local config.py, read configuration... config = common.read_config(options) + # enable apksigner by default so v2/v3 APK signatures validate + if common.find_apksigner() is not None: + test_config['apksigner'] = common.find_apksigner() + # the NDK is optional and there may be multiple versions of it, so it's # left for the user to configure From fd41b70e2747d83b7e7437e8fca70eddf818cbf7 Mon Sep 17 00:00:00 2001 From: Hans-Christoph Steiner Date: Wed, 21 Oct 2020 19:51:32 +0200 Subject: [PATCH 9/9] purge common.use_androguard, it is now used by default Up until now, the buildserver has not included androguard. Since a good version of androguard (v3.3.3+) is included in stretch-backports and the buildserver is already setup to use stretch-backports, this sets up the buildserver with androguard. closes #627 --- buildserver/provision-apt-get-install | 2 + fdroidserver/init.py | 97 ++++++++++----------------- tests/common.TestCase | 9 ++- 3 files changed, 41 insertions(+), 67 deletions(-) diff --git a/buildserver/provision-apt-get-install b/buildserver/provision-apt-get-install index de8dd3fb..5ef8d1b3 100644 --- a/buildserver/provision-apt-get-install +++ b/buildserver/provision-apt-get-install @@ -49,6 +49,7 @@ apt-get upgrade --download-only apt-get upgrade packages=" + androguard/stretch-backports ant asn1c ant-contrib @@ -99,6 +100,7 @@ packages=" python-magic python-pip python-setuptools + python3-asn1crypto/stretch-backports python3-defusedxml python3-git python3-gitdb diff --git a/fdroidserver/init.py b/fdroidserver/init.py index 64f63f73..f53057a0 100644 --- a/fdroidserver/init.py +++ b/fdroidserver/init.py @@ -75,46 +75,44 @@ def main(): # in ANDROID_HOME if that exists, otherwise None if options.android_home is not None: test_config['sdk_path'] = options.android_home - elif common.use_androguard(): - pass elif not common.test_sdk_exists(test_config): - if os.path.isfile('/usr/bin/aapt'): - # remove sdk_path and build_tools, they are not required - test_config.pop('sdk_path', None) - test_config.pop('build_tools', None) - # make sure at least aapt is found, since this can't do anything without it - test_config['aapt'] = common.find_sdk_tools_cmd('aapt') + # if neither --android-home nor the default sdk_path + # exist, prompt the user using platform-specific default + # and if the user leaves it blank, ignore and move on. + default_sdk_path = '' + if sys.platform == 'win32' or sys.platform == 'cygwin': + p = os.path.join(os.getenv('USERPROFILE'), + 'AppData', 'Local', 'Android', 'android-sdk') + elif sys.platform == 'darwin': + # on OSX, Homebrew is common and has an easy path to detect + p = '/usr/local/opt/android-sdk' + elif os.path.isdir('/usr/lib/android-sdk'): + # if the Debian packages are installed, suggest them + p = '/usr/lib/android-sdk' else: - # if neither --android-home nor the default sdk_path - # exist, prompt the user using platform-specific default - default_sdk_path = '/opt/android-sdk' - if sys.platform == 'win32' or sys.platform == 'cygwin': - p = os.path.join(os.getenv('USERPROFILE'), - 'AppData', 'Local', 'Android', 'android-sdk') - elif sys.platform == 'darwin': - # on OSX, Homebrew is common and has an easy path to detect - p = '/usr/local/opt/android-sdk' - else: - # if the Debian packages are installed, suggest them - p = '/usr/lib/android-sdk' - if os.path.exists(p): - default_sdk_path = p + p = '/opt/android-sdk' + if os.path.exists(p): + default_sdk_path = p - while not options.no_prompt: - try: - s = input(_('Enter the path to the Android SDK (%s) here:\n> ') % default_sdk_path) - except KeyboardInterrupt: - print('') - sys.exit(1) - if re.match(r'^\s*$', s) is not None: - test_config['sdk_path'] = default_sdk_path - else: - test_config['sdk_path'] = s - if common.test_sdk_exists(test_config): - break - if (options.android_home is not None or not common.use_androguard()) \ - and not common.test_sdk_exists(test_config): - raise FDroidException("Android SDK not found.") + while not options.no_prompt: + try: + s = input(_('Enter the path to the Android SDK (%s) here:\n> ') % default_sdk_path) + except KeyboardInterrupt: + print('') + sys.exit(1) + if re.match(r'^\s*$', s) is not None: + test_config['sdk_path'] = default_sdk_path + else: + test_config['sdk_path'] = s + if not default_sdk_path: + del(test_config['sdk_path']) + break + if common.test_sdk_exists(test_config): + break + + if test_config.get('sdk_path') and not common.test_sdk_exists(test_config): + raise FDroidException(_("Android SDK not found at {path}!") + .format(path=test_config['sdk_path'])) if not os.path.exists('config.py'): # 'metadata' and 'tmp' are created in fdroid @@ -134,31 +132,6 @@ def main(): logging.info('Try running `fdroid init` in an empty directory.') raise FDroidException('Repository already exists.') - if common.use_androguard(): - pass - elif 'aapt' not in test_config or not os.path.isfile(test_config['aapt']): - # try to find a working aapt, in all the recent possible paths - build_tools = os.path.join(test_config['sdk_path'], 'build-tools') - aaptdirs = [] - aaptdirs.append(os.path.join(build_tools, test_config['build_tools'])) - aaptdirs.append(build_tools) - for f in os.listdir(build_tools): - if os.path.isdir(os.path.join(build_tools, f)): - aaptdirs.append(os.path.join(build_tools, f)) - for d in sorted(aaptdirs, reverse=True): - if os.path.isfile(os.path.join(d, 'aapt')): - aapt = os.path.join(d, 'aapt') - break - if aapt and os.path.isfile(aapt): - dirname = os.path.basename(os.path.dirname(aapt)) - if dirname == 'build-tools': - # this is the old layout, before versioned build-tools - test_config['build_tools'] = '' - else: - test_config['build_tools'] = dirname - common.write_to_config(test_config, 'build_tools') - common.ensure_build_tools_exists(test_config) - # now that we have a local config.py, read configuration... config = common.read_config(options) diff --git a/tests/common.TestCase b/tests/common.TestCase index 4e887d3c..ff7befb1 100755 --- a/tests/common.TestCase +++ b/tests/common.TestCase @@ -746,16 +746,15 @@ class CommonTest(unittest.TestCase): ('repo/urzip-; Рахма́, [rɐxˈmanʲɪnəf] سيرجي_رخمانينوف 谢·.apk', 'info.guardianproject.urzip', '100', '0.1'), ] for apkfilename, appid, versionCode, versionName in testcases: + a, vc, vn = fdroidserver.common.get_apk_id(apkfilename) + self.assertEqual(appid, a, 'androguard appid parsing failed for ' + apkfilename) + self.assertEqual(versionName, vn, 'androguard versionName parsing failed for ' + apkfilename) + self.assertEqual(versionCode, vc, 'androguard versionCode parsing failed for ' + apkfilename) if 'aapt' in config: a, vc, vn = fdroidserver.common.get_apk_id_aapt(apkfilename) self.assertEqual(appid, a, 'aapt appid parsing failed for ' + apkfilename) self.assertEqual(versionCode, vc, 'aapt versionCode parsing failed for ' + apkfilename) self.assertEqual(versionName, vn, 'aapt versionName parsing failed for ' + apkfilename) - if fdroidserver.common.use_androguard(): - a, vc, vn = fdroidserver.common.get_apk_id(apkfilename) - self.assertEqual(appid, a, 'androguard appid parsing failed for ' + apkfilename) - self.assertEqual(versionName, vn, 'androguard versionName parsing failed for ' + apkfilename) - self.assertEqual(versionCode, vc, 'androguard versionCode parsing failed for ' + apkfilename) with self.assertRaises(FDroidException): fdroidserver.common.get_apk_id('nope')