# Copyright 2016 The Chromium Authors. All rights reserved. # Use of this source code is governed by a BSD-style license that can be # found in the LICENSE file. import os import shutil import tempfile from xml.etree import ElementTree from devil.utils import cmd_helper from pylib import constants DEXDUMP_PATH = os.path.join(constants.ANDROID_SDK_TOOLS, 'dexdump') def Dump(apk_path): """Dumps class and method information from a APK into a dict via dexdump. Args: apk_path: An absolute path to an APK file to dump. Returns: A dict in the following format: { : { 'classes': { : { 'methods': [, ] } } } } """ # TODO(mikecase): Support multi-dex try: dexfile_dir = tempfile.mkdtemp() # Python zipfile module is unable to unzip APKs. cmd_helper.RunCmd(['unzip', apk_path, 'classes.dex'], cwd=dexfile_dir) dexfile = os.path.join(dexfile_dir, 'classes.dex') output_xml = cmd_helper.GetCmdOutput([DEXDUMP_PATH, '-l', 'xml', dexfile]) return _ParseRootNode(ElementTree.fromstring(output_xml)) finally: shutil.rmtree(dexfile_dir) def _ParseRootNode(root): """Parses the XML output of dexdump. This output is in the following format. This is a subset of the information contained within dexdump output. """ results = {} for child in root: if child.tag == 'package': package_name = child.attrib['name'] parsed_node = _ParsePackageNode(child) if package_name in results: results[package_name]['classes'].update(parsed_node['classes']) else: results[package_name] = parsed_node return results def _ParsePackageNode(package_node): """Parses a node from the dexdump xml output. Returns: A dict in the format: { 'classes': { : { 'methods': [, ] }, : { 'methods': [, ] }, } } """ classes = {} for child in package_node: if child.tag == 'class': classes[child.attrib['name']] = _ParseClassNode(child) return {'classes': classes} def _ParseClassNode(class_node): """Parses a node from the dexdump xml output. Returns: A dict in the format: { 'methods': [, ] } """ methods = [] for child in class_node: if child.tag == 'method': methods.append(child.attrib['name']) return {'methods': methods, 'superclass': class_node.attrib['extends']}