1#!/usr/bin/env python3 2 3# Copyright (C) 2019 The Android Open Source Project 4# 5# Licensed under the Apache License, Version 2.0 (the "License"); 6# you may not use this file except in compliance with the License. 7# You may obtain a copy of the License at 8# 9# http://www.apache.org/licenses/LICENSE-2.0 10# 11# Unless required by applicable law or agreed to in writing, software 12# distributed under the License is distributed on an "AS IS" BASIS, 13# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14# See the License for the specific language governing permissions and 15# limitations under the License. 16# 17 18import argparse 19import fnmatch 20import logging 21import os 22import os.path 23import subprocess 24import sys 25import zipfile 26 27logging.basicConfig(format='%(message)s') 28 29 30class FSObject: 31 def __init__(self, name, is_dir, is_exec, is_symlink): 32 self.name = name 33 self.is_dir = is_dir 34 self.is_exec = is_exec 35 self.is_symlink = is_symlink 36 37 def __str__(self): 38 return '%s(dir=%r,exec=%r,symlink=%r)' % (self.name, self.is_dir, self.is_exec, self.is_symlink) 39 40 41class TargetApexProvider: 42 def __init__(self, apex, tmpdir, debugfs): 43 self._tmpdir = tmpdir 44 self._debugfs = debugfs 45 self._folder_cache = {} 46 self._payload = os.path.join(self._tmpdir, 'apex_payload.img') 47 # Extract payload to tmpdir. 48 apex_zip = zipfile.ZipFile(apex) 49 apex_zip.extract('apex_payload.img', tmpdir) 50 51 def __del__(self): 52 # Delete temps. 53 if os.path.exists(self._payload): 54 os.remove(self._payload) 55 56 def get(self, path): 57 apex_dir, name = os.path.split(path) 58 if not apex_dir: 59 apex_dir = '.' 60 apex_map = self.read_dir(apex_dir) 61 return apex_map[name] if name in apex_map else None 62 63 def read_dir(self, apex_dir): 64 if apex_dir in self._folder_cache: 65 return self._folder_cache[apex_dir] 66 # Cannot use check_output as it will annoy with stderr. 67 process = subprocess.Popen([self._debugfs, '-R', 'ls -l -p %s' % apex_dir, self._payload], 68 stdout=subprocess.PIPE, stderr=subprocess.PIPE, 69 universal_newlines=True) 70 stdout, _ = process.communicate() 71 res = str(stdout) 72 apex_map = {} 73 # Debugfs output looks like this: 74 # debugfs 1.44.4 (18-Aug-2018) 75 # /12/040755/0/2000/.// 76 # /2/040755/1000/1000/..// 77 # /13/100755/0/2000/dalvikvm32/28456/ 78 # /14/100755/0/2000/dexoptanalyzer/20396/ 79 # /15/100755/0/2000/linker/1152724/ 80 # /16/100755/0/2000/dex2oat/563508/ 81 # /17/100755/0/2000/linker64/1605424/ 82 # /18/100755/0/2000/profman/85304/ 83 # /19/100755/0/2000/dalvikvm64/28576/ 84 # | | | | | | 85 # | | | #- gid #- name #- size 86 # | | #- uid 87 # | #- type and permission bits 88 # #- inode nr (?) 89 # 90 # Note: could break just on '/' to avoid names with newlines. 91 for line in res.split("\n"): 92 if not line: 93 continue 94 comps = line.split('/') 95 if len(comps) != 8: 96 logging.warning('Could not break and parse line \'%s\'', line) 97 continue 98 bits = comps[2] 99 name = comps[5] 100 if len(bits) != 6: 101 logging.warning('Dont understand bits \'%s\'', bits) 102 continue 103 is_dir = bits[1] == '4' 104 105 def is_exec_bit(ch): 106 return int(ch) & 1 == 1 107 108 is_exec = is_exec_bit(bits[3]) and is_exec_bit(bits[4]) and is_exec_bit(bits[5]) 109 is_symlink = bits[1] == '2' 110 apex_map[name] = FSObject(name, is_dir, is_exec, is_symlink) 111 self._folder_cache[apex_dir] = apex_map 112 return apex_map 113 114 115class HostApexProvider: 116 def __init__(self, apex, tmpdir): 117 self._tmpdir = tmpdir 118 self.folder_cache = {} 119 self._payload = os.path.join(self._tmpdir, 'apex_payload.zip') 120 # Extract payload to tmpdir. 121 apex_zip = zipfile.ZipFile(apex) 122 apex_zip.extract('apex_payload.zip', tmpdir) 123 124 def __del__(self): 125 # Delete temps. 126 if os.path.exists(self._payload): 127 os.remove(self._payload) 128 129 def get(self, path): 130 apex_dir, name = os.path.split(path) 131 if not apex_dir: 132 apex_dir = '' 133 apex_map = self.read_dir(apex_dir) 134 return apex_map[name] if name in apex_map else None 135 136 def read_dir(self, apex_dir): 137 if apex_dir in self.folder_cache: 138 return self.folder_cache[apex_dir] 139 if not self.folder_cache: 140 self.parse_zip() 141 if apex_dir in self.folder_cache: 142 return self.folder_cache[apex_dir] 143 return {} 144 145 def parse_zip(self): 146 apex_zip = zipfile.ZipFile(self._payload) 147 infos = apex_zip.infolist() 148 for zipinfo in infos: 149 path = zipinfo.filename 150 151 # Assume no empty file is stored. 152 assert path 153 154 def get_octal(val, index): 155 return (val >> (index * 3)) & 0x7 156 157 def bits_is_exec(val): 158 # TODO: Enforce group/other, too? 159 return get_octal(val, 2) & 1 == 1 160 161 is_zipinfo = True 162 while path: 163 apex_dir, base = os.path.split(path) 164 # TODO: If directories are stored, base will be empty. 165 166 if apex_dir not in self.folder_cache: 167 self.folder_cache[apex_dir] = {} 168 dir_map = self.folder_cache[apex_dir] 169 if base not in dir_map: 170 if is_zipinfo: 171 bits = (zipinfo.external_attr >> 16) & 0xFFFF 172 is_dir = get_octal(bits, 4) == 4 173 is_symlink = get_octal(bits, 4) == 2 174 is_exec = bits_is_exec(bits) 175 else: 176 is_exec = False # Seems we can't get this easily? 177 is_symlink = False 178 is_dir = True 179 dir_map[base] = FSObject(base, is_dir, is_exec, is_symlink) 180 is_zipinfo = False 181 path = apex_dir 182 183 184# DO NOT USE DIRECTLY! This is an "abstract" base class. 185class Checker: 186 def __init__(self, provider): 187 self._provider = provider 188 self._errors = 0 189 self._expected_file_globs = set() 190 191 def fail(self, msg, *fail_args): 192 self._errors += 1 193 logging.error(msg, *fail_args) 194 195 def error_count(self): 196 return self._errors 197 198 def reset_errors(self): 199 self._errors = 0 200 201 def is_file(self, path): 202 fs_object = self._provider.get(path) 203 if fs_object is None: 204 return False, 'Could not find %s' 205 if fs_object.is_dir: 206 return False, '%s is a directory' 207 return True, '' 208 209 def check_file(self, path): 210 ok, msg = self.is_file(path) 211 if not ok: 212 self.fail(msg, path) 213 self._expected_file_globs.add(path) 214 return ok 215 216 def check_executable(self, filename): 217 path = 'bin/%s' % filename 218 if not self.check_file(path): 219 return 220 if not self._provider.get(path).is_exec: 221 self.fail('%s is not executable', path) 222 223 def check_executable_symlink(self, filename): 224 path = 'bin/%s' % filename 225 fs_object = self._provider.get(path) 226 if fs_object is None: 227 self.fail('Could not find %s', path) 228 return 229 if fs_object.is_dir: 230 self.fail('%s is a directory', path) 231 return 232 if not fs_object.is_symlink: 233 self.fail('%s is not a symlink', path) 234 self._expected_file_globs.add(path) 235 236 def check_single_library(self, filename): 237 lib_path = 'lib/%s' % filename 238 lib64_path = 'lib64/%s' % filename 239 lib_is_file, _ = self.is_file(lib_path) 240 if lib_is_file: 241 self._expected_file_globs.add(lib_path) 242 lib64_is_file, _ = self.is_file(lib64_path) 243 if lib64_is_file: 244 self._expected_file_globs.add(lib64_path) 245 if not lib_is_file and not lib64_is_file: 246 self.fail('Library missing: %s', filename) 247 248 def check_java_library(self, basename): 249 return self.check_file('javalib/%s.jar' % basename) 250 251 def ignore_path(self, path_glob): 252 self._expected_file_globs.add(path_glob) 253 254 def check_no_superfluous_files(self, dir_path): 255 paths = [] 256 for name in sorted(self._provider.read_dir(dir_path).keys()): 257 if name not in ('.', '..'): 258 paths.append(os.path.join(dir_path, name)) 259 expected_paths = set() 260 dir_prefix = dir_path + '/' 261 for path_glob in self._expected_file_globs: 262 expected_paths |= set(fnmatch.filter(paths, path_glob)) 263 # If there are globs in subdirectories of dir_path we want to match their 264 # path segments at this directory level. 265 if path_glob.startswith(dir_prefix): 266 subpath = path_glob[len(dir_prefix):] 267 subpath_first_segment, _, _ = subpath.partition('/') 268 expected_paths |= set(fnmatch.filter(paths, dir_prefix + subpath_first_segment)) 269 for unexpected_path in set(paths) - expected_paths: 270 self.fail('Unexpected file \'%s\'', unexpected_path) 271 272 # Just here for docs purposes, even if it isn't good Python style. 273 274 def check_symlinked_multilib_executable(self, filename): 275 """Check bin/filename32, and/or bin/filename64, with symlink bin/filename.""" 276 raise NotImplementedError 277 278 def check_multilib_executable(self, filename): 279 """Check bin/filename for 32 bit, and/or bin/filename64.""" 280 raise NotImplementedError 281 282 def check_native_library(self, basename): 283 """Check lib/basename.so, and/or lib64/basename.so.""" 284 raise NotImplementedError 285 286 def check_optional_native_library(self, basename_glob): 287 """Allow lib/basename.so and/or lib64/basename.so to exist.""" 288 raise NotImplementedError 289 290 def check_prefer64_library(self, basename): 291 """Check lib64/basename.so, or lib/basename.so on 32 bit only.""" 292 raise NotImplementedError 293 294 295class Arch32Checker(Checker): 296 def check_symlinked_multilib_executable(self, filename): 297 self.check_executable('%s32' % filename) 298 self.check_executable_symlink(filename) 299 300 def check_multilib_executable(self, filename): 301 self.check_executable(filename) 302 303 def check_native_library(self, basename): 304 # TODO: Use $TARGET_ARCH (e.g. check whether it is "arm" or "arm64") to improve 305 # the precision of this test? 306 self.check_file('lib/%s.so' % basename) 307 308 def check_optional_native_library(self, basename_glob): 309 self.ignore_path('lib/%s.so' % basename_glob) 310 311 def check_prefer64_library(self, basename): 312 self.check_native_library(basename) 313 314 315class Arch64Checker(Checker): 316 def check_symlinked_multilib_executable(self, filename): 317 self.check_executable('%s64' % filename) 318 self.check_executable_symlink(filename) 319 320 def check_multilib_executable(self, filename): 321 self.check_executable('%s64' % filename) 322 323 def check_native_library(self, basename): 324 # TODO: Use $TARGET_ARCH (e.g. check whether it is "arm" or "arm64") to improve 325 # the precision of this test? 326 self.check_file('lib64/%s.so' % basename) 327 328 def check_optional_native_library(self, basename_glob): 329 self.ignore_path('lib64/%s.so' % basename_glob) 330 331 def check_prefer64_library(self, basename): 332 self.check_native_library(basename) 333 334 335class MultilibChecker(Checker): 336 def check_symlinked_multilib_executable(self, filename): 337 self.check_executable('%s32' % filename) 338 self.check_executable('%s64' % filename) 339 self.check_executable_symlink(filename) 340 341 def check_multilib_executable(self, filename): 342 self.check_executable('%s64' % filename) 343 self.check_executable(filename) 344 345 def check_native_library(self, basename): 346 # TODO: Use $TARGET_ARCH (e.g. check whether it is "arm" or "arm64") to improve 347 # the precision of this test? 348 self.check_file('lib/%s.so' % basename) 349 self.check_file('lib64/%s.so' % basename) 350 351 def check_optional_native_library(self, basename_glob): 352 self.ignore_path('lib/%s.so' % basename_glob) 353 self.ignore_path('lib64/%s.so' % basename_glob) 354 355 def check_prefer64_library(self, basename): 356 self.check_file('lib64/%s.so' % basename) 357 358 359class ReleaseChecker: 360 def __init__(self, checker): 361 self._checker = checker 362 363 def __str__(self): 364 return 'Release Checker' 365 366 def run(self): 367 # Check the APEX manifest. 368 self._checker.check_file('apex_manifest.json') 369 370 # Check binaries for ART. 371 self._checker.check_executable('dex2oat') 372 self._checker.check_executable('dexdump') 373 self._checker.check_executable('dexlist') 374 self._checker.check_executable('dexoptanalyzer') 375 self._checker.check_executable('profman') 376 self._checker.check_symlinked_multilib_executable('dalvikvm') 377 378 # Check exported libraries for ART. 379 self._checker.check_native_library('libdexfile_external') 380 self._checker.check_native_library('libnativebridge') 381 self._checker.check_native_library('libnativehelper') 382 self._checker.check_native_library('libnativeloader') 383 384 # Check internal libraries for ART. 385 self._checker.check_native_library('libadbconnection') 386 self._checker.check_native_library('libart') 387 self._checker.check_native_library('libart-compiler') 388 self._checker.check_native_library('libart-dexlayout') 389 self._checker.check_native_library('libartbase') 390 self._checker.check_native_library('libartpalette') 391 self._checker.check_native_library('libdexfile') 392 self._checker.check_native_library('libdexfile_support') 393 self._checker.check_native_library('libopenjdkjvm') 394 self._checker.check_native_library('libopenjdkjvmti') 395 self._checker.check_native_library('libprofile') 396 self._checker.check_native_library('libsigchain') 397 398 # Check java libraries for Managed Core Library. 399 self._checker.check_java_library('apache-xml') 400 self._checker.check_java_library('bouncycastle') 401 self._checker.check_java_library('core-libart') 402 self._checker.check_java_library('core-oj') 403 self._checker.check_java_library('okhttp') 404 405 # Check internal native libraries for Managed Core Library. 406 self._checker.check_native_library('libjavacore') 407 self._checker.check_native_library('libopenjdk') 408 409 # Check internal native library dependencies. 410 # 411 # Any internal dependency not listed here will cause a failure in 412 # NoSuperfluousLibrariesChecker. Internal dependencies are generally just 413 # implementation details, but in the release package we want to track them 414 # because a) they add to the package size and the RAM usage (in particular 415 # if the library is also present in /system or another APEX and hence might 416 # get loaded twice through linker namespace separation), and b) we need to 417 # catch invalid dependencies on /system or other APEXes that should go 418 # through an exported library with stubs (b/128708192 tracks implementing a 419 # better approach for that). 420 self._checker.check_native_library('libbacktrace') 421 self._checker.check_native_library('libbase') 422 self._checker.check_native_library('libc++') 423 self._checker.check_native_library('libdt_fd_forward') 424 self._checker.check_native_library('libdt_socket') 425 self._checker.check_native_library('libjdwp') 426 self._checker.check_native_library('liblzma') 427 self._checker.check_native_library('libnpt') 428 self._checker.check_native_library('libunwindstack') 429 self._checker.check_native_library('libziparchive') 430 self._checker.check_optional_native_library('libvixl') # Only on ARM/ARM64 431 432 # Allow extra dependencies that appear in ASAN builds. 433 self._checker.check_optional_native_library('libclang_rt.asan*') 434 self._checker.check_optional_native_library('libclang_rt.hwasan*') 435 self._checker.check_optional_native_library('libclang_rt.ubsan*') 436 437 438class ReleaseTargetChecker: 439 def __init__(self, checker): 440 self._checker = checker 441 442 def __str__(self): 443 return 'Release (Target) Checker' 444 445 def run(self): 446 # Check the APEX package scripts. 447 self._checker.check_executable('art_postinstall_hook') 448 self._checker.check_executable('art_preinstall_hook') 449 self._checker.check_executable('art_preinstall_hook_boot') 450 self._checker.check_executable('art_preinstall_hook_system_server') 451 self._checker.check_executable('art_prepostinstall_utils') 452 453 # Check binaries for ART. 454 self._checker.check_executable('oatdump') 455 456 # Check internal libraries for ART. 457 self._checker.check_prefer64_library('libart-disassembler') 458 459 # Check binaries for Bionic. 460 self._checker.check_multilib_executable('linker') 461 self._checker.check_multilib_executable('linker_asan') 462 463 # Check libraries for Bionic. 464 self._checker.check_native_library('bionic/libc') 465 self._checker.check_native_library('bionic/libdl') 466 self._checker.check_native_library('bionic/libm') 467 # ... and its internal dependencies 468 self._checker.check_native_library('libc_malloc_hooks') 469 self._checker.check_native_library('libc_malloc_debug') 470 471 # Check exported native libraries for Managed Core Library. 472 self._checker.check_native_library('libandroidicu') 473 self._checker.check_native_library('libandroidio') 474 475 # Check internal native library dependencies. 476 self._checker.check_native_library('libcrypto') 477 self._checker.check_native_library('libexpat') 478 self._checker.check_native_library('libicui18n') 479 self._checker.check_native_library('libicuuc') 480 self._checker.check_native_library('libpac') 481 self._checker.check_native_library('libz') 482 483 # TODO(b/124293228): Cuttlefish puts ARM libs in a lib/arm subdirectory. 484 # Check that properly on that arch, but for now just ignore the directory. 485 self._checker.ignore_path('lib/arm') 486 self._checker.ignore_path('lib/arm64') 487 488 489class ReleaseHostChecker: 490 def __init__(self, checker): 491 self._checker = checker 492 493 def __str__(self): 494 return 'Release (Host) Checker' 495 496 def run(self): 497 # Check binaries for ART. 498 self._checker.check_executable('hprof-conv') 499 self._checker.check_symlinked_multilib_executable('dex2oatd') 500 501 # Check exported native libraries for Managed Core Library. 502 self._checker.check_native_library('libandroidicu-host') 503 self._checker.check_native_library('libandroidio') 504 505 # Check internal libraries for Managed Core Library. 506 self._checker.check_native_library('libexpat-host') 507 self._checker.check_native_library('libicui18n-host') 508 self._checker.check_native_library('libicuuc-host') 509 self._checker.check_native_library('libz-host') 510 511 512class DebugChecker: 513 def __init__(self, checker): 514 self._checker = checker 515 516 def __str__(self): 517 return 'Debug Checker' 518 519 def run(self): 520 # Check binaries for ART. 521 self._checker.check_executable('dexdiag') 522 523 # Check debug binaries for ART. 524 self._checker.check_executable('dexoptanalyzerd') 525 self._checker.check_executable('profmand') 526 527 # Check internal libraries for ART. 528 self._checker.check_native_library('libadbconnectiond') 529 self._checker.check_native_library('libartbased') 530 self._checker.check_native_library('libartd') 531 self._checker.check_native_library('libartd-compiler') 532 self._checker.check_native_library('libartd-dexlayout') 533 self._checker.check_native_library('libdexfiled') 534 self._checker.check_native_library('libopenjdkjvmd') 535 self._checker.check_native_library('libopenjdkjvmtid') 536 self._checker.check_native_library('libprofiled') 537 538 # Check internal libraries for Managed Core Library. 539 self._checker.check_native_library('libopenjdkd') 540 541 542class DebugTargetChecker: 543 def __init__(self, checker): 544 self._checker = checker 545 546 def __str__(self): 547 return 'Debug (Target) Checker' 548 549 def run(self): 550 # Check ART debug binaries. 551 self._checker.check_executable('dex2oatd') 552 self._checker.check_executable('oatdumpd') 553 554 # Check ART internal libraries. 555 self._checker.check_prefer64_library('libartd-disassembler') 556 557 # Check internal native library dependencies. 558 # 559 # Like in the release package, we check that we don't get other dependencies 560 # besides those listed here. In this case the concern is not bloat, but 561 # rather that we don't get behavioural differences between user (release) 562 # and userdebug/eng builds, which could happen if the debug package has 563 # duplicate library instances where releases don't. In other words, it's 564 # uncontroversial to add debug-only dependencies, as long as they don't make 565 # assumptions on having a single global state (ideally they should have 566 # double_loadable:true, cf. go/double_loadable). Also, like in the release 567 # package we need to look out for dependencies that should go through 568 # exported library stubs (until b/128708192 is fixed). 569 self._checker.check_optional_native_library('libvixld') # Only on ARM/ARM64 570 self._checker.check_prefer64_library('libmeminfo') 571 self._checker.check_prefer64_library('libprocinfo') 572 573 574class NoSuperfluousBinariesChecker: 575 def __init__(self, checker): 576 self._checker = checker 577 578 def __str__(self): 579 return 'No superfluous binaries checker' 580 581 def run(self): 582 self._checker.check_no_superfluous_files('bin') 583 584 585class NoSuperfluousLibrariesChecker: 586 def __init__(self, checker): 587 self._checker = checker 588 589 def __str__(self): 590 return 'No superfluous libraries checker' 591 592 def run(self): 593 self._checker.check_no_superfluous_files('javalib') 594 self._checker.check_no_superfluous_files('lib') 595 self._checker.check_no_superfluous_files('lib/bionic') 596 self._checker.check_no_superfluous_files('lib64') 597 self._checker.check_no_superfluous_files('lib64/bionic') 598 599 600class List: 601 def __init__(self, provider): 602 self._provider = provider 603 self._path = '' 604 605 def print_list(self): 606 apex_map = self._provider.read_dir(self._path) 607 if apex_map is None: 608 return 609 apex_map = dict(apex_map) 610 if '.' in apex_map: 611 del apex_map['.'] 612 if '..' in apex_map: 613 del apex_map['..'] 614 for (_, val) in sorted(apex_map.items()): 615 self._path = os.path.join(self._path, val.name) 616 print(self._path) 617 if val.is_dir: 618 self.print_list() 619 620 621class Tree: 622 def __init__(self, provider, title): 623 print('%s' % title) 624 self._provider = provider 625 self._path = '' 626 self._has_next_list = [] 627 628 @staticmethod 629 def get_vertical(has_next_list): 630 string = '' 631 for v in has_next_list: 632 string += '%s ' % ('│' if v else ' ') 633 return string 634 635 @staticmethod 636 def get_last_vertical(last): 637 return '└── ' if last else '├── ' 638 639 def print_tree(self): 640 apex_map = self._provider.read_dir(self._path) 641 if apex_map is None: 642 return 643 apex_map = dict(apex_map) 644 if '.' in apex_map: 645 del apex_map['.'] 646 if '..' in apex_map: 647 del apex_map['..'] 648 key_list = list(sorted(apex_map.keys())) 649 for i, key in enumerate(key_list): 650 prev = self.get_vertical(self._has_next_list) 651 last = self.get_last_vertical(i == len(key_list) - 1) 652 val = apex_map[key] 653 print('%s%s%s' % (prev, last, val.name)) 654 if val.is_dir: 655 self._has_next_list.append(i < len(key_list) - 1) 656 saved_dir = self._path 657 self._path = os.path.join(self._path, val.name) 658 self.print_tree() 659 self._path = saved_dir 660 self._has_next_list.pop() 661 662 663# Note: do not sys.exit early, for __del__ cleanup. 664def art_apex_test_main(test_args): 665 if test_args.tree and test_args.debug: 666 logging.error("Both of --tree and --debug set") 667 return 1 668 if test_args.list and test_args.debug: 669 logging.error("Both of --list and --debug set") 670 return 1 671 if test_args.list and test_args.tree: 672 logging.error("Both of --list and --tree set") 673 return 1 674 if not test_args.tmpdir: 675 logging.error("Need a tmpdir.") 676 return 1 677 if not test_args.host and not test_args.debugfs: 678 logging.error("Need debugfs.") 679 return 1 680 if test_args.bitness not in ['32', '64', 'multilib', 'auto']: 681 logging.error('--bitness needs to be one of 32|64|multilib|auto') 682 683 try: 684 if test_args.host: 685 apex_provider = HostApexProvider(test_args.apex, test_args.tmpdir) 686 else: 687 apex_provider = TargetApexProvider(test_args.apex, test_args.tmpdir, test_args.debugfs) 688 except (zipfile.BadZipFile, zipfile.LargeZipFile) as e: 689 logging.error('Failed to create provider: %s', e) 690 return 1 691 692 if test_args.tree: 693 Tree(apex_provider, test_args.apex).print_tree() 694 return 0 695 if test_args.list: 696 List(apex_provider).print_list() 697 return 0 698 699 checkers = [] 700 if test_args.bitness == 'auto': 701 logging.warning('--bitness=auto, trying to autodetect. This may be incorrect!') 702 has_32 = apex_provider.get('lib') is not None 703 has_64 = apex_provider.get('lib64') is not None 704 if has_32 and has_64: 705 logging.warning(' Detected multilib') 706 test_args.bitness = 'multilib' 707 elif has_32: 708 logging.warning(' Detected 32-only') 709 test_args.bitness = '32' 710 elif has_64: 711 logging.warning(' Detected 64-only') 712 test_args.bitness = '64' 713 else: 714 logging.error(' Could not detect bitness, neither lib nor lib64 contained.') 715 print('%s' % apex_provider.folder_cache) 716 return 1 717 718 if test_args.bitness == '32': 719 base_checker = Arch32Checker(apex_provider) 720 elif test_args.bitness == '64': 721 base_checker = Arch64Checker(apex_provider) 722 else: 723 assert test_args.bitness == 'multilib' 724 base_checker = MultilibChecker(apex_provider) 725 726 checkers.append(ReleaseChecker(base_checker)) 727 if test_args.host: 728 checkers.append(ReleaseHostChecker(base_checker)) 729 else: 730 checkers.append(ReleaseTargetChecker(base_checker)) 731 if test_args.debug: 732 checkers.append(DebugChecker(base_checker)) 733 if test_args.debug and not test_args.host: 734 checkers.append(DebugTargetChecker(base_checker)) 735 736 # These checkers must be last. 737 checkers.append(NoSuperfluousBinariesChecker(base_checker)) 738 if not test_args.host: 739 # We only care about superfluous libraries on target, where their absence 740 # can be vital to ensure they get picked up from the right package. 741 checkers.append(NoSuperfluousLibrariesChecker(base_checker)) 742 743 failed = False 744 for checker in checkers: 745 logging.info('%s...', checker) 746 checker.run() 747 if base_checker.error_count() > 0: 748 logging.error('%s FAILED', checker) 749 failed = True 750 else: 751 logging.info('%s SUCCEEDED', checker) 752 base_checker.reset_errors() 753 754 return 1 if failed else 0 755 756 757def art_apex_test_default(test_parser): 758 if 'ANDROID_PRODUCT_OUT' not in os.environ: 759 logging.error('No-argument use requires ANDROID_PRODUCT_OUT') 760 sys.exit(1) 761 product_out = os.environ['ANDROID_PRODUCT_OUT'] 762 if 'ANDROID_HOST_OUT' not in os.environ: 763 logging.error('No-argument use requires ANDROID_HOST_OUT') 764 sys.exit(1) 765 host_out = os.environ['ANDROID_HOST_OUT'] 766 767 test_args = test_parser.parse_args(['dummy']) # For consistency. 768 test_args.debugfs = '%s/bin/debugfs' % host_out 769 test_args.tmpdir = '.' 770 test_args.tree = False 771 test_args.list = False 772 test_args.bitness = 'auto' 773 failed = False 774 775 if not os.path.exists(test_args.debugfs): 776 logging.error("Cannot find debugfs (default path %s). Please build it, e.g., m debugfs", 777 test_args.debugfs) 778 sys.exit(1) 779 780 # TODO: Add host support 781 configs = [ 782 {'name': 'com.android.runtime.release', 'debug': False, 'host': False}, 783 {'name': 'com.android.runtime.debug', 'debug': True, 'host': False}, 784 ] 785 786 for config in configs: 787 logging.info(config['name']) 788 # TODO: Host will need different path. 789 test_args.apex = '%s/system/apex/%s.apex' % (product_out, config['name']) 790 if not os.path.exists(test_args.apex): 791 failed = True 792 logging.error("Cannot find APEX %s. Please build it first.", test_args.apex) 793 continue 794 test_args.debug = config['debug'] 795 test_args.host = config['host'] 796 failed = art_apex_test_main(test_args) != 0 797 798 if failed: 799 sys.exit(1) 800 801 802if __name__ == "__main__": 803 parser = argparse.ArgumentParser(description='Check integrity of a Runtime APEX.') 804 805 parser.add_argument('apex', help='apex file input') 806 807 parser.add_argument('--host', help='Check as host apex', action='store_true') 808 809 parser.add_argument('--debug', help='Check as debug apex', action='store_true') 810 811 parser.add_argument('--list', help='List all files', action='store_true') 812 parser.add_argument('--tree', help='Print directory tree', action='store_true') 813 814 parser.add_argument('--tmpdir', help='Directory for temp files') 815 parser.add_argument('--debugfs', help='Path to debugfs') 816 817 parser.add_argument('--bitness', help='Bitness to check, 32|64|multilib|auto', default='auto') 818 819 if len(sys.argv) == 1: 820 art_apex_test_default(parser) 821 else: 822 args = parser.parse_args() 823 824 if args is None: 825 sys.exit(1) 826 827 exit_code = art_apex_test_main(args) 828 sys.exit(exit_code) 829