blob: 35acde349e462c2fbee4d4fa61efad008a45fabd [file] [log] [blame]
Daniel Normanb8d52a22020-10-26 17:55:00 -07001#!/usr/bin/env python
2#
3# Copyright (C) 2019 The Android Open Source Project
4#
5# Licensed under the Apache License, Version 2.0 (the "License");
6# you may not use this file except in compliance with the License.
7# You may obtain a copy of the License at
8#
9# http://www.apache.org/licenses/LICENSE-2.0
10#
11# Unless required by applicable law or agreed to in writing, software
12# distributed under the License is distributed on an "AS IS" BASIS,
13# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14# See the License for the specific language governing permissions and
15# limitations under the License.
16#
17"""Find APK sharedUserId violators.
18
19Usage: find_shareduid_violation [args]
20
21 --product_out
22 PRODUCT_OUT directory
23
24 --aapt
25 Path to aapt or aapt2
26
27 --copy_out_system
28 TARGET_COPY_OUT_SYSTEM
29
30 --copy_out_vendor_
31 TARGET_COPY_OUT_VENDOR
32
33 --copy_out_product
34 TARGET_COPY_OUT_PRODUCT
35
36 --copy_out_system_ext
37 TARGET_COPY_OUT_SYSTEM_EXT
38"""
39
40import json
41import logging
42import os
43import re
44import subprocess
45import sys
46
47from collections import defaultdict
48from glob import glob
49
50import common
51
52logger = logging.getLogger(__name__)
53
54OPTIONS = common.OPTIONS
55OPTIONS.product_out = os.environ.get("PRODUCT_OUT")
56OPTIONS.aapt = "aapt2"
57OPTIONS.copy_out_system = "system"
58OPTIONS.copy_out_vendor = "vendor"
59OPTIONS.copy_out_product = "product"
60OPTIONS.copy_out_system_ext = "system_ext"
61
62
63def execute(cmd):
64 p = subprocess.Popen(
65 cmd, shell=True, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
66 out, err = map(lambda b: b.decode("utf-8"), p.communicate())
67 return p.returncode == 0, out, err
68
69
70def make_aapt_cmds(aapt, apk):
71 return [
72 aapt + " dump " + apk + " --file AndroidManifest.xml",
73 aapt + " dump xmltree " + apk + " --file AndroidManifest.xml"
74 ]
75
76
77def extract_shared_uid(aapt, apk):
78 for cmd in make_aapt_cmds(aapt, apk):
79 success, manifest, error_msg = execute(cmd)
80 if success:
81 break
82 else:
83 logger.error(error_msg)
84 sys.exit()
85
86 pattern = re.compile(r"sharedUserId.*=\"([^\"]*)")
87
88 for line in manifest.split("\n"):
89 match = pattern.search(line)
90 if match:
91 return match.group(1)
92 return None
93
94
95def FindShareduidViolation(product_out, partition_map, aapt="aapt2"):
96 """Find sharedUserId violators in the given partitions.
97
98 Args:
99 product_out: The base directory containing the partition directories.
100 partition_map: A map of partition name -> directory name.
101 aapt: The name of the aapt binary. Defaults to aapt2.
102
103 Returns:
104 A string containing a JSON object describing the shared UIDs.
105 """
106 shareduid_app_dict = defaultdict(lambda: defaultdict(list))
107
108 for part, location in partition_map.items():
109 for f in glob(os.path.join(product_out, location, "*", "*", "*.apk")):
110 apk_file = os.path.basename(f)
111 shared_uid = extract_shared_uid(aapt, f)
112
113 if shared_uid is None:
114 continue
115 shareduid_app_dict[shared_uid][part].append(apk_file)
116
117 # Only output sharedUserId values that appear in >1 partition.
118 output = {}
119 for uid, partitions in shareduid_app_dict.items():
120 if len(partitions) > 1:
121 output[uid] = shareduid_app_dict[uid]
122
123 return json.dumps(output, indent=2, sort_keys=True)
124
125
126def main():
127 common.InitLogging()
128
129 def option_handler(o, a):
130 if o == "--product_out":
131 OPTIONS.product_out = a
132 elif o == "--aapt":
133 OPTIONS.aapt = a
134 elif o == "--copy_out_system":
135 OPTIONS.copy_out_system = a
136 elif o == "--copy_out_vendor":
137 OPTIONS.copy_out_vendor = a
138 elif o == "--copy_out_product":
139 OPTIONS.copy_out_product = a
140 elif o == "--copy_out_system_ext":
141 OPTIONS.copy_out_system_ext = a
142 else:
143 return False
144 return True
145
146 args = common.ParseOptions(
147 sys.argv[1:],
148 __doc__,
149 extra_long_opts=[
150 "product_out=",
151 "aapt=",
152 "copy_out_system=",
153 "copy_out_vendor=",
154 "copy_out_product=",
155 "copy_out_system_ext=",
156 ],
157 extra_option_handler=option_handler)
158
159 if args:
160 common.Usage(__doc__)
161 sys.exit(1)
162
163 partition_map = {
164 "system": OPTIONS.copy_out_system,
165 "vendor": OPTIONS.copy_out_vendor,
166 "product": OPTIONS.copy_out_product,
167 "system_ext": OPTIONS.copy_out_system_ext,
168 }
169
170 print(
171 FindShareduidViolation(OPTIONS.product_out, partition_map, OPTIONS.aapt))
172
173
174if __name__ == "__main__":
175 main()