1#!/usr/bin/env python3 2# Copyright (C) 2024 The Android Open Source Project 3# 4# Licensed under the Apache License, Version 2.0 (the "License"); 5# you may not use this file except in compliance with the License. 6# You may obtain a copy of the License at 7# 8# http://www.apache.org/licenses/LICENSE-2.0 9# 10# Unless required by applicable law or agreed to in writing, software 11# distributed under the License is distributed on an "AS IS" BASIS, 12# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13# See the License for the specific language governing permissions and 14# limitations under the License. 15 16import argparse 17import os 18import re 19import sys 20import tempfile 21import time 22 23from perfetto.prebuilts.perfetto_prebuilts import * 24 25PERMSISION_REGEX = re.compile(r'''uses-permission: name='(.*)'.*''') 26NAME_REGEX = re.compile(r'''package: name='(.*?)' .*''') 27 28 29def cmd(args: list[str]): 30 print('Running command ' + ' '.join(args)) 31 return subprocess.check_output(args) 32 33 34def main(): 35 parser = argparse.ArgumentParser() 36 parser.add_argument('--apk', help='Local APK to use instead of builtin') 37 38 args = parser.parse_args() 39 40 if args.apk: 41 apk = args.apk 42 else: 43 apk = download_or_get_cached( 44 'CtsPerfettoReporterApp.apk', 45 'https://storage.googleapis.com/perfetto/CtsPerfettoReporterApp.apk', 46 'f21dda36668c368793500b13724ab2a6231d12ded05746f7cfaaba4adedd7d46') 47 48 # Figure out the package name and the permissions we need 49 aapt = subprocess.check_output(['aapt', 'dump', 'badging', apk]).decode() 50 permission_names = [] 51 name = '' 52 for l in aapt.splitlines(): 53 name_match = NAME_REGEX.match(l) 54 if name_match: 55 name = name_match[1] 56 continue 57 58 permission_match = PERMSISION_REGEX.match(l) 59 if permission_match: 60 permission_names.append(permission_match[1]) 61 continue 62 63 # Root and remount the device. 64 cmd(['adb', 'root']) 65 cmd(['adb', 'wait-for-device']) 66 cmd(['adb', 'remount', '-R']) 67 input('The device might now reboot. If so, please unlock the device then ' 68 'press enter to continue') 69 cmd(['adb', 'wait-for-device']) 70 cmd(['adb', 'root']) 71 cmd(['adb', 'wait-for-device']) 72 cmd(['adb', 'remount', '-R']) 73 74 # Write out the permission file on device. 75 permissions = '\n'.join( 76 f'''<permission name='{p}' />''' for p in permission_names) 77 permission_file_contents = f''' 78 <permissions> 79 <privapp-permissions package="{name}"> 80 {permissions} 81 </privapp-permissions> 82 </permissions> 83 ''' 84 with tempfile.NamedTemporaryFile() as f: 85 f.write(permission_file_contents.encode()) 86 f.flush() 87 88 cmd([ 89 'adb', 'push', f.name, 90 f'/system/etc/permissions/privapp-permissions-{name}.xml' 91 ]) 92 93 # Stop system_server, push the apk on device and restart system_server 94 priv_app_path = f'/system/priv-app/{name}/{name}.apk' 95 cmd(['adb', 'shell', 'stop']) 96 cmd(['adb', 'push', apk, priv_app_path]) 97 cmd(['adb', 'shell', 'start']) 98 cmd(['adb', 'wait-for-device']) 99 time.sleep(10) 100 101 # Wait for system_server and package manager to come up. 102 while 'system_server' not in cmd(['adb', 'shell', 'ps']).decode(): 103 time.sleep(1) 104 while True: 105 ps = set([ 106 l.strip() 107 for l in cmd(['adb', 'shell', 'dumpsys', '-l']).decode().splitlines() 108 ]) 109 if 'storaged' in ps and 'settings' in ps and 'package' in ps: 110 break 111 time.sleep(1) 112 113 # Install the actual APK. 114 cmd(['adb', 'shell', 'pm', 'install', '-r', '-d', '-g', '-t', priv_app_path]) 115 116 return 0 117 118 119sys.exit(main()) 120