• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1#!/usr/bin/env python3
2# -*- coding: utf-8 -*-
3
4# Copyright (C) 2019 The Android Open Source Project
5#
6# Licensed under the Apache License, Version 2.0 (the "License");
7# you may not use this file except in compliance with the License.
8# You may obtain a copy of the License at
9#
10#      http://www.apache.org/licenses/LICENSE-2.0
11#
12# Unless required by applicable law or agreed to in writing, software
13# distributed under the License is distributed on an "AS IS" BASIS,
14# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15# See the License for the specific language governing permissions and
16# limitations under the License.
17#
18
19import argparse
20import fnmatch
21import logging
22import os
23import os.path
24import shutil
25import subprocess
26import sys
27import zipfile
28
29logging.basicConfig(format='%(message)s')
30
31# Flavors of ART APEX package.
32FLAVOR_RELEASE = 'release'
33FLAVOR_DEBUG = 'debug'
34FLAVOR_TESTING = 'testing'
35FLAVOR_AUTO = 'auto'
36FLAVORS_ALL = [FLAVOR_RELEASE, FLAVOR_DEBUG, FLAVOR_TESTING, FLAVOR_AUTO]
37
38# Bitness options for APEX package
39BITNESS_32 = '32'
40BITNESS_64 = '64'
41BITNESS_MULTILIB = 'multilib'
42BITNESS_AUTO = 'auto'
43BITNESS_ALL = [BITNESS_32, BITNESS_64, BITNESS_MULTILIB, BITNESS_AUTO]
44
45# Architectures supported by APEX packages.
46ARCHS_32 = ["arm", "x86"]
47ARCHS_64 = ["arm64", "riscv64", "x86_64"]
48
49# Multilib options
50MULTILIB_32 = '32'
51MULTILIB_64 = '64'
52MULTILIB_BOTH = 'both'
53MULTILIB_FIRST = 'first'
54
55# Directory containing ART tests within an ART APEX (if the package includes
56# any). ART test executables are installed in `bin/art/<arch>`. Segregating
57# tests by architecture is useful on devices supporting more than one
58# architecture, as it permits testing all of them using a single ART APEX
59# package.
60ART_TEST_DIR = 'bin/art'
61
62
63# Test if a given variable is set to a string "true".
64def isEnvTrue(var):
65  return var in os.environ and os.environ[var] == 'true'
66
67
68def extract_apex(apex_path, deapexer_path, debugfs_path, fsckerofs_path,
69                 blkid_path, tmpdir):
70  _, apex_name = os.path.split(apex_path)
71  extract_path = os.path.join(tmpdir, apex_name)
72  if os.path.exists(extract_path):
73    shutil.rmtree(extract_path)
74  subprocess.check_call([deapexer_path, '--debugfs', debugfs_path,
75                         '--fsckerofs', fsckerofs_path, '--blkid', blkid_path,
76                         'extract', apex_path, extract_path],
77                        stdout=subprocess.DEVNULL)
78  return extract_path
79
80
81class FSObject:
82  def __init__(self, name, is_dir, is_exec, is_symlink, size):
83    self.name = name
84    self.is_dir = is_dir
85    self.is_exec = is_exec
86    self.is_symlink = is_symlink
87    self.size = size
88
89  def __str__(self):
90    return '%s(dir=%r,exec=%r,symlink=%r,size=%d)' \
91             % (self.name, self.is_dir, self.is_exec, self.is_symlink, self.size)
92
93
94class TargetApexProvider:
95  def __init__(self, apex):
96    self._folder_cache = {}
97    self._apex = apex
98
99  def get(self, path):
100    apex_dir, name = os.path.split(path)
101    if not apex_dir:
102      apex_dir = '.'
103    apex_map = self.read_dir(apex_dir)
104    return apex_map[name] if name in apex_map else None
105
106  def read_dir(self, apex_dir):
107    if apex_dir in self._folder_cache:
108      return self._folder_cache[apex_dir]
109    apex_map = {}
110    dirname = os.path.join(self._apex, apex_dir)
111    if os.path.exists(dirname):
112      for basename in os.listdir(dirname):
113        filepath = os.path.join(dirname, basename)
114        is_dir = os.path.isdir(filepath)
115        is_exec = os.access(filepath, os.X_OK)
116        is_symlink = os.path.islink(filepath)
117        if is_symlink:
118          # Report the length of the symlink's target's path as file size, like `ls`.
119          size = len(os.readlink(filepath))
120        else:
121          size = os.path.getsize(filepath)
122        apex_map[basename] = FSObject(basename, is_dir, is_exec, is_symlink, size)
123    self._folder_cache[apex_dir] = apex_map
124    return apex_map
125
126
127class HostApexProvider:
128  def __init__(self, apex, tmpdir):
129    self._tmpdir = tmpdir
130    self._folder_cache = {}
131    self._payload = os.path.join(self._tmpdir, 'apex_payload.zip')
132    # Extract payload to tmpdir.
133    apex_zip = zipfile.ZipFile(apex)
134    apex_zip.extract('apex_payload.zip', tmpdir)
135
136  def __del__(self):
137    # Delete temps.
138    if os.path.exists(self._payload):
139      os.remove(self._payload)
140
141  def get(self, path):
142    apex_dir, name = os.path.split(path)
143    if not apex_dir:
144      apex_dir = ''
145    apex_map = self.read_dir(apex_dir)
146    return apex_map[name] if name in apex_map else None
147
148  def read_dir(self, apex_dir):
149    if apex_dir in self._folder_cache:
150      return self._folder_cache[apex_dir]
151    if not self._folder_cache:
152      self.parse_zip()
153    if apex_dir in self._folder_cache:
154      return self._folder_cache[apex_dir]
155    return {}
156
157  def parse_zip(self):
158    apex_zip = zipfile.ZipFile(self._payload)
159    infos = apex_zip.infolist()
160    for zipinfo in infos:
161      path = zipinfo.filename
162
163      # Assume no empty file is stored.
164      assert path
165
166      def get_octal(val, index):
167        return (val >> (index * 3)) & 0x7
168
169      def bits_is_exec(val):
170        # TODO: Enforce group/other, too?
171        return get_octal(val, 2) & 1 == 1
172
173      is_zipinfo = True
174      while path:
175        apex_dir, base = os.path.split(path)
176        # TODO: If directories are stored, base will be empty.
177
178        if apex_dir not in self._folder_cache:
179          self._folder_cache[apex_dir] = {}
180        dir_map = self._folder_cache[apex_dir]
181        if base not in dir_map:
182          if is_zipinfo:
183            bits = (zipinfo.external_attr >> 16) & 0xFFFF
184            is_dir = get_octal(bits, 4) == 4
185            is_symlink = get_octal(bits, 4) == 2
186            is_exec = bits_is_exec(bits)
187            size = zipinfo.file_size
188          else:
189            is_exec = False  # Seems we can't get this easily?
190            is_symlink = False
191            is_dir = True
192            # Use a negative value as an indicator of undefined/unknown size.
193            size = -1
194          dir_map[base] = FSObject(base, is_dir, is_exec, is_symlink, size)
195        is_zipinfo = False
196        path = apex_dir
197
198
199# DO NOT USE DIRECTLY! This is an "abstract" base class.
200class Checker:
201  def __init__(self, provider):
202    self._provider = provider
203    self._errors = 0
204    self._expected_file_globs = set()
205
206  def fail(self, msg, *fail_args):
207    self._errors += 1
208    logging.error(msg, *fail_args)
209
210  def error_count(self):
211    return self._errors
212
213  def reset_errors(self):
214    self._errors = 0
215
216  def is_file(self, path):
217    fs_object = self._provider.get(path)
218    if fs_object is None:
219      return False, 'Could not find %s'
220    if fs_object.is_dir:
221      return False, '%s is a directory'
222    if fs_object.is_symlink:
223      return False, '%s is a symlink'
224    return True, ''
225
226  def is_dir(self, path):
227    fs_object = self._provider.get(path)
228    if fs_object is None:
229      return False, 'Could not find %s'
230    if not fs_object.is_dir:
231      return False, '%s is not a directory'
232    return True, ''
233
234  def check_file(self, path):
235    ok, msg = self.is_file(path)
236    if not ok:
237      self.fail(msg, path)
238    self._expected_file_globs.add(path)
239    return ok
240
241  def check_executable(self, filename):
242    path = 'bin/%s' % filename
243    if not self.check_file(path):
244      return
245    if not self._provider.get(path).is_exec:
246      self.fail('%s is not executable', path)
247
248  def check_executable_symlink(self, filename):
249    path = 'bin/%s' % filename
250    fs_object = self._provider.get(path)
251    if fs_object is None:
252      self.fail('Could not find %s', path)
253      return
254    if fs_object.is_dir:
255      self.fail('%s is a directory', path)
256      return
257    if not fs_object.is_symlink:
258      self.fail('%s is not a symlink', path)
259    self._expected_file_globs.add(path)
260
261  def arch_dirs_for_path(self, path, multilib=None):
262    # Look for target-specific subdirectories for the given directory path.
263    # This is needed because the list of build targets is not propagated
264    # to this script.
265    #
266    # TODO(b/123602136): Pass build target information to this script and fix
267    # all places where this function in used (or similar workarounds).
268    dirs = []
269    for arch in self.possible_archs(multilib):
270      dir = '%s/%s' % (path, arch)
271      found, _ = self.is_dir(dir)
272      if found:
273        dirs.append(dir)
274    return dirs
275
276  def check_art_test_executable(self, filename, multilib=None):
277    dirs = self.arch_dirs_for_path(ART_TEST_DIR, multilib)
278    if not dirs:
279      self.fail('Directories for ART test binary missing: %s', filename)
280      return
281    for dir in dirs:
282      test_path = '%s/%s' % (dir, filename)
283      self._expected_file_globs.add(test_path)
284      file_obj = self._provider.get(test_path)
285      if not file_obj:
286        self.fail('ART test binary missing: %s', test_path)
287      elif not file_obj.is_exec:
288        self.fail('%s is not executable', test_path)
289
290  def check_art_test_data(self, filename):
291    dirs = self.arch_dirs_for_path(ART_TEST_DIR)
292    if not dirs:
293      self.fail('Directories for ART test data missing: %s', filename)
294      return
295    for dir in dirs:
296      if not self.check_file('%s/%s' % (dir, filename)):
297        return
298
299  def check_single_library(self, filename):
300    lib_path = 'lib/%s' % filename
301    lib64_path = 'lib64/%s' % filename
302    lib_is_file, _ = self.is_file(lib_path)
303    if lib_is_file:
304      self._expected_file_globs.add(lib_path)
305    lib64_is_file, _ = self.is_file(lib64_path)
306    if lib64_is_file:
307      self._expected_file_globs.add(lib64_path)
308    if not lib_is_file and not lib64_is_file:
309      self.fail('Library missing: %s', filename)
310
311  def check_dexpreopt(self, basename):
312    dirs = self.arch_dirs_for_path('javalib')
313    for dir in dirs:
314      for ext in ['art', 'oat', 'vdex']:
315        self.check_file('%s/%s.%s' % (dir, basename, ext))
316
317  def check_java_library(self, basename):
318    return self.check_file('javalib/%s.jar' % basename)
319
320  def ignore_path(self, path_glob):
321    self._expected_file_globs.add(path_glob)
322
323  def check_optional_art_test_executable(self, filename):
324    for arch in self.possible_archs():
325      self.ignore_path('%s/%s/%s' % (ART_TEST_DIR, arch, filename))
326
327  def check_no_superfluous_files(self, dir_path):
328    paths = []
329    for name in sorted(self._provider.read_dir(dir_path).keys()):
330      if name not in ('.', '..'):
331        paths.append(os.path.join(dir_path, name))
332    expected_paths = set()
333    dir_prefix = dir_path + '/'
334    for path_glob in self._expected_file_globs:
335      expected_paths |= set(fnmatch.filter(paths, path_glob))
336      # If there are globs in subdirectories of dir_path we want to match their
337      # path segments at this directory level.
338      if path_glob.startswith(dir_prefix):
339        subpath = path_glob[len(dir_prefix):]
340        subpath_first_segment, _, _ = subpath.partition('/')
341        expected_paths |= set(fnmatch.filter(paths, dir_prefix + subpath_first_segment))
342    for unexpected_path in set(paths) - expected_paths:
343      self.fail('Unexpected file \'%s\'', unexpected_path)
344
345  # Just here for docs purposes, even if it isn't good Python style.
346
347  def check_symlinked_multilib_executable(self, filename):
348    """Check bin/filename32, and/or bin/filename64, with symlink bin/filename."""
349    raise NotImplementedError
350
351  def check_symlinked_first_executable(self, filename):
352    """Check bin/filename32, and/or bin/filename64, with symlink bin/filename."""
353    raise NotImplementedError
354
355  def check_native_library(self, basename):
356    """Check lib/basename.so, and/or lib64/basename.so."""
357    raise NotImplementedError
358
359  def check_optional_native_library(self, basename_glob):
360    """Allow lib/basename.so and/or lib64/basename.so to exist."""
361    raise NotImplementedError
362
363  def check_prefer64_library(self, basename):
364    """Check lib64/basename.so, or lib/basename.so on 32 bit only."""
365    raise NotImplementedError
366
367  def possible_archs(self, multilib=None):
368    """Returns names of possible archs."""
369    raise NotImplementedError
370
371class Arch32Checker(Checker):
372  def check_symlinked_multilib_executable(self, filename):
373    self.check_executable('%s32' % filename)
374    self.check_executable_symlink(filename)
375
376  def check_symlinked_first_executable(self, filename):
377    self.check_executable('%s32' % filename)
378    self.check_executable_symlink(filename)
379
380  def check_native_library(self, basename):
381    # TODO: Use $TARGET_ARCH (e.g. check whether it is "arm" or "arm64") to improve
382    # the precision of this test?
383    self.check_file('lib/%s.so' % basename)
384
385  def check_optional_native_library(self, basename_glob):
386    self.ignore_path('lib/%s.so' % basename_glob)
387
388  def check_prefer64_library(self, basename):
389    self.check_native_library(basename)
390
391  def possible_archs(self, multilib=None):
392    return ARCHS_32
393
394class Arch64Checker(Checker):
395  def check_symlinked_multilib_executable(self, filename):
396    self.check_executable('%s64' % filename)
397    self.check_executable_symlink(filename)
398
399  def check_symlinked_first_executable(self, filename):
400    self.check_executable('%s64' % filename)
401    self.check_executable_symlink(filename)
402
403  def check_native_library(self, basename):
404    # TODO: Use $TARGET_ARCH (e.g. check whether it is "arm" or "arm64") to improve
405    # the precision of this test?
406    self.check_file('lib64/%s.so' % basename)
407
408  def check_optional_native_library(self, basename_glob):
409    self.ignore_path('lib64/%s.so' % basename_glob)
410
411  def check_prefer64_library(self, basename):
412    self.check_native_library(basename)
413
414  def possible_archs(self, multilib=None):
415    return ARCHS_64
416
417
418class MultilibChecker(Checker):
419  def check_symlinked_multilib_executable(self, filename):
420    self.check_executable('%s32' % filename)
421    self.check_executable('%s64' % filename)
422    self.check_executable_symlink(filename)
423
424  def check_symlinked_first_executable(self, filename):
425    self.check_executable('%s64' % filename)
426    self.check_executable_symlink(filename)
427
428  def check_native_library(self, basename):
429    # TODO: Use $TARGET_ARCH (e.g. check whether it is "arm" or "arm64") to improve
430    # the precision of this test?
431    self.check_file('lib/%s.so' % basename)
432    self.check_file('lib64/%s.so' % basename)
433
434  def check_optional_native_library(self, basename_glob):
435    self.ignore_path('lib/%s.so' % basename_glob)
436    self.ignore_path('lib64/%s.so' % basename_glob)
437
438  def check_prefer64_library(self, basename):
439    self.check_file('lib64/%s.so' % basename)
440
441  def possible_archs(self, multilib=None):
442    if multilib is None or multilib == MULTILIB_BOTH:
443      return ARCHS_32 + ARCHS_64
444    if multilib == MULTILIB_FIRST or multilib == MULTILIB_64:
445      return ARCHS_64
446    elif multilib == MULTILIB_32:
447      return ARCHS_32
448    self.fail('Unrecognized multilib option "%s"', multilib)
449
450
451class ReleaseChecker:
452  def __init__(self, checker):
453    self._checker = checker
454
455  def __str__(self):
456    return 'Release Checker'
457
458  def run(self):
459    # Check the Protocol Buffers APEX manifest.
460    self._checker.check_file('apex_manifest.pb')
461
462    # Check binaries for ART.
463    self._checker.check_executable('dexdump')
464    self._checker.check_executable('dexlist')
465    self._checker.check_executable('dexoptanalyzer')
466    self._checker.check_executable('profman')
467    self._checker.check_symlinked_multilib_executable('dalvikvm')
468
469    # Check exported libraries for ART.
470    self._checker.check_native_library('libdexfile')
471    self._checker.check_native_library('libnativebridge')
472    self._checker.check_native_library('libnativehelper')
473    self._checker.check_native_library('libnativeloader')
474
475    # Check internal libraries for ART.
476    self._checker.check_native_library('libadbconnection')
477    self._checker.check_native_library('libart')
478    self._checker.check_native_library('libart-compiler')
479    self._checker.check_native_library('libart-dexlayout')
480    self._checker.check_native_library('libart-disassembler')
481    self._checker.check_native_library('libartbase')
482    self._checker.check_native_library('libartpalette')
483    self._checker.check_native_library('libarttools')
484    self._checker.check_native_library('libdt_fd_forward')
485    self._checker.check_native_library('libopenjdkjvm')
486    self._checker.check_native_library('libopenjdkjvmti')
487    self._checker.check_native_library('libprofile')
488    self._checker.check_native_library('libsigchain')
489
490    # Check Java libraries for Managed Core Library.
491    self._checker.check_java_library('apache-xml')
492    self._checker.check_java_library('bouncycastle')
493    self._checker.check_java_library('core-libart')
494    self._checker.check_java_library('core-oj')
495    self._checker.check_java_library('okhttp')
496    if isEnvTrue('EMMA_INSTRUMENT_FRAMEWORK'):
497      # In coverage builds jacoco is added to the list of ART apex jars.
498      self._checker.check_java_library('jacocoagent')
499
500    # Check internal native libraries for Managed Core Library.
501    self._checker.check_native_library('libjavacore')
502    self._checker.check_native_library('libopenjdk')
503
504    # Check internal native library dependencies.
505    #
506    # Any internal dependency not listed here will cause a failure in
507    # NoSuperfluousLibrariesChecker. Internal dependencies are generally just
508    # implementation details, but in the release package we want to track them
509    # because a) they add to the package size and the RAM usage (in particular
510    # if the library is also present in /system or another APEX and hence might
511    # get loaded twice through linker namespace separation), and b) we need to
512    # catch invalid dependencies on /system or other APEXes that should go
513    # through an exported library with stubs (b/128708192 tracks implementing a
514    # better approach for that).
515    self._checker.check_native_library('libbase')
516    self._checker.check_native_library('libc++')
517    self._checker.check_native_library('libdt_socket')
518    self._checker.check_native_library('libjdwp')
519    self._checker.check_native_library('liblz4')
520    self._checker.check_native_library('liblzma')
521    self._checker.check_native_library('libnpt')
522    self._checker.check_native_library('libunwindstack')
523
524    # Allow extra dependencies that appear in ASAN builds.
525    self._checker.check_optional_native_library('libclang_rt.asan*')
526    self._checker.check_optional_native_library('libclang_rt.hwasan*')
527    self._checker.check_optional_native_library('libclang_rt.ubsan*')
528
529    # Check dexpreopt files for libcore bootclasspath jars.
530    self._checker.check_dexpreopt('boot')
531    self._checker.check_dexpreopt('boot-apache-xml')
532    self._checker.check_dexpreopt('boot-bouncycastle')
533    self._checker.check_dexpreopt('boot-core-libart')
534    self._checker.check_dexpreopt('boot-okhttp')
535    if isEnvTrue('EMMA_INSTRUMENT_FRAMEWORK'):
536      # In coverage builds the ART boot image includes jacoco.
537      self._checker.check_dexpreopt('boot-jacocoagent')
538
539class ReleaseTargetChecker:
540  def __init__(self, checker):
541    self._checker = checker
542
543  def __str__(self):
544    return 'Release (Target) Checker'
545
546  def run(self):
547    # We don't check for the presence of the JSON APEX manifest (file
548    # `apex_manifest.json`, only present in target APEXes), as it is only
549    # included for compatibility reasons with Android Q and will likely be
550    # removed in Android R.
551
552    # Check binaries for ART.
553    self._checker.check_executable('art_boot')
554    self._checker.check_executable('art_exec')
555    self._checker.check_executable('artd')
556    self._checker.check_executable('oatdump')
557    self._checker.check_executable("odrefresh")
558    self._checker.check_symlinked_multilib_executable('dex2oat')
559
560    # Check internal libraries for ART.
561    self._checker.check_native_library('libartservice')
562    self._checker.check_native_library('libperfetto_hprof')
563
564    # Check internal Java libraries
565    self._checker.check_java_library("service-art")
566
567    # Check exported native libraries for Managed Core Library.
568    self._checker.check_native_library('libandroidio')
569
570    # Check internal native library dependencies.
571    self._checker.check_native_library('libcrypto')
572    self._checker.check_native_library('libexpat')
573
574
575class ReleaseHostChecker:
576  def __init__(self, checker):
577    self._checker = checker
578
579  def __str__(self):
580    return 'Release (Host) Checker'
581
582  def run(self):
583    # Check binaries for ART.
584    self._checker.check_executable('hprof-conv')
585    self._checker.check_symlinked_first_executable('dex2oatd')
586    self._checker.check_symlinked_first_executable('dex2oat')
587
588    # Check exported native libraries for Managed Core Library.
589    self._checker.check_native_library('libicu')
590    self._checker.check_native_library('libandroidio')
591
592    # Check internal libraries for Managed Core Library.
593    self._checker.check_native_library('libexpat-host')
594    self._checker.check_native_library('libz-host')
595
596
597class DebugChecker:
598  def __init__(self, checker):
599    self._checker = checker
600
601  def __str__(self):
602    return 'Debug Checker'
603
604  def run(self):
605    # Check binaries for ART.
606    self._checker.check_executable('dexdiag')
607    self._checker.check_executable('dexanalyze')
608    self._checker.check_executable('dexlayout')
609    self._checker.check_symlinked_multilib_executable('imgdiag')
610
611    # Check debug binaries for ART.
612    self._checker.check_executable('dexlayoutd')
613    self._checker.check_executable('dexoptanalyzerd')
614    self._checker.check_symlinked_multilib_executable('imgdiagd')
615    self._checker.check_executable('profmand')
616
617    # Check exported libraries for ART.
618    self._checker.check_native_library('libdexfiled')
619
620    # Check internal libraries for ART.
621    self._checker.check_native_library('libadbconnectiond')
622    self._checker.check_native_library('libartbased')
623    self._checker.check_native_library('libartd')
624    self._checker.check_native_library('libartd-compiler')
625    self._checker.check_native_library('libartd-dexlayout')
626    self._checker.check_native_library('libartd-disassembler')
627    self._checker.check_native_library('libopenjdkjvmd')
628    self._checker.check_native_library('libopenjdkjvmtid')
629    self._checker.check_native_library('libprofiled')
630
631    # Check internal libraries for Managed Core Library.
632    self._checker.check_native_library('libopenjdkd')
633
634
635class DebugTargetChecker:
636  def __init__(self, checker):
637    self._checker = checker
638
639  def __str__(self):
640    return 'Debug (Target) Checker'
641
642  def run(self):
643    # Check ART debug binaries.
644    self._checker.check_executable('oatdumpd')
645    self._checker.check_symlinked_multilib_executable('dex2oatd')
646
647    # Check ART internal libraries.
648    self._checker.check_native_library('libartserviced')
649    self._checker.check_native_library('libperfetto_hprofd')
650
651    # Check internal native library dependencies.
652    #
653    # Like in the release package, we check that we don't get other dependencies
654    # besides those listed here. In this case the concern is not bloat, but
655    # rather that we don't get behavioural differences between user (release)
656    # and userdebug/eng builds, which could happen if the debug package has
657    # duplicate library instances where releases don't. In other words, it's
658    # uncontroversial to add debug-only dependencies, as long as they don't make
659    # assumptions on having a single global state (ideally they should have
660    # double_loadable:true, cf. go/double_loadable). Also, like in the release
661    # package we need to look out for dependencies that should go through
662    # exported library stubs (until b/128708192 is fixed).
663    self._checker.check_prefer64_library('libmeminfo')
664    self._checker.check_prefer64_library('libprocinfo')
665
666
667class TestingTargetChecker:
668  def __init__(self, checker):
669    self._checker = checker
670
671  def __str__(self):
672    return 'Testing (Target) Checker'
673
674  def run(self):
675    # Check ART test binaries.
676    self._checker.check_art_test_executable('art_artd_tests')
677    self._checker.check_art_test_executable('art_cmdline_tests')
678    self._checker.check_art_test_executable('art_compiler_tests')
679    self._checker.check_art_test_executable('art_dex2oat_tests')
680    self._checker.check_art_test_executable('art_dexanalyze_tests')
681    self._checker.check_art_test_executable('art_dexdiag_tests')
682    self._checker.check_art_test_executable('art_dexdump_tests')
683    self._checker.check_art_test_executable('art_dexlayout_tests')
684    self._checker.check_art_test_executable('art_dexlist_tests')
685    self._checker.check_art_test_executable('art_dexoptanalyzer_tests')
686    self._checker.check_art_test_executable('art_disassembler_tests')
687    self._checker.check_art_test_executable('art_imgdiag_tests')
688    self._checker.check_art_test_executable('art_libartbase_tests')
689    self._checker.check_art_test_executable('art_libartpalette_tests')
690    self._checker.check_art_test_executable('art_libartservice_tests')
691    self._checker.check_art_test_executable('art_libarttools_tests')
692    self._checker.check_art_test_executable('art_libdexfile_support_tests')
693    self._checker.check_art_test_executable('art_libdexfile_tests')
694    self._checker.check_art_test_executable('art_libprofile_tests')
695    self._checker.check_art_test_executable('art_oatdump_tests')
696    self._checker.check_art_test_executable('art_odrefresh_tests')
697    self._checker.check_art_test_executable('art_profman_tests')
698    self._checker.check_art_test_executable('art_runtime_tests')
699    self._checker.check_art_test_executable('art_sigchain_tests')
700
701    # Check ART test (internal) libraries.
702    self._checker.check_native_library('libartd-gtest')
703    self._checker.check_native_library('libartd-simulator-container')
704    self._checker.check_native_library('libartbased-testing')
705
706    # Check ART test tools.
707    self._checker.check_executable('signal_dumper')
708
709    # Check ART jar files which are needed for gtests.
710    self._checker.check_art_test_data('art-gtest-jars-AbstractMethod.jar')
711    self._checker.check_art_test_data('art-gtest-jars-ArrayClassWithUnresolvedComponent.dex')
712    self._checker.check_art_test_data('art-gtest-jars-MyClassNatives.jar')
713    self._checker.check_art_test_data('art-gtest-jars-Main.jar')
714    self._checker.check_art_test_data('art-gtest-jars-ProtoCompare.jar')
715    self._checker.check_art_test_data('art-gtest-jars-Transaction.jar')
716    self._checker.check_art_test_data('art-gtest-jars-VerifierDepsMulti.dex')
717    self._checker.check_art_test_data('art-gtest-jars-Nested.jar')
718    self._checker.check_art_test_data('art-gtest-jars-MyClass.jar')
719    self._checker.check_art_test_data('art-gtest-jars-ManyMethods.jar')
720    self._checker.check_art_test_data('art-gtest-jars-GetMethodSignature.jar')
721    self._checker.check_art_test_data('art-gtest-jars-Lookup.jar')
722    self._checker.check_art_test_data('art-gtest-jars-Instrumentation.jar')
723    self._checker.check_art_test_data('art-gtest-jars-MainUncompressedAligned.jar')
724    self._checker.check_art_test_data('art-gtest-jars-ForClassLoaderD.jar')
725    self._checker.check_art_test_data('art-gtest-jars-ForClassLoaderC.jar')
726    self._checker.check_art_test_data('art-gtest-jars-ErroneousA.jar')
727    self._checker.check_art_test_data('art-gtest-jars-HiddenApiSignatures.jar')
728    self._checker.check_art_test_data('art-gtest-jars-ForClassLoaderB.jar')
729    self._checker.check_art_test_data('art-gtest-jars-LinkageTest.dex')
730    self._checker.check_art_test_data('art-gtest-jars-MethodTypes.jar')
731    self._checker.check_art_test_data('art-gtest-jars-ErroneousInit.jar')
732    self._checker.check_art_test_data('art-gtest-jars-VerifierDeps.dex')
733    self._checker.check_art_test_data('art-gtest-jars-StringLiterals.jar')
734    self._checker.check_art_test_data('art-gtest-jars-XandY.jar')
735    self._checker.check_art_test_data('art-gtest-jars-ExceptionHandle.jar')
736    self._checker.check_art_test_data('art-gtest-jars-ImageLayoutB.jar')
737    self._checker.check_art_test_data('art-gtest-jars-Interfaces.jar')
738    self._checker.check_art_test_data('art-gtest-jars-IMTB.jar')
739    self._checker.check_art_test_data('art-gtest-jars-Extension2.jar')
740    self._checker.check_art_test_data('art-gtest-jars-Extension1.jar')
741    self._checker.check_art_test_data('art-gtest-jars-MainEmptyUncompressedAligned.jar')
742    self._checker.check_art_test_data('art-gtest-jars-ErroneousB.jar')
743    self._checker.check_art_test_data('art-gtest-jars-MultiDexModifiedSecondary.jar')
744    self._checker.check_art_test_data('art-gtest-jars-NonStaticLeafMethods.jar')
745    self._checker.check_art_test_data('art-gtest-jars-DefaultMethods.jar')
746    self._checker.check_art_test_data('art-gtest-jars-MultiDexUncompressedAligned.jar')
747    self._checker.check_art_test_data('art-gtest-jars-StaticsFromCode.jar')
748    self._checker.check_art_test_data('art-gtest-jars-ProfileTestMultiDex.jar')
749    self._checker.check_art_test_data('art-gtest-jars-VerifySoftFailDuringClinit.dex')
750    self._checker.check_art_test_data('art-gtest-jars-MainStripped.jar')
751    self._checker.check_art_test_data('art-gtest-jars-ForClassLoaderA.jar')
752    self._checker.check_art_test_data('art-gtest-jars-StaticLeafMethods.jar')
753    self._checker.check_art_test_data('art-gtest-jars-MultiDex.jar')
754    self._checker.check_art_test_data('art-gtest-jars-Packages.jar')
755    self._checker.check_art_test_data('art-gtest-jars-ProtoCompare2.jar')
756    self._checker.check_art_test_data('art-gtest-jars-Statics.jar')
757    self._checker.check_art_test_data('art-gtest-jars-AllFields.jar')
758    self._checker.check_art_test_data('art-gtest-jars-IMTA.jar')
759    self._checker.check_art_test_data('art-gtest-jars-ImageLayoutA.jar')
760    self._checker.check_art_test_data('art-gtest-jars-MainEmptyUncompressed.jar')
761    self._checker.check_art_test_data('art-gtest-jars-Dex2oatVdexTestDex.jar')
762    self._checker.check_art_test_data('art-gtest-jars-Dex2oatVdexPublicSdkDex.dex')
763    self._checker.check_art_test_data('art-gtest-jars-SuperWithAccessChecks.dex')
764
765
766class NoSuperfluousBinariesChecker:
767  def __init__(self, checker):
768    self._checker = checker
769
770  def __str__(self):
771    return 'No superfluous binaries checker'
772
773  def run(self):
774    self._checker.check_no_superfluous_files('bin')
775
776
777class NoSuperfluousLibrariesChecker:
778  def __init__(self, checker):
779    self._checker = checker
780
781  def __str__(self):
782    return 'No superfluous libraries checker'
783
784  def run(self):
785    self._checker.check_no_superfluous_files('javalib')
786    self._checker.check_no_superfluous_files('lib')
787    self._checker.check_no_superfluous_files('lib64')
788
789
790class NoSuperfluousArtTestsChecker:
791  def __init__(self, checker):
792    self._checker = checker
793
794  def __str__(self):
795    return 'No superfluous ART tests checker'
796
797  def run(self):
798    for arch in self._checker.possible_archs():
799      self._checker.check_no_superfluous_files('%s/%s' % (ART_TEST_DIR, arch))
800
801
802class List:
803  def __init__(self, provider, print_size=False):
804    self._provider = provider
805    self._print_size = print_size
806
807  def print_list(self):
808
809    def print_list_rec(path):
810      apex_map = self._provider.read_dir(path)
811      if apex_map is None:
812        return
813      apex_map = dict(apex_map)
814      if '.' in apex_map:
815        del apex_map['.']
816      if '..' in apex_map:
817        del apex_map['..']
818      for (_, val) in sorted(apex_map.items()):
819        val_path = os.path.join(path, val.name)
820        if self._print_size:
821          if val.size < 0:
822            print('[    n/a    ]  %s' % val_path)
823          else:
824            print('[%11d]  %s' % (val.size, val_path))
825        else:
826          print(val_path)
827        if val.is_dir:
828          print_list_rec(val_path)
829
830    print_list_rec('')
831
832
833class Tree:
834  def __init__(self, provider, title, print_size=False):
835    print('%s' % title)
836    self._provider = provider
837    self._has_next_list = []
838    self._print_size = print_size
839
840  @staticmethod
841  def get_vertical(has_next_list):
842    string = ''
843    for v in has_next_list:
844      string += '%s   ' % ('│' if v else ' ')
845    return string
846
847  @staticmethod
848  def get_last_vertical(last):
849    return '└── ' if last else '├── '
850
851  def print_tree(self):
852
853    def print_tree_rec(path):
854      apex_map = self._provider.read_dir(path)
855      if apex_map is None:
856        return
857      apex_map = dict(apex_map)
858      if '.' in apex_map:
859        del apex_map['.']
860      if '..' in apex_map:
861        del apex_map['..']
862      key_list = list(sorted(apex_map.keys()))
863      for i, key in enumerate(key_list):
864        prev = self.get_vertical(self._has_next_list)
865        last = self.get_last_vertical(i == len(key_list) - 1)
866        val = apex_map[key]
867        if self._print_size:
868          if val.size < 0:
869            print('%s%s[    n/a    ]  %s' % (prev, last, val.name))
870          else:
871            print('%s%s[%11d]  %s' % (prev, last, val.size, val.name))
872        else:
873          print('%s%s%s' % (prev, last, val.name))
874        if val.is_dir:
875          self._has_next_list.append(i < len(key_list) - 1)
876          val_path = os.path.join(path, val.name)
877          print_tree_rec(val_path)
878          self._has_next_list.pop()
879
880    print_tree_rec('')
881
882
883# Note: do not sys.exit early, for __del__ cleanup.
884def art_apex_test_main(test_args):
885  if test_args.host and test_args.flattened:
886    logging.error("Both of --host and --flattened set")
887    return 1
888  if test_args.list and test_args.tree:
889    logging.error("Both of --list and --tree set")
890    return 1
891  if test_args.size and not (test_args.list or test_args.tree):
892    logging.error("--size set but neither --list nor --tree set")
893    return 1
894  if not test_args.flattened and not test_args.tmpdir:
895    logging.error("Need a tmpdir.")
896    return 1
897  if not test_args.flattened and not test_args.host:
898    if not test_args.deapexer:
899      logging.error("Need deapexer.")
900      return 1
901    if not test_args.debugfs:
902      logging.error("Need debugfs.")
903      return 1
904    if not test_args.fsckerofs:
905      logging.error("Need fsck.erofs.")
906      return 1
907    if not test_args.blkid:
908      logging.error("Need blkid.")
909      return 1
910
911  if test_args.host:
912    # Host APEX.
913    if test_args.flavor not in [FLAVOR_DEBUG, FLAVOR_AUTO]:
914      logging.error("Using option --host with non-Debug APEX")
915      return 1
916    # Host APEX is always a debug flavor (for now).
917    test_args.flavor = FLAVOR_DEBUG
918  else:
919    # Device APEX.
920    if test_args.flavor == FLAVOR_AUTO:
921      logging.warning('--flavor=auto, trying to autodetect. This may be incorrect!')
922      # The order of flavors in the list below matters, as the release tag (empty string) will
923      # match any package name.
924      for flavor in [ FLAVOR_DEBUG, FLAVOR_TESTING, FLAVOR_RELEASE ]:
925        flavor_tag = flavor
926        # Special handling for the release flavor, whose name is no longer part of the Release ART
927        # APEX file name (`com.android.art.capex` / `com.android.art`).
928        if flavor == FLAVOR_RELEASE:
929          flavor_tag = ''
930        flavor_pattern = '*.%s*' % flavor_tag
931        if fnmatch.fnmatch(test_args.apex, flavor_pattern):
932          test_args.flavor = flavor
933          logging.warning('  Detected %s flavor', flavor)
934          break
935      if test_args.flavor == FLAVOR_AUTO:
936        logging.error('  Could not detect APEX flavor, neither %s, %s nor %s for \'%s\'',
937                    FLAVOR_RELEASE, FLAVOR_DEBUG, FLAVOR_TESTING, test_args.apex)
938        return 1
939
940  try:
941    if test_args.host:
942      apex_provider = HostApexProvider(test_args.apex, test_args.tmpdir)
943    else:
944      apex_dir = test_args.apex
945      if not test_args.flattened:
946        # Extract the apex. It would be nice to use the output from "deapexer list"
947        # to avoid this work, but it doesn't provide info about executable bits.
948        apex_dir = extract_apex(test_args.apex, test_args.deapexer, test_args.debugfs,
949                                test_args.fsckerofs, test_args.blkid, test_args.tmpdir)
950      apex_provider = TargetApexProvider(apex_dir)
951  except (zipfile.BadZipFile, zipfile.LargeZipFile) as e:
952    logging.error('Failed to create provider: %s', e)
953    return 1
954
955  if test_args.tree:
956    Tree(apex_provider, test_args.apex, test_args.size).print_tree()
957    return 0
958  if test_args.list:
959    List(apex_provider, test_args.size).print_list()
960    return 0
961
962  checkers = []
963  if test_args.bitness == BITNESS_AUTO:
964    logging.warning('--bitness=auto, trying to autodetect. This may be incorrect!')
965    has_32 = apex_provider.get('lib') is not None
966    has_64 = apex_provider.get('lib64') is not None
967    if has_32 and has_64:
968      logging.warning('  Detected multilib')
969      test_args.bitness = BITNESS_MULTILIB
970    elif has_32:
971      logging.warning('  Detected 32-only')
972      test_args.bitness = BITNESS_32
973    elif has_64:
974      logging.warning('  Detected 64-only')
975      test_args.bitness = BITNESS_64
976    else:
977      logging.error('  Could not detect bitness, neither lib nor lib64 contained.')
978      List(apex_provider).print_list()
979      return 1
980
981  if test_args.bitness == BITNESS_32:
982    base_checker = Arch32Checker(apex_provider)
983  elif test_args.bitness == BITNESS_64:
984    base_checker = Arch64Checker(apex_provider)
985  else:
986    assert test_args.bitness == BITNESS_MULTILIB
987    base_checker = MultilibChecker(apex_provider)
988
989  checkers.append(ReleaseChecker(base_checker))
990  if test_args.host:
991    checkers.append(ReleaseHostChecker(base_checker))
992  else:
993    checkers.append(ReleaseTargetChecker(base_checker))
994  if test_args.flavor == FLAVOR_DEBUG or test_args.flavor == FLAVOR_TESTING:
995    checkers.append(DebugChecker(base_checker))
996    if not test_args.host:
997      checkers.append(DebugTargetChecker(base_checker))
998  if test_args.flavor == FLAVOR_TESTING:
999    checkers.append(TestingTargetChecker(base_checker))
1000
1001  # These checkers must be last.
1002  checkers.append(NoSuperfluousBinariesChecker(base_checker))
1003  checkers.append(NoSuperfluousArtTestsChecker(base_checker))
1004  if not test_args.host:
1005    # We only care about superfluous libraries on target, where their absence
1006    # can be vital to ensure they get picked up from the right package.
1007    checkers.append(NoSuperfluousLibrariesChecker(base_checker))
1008
1009  failed = False
1010  for checker in checkers:
1011    logging.info('%s...', checker)
1012    checker.run()
1013    if base_checker.error_count() > 0:
1014      logging.error('%s FAILED', checker)
1015      failed = True
1016    else:
1017      logging.info('%s SUCCEEDED', checker)
1018    base_checker.reset_errors()
1019
1020  return 1 if failed else 0
1021
1022
1023def art_apex_test_default(test_parser):
1024  if 'ANDROID_PRODUCT_OUT' not in os.environ:
1025    logging.error('No-argument use requires ANDROID_PRODUCT_OUT')
1026    sys.exit(1)
1027  product_out = os.environ['ANDROID_PRODUCT_OUT']
1028  if 'ANDROID_HOST_OUT' not in os.environ:
1029    logging.error('No-argument use requires ANDROID_HOST_OUT')
1030    sys.exit(1)
1031  host_out = os.environ['ANDROID_HOST_OUT']
1032
1033  test_args = test_parser.parse_args(['unused'])  # For consistency.
1034  test_args.debugfs = '%s/bin/debugfs' % host_out
1035  test_args.fsckerofs = '%s/bin/fsck.erofs' % host_out
1036  test_args.blkid = '%s/bin/blkid_static' % host_out
1037  test_args.tmpdir = '.'
1038  test_args.tree = False
1039  test_args.list = False
1040  test_args.bitness = BITNESS_AUTO
1041  failed = False
1042
1043  if not os.path.exists(test_args.debugfs):
1044    logging.error("Cannot find debugfs (default path %s). Please build it, e.g., m debugfs",
1045                  test_args.debugfs)
1046    sys.exit(1)
1047
1048  # TODO: Add host support.
1049  # TODO: Add support for flattened APEX packages.
1050  configs = [
1051    {'name': 'com.android.art.capex',         'flavor': FLAVOR_RELEASE, 'host': False},
1052    {'name': 'com.android.art.debug.capex',   'flavor': FLAVOR_DEBUG,   'host': False},
1053    # Note: The Testing ART APEX is not a Compressed APEX.
1054    {'name': 'com.android.art.testing.apex',  'flavor': FLAVOR_TESTING, 'host': False},
1055  ]
1056
1057  for config in configs:
1058    logging.info(config['name'])
1059    # TODO: Host will need different path.
1060    test_args.apex = '%s/system/apex/%s' % (product_out, config['name'])
1061    if not os.path.exists(test_args.apex):
1062      failed = True
1063      logging.error("Cannot find APEX %s. Please build it first.", test_args.apex)
1064      continue
1065    test_args.flavor = config['flavor']
1066    test_args.host = config['host']
1067    failed = art_apex_test_main(test_args) != 0
1068
1069  if failed:
1070    sys.exit(1)
1071
1072
1073if __name__ == "__main__":
1074  parser = argparse.ArgumentParser(description='Check integrity of an ART APEX.')
1075
1076  parser.add_argument('apex', help='APEX file input')
1077
1078  parser.add_argument('--host', help='Check as host APEX', action='store_true')
1079
1080  parser.add_argument('--flattened', help='Check as flattened (target) APEX', action='store_true')
1081
1082  parser.add_argument('--flavor', help='Check as FLAVOR APEX', choices=FLAVORS_ALL,
1083                      default=FLAVOR_AUTO)
1084
1085  parser.add_argument('--list', help='List all files', action='store_true')
1086  parser.add_argument('--tree', help='Print directory tree', action='store_true')
1087  parser.add_argument('--size', help='Print file sizes', action='store_true')
1088
1089  parser.add_argument('--tmpdir', help='Directory for temp files')
1090  parser.add_argument('--deapexer', help='Path to deapexer')
1091  parser.add_argument('--debugfs', help='Path to debugfs')
1092  parser.add_argument('--fsckerofs', help='Path to fsck.erofs')
1093  parser.add_argument('--blkid', help='Path to blkid')
1094
1095  parser.add_argument('--bitness', help='Bitness to check', choices=BITNESS_ALL,
1096                      default=BITNESS_AUTO)
1097
1098  if len(sys.argv) == 1:
1099    art_apex_test_default(parser)
1100  else:
1101    args = parser.parse_args()
1102
1103    if args is None:
1104      sys.exit(1)
1105
1106    exit_code = art_apex_test_main(args)
1107    sys.exit(exit_code)
1108