summaryrefslogtreecommitdiff
path: root/systrace/catapult/devil/devil/android/apk_helper.py
diff options
context:
space:
mode:
Diffstat (limited to 'systrace/catapult/devil/devil/android/apk_helper.py')
-rw-r--r--systrace/catapult/devil/devil/android/apk_helper.py150
1 files changed, 5 insertions, 145 deletions
diff --git a/systrace/catapult/devil/devil/android/apk_helper.py b/systrace/catapult/devil/devil/android/apk_helper.py
index abdf907..8acb41e 100644
--- a/systrace/catapult/devil/devil/android/apk_helper.py
+++ b/systrace/catapult/devil/devil/android/apk_helper.py
@@ -5,13 +5,9 @@
"""Module containing utilities for apk packages."""
import re
-import xml.etree.ElementTree
-import zipfile
from devil import base_error
-from devil.android.ndk import abis
from devil.android.sdk import aapt
-from devil.utils import cmd_helper
_MANIFEST_ATTRIBUTE_RE = re.compile(
@@ -48,8 +44,9 @@ def ToHelper(path_or_helper):
# matches the height of the stack). Each line parsed (either an attribute or an
# element) is added to the node at the top of the stack (after the stack has
# been popped/pushed due to indentation).
-def _ParseManifestFromApk(apk):
- aapt_output = aapt.Dump('xmltree', apk.path, 'AndroidManifest.xml')
+def _ParseManifestFromApk(apk_path):
+ aapt_output = aapt.Dump('xmltree', apk_path, 'AndroidManifest.xml')
+
parsed_manifest = {}
node_stack = [parsed_manifest]
indent = ' '
@@ -98,8 +95,7 @@ def _ParseManifestFromApk(apk):
manifest_key = m.group(1)
if manifest_key in node:
raise base_error.BaseError(
- "A single attribute should have one key and one value: {}"
- .format(line))
+ "A single attribute should have one key and one value")
else:
node[manifest_key] = m.group(2) or m.group(3)
continue
@@ -107,47 +103,6 @@ def _ParseManifestFromApk(apk):
return parsed_manifest
-def _ParseManifestFromBundle(bundle):
- cmd = [bundle.path, 'dump-manifest']
- status, stdout, stderr = cmd_helper.GetCmdStatusOutputAndError(cmd)
- if status != 0:
- raise Exception('Failed running {} with output\n{}\n{}'.format(
- ' '.join(cmd), stdout, stderr))
- return ParseManifestFromXml(stdout)
-
-
-def ParseManifestFromXml(xml_str):
- """Parse an android bundle manifest.
-
- As ParseManifestFromAapt, but uses the xml output from bundletool. Each
- element is a dict, mapping attribute or children by name. Attributes map to
- a dict (as they are unique), children map to a list of dicts (as there may
- be multiple children with the same name).
-
- Args:
- xml_str (str) An xml string that is an android manifest.
-
- Returns:
- A dict holding the parsed manifest, as with ParseManifestFromAapt.
- """
- root = xml.etree.ElementTree.fromstring(xml_str)
- return {root.tag: [_ParseManifestXMLNode(root)]}
-
-
-def _ParseManifestXMLNode(node):
- out = {}
- for name, value in node.attrib.items():
- cleaned_name = name.replace(
- '{http://schemas.android.com/apk/res/android}',
- 'android:').replace(
- '{http://schemas.android.com/tools}',
- 'tools:')
- out[cleaned_name] = value
- for child in node:
- out.setdefault(child.tag, []).append(_ParseManifestXMLNode(child))
- return out
-
-
def _ParseNumericKey(obj, key, default=0):
val = obj.get(key)
if val is None:
@@ -196,10 +151,6 @@ class ApkHelper(object):
def path(self):
return self._apk_path
- @property
- def is_bundle(self):
- return self._apk_path.endswith('_bundle')
-
def GetActivityName(self):
"""Returns the name of the first launcher Activity in the apk."""
manifest_info = self._GetManifest()
@@ -281,73 +232,9 @@ class ApkHelper(object):
except KeyError:
return []
- def GetVersionCode(self):
- """Returns the versionCode as an integer, or None if not available."""
- manifest_info = self._GetManifest()
- try:
- version_code = manifest_info['manifest'][0]['android:versionCode']
- return int(version_code, 16)
- except KeyError:
- return None
-
- def GetVersionName(self):
- """Returns the versionName as a string."""
- manifest_info = self._GetManifest()
- try:
- version_name = manifest_info['manifest'][0]['android:versionName']
- return version_name
- except KeyError:
- return ''
-
- def GetMinSdkVersion(self):
- """Returns the minSdkVersion as a string, or None if not available.
-
- Note: this cannot always be cast to an integer."""
- manifest_info = self._GetManifest()
- try:
- uses_sdk = manifest_info['manifest'][0]['uses-sdk'][0]
- min_sdk_version = uses_sdk['android:minSdkVersion']
- try:
- # The common case is for this to be an integer. Convert to decimal
- # notation (rather than hexadecimal) for readability, but convert back
- # to a string for type consistency with the general case.
- return str(int(min_sdk_version, 16))
- except ValueError:
- # In general (ex. apps with minSdkVersion set to pre-release Android
- # versions), minSdkVersion can be a string (usually, the OS codename
- # letter). For simplicity, don't do any validation on the value.
- return min_sdk_version
- except KeyError:
- return None
-
- def GetTargetSdkVersion(self):
- """Returns the targetSdkVersion as a string, or None if not available.
-
- Note: this cannot always be cast to an integer."""
- manifest_info = self._GetManifest()
- try:
- uses_sdk = manifest_info['manifest'][0]['uses-sdk'][0]
- target_sdk_version = uses_sdk['android:targetSdkVersion']
- try:
- # The common case is for this to be an integer. Convert to decimal
- # notation (rather than hexadecimal) for readability, but convert back
- # to a string for type consistency with the general case.
- return str(int(target_sdk_version, 16))
- except ValueError:
- # In general (ex. apps targeting pre-release Android versions),
- # targetSdkVersion can be a string (usually, the OS codename letter).
- # For simplicity, don't do any validation on the value.
- return target_sdk_version
- except KeyError:
- return None
-
def _GetManifest(self):
if not self._manifest:
- app = ToHelper(self._apk_path)
- if app.is_bundle:
- self._manifest = _ParseManifestFromBundle(app)
- else:
- self._manifest = _ParseManifestFromApk(app)
+ self._manifest = _ParseManifestFromApk(self._apk_path)
return self._manifest
def _ResolveName(self, name):
@@ -355,30 +242,3 @@ class ApkHelper(object):
if '.' not in name:
return '%s.%s' % (self.GetPackageName(), name)
return name
-
- def _ListApkPaths(self):
- with zipfile.ZipFile(self._apk_path) as z:
- return z.namelist()
-
- def GetAbis(self):
- """Returns a list of ABIs in the apk (empty list if no native code)."""
- # Use lib/* to determine the compatible ABIs.
- libs = set()
- for path in self._ListApkPaths():
- path_tokens = path.split('/')
- if len(path_tokens) >= 2 and path_tokens[0] == 'lib':
- libs.add(path_tokens[1])
- lib_to_abi = {
- abis.ARM: [abis.ARM, abis.ARM_64],
- abis.ARM_64: [abis.ARM_64],
- abis.X86: [abis.X86, abis.X86_64],
- abis.X86_64: [abis.X86_64]
- }
- try:
- output = set()
- for lib in libs:
- for abi in lib_to_abi[lib]:
- output.add(abi)
- return sorted(output)
- except KeyError:
- raise base_error.BaseError('Unexpected ABI in lib/* folder.')