summaryrefslogtreecommitdiffstats
path: root/tools
diff options
context:
space:
mode:
authormarkdr <markdr@google.com>2017-12-11 20:15:03 -0800
committermarkdr <markdr@google.com>2018-01-26 14:31:33 -0800
commit9066da53f5e9963dd0e1ae9a1e8925c0e4084f0e (patch)
tree5f63f2e3725377feaa31685e64fa717fa8e5fdc9 /tools
parent6151e1a1ade62f4a21778c08853590a847db0487 (diff)
downloadandroid_development-9066da53f5e9963dd0e1ae9a1e8925c0e4084f0e.tar.gz
android_development-9066da53f5e9963dd0e1ae9a1e8925c0e4084f0e.tar.bz2
android_development-9066da53f5e9963dd0e1ae9a1e8925c0e4084f0e.zip
Made privapp_permissions.py more dynamic.
This was done largely so automation can make use of privapp-permissions.xml generation. Privapp_permissions.py no longer requires the build environment to be setup. Instead, system files can be pulled from a connected device, and aapt & adb can be set via the commandline. Otherwise, they will default to the environment's default aapt/adb. Priv-apps and the permission lists can be queried from the device using command flags as well, removing the need for an Android image to be built locally. Bug: None Test: Diff between previous privapp_permissions output and the output for these changes was empty for the following runs: ./old_privapp_permissions.py vs ./new_privapp_permissions.py [-d && -s SERIAL] ./new_privapp_permissions.py --aapt /path/to/aapt ./old_privapp_permissions.py /path/to.apk vs ./new_privapp_permissions.py /path/to.apk ./new_privapp_permissions.py device:/path/to/apk Change-Id: Ifaf35607d38c1d74111fd2f05628c0192fc791cb
Diffstat (limited to 'tools')
-rw-r--r--tools/privapp_permissions/OWNERS2
-rwxr-xr-xtools/privapp_permissions/privapp_permissions.py614
2 files changed, 486 insertions, 130 deletions
diff --git a/tools/privapp_permissions/OWNERS b/tools/privapp_permissions/OWNERS
new file mode 100644
index 000000000..3c0eadc7f
--- /dev/null
+++ b/tools/privapp_permissions/OWNERS
@@ -0,0 +1,2 @@
+fkupolov@google.com
+markdr@google.com
diff --git a/tools/privapp_permissions/privapp_permissions.py b/tools/privapp_permissions/privapp_permissions.py
index 4e6adf183..8a9510a16 100755
--- a/tools/privapp_permissions/privapp_permissions.py
+++ b/tools/privapp_permissions/privapp_permissions.py
@@ -1,67 +1,425 @@
#!/usr/bin/env python
-
-#
-# Copyright 2016, The Android Open Source Project
#
-# Licensed under the Apache License, Version 2.0 (the "License");
-# you may not use this file except in compliance with the License.
-# You may obtain a copy of the License at
+# Copyright 2017 - The Android Open Source Project
#
-# http://www.apache.org/licenses/LICENSE-2.0
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS,
-# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-# See the License for the specific language governing permissions and
-# limitations under the License.
+# http://www.apache.org/licenses/LICENSE-2.0
#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
-"""
- privapp_permission.py: Generates privapp-permissions.xml file for
- apps in system/priv-app directory
-
- Usage:
- . build/envsetup.sh
- lunch product_name
- m -j32
- development/tools/privapp_permissions/privapp_permissions.py [package_name]
-
-"""
+from __future__ import print_function
+from xml.dom import minidom
+import argparse
+import itertools
import os
import re
import subprocess
import sys
-from xml.dom import minidom
+import tempfile
+import shutil
+
+DEVICE_PREFIX = 'device:'
+ANDROID_NAME_REGEX = r'A: android:name\([\S]+\)=\"([\S]+)\"'
+ANDROID_PROTECTION_LEVEL_REGEX = \
+ r'A: android:protectionLevel\([^\)]+\)=\(type [\S]+\)0x([\S]+)'
+BASE_XML_FILENAME = 'privapp-permissions-platform.xml'
+
+HELP_MESSAGE = """\
+Generates privapp-permissions.xml file for priv-apps.
+
+Usage:
+ Specify which apk to generate priv-app permissions for. If no apk is \
+specified, this will default to all APKs under "<ANDROID_PRODUCT_OUT>/ \
+system/priv-app".
+
+Examples:
+
+ For all APKs under $ANDROID_PRODUCT_OUT:
+ # If the build environment has not been set up, do so:
+ . build/envsetup.sh
+ lunch product_name
+ m -j32
+ # then use:
+ cd development/tools/privapp_permissions/
+ ./privapp_permissions.py
+
+ For a given apk:
+ ./privapp_permissions.py path/to/the.apk
+
+ For an APK already on the device:
+ ./privapp_permissions.py device:/device/path/to/the.apk
+
+ For all APKs on a device:
+ ./privapp_permissions.py -d
+ # or if more than one device is attached
+ ./privapp_permissions.py -s <ANDROID_SERIAL>\
+"""
+
+# An array of all generated temp directories.
+temp_dirs = []
+# An array of all generated temp files.
+temp_files = []
+
+
+class MissingResourceError(Exception):
+ """Raised when a dependency cannot be located."""
+
+
+class Adb(object):
+ """A small wrapper around ADB calls."""
+
+ def __init__(self, path, serial=None):
+ self.path = path
+ self.serial = serial
-try:
- ANDROID_PRODUCT_OUT = os.environ['ANDROID_PRODUCT_OUT']
- ANDROID_HOST_OUT = os.environ['ANDROID_HOST_OUT']
-except KeyError as e:
- exit("Build environment not set up - " + str(e))
-BASE_XML_FNAME = "privapp-permissions-platform.xml"
+ def pull(self, src, dst=None):
+ """A wrapper for `adb -s <SERIAL> pull <src> <dst>`.
+ Args:
+ src: The source path on the device
+ dst: The destination path on the host
-def main():
- # Parse base XML files in /etc dir, permissions listed there don't have to be re-added
+ Throws:
+ subprocess.CalledProcessError upon pull failure.
+ """
+ if not dst:
+ if self.call('shell \'if [ -d "%s" ]; then echo True; fi\'' % src):
+ dst = tempfile.mkdtemp()
+ temp_dirs.append(dst)
+ else:
+ _, dst = tempfile.mkstemp()
+ temp_files.append(dst)
+ self.call('pull %s %s' % (src, dst))
+ return dst
+
+ def call(self, cmdline):
+ """Calls an adb command.
+
+ Throws:
+ subprocess.CalledProcessError upon command failure.
+ """
+ command = '%s -s %s %s' % (self.path, self.serial, cmdline)
+ return get_output(command)
+
+
+class Aapt(object):
+ def __init__(self, path):
+ self.path = path
+
+ def call(self, arguments):
+ """Run an aapt command with the given args.
+
+ Args:
+ arguments: a list of string arguments
+ Returns:
+ The output of the aapt command as a string.
+ """
+ output = subprocess.check_output([self.path] + arguments,
+ stderr=subprocess.STDOUT)
+ return output.decode(encoding='UTF-8')
+
+
+class Resources(object):
+ """A class that contains the resources needed to generate permissions.
+
+ Attributes:
+ adb: A wrapper class around ADB with a default serial. Only needed when
+ using -d, -s, or "device:"
+ _aapt_path: The path to aapt.
+ """
+
+ def __init__(self, adb_path=None, aapt_path=None, use_device=None,
+ serial=None, apks=None):
+ self.adb = Resources._resolve_adb(adb_path)
+ self.aapt = Resources._resolve_aapt(aapt_path)
+
+ self._is_android_env = 'ANDROID_PRODUCT_OUT' in os.environ and \
+ 'ANDROID_HOST_OUT' in os.environ
+ use_device = use_device or serial or \
+ (apks and DEVICE_PREFIX in '&'.join(apks))
+
+ self.adb.serial = self._resolve_serial(use_device, serial)
+
+ if self.adb.serial:
+ self.adb.call('root')
+ self.adb.call('wait-for-device')
+
+ if self.adb.serial is None and not self._is_android_env:
+ raise MissingResourceError(
+ 'You must either set up your build environment, or specify a '
+ 'device to run against. See --help for more info.')
+
+ self.privapp_apks = self._resolve_apks(apks)
+ self.permissions_dir = self._resolve_sys_path('system/etc/permissions')
+ self.sysconfig_dir = self._resolve_sys_path('system/etc/sysconfig')
+ self.framework_res_apk = self._resolve_sys_path('system/framework/'
+ 'framework-res.apk')
+
+ @staticmethod
+ def _resolve_adb(adb_path):
+ """Resolves ADB from either the cmdline argument or the os environment.
+
+ Args:
+ adb_path: The argument passed in for adb. Can be None.
+ Returns:
+ An Adb object.
+ Raises:
+ MissingResourceError if adb cannot be resolved.
+ """
+ if adb_path:
+ if os.path.isfile(adb_path):
+ adb = adb_path
+ else:
+ raise MissingResourceError('Cannot resolve adb: No such file '
+ '"%s" exists.' % adb_path)
+ else:
+ try:
+ adb = get_output('which adb').strip()
+ except subprocess.CalledProcessError as e:
+ print('Cannot resolve adb: ADB does not exist within path. '
+ 'Did you forget to setup the build environment or set '
+ '--adb?',
+ file=sys.stderr)
+ raise MissingResourceError(e)
+ # Start the adb server immediately so server daemon startup
+ # does not get added to the output of subsequent adb calls.
+ try:
+ get_output('%s start-server' % adb)
+ return Adb(adb)
+ except:
+ print('Unable to reach adb server daemon.', file=sys.stderr)
+ raise
+
+ @staticmethod
+ def _resolve_aapt(aapt_path):
+ """Resolves AAPT from either the cmdline argument or the os environment.
+
+ Returns:
+ An Aapt Object
+ """
+ if aapt_path:
+ if os.path.isfile(aapt_path):
+ return Aapt(aapt_path)
+ else:
+ raise MissingResourceError('Cannot resolve aapt: No such file '
+ '%s exists.' % aapt_path)
+ else:
+ try:
+ return Aapt(get_output('which aapt').strip())
+ except subprocess.CalledProcessError:
+ print('Cannot resolve aapt: AAPT does not exist within path. '
+ 'Did you forget to setup the build environment or set '
+ '--aapt?',
+ file=sys.stderr)
+ raise
+
+ def _resolve_serial(self, device, serial):
+ """Resolves the serial used for device files or generating permissions.
+
+ Returns:
+ If -s/--serial is specified, it will return that serial.
+ If -d or device: is found, it will grab the only available device.
+ If there are multiple devices, it will use $ANDROID_SERIAL.
+ Raises:
+ MissingResourceError if the resolved serial would not be usable.
+ subprocess.CalledProcessError if a command error occurs.
+ """
+ if device:
+ if serial:
+ try:
+ output = get_output('%s -s %s get-state' %
+ (self.adb.path, serial))
+ except subprocess.CalledProcessError:
+ raise MissingResourceError(
+ 'Received error when trying to get the state of '
+ 'device with serial "%s". Is it connected and in '
+ 'device mode?' % serial)
+ if 'device' not in output:
+ raise MissingResourceError(
+ 'Device "%s" is not in device mode. Reboot the phone '
+ 'into device mode and try again.' % serial)
+ return serial
+
+ elif 'ANDROID_SERIAL' in os.environ:
+ serial = os.environ['ANDROID_SERIAL']
+ command = '%s -s %s get-state' % (self.adb, serial)
+ try:
+ output = get_output(command)
+ except subprocess.CalledProcessError:
+ raise MissingResourceError(
+ 'Device with serial $ANDROID_SERIAL ("%s") not '
+ 'found.' % serial)
+ if 'device' in output:
+ return serial
+ raise MissingResourceError(
+ 'Device with serial $ANDROID_SERIAL ("%s") was '
+ 'found, but was not in the "device" state.')
+
+ # Parses `adb devices` so it only returns a string of serials.
+ get_serials_cmd = ('%s devices | tail -n +2 | head -n -1 | '
+ 'cut -f1' % self.adb.path)
+ try:
+ output = get_output(get_serials_cmd)
+ # If multiple serials appear in the output, raise an error.
+ if len(output.split()) > 1:
+ raise MissingResourceError(
+ 'Multiple devices are connected. You must specify '
+ 'which device to run against with flag --serial.')
+ return output.strip()
+ except subprocess.CalledProcessError:
+ print('Unexpected error when querying for connected '
+ 'devices.', file=sys.stderr)
+ raise
+
+ def _resolve_apks(self, apks):
+ """Resolves all APKs to run against.
+
+ Returns:
+ If no apk is specified in the arguments, return all apks in
+ system/priv-app. Otherwise, returns a list with the specified apk.
+ Throws:
+ MissingResourceError if the specified apk or system/priv-app cannot
+ be found.
+ """
+ if not apks:
+ return self._resolve_all_privapps()
+
+ ret_apks = []
+ for apk in apks:
+ if apk.startswith(DEVICE_PREFIX):
+ device_apk = apk[len(DEVICE_PREFIX):]
+ try:
+ apk = self.adb.pull(device_apk)
+ except subprocess.CalledProcessError:
+ raise MissingResourceError(
+ 'File "%s" could not be located on device "%s".' %
+ (device_apk, self.adb.serial))
+ ret_apks.append(apk)
+ elif not os.path.isfile(apk):
+ raise MissingResourceError('File "%s" does not exist.' % apk)
+ else:
+ ret_apks.append(apk)
+ return ret_apks
+
+ def _resolve_all_privapps(self):
+ """Extract package name and requested permissions."""
+ if self._is_android_env:
+ priv_app_dir = os.path.join(os.environ['ANDROID_PRODUCT_OUT'],
+ 'system/priv-app')
+ else:
+ try:
+ priv_app_dir = self.adb.pull('/system/priv-app/')
+ except subprocess.CalledProcessError:
+ raise MissingResourceError(
+ 'Directory "/system/priv-app" could not be pulled from on '
+ 'device "%s".' % self.adb.serial)
+
+ return get_output('find %s -name "*.apk"' % priv_app_dir).split()
+
+ def _resolve_sys_path(self, file_path):
+ """Resolves a path that is a part of an Android System Image."""
+ if self._is_android_env:
+ return os.path.join(os.environ['ANDROID_PRODUCT_OUT'], file_path)
+ else:
+ return self.adb.pull(file_path)
+
+
+def get_output(command):
+ """Returns the output of the command as a string.
+
+ Throws:
+ subprocess.CalledProcessError if exit status is non-zero.
+ """
+ output = subprocess.check_output(command, shell=True)
+ # For Python3.4, decode the byte string so it is usable.
+ return output.decode(encoding='UTF-8')
+
+
+def parse_args():
+ """Parses the CLI."""
+ parser = argparse.ArgumentParser(
+ description=HELP_MESSAGE,
+ formatter_class=argparse.RawDescriptionHelpFormatter)
+ parser.add_argument(
+ '-d',
+ '--device',
+ action='store_true',
+ default=False,
+ required=False,
+ help='Whether or not to generate the privapp_permissions file for the '
+ 'build already on a device. See -s/--serial below for more '
+ 'details.'
+ )
+ parser.add_argument(
+ '--adb',
+ type=str,
+ required=False,
+ metavar='<ADB_PATH',
+ help='Path to adb. If none specified, uses the environment\'s adb.'
+ )
+ parser.add_argument(
+ '--aapt',
+ type=str,
+ required=False,
+ metavar='<AAPT_PATH>',
+ help='Path to aapt. If none specified, uses the environment\'s aapt.'
+ )
+ parser.add_argument(
+ '-s',
+ '--serial',
+ type=str,
+ required=False,
+ metavar='<SERIAL>',
+ help='The serial of the device to generate permissions for. If no '
+ 'serial is given, it will pick the only device connected over '
+ 'adb. If multiple devices are found, it will default to '
+ '$ANDROID_SERIAL. Otherwise, the program will exit with error '
+ 'code 1. If -s is given, -d is not needed.'
+ )
+ parser.add_argument(
+ 'apks',
+ nargs='*',
+ type=str,
+ help='A list of paths to priv-app APKs to generate permissions for. '
+ 'To make a path device-side, prefix the path with "device:".'
+ )
+ cmd_args = parser.parse_args()
+
+ return cmd_args
+
+
+def create_permission_file(resources):
+ # Parse base XML files in /etc dir, permissions listed there don't have
+ # to be re-added
base_permissions = {}
- for xml_file in list_config_xml_files():
+ base_xml_files = itertools.chain(list_xml_files(resources.permissions_dir),
+ list_xml_files(resources.sysconfig_dir))
+ for xml_file in base_xml_files:
parse_config_xml(xml_file, base_permissions)
- # Extract signature|privileged permissions available in the platform
- framework_apk = os.path.join(ANDROID_PRODUCT_OUT, 'system/framework/framework-res.apk')
- platform_priv_permissions = extract_priv_permissions(framework_apk)
+ priv_permissions = extract_priv_permissions(resources.aapt,
+ resources.framework_res_apk)
- priv_apps = [sys.argv[1]] if len(sys.argv) > 1 else list_privapps()
apps_redefine_base = []
results = {}
- for priv_app in priv_apps:
- pkg_info = extract_pkg_and_requested_permissions(priv_app)
+ for priv_app in resources.privapp_apks:
+ pkg_info = extract_pkg_and_requested_permissions(resources.aapt,
+ priv_app)
pkg_name = pkg_info['package_name']
- priv_perms = get_priv_permissions(pkg_info['permissions'], platform_priv_permissions)
+ priv_perms = get_priv_permissions(pkg_info['permissions'],
+ priv_permissions)
# Compute diff against permissions defined in base file
if base_permissions and (pkg_name in base_permissions):
base_permissions_pkg = base_permissions[pkg_name]
- priv_perms = remove_base_permissions(priv_perms, base_permissions_pkg)
+ priv_perms = remove_base_permissions(priv_perms,
+ base_permissions_pkg)
if priv_perms:
apps_redefine_base.append(pkg_name)
if priv_perms:
@@ -69,142 +427,120 @@ def main():
print_xml(results, apps_redefine_base)
-def print_xml(results, apps_redefine_base):
- """
- Print results to xml file
- """
- print """\
-<?xml version="1.0" encoding="utf-8"?>
-<permissions>"""
+
+def print_xml(results, apps_redefine_base, fd=sys.stdout):
+ """Print results to the given file."""
+ fd.write('<?xml version="1.0" encoding="utf-8"?>\n<permissions>\n')
for package_name in sorted(results):
if package_name in apps_redefine_base:
- print ' <!-- Additional permissions on top of %s -->' % BASE_XML_FNAME
- print ' <privapp-permissions package="%s">' % package_name
+ fd.write(' <!-- Additional permissions on top of %s -->\n' %
+ BASE_XML_FILENAME)
+ fd.write(' <privapp-permissions package="%s">\n' % package_name)
for p in results[package_name]:
- print ' <permission name="%s"/>' % p
- print ' </privapp-permissions>'
- print
+ fd.write(' <permission name="%s"/>\n' % p)
+ fd.write(' </privapp-permissions>\n')
+ fd.write('\n')
+
+ fd.write('</permissions>\n')
- print "</permissions>"
def remove_base_permissions(priv_perms, base_perms):
- """
- Removes set of base_perms from set of priv_perms
- """
- if (not priv_perms) or (not base_perms): return priv_perms
+ """Removes set of base_perms from set of priv_perms."""
+ if (not priv_perms) or (not base_perms):
+ return priv_perms
return set(priv_perms) - set(base_perms)
+
def get_priv_permissions(requested_perms, priv_perms):
- """
- Return only permissions that are in priv_perms set
- """
+ """Return only permissions that are in priv_perms set."""
return set(requested_perms).intersection(set(priv_perms))
-def list_privapps():
- """
- Extract package name and requested permissions.
- """
- priv_app_dir = os.path.join(ANDROID_PRODUCT_OUT, 'system/priv-app')
- apks = []
- for dirName, subdirList, fileList in os.walk(priv_app_dir):
- for fname in fileList:
- if fname.endswith(".apk"):
- file_path = os.path.join(dirName, fname)
- apks.append(file_path)
- return apks
+def list_xml_files(directory):
+ """Returns a list of all .xml files within a given directory.
-def list_config_xml_files():
- """
- Extract package name and requested permissions.
+ Args:
+ directory: the directory to look for xml files in.
"""
- perm_dir = os.path.join(ANDROID_PRODUCT_OUT, 'system/etc/permissions')
- conf_dir = os.path.join(ANDROID_PRODUCT_OUT, 'system/etc/sysconfig')
-
xml_files = []
- for root_dir in [perm_dir, conf_dir]:
- for dirName, subdirList, fileList in os.walk(root_dir):
- for fname in fileList:
- if fname.endswith(".xml"):
- file_path = os.path.join(dirName, fname);
- xml_files.append(file_path)
+ for dirName, subdirList, file_list in os.walk(directory):
+ for file in file_list:
+ if file.endswith('.xml'):
+ file_path = os.path.join(dirName, file)
+ xml_files.append(file_path)
return xml_files
-def extract_pkg_and_requested_permissions(apk_path):
+def extract_pkg_and_requested_permissions(aapt, apk_path):
"""
Extract package name and list of requested permissions from the
dump of manifest file
"""
- aapt_args = ["d", "permissions", apk_path]
- txt = aapt(aapt_args)
+ aapt_args = ['d', 'permissions', apk_path]
+ txt = aapt.call(aapt_args)
permissions = []
package_name = None
- rawLines = txt.split('\n')
- for line in rawLines:
+ raw_lines = txt.split('\n')
+ for line in raw_lines:
regex = r"uses-permission: name='([\S]+)'"
matches = re.search(regex, line)
if matches:
name = matches.group(1)
permissions.append(name)
- regex = r"package: ([\S]+)"
+ regex = r'package: ([\S]+)'
matches = re.search(regex, line)
if matches:
package_name = matches.group(1)
- return {'package_name': package_name, 'permissions' : permissions}
+ return {'package_name': package_name, 'permissions': permissions}
-def extract_priv_permissions(apk_path):
- """
- Extract list signature|privileged permissions from the dump of
- manifest file
- """
- aapt_args = ["d", "xmltree", apk_path, "AndroidManifest.xml"]
- txt = aapt(aapt_args)
- rawLines = txt.split('\n')
- n = len(rawLines)
+
+def extract_priv_permissions(aapt, apk_path):
+ """Extract signature|privileged permissions from dump of manifest file."""
+ aapt_args = ['d', 'xmltree', apk_path, 'AndroidManifest.xml']
+ txt = aapt.call(aapt_args)
+ raw_lines = txt.split('\n')
+ n = len(raw_lines)
i = 0
permissions_list = []
- while i<n:
- line = rawLines[i]
- if line.find("E: permission (") != -1:
- i+=1
+ while i < n:
+ line = raw_lines[i]
+ if line.find('E: permission (') != -1:
+ i += 1
name = None
level = None
- while i<n:
- line = rawLines[i];
- if line.find("E: ") != -1:
+ while i < n:
+ line = raw_lines[i]
+ if line.find('E: ') != -1:
break
- regex = r'A: android:name\([\S]+\)=\"([\S]+)\"';
- matches = re.search(regex, line);
+ matches = re.search(ANDROID_NAME_REGEX, line)
if matches:
name = matches.group(1)
- i+=1
+ i += 1
continue
- regex = r'A: android:protectionLevel\([^\)]+\)=\(type [\S]+\)0x([\S]+)';
- matches = re.search(regex, line);
+ matches = re.search(ANDROID_PROTECTION_LEVEL_REGEX, line)
if matches:
level = int(matches.group(1), 16)
- i+=1
+ i += 1
continue
- i+=1
+ i += 1
if name and level and level & 0x12 == 0x12:
- permissions_list.append(name)
+ permissions_list.append(name)
else:
- i+=1
+ i += 1
return permissions_list
+
def parse_config_xml(base_xml, results):
- """
- Parse an XML file that will be used as base.
- """
+ """Parse an XML file that will be used as base."""
dom = minidom.parse(base_xml)
- nodes = dom.getElementsByTagName("privapp-permissions")
+ nodes = dom.getElementsByTagName('privapp-permissions')
for node in nodes:
- permissions = node.getElementsByTagName("permission")
- package_name = node.getAttribute('package');
+ permissions = (node.getElementsByTagName('permission') +
+ node.getElementsByTagName('deny-permission'))
+ package_name = node.getAttribute('package')
plist = []
if package_name in results:
plist = results[package_name]
@@ -215,12 +551,30 @@ def parse_config_xml(base_xml, results):
results[package_name] = plist
return results
-def aapt(args):
- """
- Run aapt command
- """
- return subprocess.check_output([ANDROID_HOST_OUT + '/bin/aapt'] + args,
- stderr=subprocess.STDOUT)
+
+def cleanup():
+ """Cleans up temp files."""
+ for directory in temp_dirs:
+ shutil.rmtree(directory, ignore_errors=True)
+ for file in temp_files:
+ os.remove(file)
+ del temp_dirs[:]
+ del temp_files[:]
+
if __name__ == '__main__':
- main()
+ args = parse_args()
+ try:
+ tool_resources = Resources(
+ aapt_path=args.aapt,
+ adb_path=args.adb,
+ use_device=args.device,
+ serial=args.serial,
+ apks=args.apks
+ )
+ create_permission_file(tool_resources)
+ except MissingResourceError as e:
+ print(str(e), file=sys.stderr)
+ exit(1)
+ finally:
+ cleanup()