1*90c8c64dSAndroid Build Coastguard Worker#!/usr/bin/env python 2*90c8c64dSAndroid Build Coastguard Worker# 3*90c8c64dSAndroid Build Coastguard Worker# Copyright 2017 - The Android Open Source Project 4*90c8c64dSAndroid Build Coastguard Worker# 5*90c8c64dSAndroid Build Coastguard Worker# Licensed under the Apache License, Version 2.0 (the "License"); 6*90c8c64dSAndroid Build Coastguard Worker# you may not use this file except in compliance with the License. 7*90c8c64dSAndroid Build Coastguard Worker# You may obtain a copy of the License at 8*90c8c64dSAndroid Build Coastguard Worker# 9*90c8c64dSAndroid Build Coastguard Worker# http://www.apache.org/licenses/LICENSE-2.0 10*90c8c64dSAndroid Build Coastguard Worker# 11*90c8c64dSAndroid Build Coastguard Worker# Unless required by applicable law or agreed to in writing, software 12*90c8c64dSAndroid Build Coastguard Worker# distributed under the License is distributed on an "AS IS" BASIS, 13*90c8c64dSAndroid Build Coastguard Worker# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14*90c8c64dSAndroid Build Coastguard Worker# See the License for the specific language governing permissions and 15*90c8c64dSAndroid Build Coastguard Worker# limitations under the License. 16*90c8c64dSAndroid Build Coastguard Worker 17*90c8c64dSAndroid Build Coastguard Workerfrom __future__ import print_function 18*90c8c64dSAndroid Build Coastguard Workerfrom xml.dom import minidom 19*90c8c64dSAndroid Build Coastguard Worker 20*90c8c64dSAndroid Build Coastguard Workerimport argparse 21*90c8c64dSAndroid Build Coastguard Workerimport itertools 22*90c8c64dSAndroid Build Coastguard Workerimport os 23*90c8c64dSAndroid Build Coastguard Workerimport re 24*90c8c64dSAndroid Build Coastguard Workerimport subprocess 25*90c8c64dSAndroid Build Coastguard Workerimport sys 26*90c8c64dSAndroid Build Coastguard Workerimport tempfile 27*90c8c64dSAndroid Build Coastguard Workerimport shutil 28*90c8c64dSAndroid Build Coastguard Worker 29*90c8c64dSAndroid Build Coastguard WorkerDEVICE_PREFIX = 'device:' 30*90c8c64dSAndroid Build Coastguard WorkerANDROID_NAME_REGEX = r'A: android:name\([\S]+\)=\"([\S]+)\"' 31*90c8c64dSAndroid Build Coastguard WorkerANDROID_PROTECTION_LEVEL_REGEX = \ 32*90c8c64dSAndroid Build Coastguard Worker r'A: android:protectionLevel\([^\)]+\)=\(type [\S]+\)0x([\S]+)' 33*90c8c64dSAndroid Build Coastguard WorkerBASE_XML_FILENAME = 'privapp-permissions-platform.xml' 34*90c8c64dSAndroid Build Coastguard Worker 35*90c8c64dSAndroid Build Coastguard WorkerHELP_MESSAGE = """\ 36*90c8c64dSAndroid Build Coastguard WorkerGenerates privapp-permissions.xml file for priv-apps. 37*90c8c64dSAndroid Build Coastguard Worker 38*90c8c64dSAndroid Build Coastguard WorkerUsage: 39*90c8c64dSAndroid Build Coastguard Worker Specify which apk to generate priv-app permissions for. If no apk is \ 40*90c8c64dSAndroid Build Coastguard Workerspecified, this will default to all APKs under "<ANDROID_PRODUCT_OUT>/\ 41*90c8c64dSAndroid Build Coastguard Worker<all the partitions>/priv-app/". 42*90c8c64dSAndroid Build Coastguard Worker 43*90c8c64dSAndroid Build Coastguard Worker To specify a target partition(s), use "-p <PARTITION>," where <PARTITION> \ 44*90c8c64dSAndroid Build Coastguard Workercan be "system", "product", "system/product", "system_ext", \ 45*90c8c64dSAndroid Build Coastguard Worker"system/system_ext", "system,system/product,vendor,system_ext", etc. 46*90c8c64dSAndroid Build Coastguard Worker 47*90c8c64dSAndroid Build Coastguard Worker When using adb, adb pull can take a long time. To see the adb pull \ 48*90c8c64dSAndroid Build Coastguard Workerprogress, use "-v" 49*90c8c64dSAndroid Build Coastguard Worker 50*90c8c64dSAndroid Build Coastguard WorkerExamples: 51*90c8c64dSAndroid Build Coastguard Worker 52*90c8c64dSAndroid Build Coastguard Worker For all APKs under $ANDROID_PRODUCT_OUT/<all partitions>/priv-app/: 53*90c8c64dSAndroid Build Coastguard Worker # If the build environment has not been set up, do so: 54*90c8c64dSAndroid Build Coastguard Worker . build/envsetup.sh 55*90c8c64dSAndroid Build Coastguard Worker lunch product_name 56*90c8c64dSAndroid Build Coastguard Worker m -j32 57*90c8c64dSAndroid Build Coastguard Worker # then use: 58*90c8c64dSAndroid Build Coastguard Worker cd development/tools/privapp_permissions/ 59*90c8c64dSAndroid Build Coastguard Worker ./privapp_permissions.py 60*90c8c64dSAndroid Build Coastguard Worker # or to search for apks in "product" partition 61*90c8c64dSAndroid Build Coastguard Worker ./privapp_permissions.py -p product 62*90c8c64dSAndroid Build Coastguard Worker # or to search for apks in system, product, and vendor partitions 63*90c8c64dSAndroid Build Coastguard Worker ./privapp_permissions.py -p system,product,vendor 64*90c8c64dSAndroid Build Coastguard Worker 65*90c8c64dSAndroid Build Coastguard Worker For an APK against $ANDROID_PRODUCT_OUT/<all partitions>/etc/permissions/: 66*90c8c64dSAndroid Build Coastguard Worker ./privapp_permissions.py path/to/the.apk 67*90c8c64dSAndroid Build Coastguard Worker # or against /product/etc/permissions/ 68*90c8c64dSAndroid Build Coastguard Worker ./privapp_permissions.py path/to/the.apk -p product 69*90c8c64dSAndroid Build Coastguard Worker 70*90c8c64dSAndroid Build Coastguard Worker For an APK already on the device against /<all partitions>/etc/permissions/: 71*90c8c64dSAndroid Build Coastguard Worker ./privapp_permissions.py device:/device/path/to/the.apk 72*90c8c64dSAndroid Build Coastguard Worker # or against /product/etc/permissions/ 73*90c8c64dSAndroid Build Coastguard Worker ./privapp_permissions.py path/to/the.apk -p product 74*90c8c64dSAndroid Build Coastguard Worker 75*90c8c64dSAndroid Build Coastguard Worker For all APKs on a device under /<all partitions>/priv-app/: 76*90c8c64dSAndroid Build Coastguard Worker ./privapp_permissions.py -d 77*90c8c64dSAndroid Build Coastguard Worker # or if more than one device is attached 78*90c8c64dSAndroid Build Coastguard Worker ./privapp_permissions.py -s <ANDROID_SERIAL> 79*90c8c64dSAndroid Build Coastguard Worker # or for all APKs on the "system" partitions 80*90c8c64dSAndroid Build Coastguard Worker ./privapp_permissions.py -d -p system 81*90c8c64dSAndroid Build Coastguard Worker""" 82*90c8c64dSAndroid Build Coastguard Worker 83*90c8c64dSAndroid Build Coastguard Worker# An array of all generated temp directories. 84*90c8c64dSAndroid Build Coastguard Workertemp_dirs = [] 85*90c8c64dSAndroid Build Coastguard Worker# An array of all generated temp files. 86*90c8c64dSAndroid Build Coastguard Workertemp_files = [] 87*90c8c64dSAndroid Build Coastguard Worker 88*90c8c64dSAndroid Build Coastguard Workerdef vprint(enable, message, *args): 89*90c8c64dSAndroid Build Coastguard Worker if enable: 90*90c8c64dSAndroid Build Coastguard Worker # Use stderr to avoid poluting print_xml result 91*90c8c64dSAndroid Build Coastguard Worker sys.stderr.write(message % args + '\n') 92*90c8c64dSAndroid Build Coastguard Worker 93*90c8c64dSAndroid Build Coastguard Workerclass MissingResourceError(Exception): 94*90c8c64dSAndroid Build Coastguard Worker """Raised when a dependency cannot be located.""" 95*90c8c64dSAndroid Build Coastguard Worker 96*90c8c64dSAndroid Build Coastguard Worker 97*90c8c64dSAndroid Build Coastguard Workerclass Adb(object): 98*90c8c64dSAndroid Build Coastguard Worker """A small wrapper around ADB calls.""" 99*90c8c64dSAndroid Build Coastguard Worker 100*90c8c64dSAndroid Build Coastguard Worker def __init__(self, path, serial=None, verbose=False): 101*90c8c64dSAndroid Build Coastguard Worker self.path = path 102*90c8c64dSAndroid Build Coastguard Worker self.serial = serial 103*90c8c64dSAndroid Build Coastguard Worker self.verbose = verbose 104*90c8c64dSAndroid Build Coastguard Worker 105*90c8c64dSAndroid Build Coastguard Worker def pull(self, src, dst=None): 106*90c8c64dSAndroid Build Coastguard Worker """A wrapper for `adb -s <SERIAL> pull <src> <dst>`. 107*90c8c64dSAndroid Build Coastguard Worker Args: 108*90c8c64dSAndroid Build Coastguard Worker src: The source path on the device 109*90c8c64dSAndroid Build Coastguard Worker dst: The destination path on the host 110*90c8c64dSAndroid Build Coastguard Worker 111*90c8c64dSAndroid Build Coastguard Worker Throws: 112*90c8c64dSAndroid Build Coastguard Worker subprocess.CalledProcessError upon pull failure. 113*90c8c64dSAndroid Build Coastguard Worker """ 114*90c8c64dSAndroid Build Coastguard Worker if not dst: 115*90c8c64dSAndroid Build Coastguard Worker if self.call('shell \'if [ -d "%s" ]; then echo True; fi\'' % src): 116*90c8c64dSAndroid Build Coastguard Worker dst = tempfile.mkdtemp() 117*90c8c64dSAndroid Build Coastguard Worker temp_dirs.append(dst) 118*90c8c64dSAndroid Build Coastguard Worker else: 119*90c8c64dSAndroid Build Coastguard Worker _, dst = tempfile.mkstemp() 120*90c8c64dSAndroid Build Coastguard Worker temp_files.append(dst) 121*90c8c64dSAndroid Build Coastguard Worker self.call('pull %s %s' % (src, dst), False, self.verbose) 122*90c8c64dSAndroid Build Coastguard Worker return dst 123*90c8c64dSAndroid Build Coastguard Worker 124*90c8c64dSAndroid Build Coastguard Worker def call(self, cmdline, getoutput=True, verbose=False): 125*90c8c64dSAndroid Build Coastguard Worker """Calls an adb command. 126*90c8c64dSAndroid Build Coastguard Worker 127*90c8c64dSAndroid Build Coastguard Worker Throws: 128*90c8c64dSAndroid Build Coastguard Worker subprocess.CalledProcessError upon command failure. 129*90c8c64dSAndroid Build Coastguard Worker """ 130*90c8c64dSAndroid Build Coastguard Worker command = '%s -s %s %s' % (self.path, self.serial, cmdline) 131*90c8c64dSAndroid Build Coastguard Worker if getoutput: 132*90c8c64dSAndroid Build Coastguard Worker return get_output(command) 133*90c8c64dSAndroid Build Coastguard Worker else: 134*90c8c64dSAndroid Build Coastguard Worker # Handle verbose mode only when the output is not needed 135*90c8c64dSAndroid Build Coastguard Worker # This is mainly for adb pull, which can take a long time 136*90c8c64dSAndroid Build Coastguard Worker extracmd = ' > /dev/null 2>&1' 137*90c8c64dSAndroid Build Coastguard Worker if verbose: 138*90c8c64dSAndroid Build Coastguard Worker # Use stderr to avoid poluting print_xml result 139*90c8c64dSAndroid Build Coastguard Worker extracmd = ' 1>&2' 140*90c8c64dSAndroid Build Coastguard Worker os.system(command + extracmd) 141*90c8c64dSAndroid Build Coastguard Worker 142*90c8c64dSAndroid Build Coastguard Workerclass Aapt(object): 143*90c8c64dSAndroid Build Coastguard Worker def __init__(self, path): 144*90c8c64dSAndroid Build Coastguard Worker self.path = path 145*90c8c64dSAndroid Build Coastguard Worker 146*90c8c64dSAndroid Build Coastguard Worker def call(self, arguments): 147*90c8c64dSAndroid Build Coastguard Worker """Run an aapt command with the given args. 148*90c8c64dSAndroid Build Coastguard Worker 149*90c8c64dSAndroid Build Coastguard Worker Args: 150*90c8c64dSAndroid Build Coastguard Worker arguments: a list of string arguments 151*90c8c64dSAndroid Build Coastguard Worker Returns: 152*90c8c64dSAndroid Build Coastguard Worker The output of the aapt command as a string. 153*90c8c64dSAndroid Build Coastguard Worker """ 154*90c8c64dSAndroid Build Coastguard Worker output = subprocess.check_output([self.path] + arguments, 155*90c8c64dSAndroid Build Coastguard Worker stderr=subprocess.STDOUT) 156*90c8c64dSAndroid Build Coastguard Worker return output.decode(encoding='UTF-8') 157*90c8c64dSAndroid Build Coastguard Worker 158*90c8c64dSAndroid Build Coastguard Worker 159*90c8c64dSAndroid Build Coastguard Workerclass Resources(object): 160*90c8c64dSAndroid Build Coastguard Worker """A class that contains the resources needed to generate permissions. 161*90c8c64dSAndroid Build Coastguard Worker 162*90c8c64dSAndroid Build Coastguard Worker Attributes: 163*90c8c64dSAndroid Build Coastguard Worker adb: A wrapper class around ADB with a default serial. Only needed when 164*90c8c64dSAndroid Build Coastguard Worker using -d, -s, or "device:" 165*90c8c64dSAndroid Build Coastguard Worker _aapt_path: The path to aapt. 166*90c8c64dSAndroid Build Coastguard Worker """ 167*90c8c64dSAndroid Build Coastguard Worker 168*90c8c64dSAndroid Build Coastguard Worker def __init__(self, adb_path=None, aapt_path=None, use_device=None, 169*90c8c64dSAndroid Build Coastguard Worker serial=None, partitions=None, verbose=False, 170*90c8c64dSAndroid Build Coastguard Worker writetodisk=None, systemfile=None, productfile=None, 171*90c8c64dSAndroid Build Coastguard Worker apks=None): 172*90c8c64dSAndroid Build Coastguard Worker self.adb = Resources._resolve_adb(adb_path) 173*90c8c64dSAndroid Build Coastguard Worker self.aapt = Resources._resolve_aapt(aapt_path) 174*90c8c64dSAndroid Build Coastguard Worker 175*90c8c64dSAndroid Build Coastguard Worker self.verbose = self.adb.verbose = verbose 176*90c8c64dSAndroid Build Coastguard Worker self.writetodisk = writetodisk 177*90c8c64dSAndroid Build Coastguard Worker self.systemfile = systemfile; 178*90c8c64dSAndroid Build Coastguard Worker self.productfile = productfile; 179*90c8c64dSAndroid Build Coastguard Worker 180*90c8c64dSAndroid Build Coastguard Worker self._is_android_env = 'ANDROID_PRODUCT_OUT' in os.environ and \ 181*90c8c64dSAndroid Build Coastguard Worker 'ANDROID_HOST_OUT' in os.environ 182*90c8c64dSAndroid Build Coastguard Worker use_device = use_device or serial or \ 183*90c8c64dSAndroid Build Coastguard Worker (apks and DEVICE_PREFIX in '&'.join(apks)) 184*90c8c64dSAndroid Build Coastguard Worker 185*90c8c64dSAndroid Build Coastguard Worker self.adb.serial = self._resolve_serial(use_device, serial) 186*90c8c64dSAndroid Build Coastguard Worker 187*90c8c64dSAndroid Build Coastguard Worker if self.adb.serial: 188*90c8c64dSAndroid Build Coastguard Worker self.adb.call('root') 189*90c8c64dSAndroid Build Coastguard Worker self.adb.call('wait-for-device') 190*90c8c64dSAndroid Build Coastguard Worker 191*90c8c64dSAndroid Build Coastguard Worker if self.adb.serial is None and not self._is_android_env: 192*90c8c64dSAndroid Build Coastguard Worker raise MissingResourceError( 193*90c8c64dSAndroid Build Coastguard Worker 'You must either set up your build environment, or specify a ' 194*90c8c64dSAndroid Build Coastguard Worker 'device to run against. See --help for more info.') 195*90c8c64dSAndroid Build Coastguard Worker 196*90c8c64dSAndroid Build Coastguard Worker if apks and (partitions == "all" or partitions.find(',') != -1): 197*90c8c64dSAndroid Build Coastguard Worker # override the partition to "system 198*90c8c64dSAndroid Build Coastguard Worker print('\n# Defaulting the target partition to "system". ' 199*90c8c64dSAndroid Build Coastguard Worker 'Use -p option to specify the target partition ' 200*90c8c64dSAndroid Build Coastguard Worker '(must provide one target instead of a list).\n', 201*90c8c64dSAndroid Build Coastguard Worker file=sys.stderr) 202*90c8c64dSAndroid Build Coastguard Worker partitions = "system" 203*90c8c64dSAndroid Build Coastguard Worker 204*90c8c64dSAndroid Build Coastguard Worker if partitions == "all": 205*90c8c64dSAndroid Build Coastguard Worker # This is the default scenario 206*90c8c64dSAndroid Build Coastguard Worker # Find all the partitions where priv-app exists 207*90c8c64dSAndroid Build Coastguard Worker self.partitions = self._get_partitions() 208*90c8c64dSAndroid Build Coastguard Worker else: 209*90c8c64dSAndroid Build Coastguard Worker # Initialize self.partitions with the specified partitions 210*90c8c64dSAndroid Build Coastguard Worker self.partitions = [] 211*90c8c64dSAndroid Build Coastguard Worker for p in partitions.split(','): 212*90c8c64dSAndroid Build Coastguard Worker if p.endswith('/'): 213*90c8c64dSAndroid Build Coastguard Worker p = p[:-1] 214*90c8c64dSAndroid Build Coastguard Worker self.partitions.append(p) 215*90c8c64dSAndroid Build Coastguard Worker # Check if the directory exists 216*90c8c64dSAndroid Build Coastguard Worker self._check_dir(p + '/priv-app') 217*90c8c64dSAndroid Build Coastguard Worker 218*90c8c64dSAndroid Build Coastguard Worker vprint(self.verbose, 219*90c8c64dSAndroid Build Coastguard Worker '# Examining the partitions: ' + str(self.partitions)) 220*90c8c64dSAndroid Build Coastguard Worker 221*90c8c64dSAndroid Build Coastguard Worker # Create dictionary of array (partition as the key) 222*90c8c64dSAndroid Build Coastguard Worker self.privapp_apks = self._resolve_apks(apks, self.partitions) 223*90c8c64dSAndroid Build Coastguard Worker self.permissions_dirs = self._resolve_sys_paths('etc/permissions', 224*90c8c64dSAndroid Build Coastguard Worker self.partitions) 225*90c8c64dSAndroid Build Coastguard Worker self.sysconfig_dirs = self._resolve_sys_paths('etc/sysconfig', 226*90c8c64dSAndroid Build Coastguard Worker self.partitions) 227*90c8c64dSAndroid Build Coastguard Worker 228*90c8c64dSAndroid Build Coastguard Worker # Always use the one in /system partition, 229*90c8c64dSAndroid Build Coastguard Worker # as that is the only place we will find framework-res.apk 230*90c8c64dSAndroid Build Coastguard Worker self.framework_res_apk = self._resolve_sys_path('system/framework/' 231*90c8c64dSAndroid Build Coastguard Worker 'framework-res.apk') 232*90c8c64dSAndroid Build Coastguard Worker @staticmethod 233*90c8c64dSAndroid Build Coastguard Worker def _resolve_adb(adb_path): 234*90c8c64dSAndroid Build Coastguard Worker """Resolves ADB from either the cmdline argument or the os environment. 235*90c8c64dSAndroid Build Coastguard Worker 236*90c8c64dSAndroid Build Coastguard Worker Args: 237*90c8c64dSAndroid Build Coastguard Worker adb_path: The argument passed in for adb. Can be None. 238*90c8c64dSAndroid Build Coastguard Worker Returns: 239*90c8c64dSAndroid Build Coastguard Worker An Adb object. 240*90c8c64dSAndroid Build Coastguard Worker Raises: 241*90c8c64dSAndroid Build Coastguard Worker MissingResourceError if adb cannot be resolved. 242*90c8c64dSAndroid Build Coastguard Worker """ 243*90c8c64dSAndroid Build Coastguard Worker if adb_path: 244*90c8c64dSAndroid Build Coastguard Worker if os.path.isfile(adb_path): 245*90c8c64dSAndroid Build Coastguard Worker adb = adb_path 246*90c8c64dSAndroid Build Coastguard Worker else: 247*90c8c64dSAndroid Build Coastguard Worker raise MissingResourceError('Cannot resolve adb: No such file ' 248*90c8c64dSAndroid Build Coastguard Worker '"%s" exists.' % adb_path) 249*90c8c64dSAndroid Build Coastguard Worker else: 250*90c8c64dSAndroid Build Coastguard Worker try: 251*90c8c64dSAndroid Build Coastguard Worker adb = get_output('which adb').strip() 252*90c8c64dSAndroid Build Coastguard Worker except subprocess.CalledProcessError as e: 253*90c8c64dSAndroid Build Coastguard Worker print('Cannot resolve adb: ADB does not exist within path. ' 254*90c8c64dSAndroid Build Coastguard Worker 'Did you forget to setup the build environment or set ' 255*90c8c64dSAndroid Build Coastguard Worker '--adb?', 256*90c8c64dSAndroid Build Coastguard Worker file=sys.stderr) 257*90c8c64dSAndroid Build Coastguard Worker raise MissingResourceError(e) 258*90c8c64dSAndroid Build Coastguard Worker # Start the adb server immediately so server daemon startup 259*90c8c64dSAndroid Build Coastguard Worker # does not get added to the output of subsequent adb calls. 260*90c8c64dSAndroid Build Coastguard Worker try: 261*90c8c64dSAndroid Build Coastguard Worker get_output('%s start-server' % adb) 262*90c8c64dSAndroid Build Coastguard Worker return Adb(adb) 263*90c8c64dSAndroid Build Coastguard Worker except: 264*90c8c64dSAndroid Build Coastguard Worker print('Unable to reach adb server daemon.', file=sys.stderr) 265*90c8c64dSAndroid Build Coastguard Worker raise 266*90c8c64dSAndroid Build Coastguard Worker 267*90c8c64dSAndroid Build Coastguard Worker @staticmethod 268*90c8c64dSAndroid Build Coastguard Worker def _resolve_aapt(aapt_path): 269*90c8c64dSAndroid Build Coastguard Worker """Resolves AAPT from either the cmdline argument or the os environment. 270*90c8c64dSAndroid Build Coastguard Worker 271*90c8c64dSAndroid Build Coastguard Worker Returns: 272*90c8c64dSAndroid Build Coastguard Worker An Aapt Object 273*90c8c64dSAndroid Build Coastguard Worker """ 274*90c8c64dSAndroid Build Coastguard Worker if aapt_path: 275*90c8c64dSAndroid Build Coastguard Worker if os.path.isfile(aapt_path): 276*90c8c64dSAndroid Build Coastguard Worker return Aapt(aapt_path) 277*90c8c64dSAndroid Build Coastguard Worker else: 278*90c8c64dSAndroid Build Coastguard Worker raise MissingResourceError('Cannot resolve aapt: No such file ' 279*90c8c64dSAndroid Build Coastguard Worker '%s exists.' % aapt_path) 280*90c8c64dSAndroid Build Coastguard Worker else: 281*90c8c64dSAndroid Build Coastguard Worker try: 282*90c8c64dSAndroid Build Coastguard Worker return Aapt(get_output('which aapt').strip()) 283*90c8c64dSAndroid Build Coastguard Worker except subprocess.CalledProcessError: 284*90c8c64dSAndroid Build Coastguard Worker print('Cannot resolve aapt: AAPT does not exist within path. ' 285*90c8c64dSAndroid Build Coastguard Worker 'Did you forget to setup the build environment or set ' 286*90c8c64dSAndroid Build Coastguard Worker '--aapt?', 287*90c8c64dSAndroid Build Coastguard Worker file=sys.stderr) 288*90c8c64dSAndroid Build Coastguard Worker raise 289*90c8c64dSAndroid Build Coastguard Worker 290*90c8c64dSAndroid Build Coastguard Worker def _resolve_serial(self, device, serial): 291*90c8c64dSAndroid Build Coastguard Worker """Resolves the serial used for device files or generating permissions. 292*90c8c64dSAndroid Build Coastguard Worker 293*90c8c64dSAndroid Build Coastguard Worker Returns: 294*90c8c64dSAndroid Build Coastguard Worker If -s/--serial is specified, it will return that serial. 295*90c8c64dSAndroid Build Coastguard Worker If -d or device: is found, it will grab the only available device. 296*90c8c64dSAndroid Build Coastguard Worker If there are multiple devices, it will use $ANDROID_SERIAL. 297*90c8c64dSAndroid Build Coastguard Worker Raises: 298*90c8c64dSAndroid Build Coastguard Worker MissingResourceError if the resolved serial would not be usable. 299*90c8c64dSAndroid Build Coastguard Worker subprocess.CalledProcessError if a command error occurs. 300*90c8c64dSAndroid Build Coastguard Worker """ 301*90c8c64dSAndroid Build Coastguard Worker if device: 302*90c8c64dSAndroid Build Coastguard Worker if serial: 303*90c8c64dSAndroid Build Coastguard Worker try: 304*90c8c64dSAndroid Build Coastguard Worker output = get_output('%s -s %s get-state' % 305*90c8c64dSAndroid Build Coastguard Worker (self.adb.path, serial)) 306*90c8c64dSAndroid Build Coastguard Worker except subprocess.CalledProcessError: 307*90c8c64dSAndroid Build Coastguard Worker raise MissingResourceError( 308*90c8c64dSAndroid Build Coastguard Worker 'Received error when trying to get the state of ' 309*90c8c64dSAndroid Build Coastguard Worker 'device with serial "%s". Is it connected and in ' 310*90c8c64dSAndroid Build Coastguard Worker 'device mode?' % serial) 311*90c8c64dSAndroid Build Coastguard Worker if 'device' not in output: 312*90c8c64dSAndroid Build Coastguard Worker raise MissingResourceError( 313*90c8c64dSAndroid Build Coastguard Worker 'Device "%s" is not in device mode. Reboot the phone ' 314*90c8c64dSAndroid Build Coastguard Worker 'into device mode and try again.' % serial) 315*90c8c64dSAndroid Build Coastguard Worker return serial 316*90c8c64dSAndroid Build Coastguard Worker 317*90c8c64dSAndroid Build Coastguard Worker elif 'ANDROID_SERIAL' in os.environ: 318*90c8c64dSAndroid Build Coastguard Worker serial = os.environ['ANDROID_SERIAL'] 319*90c8c64dSAndroid Build Coastguard Worker command = '%s -s %s get-state' % (self.adb, serial) 320*90c8c64dSAndroid Build Coastguard Worker try: 321*90c8c64dSAndroid Build Coastguard Worker output = get_output(command) 322*90c8c64dSAndroid Build Coastguard Worker except subprocess.CalledProcessError: 323*90c8c64dSAndroid Build Coastguard Worker raise MissingResourceError( 324*90c8c64dSAndroid Build Coastguard Worker 'Device with serial $ANDROID_SERIAL ("%s") not ' 325*90c8c64dSAndroid Build Coastguard Worker 'found.' % serial) 326*90c8c64dSAndroid Build Coastguard Worker if 'device' in output: 327*90c8c64dSAndroid Build Coastguard Worker return serial 328*90c8c64dSAndroid Build Coastguard Worker raise MissingResourceError( 329*90c8c64dSAndroid Build Coastguard Worker 'Device with serial $ANDROID_SERIAL ("%s") was ' 330*90c8c64dSAndroid Build Coastguard Worker 'found, but was not in the "device" state.') 331*90c8c64dSAndroid Build Coastguard Worker 332*90c8c64dSAndroid Build Coastguard Worker # Parses `adb devices` so it only returns a string of serials. 333*90c8c64dSAndroid Build Coastguard Worker get_serials_cmd = ('%s devices | tail -n +2 | head -n -1 | ' 334*90c8c64dSAndroid Build Coastguard Worker 'cut -f1' % self.adb.path) 335*90c8c64dSAndroid Build Coastguard Worker try: 336*90c8c64dSAndroid Build Coastguard Worker output = get_output(get_serials_cmd) 337*90c8c64dSAndroid Build Coastguard Worker # If multiple serials appear in the output, raise an error. 338*90c8c64dSAndroid Build Coastguard Worker if len(output.split()) > 1: 339*90c8c64dSAndroid Build Coastguard Worker raise MissingResourceError( 340*90c8c64dSAndroid Build Coastguard Worker 'Multiple devices are connected. You must specify ' 341*90c8c64dSAndroid Build Coastguard Worker 'which device to run against with flag --serial.') 342*90c8c64dSAndroid Build Coastguard Worker return output.strip() 343*90c8c64dSAndroid Build Coastguard Worker except subprocess.CalledProcessError: 344*90c8c64dSAndroid Build Coastguard Worker print('Unexpected error when querying for connected ' 345*90c8c64dSAndroid Build Coastguard Worker 'devices.', file=sys.stderr) 346*90c8c64dSAndroid Build Coastguard Worker raise 347*90c8c64dSAndroid Build Coastguard Worker 348*90c8c64dSAndroid Build Coastguard Worker def _get_partitions(self): 349*90c8c64dSAndroid Build Coastguard Worker """Find all the partitions to examine 350*90c8c64dSAndroid Build Coastguard Worker 351*90c8c64dSAndroid Build Coastguard Worker Returns: 352*90c8c64dSAndroid Build Coastguard Worker The array of partitions where priv-app exists 353*90c8c64dSAndroid Build Coastguard Worker Raises: 354*90c8c64dSAndroid Build Coastguard Worker MissingResourceError find command over adb shell fails. 355*90c8c64dSAndroid Build Coastguard Worker """ 356*90c8c64dSAndroid Build Coastguard Worker if not self.adb.serial: 357*90c8c64dSAndroid Build Coastguard Worker privapp_dirs = get_output('cd %s; find * -name "priv-app"' 358*90c8c64dSAndroid Build Coastguard Worker % os.environ['ANDROID_PRODUCT_OUT'] 359*90c8c64dSAndroid Build Coastguard Worker + ' -type d | grep -v obj').split() 360*90c8c64dSAndroid Build Coastguard Worker else: 361*90c8c64dSAndroid Build Coastguard Worker try: 362*90c8c64dSAndroid Build Coastguard Worker privapp_dirs = self.adb.call('shell find \'/!(proc)\' \ 363*90c8c64dSAndroid Build Coastguard Worker -name "priv-app" -type d').split() 364*90c8c64dSAndroid Build Coastguard Worker except subprocess.CalledProcessError: 365*90c8c64dSAndroid Build Coastguard Worker raise MissingResourceError( 366*90c8c64dSAndroid Build Coastguard Worker '"adb shell find / -name priv-app -type d" did not succeed' 367*90c8c64dSAndroid Build Coastguard Worker ' on device "%s".' % self.adb.serial) 368*90c8c64dSAndroid Build Coastguard Worker 369*90c8c64dSAndroid Build Coastguard Worker # Remove 'priv-app' from the privapp_dirs 370*90c8c64dSAndroid Build Coastguard Worker partitions = [] 371*90c8c64dSAndroid Build Coastguard Worker for i in range(len(privapp_dirs)): 372*90c8c64dSAndroid Build Coastguard Worker partitions.append('/'.join(privapp_dirs[i].split('/')[:-1])) 373*90c8c64dSAndroid Build Coastguard Worker 374*90c8c64dSAndroid Build Coastguard Worker return partitions 375*90c8c64dSAndroid Build Coastguard Worker 376*90c8c64dSAndroid Build Coastguard Worker def _check_dir(self, directory): 377*90c8c64dSAndroid Build Coastguard Worker """Check if a given directory is valid 378*90c8c64dSAndroid Build Coastguard Worker 379*90c8c64dSAndroid Build Coastguard Worker Raises: 380*90c8c64dSAndroid Build Coastguard Worker MissingResourceError if a given directory does not exist. 381*90c8c64dSAndroid Build Coastguard Worker """ 382*90c8c64dSAndroid Build Coastguard Worker if not self.adb.serial: 383*90c8c64dSAndroid Build Coastguard Worker if not os.path.isdir(os.environ['ANDROID_PRODUCT_OUT'] 384*90c8c64dSAndroid Build Coastguard Worker + '/' + directory): 385*90c8c64dSAndroid Build Coastguard Worker raise MissingResourceError( 386*90c8c64dSAndroid Build Coastguard Worker '%s does not exist' % directory) 387*90c8c64dSAndroid Build Coastguard Worker else: 388*90c8c64dSAndroid Build Coastguard Worker try: 389*90c8c64dSAndroid Build Coastguard Worker self.adb.call('shell ls %s' % directory) 390*90c8c64dSAndroid Build Coastguard Worker except subprocess.CalledProcessError: 391*90c8c64dSAndroid Build Coastguard Worker raise MissingResourceError( 392*90c8c64dSAndroid Build Coastguard Worker '"adb shell ls %s" did not succeed on ' 393*90c8c64dSAndroid Build Coastguard Worker 'device "%s".' % (directory, self.adb.serial)) 394*90c8c64dSAndroid Build Coastguard Worker 395*90c8c64dSAndroid Build Coastguard Worker 396*90c8c64dSAndroid Build Coastguard Worker def _resolve_apks(self, apks, partitions): 397*90c8c64dSAndroid Build Coastguard Worker """Resolves all APKs to run against. 398*90c8c64dSAndroid Build Coastguard Worker 399*90c8c64dSAndroid Build Coastguard Worker Returns: 400*90c8c64dSAndroid Build Coastguard Worker If no apk is specified in the arguments, return all apks in 401*90c8c64dSAndroid Build Coastguard Worker priv-app in all the partitions. 402*90c8c64dSAndroid Build Coastguard Worker Otherwise, returns a list with the specified apk. 403*90c8c64dSAndroid Build Coastguard Worker Throws: 404*90c8c64dSAndroid Build Coastguard Worker MissingResourceError if the specified apk or 405*90c8c64dSAndroid Build Coastguard Worker <partition>/priv-app cannot be found. 406*90c8c64dSAndroid Build Coastguard Worker """ 407*90c8c64dSAndroid Build Coastguard Worker results = {} 408*90c8c64dSAndroid Build Coastguard Worker if not apks: 409*90c8c64dSAndroid Build Coastguard Worker for p in partitions: 410*90c8c64dSAndroid Build Coastguard Worker results[p] = self._resolve_all_privapps(p) 411*90c8c64dSAndroid Build Coastguard Worker return results 412*90c8c64dSAndroid Build Coastguard Worker 413*90c8c64dSAndroid Build Coastguard Worker # The first element is what is passed via '-p' option 414*90c8c64dSAndroid Build Coastguard Worker # (default is overwritten to 'system' when apk is specified) 415*90c8c64dSAndroid Build Coastguard Worker p = partitions[0] 416*90c8c64dSAndroid Build Coastguard Worker results[p] = [] 417*90c8c64dSAndroid Build Coastguard Worker for apk in apks: 418*90c8c64dSAndroid Build Coastguard Worker if apk.startswith(DEVICE_PREFIX): 419*90c8c64dSAndroid Build Coastguard Worker device_apk = apk[len(DEVICE_PREFIX):] 420*90c8c64dSAndroid Build Coastguard Worker try: 421*90c8c64dSAndroid Build Coastguard Worker apk = self.adb.pull(device_apk) 422*90c8c64dSAndroid Build Coastguard Worker except subprocess.CalledProcessError: 423*90c8c64dSAndroid Build Coastguard Worker raise MissingResourceError( 424*90c8c64dSAndroid Build Coastguard Worker 'File "%s" could not be located on device "%s".' % 425*90c8c64dSAndroid Build Coastguard Worker (device_apk, self.adb.serial)) 426*90c8c64dSAndroid Build Coastguard Worker results[p].append(apk) 427*90c8c64dSAndroid Build Coastguard Worker elif not os.path.isfile(apk): 428*90c8c64dSAndroid Build Coastguard Worker raise MissingResourceError('File "%s" does not exist.' % apk) 429*90c8c64dSAndroid Build Coastguard Worker else: 430*90c8c64dSAndroid Build Coastguard Worker results[p].append(apk) 431*90c8c64dSAndroid Build Coastguard Worker return results 432*90c8c64dSAndroid Build Coastguard Worker 433*90c8c64dSAndroid Build Coastguard Worker def _resolve_all_privapps(self, partition): 434*90c8c64dSAndroid Build Coastguard Worker """Resolves all APKs in <partition>/priv-app 435*90c8c64dSAndroid Build Coastguard Worker 436*90c8c64dSAndroid Build Coastguard Worker Returns: 437*90c8c64dSAndroid Build Coastguard Worker Return all apks in <partition>/priv-app 438*90c8c64dSAndroid Build Coastguard Worker Throws: 439*90c8c64dSAndroid Build Coastguard Worker MissingResourceError <partition>/priv-app cannot be found. 440*90c8c64dSAndroid Build Coastguard Worker """ 441*90c8c64dSAndroid Build Coastguard Worker if not self.adb.serial: 442*90c8c64dSAndroid Build Coastguard Worker priv_app_dir = os.path.join(os.environ['ANDROID_PRODUCT_OUT'], 443*90c8c64dSAndroid Build Coastguard Worker partition + '/priv-app') 444*90c8c64dSAndroid Build Coastguard Worker else: 445*90c8c64dSAndroid Build Coastguard Worker try: 446*90c8c64dSAndroid Build Coastguard Worker priv_app_dir = self.adb.pull(partition + '/priv-app/') 447*90c8c64dSAndroid Build Coastguard Worker except subprocess.CalledProcessError: 448*90c8c64dSAndroid Build Coastguard Worker raise MissingResourceError( 449*90c8c64dSAndroid Build Coastguard Worker 'Directory "%s/priv-app" could not be pulled from on ' 450*90c8c64dSAndroid Build Coastguard Worker 'device "%s".' % (partition, self.adb.serial)) 451*90c8c64dSAndroid Build Coastguard Worker return get_output('find %s -name "*.apk"' % priv_app_dir).split() 452*90c8c64dSAndroid Build Coastguard Worker 453*90c8c64dSAndroid Build Coastguard Worker def _resolve_sys_path(self, file_path): 454*90c8c64dSAndroid Build Coastguard Worker """Resolves a path that is a part of an Android System Image.""" 455*90c8c64dSAndroid Build Coastguard Worker if not self.adb.serial: 456*90c8c64dSAndroid Build Coastguard Worker return os.path.join(os.environ['ANDROID_PRODUCT_OUT'], file_path) 457*90c8c64dSAndroid Build Coastguard Worker else: 458*90c8c64dSAndroid Build Coastguard Worker return self.adb.pull(file_path) 459*90c8c64dSAndroid Build Coastguard Worker 460*90c8c64dSAndroid Build Coastguard Worker def _resolve_sys_paths(self, file_path, partitions): 461*90c8c64dSAndroid Build Coastguard Worker """Resolves a path that is a part of an Android System Image, for the 462*90c8c64dSAndroid Build Coastguard Worker specified partitions.""" 463*90c8c64dSAndroid Build Coastguard Worker results = {} 464*90c8c64dSAndroid Build Coastguard Worker for p in partitions: 465*90c8c64dSAndroid Build Coastguard Worker results[p] = self._resolve_sys_path(p + '/' + file_path) 466*90c8c64dSAndroid Build Coastguard Worker return results 467*90c8c64dSAndroid Build Coastguard Worker 468*90c8c64dSAndroid Build Coastguard Worker 469*90c8c64dSAndroid Build Coastguard Workerdef get_output(command): 470*90c8c64dSAndroid Build Coastguard Worker """Returns the output of the command as a string. 471*90c8c64dSAndroid Build Coastguard Worker 472*90c8c64dSAndroid Build Coastguard Worker Throws: 473*90c8c64dSAndroid Build Coastguard Worker subprocess.CalledProcessError if exit status is non-zero. 474*90c8c64dSAndroid Build Coastguard Worker """ 475*90c8c64dSAndroid Build Coastguard Worker output = subprocess.check_output(command, shell=True) 476*90c8c64dSAndroid Build Coastguard Worker # For Python3.4, decode the byte string so it is usable. 477*90c8c64dSAndroid Build Coastguard Worker return output.decode(encoding='UTF-8') 478*90c8c64dSAndroid Build Coastguard Worker 479*90c8c64dSAndroid Build Coastguard Worker 480*90c8c64dSAndroid Build Coastguard Workerdef parse_args(): 481*90c8c64dSAndroid Build Coastguard Worker """Parses the CLI.""" 482*90c8c64dSAndroid Build Coastguard Worker parser = argparse.ArgumentParser( 483*90c8c64dSAndroid Build Coastguard Worker description=HELP_MESSAGE, 484*90c8c64dSAndroid Build Coastguard Worker formatter_class=argparse.RawDescriptionHelpFormatter) 485*90c8c64dSAndroid Build Coastguard Worker parser.add_argument( 486*90c8c64dSAndroid Build Coastguard Worker '-d', 487*90c8c64dSAndroid Build Coastguard Worker '--device', 488*90c8c64dSAndroid Build Coastguard Worker action='store_true', 489*90c8c64dSAndroid Build Coastguard Worker default=False, 490*90c8c64dSAndroid Build Coastguard Worker required=False, 491*90c8c64dSAndroid Build Coastguard Worker help='Whether or not to generate the privapp_permissions file for the ' 492*90c8c64dSAndroid Build Coastguard Worker 'build already on a device. See -s/--serial below for more ' 493*90c8c64dSAndroid Build Coastguard Worker 'details.' 494*90c8c64dSAndroid Build Coastguard Worker ) 495*90c8c64dSAndroid Build Coastguard Worker parser.add_argument( 496*90c8c64dSAndroid Build Coastguard Worker '-v', 497*90c8c64dSAndroid Build Coastguard Worker '--verbose', 498*90c8c64dSAndroid Build Coastguard Worker action='store_true', 499*90c8c64dSAndroid Build Coastguard Worker default=False, 500*90c8c64dSAndroid Build Coastguard Worker required=False, 501*90c8c64dSAndroid Build Coastguard Worker help='Whether or not to enable more verbose logs such as ' 502*90c8c64dSAndroid Build Coastguard Worker 'adb pull progress to be shown' 503*90c8c64dSAndroid Build Coastguard Worker ) 504*90c8c64dSAndroid Build Coastguard Worker parser.add_argument( 505*90c8c64dSAndroid Build Coastguard Worker '--adb', 506*90c8c64dSAndroid Build Coastguard Worker type=str, 507*90c8c64dSAndroid Build Coastguard Worker required=False, 508*90c8c64dSAndroid Build Coastguard Worker metavar='<ADB_PATH>', 509*90c8c64dSAndroid Build Coastguard Worker help='Path to adb. If none specified, uses the environment\'s adb.' 510*90c8c64dSAndroid Build Coastguard Worker ) 511*90c8c64dSAndroid Build Coastguard Worker parser.add_argument( 512*90c8c64dSAndroid Build Coastguard Worker '--aapt', 513*90c8c64dSAndroid Build Coastguard Worker type=str, 514*90c8c64dSAndroid Build Coastguard Worker required=False, 515*90c8c64dSAndroid Build Coastguard Worker metavar='<AAPT_PATH>', 516*90c8c64dSAndroid Build Coastguard Worker help='Path to aapt. If none specified, uses the environment\'s aapt.' 517*90c8c64dSAndroid Build Coastguard Worker ) 518*90c8c64dSAndroid Build Coastguard Worker parser.add_argument( 519*90c8c64dSAndroid Build Coastguard Worker '-s', 520*90c8c64dSAndroid Build Coastguard Worker '--serial', 521*90c8c64dSAndroid Build Coastguard Worker type=str, 522*90c8c64dSAndroid Build Coastguard Worker required=False, 523*90c8c64dSAndroid Build Coastguard Worker metavar='<SERIAL>', 524*90c8c64dSAndroid Build Coastguard Worker help='The serial of the device to generate permissions for. If no ' 525*90c8c64dSAndroid Build Coastguard Worker 'serial is given, it will pick the only device connected over ' 526*90c8c64dSAndroid Build Coastguard Worker 'adb. If multiple devices are found, it will default to ' 527*90c8c64dSAndroid Build Coastguard Worker '$ANDROID_SERIAL. Otherwise, the program will exit with error ' 528*90c8c64dSAndroid Build Coastguard Worker 'code 1. If -s is given, -d is not needed.' 529*90c8c64dSAndroid Build Coastguard Worker ) 530*90c8c64dSAndroid Build Coastguard Worker parser.add_argument( 531*90c8c64dSAndroid Build Coastguard Worker '-p', 532*90c8c64dSAndroid Build Coastguard Worker '--partitions', 533*90c8c64dSAndroid Build Coastguard Worker type=str, 534*90c8c64dSAndroid Build Coastguard Worker required=False, 535*90c8c64dSAndroid Build Coastguard Worker default='all', 536*90c8c64dSAndroid Build Coastguard Worker metavar='<PARTITION>', 537*90c8c64dSAndroid Build Coastguard Worker help='The target partition(s) to examine permissions for. ' 538*90c8c64dSAndroid Build Coastguard Worker 'It is set to "all" by default, which means all the partitions ' 539*90c8c64dSAndroid Build Coastguard Worker 'where priv-app diectory exists will be examined' 540*90c8c64dSAndroid Build Coastguard Worker 'Use "," as a delimiter when specifying multiple partitions. ' 541*90c8c64dSAndroid Build Coastguard Worker 'E.g. "system,product"' 542*90c8c64dSAndroid Build Coastguard Worker ) 543*90c8c64dSAndroid Build Coastguard Worker parser.add_argument( 544*90c8c64dSAndroid Build Coastguard Worker 'apks', 545*90c8c64dSAndroid Build Coastguard Worker nargs='*', 546*90c8c64dSAndroid Build Coastguard Worker type=str, 547*90c8c64dSAndroid Build Coastguard Worker help='A list of paths to priv-app APKs to generate permissions for. ' 548*90c8c64dSAndroid Build Coastguard Worker 'To make a path device-side, prefix the path with "device:".' 549*90c8c64dSAndroid Build Coastguard Worker ) 550*90c8c64dSAndroid Build Coastguard Worker parser.add_argument( 551*90c8c64dSAndroid Build Coastguard Worker '-w', 552*90c8c64dSAndroid Build Coastguard Worker '--writetodisk', 553*90c8c64dSAndroid Build Coastguard Worker action='store_true', 554*90c8c64dSAndroid Build Coastguard Worker default=False, 555*90c8c64dSAndroid Build Coastguard Worker required=False, 556*90c8c64dSAndroid Build Coastguard Worker help='Whether or not to store the generated permissions directly to ' 557*90c8c64dSAndroid Build Coastguard Worker 'a file. See --systemfile/--productfile for more information.' 558*90c8c64dSAndroid Build Coastguard Worker ) 559*90c8c64dSAndroid Build Coastguard Worker parser.add_argument( 560*90c8c64dSAndroid Build Coastguard Worker '--systemfile', 561*90c8c64dSAndroid Build Coastguard Worker default='./system.xml', 562*90c8c64dSAndroid Build Coastguard Worker required=False, 563*90c8c64dSAndroid Build Coastguard Worker help='Path to system permissions file. Default value is ./system.xml' 564*90c8c64dSAndroid Build Coastguard Worker ) 565*90c8c64dSAndroid Build Coastguard Worker parser.add_argument( 566*90c8c64dSAndroid Build Coastguard Worker '--productfile', 567*90c8c64dSAndroid Build Coastguard Worker default='./product.xml', 568*90c8c64dSAndroid Build Coastguard Worker required=False, 569*90c8c64dSAndroid Build Coastguard Worker help='Path to system permissions file. Default value is ./product.xml' 570*90c8c64dSAndroid Build Coastguard Worker ) 571*90c8c64dSAndroid Build Coastguard Worker cmd_args = parser.parse_args() 572*90c8c64dSAndroid Build Coastguard Worker 573*90c8c64dSAndroid Build Coastguard Worker return cmd_args 574*90c8c64dSAndroid Build Coastguard Worker 575*90c8c64dSAndroid Build Coastguard Worker 576*90c8c64dSAndroid Build Coastguard Workerdef create_permission_file(resources): 577*90c8c64dSAndroid Build Coastguard Worker """Prints out/creates permission file with missing permissions.""" 578*90c8c64dSAndroid Build Coastguard Worker # First extract privileged permissions from framework-res.apk 579*90c8c64dSAndroid Build Coastguard Worker priv_permissions = extract_priv_permissions(resources.aapt, 580*90c8c64dSAndroid Build Coastguard Worker resources.framework_res_apk) 581*90c8c64dSAndroid Build Coastguard Worker 582*90c8c64dSAndroid Build Coastguard Worker results = {} 583*90c8c64dSAndroid Build Coastguard Worker for p in resources.partitions: 584*90c8c64dSAndroid Build Coastguard Worker results[p], apps_redefine_base = \ 585*90c8c64dSAndroid Build Coastguard Worker generate_missing_permissions(resources, priv_permissions, p) 586*90c8c64dSAndroid Build Coastguard Worker enable_print = True 587*90c8c64dSAndroid Build Coastguard Worker vprint(enable_print, '#' * 80) 588*90c8c64dSAndroid Build Coastguard Worker vprint(enable_print, '#') 589*90c8c64dSAndroid Build Coastguard Worker if resources.writetodisk: 590*90c8c64dSAndroid Build Coastguard Worker # Check if it is likely a product partition 591*90c8c64dSAndroid Build Coastguard Worker if p.endswith('product'): 592*90c8c64dSAndroid Build Coastguard Worker out_file_name = resources.productfile; 593*90c8c64dSAndroid Build Coastguard Worker # Check if it is a system partition 594*90c8c64dSAndroid Build Coastguard Worker elif p.endswith('system'): 595*90c8c64dSAndroid Build Coastguard Worker out_file_name = resources.systemfile 596*90c8c64dSAndroid Build Coastguard Worker # Fallback to the partition name itself 597*90c8c64dSAndroid Build Coastguard Worker else: 598*90c8c64dSAndroid Build Coastguard Worker out_file_name = str(p).replace('/', '_') + '.xml' 599*90c8c64dSAndroid Build Coastguard Worker 600*90c8c64dSAndroid Build Coastguard Worker out_file = open(out_file_name, 'w') 601*90c8c64dSAndroid Build Coastguard Worker vprint(enable_print, '# %s XML written to %s:', p, out_file_name) 602*90c8c64dSAndroid Build Coastguard Worker vprint(enable_print, '#') 603*90c8c64dSAndroid Build Coastguard Worker vprint(enable_print, '#' * 80) 604*90c8c64dSAndroid Build Coastguard Worker print_xml(results[p], apps_redefine_base, p, out_file) 605*90c8c64dSAndroid Build Coastguard Worker out_file.close() 606*90c8c64dSAndroid Build Coastguard Worker else: 607*90c8c64dSAndroid Build Coastguard Worker vprint(enable_print, '# %s XML:', p) 608*90c8c64dSAndroid Build Coastguard Worker vprint(enable_print, '#') 609*90c8c64dSAndroid Build Coastguard Worker vprint(enable_print, '#' * 80) 610*90c8c64dSAndroid Build Coastguard Worker 611*90c8c64dSAndroid Build Coastguard Worker # Print it to stdout regardless of whether writing to a file or not 612*90c8c64dSAndroid Build Coastguard Worker print_xml(results[p], apps_redefine_base, p) 613*90c8c64dSAndroid Build Coastguard Worker 614*90c8c64dSAndroid Build Coastguard Worker 615*90c8c64dSAndroid Build Coastguard Workerdef generate_missing_permissions(resources, priv_permissions, partition): 616*90c8c64dSAndroid Build Coastguard Worker """Generates the missing permissions for the specified partition.""" 617*90c8c64dSAndroid Build Coastguard Worker # Parse base XML files in /etc dir, permissions listed there don't have 618*90c8c64dSAndroid Build Coastguard Worker # to be re-added 619*90c8c64dSAndroid Build Coastguard Worker base_permissions = {} 620*90c8c64dSAndroid Build Coastguard Worker base_xml_files = itertools.chain( 621*90c8c64dSAndroid Build Coastguard Worker list_xml_files(resources.permissions_dirs[partition]), 622*90c8c64dSAndroid Build Coastguard Worker list_xml_files(resources.sysconfig_dirs[partition])) 623*90c8c64dSAndroid Build Coastguard Worker 624*90c8c64dSAndroid Build Coastguard Worker for xml_file in base_xml_files: 625*90c8c64dSAndroid Build Coastguard Worker parse_config_xml(xml_file, base_permissions) 626*90c8c64dSAndroid Build Coastguard Worker 627*90c8c64dSAndroid Build Coastguard Worker apps_redefine_base = [] 628*90c8c64dSAndroid Build Coastguard Worker results = {} 629*90c8c64dSAndroid Build Coastguard Worker for priv_app in resources.privapp_apks[partition]: 630*90c8c64dSAndroid Build Coastguard Worker pkg_info = extract_pkg_and_requested_permissions(resources.aapt, 631*90c8c64dSAndroid Build Coastguard Worker priv_app) 632*90c8c64dSAndroid Build Coastguard Worker pkg_name = pkg_info['package_name'] 633*90c8c64dSAndroid Build Coastguard Worker # get intersection of what's requested by app and by framework 634*90c8c64dSAndroid Build Coastguard Worker priv_perms = get_priv_permissions(pkg_info['permissions'], 635*90c8c64dSAndroid Build Coastguard Worker priv_permissions) 636*90c8c64dSAndroid Build Coastguard Worker # Compute diff against permissions defined in base file 637*90c8c64dSAndroid Build Coastguard Worker if base_permissions and (pkg_name in base_permissions): 638*90c8c64dSAndroid Build Coastguard Worker base_permissions_pkg = base_permissions[pkg_name] 639*90c8c64dSAndroid Build Coastguard Worker priv_perms = remove_base_permissions(priv_perms, 640*90c8c64dSAndroid Build Coastguard Worker base_permissions_pkg) 641*90c8c64dSAndroid Build Coastguard Worker if priv_perms: 642*90c8c64dSAndroid Build Coastguard Worker apps_redefine_base.append(pkg_name) 643*90c8c64dSAndroid Build Coastguard Worker if priv_perms: 644*90c8c64dSAndroid Build Coastguard Worker results[pkg_name] = sorted(priv_perms) 645*90c8c64dSAndroid Build Coastguard Worker 646*90c8c64dSAndroid Build Coastguard Worker return results, apps_redefine_base 647*90c8c64dSAndroid Build Coastguard Worker 648*90c8c64dSAndroid Build Coastguard Worker 649*90c8c64dSAndroid Build Coastguard Workerdef print_xml(results, apps_redefine_base, partition, fd=sys.stdout): 650*90c8c64dSAndroid Build Coastguard Worker """Print results to the given file.""" 651*90c8c64dSAndroid Build Coastguard Worker fd.write('<?xml version="1.0" encoding="utf-8"?>\n') 652*90c8c64dSAndroid Build Coastguard Worker fd.write('<!-- for the partition: /%s -->\n' % partition) 653*90c8c64dSAndroid Build Coastguard Worker fd.write('<permissions>\n') 654*90c8c64dSAndroid Build Coastguard Worker for package_name in sorted(results): 655*90c8c64dSAndroid Build Coastguard Worker if package_name in apps_redefine_base: 656*90c8c64dSAndroid Build Coastguard Worker fd.write(' <!-- Additional permissions on top of %s -->\n' % 657*90c8c64dSAndroid Build Coastguard Worker BASE_XML_FILENAME) 658*90c8c64dSAndroid Build Coastguard Worker fd.write(' <privapp-permissions package="%s">\n' % package_name) 659*90c8c64dSAndroid Build Coastguard Worker for p in results[package_name]: 660*90c8c64dSAndroid Build Coastguard Worker fd.write(' <permission name="%s"/>\n' % p) 661*90c8c64dSAndroid Build Coastguard Worker fd.write(' </privapp-permissions>\n') 662*90c8c64dSAndroid Build Coastguard Worker fd.write('\n') 663*90c8c64dSAndroid Build Coastguard Worker 664*90c8c64dSAndroid Build Coastguard Worker fd.write('</permissions>\n') 665*90c8c64dSAndroid Build Coastguard Worker 666*90c8c64dSAndroid Build Coastguard Worker 667*90c8c64dSAndroid Build Coastguard Workerdef remove_base_permissions(priv_perms, base_perms): 668*90c8c64dSAndroid Build Coastguard Worker """Removes set of base_perms from set of priv_perms.""" 669*90c8c64dSAndroid Build Coastguard Worker if (not priv_perms) or (not base_perms): 670*90c8c64dSAndroid Build Coastguard Worker return priv_perms 671*90c8c64dSAndroid Build Coastguard Worker return set(priv_perms) - set(base_perms) 672*90c8c64dSAndroid Build Coastguard Worker 673*90c8c64dSAndroid Build Coastguard Worker 674*90c8c64dSAndroid Build Coastguard Workerdef get_priv_permissions(requested_perms, priv_perms): 675*90c8c64dSAndroid Build Coastguard Worker """Return only permissions that are in priv_perms set.""" 676*90c8c64dSAndroid Build Coastguard Worker return set(requested_perms).intersection(set(priv_perms)) 677*90c8c64dSAndroid Build Coastguard Worker 678*90c8c64dSAndroid Build Coastguard Worker 679*90c8c64dSAndroid Build Coastguard Workerdef list_xml_files(directory): 680*90c8c64dSAndroid Build Coastguard Worker """Returns a list of all .xml files within a given directory. 681*90c8c64dSAndroid Build Coastguard Worker 682*90c8c64dSAndroid Build Coastguard Worker Args: 683*90c8c64dSAndroid Build Coastguard Worker directory: the directory to look for xml files in. 684*90c8c64dSAndroid Build Coastguard Worker """ 685*90c8c64dSAndroid Build Coastguard Worker xml_files = [] 686*90c8c64dSAndroid Build Coastguard Worker for dirName, subdirList, file_list in os.walk(directory): 687*90c8c64dSAndroid Build Coastguard Worker for file in file_list: 688*90c8c64dSAndroid Build Coastguard Worker if file.endswith('.xml'): 689*90c8c64dSAndroid Build Coastguard Worker file_path = os.path.join(dirName, file) 690*90c8c64dSAndroid Build Coastguard Worker xml_files.append(file_path) 691*90c8c64dSAndroid Build Coastguard Worker return xml_files 692*90c8c64dSAndroid Build Coastguard Worker 693*90c8c64dSAndroid Build Coastguard Worker 694*90c8c64dSAndroid Build Coastguard Workerdef extract_pkg_and_requested_permissions(aapt, apk_path): 695*90c8c64dSAndroid Build Coastguard Worker """ 696*90c8c64dSAndroid Build Coastguard Worker Extract package name and list of requested permissions from the 697*90c8c64dSAndroid Build Coastguard Worker dump of manifest file 698*90c8c64dSAndroid Build Coastguard Worker """ 699*90c8c64dSAndroid Build Coastguard Worker aapt_args = ['d', 'permissions', apk_path] 700*90c8c64dSAndroid Build Coastguard Worker txt = aapt.call(aapt_args) 701*90c8c64dSAndroid Build Coastguard Worker 702*90c8c64dSAndroid Build Coastguard Worker permissions = [] 703*90c8c64dSAndroid Build Coastguard Worker package_name = None 704*90c8c64dSAndroid Build Coastguard Worker raw_lines = txt.split('\n') 705*90c8c64dSAndroid Build Coastguard Worker for line in raw_lines: 706*90c8c64dSAndroid Build Coastguard Worker regex = r"uses-permission.*: name='([\S]+)'" 707*90c8c64dSAndroid Build Coastguard Worker matches = re.search(regex, line) 708*90c8c64dSAndroid Build Coastguard Worker if matches: 709*90c8c64dSAndroid Build Coastguard Worker name = matches.group(1) 710*90c8c64dSAndroid Build Coastguard Worker permissions.append(name) 711*90c8c64dSAndroid Build Coastguard Worker regex = r'package: ([\S]+)' 712*90c8c64dSAndroid Build Coastguard Worker matches = re.search(regex, line) 713*90c8c64dSAndroid Build Coastguard Worker if matches: 714*90c8c64dSAndroid Build Coastguard Worker package_name = matches.group(1) 715*90c8c64dSAndroid Build Coastguard Worker 716*90c8c64dSAndroid Build Coastguard Worker return {'package_name': package_name, 'permissions': permissions} 717*90c8c64dSAndroid Build Coastguard Worker 718*90c8c64dSAndroid Build Coastguard Worker 719*90c8c64dSAndroid Build Coastguard Workerdef extract_priv_permissions(aapt, apk_path): 720*90c8c64dSAndroid Build Coastguard Worker """Extract signature|privileged permissions from dump of manifest file.""" 721*90c8c64dSAndroid Build Coastguard Worker aapt_args = ['d', 'xmltree', apk_path, 'AndroidManifest.xml'] 722*90c8c64dSAndroid Build Coastguard Worker txt = aapt.call(aapt_args) 723*90c8c64dSAndroid Build Coastguard Worker raw_lines = txt.split('\n') 724*90c8c64dSAndroid Build Coastguard Worker n = len(raw_lines) 725*90c8c64dSAndroid Build Coastguard Worker i = 0 726*90c8c64dSAndroid Build Coastguard Worker permissions_list = [] 727*90c8c64dSAndroid Build Coastguard Worker while i < n: 728*90c8c64dSAndroid Build Coastguard Worker line = raw_lines[i] 729*90c8c64dSAndroid Build Coastguard Worker if line.find('E: permission (') != -1: 730*90c8c64dSAndroid Build Coastguard Worker i += 1 731*90c8c64dSAndroid Build Coastguard Worker name = None 732*90c8c64dSAndroid Build Coastguard Worker level = None 733*90c8c64dSAndroid Build Coastguard Worker while i < n: 734*90c8c64dSAndroid Build Coastguard Worker line = raw_lines[i] 735*90c8c64dSAndroid Build Coastguard Worker if line.find('E: ') != -1: 736*90c8c64dSAndroid Build Coastguard Worker break 737*90c8c64dSAndroid Build Coastguard Worker matches = re.search(ANDROID_NAME_REGEX, line) 738*90c8c64dSAndroid Build Coastguard Worker if matches: 739*90c8c64dSAndroid Build Coastguard Worker name = matches.group(1) 740*90c8c64dSAndroid Build Coastguard Worker i += 1 741*90c8c64dSAndroid Build Coastguard Worker continue 742*90c8c64dSAndroid Build Coastguard Worker matches = re.search(ANDROID_PROTECTION_LEVEL_REGEX, line) 743*90c8c64dSAndroid Build Coastguard Worker if matches: 744*90c8c64dSAndroid Build Coastguard Worker level = int(matches.group(1), 16) 745*90c8c64dSAndroid Build Coastguard Worker i += 1 746*90c8c64dSAndroid Build Coastguard Worker continue 747*90c8c64dSAndroid Build Coastguard Worker i += 1 748*90c8c64dSAndroid Build Coastguard Worker if name and level and level & 0x12 == 0x12: 749*90c8c64dSAndroid Build Coastguard Worker permissions_list.append(name) 750*90c8c64dSAndroid Build Coastguard Worker else: 751*90c8c64dSAndroid Build Coastguard Worker i += 1 752*90c8c64dSAndroid Build Coastguard Worker 753*90c8c64dSAndroid Build Coastguard Worker return permissions_list 754*90c8c64dSAndroid Build Coastguard Worker 755*90c8c64dSAndroid Build Coastguard Worker 756*90c8c64dSAndroid Build Coastguard Workerdef parse_config_xml(base_xml, results): 757*90c8c64dSAndroid Build Coastguard Worker """Parse an XML file that will be used as base.""" 758*90c8c64dSAndroid Build Coastguard Worker dom = minidom.parse(base_xml) 759*90c8c64dSAndroid Build Coastguard Worker nodes = dom.getElementsByTagName('privapp-permissions') 760*90c8c64dSAndroid Build Coastguard Worker for node in nodes: 761*90c8c64dSAndroid Build Coastguard Worker permissions = (node.getElementsByTagName('permission') + 762*90c8c64dSAndroid Build Coastguard Worker node.getElementsByTagName('deny-permission')) 763*90c8c64dSAndroid Build Coastguard Worker package_name = node.getAttribute('package') 764*90c8c64dSAndroid Build Coastguard Worker plist = [] 765*90c8c64dSAndroid Build Coastguard Worker if package_name in results: 766*90c8c64dSAndroid Build Coastguard Worker plist = results[package_name] 767*90c8c64dSAndroid Build Coastguard Worker for p in permissions: 768*90c8c64dSAndroid Build Coastguard Worker perm_name = p.getAttribute('name') 769*90c8c64dSAndroid Build Coastguard Worker if perm_name: 770*90c8c64dSAndroid Build Coastguard Worker plist.append(perm_name) 771*90c8c64dSAndroid Build Coastguard Worker results[package_name] = plist 772*90c8c64dSAndroid Build Coastguard Worker return results 773*90c8c64dSAndroid Build Coastguard Worker 774*90c8c64dSAndroid Build Coastguard Worker 775*90c8c64dSAndroid Build Coastguard Workerdef cleanup(): 776*90c8c64dSAndroid Build Coastguard Worker """Cleans up temp files.""" 777*90c8c64dSAndroid Build Coastguard Worker for directory in temp_dirs: 778*90c8c64dSAndroid Build Coastguard Worker shutil.rmtree(directory, ignore_errors=True) 779*90c8c64dSAndroid Build Coastguard Worker for file in temp_files: 780*90c8c64dSAndroid Build Coastguard Worker os.remove(file) 781*90c8c64dSAndroid Build Coastguard Worker del temp_dirs[:] 782*90c8c64dSAndroid Build Coastguard Worker del temp_files[:] 783*90c8c64dSAndroid Build Coastguard Worker 784*90c8c64dSAndroid Build Coastguard Workerif __name__ == '__main__': 785*90c8c64dSAndroid Build Coastguard Worker args = parse_args() 786*90c8c64dSAndroid Build Coastguard Worker try: 787*90c8c64dSAndroid Build Coastguard Worker tool_resources = Resources( 788*90c8c64dSAndroid Build Coastguard Worker aapt_path=args.aapt, 789*90c8c64dSAndroid Build Coastguard Worker adb_path=args.adb, 790*90c8c64dSAndroid Build Coastguard Worker use_device=args.device, 791*90c8c64dSAndroid Build Coastguard Worker serial=args.serial, 792*90c8c64dSAndroid Build Coastguard Worker partitions=args.partitions, 793*90c8c64dSAndroid Build Coastguard Worker verbose=args.verbose, 794*90c8c64dSAndroid Build Coastguard Worker writetodisk=args.writetodisk, 795*90c8c64dSAndroid Build Coastguard Worker systemfile=args.systemfile, 796*90c8c64dSAndroid Build Coastguard Worker productfile=args.productfile, 797*90c8c64dSAndroid Build Coastguard Worker apks=args.apks 798*90c8c64dSAndroid Build Coastguard Worker ) 799*90c8c64dSAndroid Build Coastguard Worker create_permission_file(tool_resources) 800*90c8c64dSAndroid Build Coastguard Worker except MissingResourceError as e: 801*90c8c64dSAndroid Build Coastguard Worker print(str(e), file=sys.stderr) 802*90c8c64dSAndroid Build Coastguard Worker exit(1) 803*90c8c64dSAndroid Build Coastguard Worker finally: 804*90c8c64dSAndroid Build Coastguard Worker cleanup() 805