Don't extract APEXes directly.

Use deapexer instead, to avoid knowing about .apex file format
internals.

Test: m art-check-{release,debug,testing}-apex-gen
Test: art/build/apex/runtests.sh
Bug: 188809029
Change-Id: I8414acfe23feaafb5bacc748e362d9677db7cc1a
Merged-In: I8414acfe23feaafb5bacc748e362d9677db7cc1a
(cherry picked from commit 571a6bea24452e9b288106f379c7510c380a7f61)
diff --git a/build/apex/Android.bp b/build/apex/Android.bp
index 2604ea8..d5cf74c 100644
--- a/build/apex/Android.bp
+++ b/build/apex/Android.bp
@@ -437,7 +437,8 @@
 // Genrules so we can run the checker, and empty Java library so that it gets executed.
 
 art_check_apex_gen_stem = "$(location art-apex-tester)" +
-    " --debugfs $(location debugfs)" +
+    " --deapexer $(location deapexer)" +
+    " --debugfs $(location debugfs_static)" +
     " --tmpdir $(genDir)"
 
 // The non-flattened APEXes are always checked, as they are always generated
@@ -455,7 +456,8 @@
 
     tools: [
         "art-apex-tester",
-        "debugfs",
+        "deapexer",
+        "debugfs_static",
     ],
 }
 
diff --git a/build/apex/art_apex_test.py b/build/apex/art_apex_test.py
index 5dcfe39..bb97416 100755
--- a/build/apex/art_apex_test.py
+++ b/build/apex/art_apex_test.py
@@ -21,6 +21,7 @@
 import logging
 import os
 import os.path
+import shutil
 import subprocess
 import sys
 import zipfile
@@ -57,6 +58,17 @@
   return var in os.environ and os.environ[var] == 'true'
 
 
+def extract_apex(apex_path, deapexer_path, debugfs_path, tmpdir):
+  _, apex_name = os.path.split(apex_path)
+  extract_path = os.path.join(tmpdir, apex_name)
+  if os.path.exists(extract_path):
+    shutil.rmtree(extract_path)
+  subprocess.check_call([deapexer_path, '--debugfs', debugfs_path,
+                         'extract', apex_path, extract_path],
+                        stdout=subprocess.DEVNULL)
+  return extract_path
+
+
 class FSObject:
   def __init__(self, name, is_dir, is_exec, is_symlink, size):
     self.name = name
@@ -71,83 +83,6 @@
 
 
 class TargetApexProvider:
-  def __init__(self, apex, tmpdir, debugfs):
-    self._tmpdir = tmpdir
-    self._debugfs = debugfs
-    self._folder_cache = {}
-    self._payload = os.path.join(self._tmpdir, 'apex_payload.img')
-    # Extract payload to tmpdir.
-    apex_zip = zipfile.ZipFile(apex)
-    apex_zip.extract('apex_payload.img', tmpdir)
-
-  def __del__(self):
-    # Delete temps.
-    if os.path.exists(self._payload):
-      os.remove(self._payload)
-
-  def get(self, path):
-    apex_dir, name = os.path.split(path)
-    if not apex_dir:
-      apex_dir = '.'
-    apex_map = self.read_dir(apex_dir)
-    return apex_map[name] if name in apex_map else None
-
-  def read_dir(self, apex_dir):
-    if apex_dir in self._folder_cache:
-      return self._folder_cache[apex_dir]
-    # Cannot use check_output as it will annoy with stderr.
-    process = subprocess.Popen([self._debugfs, '-R', 'ls -l -p %s' % apex_dir, self._payload],
-                               stdout=subprocess.PIPE, stderr=subprocess.PIPE,
-                               universal_newlines=True)
-    stdout, _ = process.communicate()
-    res = str(stdout)
-    apex_map = {}
-    # Debugfs output looks like this:
-    #   debugfs 1.44.4 (18-Aug-2018)
-    #   /12/040755/0/2000/.//
-    #   /2/040755/1000/1000/..//
-    #   /13/100755/0/2000/dalvikvm32/28456/
-    #   /14/100755/0/2000/dexoptanalyzer/20396/
-    #   /15/100755/0/2000/linker/1152724/
-    #   /16/100755/0/2000/dex2oat/563508/
-    #   /17/100755/0/2000/linker64/1605424/
-    #   /18/100755/0/2000/profman/85304/
-    #   /19/100755/0/2000/dalvikvm64/28576/
-    #    |     |   |   |       |        |
-    #    |     |   |   #- gid  #- name  #- size
-    #    |     |   #- uid
-    #    |     #- type and permission bits
-    #    #- inode nr (?)
-    #
-    # Note: could break just on '/' to avoid names with newlines.
-    for line in res.split("\n"):
-      if not line:
-        continue
-      comps = line.split('/')
-      if len(comps) != 8:
-        logging.warning('Could not break and parse line \'%s\'', line)
-        continue
-      bits = comps[2]
-      name = comps[5]
-      size_str = comps[6]
-      # Use a negative value as an indicator of undefined/unknown size.
-      size = int(size_str) if size_str != '' else -1
-      if len(bits) != 6:
-        logging.warning('Dont understand bits \'%s\'', bits)
-        continue
-      is_dir = bits[1] == '4'
-
-      def is_exec_bit(ch):
-        return int(ch) & 1 == 1
-
-      is_exec = is_exec_bit(bits[3]) and is_exec_bit(bits[4]) and is_exec_bit(bits[5])
-      is_symlink = bits[1] == '2'
-      apex_map[name] = FSObject(name, is_dir, is_exec, is_symlink, size)
-    self._folder_cache[apex_dir] = apex_map
-    return apex_map
-
-
-class TargetFlattenedApexProvider:
   def __init__(self, apex):
     self._folder_cache = {}
     self._apex = apex
@@ -944,9 +879,13 @@
   if not test_args.flattened and not test_args.tmpdir:
     logging.error("Need a tmpdir.")
     return 1
-  if not test_args.flattened and not test_args.host and not test_args.debugfs:
-    logging.error("Need debugfs.")
-    return 1
+  if not test_args.flattened and not test_args.host:
+    if not test_args.deapexer:
+      logging.error("Need deapexer.")
+      return 1
+    if not test_args.debugfs:
+      logging.error("Need debugfs.")
+      return 1
 
   if test_args.host:
     # Host APEX.
@@ -981,10 +920,13 @@
     if test_args.host:
       apex_provider = HostApexProvider(test_args.apex, test_args.tmpdir)
     else:
-      if test_args.flattened:
-        apex_provider = TargetFlattenedApexProvider(test_args.apex)
-      else:
-        apex_provider = TargetApexProvider(test_args.apex, test_args.tmpdir, test_args.debugfs)
+      apex_dir = test_args.apex
+      if not test_args.flattened:
+        # Extract the apex. It would be nice to use the output from "deapexer list"
+        # to avoid this work, but it doesn't provide info about executable bits.
+        apex_dir = extract_apex(test_args.apex, test_args.deapexer, test_args.debugfs,
+                                test_args.tmpdir)
+      apex_provider = TargetApexProvider(apex_dir)
   except (zipfile.BadZipFile, zipfile.LargeZipFile) as e:
     logging.error('Failed to create provider: %s', e)
     return 1
@@ -1121,6 +1063,7 @@
   parser.add_argument('--size', help='Print file sizes', action='store_true')
 
   parser.add_argument('--tmpdir', help='Directory for temp files')
+  parser.add_argument('--deapexer', help='Path to deapexer')
   parser.add_argument('--debugfs', help='Path to debugfs')
 
   parser.add_argument('--bitness', help='Bitness to check', choices=BITNESS_ALL,
diff --git a/build/apex/runtests.sh b/build/apex/runtests.sh
index d4ecbca..5911288 100755
--- a/build/apex/runtests.sh
+++ b/build/apex/runtests.sh
@@ -34,7 +34,7 @@
 }
 
 function setup_die {
-  die "You need to source and lunch before you can use this script."
+  die "You need to run lunch, banchan, or tapas before you can use this script."
 }
 
 [[ -n "$ANDROID_BUILD_TOP" ]] || setup_die
@@ -49,13 +49,14 @@
   export TARGET_BUILD_UNBUNDLED=true
 fi
 
-have_debugfs_p=false
+have_deapexer_p=false
 if $flattened_apex_p; then :; else
-  if [ ! -e "$ANDROID_HOST_OUT/bin/debugfs" ] ; then
-    say "Could not find debugfs, building now."
-    build/soong/soong_ui.bash --make-mode debugfs-host || die "Cannot build debugfs"
+  if [ ! -e "$ANDROID_HOST_OUT/bin/deapexer" -o ! -e "$ANDROID_HOST_OUT/bin/debugfs_static" ] ; then
+    say "Could not find deapexer and/or debugfs_static, building now."
+    build/soong/soong_ui.bash --make-mode deapexer debugfs_static-host || \
+      die "Cannot build deapexer and debugfs_static"
   fi
-  have_debugfs_p=true
+  have_deapexer_p=true
 fi
 
 # Fail early.
@@ -181,8 +182,9 @@
     else
       apex_path="$ANDROID_PRODUCT_OUT/system/apex/${apex_module}.apex"
     fi
-    if $have_debugfs_p; then
-      art_apex_test_args="$art_apex_test_args --debugfs $ANDROID_HOST_OUT/bin/debugfs"
+    if $have_deapexer_p; then
+      art_apex_test_args="$art_apex_test_args --deapexer $ANDROID_HOST_OUT/bin/deapexer"
+      art_apex_test_args="$art_apex_test_args --debugfs $ANDROID_HOST_OUT/bin/debugfs_static"
     fi
     case $apex_module in
       (*.debug)   test_only_args="--flavor debug";;