• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1#!/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()
176