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