APK_NAME_PAT = re.compile(".*name='([a-zA-Z0-9._]*)'.*")
APK_VERCODE_PAT = re.compile(".*versionCode='([0-9]*)'.*")
APK_VERNAME_PAT = re.compile(".*versionName='([^']*)'.*")
-APK_LABEL_PAT = re.compile(".*label='(.*?)'(\n| [a-z]*?=).*")
-APK_ICON_PAT = re.compile(".*application-icon-([0-9]+):'([^']+?)'.*")
-APK_ICON_PAT_NODPI = re.compile(".*icon='([^']+?)'.*")
+APK_LABEL_ICON_PAT = re.compile(".*\s+label='(.*)'\s+icon='(.*?)'")
APK_SDK_VERSION_PAT = re.compile(".*'([0-9]*)'.*")
APK_PERMISSION_PAT = \
re.compile(".*(name='(?P<name>.*?)')(.*maxSdkVersion='(?P<maxSdkVersion>.*?)')?.*")
APK_FEATURE_PAT = re.compile(".*name='([^']*)'.*")
screen_densities = ['65534', '640', '480', '320', '240', '160', '120']
+# resolutions must end with 'dpi'
screen_resolutions = {
"xxxhdpi": '640',
"xxhdpi": '480',
"hdpi": '240',
"mdpi": '160',
"ldpi": '120',
- "undefined": '-1',
+ "tvdpi": '213',
+ "undefineddpi": '-1',
"anydpi": '65534',
"nodpi": '65535'
}
# Make a redirect from the name to the ID too, unless there's
# already an existing page with the name and it isn't a redirect.
noclobber = False
- apppagename = app.Name.replace('_', ' ')
- apppagename = apppagename.replace('{', '')
- apppagename = apppagename.replace('}', ' ')
- apppagename = apppagename.replace(':', ' ')
- apppagename = apppagename.replace('[', ' ')
- apppagename = apppagename.replace(']', ' ')
+ apppagename = app.Name
+ for ch in '_{}:[]|':
+ apppagename = apppagename.replace(ch, ' ')
# Drop double spaces caused mostly by replacing ':' above
apppagename = apppagename.replace(' ', ' ')
for expagename in site.allpages(prefix=apppagename,
for page in catpages:
existingpages.append(page.name)
if page.name in genp:
- pagetxt = page.edit()
+ pagetxt = page.text()
if pagetxt != genp[page.name]:
logging.debug("Updating modified page " + page.name)
page.save(genp[page.name], summary='Auto-updated')
txt += "* command line: <code>" + ' '.join(sys.argv) + "</code>\n"
txt += "* started at " + common.get_wiki_timestamp(start_timestamp) + '\n'
txt += "* completed at " + common.get_wiki_timestamp() + '\n'
+ txt += common.get_git_describe_link()
txt += "\n\n"
txt += common.get_android_tools_version_log()
newpage.save(txt, summary='Run log')
repo_file['hash'] = shasum
repo_file['hashType'] = 'sha256'
repo_file['versionCode'] = 0
- repo_file['versionName'] = shasum
+ repo_file['versionName'] = shasum[0:7]
# the static ID is the SHA256 unless it is set in the metadata
repo_file['packageName'] = shasum
'antiFeatures': set(),
}
- if SdkToolsPopen(['aapt', 'version'], output=False):
- scan_apk_aapt(apk, apk_file)
- else:
+ if common.use_androguard():
scan_apk_androguard(apk, apk_file)
+ else:
+ scan_apk_aapt(apk, apk_file)
# Get the signature, or rather the signing key fingerprints
logging.debug('Getting signature of {0}'.format(os.path.basename(apk_file)))
if 'minSdkVersion' not in apk:
logging.warning("No SDK version information found in {0}".format(apk_file))
- apk['minSdkVersion'] = 1
+ apk['minSdkVersion'] = 3 # aapt defaults to 3 as the min
+ if 'targetSdkVersion' not in apk:
+ apk['targetSdkVersion'] = apk['minSdkVersion']
# Check for known vulnerabilities
if has_known_vulnerability(apk_file):
return apk
+def _get_apk_icons_src(apkfile, icon_name):
+ """Extract the paths to the app icon in all available densities
+
+ """
+ icons_src = dict()
+ density_re = re.compile('^res/(.*)/{}\.(png|xml)$'.format(icon_name))
+ with zipfile.ZipFile(apkfile) as zf:
+ for filename in zf.namelist():
+ m = density_re.match(filename)
+ if m:
+ folder = m.group(1).split('-')
+ if len(folder) > 1 and folder[1].endswith('dpi'):
+ density = screen_resolutions[folder[1]]
+ else:
+ density = '160'
+ icons_src[density] = m.group(0)
+ if icons_src.get('-1') is None and '160' in icons_src:
+ icons_src['-1'] = icons_src['160']
+ return icons_src
+
+
def scan_apk_aapt(apk, apkfile):
p = SdkToolsPopen(['aapt', 'dump', 'badging', apkfile], output=False)
if p.returncode != 0:
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:
except Exception as e:
raise FDroidException("Package matching failed: " + str(e) + "\nLine was: " + line)
elif line.startswith("application:"):
- apk['name'] = re.match(APK_LABEL_PAT, line).group(1)
- # Keep path to non-dpi icon in case we need it
- match = re.match(APK_ICON_PAT_NODPI, line)
- if match:
- apk['icons_src']['-1'] = match.group(1)
- elif line.startswith("launchable-activity:"):
+ 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
- if not apk['name']:
- apk['name'] = re.match(APK_LABEL_PAT, line).group(1)
- if '-1' not in apk['icons_src']:
- match = re.match(APK_ICON_PAT_NODPI, line)
- if match:
- apk['icons_src']['-1'] = match.group(1)
- elif line.startswith("application-icon-"):
- match = re.match(APK_ICON_PAT, line)
- if match:
- density = match.group(1)
- path = match.group(2)
- apk['icons_src'][density] = path
+ 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:
+ ' is not a valid minSdkVersion!')
else:
apk['minSdkVersion'] = m.group(1)
- # if target not set, default to min
- if 'targetSdkVersion' not in apk:
- apk['targetSdkVersion'] = m.group(1)
elif line.startswith("targetSdkVersion:"):
m = re.match(APK_SDK_VERSION_PAT, line)
if m is None:
if feature.startswith("android.feature."):
feature = feature[16:]
apk['features'].add(feature)
+ apk['icons_src'] = _get_apk_icons_src(apkfile, icon_name)
def scan_apk_androguard(apk, apkfile):
apk['packageName'] = apkobject.get_package()
apk['versionCode'] = int(apkobject.get_androidversion_code())
- apk['versionName'] = apkobject.get_androidversion_name()
- if apk['versionName'][0] == "@":
- version_id = int(apk['versionName'].replace("@", "0x"), 16)
- version_id = arsc.get_id(apk['packageName'], version_id)[1]
- apk['versionName'] = arsc.get_string(apk['packageName'], version_id)[1]
apk['name'] = apkobject.get_app_name()
+ versionName = apkobject.get_androidversion_name()
+ if versionName:
+ apk['versionName'] = versionName
+ try: # can be a literal value or a resId
+ res_id = int(versionName.replace("@", "0x"), 16)
+ res_id = arsc.get_id(apk['packageName'], res_id)[1]
+ apk['versionName'] = arsc.get_string(apk['packageName'], res_id)[1]
+ except ValueError:
+ pass
+
if apkobject.get_max_sdk_version() is not None:
apk['maxSdkVersion'] = apkobject.get_max_sdk_version()
- apk['minSdkVersion'] = apkobject.get_min_sdk_version()
- apk['targetSdkVersion'] = apkobject.get_target_sdk_version()
-
- icon_id = int(apkobject.get_element("application", "icon").replace("@", "0x"), 16)
- icon_name = arsc.get_id(apk['packageName'], icon_id)[1]
-
- density_re = re.compile("^res/(.*)/" + icon_name + ".*$")
-
- for file in apkobject.get_files():
- d_re = density_re.match(file)
- if d_re:
- folder = d_re.group(1).split('-')
- if len(folder) > 1:
- resolution = folder[1]
- else:
- resolution = 'mdpi'
- density = screen_resolutions[resolution]
- apk['icons_src'][density] = d_re.group(0)
-
- if apk['icons_src'].get('-1') is None:
- apk['icons_src']['-1'] = apk['icons_src']['160']
+ if apkobject.get_min_sdk_version() is not None:
+ apk['minSdkVersion'] = apkobject.get_min_sdk_version()
+ if apkobject.get_target_sdk_version() is not None:
+ apk['targetSdkVersion'] = apkobject.get_target_sdk_version()
+
+ icon_id_str = apkobject.get_element("application", "icon")
+ if icon_id_str:
+ icon_id = int(icon_id_str.replace("@", "0x"), 16)
+ resource_id = arsc.get_id(apk['packageName'], icon_id)
+ if resource_id:
+ icon_name = arsc.get_id(apk['packageName'], icon_id)[1]
+ else:
+ icon_name = os.path.splitext(os.path.basename(apkobject.get_app_icon()))[0]
+ apk['icons_src'] = _get_apk_icons_src(apkfile, icon_name)
arch_re = re.compile("^lib/(.*)/.*$")
arch = set([arch_re.match(file).group(1) for file in apkobject.get_files() if arch_re.match(file)])
apk['nativecode'].extend(sorted(list(arch)))
xml = apkobject.get_android_manifest_xml()
-
- for item in xml.getElementsByTagName('uses-permission'):
- name = str(item.getAttribute("android:name"))
- maxSdkVersion = item.getAttribute("android:maxSdkVersion")
- maxSdkVersion = None if maxSdkVersion is '' else int(maxSdkVersion)
+ xmlns = xml.nsmap.get('android')
+ if not xmlns:
+ xmlns = 'http://schemas.android.com/apk/res/android'
+
+ for item in xml.findall('uses-permission'):
+ name = str(item.attrib['{' + xmlns + '}name'])
+ maxSdkVersion = item.attrib.get('{' + xmlns + '}maxSdkVersion')
+ maxSdkVersion = int(maxSdkVersion) if maxSdkVersion else None
+ permission = UsesPermission(
+ name,
+ maxSdkVersion
+ )
+ apk['uses-permission'].append(permission)
+ for name, maxSdkVersion in apkobject.get_uses_implied_permission_list():
permission = UsesPermission(
name,
maxSdkVersion
)
apk['uses-permission'].append(permission)
- for item in xml.getElementsByTagName('uses-permission-sdk-23'):
- name = str(item.getAttribute("android:name"))
- maxSdkVersion = item.getAttribute("android:maxSdkVersion")
- maxSdkVersion = None if maxSdkVersion is '' else int(maxSdkVersion)
+ for item in xml.findall('uses-permission-sdk-23'):
+ name = str(item.attrib['{' + xmlns + '}name'])
+ maxSdkVersion = item.attrib.get('{' + xmlns + '}maxSdkVersion')
+ maxSdkVersion = int(maxSdkVersion) if maxSdkVersion else None
permission_sdk_23 = UsesPermissionSdk23(
name,
maxSdkVersion
)
apk['uses-permission-sdk-23'].append(permission_sdk_23)
- for item in xml.getElementsByTagName('uses-feature'):
- feature = str(item.getAttribute("android:name"))
+ for item in xml.findall('uses-feature'):
+ key = '{' + xmlns + '}name'
+ if key not in item.attrib:
+ continue
+ feature = str(item.attrib[key])
if feature != "android.hardware.screen.portrait" \
and feature != "android.hardware.screen.landscape":
if feature.startswith("android.feature."):
feature = feature[16:]
- apk['features'].append(feature)
+ required = item.attrib.get('{' + xmlns + '}required')
+ if required is None or required == 'true':
+ apk['features'].append(feature)
def process_apk(apkcache, apkfilename, repodir, knownapks, use_date_from_apk=False,
return True, None, False
# Check for debuggable apks...
- if common.isApkAndDebuggable(apkfile):
+ if common.is_apk_and_debuggable(apkfile):
logging.warning('{0} is set to android:debuggable="true"'.format(apkfile))
if options.rename_apks:
if density in apk['icons']:
break
if density == screen_densities[-1] or dpi >= int(density):
- apk['icons'][density] = icon_filename
+ apk['icons'][density] = icon_filename + icon_type
shutil.move(icon_path,
- os.path.join(get_icon_dir(repo_dir, density), icon_filename))
+ os.path.join(get_icon_dir(repo_dir, density), icon_filename + icon_type))
empty_densities.remove(density)
break
except Exception as e: