Otapreopt: Implement new A/B OTA path

Refactor otapreopt_script, otapreopt_chroot and otapreopt such
that the actual work will be done as a child of the script driven
by update_engine.

The script now uses the new OtaDexoptService command to get the
right parameters for a dexopt call in otapreopt. As we reach
otapreopt_chroot and otapreopt directly, we can add parameters
without tainting the regular installd path. Use this to add the
target slot suffix, and make naming of the A/B artifacts include
the suffix instead of the generic "b."

Otapreopt_chroot startup is slightly complicated because all file
descriptors must be closed. Otherwise, dex2oat needs selinux
permissions to inherit the descriptors from update_engine.

Bug: 25612095
Bug: 28069686
Change-Id: I9d7b86ac8ecfdf91af3800f7e96f41534c0afc0f
diff --git a/cmds/installd/commands.cpp b/cmds/installd/commands.cpp
index 8999441..43b7e10 100644
--- a/cmds/installd/commands.cpp
+++ b/cmds/installd/commands.cpp
@@ -18,6 +18,7 @@
 
 #include <errno.h>
 #include <inttypes.h>
+#include <regex>
 #include <stdlib.h>
 #include <sys/capability.h>
 #include <sys/file.h>
@@ -58,6 +59,26 @@
 static constexpr const char* kCpPath = "/system/bin/cp";
 static constexpr const char* kXattrDefault = "user.default";
 
+static constexpr const char* PKG_LIB_POSTFIX = "/lib";
+static constexpr const char* CACHE_DIR_POSTFIX = "/cache";
+static constexpr const char* CODE_CACHE_DIR_POSTFIX = "/code_cache";
+
+static constexpr const char* IDMAP_PREFIX = "/data/resource-cache/";
+static constexpr const char* IDMAP_SUFFIX = "@idmap";
+
+// NOTE: keep in sync with StorageManager
+static constexpr int FLAG_STORAGE_DE = 1 << 0;
+static constexpr int FLAG_STORAGE_CE = 1 << 1;
+
+// NOTE: keep in sync with Installer
+static constexpr int FLAG_CLEAR_CACHE_ONLY = 1 << 8;
+static constexpr int FLAG_CLEAR_CODE_CACHE_ONLY = 1 << 9;
+
+/* dexopt needed flags matching those in dalvik.system.DexFile */
+static constexpr int DEXOPT_DEX2OAT_NEEDED       = 1;
+static constexpr int DEXOPT_PATCHOAT_NEEDED      = 2;
+static constexpr int DEXOPT_SELF_PATCHOAT_NEEDED = 3;
+
 #define MIN_RESTRICTED_HOME_SDK_VERSION 24 // > M
 
 typedef int fd_t;
@@ -1413,7 +1434,7 @@
     }
 }
 
-int dexopt(const char* params[DEXOPT_PARAM_COUNT]) {
+int dexopt(const char* const params[DEXOPT_PARAM_COUNT]) {
     return dexopt(params[0],                    // apk_path
                   atoi(params[1]),              // uid
                   params[2],                    // pkgname
@@ -2080,6 +2101,26 @@
         LOG(ERROR) << "Cannot move_ab with null input";
         return -1;
     }
+
+    // Get the current slot suffix. No suffix, no A/B.
+    std::string slot_suffix;
+    {
+        char buf[kPropertyValueMax];
+        if (get_property("ro.boot.slot_suffix", buf, nullptr) <= 0) {
+            return -1;
+        }
+        slot_suffix = buf;
+
+        // Validate.
+        std::regex slot_suffix_regex("[a-zA-Z0-9_]+");
+        std::smatch slot_suffix_match;
+        if (!std::regex_match(slot_suffix, slot_suffix_match, slot_suffix_regex)) {
+            LOG(ERROR) << "Target slot suffix not legal: " << slot_suffix;
+            return -1;
+        }
+    }
+
+    // Validate other inputs.
     if (validate_apk_path(apk_path) != 0) {
         LOG(ERROR) << "invalid apk_path " << apk_path;
         return -1;
@@ -2095,9 +2136,11 @@
     }
     const std::string a_image_path = create_image_filename(a_path);
 
-    // B path = A path + ".b"
-    const std::string b_path = StringPrintf("%s.b", a_path);
-    const std::string b_image_path = StringPrintf("%s.b", a_image_path.c_str());
+    // B path = A path + slot suffix.
+    const std::string b_path = StringPrintf("%s.%s", a_path, slot_suffix.c_str());
+    const std::string b_image_path = StringPrintf("%s.%s",
+                                                  a_image_path.c_str(),
+                                                  slot_suffix.c_str());
 
     bool oat_success = move_ab_path(b_path, a_path);
     bool success;
diff --git a/cmds/installd/commands.h b/cmds/installd/commands.h
index c0c39c5..e990f1b 100644
--- a/cmds/installd/commands.h
+++ b/cmds/installd/commands.h
@@ -28,6 +28,8 @@
 namespace android {
 namespace installd {
 
+static constexpr size_t DEXOPT_PARAM_COUNT = 10U;
+
 int create_app_data(const char *uuid, const char *pkgname, userid_t userid, int flags,
         appid_t appid, const char* seinfo, int target_sdk_version);
 int restorecon_app_data(const char* uuid, const char* pkgName, userid_t userid, int flags,
@@ -69,7 +71,7 @@
 static_assert(DEXOPT_PARAM_COUNT == 10U, "Unexpected dexopt param size");
 
 // Helper for the above, converting arguments.
-int dexopt(const char* params[DEXOPT_PARAM_COUNT]);
+int dexopt(const char* const params[DEXOPT_PARAM_COUNT]);
 
 int mark_boot_complete(const char *instruction_set);
 int linklib(const char* uuid, const char* pkgname, const char* asecLibDir, int userId);
diff --git a/cmds/installd/globals.cpp b/cmds/installd/globals.cpp
index 6a67e29..93e1ce5 100644
--- a/cmds/installd/globals.cpp
+++ b/cmds/installd/globals.cpp
@@ -30,6 +30,22 @@
 namespace android {
 namespace installd {
 
+static constexpr const char* APP_SUBDIR = "app/"; // sub-directory under ANDROID_DATA
+
+static constexpr const char* PRIV_APP_SUBDIR = "priv-app/"; // sub-directory under ANDROID_DATA
+
+static constexpr const char* EPHEMERAL_APP_SUBDIR = "app-ephemeral/"; // sub-directory under
+                                                                      // ANDROID_DATA
+
+static constexpr const char* APP_LIB_SUBDIR = "app-lib/"; // sub-directory under ANDROID_DATA
+
+static constexpr const char* MEDIA_SUBDIR = "media/"; // sub-directory under ANDROID_DATA
+
+static constexpr const char* PROFILES_SUBDIR = "misc/profiles"; // sub-directory under ANDROID_DATA
+
+static constexpr const char* PRIVATE_APP_SUBDIR = "app-private/"; // sub-directory under
+                                                                  // ANDROID_DATA
+
 /* Directory records that are used in execution of commands. */
 dir_rec_t android_app_dir;
 dir_rec_t android_app_ephemeral_dir;
@@ -77,7 +93,7 @@
 
     // Get the android ephemeral app directory.
     if (copy_and_append(&android_app_ephemeral_dir, &android_data_dir, EPHEMERAL_APP_SUBDIR) < 0) {
-        return -1;
+        return false;
     }
 
     // Get the android app native library directory.
@@ -86,7 +102,7 @@
     }
 
     // Get the sd-card ASEC mount point.
-    if (get_path_from_env(&android_asec_dir, "ASEC_MOUNTPOINT") < 0) {
+    if (get_path_from_env(&android_asec_dir, ASEC_MOUNTPOINT_ENV_NAME) < 0) {
         return false;
     }
 
diff --git a/cmds/installd/globals.h b/cmds/installd/globals.h
index 3e52346..c90beec 100644
--- a/cmds/installd/globals.h
+++ b/cmds/installd/globals.h
@@ -23,6 +23,11 @@
 namespace android {
 namespace installd {
 
+/* constants */
+
+// Name of the environment variable that contains the asec mountpoint.
+static constexpr const char* ASEC_MOUNTPOINT_ENV_NAME = "ASEC_MOUNTPOINT";
+
 /* data structures */
 
 struct dir_rec_t {
diff --git a/cmds/installd/installd.cpp b/cmds/installd/installd.cpp
index 9d2f71b..facbc72 100644
--- a/cmds/installd/installd.cpp
+++ b/cmds/installd/installd.cpp
@@ -557,7 +557,7 @@
     return 0;
 }
 
-bool initialize_globals() {
+static bool initialize_globals() {
     const char* data_path = getenv("ANDROID_DATA");
     if (data_path == nullptr) {
         ALOGE("Could not find ANDROID_DATA");
diff --git a/cmds/installd/installd_constants.h b/cmds/installd/installd_constants.h
index 823b8ee..41732cc 100644
--- a/cmds/installd/installd_constants.h
+++ b/cmds/installd/installd_constants.h
@@ -21,58 +21,19 @@
 namespace android {
 namespace installd {
 
-constexpr size_t DEXOPT_PARAM_COUNT = 10U;
-
 /* elements combined with a valid package name to form paths */
 
 constexpr const char* PRIMARY_USER_PREFIX = "data/";
 constexpr const char* SECONDARY_USER_PREFIX = "user/";
 
-constexpr const char* PKG_DIR_POSTFIX = "";
-
-constexpr const char* PKG_LIB_POSTFIX = "/lib";
-
-constexpr const char* CACHE_DIR_POSTFIX = "/cache";
-constexpr const char* CODE_CACHE_DIR_POSTFIX = "/code_cache";
-
-constexpr const char* APP_SUBDIR = "app/"; // sub-directory under ANDROID_DATA
-constexpr const char* PRIV_APP_SUBDIR = "priv-app/"; // sub-directory under ANDROID_DATA
-constexpr const char* EPHEMERAL_APP_SUBDIR = "app-ephemeral/"; // sub-directory under ANDROID_DATA
-
-constexpr const char* APP_LIB_SUBDIR = "app-lib/"; // sub-directory under ANDROID_DATA
-
-constexpr const char* MEDIA_SUBDIR = "media/"; // sub-directory under ANDROID_DATA
-
-constexpr const char* PROFILES_SUBDIR = "misc/profiles"; // sub-directory under ANDROID_DATA
-
-/* other handy constants */
-
-constexpr const char* PRIVATE_APP_SUBDIR = "app-private/"; // sub-directory under ANDROID_DATA
-
 // This is used as a string literal, can't be constants. TODO: std::string...
 #define DALVIK_CACHE "dalvik-cache"
 constexpr const char* DALVIK_CACHE_POSTFIX = "/classes.dex";
 constexpr const char* DALVIK_CACHE_POSTFIX2 = "@classes.dex";
 
-constexpr const char* IDMAP_PREFIX = "/data/resource-cache/";
-constexpr const char* IDMAP_SUFFIX = "@idmap";
-
 constexpr size_t PKG_NAME_MAX = 128u;   /* largest allowed package name */
 constexpr size_t PKG_PATH_MAX = 256u;   /* max size of any path we use */
 
-// NOTE: keep in sync with StorageManager
-constexpr int FLAG_STORAGE_DE = 1 << 0;
-constexpr int FLAG_STORAGE_CE = 1 << 1;
-
-// NOTE: keep in sync with Installer
-constexpr int FLAG_CLEAR_CACHE_ONLY = 1 << 8;
-constexpr int FLAG_CLEAR_CODE_CACHE_ONLY = 1 << 9;
-
-/* dexopt needed flags matching those in dalvik.system.DexFile */
-constexpr int DEXOPT_DEX2OAT_NEEDED       = 1;
-constexpr int DEXOPT_PATCHOAT_NEEDED      = 2;
-constexpr int DEXOPT_SELF_PATCHOAT_NEEDED = 3;
-
 /****************************************************************************
  * IMPORTANT: These values are passed from Java code. Keep them in sync with
  * frameworks/base/services/core/java/com/android/server/pm/Installer.java
diff --git a/cmds/installd/installd_deps.h b/cmds/installd/installd_deps.h
index 5ff46e6..5093178 100644
--- a/cmds/installd/installd_deps.h
+++ b/cmds/installd/installd_deps.h
@@ -57,9 +57,6 @@
                               const char *src,
                               const char *instruction_set);
 
-// Initialize globals. May be implemented with the helper in globals.h.
-extern bool initialize_globals();
-
 }  // namespace installd
 }  // namespace android
 
diff --git a/cmds/installd/otapreopt.cpp b/cmds/installd/otapreopt.cpp
index e1cfc9d..6c87f7a 100644
--- a/cmds/installd/otapreopt.cpp
+++ b/cmds/installd/otapreopt.cpp
@@ -60,11 +60,6 @@
 namespace android {
 namespace installd {
 
-static constexpr const char* kBootClassPathPropertyName = "BOOTCLASSPATH";
-static constexpr const char* kAndroidRootPathPropertyName = "ANDROID_ROOT";
-static constexpr const char* kOTARootDirectory = "/system-b";
-static constexpr size_t kISAIndex = 3;
-
 template<typename T>
 static constexpr T RoundDown(T x, typename std::decay<T>::type n) {
     return DCHECK_CONSTEXPR(IsPowerOfTwo(n), , T(0))(x & -n);
@@ -77,8 +72,6 @@
 
 class OTAPreoptService {
  public:
-    static constexpr const char* kOTADataDirectory = "/data/ota";
-
     // Main driver. Performs the following steps.
     //
     // 1) Parse options (read system properties etc from B partition).
@@ -91,26 +84,31 @@
     //
     // 5) Run update.
     int Main(int argc, char** argv) {
+        if (!ReadArguments(argc, argv)) {
+            LOG(ERROR) << "Failed reading command line.";
+            return 1;
+        }
+
         if (!ReadSystemProperties()) {
             LOG(ERROR)<< "Failed reading system properties.";
-            return 1;
+            return 2;
         }
 
         if (!ReadEnvironment()) {
             LOG(ERROR) << "Failed reading environment properties.";
-            return 2;
+            return 3;
         }
 
-        if (!ReadPackage(argc, argv)) {
-            LOG(ERROR) << "Failed reading command line file.";
-            return 3;
+        if (!CheckAndInitializeInstalldGlobals()) {
+            LOG(ERROR) << "Failed initializing globals.";
+            return 4;
         }
 
         PrepareEnvironment();
 
-        if (!PrepareBootImage()) {
+        if (!PrepareBootImage(/* force */ false)) {
             LOG(ERROR) << "Failed preparing boot image.";
-            return 4;
+            return 5;
         }
 
         int dexopt_retcode = RunPreopt();
@@ -118,7 +116,7 @@
         return dexopt_retcode;
     }
 
-    int GetProperty(const char* key, char* value, const char* default_value) {
+    int GetProperty(const char* key, char* value, const char* default_value) const {
         const std::string* prop_value = system_properties_.GetProperty(key);
         if (prop_value == nullptr) {
             if (default_value == nullptr) {
@@ -135,7 +133,16 @@
         return static_cast<int>(size);
     }
 
+    std::string GetOTADataDirectory() const {
+        return StringPrintf("%s/%s", GetOtaDirectoryPrefix().c_str(), target_slot_.c_str());
+    }
+
+    const std::string& GetTargetSlot() const {
+        return target_slot_;
+    }
+
 private:
+
     bool ReadSystemProperties() {
         static constexpr const char* kPropertyFiles[] = {
                 "/default.prop", "/system/build.prop"
@@ -177,29 +184,110 @@
             return false;
         }
 
-        // Check that we found important properties.
-        constexpr const char* kRequiredProperties[] = {
-                kBootClassPathPropertyName, kAndroidRootPathPropertyName
-        };
-        for (size_t i = 0; i < arraysize(kRequiredProperties); ++i) {
-            if (system_properties_.GetProperty(kRequiredProperties[i]) == nullptr) {
-                return false;
-            }
+        if (system_properties_.GetProperty(kAndroidDataPathPropertyName) == nullptr) {
+            return false;
+        }
+        android_data_ = *system_properties_.GetProperty(kAndroidDataPathPropertyName);
+
+        if (system_properties_.GetProperty(kAndroidRootPathPropertyName) == nullptr) {
+            return false;
+        }
+        android_root_ = *system_properties_.GetProperty(kAndroidRootPathPropertyName);
+
+        if (system_properties_.GetProperty(kBootClassPathPropertyName) == nullptr) {
+            return false;
+        }
+        boot_classpath_ = *system_properties_.GetProperty(kBootClassPathPropertyName);
+
+        if (system_properties_.GetProperty(ASEC_MOUNTPOINT_ENV_NAME) == nullptr) {
+            return false;
+        }
+        asec_mountpoint_ = *system_properties_.GetProperty(ASEC_MOUNTPOINT_ENV_NAME);
+
+        return true;
+    }
+
+    const std::string& GetAndroidData() const {
+        return android_data_;
+    }
+
+    const std::string& GetAndroidRoot() const {
+        return android_root_;
+    }
+
+    const std::string GetOtaDirectoryPrefix() const {
+        return GetAndroidData() + "/ota";
+    }
+
+    bool CheckAndInitializeInstalldGlobals() {
+        // init_globals_from_data_and_root requires "ASEC_MOUNTPOINT" in the environment. We
+        // do not use any datapath that includes this, but we'll still have to set it.
+        CHECK(system_properties_.GetProperty(ASEC_MOUNTPOINT_ENV_NAME) != nullptr);
+        int result = setenv(ASEC_MOUNTPOINT_ENV_NAME, asec_mountpoint_.c_str(), 0);
+        if (result != 0) {
+            LOG(ERROR) << "Could not set ASEC_MOUNTPOINT environment variable";
+            return false;
+        }
+
+        if (!init_globals_from_data_and_root(GetAndroidData().c_str(), GetAndroidRoot().c_str())) {
+            LOG(ERROR) << "Could not initialize globals; exiting.";
+            return false;
+        }
+
+        // This is different from the normal installd. We only do the base
+        // directory, the rest will be created on demand when each app is compiled.
+        if (access(GetOtaDirectoryPrefix().c_str(), R_OK) < 0) {
+            LOG(ERROR) << "Could not access " << GetOtaDirectoryPrefix();
+            return false;
         }
 
         return true;
     }
 
-    bool ReadPackage(int argc ATTRIBUTE_UNUSED, char** argv) {
-        size_t index = 0;
+    bool ReadArguments(int argc ATTRIBUTE_UNUSED, char** argv) {
+        // Expected command line:
+        //   target-slot dexopt {DEXOPT_PARAMETERS}
+        // The DEXOPT_PARAMETERS are passed on to dexopt(), so we expect DEXOPT_PARAM_COUNT
+        // of them. We store them in package_parameters_ (size checks are done when
+        // parsing the special parameters and when copying into package_parameters_.
+
         static_assert(DEXOPT_PARAM_COUNT == ARRAY_SIZE(package_parameters_),
                       "Unexpected dexopt param count");
+
+        const char* target_slot_arg = argv[1];
+        if (target_slot_arg == nullptr) {
+            LOG(ERROR) << "Missing parameters";
+            return false;
+        }
+        // Sanitize value. Only allow (a-zA-Z0-9_)+.
+        target_slot_ = target_slot_arg;
+        {
+            std::regex slot_suffix_regex("[a-zA-Z0-9_]+");
+            std::smatch slot_suffix_match;
+            if (!std::regex_match(target_slot_, slot_suffix_match, slot_suffix_regex)) {
+                LOG(ERROR) << "Target slot suffix not legal: " << target_slot_;
+                return false;
+            }
+        }
+
+        // Check for "dexopt" next.
+        if (argv[2] == nullptr) {
+            LOG(ERROR) << "Missing parameters";
+            return false;
+        }
+        if (std::string("dexopt").compare(argv[2]) != 0) {
+            LOG(ERROR) << "Second parameter not dexopt: " << argv[2];
+            return false;
+        }
+
+        // Copy the rest into package_parameters_, but be careful about over- and underflow.
+        size_t index = 0;
         while (index < DEXOPT_PARAM_COUNT &&
-                argv[index + 1] != nullptr) {
-            package_parameters_[index] = argv[index + 1];
+                argv[index + 3] != nullptr) {
+            package_parameters_[index] = argv[index + 3];
             index++;
         }
-        if (index != ARRAY_SIZE(package_parameters_) || argv[index + 1] != nullptr) {
+        if (index != ARRAY_SIZE(package_parameters_) || argv[index + 3] != nullptr) {
             LOG(ERROR) << "Wrong number of parameters";
             return false;
         }
@@ -208,15 +296,9 @@
     }
 
     void PrepareEnvironment() {
-        CHECK(system_properties_.GetProperty(kBootClassPathPropertyName) != nullptr);
-        const std::string& boot_cp =
-                *system_properties_.GetProperty(kBootClassPathPropertyName);
-        environ_.push_back(StringPrintf("BOOTCLASSPATH=%s", boot_cp.c_str()));
-        environ_.push_back(StringPrintf("ANDROID_DATA=%s", kOTADataDirectory));
-        CHECK(system_properties_.GetProperty(kAndroidRootPathPropertyName) != nullptr);
-        const std::string& android_root =
-                *system_properties_.GetProperty(kAndroidRootPathPropertyName);
-        environ_.push_back(StringPrintf("ANDROID_ROOT=%s", android_root.c_str()));
+        environ_.push_back(StringPrintf("BOOTCLASSPATH=%s", boot_classpath_.c_str()));
+        environ_.push_back(StringPrintf("ANDROID_DATA=%s", GetOTADataDirectory().c_str()));
+        environ_.push_back(StringPrintf("ANDROID_ROOT=%s", android_root_.c_str()));
 
         for (const std::string& e : environ_) {
             putenv(const_cast<char*>(e.c_str()));
@@ -225,7 +307,7 @@
 
     // Ensure that we have the right boot image. The first time any app is
     // compiled, we'll try to generate it.
-    bool PrepareBootImage() {
+    bool PrepareBootImage(bool force) const {
         if (package_parameters_[kISAIndex] == nullptr) {
             LOG(ERROR) << "Instruction set missing.";
             return false;
@@ -233,44 +315,114 @@
         const char* isa = package_parameters_[kISAIndex];
 
         // Check whether the file exists where expected.
-        std::string dalvik_cache = std::string(kOTADataDirectory) + "/" + DALVIK_CACHE;
+        std::string dalvik_cache = GetOTADataDirectory() + "/" + DALVIK_CACHE;
         std::string isa_path = dalvik_cache + "/" + isa;
         std::string art_path = isa_path + "/system@framework@boot.art";
         std::string oat_path = isa_path + "/system@framework@boot.oat";
-        if (access(art_path.c_str(), F_OK) == 0 &&
-                access(oat_path.c_str(), F_OK) == 0) {
-            // Files exist, assume everything is alright.
-            return true;
+        bool cleared = false;
+        if (access(art_path.c_str(), F_OK) == 0 && access(oat_path.c_str(), F_OK) == 0) {
+            // Files exist, assume everything is alright if not forced. Otherwise clean up.
+            if (!force) {
+                return true;
+            }
+            ClearDirectory(isa_path);
+            cleared = true;
         }
 
+        // Reset umask in otapreopt, so that we control the the access for the files we create.
+        umask(0);
+
         // Create the directories, if necessary.
         if (access(dalvik_cache.c_str(), F_OK) != 0) {
-            if (mkdir(dalvik_cache.c_str(), 0711) != 0) {
-                PLOG(ERROR) << "Could not create dalvik-cache dir";
+            if (!CreatePath(dalvik_cache)) {
+                PLOG(ERROR) << "Could not create dalvik-cache dir " << dalvik_cache;
                 return false;
             }
         }
         if (access(isa_path.c_str(), F_OK) != 0) {
-            if (mkdir(isa_path.c_str(), 0711) != 0) {
+            if (!CreatePath(isa_path)) {
                 PLOG(ERROR) << "Could not create dalvik-cache isa dir";
                 return false;
             }
         }
 
         // Prepare to create.
-        // TODO: Delete files, just for a blank slate.
-        const std::string& boot_cp = *system_properties_.GetProperty(kBootClassPathPropertyName);
+        if (!cleared) {
+            ClearDirectory(isa_path);
+        }
 
         std::string preopted_boot_art_path = StringPrintf("/system/framework/%s/boot.art", isa);
         if (access(preopted_boot_art_path.c_str(), F_OK) == 0) {
           return PatchoatBootImage(art_path, isa);
         } else {
           // No preopted boot image. Try to compile.
-          return Dex2oatBootImage(boot_cp, art_path, oat_path, isa);
+          return Dex2oatBootImage(boot_classpath_, art_path, oat_path, isa);
         }
     }
 
-    bool PatchoatBootImage(const std::string& art_path, const char* isa) {
+    static bool CreatePath(const std::string& path) {
+        // Create the given path. Use string processing instead of dirname, as dirname's need for
+        // a writable char buffer is painful.
+
+        // First, try to use the full path.
+        if (mkdir(path.c_str(), 0711) == 0) {
+            return true;
+        }
+        if (errno != ENOENT) {
+            PLOG(ERROR) << "Could not create path " << path;
+            return false;
+        }
+
+        // Now find the parent and try that first.
+        size_t last_slash = path.find_last_of('/');
+        if (last_slash == std::string::npos || last_slash == 0) {
+            PLOG(ERROR) << "Could not create " << path;
+            return false;
+        }
+
+        if (!CreatePath(path.substr(0, last_slash))) {
+            return false;
+        }
+
+        if (mkdir(path.c_str(), 0711) == 0) {
+            return true;
+        }
+        PLOG(ERROR) << "Could not create " << path;
+        return false;
+    }
+
+    static void ClearDirectory(const std::string& dir) {
+        DIR* c_dir = opendir(dir.c_str());
+        if (c_dir == nullptr) {
+            PLOG(WARNING) << "Unable to open " << dir << " to delete it's contents";
+            return;
+        }
+
+        for (struct dirent* de = readdir(c_dir); de != nullptr; de = readdir(c_dir)) {
+            const char* name = de->d_name;
+            if (strcmp(name, ".") == 0 || strcmp(name, "..") == 0) {
+                continue;
+            }
+            // We only want to delete regular files and symbolic links.
+            std::string file = StringPrintf("%s/%s", dir.c_str(), name);
+            if (de->d_type != DT_REG && de->d_type != DT_LNK) {
+                LOG(WARNING) << "Unexpected file "
+                             << file
+                             << " of type "
+                             << std::hex
+                             << de->d_type
+                             << " encountered.";
+            } else {
+                // Try to unlink the file.
+                if (unlink(file.c_str()) != 0) {
+                    PLOG(ERROR) << "Unable to unlink " << file;
+                }
+            }
+        }
+        CHECK_EQ(0, closedir(c_dir)) << "Unable to close directory.";
+    }
+
+    bool PatchoatBootImage(const std::string& art_path, const char* isa) const {
         // This needs to be kept in sync with ART, see art/runtime/gc/space/image_space.cc.
 
         std::vector<std::string> cmd;
@@ -296,7 +448,7 @@
     bool Dex2oatBootImage(const std::string& boot_cp,
                           const std::string& art_path,
                           const std::string& oat_path,
-                          const char* isa) {
+                          const char* isa) const {
         // This needs to be kept in sync with ART, see art/runtime/gc/space/image_space.cc.
         std::vector<std::string> cmd;
         cmd.push_back("/system/bin/dex2oat");
@@ -362,9 +514,7 @@
         return (strcmp(arg, "!") == 0) ? nullptr : arg;
     }
 
-    int RunPreopt() {
-        // Run the preopt.
-        //
+    bool ShouldSkipPreopt() const {
         // There's one thing we have to be careful about: we may/will be asked to compile an app
         // living in the system image. This may be a valid request - if the app wasn't compiled,
         // e.g., if the system image wasn't large enough to include preopted files. However, the
@@ -391,9 +541,7 @@
         constexpr size_t kApkPathIndex = 0;
         CHECK_GT(DEXOPT_PARAM_COUNT, kApkPathIndex);
         CHECK(package_parameters_[kApkPathIndex] != nullptr);
-        CHECK(system_properties_.GetProperty(kAndroidRootPathPropertyName) != nullptr);
-        if (StartsWith(package_parameters_[kApkPathIndex],
-                       system_properties_.GetProperty(kAndroidRootPathPropertyName)->c_str())) {
+        if (StartsWith(package_parameters_[kApkPathIndex], android_root_.c_str())) {
             const char* last_slash = strrchr(package_parameters_[kApkPathIndex], '/');
             if (last_slash != nullptr) {
                 std::string path(package_parameters_[kApkPathIndex],
@@ -401,11 +549,45 @@
                 CHECK(EndsWith(path, "/"));
                 path = path + "oat";
                 if (access(path.c_str(), F_OK) == 0) {
-                    return 0;
+                    return true;
                 }
             }
         }
 
+        // Another issue is unavailability of files in the new system. If the partition
+        // layout changes, otapreopt_chroot may not know about this. Then files from that
+        // partition will not be available and fail to build. This is problematic, as
+        // this tool will wipe the OTA artifact cache and try again (for robustness after
+        // a failed OTA with remaining cache artifacts).
+        if (access(package_parameters_[kApkPathIndex], F_OK) != 0) {
+            LOG(WARNING) << "Skipping preopt of non-existing package "
+                         << package_parameters_[kApkPathIndex];
+            return true;
+        }
+
+        return false;
+    }
+
+    int RunPreopt() {
+        if (ShouldSkipPreopt()) {
+            return 0;
+        }
+
+        int dexopt_result = dexopt(package_parameters_);
+        if (dexopt_result == 0) {
+            return 0;
+        }
+
+        // If the dexopt failed, we may have a stale boot image from a previous OTA run.
+        // Try to delete and retry.
+
+        if (!PrepareBootImage(/* force */ true)) {
+            LOG(ERROR) << "Forced boot image creating failed. Original error return was "
+                         << dexopt_result;
+            return dexopt_result;
+        }
+
+        LOG(WARNING) << "Original dexopt failed, re-trying after boot image was regenerated.";
         return dexopt(package_parameters_);
     }
 
@@ -414,7 +596,7 @@
     ////////////////////////////////////
 
     // Wrapper on fork/execv to run a command in a subprocess.
-    bool Exec(const std::vector<std::string>& arg_vector, std::string* error_msg) {
+    static bool Exec(const std::vector<std::string>& arg_vector, std::string* error_msg) {
         const std::string command_line = Join(arg_vector, ' ');
 
         CHECK_GE(arg_vector.size(), 1U) << command_line;
@@ -504,9 +686,8 @@
     void AddCompilerOptionFromSystemProperty(const char* system_property,
             const char* prefix,
             bool runtime,
-            std::vector<std::string>& out) {
-        const std::string* value =
-        system_properties_.GetProperty(system_property);
+            std::vector<std::string>& out) const {
+        const std::string* value = system_properties_.GetProperty(system_property);
         if (value != nullptr) {
             if (runtime) {
                 out.push_back("--runtime-arg");
@@ -519,10 +700,24 @@
         }
     }
 
+    static constexpr const char* kBootClassPathPropertyName = "BOOTCLASSPATH";
+    static constexpr const char* kAndroidRootPathPropertyName = "ANDROID_ROOT";
+    static constexpr const char* kAndroidDataPathPropertyName = "ANDROID_DATA";
+    // The index of the instruction-set string inside the package parameters. Needed for
+    // some special-casing that requires knowledge of the instruction-set.
+    static constexpr size_t kISAIndex = 3;
+
     // Stores the system properties read out of the B partition. We need to use these properties
     // to compile, instead of the A properties we could get from init/get_property.
     SystemProperties system_properties_;
 
+    // Some select properties that are always needed.
+    std::string target_slot_;
+    std::string android_root_;
+    std::string android_data_;
+    std::string boot_classpath_;
+    std::string asec_mountpoint_;
+
     const char* package_parameters_[DEXOPT_PARAM_COUNT];
 
     // Store environment values we need to set.
@@ -563,8 +758,13 @@
     std::string file_name(file_name_start, file_name_len);
 
     // <apk_parent_dir>/oat/<isa>/<file_name>.odex.b
-    snprintf(path, PKG_PATH_MAX, "%s/%s/%s.odex.b", oat_dir, instruction_set,
-             file_name.c_str());
+    snprintf(path,
+             PKG_PATH_MAX,
+             "%s/%s/%s.odex.%s",
+             oat_dir,
+             instruction_set,
+             file_name.c_str(),
+             gOps.GetTargetSlot().c_str());
     return true;
 }
 
@@ -576,11 +776,6 @@
  */
 bool calculate_odex_file_path(char path[PKG_PATH_MAX], const char *apk_path,
                               const char *instruction_set) {
-    if (StringPrintf("%soat/%s/odex.b", apk_path, instruction_set).length() + 1 > PKG_PATH_MAX) {
-        ALOGE("apk_path '%s' may be too long to form odex file path.\n", apk_path);
-        return false;
-    }
-
     const char *path_end = strrchr(apk_path, '/');
     if (path_end == nullptr) {
         ALOGE("apk_path '%s' has no '/'s in it?!\n", apk_path);
@@ -596,11 +791,15 @@
     }
     std::string name_component(name_begin, extension_start - name_begin);
 
-    std::string new_path = StringPrintf("%s/oat/%s/%s.odex.b",
+    std::string new_path = StringPrintf("%s/oat/%s/%s.odex.%s",
                                         path_component.c_str(),
                                         instruction_set,
-                                        name_component.c_str());
-    CHECK_LT(new_path.length(), PKG_PATH_MAX);
+                                        name_component.c_str(),
+                                        gOps.GetTargetSlot().c_str());
+    if (new_path.length() >= PKG_PATH_MAX) {
+        LOG(ERROR) << "apk_path of " << apk_path << " is too long: " << new_path;
+        return false;
+    }
     strcpy(path, new_path.c_str());
     return true;
 }
@@ -623,7 +822,7 @@
     std::replace(from_src.begin(), from_src.end(), '/', '@');
 
     std::string assembled_path = StringPrintf("%s/%s/%s/%s%s",
-                                              OTAPreoptService::kOTADataDirectory,
+                                              gOps.GetOTADataDirectory().c_str(),
                                               DALVIK_CACHE,
                                               instruction_set,
                                               from_src.c_str(),
@@ -637,27 +836,6 @@
     return true;
 }
 
-bool initialize_globals() {
-    const char* data_path = getenv("ANDROID_DATA");
-    if (data_path == nullptr) {
-        ALOGE("Could not find ANDROID_DATA");
-        return false;
-    }
-    return init_globals_from_data_and_root(data_path, kOTARootDirectory);
-}
-
-static bool initialize_directories() {
-    // This is different from the normal installd. We only do the base
-    // directory, the rest will be created on demand when each app is compiled.
-    mode_t old_umask = umask(0);
-    LOG(INFO) << "Old umask: " << old_umask;
-    if (access(OTAPreoptService::kOTADataDirectory, R_OK) < 0) {
-        ALOGE("Could not access %s\n", OTAPreoptService::kOTADataDirectory);
-        return false;
-    }
-    return true;
-}
-
 static int log_callback(int type, const char *fmt, ...) {
     va_list ap;
     int priority;
@@ -685,8 +863,6 @@
     setenv("ANDROID_LOG_TAGS", "*:v", 1);
     android::base::InitLogging(argv);
 
-    ALOGI("otapreopt firing up\n");
-
     if (argc < 2) {
         ALOGE("Expecting parameters");
         exit(1);
@@ -696,16 +872,6 @@
     cb.func_log = log_callback;
     selinux_set_callback(SELINUX_CB_LOG, cb);
 
-    if (!initialize_globals()) {
-        ALOGE("Could not initialize globals; exiting.\n");
-        exit(1);
-    }
-
-    if (!initialize_directories()) {
-        ALOGE("Could not create directories; exiting.\n");
-        exit(1);
-    }
-
     if (selinux_enabled && selinux_status_open(true) < 0) {
         ALOGE("Could not open selinux status; exiting.\n");
         exit(1);
diff --git a/cmds/installd/otapreopt_chroot.cpp b/cmds/installd/otapreopt_chroot.cpp
index be0ff2e..e6030bf 100644
--- a/cmds/installd/otapreopt_chroot.cpp
+++ b/cmds/installd/otapreopt_chroot.cpp
@@ -14,15 +14,18 @@
  ** limitations under the License.
  */
 
+#include <fcntl.h>
 #include <linux/unistd.h>
 #include <sys/mount.h>
 #include <sys/wait.h>
 
+#include <sstream>
+
 #include <android-base/logging.h>
 #include <android-base/macros.h>
 #include <android-base/stringprintf.h>
 
-#include <installd_constants.h>
+#include <commands.h>
 
 #ifndef LOG_TAG
 #define LOG_TAG "otapreopt"
@@ -33,7 +36,37 @@
 namespace android {
 namespace installd {
 
+static void CloseDescriptor(int fd) {
+    if (fd >= 0) {
+        int result = close(fd);
+        UNUSED(result);  // Ignore result. Printing to logcat will open a new descriptor
+                         // that we do *not* want.
+    }
+}
+
+static void CloseDescriptor(const char* descriptor_string) {
+    int fd = -1;
+    std::istringstream stream(descriptor_string);
+    stream >> fd;
+    if (!stream.fail()) {
+        CloseDescriptor(fd);
+    }
+}
+
+// Entry for otapreopt_chroot. Expected parameters are:
+//   [cmd] [status-fd] [target-slot] "dexopt" [dexopt-params]
+// The file descriptor denoted by status-fd will be closed. The rest of the parameters will
+// be passed on to otapreopt in the chroot.
 static int otapreopt_chroot(const int argc, char **arg) {
+    // Close all file descriptors. They are coming from the caller, we do not want to pass them
+    // on across our fork/exec into a different domain.
+    // 1) Default descriptors.
+    CloseDescriptor(STDIN_FILENO);
+    CloseDescriptor(STDOUT_FILENO);
+    CloseDescriptor(STDERR_FILENO);
+    // 2) The status channel.
+    CloseDescriptor(arg[1]);
+
     // We need to run the otapreopt tool from the postinstall partition. As such, set up a
     // mount namespace and change root.
 
@@ -80,13 +113,42 @@
 
     // Now go on and run otapreopt.
 
-    const char* argv[1 + DEXOPT_PARAM_COUNT + 1];
-    CHECK_EQ(static_cast<size_t>(argc), DEXOPT_PARAM_COUNT + 1);
-    argv[0] = "/system/bin/otapreopt";
-    for (size_t i = 1; i <= DEXOPT_PARAM_COUNT; ++i) {
-        argv[i] = arg[i];
+    // Incoming:  cmd + status-fd + target-slot + "dexopt" + dexopt-params + null
+    // Outgoing:  cmd             + target-slot + "dexopt" + dexopt-params + null
+    constexpr size_t kInArguments =   1                       // Binary name.
+                                    + 1                       // status file descriptor.
+                                    + 1                       // target-slot.
+                                    + 1                       // "dexopt."
+                                    + DEXOPT_PARAM_COUNT      // dexopt parameters.
+                                    + 1;                      // null termination.
+    constexpr size_t kOutArguments =   1                       // Binary name.
+                                     + 1                       // target-slot.
+                                     + 1                       // "dexopt."
+                                     + DEXOPT_PARAM_COUNT      // dexopt parameters.
+                                     + 1;                      // null termination.
+    const char* argv[kOutArguments];
+    if (static_cast<size_t>(argc) !=  kInArguments - 1 /* null termination */) {
+        LOG(ERROR) << "Unexpected argument size "
+                   << argc
+                   << " vs "
+                   << (kInArguments - 1);
+        for (size_t i = 0; i < static_cast<size_t>(argc); ++i) {
+            if (arg[i] == nullptr) {
+                LOG(ERROR) << "(null)";
+            } else {
+                LOG(ERROR) << "\"" << arg[i] << "\"";
+            }
+        }
+        exit(206);
     }
-    argv[DEXOPT_PARAM_COUNT + 1] = nullptr;
+    argv[0] = "/system/bin/otapreopt";
+
+    // The first parameter is the status file descriptor, skip.
+
+    for (size_t i = 1; i <= kOutArguments - 2 /* cmd + null */; ++i) {
+        argv[i] = arg[i + 1];
+    }
+    argv[kOutArguments - 1] = nullptr;
 
     execv(argv[0], (char * const *)argv);
     PLOG(ERROR) << "execv(OTAPREOPT) failed.";
diff --git a/cmds/installd/otapreopt_script.sh b/cmds/installd/otapreopt_script.sh
index 394c244..8af4a90 100644
--- a/cmds/installd/otapreopt_script.sh
+++ b/cmds/installd/otapreopt_script.sh
@@ -18,11 +18,25 @@
 
 # This script will run as a postinstall step to drive otapreopt.
 
+TARGET_SLOT="$1"
 STATUS_FD="$2"
 
 # Maximum number of packages/steps.
 MAXIMUM_PACKAGES=1000
 
+# Compute target slot suffix.
+# TODO: Once bootctl is not restricted, we should query from there. Or get this from
+#       update_engine as a parameter.
+if [ "$TARGET_SLOT" = "0" ] ; then
+  TARGET_SLOT_SUFFIX="_a"
+elif [ "$TARGET_SLOT" = "1" ] ; then
+  TARGET_SLOT_SUFFIX="_b"
+else
+  echo "Unknown target slot $TARGET_SLOT"
+  exit 1
+fi
+
+
 PREPARE=$(cmd otadexopt prepare)
 # Note: Ignore preparation failures. Step and done will fail and exit this.
 #       This is necessary to support suspends - the OTA service will keep
@@ -33,7 +47,9 @@
 
 i=0
 while ((i<MAXIMUM_PACKAGES)) ; do
-  cmd otadexopt step
+  DEXOPT_PARAMS=$(cmd otadexopt next)
+
+  /system/bin/otapreopt_chroot $STATUS_FD $TARGET_SLOT_SUFFIX $DEXOPT_PARAMS >&- 2>&-
 
   PROGRESS=$(cmd otadexopt progress)
   print -u${STATUS_FD} "global_progress $PROGRESS"