mirror of
https://github.com/f-droid/fdroidserver.git
synced 2025-09-13 22:42:29 +03:00
Merge branch 'purge-aapt' into 'master'
purge aapt requirements and elevate apksigner Closes #627 See merge request fdroid/fdroidserver!821
This commit is contained in:
commit
9f11495934
10 changed files with 131 additions and 302 deletions
|
@ -49,6 +49,7 @@ apt-get upgrade --download-only
|
||||||
apt-get upgrade
|
apt-get upgrade
|
||||||
|
|
||||||
packages="
|
packages="
|
||||||
|
androguard/stretch-backports
|
||||||
ant
|
ant
|
||||||
asn1c
|
asn1c
|
||||||
ant-contrib
|
ant-contrib
|
||||||
|
@ -99,6 +100,7 @@ packages="
|
||||||
python-magic
|
python-magic
|
||||||
python-pip
|
python-pip
|
||||||
python-setuptools
|
python-setuptools
|
||||||
|
python3-asn1crypto/stretch-backports
|
||||||
python3-defusedxml
|
python3-defusedxml
|
||||||
python3-git
|
python3-git
|
||||||
python3-gitdb
|
python3-gitdb
|
||||||
|
|
|
@ -114,7 +114,7 @@ default_config = {
|
||||||
'r16b': None,
|
'r16b': None,
|
||||||
},
|
},
|
||||||
'cachedir': os.path.join(os.getenv('HOME'), '.cache', 'fdroidserver'),
|
'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,
|
'java_paths': None,
|
||||||
'scan_binary': False,
|
'scan_binary': False,
|
||||||
'ant': "ant",
|
'ant': "ant",
|
||||||
|
@ -423,7 +423,8 @@ def find_apksigner():
|
||||||
Returns the best version of apksigner following this algorithm:
|
Returns the best version of apksigner following this algorithm:
|
||||||
* use config['apksigner'] if set
|
* use config['apksigner'] if set
|
||||||
* try to find apksigner in path
|
* 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
|
:return: path to apksigner or None if no version is found
|
||||||
"""
|
"""
|
||||||
if set_command_in_config('apksigner'):
|
if set_command_in_config('apksigner'):
|
||||||
|
@ -434,7 +435,7 @@ def find_apksigner():
|
||||||
for f in sorted(os.listdir(build_tools_path), reverse=True):
|
for f in sorted(os.listdir(build_tools_path), reverse=True):
|
||||||
if not os.path.isdir(os.path.join(build_tools_path, f)):
|
if not os.path.isdir(os.path.join(build_tools_path, f)):
|
||||||
continue
|
continue
|
||||||
if LooseVersion(f) < LooseVersion(MINIMUM_AAPT_BUILD_TOOLS_VERSION):
|
if LooseVersion(f) < LooseVersion(MINIMUM_APKSIGNER_BUILD_TOOLS_VERSION):
|
||||||
return None
|
return None
|
||||||
if os.path.exists(os.path.join(build_tools_path, f, 'apksigner')):
|
if os.path.exists(os.path.join(build_tools_path, f, 'apksigner')):
|
||||||
apksigner = os.path.join(build_tools_path, f, 'apksigner')
|
apksigner = os.path.join(build_tools_path, f, 'apksigner')
|
||||||
|
@ -507,6 +508,7 @@ def test_aapt_version(aapt):
|
||||||
|
|
||||||
def test_sdk_exists(thisconfig):
|
def test_sdk_exists(thisconfig):
|
||||||
if 'sdk_path' not in 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']):
|
if 'aapt' in thisconfig and os.path.isfile(thisconfig['aapt']):
|
||||||
test_aapt_version(thisconfig['aapt'])
|
test_aapt_version(thisconfig['aapt'])
|
||||||
return True
|
return True
|
||||||
|
@ -2388,19 +2390,15 @@ def ensure_final_value(packageName, arsc, value):
|
||||||
return ''
|
return ''
|
||||||
|
|
||||||
|
|
||||||
def is_apk_and_debuggable_aapt(apkfile):
|
def is_apk_and_debuggable(apkfile):
|
||||||
p = SdkToolsPopen(['aapt', 'dump', 'xmltree', apkfile, 'AndroidManifest.xml'],
|
"""Returns True if the given file is an APK and is debuggable
|
||||||
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
|
|
||||||
|
|
||||||
|
Parse only <application android:debuggable=""> from the APK.
|
||||||
|
|
||||||
def is_apk_and_debuggable_androguard(apkfile):
|
:param apkfile: full path to the apk to check"""
|
||||||
"""Parse only <application android:debuggable=""> from the APK"""
|
|
||||||
|
if get_file_extension(apkfile) != 'apk':
|
||||||
|
return False
|
||||||
from androguard.core.bytecodes.axml import AXMLParser, format_value, START_TAG
|
from androguard.core.bytecodes.axml import AXMLParser, format_value, START_TAG
|
||||||
with ZipFile(apkfile) as apk:
|
with ZipFile(apkfile) as apk:
|
||||||
with apk.open('AndroidManifest.xml') as manifest:
|
with apk.open('AndroidManifest.xml') as manifest:
|
||||||
|
@ -2422,20 +2420,6 @@ def is_apk_and_debuggable_androguard(apkfile):
|
||||||
return False
|
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):
|
def get_apk_id(apkfile):
|
||||||
"""Extract identification information from APK.
|
"""Extract identification information from APK.
|
||||||
|
|
||||||
|
@ -2448,15 +2432,12 @@ def get_apk_id(apkfile):
|
||||||
:returns: triplet (appid, version code, version name)
|
:returns: triplet (appid, version code, version name)
|
||||||
|
|
||||||
"""
|
"""
|
||||||
if use_androguard():
|
try:
|
||||||
try:
|
return get_apk_id_androguard(apkfile)
|
||||||
return get_apk_id_androguard(apkfile)
|
except zipfile.BadZipFile as e:
|
||||||
except zipfile.BadZipFile as e:
|
logging.error(apkfile + ': ' + str(e))
|
||||||
logging.error(apkfile + ': ' + str(e))
|
if 'aapt' in config:
|
||||||
if 'aapt' in config:
|
return get_apk_id_aapt(apkfile)
|
||||||
return get_apk_id_aapt(apkfile)
|
|
||||||
else:
|
|
||||||
return get_apk_id_aapt(apkfile)
|
|
||||||
|
|
||||||
|
|
||||||
def get_apk_id_androguard(apkfile):
|
def get_apk_id_androguard(apkfile):
|
||||||
|
|
|
@ -75,46 +75,44 @@ def main():
|
||||||
# in ANDROID_HOME if that exists, otherwise None
|
# in ANDROID_HOME if that exists, otherwise None
|
||||||
if options.android_home is not None:
|
if options.android_home is not None:
|
||||||
test_config['sdk_path'] = options.android_home
|
test_config['sdk_path'] = options.android_home
|
||||||
elif common.use_androguard():
|
|
||||||
pass
|
|
||||||
elif not common.test_sdk_exists(test_config):
|
elif not common.test_sdk_exists(test_config):
|
||||||
if os.path.isfile('/usr/bin/aapt'):
|
# if neither --android-home nor the default sdk_path
|
||||||
# remove sdk_path and build_tools, they are not required
|
# exist, prompt the user using platform-specific default
|
||||||
test_config.pop('sdk_path', None)
|
# and if the user leaves it blank, ignore and move on.
|
||||||
test_config.pop('build_tools', None)
|
default_sdk_path = ''
|
||||||
# make sure at least aapt is found, since this can't do anything without it
|
if sys.platform == 'win32' or sys.platform == 'cygwin':
|
||||||
test_config['aapt'] = common.find_sdk_tools_cmd('aapt')
|
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:
|
else:
|
||||||
# if neither --android-home nor the default sdk_path
|
p = '/opt/android-sdk'
|
||||||
# exist, prompt the user using platform-specific default
|
if os.path.exists(p):
|
||||||
default_sdk_path = '/opt/android-sdk'
|
default_sdk_path = p
|
||||||
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
|
|
||||||
|
|
||||||
while not options.no_prompt:
|
while not options.no_prompt:
|
||||||
try:
|
try:
|
||||||
s = input(_('Enter the path to the Android SDK (%s) here:\n> ') % default_sdk_path)
|
s = input(_('Enter the path to the Android SDK (%s) here:\n> ') % default_sdk_path)
|
||||||
except KeyboardInterrupt:
|
except KeyboardInterrupt:
|
||||||
print('')
|
print('')
|
||||||
sys.exit(1)
|
sys.exit(1)
|
||||||
if re.match(r'^\s*$', s) is not None:
|
if re.match(r'^\s*$', s) is not None:
|
||||||
test_config['sdk_path'] = default_sdk_path
|
test_config['sdk_path'] = default_sdk_path
|
||||||
else:
|
else:
|
||||||
test_config['sdk_path'] = s
|
test_config['sdk_path'] = s
|
||||||
if common.test_sdk_exists(test_config):
|
if not default_sdk_path:
|
||||||
break
|
del(test_config['sdk_path'])
|
||||||
if (options.android_home is not None or not common.use_androguard()) \
|
break
|
||||||
and not common.test_sdk_exists(test_config):
|
if common.test_sdk_exists(test_config):
|
||||||
raise FDroidException("Android SDK not found.")
|
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'):
|
if not os.path.exists('config.py'):
|
||||||
# 'metadata' and 'tmp' are created in fdroid
|
# 'metadata' and 'tmp' are created in fdroid
|
||||||
|
@ -134,34 +132,13 @@ def main():
|
||||||
logging.info('Try running `fdroid init` in an empty directory.')
|
logging.info('Try running `fdroid init` in an empty directory.')
|
||||||
raise FDroidException('Repository already exists.')
|
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...
|
# now that we have a local config.py, read configuration...
|
||||||
config = common.read_config(options)
|
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
|
# the NDK is optional and there may be multiple versions of it, so it's
|
||||||
# left for the user to configure
|
# left for the user to configure
|
||||||
|
|
||||||
|
|
|
@ -49,7 +49,6 @@ from . import _
|
||||||
from . import common
|
from . import common
|
||||||
from . import index
|
from . import index
|
||||||
from . import metadata
|
from . import metadata
|
||||||
from .common import SdkToolsPopen
|
|
||||||
from .exception import BuildException, FDroidException
|
from .exception import BuildException, FDroidException
|
||||||
|
|
||||||
from PIL import Image, PngImagePlugin
|
from PIL import Image, PngImagePlugin
|
||||||
|
@ -1350,10 +1349,7 @@ def scan_apk(apk_file):
|
||||||
'antiFeatures': set(),
|
'antiFeatures': set(),
|
||||||
}
|
}
|
||||||
|
|
||||||
if common.use_androguard():
|
scan_apk_androguard(apk, apk_file)
|
||||||
scan_apk_androguard(apk, apk_file)
|
|
||||||
else:
|
|
||||||
scan_apk_aapt(apk, apk_file)
|
|
||||||
|
|
||||||
if not common.is_valid_package_name(apk['packageName']):
|
if not common.is_valid_package_name(apk['packageName']):
|
||||||
raise BuildException(_("{appid} from {path} is not a valid Java Package Name!")
|
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
|
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):
|
def _sanitize_sdk_version(value):
|
||||||
"""Sanitize the raw values from androguard to handle bad values
|
"""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}")
|
logging.error(_("Failed to get apk information, skipping {path}")
|
||||||
.format(path=apkfile))
|
.format(path=apkfile))
|
||||||
raise BuildException(_("Invalid APK"))
|
raise BuildException(_("Invalid APK"))
|
||||||
except ImportError:
|
|
||||||
raise FDroidException("androguard library is not installed and aapt not present")
|
|
||||||
except FileNotFoundError:
|
except FileNotFoundError:
|
||||||
logging.error(_("Could not open apk file for analysis"))
|
logging.error(_("Could not open apk file for analysis"))
|
||||||
raise BuildException(_("Invalid APK"))
|
raise BuildException(_("Invalid APK"))
|
||||||
|
|
|
@ -43,13 +43,6 @@ class BuildTest(unittest.TestCase):
|
||||||
print('no build-tools found: ' + build_tools)
|
print('no build-tools found: ' + build_tools)
|
||||||
return False
|
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):
|
def setUp(self):
|
||||||
logging.basicConfig(level=logging.DEBUG)
|
logging.basicConfig(level=logging.DEBUG)
|
||||||
self.basedir = os.path.join(localmodule, 'tests')
|
self.basedir = os.path.join(localmodule, 'tests')
|
||||||
|
|
|
@ -78,7 +78,7 @@ class CommonTest(unittest.TestCase):
|
||||||
for f in sorted(os.listdir(build_tools), reverse=True):
|
for f in sorted(os.listdir(build_tools), reverse=True):
|
||||||
versioned = os.path.join(build_tools, f)
|
versioned = os.path.join(build_tools, f)
|
||||||
if os.path.isdir(versioned) \
|
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
|
fdroidserver.common.config['build_tools'] = versioned
|
||||||
break
|
break
|
||||||
return True
|
return True
|
||||||
|
@ -148,11 +148,6 @@ class CommonTest(unittest.TestCase):
|
||||||
config = dict()
|
config = dict()
|
||||||
fdroidserver.common.fill_config_defaults(config)
|
fdroidserver.common.fill_config_defaults(config)
|
||||||
fdroidserver.common.config = 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
|
# these are set debuggable
|
||||||
testfiles = []
|
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-badsig.apk'))
|
||||||
testfiles.append(os.path.join(self.basedir, 'urzip-badcert.apk'))
|
testfiles.append(os.path.join(self.basedir, 'urzip-badcert.apk'))
|
||||||
for apkfile in testfiles:
|
for apkfile in testfiles:
|
||||||
debuggable = fdroidserver.common.is_apk_and_debuggable(apkfile)
|
self.assertTrue(fdroidserver.common.is_apk_and_debuggable(apkfile),
|
||||||
self.assertTrue(debuggable,
|
|
||||||
"debuggable APK state was not properly parsed!")
|
"debuggable APK state was not properly parsed!")
|
||||||
if 'aapt' in config:
|
|
||||||
self.assertTrue(fdroidserver.common.is_apk_and_debuggable_aapt(apkfile),
|
|
||||||
'aapt parsing missed <application android:debuggable="">!')
|
|
||||||
if fdroidserver.common.use_androguard():
|
|
||||||
self.assertTrue(fdroidserver.common.is_apk_and_debuggable_androguard(apkfile),
|
|
||||||
'androguard missed <application android:debuggable="">!')
|
|
||||||
|
|
||||||
# these are set NOT debuggable
|
# these are set NOT debuggable
|
||||||
testfiles = []
|
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, 'urzip-release-unsigned.apk'))
|
||||||
testfiles.append(os.path.join(self.basedir, 'v2.only.sig_2.apk'))
|
testfiles.append(os.path.join(self.basedir, 'v2.only.sig_2.apk'))
|
||||||
for apkfile in testfiles:
|
for apkfile in testfiles:
|
||||||
debuggable = fdroidserver.common.is_apk_and_debuggable(apkfile)
|
self.assertFalse(fdroidserver.common.is_apk_and_debuggable(apkfile),
|
||||||
self.assertFalse(debuggable,
|
|
||||||
"debuggable APK state was not properly parsed!")
|
"debuggable APK state was not properly parsed!")
|
||||||
if 'aapt' in config:
|
|
||||||
self.assertFalse(fdroidserver.common.is_apk_and_debuggable_aapt(apkfile),
|
|
||||||
'aapt parsing missed <application android:debuggable="">!')
|
|
||||||
if fdroidserver.common.use_androguard():
|
|
||||||
self.assertFalse(fdroidserver.common.is_apk_and_debuggable_androguard(apkfile),
|
|
||||||
'androguard missed <application android:debuggable="">!')
|
|
||||||
|
|
||||||
VALID_STRICT_PACKAGE_NAMES = [
|
VALID_STRICT_PACKAGE_NAMES = [
|
||||||
"An.stop",
|
"An.stop",
|
||||||
|
@ -765,16 +746,15 @@ class CommonTest(unittest.TestCase):
|
||||||
('repo/urzip-; Рахма́, [rɐxˈmanʲɪnəf] سيرجي_رخمانينوف 谢·.apk', 'info.guardianproject.urzip', '100', '0.1'),
|
('repo/urzip-; Рахма́, [rɐxˈmanʲɪnəf] سيرجي_رخمانينوف 谢·.apk', 'info.guardianproject.urzip', '100', '0.1'),
|
||||||
]
|
]
|
||||||
for apkfilename, appid, versionCode, versionName in testcases:
|
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:
|
if 'aapt' in config:
|
||||||
a, vc, vn = fdroidserver.common.get_apk_id_aapt(apkfilename)
|
a, vc, vn = fdroidserver.common.get_apk_id_aapt(apkfilename)
|
||||||
self.assertEqual(appid, a, 'aapt appid parsing failed for ' + apkfilename)
|
self.assertEqual(appid, a, 'aapt appid parsing failed for ' + apkfilename)
|
||||||
self.assertEqual(versionCode, vc, 'aapt versionCode parsing failed for ' + apkfilename)
|
self.assertEqual(versionCode, vc, 'aapt versionCode parsing failed for ' + apkfilename)
|
||||||
self.assertEqual(versionName, vn, 'aapt versionName 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):
|
with self.assertRaises(FDroidException):
|
||||||
fdroidserver.common.get_apk_id('nope')
|
fdroidserver.common.get_apk_id('nope')
|
||||||
|
|
|
@ -10,6 +10,7 @@ icons_src:
|
||||||
'-1': res/drawable/ic_launcher.png
|
'-1': res/drawable/ic_launcher.png
|
||||||
'160': res/drawable/ic_launcher.png
|
'160': res/drawable/ic_launcher.png
|
||||||
minSdkVersion: 4
|
minSdkVersion: 4
|
||||||
|
name: urzip
|
||||||
packageName: info.guardianproject.urzip
|
packageName: info.guardianproject.urzip
|
||||||
sig: e0ecb5fc2d63088e4a07ae410a127722
|
sig: e0ecb5fc2d63088e4a07ae410a127722
|
||||||
signer: 7eabd8c15de883d1e82b5df2fd4f7f769e498078e9ad6dc901f0e96db77ceac3
|
signer: 7eabd8c15de883d1e82b5df2fd4f7f769e498078e9ad6dc901f0e96db77ceac3
|
||||||
|
|
|
@ -14,6 +14,7 @@ icons_src:
|
||||||
'160': res/drawable-mdpi-v4/icon_launcher.png
|
'160': res/drawable-mdpi-v4/icon_launcher.png
|
||||||
'240': res/drawable-hdpi-v4/icon_launcher.png
|
'240': res/drawable-hdpi-v4/icon_launcher.png
|
||||||
minSdkVersion: 7
|
minSdkVersion: 7
|
||||||
|
name: Compass Keyboard
|
||||||
nativecode:
|
nativecode:
|
||||||
- arm64-v8a
|
- arm64-v8a
|
||||||
- armeabi
|
- armeabi
|
||||||
|
|
|
@ -24,8 +24,13 @@ copy_apks_into_repo() {
|
||||||
echo "$f --> repo/$apk"
|
echo "$f --> repo/$apk"
|
||||||
ln "$f" $1/repo/$apk || \
|
ln "$f" $1/repo/$apk || \
|
||||||
rsync -axv "$f" $1/repo/$apk # rsync if hard link is not possible
|
rsync -axv "$f" $1/repo/$apk # rsync if hard link is not possible
|
||||||
|
touch $1/.found-apks
|
||||||
fi
|
fi
|
||||||
done
|
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
|
set -x
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -458,98 +458,72 @@ class UpdateTest(unittest.TestCase):
|
||||||
fdroidserver.common.config = config
|
fdroidserver.common.config = config
|
||||||
fdroidserver.update.config = config
|
fdroidserver.update.config = config
|
||||||
os.chdir(os.path.join(localmodule, 'tests'))
|
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):
|
apksigner = fdroidserver.common.find_apksigner()
|
||||||
if use_androguard:
|
if apksigner:
|
||||||
try:
|
config['apksigner'] = apksigner
|
||||||
import androguard
|
apk_info = fdroidserver.update.scan_apk('v2.only.sig_2.apk')
|
||||||
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')
|
|
||||||
self.assertIsNone(apk_info.get('maxSdkVersion'))
|
self.assertIsNone(apk_info.get('maxSdkVersion'))
|
||||||
self.assertEqual(apk_info.get('versionName'), 'v1+2')
|
self.assertEqual(apk_info.get('versionName'), 'v2-only')
|
||||||
self.assertEqual(apk_info.get('versionCode'), 1020)
|
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')
|
apk_info = fdroidserver.update.scan_apk('repo/souch.smsbypass_9.apk')
|
||||||
self.assertIsNone(apk_info.get('maxSdkVersion'))
|
self.assertIsNone(apk_info.get('maxSdkVersion'))
|
||||||
self.assertEqual(apk_info.get('versionName'), '0.9')
|
self.assertEqual(apk_info.get('versionName'), '0.9')
|
||||||
|
|
||||||
apk_info = fdroidserver.update.scan_apk('repo/duplicate.permisssions_9999999.apk')
|
apk_info = fdroidserver.update.scan_apk('repo/duplicate.permisssions_9999999.apk')
|
||||||
self.assertEqual(apk_info.get('versionName'), '')
|
self.assertEqual(apk_info.get('versionName'), '')
|
||||||
self.assertEqual(apk_info['icons_src'], {'160': 'res/drawable/ic_launcher.png',
|
self.assertEqual(apk_info['icons_src'], {'160': 'res/drawable/ic_launcher.png',
|
||||||
'-1': 'res/drawable/ic_launcher.png'})
|
'-1': 'res/drawable/ic_launcher.png'})
|
||||||
|
|
||||||
apk_info = fdroidserver.update.scan_apk('org.dyndns.fules.ck_20.apk')
|
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',
|
self.assertEqual(apk_info['icons_src'], {'240': 'res/drawable-hdpi-v4/icon_launcher.png',
|
||||||
'120': 'res/drawable-ldpi-v4/icon_launcher.png',
|
'120': 'res/drawable-ldpi-v4/icon_launcher.png',
|
||||||
'160': 'res/drawable-mdpi-v4/icon_launcher.png',
|
'160': 'res/drawable-mdpi-v4/icon_launcher.png',
|
||||||
'-1': 'res/drawable-mdpi-v4/icon_launcher.png'})
|
'-1': 'res/drawable-mdpi-v4/icon_launcher.png'})
|
||||||
self.assertEqual(apk_info['icons'], {})
|
self.assertEqual(apk_info['icons'], {})
|
||||||
self.assertEqual(apk_info['features'], [])
|
self.assertEqual(apk_info['features'], [])
|
||||||
self.assertEqual(apk_info['antiFeatures'], set())
|
self.assertEqual(apk_info['antiFeatures'], set())
|
||||||
self.assertEqual(apk_info['versionName'], 'v1.6pre2')
|
self.assertEqual(apk_info['versionName'], 'v1.6pre2')
|
||||||
self.assertEqual(apk_info['hash'],
|
self.assertEqual(apk_info['hash'],
|
||||||
'897486e1f857c6c0ee32ccbad0e1b8cd82f6d0e65a44a23f13f852d2b63a18c8')
|
'897486e1f857c6c0ee32ccbad0e1b8cd82f6d0e65a44a23f13f852d2b63a18c8')
|
||||||
self.assertEqual(apk_info['packageName'], 'org.dyndns.fules.ck')
|
self.assertEqual(apk_info['packageName'], 'org.dyndns.fules.ck')
|
||||||
self.assertEqual(apk_info['versionCode'], 20)
|
self.assertEqual(apk_info['versionCode'], 20)
|
||||||
self.assertEqual(apk_info['size'], 132453)
|
self.assertEqual(apk_info['size'], 132453)
|
||||||
self.assertEqual(apk_info['nativecode'],
|
self.assertEqual(apk_info['nativecode'],
|
||||||
['arm64-v8a', 'armeabi', 'armeabi-v7a', 'mips', 'mips64', 'x86', 'x86_64'])
|
['arm64-v8a', 'armeabi', 'armeabi-v7a', 'mips', 'mips64', 'x86', 'x86_64'])
|
||||||
self.assertEqual(apk_info['minSdkVersion'], 7)
|
self.assertEqual(apk_info['minSdkVersion'], 7)
|
||||||
self.assertEqual(apk_info['sig'], '9bf7a6a67f95688daec75eab4b1436ac')
|
self.assertEqual(apk_info['sig'], '9bf7a6a67f95688daec75eab4b1436ac')
|
||||||
self.assertEqual(apk_info['hashType'], 'sha256')
|
self.assertEqual(apk_info['hashType'], 'sha256')
|
||||||
self.assertEqual(apk_info['targetSdkVersion'], 8)
|
self.assertEqual(apk_info['targetSdkVersion'], 8)
|
||||||
|
|
||||||
apk_info = fdroidserver.update.scan_apk('org.bitbucket.tickytacky.mirrormirror_4.apk')
|
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.get('versionName'), '1.0.3')
|
||||||
self.assertEqual(apk_info['icons_src'], {'160': 'res/drawable-mdpi/mirror.png',
|
self.assertEqual(apk_info['icons_src'], {'160': 'res/drawable-mdpi/mirror.png',
|
||||||
'-1': 'res/drawable-mdpi/mirror.png'})
|
'-1': 'res/drawable-mdpi/mirror.png'})
|
||||||
|
|
||||||
apk_info = fdroidserver.update.scan_apk('repo/info.zwanenburg.caffeinetile_4.apk')
|
apk_info = fdroidserver.update.scan_apk('repo/info.zwanenburg.caffeinetile_4.apk')
|
||||||
self.assertEqual(apk_info.get('versionName'), '1.3')
|
self.assertEqual(apk_info.get('versionName'), '1.3')
|
||||||
self.assertEqual(apk_info['icons_src'], {'160': 'res/drawable/ic_coffee_on.xml',
|
self.assertEqual(apk_info['icons_src'], {'160': 'res/drawable/ic_coffee_on.xml',
|
||||||
'-1': 'res/drawable/ic_coffee_on.xml'})
|
'-1': 'res/drawable/ic_coffee_on.xml'})
|
||||||
|
|
||||||
apk_info = fdroidserver.update.scan_apk('repo/com.politedroid_6.apk')
|
apk_info = fdroidserver.update.scan_apk('repo/com.politedroid_6.apk')
|
||||||
self.assertEqual(apk_info.get('versionName'), '1.5')
|
self.assertEqual(apk_info.get('versionName'), '1.5')
|
||||||
self.assertEqual(apk_info['icons_src'], {'120': 'res/drawable-ldpi-v4/icon.png',
|
self.assertEqual(apk_info['icons_src'], {'120': 'res/drawable-ldpi-v4/icon.png',
|
||||||
'160': 'res/drawable-mdpi-v4/icon.png',
|
'160': 'res/drawable-mdpi-v4/icon.png',
|
||||||
'240': 'res/drawable-hdpi-v4/icon.png',
|
'240': 'res/drawable-hdpi-v4/icon.png',
|
||||||
'320': 'res/drawable-xhdpi-v4/icon.png',
|
'320': 'res/drawable-xhdpi-v4/icon.png',
|
||||||
'-1': 'res/drawable-mdpi-v4/icon.png'})
|
'-1': 'res/drawable-mdpi-v4/icon.png'})
|
||||||
|
|
||||||
apk_info = fdroidserver.update.scan_apk('SpeedoMeterApp.main_1.apk')
|
apk_info = fdroidserver.update.scan_apk('SpeedoMeterApp.main_1.apk')
|
||||||
self.assertEqual(apk_info.get('versionName'), '1.0')
|
self.assertEqual(apk_info.get('versionName'), '1.0')
|
||||||
self.assertEqual(apk_info['icons_src'], {})
|
self.assertEqual(apk_info['icons_src'], {})
|
||||||
|
|
||||||
def test_scan_apk_no_min_target(self):
|
def test_scan_apk_no_min_target(self):
|
||||||
config = dict()
|
config = dict()
|
||||||
|
@ -627,8 +601,6 @@ class UpdateTest(unittest.TestCase):
|
||||||
# Don't care about the date added to the repo and relative apkName
|
# Don't care about the date added to the repo and relative apkName
|
||||||
del apk['added']
|
del apk['added']
|
||||||
del apk['apkName']
|
del apk['apkName']
|
||||||
# avoid AAPT application name bug
|
|
||||||
del apk['name']
|
|
||||||
|
|
||||||
# ensure that icons have been extracted properly
|
# ensure that icons have been extracted properly
|
||||||
if apkName == '../urzip.apk':
|
if apkName == '../urzip.apk':
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue