File: system_image_diff.py

package info (click to toggle)
android-platform-development 10.0.0%2Br36-1
  • links: PTS, VCS
  • area: main
  • in suites: bullseye
  • size: 135,564 kB
  • sloc: java: 160,253; xml: 127,434; python: 40,579; cpp: 17,579; sh: 2,569; javascript: 1,612; ansic: 879; lisp: 261; ruby: 183; makefile: 172; sql: 140; perl: 88
file content (170 lines) | stat: -rwxr-xr-x 5,660 bytes parent folder | download | duplicates (5)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
#!/usr/bin/env python3

import argparse
import csv
import glob
import json
import os
import sys

HELP_MSG = '''
This script computes the differences between two system images (system1 -
system2), and lists the files grouped by package. The difference is just based
on the existence of the file, not on its contents.
'''

VENDOR_PATH_MAP = {
    'vendor/google' : 'Google',
    'vendor/unbundled_google': 'Google',
    'vendor/verizon' : 'Verizon',
    'vendor/qcom' : 'Qualcomm',
    'vendor/tmobile' : 'TMobile',
    'vendor/mediatek' : 'Mediatek',
    'vendor/htc' : 'HTC',
    'vendor/realtek' : 'Realtek'
}

def _get_relative_out_path_from_root(out_path):
  """Given a path to a target out directory, get the relative path from the
  Android root.

  The module-info.json file paths are relative to the root source folder
  ie. one directory before out."""
  system_path = os.path.normpath(os.path.join(out_path, 'system'))
  system_path_dirs = system_path.split(os.sep)
  out_index = system_path_dirs.index("out")
  return os.path.join(*system_path_dirs[out_index:])

def system_files(path):
  """Returns an array of the files under /system, recursively, and ignoring
  symbolic-links"""
  system_files = []
  system_prefix = os.path.join(path, 'system')
  # Skip trailing '/'
  system_prefix_len = len(system_prefix) + 1

  for root, dirs, files in os.walk(system_prefix, topdown=True):
    for file in files:
      # Ignore symbolic links.
      if not os.path.islink(os.path.join(root, file)):
        system_files.append(os.path.join(root[system_prefix_len:], file))

  return system_files

def system_files_to_package_map(path):
  """Returns a dictionary mapping from each file in the /system partition to its
  package, according to modules-info.json."""
  system_files_to_package_map = {}
  system_prefix = _get_relative_out_path_from_root(path)
  # Skip trailing '/'
  system_prefix_len = len(system_prefix) + 1

  with open(os.path.join(path, 'module-info.json')) as module_info_json:
    module_info = json.load(module_info_json)
    for module in module_info:
      installs = module_info[module]['installed']
      for install in installs:
        if install.startswith(system_prefix):
          system_file = install[system_prefix_len:]
          # Not clear if collisions can ever happen in modules-info.json (e.g.
          # the same file installed by multiple packages), but it doesn't hurt
          # to check.
          if system_file in system_files_to_package_map:
            system_files_to_package_map[system_file] = "--multiple--"
          else:
            system_files_to_package_map[system_file] = module

  return system_files_to_package_map

def package_to_vendor_map(path):
  """Returns a dictionary mapping from each package in modules-info.json to its
  vendor. If a vendor cannot be found, it maps to "--unknown--". Those cases
  are:

    1. The package maps to multiple modules (e.g., one in APPS and one in
       SHARED_LIBRARIES.
    2. The path to the module is not one of the recognized vendor paths in
       VENDOR_PATH_MAP."""
  package_vendor_map = {}
  system_prefix = os.path.join(path, 'system')
  # Skip trailing '/'
  system_prefix_len = len(system_prefix) + 1
  vendor_prefixes = VENDOR_PATH_MAP.keys()

  with open(os.path.join(path, 'module-info.json')) as module_info_json:
    module_info = json.load(module_info_json)
    for module in module_info:
      paths = module_info[module]['path']
      vendor = ""
      if len(paths) == 1:
        path = paths[0]
        for prefix in vendor_prefixes:
          if path.startswith(prefix):
            vendor = VENDOR_PATH_MAP[prefix]
            break
        if vendor == "":
          vendor = "--unknown--"
      else:
        vendor = "--multiple--"
      package_vendor_map[module] = vendor

  return package_vendor_map

def main():
  parser = argparse.ArgumentParser(description=HELP_MSG)
  parser.add_argument("out1", help="First $OUT directory")
  parser.add_argument("out2", help="Second $OUT directory")
  args = parser.parse_args()

  system_files1 = system_files(args.out1)
  system_files2 = system_files(args.out2)
  system_files_diff = set(system_files1) - set(system_files2)

  system_files_map = system_files_to_package_map(args.out1)
  package_vendor_map = package_to_vendor_map(args.out1)
  packages = {}

  for file in system_files_diff:
    if file in system_files_map:
      package = system_files_map[file]
    else:
      package = "--unknown--"

    if package in packages:
      packages[package].append(file)
    else:
      packages[package] = [file]

  with open(os.path.join(args.out1, 'module-info.json')) as module_info_json:
    module_info = json.load(module_info_json)

  writer = csv.writer(sys.stdout, quoting = csv.QUOTE_NONNUMERIC,
                      delimiter = ',', lineterminator = '\n')
  for package, files in packages.iteritems():
    for file in files:
      # Group sources of the deltas.
      if package in package_vendor_map:
        vendor = package_vendor_map[package]
      else:
        vendor = "--unknown--"
      # Get file size.
      full_path = os.path.join(args.out1, 'system', file)
      size = os.stat(full_path).st_size
      if package in module_info.keys():
        module_path = module_info[package]['path']
      else:
        module_path = ''
      writer.writerow([
          # File that exists in out1 but not out2.
          file,
          # Module name that the file came from.
          package,
          # Path to the module.
          module_path,
          # File size.
          size,
          # Vendor owner.
          vendor])

if __name__ == '__main__':
  main()