Merge "Revert "Use arm instruction set with clang 7.0 LTO""
diff --git a/cmds/atrace/atrace.cpp b/cmds/atrace/atrace.cpp
index 9dbbb77..e9758a0 100644
--- a/cmds/atrace/atrace.cpp
+++ b/cmds/atrace/atrace.cpp
@@ -56,7 +56,6 @@
 using std::string;
 
 #define MAX_SYS_FILES 10
-#define MAX_PACKAGES 16
 
 const char* k_traceTagsProperty = "debug.atrace.tags.enableflags";
 
@@ -588,12 +587,6 @@
 
 static void clearAppProperties()
 {
-    for (int i = 0; i < MAX_PACKAGES; i++) {
-        std::string key = android::base::StringPrintf(k_traceAppsPropertyTemplate, i);
-        if (!android::base::SetProperty(key, "")) {
-            fprintf(stderr, "failed to clear system property: %s\n", key.c_str());
-        }
-    }
     if (!android::base::SetProperty(k_traceAppsNumberProperty, "")) {
         fprintf(stderr, "failed to clear system property: %s",
               k_traceAppsNumberProperty);
@@ -607,11 +600,6 @@
     int i = 0;
     char* start = cmdline;
     while (start != NULL) {
-        if (i == MAX_PACKAGES) {
-            fprintf(stderr, "error: only 16 packages could be traced at once\n");
-            clearAppProperties();
-            return false;
-        }
         char* end = strchr(start, ',');
         if (end != NULL) {
             *end = '\0';
@@ -1049,7 +1037,7 @@
     fprintf(stderr, "usage: %s [options] [categories...]\n", cmd);
     fprintf(stderr, "options include:\n"
                     "  -a appname      enable app-level tracing for a comma "
-                        "separated list of cmdlines\n"
+                        "separated list of cmdlines; * is a wildcard matching any process\n"
                     "  -b N            use a trace buffer size of N KB\n"
                     "  -c              trace into a circular buffer\n"
                     "  -f filename     use the categories written in a file as space-separated\n"
diff --git a/cmds/atrace/atrace.rc b/cmds/atrace/atrace.rc
index 89ce1e5..3ec0163 100644
--- a/cmds/atrace/atrace.rc
+++ b/cmds/atrace/atrace.rc
@@ -6,6 +6,9 @@
     chmod 0222 /sys/kernel/debug/tracing/trace_marker
     chmod 0222 /sys/kernel/tracing/trace_marker
 
+# Scheduler tracepoints require schedstats=enable
+    write /proc/sys/kernel/sched_schedstats 1
+
 # Grant unix world read/write permissions to kernel tracepoints.
 # Access control to these files is now entirely in selinux policy.
     chmod 0666 /sys/kernel/debug/tracing/trace_clock
diff --git a/cmds/dumpstate/dumpstate.cpp b/cmds/dumpstate/dumpstate.cpp
index a641dbb..98ad138 100644
--- a/cmds/dumpstate/dumpstate.cpp
+++ b/cmds/dumpstate/dumpstate.cpp
@@ -180,7 +180,7 @@
             continue;
         }
 
-        if (limit_by_mtime && st.st_mtime >= thirty_minutes_ago) {
+        if (limit_by_mtime && st.st_mtime < thirty_minutes_ago) {
             MYLOGI("Excluding stale dump file: %s\n", abs_path.c_str());
             continue;
         }
@@ -1585,6 +1585,7 @@
             is_remote_mode = 1;
             do_fb = 0;
         } else if (ds.extra_options_ == "bugreportwear") {
+            do_start_service = true;
             ds.update_progress_ = true;
         } else if (ds.extra_options_ == "bugreporttelephony") {
             telephony_only = true;
diff --git a/cmds/installd/Android.bp b/cmds/installd/Android.bp
index 7d1537a..352cf0a 100644
--- a/cmds/installd/Android.bp
+++ b/cmds/installd/Android.bp
@@ -18,6 +18,7 @@
     shared_libs: [
         "libbase",
         "libbinder",
+        "libcrypto",
         "libcutils",
         "liblog",
         "liblogwrap",
@@ -91,4 +92,28 @@
     ],
 }
 
+//
+// Static library for otapreopt used in testing
+//
+cc_library_static {
+    name: "libotapreoptparameters",
+    cflags: [
+        "-Wall",
+        "-Werror"
+    ],
+    clang: true,
+
+    srcs: [
+        "otapreopt_parameters.cpp"],
+
+    export_include_dirs: ["."],
+
+    shared_libs: [
+        "libbase",
+        "libcutils",
+        "liblog",
+        "libutils",
+    ],
+}
+
 subdirs = ["tests"]
diff --git a/cmds/installd/Android.mk b/cmds/installd/Android.mk
index 1d21b3c..a4f95da 100644
--- a/cmds/installd/Android.mk
+++ b/cmds/installd/Android.mk
@@ -23,10 +23,11 @@
 LOCAL_CFLAGS += -DART_BASE_ADDRESS_MIN_DELTA=$(LOCAL_LIBART_IMG_HOST_MIN_BASE_ADDRESS_DELTA)
 LOCAL_CFLAGS += -DART_BASE_ADDRESS_MAX_DELTA=$(LOCAL_LIBART_IMG_HOST_MAX_BASE_ADDRESS_DELTA)
 
-LOCAL_SRC_FILES := otapreopt.cpp globals.cpp utils.cpp dexopt.cpp
+LOCAL_SRC_FILES := otapreopt.cpp otapreopt_parameters.cpp globals.cpp utils.cpp dexopt.cpp
 LOCAL_HEADER_LIBRARIES := dex2oat_headers
 LOCAL_SHARED_LIBRARIES := \
     libbase \
+    libcrypto \
     libcutils \
     liblog \
     liblogwrap \
diff --git a/cmds/installd/InstalldNativeService.cpp b/cmds/installd/InstalldNativeService.cpp
index b0661c5..68acc69 100644
--- a/cmds/installd/InstalldNativeService.cpp
+++ b/cmds/installd/InstalldNativeService.cpp
@@ -103,6 +103,7 @@
 }
 
 static binder::Status exception(uint32_t code, const std::string& msg) {
+    LOG(ERROR) << msg << " (" << code << ")";
     return binder::Status::fromExceptionCode(code, String8(msg.c_str()));
 }
 
@@ -366,13 +367,6 @@
         PLOG(ERROR) << "Failed to prepare " << profile_dir;
         return false;
     }
-    const std::string profile_file = create_current_profile_path(userId, packageName,
-            /*is_secondary_dex*/false);
-    // read-write only for the app user.
-    if (fs_prepare_file_strict(profile_file.c_str(), 0600, uid, uid) != 0) {
-        PLOG(ERROR) << "Failed to prepare " << profile_file;
-        return false;
-    }
 
     const std::string ref_profile_path =
             create_primary_reference_profile_package_dir_path(packageName);
@@ -522,16 +516,17 @@
 }
 
 
-binder::Status InstalldNativeService::clearAppProfiles(const std::string& packageName) {
+binder::Status InstalldNativeService::clearAppProfiles(const std::string& packageName,
+        const std::string& profileName) {
     ENFORCE_UID(AID_SYSTEM);
     CHECK_ARGUMENT_PACKAGE_NAME(packageName);
     std::lock_guard<std::recursive_mutex> lock(mLock);
 
     binder::Status res = ok();
-    if (!clear_primary_reference_profile(packageName)) {
+    if (!clear_primary_reference_profile(packageName, profileName)) {
         res = error("Failed to clear reference profile for " + packageName);
     }
-    if (!clear_primary_current_profiles(packageName)) {
+    if (!clear_primary_current_profiles(packageName, profileName)) {
         res = error("Failed to clear current profiles for " + packageName);
     }
     return res;
@@ -581,11 +576,6 @@
                 res = error("Failed to delete contents of " + path);
             }
         }
-        if (!only_cache) {
-            if (!clear_primary_current_profile(packageName, userId)) {
-                res = error("Failed to clear current profile for " + packageName);
-            }
-        }
     }
     return res;
 }
@@ -1833,68 +1823,74 @@
 // Dumps the contents of a profile file, using pkgname's dex files for pretty
 // printing the result.
 binder::Status InstalldNativeService::dumpProfiles(int32_t uid, const std::string& packageName,
-        const std::string& codePaths, bool* _aidl_return) {
+        const std::string& profileName, const std::string& codePath, bool* _aidl_return) {
     ENFORCE_UID(AID_SYSTEM);
     CHECK_ARGUMENT_PACKAGE_NAME(packageName);
     std::lock_guard<std::recursive_mutex> lock(mLock);
 
-    const char* pkgname = packageName.c_str();
-    const char* code_paths = codePaths.c_str();
-
-    *_aidl_return = dump_profiles(uid, pkgname, code_paths);
+    *_aidl_return = dump_profiles(uid, packageName, profileName, codePath);
     return ok();
 }
 
 // Copy the contents of a system profile over the data profile.
 binder::Status InstalldNativeService::copySystemProfile(const std::string& systemProfile,
-        int32_t packageUid, const std::string& packageName, bool* _aidl_return) {
+        int32_t packageUid, const std::string& packageName, const std::string& profileName,
+        bool* _aidl_return) {
     ENFORCE_UID(AID_SYSTEM);
     CHECK_ARGUMENT_PACKAGE_NAME(packageName);
     std::lock_guard<std::recursive_mutex> lock(mLock);
-    *_aidl_return = copy_system_profile(systemProfile, packageUid, packageName);
+    *_aidl_return = copy_system_profile(systemProfile, packageUid, packageName, profileName);
     return ok();
 }
 
 // TODO: Consider returning error codes.
 binder::Status InstalldNativeService::mergeProfiles(int32_t uid, const std::string& packageName,
-        bool* _aidl_return) {
+        const std::string& profileName, bool* _aidl_return) {
     ENFORCE_UID(AID_SYSTEM);
     CHECK_ARGUMENT_PACKAGE_NAME(packageName);
     std::lock_guard<std::recursive_mutex> lock(mLock);
 
-    *_aidl_return = analyze_primary_profiles(uid, packageName);
+    *_aidl_return = analyze_primary_profiles(uid, packageName, profileName);
     return ok();
 }
 
 binder::Status InstalldNativeService::createProfileSnapshot(int32_t appId,
-        const std::string& packageName, const std::string& codePath, bool* _aidl_return) {
+        const std::string& packageName, const std::string& profileName,
+        const std::string& classpath, bool* _aidl_return) {
     ENFORCE_UID(AID_SYSTEM);
     CHECK_ARGUMENT_PACKAGE_NAME(packageName);
     std::lock_guard<std::recursive_mutex> lock(mLock);
 
-    *_aidl_return = create_profile_snapshot(appId, packageName, codePath);
+    *_aidl_return = create_profile_snapshot(appId, packageName, profileName, classpath);
     return ok();
 }
 
 binder::Status InstalldNativeService::destroyProfileSnapshot(const std::string& packageName,
-        const std::string& codePath) {
+        const std::string& profileName) {
     ENFORCE_UID(AID_SYSTEM);
     CHECK_ARGUMENT_PACKAGE_NAME(packageName);
     std::lock_guard<std::recursive_mutex> lock(mLock);
 
-    std::string snapshot = create_snapshot_profile_path(packageName, codePath);
+    std::string snapshot = create_snapshot_profile_path(packageName, profileName);
     if ((unlink(snapshot.c_str()) != 0) && (errno != ENOENT)) {
-        return error("Failed to destroy profile snapshot for " + packageName + ":" + codePath);
+        return error("Failed to destroy profile snapshot for " + packageName + ":" + profileName);
     }
     return ok();
 }
 
+static const char* getCStr(const std::unique_ptr<std::string>& data,
+        const char* default_value = nullptr) {
+    return data == nullptr ? default_value : data->c_str();
+}
 binder::Status InstalldNativeService::dexopt(const std::string& apkPath, int32_t uid,
         const std::unique_ptr<std::string>& packageName, const std::string& instructionSet,
         int32_t dexoptNeeded, const std::unique_ptr<std::string>& outputPath, int32_t dexFlags,
         const std::string& compilerFilter, const std::unique_ptr<std::string>& uuid,
         const std::unique_ptr<std::string>& classLoaderContext,
-        const std::unique_ptr<std::string>& seInfo, bool downgrade, int32_t targetSdkVersion) {
+        const std::unique_ptr<std::string>& seInfo, bool downgrade, int32_t targetSdkVersion,
+        const std::unique_ptr<std::string>& profileName,
+        const std::unique_ptr<std::string>& dexMetadataPath,
+        const std::unique_ptr<std::string>& compilationReason) {
     ENFORCE_UID(AID_SYSTEM);
     CHECK_ARGUMENT_UUID(uuid);
     if (packageName && *packageName != "*") {
@@ -1903,16 +1899,19 @@
     std::lock_guard<std::recursive_mutex> lock(mLock);
 
     const char* apk_path = apkPath.c_str();
-    const char* pkgname = packageName ? packageName->c_str() : "*";
+    const char* pkgname = getCStr(packageName, "*");
     const char* instruction_set = instructionSet.c_str();
-    const char* oat_dir = outputPath ? outputPath->c_str() : nullptr;
+    const char* oat_dir = getCStr(outputPath);
     const char* compiler_filter = compilerFilter.c_str();
-    const char* volume_uuid = uuid ? uuid->c_str() : nullptr;
-    const char* class_loader_context = classLoaderContext ? classLoaderContext->c_str() : nullptr;
-    const char* se_info = seInfo ? seInfo->c_str() : nullptr;
+    const char* volume_uuid = getCStr(uuid);
+    const char* class_loader_context = getCStr(classLoaderContext);
+    const char* se_info = getCStr(seInfo);
+    const char* profile_name = getCStr(profileName);
+    const char* dm_path = getCStr(dexMetadataPath);
+    const char* compilation_reason = getCStr(compilationReason);
     int res = android::installd::dexopt(apk_path, uid, pkgname, instruction_set, dexoptNeeded,
             oat_dir, dexFlags, compiler_filter, volume_uuid, class_loader_context, se_info,
-            downgrade, targetSdkVersion);
+            downgrade, targetSdkVersion, profile_name, dm_path, compilation_reason);
     return res ? error(res, "Failed to dexopt") : ok();
 }
 
@@ -2379,6 +2378,22 @@
     return result ? ok() : error();
 }
 
+binder::Status InstalldNativeService::hashSecondaryDexFile(
+        const std::string& dexPath, const std::string& packageName, int32_t uid,
+        const std::unique_ptr<std::string>& volumeUuid, int32_t storageFlag,
+        std::vector<uint8_t>* _aidl_return) {
+    ENFORCE_UID(AID_SYSTEM);
+    CHECK_ARGUMENT_UUID(volumeUuid);
+    CHECK_ARGUMENT_PACKAGE_NAME(packageName);
+
+    // mLock is not taken here since we will never modify the file system.
+    // If a file is modified just as we are reading it this may result in an
+    // anomalous hash, but that's ok.
+    bool result = android::installd::hash_secondary_dex_file(
+        dexPath, packageName, uid, volumeUuid, storageFlag, _aidl_return);
+    return result ? ok() : error();
+}
+
 binder::Status InstalldNativeService::invalidateMounts() {
     ENFORCE_UID(AID_SYSTEM);
     std::lock_guard<std::recursive_mutex> lock(mMountsLock);
@@ -2457,5 +2472,17 @@
     return ok();
 }
 
+binder::Status InstalldNativeService::prepareAppProfile(const std::string& packageName,
+        int32_t userId, int32_t appId, const std::string& profileName, const std::string& codePath,
+        const std::unique_ptr<std::string>& dexMetadata, bool* _aidl_return) {
+    ENFORCE_UID(AID_SYSTEM);
+    CHECK_ARGUMENT_PACKAGE_NAME(packageName);
+    std::lock_guard<std::recursive_mutex> lock(mLock);
+
+    *_aidl_return = prepare_app_profile(packageName, userId, appId, profileName, codePath,
+        dexMetadata);
+    return ok();
+}
+
 }  // namespace installd
 }  // namespace android
diff --git a/cmds/installd/InstalldNativeService.h b/cmds/installd/InstalldNativeService.h
index 93b59ed..1648603 100644
--- a/cmds/installd/InstalldNativeService.h
+++ b/cmds/installd/InstalldNativeService.h
@@ -85,22 +85,26 @@
             const std::string& compilerFilter, const std::unique_ptr<std::string>& uuid,
             const std::unique_ptr<std::string>& classLoaderContext,
             const std::unique_ptr<std::string>& seInfo, bool downgrade,
-            int32_t targetSdkVersion);
+            int32_t targetSdkVersion, const std::unique_ptr<std::string>& profileName,
+            const std::unique_ptr<std::string>& dexMetadataPath,
+            const std::unique_ptr<std::string>& compilationReason);
 
     binder::Status rmdex(const std::string& codePath, const std::string& instructionSet);
 
-    binder::Status mergeProfiles(int32_t uid, const std::string& packageName, bool* _aidl_return);
+    binder::Status mergeProfiles(int32_t uid, const std::string& packageName,
+            const std::string& profileName, bool* _aidl_return);
     binder::Status dumpProfiles(int32_t uid, const std::string& packageName,
-            const std::string& codePaths, bool* _aidl_return);
+            const std::string& profileName, const std::string& codePath, bool* _aidl_return);
     binder::Status copySystemProfile(const std::string& systemProfile,
-            int32_t uid, const std::string& packageName, bool* _aidl_return);
-    binder::Status clearAppProfiles(const std::string& packageName);
+            int32_t uid, const std::string& packageName, const std::string& profileName,
+            bool* _aidl_return);
+    binder::Status clearAppProfiles(const std::string& packageName, const std::string& profileName);
     binder::Status destroyAppProfiles(const std::string& packageName);
 
     binder::Status createProfileSnapshot(int32_t appId, const std::string& packageName,
-            const std::string& codePath, bool* _aidl_return);
+            const std::string& profileName, const std::string& classpath, bool* _aidl_return);
     binder::Status destroyProfileSnapshot(const std::string& packageName,
-            const std::string& codePath);
+            const std::string& profileName);
 
     binder::Status idmap(const std::string& targetApkPath, const std::string& overlayApkPath,
             int32_t uid);
@@ -123,11 +127,19 @@
     binder::Status reconcileSecondaryDexFile(const std::string& dexPath,
         const std::string& packageName, int32_t uid, const std::vector<std::string>& isa,
         const std::unique_ptr<std::string>& volumeUuid, int32_t storage_flag, bool* _aidl_return);
+    binder::Status hashSecondaryDexFile(const std::string& dexPath,
+        const std::string& packageName, int32_t uid, const std::unique_ptr<std::string>& volumeUuid,
+        int32_t storageFlag, std::vector<uint8_t>* _aidl_return);
 
     binder::Status invalidateMounts();
     binder::Status isQuotaSupported(const std::unique_ptr<std::string>& volumeUuid,
             bool* _aidl_return);
 
+    binder::Status prepareAppProfile(const std::string& packageName,
+            int32_t userId, int32_t appId, const std::string& profileName,
+            const std::string& codePath, const std::unique_ptr<std::string>& dexMetadata,
+            bool* _aidl_return);
+
 private:
     std::recursive_mutex mLock;
 
diff --git a/cmds/installd/binder/android/os/IInstalld.aidl b/cmds/installd/binder/android/os/IInstalld.aidl
index 2c9c6bd..432751f 100644
--- a/cmds/installd/binder/android/os/IInstalld.aidl
+++ b/cmds/installd/binder/android/os/IInstalld.aidl
@@ -51,20 +51,24 @@
             @nullable @utf8InCpp String outputPath, int dexFlags,
             @utf8InCpp String compilerFilter, @nullable @utf8InCpp String uuid,
             @nullable @utf8InCpp String sharedLibraries,
-            @nullable @utf8InCpp String seInfo, boolean downgrade, int targetSdkVersion);
+            @nullable @utf8InCpp String seInfo, boolean downgrade, int targetSdkVersion,
+            @nullable @utf8InCpp String profileName,
+            @nullable @utf8InCpp String dexMetadataPath,
+            @nullable @utf8InCpp String compilationReason);
 
     void rmdex(@utf8InCpp String codePath, @utf8InCpp String instructionSet);
 
-    boolean mergeProfiles(int uid, @utf8InCpp String packageName);
-    boolean dumpProfiles(int uid, @utf8InCpp String packageName, @utf8InCpp String codePaths);
+    boolean mergeProfiles(int uid, @utf8InCpp String packageName, @utf8InCpp String profileName);
+    boolean dumpProfiles(int uid, @utf8InCpp String packageName, @utf8InCpp String  profileName,
+            @utf8InCpp String codePath);
     boolean copySystemProfile(@utf8InCpp String systemProfile, int uid,
-            @utf8InCpp String packageName);
-    void clearAppProfiles(@utf8InCpp String packageName);
+            @utf8InCpp String packageName, @utf8InCpp String profileName);
+    void clearAppProfiles(@utf8InCpp String packageName, @utf8InCpp String profileName);
     void destroyAppProfiles(@utf8InCpp String packageName);
 
     boolean createProfileSnapshot(int appId, @utf8InCpp String packageName,
-            @utf8InCpp String codePath);
-    void destroyProfileSnapshot(@utf8InCpp String packageName, @utf8InCpp String codePath);
+            @utf8InCpp String profileName, @utf8InCpp String classpath);
+    void destroyProfileSnapshot(@utf8InCpp String packageName, @utf8InCpp String profileName);
 
     void idmap(@utf8InCpp String targetApkPath, @utf8InCpp String overlayApkPath, int uid);
     void removeIdmap(@utf8InCpp String overlayApkPath);
@@ -87,6 +91,13 @@
         int uid, in @utf8InCpp String[] isas, @nullable @utf8InCpp String volume_uuid,
         int storage_flag);
 
+    byte[] hashSecondaryDexFile(@utf8InCpp String dexPath, @utf8InCpp String pkgName,
+        int uid, @nullable @utf8InCpp String volumeUuid, int storageFlag);
+
     void invalidateMounts();
     boolean isQuotaSupported(@nullable @utf8InCpp String uuid);
+
+    boolean prepareAppProfile(@utf8InCpp String packageName,
+        int userId, int appId, @utf8InCpp String profileName, @utf8InCpp String codePath,
+        @nullable @utf8InCpp String dexMetadata);
 }
diff --git a/cmds/installd/dexopt.cpp b/cmds/installd/dexopt.cpp
index f281010..9f1cd45 100644
--- a/cmds/installd/dexopt.cpp
+++ b/cmds/installd/dexopt.cpp
@@ -15,6 +15,7 @@
  */
 #define LOG_TAG "installed"
 
+#include <array>
 #include <fcntl.h>
 #include <stdlib.h>
 #include <string.h>
@@ -27,6 +28,7 @@
 #include <sys/wait.h>
 #include <unistd.h>
 
+#include <android-base/file.h>
 #include <android-base/logging.h>
 #include <android-base/properties.h>
 #include <android-base/stringprintf.h>
@@ -36,6 +38,7 @@
 #include <cutils/properties.h>
 #include <cutils/sched_policy.h>
 #include <log/log.h>               // TODO: Move everything to base/logging.
+#include <openssl/sha.h>
 #include <private/android_filesystem_config.h>
 #include <selinux/android.h>
 #include <system/thread_defs.h>
@@ -46,8 +49,10 @@
 #include "otapreopt_utils.h"
 #include "utils.h"
 
-using android::base::StringPrintf;
 using android::base::EndsWith;
+using android::base::ReadFully;
+using android::base::StringPrintf;
+using android::base::WriteFully;
 using android::base::unique_fd;
 
 namespace android {
@@ -132,37 +137,43 @@
 }
 
 // Clear the reference profile for the given location.
-// The location is the package name for primary apks or the dex path for secondary dex files.
-static bool clear_reference_profile(const std::string& location, bool is_secondary_dex) {
-    return clear_profile(create_reference_profile_path(location, is_secondary_dex));
+// The location is the profile name for primary apks or the dex path for secondary dex files.
+static bool clear_reference_profile(const std::string& package_name, const std::string& location,
+        bool is_secondary_dex) {
+    return clear_profile(create_reference_profile_path(package_name, location, is_secondary_dex));
 }
 
 // Clear the reference profile for the given location.
-// The location is the package name for primary apks or the dex path for secondary dex files.
-static bool clear_current_profile(const std::string& pkgname, userid_t user,
-        bool is_secondary_dex) {
-    return clear_profile(create_current_profile_path(user, pkgname, is_secondary_dex));
+// The location is the profile name for primary apks or the dex path for secondary dex files.
+static bool clear_current_profile(const std::string& package_name, const std::string& location,
+        userid_t user, bool is_secondary_dex) {
+    return clear_profile(create_current_profile_path(user, package_name, location,
+            is_secondary_dex));
 }
 
 // Clear the reference profile for the primary apk of the given package.
-bool clear_primary_reference_profile(const std::string& pkgname) {
-    return clear_reference_profile(pkgname, /*is_secondary_dex*/false);
+// The location is the profile name for primary apks or the dex path for secondary dex files.
+bool clear_primary_reference_profile(const std::string& package_name,
+        const std::string& location) {
+    return clear_reference_profile(package_name, location, /*is_secondary_dex*/false);
 }
 
 // Clear all current profile for the primary apk of the given package.
-bool clear_primary_current_profiles(const std::string& pkgname) {
+// The location is the profile name for primary apks or the dex path for secondary dex files.
+bool clear_primary_current_profiles(const std::string& package_name, const std::string& location) {
     bool success = true;
     // For secondary dex files, we don't really need the user but we use it for sanity checks.
     std::vector<userid_t> users = get_known_users(/*volume_uuid*/ nullptr);
     for (auto user : users) {
-        success &= clear_current_profile(pkgname, user, /*is_secondary_dex*/false);
+        success &= clear_current_profile(package_name, location, user, /*is_secondary_dex*/false);
     }
     return success;
 }
 
 // Clear the current profile for the primary apk of the given package and user.
-bool clear_primary_current_profile(const std::string& pkgname, userid_t user) {
-    return clear_current_profile(pkgname, user, /*is_secondary_dex*/false);
+bool clear_primary_current_profile(const std::string& package_name, const std::string& location,
+        userid_t user) {
+    return clear_current_profile(package_name, location, user, /*is_secondary_dex*/false);
 }
 
 static int split_count(const char *str)
@@ -212,7 +223,8 @@
         const char* input_file_name, const char* output_file_name, int swap_fd,
         const char* instruction_set, const char* compiler_filter,
         bool debuggable, bool post_bootcomplete, bool background_job_compile, int profile_fd,
-        const char* class_loader_context, int target_sdk_version, bool disable_hidden_api_checks) {
+        const char* class_loader_context, int target_sdk_version, bool enable_hidden_api_checks,
+        int dex_metadata_fd, const char* compilation_reason) {
     static const unsigned int MAX_INSTRUCTION_SET_LEN = 7;
 
     if (strlen(instruction_set) >= MAX_INSTRUCTION_SET_LEN) {
@@ -409,6 +421,11 @@
         sprintf(base_dir, "--classpath-dir=%s", apk_dir.c_str());
     }
 
+    std::string dex_metadata_fd_arg = "--dm-fd=" + std::to_string(dex_metadata_fd);
+
+    std::string compilation_reason_arg = compilation_reason == nullptr
+            ? ""
+            : std::string("--compilation-reason=") + compilation_reason;
 
     ALOGV("Running %s in=%s out=%s\n", dex2oat_bin, relative_input_file_name, output_file_name);
 
@@ -439,7 +456,9 @@
                      + (disable_cdex ? 1 : 0)
                      + (generate_minidebug_info ? 1 : 0)
                      + (target_sdk_version != 0 ? 2 : 0)
-                     + (disable_hidden_api_checks ? 2 : 0)];
+                     + (enable_hidden_api_checks ? 2 : 0)
+                     + (dex_metadata_fd > -1 ? 1 : 0)
+                     + (compilation_reason != nullptr ? 1 : 0)];
     int i = 0;
     argv[i++] = dex2oat_bin;
     argv[i++] = zip_fd_arg;
@@ -513,11 +532,18 @@
         argv[i++] = RUNTIME_ARG;
         argv[i++] = target_sdk_version_arg;
     }
-    if (disable_hidden_api_checks) {
+    if (enable_hidden_api_checks) {
         argv[i++] = RUNTIME_ARG;
-        argv[i++] = "-Xno-hidden-api-checks";
+        argv[i++] = "-Xhidden-api-checks";
     }
 
+    if (dex_metadata_fd > -1) {
+        argv[i++] = dex_metadata_fd_arg.c_str();
+    }
+
+    if(compilation_reason != nullptr) {
+        argv[i++] = compilation_reason_arg.c_str();
+    }
     // Do not add after dex2oat_flags, they should override others for debugging.
     argv[i] = NULL;
 
@@ -633,29 +659,31 @@
     return fd;
 }
 
-static unique_fd open_current_profile(uid_t uid, userid_t user, const std::string& location,
-        bool is_secondary_dex) {
-    std::string profile = create_current_profile_path(user, location, is_secondary_dex);
+static unique_fd open_current_profile(uid_t uid, userid_t user, const std::string& package_name,
+        const std::string& location, bool is_secondary_dex) {
+    std::string profile = create_current_profile_path(user, package_name, location,
+            is_secondary_dex);
     return open_profile(uid, profile, O_RDONLY);
 }
 
-static unique_fd open_reference_profile(uid_t uid, const std::string& location, bool read_write,
-        bool is_secondary_dex) {
-    std::string profile = create_reference_profile_path(location, is_secondary_dex);
+static unique_fd open_reference_profile(uid_t uid, const std::string& package_name,
+        const std::string& location, bool read_write, bool is_secondary_dex) {
+    std::string profile = create_reference_profile_path(package_name, location, is_secondary_dex);
     return open_profile(uid, profile, read_write ? (O_CREAT | O_RDWR) : O_RDONLY);
 }
 
 static unique_fd open_spnashot_profile(uid_t uid, const std::string& package_name,
-        const std::string& code_path) {
-    std::string profile = create_snapshot_profile_path(package_name, code_path);
+        const std::string& location) {
+    std::string profile = create_snapshot_profile_path(package_name, location);
     return open_profile(uid, profile, O_CREAT | O_RDWR | O_TRUNC);
 }
 
-static void open_profile_files(uid_t uid, const std::string& location, bool is_secondary_dex,
+static void open_profile_files(uid_t uid, const std::string& package_name,
+            const std::string& location, bool is_secondary_dex,
             /*out*/ std::vector<unique_fd>* profiles_fd, /*out*/ unique_fd* reference_profile_fd) {
     // Open the reference profile in read-write mode as profman might need to save the merge.
-    *reference_profile_fd = open_reference_profile(uid, location, /*read_write*/ true,
-            is_secondary_dex);
+    *reference_profile_fd = open_reference_profile(uid, package_name, location,
+            /*read_write*/ true, is_secondary_dex);
 
     // For secondary dex files, we don't really need the user but we use it for sanity checks.
     // Note: the user owning the dex file should be the current user.
@@ -666,7 +694,8 @@
         users = get_known_users(/*volume_uuid*/ nullptr);
     }
     for (auto user : users) {
-        unique_fd profile_fd = open_current_profile(uid, user, location, is_secondary_dex);
+        unique_fd profile_fd = open_current_profile(uid, user, package_name, location,
+                is_secondary_dex);
         // Add to the lists only if both fds are valid.
         if (profile_fd.get() >= 0) {
             profiles_fd->push_back(std::move(profile_fd));
@@ -701,28 +730,45 @@
 static constexpr int PROFMAN_BIN_RETURN_CODE_ERROR_IO = 3;
 static constexpr int PROFMAN_BIN_RETURN_CODE_ERROR_LOCKING = 4;
 
-static void run_profman_merge(const std::vector<unique_fd>& profiles_fd,
-        const unique_fd& reference_profile_fd) {
-    static const size_t MAX_INT_LEN = 32;
+static void run_profman(const std::vector<unique_fd>& profile_fds,
+                        const unique_fd& reference_profile_fd,
+                        const std::vector<unique_fd>* apk_fds,
+                        bool copy_and_update) {
     const char* profman_bin = is_debug_runtime() ? "/system/bin/profmand" : "/system/bin/profman";
 
-    std::vector<std::string> profile_args(profiles_fd.size());
-    char profile_buf[strlen("--profile-file-fd=") + MAX_INT_LEN];
-    for (size_t k = 0; k < profiles_fd.size(); k++) {
-        sprintf(profile_buf, "--profile-file-fd=%d", profiles_fd[k].get());
-        profile_args[k].assign(profile_buf);
+    if (copy_and_update) {
+        CHECK_EQ(1u, profile_fds.size());
+        CHECK(apk_fds != nullptr);
+        CHECK_EQ(1u, apk_fds->size());
     }
-    char reference_profile_arg[strlen("--reference-profile-file-fd=") + MAX_INT_LEN];
-    sprintf(reference_profile_arg, "--reference-profile-file-fd=%d", reference_profile_fd.get());
+    std::vector<std::string> profile_args(profile_fds.size());
+    for (size_t k = 0; k < profile_fds.size(); k++) {
+        profile_args[k] = "--profile-file-fd=" + std::to_string(profile_fds[k].get());
+    }
+    std::string reference_profile_arg = "--reference-profile-file-fd="
+            + std::to_string(reference_profile_fd.get());
+
+    std::vector<std::string> apk_args;
+    if (apk_fds != nullptr) {
+        for (size_t k = 0; k < apk_fds->size(); k++) {
+            apk_args.push_back("--apk-fd=" + std::to_string((*apk_fds)[k].get()));
+        }
+    }
 
     // program name, reference profile fd, the final NULL and the profile fds
-    const char* argv[3 + profiles_fd.size()];
+    const char* argv[3 + profile_args.size() + apk_args.size() + (copy_and_update ? 1 : 0)];
     int i = 0;
     argv[i++] = profman_bin;
-    argv[i++] = reference_profile_arg;
+    argv[i++] = reference_profile_arg.c_str();
     for (size_t k = 0; k < profile_args.size(); k++) {
         argv[i++] = profile_args[k].c_str();
     }
+    for (size_t k = 0; k < apk_args.size(); k++) {
+        argv[i++] = apk_args[k].c_str();
+    }
+    if (copy_and_update) {
+        argv[i++] = "--copy-and-update-profile-key";
+    }
     // Do not add after dex2oat_flags, they should override others for debugging.
     argv[i] = NULL;
 
@@ -731,16 +777,37 @@
     exit(68);   /* only get here on exec failure */
 }
 
+
+static void run_profman_merge(const std::vector<unique_fd>& profiles_fd,
+                              const unique_fd& reference_profile_fd,
+                              const std::vector<unique_fd>* apk_fds = nullptr) {
+    run_profman(profiles_fd, reference_profile_fd, apk_fds, /*copy_and_update*/false);
+}
+
+
+static void run_profman_copy_and_update(unique_fd&& profile_fd,
+                                        unique_fd&& reference_profile_fd,
+                                        unique_fd&& apk_fd) {
+    std::vector<unique_fd> profiles_fd;
+    profiles_fd.push_back(std::move(profile_fd));
+    std::vector<unique_fd> apk_fds;
+    apk_fds.push_back(std::move(apk_fd));
+
+    run_profman(profiles_fd, reference_profile_fd, &apk_fds, /*copy_and_update*/true);
+}
+
 // Decides if profile guided compilation is needed or not based on existing profiles.
 // The location is the package name for primary apks or the dex path for secondary dex files.
 // Returns true if there is enough information in the current profiles that makes it
 // worth to recompile the given location.
 // If the return value is true all the current profiles would have been merged into
 // the reference profiles accessible with open_reference_profile().
-static bool analyze_profiles(uid_t uid, const std::string& location, bool is_secondary_dex) {
+static bool analyze_profiles(uid_t uid, const std::string& package_name,
+        const std::string& location, bool is_secondary_dex) {
     std::vector<unique_fd> profiles_fd;
     unique_fd reference_profile_fd;
-    open_profile_files(uid, location, is_secondary_dex, &profiles_fd, &reference_profile_fd);
+    open_profile_files(uid, package_name, location, is_secondary_dex,
+        &profiles_fd, &reference_profile_fd);
     if (profiles_fd.empty() || (reference_profile_fd.get() < 0)) {
         // Skip profile guided compilation because no profiles were found.
         // Or if the reference profile info couldn't be opened.
@@ -802,13 +869,14 @@
     if (should_clear_current_profiles) {
         if (is_secondary_dex) {
             // For secondary dex files, the owning user is the current user.
-            clear_current_profile(location, multiuser_get_user_id(uid), is_secondary_dex);
+            clear_current_profile(package_name, location, multiuser_get_user_id(uid),
+                    is_secondary_dex);
         } else  {
-            clear_primary_current_profiles(location);
+            clear_primary_current_profiles(package_name, location);
         }
     }
     if (should_clear_reference_profile) {
-        clear_reference_profile(location, is_secondary_dex);
+        clear_reference_profile(package_name, location, is_secondary_dex);
     }
     return need_to_compile;
 }
@@ -819,8 +887,9 @@
 // worth to recompile the package.
 // If the return value is true all the current profiles would have been merged into
 // the reference profiles accessible with open_reference_profile().
-bool analyze_primary_profiles(uid_t uid, const std::string& pkgname) {
-    return analyze_profiles(uid, pkgname, /*is_secondary_dex*/false);
+bool analyze_primary_profiles(uid_t uid, const std::string& package_name,
+        const std::string& profile_name) {
+    return analyze_profiles(uid, package_name, profile_name, /*is_secondary_dex*/false);
 }
 
 static void run_profman_dump(const std::vector<unique_fd>& profile_fds,
@@ -858,12 +927,14 @@
     exit(68);   /* only get here on exec failure */
 }
 
-bool dump_profiles(int32_t uid, const std::string& pkgname, const char* code_paths) {
+bool dump_profiles(int32_t uid, const std::string& pkgname, const std::string& profile_name,
+        const std::string& code_path) {
     std::vector<unique_fd> profile_fds;
     unique_fd reference_profile_fd;
-    std::string out_file_name = StringPrintf("/data/misc/profman/%s.txt", pkgname.c_str());
+    std::string out_file_name = StringPrintf("/data/misc/profman/%s-%s.txt",
+        pkgname.c_str(), profile_name.c_str());
 
-    open_profile_files(uid, pkgname, /*is_secondary_dex*/false,
+    open_profile_files(uid, pkgname, profile_name, /*is_secondary_dex*/false,
             &profile_fds, &reference_profile_fd);
 
     const bool has_reference_profile = (reference_profile_fd.get() != -1);
@@ -877,22 +948,20 @@
     unique_fd output_fd(open(out_file_name.c_str(),
             O_WRONLY | O_CREAT | O_TRUNC | O_NOFOLLOW, 0644));
     if (fchmod(output_fd, S_IRUSR|S_IWUSR|S_IRGRP|S_IROTH) < 0) {
-        ALOGE("installd cannot chmod '%s' dump_profile\n", out_file_name.c_str());
+        LOG(ERROR) << "installd cannot chmod file for dump_profile" << out_file_name;
         return false;
     }
-    std::vector<std::string> code_full_paths = base::Split(code_paths, ";");
+
     std::vector<std::string> dex_locations;
     std::vector<unique_fd> apk_fds;
-    for (const std::string& code_full_path : code_full_paths) {
-        const char* full_path = code_full_path.c_str();
-        unique_fd apk_fd(open(full_path, O_RDONLY | O_NOFOLLOW));
-        if (apk_fd == -1) {
-            ALOGE("installd cannot open '%s'\n", full_path);
-            return false;
-        }
-        dex_locations.push_back(get_location_from_path(full_path));
-        apk_fds.push_back(std::move(apk_fd));
+    unique_fd apk_fd(open(code_path.c_str(), O_RDONLY | O_NOFOLLOW));
+    if (apk_fd == -1) {
+        PLOG(ERROR) << "installd cannot open " << code_path.c_str();
+        return false;
     }
+    dex_locations.push_back(get_location_from_path(code_path.c_str()));
+    apk_fds.push_back(std::move(apk_fd));
+
 
     pid_t pid = fork();
     if (pid == 0) {
@@ -913,10 +982,11 @@
 }
 
 bool copy_system_profile(const std::string& system_profile,
-        uid_t packageUid, const std::string& data_profile_location) {
+        uid_t packageUid, const std::string& package_name, const std::string& profile_name) {
     unique_fd in_fd(open(system_profile.c_str(), O_RDONLY | O_NOFOLLOW | O_CLOEXEC));
     unique_fd out_fd(open_reference_profile(packageUid,
-                     data_profile_location,
+                     package_name,
+                     profile_name,
                      /*read_write*/ true,
                      /*secondary*/ false));
     if (in_fd.get() < 0) {
@@ -924,7 +994,7 @@
         return false;
     }
     if (out_fd.get() < 0) {
-        PLOG(WARNING) << "Could not open profile " << data_profile_location;
+        PLOG(WARNING) << "Could not open profile " << package_name;
         return false;
     }
 
@@ -937,7 +1007,7 @@
 
         if (flock(out_fd.get(), LOCK_EX | LOCK_NB) != 0) {
             if (errno != EWOULDBLOCK) {
-                PLOG(WARNING) << "Error locking profile " << data_profile_location;
+                PLOG(WARNING) << "Error locking profile " << package_name;
             }
             // This implies that the app owning this profile is running
             // (and has acquired the lock).
@@ -945,13 +1015,13 @@
             // The app never acquires the lock for the reference profiles of primary apks.
             // Only dex2oat from installd will do that. Since installd is single threaded
             // we should not see this case. Nevertheless be prepared for it.
-            PLOG(WARNING) << "Failed to flock " << data_profile_location;
+            PLOG(WARNING) << "Failed to flock " << package_name;
             return false;
         }
 
         bool truncated = ftruncate(out_fd.get(), 0) == 0;
         if (!truncated) {
-            PLOG(WARNING) << "Could not truncate " << data_profile_location;
+            PLOG(WARNING) << "Could not truncate " << package_name;
         }
 
         // Copy over data.
@@ -965,7 +1035,7 @@
             write(out_fd.get(), buffer, bytes);
         }
         if (flock(out_fd.get(), LOCK_UN) != 0) {
-            PLOG(WARNING) << "Error unlocking profile " << data_profile_location;
+            PLOG(WARNING) << "Error unlocking profile " << package_name;
         }
         // Use _exit since we don't want to run the global destructors in the child.
         // b/62597429
@@ -1264,19 +1334,42 @@
 // Opens the reference profiles if needed.
 // Note that the reference profile might not exist so it's OK if the fd will be -1.
 Dex2oatFileWrapper maybe_open_reference_profile(const std::string& pkgname,
-        const std::string& dex_path, bool profile_guided, bool is_public, int uid,
-        bool is_secondary_dex) {
-    // Public apps should not be compiled with profile information ever. Same goes for the special
-    // package '*' used for the system server.
-    if (!profile_guided || is_public || (pkgname[0] == '*')) {
+        const std::string& dex_path, const char* profile_name, bool profile_guided,
+        bool is_public, int uid, bool is_secondary_dex) {
+    // If we are not profile guided compilation, or we are compiling system server
+    // do not bother to open the profiles; we won't be using them.
+    if (!profile_guided || (pkgname[0] == '*')) {
+        return Dex2oatFileWrapper();
+    }
+
+    // If this is a secondary dex path which is public do not open the profile.
+    // We cannot compile public secondary dex paths with profiles. That's because
+    // it will expose how the dex files are used by their owner.
+    //
+    // Note that the PackageManager is responsible to set the is_public flag for
+    // primary apks and we do not check it here. In some cases, e.g. when
+    // compiling with a public profile from the .dm file the PackageManager will
+    // set is_public toghether with the profile guided compilation.
+    if (is_secondary_dex && is_public) {
         return Dex2oatFileWrapper();
     }
 
     // Open reference profile in read only mode as dex2oat does not get write permissions.
-    const std::string location = is_secondary_dex ? dex_path : pkgname;
-    unique_fd ufd = open_reference_profile(uid, location, /*read_write*/false, is_secondary_dex);
-    const auto& cleanup = [location, is_secondary_dex]() {
-        clear_reference_profile(location.c_str(), is_secondary_dex);
+    std::string location;
+    if (is_secondary_dex) {
+        location = dex_path;
+    } else {
+        if (profile_name == nullptr) {
+            // This path is taken for system server re-compilation lunched from ZygoteInit.
+            return Dex2oatFileWrapper();
+        } else {
+            location = profile_name;
+        }
+    }
+    unique_fd ufd = open_reference_profile(uid, pkgname, location, /*read_write*/false,
+            is_secondary_dex);
+    const auto& cleanup = [pkgname, location, is_secondary_dex]() {
+        clear_reference_profile(pkgname, location, is_secondary_dex);
     };
     return Dex2oatFileWrapper(ufd.release(), cleanup);
 }
@@ -1732,7 +1825,8 @@
         }
 
         // Analyze profiles.
-        bool profile_was_updated = analyze_profiles(uid, dex_path, /*is_secondary_dex*/true);
+        bool profile_was_updated = analyze_profiles(uid, pkgname, dex_path,
+                /*is_secondary_dex*/true);
 
         // Run dexoptanalyzer to get dexopt_needed code. This is not expected to return.
         exec_dexoptanalyzer(dex_path,
@@ -1779,7 +1873,8 @@
 int dexopt(const char* dex_path, uid_t uid, const char* pkgname, const char* instruction_set,
         int dexopt_needed, const char* oat_dir, int dexopt_flags, const char* compiler_filter,
         const char* volume_uuid, const char* class_loader_context, const char* se_info,
-        bool downgrade, int target_sdk_version) {
+        bool downgrade, int target_sdk_version, const char* profile_name,
+        const char* dex_metadata_path, const char* compilation_reason) {
     CHECK(pkgname != nullptr);
     CHECK(pkgname[0] != 0);
     if ((dexopt_flags & ~DEXOPT_MASK) != 0) {
@@ -1801,7 +1896,7 @@
     bool profile_guided = (dexopt_flags & DEXOPT_PROFILE_GUIDED) != 0;
     bool is_secondary_dex = (dexopt_flags & DEXOPT_SECONDARY_DEX) != 0;
     bool background_job_compile = (dexopt_flags & DEXOPT_IDLE_BACKGROUND_JOB) != 0;
-    bool disable_hidden_api_checks = (dexopt_flags & DEXOPT_DISABLE_HIDDEN_API_CHECKS) != 0;
+    bool enable_hidden_api_checks = (dexopt_flags & DEXOPT_ENABLE_HIDDEN_API_CHECKS) != 0;
 
     // Check if we're dealing with a secondary dex file and if we need to compile it.
     std::string oat_dir_str;
@@ -1868,7 +1963,15 @@
 
     // Open the reference profile if needed.
     Dex2oatFileWrapper reference_profile_fd = maybe_open_reference_profile(
-            pkgname, dex_path, profile_guided, is_public, uid, is_secondary_dex);
+            pkgname, dex_path, profile_name, profile_guided, is_public, uid, is_secondary_dex);
+
+    unique_fd dex_metadata_fd;
+    if (dex_metadata_path != nullptr) {
+        dex_metadata_fd.reset(TEMP_FAILURE_RETRY(open(dex_metadata_path, O_RDONLY | O_NOFOLLOW)));
+        if (dex_metadata_fd.get() < 0) {
+            PLOG(ERROR) << "Failed to open dex metadata file " << dex_metadata_path;
+        }
+    }
 
     ALOGV("DexInv: --- BEGIN '%s' ---\n", dex_path);
 
@@ -1899,7 +2002,9 @@
                     reference_profile_fd.get(),
                     class_loader_context,
                     target_sdk_version,
-                    disable_hidden_api_checks);
+                    enable_hidden_api_checks,
+                    dex_metadata_fd.get(),
+                    compilation_reason);
         _exit(68);   /* only get here on exec failure */
     } else {
         int res = wait_child(pid);
@@ -2027,9 +2132,9 @@
 
             // Delete profiles.
             std::string current_profile = create_current_profile_path(
-                multiuser_get_user_id(uid), dex_path, /*is_secondary*/true);
+                multiuser_get_user_id(uid), pkgname, dex_path, /*is_secondary*/true);
             std::string reference_profile = create_reference_profile_path(
-                dex_path, /*is_secondary*/true);
+                pkgname, dex_path, /*is_secondary*/true);
             result = unlink_if_exists(current_profile) && result;
             result = unlink_if_exists(reference_profile) && result;
 
@@ -2081,6 +2186,90 @@
     }
 }
 
+// Compute and return the hash (SHA-256) of the secondary dex file at dex_path.
+// Returns true if all parameters are valid and the hash successfully computed and stored in
+// out_secondary_dex_hash.
+// Also returns true with an empty hash if the file does not currently exist or is not accessible to
+// the app.
+// For any other errors (e.g. if any of the parameters are invalid) returns false.
+bool hash_secondary_dex_file(const std::string& dex_path, const std::string& pkgname, int uid,
+        const std::unique_ptr<std::string>& volume_uuid, int storage_flag,
+        std::vector<uint8_t>* out_secondary_dex_hash) {
+    out_secondary_dex_hash->clear();
+
+    const char* volume_uuid_cstr = volume_uuid == nullptr ? nullptr : volume_uuid->c_str();
+
+    if (storage_flag != FLAG_STORAGE_CE && storage_flag != FLAG_STORAGE_DE) {
+        LOG(ERROR) << "hash_secondary_dex_file called with invalid storage_flag: "
+                << storage_flag;
+        return false;
+    }
+
+    // Pipe to get the hash result back from our child process.
+    unique_fd pipe_read, pipe_write;
+    if (!Pipe(&pipe_read, &pipe_write)) {
+        PLOG(ERROR) << "Failed to create pipe";
+        return false;
+    }
+
+    // Fork so that actual access to the files is done in the app's own UID, to ensure we only
+    // access data the app itself can access.
+    pid_t pid = fork();
+    if (pid == 0) {
+        // child -- drop privileges before continuing
+        drop_capabilities(uid);
+        pipe_read.reset();
+
+        if (!validate_secondary_dex_path(pkgname, dex_path, volume_uuid_cstr, uid, storage_flag)) {
+            LOG(ERROR) << "Could not validate secondary dex path " << dex_path;
+            _exit(1);
+        }
+
+        unique_fd fd(TEMP_FAILURE_RETRY(open(dex_path.c_str(), O_RDONLY | O_CLOEXEC | O_NOFOLLOW)));
+        if (fd == -1) {
+            if (errno == EACCES || errno == ENOENT) {
+                // Not treated as an error.
+                _exit(0);
+            }
+            PLOG(ERROR) << "Failed to open secondary dex " << dex_path;
+            _exit(1);
+        }
+
+        SHA256_CTX ctx;
+        SHA256_Init(&ctx);
+
+        std::vector<uint8_t> buffer(65536);
+        while (true) {
+            ssize_t bytes_read = TEMP_FAILURE_RETRY(read(fd, buffer.data(), buffer.size()));
+            if (bytes_read == 0) {
+                break;
+            } else if (bytes_read == -1) {
+                PLOG(ERROR) << "Failed to read secondary dex " << dex_path;
+                _exit(1);
+            }
+
+            SHA256_Update(&ctx, buffer.data(), bytes_read);
+        }
+
+        std::array<uint8_t, SHA256_DIGEST_LENGTH> hash;
+        SHA256_Final(hash.data(), &ctx);
+        if (!WriteFully(pipe_write, hash.data(), hash.size())) {
+            _exit(1);
+        }
+
+        _exit(0);
+    }
+
+    // parent
+    pipe_write.reset();
+
+    out_secondary_dex_hash->resize(SHA256_DIGEST_LENGTH);
+    if (!ReadFully(pipe_read, out_secondary_dex_hash->data(), out_secondary_dex_hash->size())) {
+        out_secondary_dex_hash->clear();
+    }
+    return wait_child(pid) == 0;
+}
+
 // Helper for move_ab, so that we can have common failure-case cleanup.
 static bool unlink_and_rename(const char* from, const char* to) {
     // Check whether "from" exists, and if so whether it's regular. If it is, unlink. Otherwise,
@@ -2336,42 +2525,206 @@
     }
 }
 
-bool create_profile_snapshot(int32_t app_id, const std::string& package_name,
-        const std::string& code_path) {
+bool open_classpath_files(const std::string& classpath, std::vector<unique_fd>* apk_fds) {
+    std::vector<std::string> classpaths_elems = base::Split(classpath, ":");
+    for (const std::string& elem : classpaths_elems) {
+        unique_fd fd(TEMP_FAILURE_RETRY(open(elem.c_str(), O_RDONLY)));
+        if (fd < 0) {
+            PLOG(ERROR) << "Could not open classpath elem " << elem;
+            return false;
+        } else {
+            apk_fds->push_back(std::move(fd));
+        }
+    }
+    return true;
+}
+
+static bool create_app_profile_snapshot(int32_t app_id,
+                                        const std::string& package_name,
+                                        const std::string& profile_name,
+                                        const std::string& classpath) {
     int app_shared_gid = multiuser_get_shared_gid(/*user_id*/ 0, app_id);
 
-    unique_fd snapshot_fd = open_spnashot_profile(AID_SYSTEM, package_name, code_path);
+    unique_fd snapshot_fd = open_spnashot_profile(AID_SYSTEM, package_name, profile_name);
     if (snapshot_fd < 0) {
         return false;
     }
 
     std::vector<unique_fd> profiles_fd;
     unique_fd reference_profile_fd;
-    open_profile_files(app_shared_gid, package_name, /*is_secondary_dex*/ false, &profiles_fd,
-            &reference_profile_fd);
+    open_profile_files(app_shared_gid, package_name, profile_name, /*is_secondary_dex*/ false,
+            &profiles_fd, &reference_profile_fd);
     if (profiles_fd.empty() || (reference_profile_fd.get() < 0)) {
         return false;
     }
 
     profiles_fd.push_back(std::move(reference_profile_fd));
 
+    // Open the class paths elements. These will be used to filter out profile data that does
+    // not belong to the classpath during merge.
+    std::vector<unique_fd> apk_fds;
+    if (!open_classpath_files(classpath, &apk_fds)) {
+        return false;
+    }
+
     pid_t pid = fork();
     if (pid == 0) {
         /* child -- drop privileges before continuing */
         drop_capabilities(app_shared_gid);
-        run_profman_merge(profiles_fd, snapshot_fd);
+        run_profman_merge(profiles_fd, snapshot_fd, &apk_fds);
         exit(42);   /* only get here on exec failure */
     }
 
     /* parent */
     int return_code = wait_child(pid);
     if (!WIFEXITED(return_code)) {
-        LOG(WARNING) << "profman failed for " << package_name << ":" << code_path;
+        LOG(WARNING) << "profman failed for " << package_name << ":" << profile_name;
         return false;
     }
 
     return true;
 }
 
+static bool create_boot_image_profile_snapshot(const std::string& package_name,
+                                               const std::string& profile_name,
+                                               const std::string& classpath) {
+    // The reference profile directory for the android package might not be prepared. Do it now.
+    const std::string ref_profile_dir =
+            create_primary_reference_profile_package_dir_path(package_name);
+    if (fs_prepare_dir(ref_profile_dir.c_str(), 0770, AID_SYSTEM, AID_SYSTEM) != 0) {
+        PLOG(ERROR) << "Failed to prepare " << ref_profile_dir;
+        return false;
+    }
+
+    // Open and create the snapshot profile.
+    unique_fd snapshot_fd = open_spnashot_profile(AID_SYSTEM, package_name, profile_name);
+
+    // Collect all non empty profiles.
+    // The collection will traverse all applications profiles and find the non empty files.
+    // This has the potential of inspecting a large number of files and directories (depending
+    // on the number of applications and users). So there is a slight increase in the chance
+    // to get get occasionally I/O errors (e.g. for opening the file). When that happens do not
+    // fail the snapshot and aggregate whatever profile we could open.
+    //
+    // The profile snapshot is a best effort based on available data it's ok if some data
+    // from some apps is missing. It will be counter productive for the snapshot to fail
+    // because we could not open or read some of the files.
+    std::vector<std::string> profiles;
+    if (!collect_profiles(&profiles)) {
+        LOG(WARNING) << "There were errors while collecting the profiles for the boot image.";
+    }
+
+    // If we have no profiles return early.
+    if (profiles.empty()) {
+        return true;
+    }
+
+    // Open the classpath elements. These will be used to filter out profile data that does
+    // not belong to the classpath during merge.
+    std::vector<unique_fd> apk_fds;
+    if (!open_classpath_files(classpath, &apk_fds)) {
+        return false;
+    }
+
+    // If we could not open any files from the classpath return an error.
+    if (apk_fds.empty()) {
+        LOG(ERROR) << "Could not open any of the classpath elements.";
+        return false;
+    }
+
+    // Aggregate the profiles in batches of kAggregationBatchSize.
+    // We do this to avoid opening a huge a amount of files.
+    static constexpr size_t kAggregationBatchSize = 10;
+
+    std::vector<unique_fd> profiles_fd;
+    for (size_t i = 0; i < profiles.size(); )  {
+        for (size_t k = 0; k < kAggregationBatchSize && i < profiles.size(); k++, i++) {
+            unique_fd fd = open_profile(AID_SYSTEM, profiles[i], O_RDONLY);
+            if (fd.get() >= 0) {
+                profiles_fd.push_back(std::move(fd));
+            }
+        }
+        pid_t pid = fork();
+        if (pid == 0) {
+            /* child -- drop privileges before continuing */
+            drop_capabilities(AID_SYSTEM);
+
+            run_profman_merge(profiles_fd, snapshot_fd, &apk_fds);
+            exit(42);   /* only get here on exec failure */
+        }
+
+        /* parent */
+        int return_code = wait_child(pid);
+        if (!WIFEXITED(return_code)) {
+            PLOG(WARNING) << "profman failed for " << package_name << ":" << profile_name;
+            return false;
+        }
+        return true;
+    }
+    return true;
+}
+
+bool create_profile_snapshot(int32_t app_id, const std::string& package_name,
+        const std::string& profile_name, const std::string& classpath) {
+    if (app_id == -1) {
+        return create_boot_image_profile_snapshot(package_name, profile_name, classpath);
+    } else {
+        return create_app_profile_snapshot(app_id, package_name, profile_name, classpath);
+    }
+}
+
+bool prepare_app_profile(const std::string& package_name,
+                         userid_t user_id,
+                         appid_t app_id,
+                         const std::string& profile_name,
+                         const std::string& code_path,
+                         const std::unique_ptr<std::string>& dex_metadata) {
+    // Prepare the current profile.
+    std::string cur_profile  = create_current_profile_path(user_id, package_name, profile_name,
+            /*is_secondary_dex*/ false);
+    uid_t uid = multiuser_get_uid(user_id, app_id);
+    if (fs_prepare_file_strict(cur_profile.c_str(), 0600, uid, uid) != 0) {
+        PLOG(ERROR) << "Failed to prepare " << cur_profile;
+        return false;
+    }
+
+    // Check if we need to install the profile from the dex metadata.
+    if (dex_metadata == nullptr) {
+        return true;
+    }
+
+    // We have a dex metdata. Merge the profile into the reference profile.
+    unique_fd ref_profile_fd = open_reference_profile(uid, package_name, profile_name,
+            /*read_write*/ true, /*is_secondary_dex*/ false);
+    unique_fd dex_metadata_fd(TEMP_FAILURE_RETRY(
+            open(dex_metadata->c_str(), O_RDONLY | O_NOFOLLOW)));
+    unique_fd apk_fd(TEMP_FAILURE_RETRY(open(code_path.c_str(), O_RDONLY | O_NOFOLLOW)));
+    if (apk_fd < 0) {
+        PLOG(ERROR) << "Could not open code path " << code_path;
+        return false;
+    }
+
+    pid_t pid = fork();
+    if (pid == 0) {
+        /* child -- drop privileges before continuing */
+        gid_t app_shared_gid = multiuser_get_shared_gid(user_id, app_id);
+        drop_capabilities(app_shared_gid);
+
+        // The copy and update takes ownership over the fds.
+        run_profman_copy_and_update(std::move(dex_metadata_fd),
+                                    std::move(ref_profile_fd),
+                                    std::move(apk_fd));
+        exit(42);   /* only get here on exec failure */
+    }
+
+    /* parent */
+    int return_code = wait_child(pid);
+    if (!WIFEXITED(return_code)) {
+        PLOG(WARNING) << "profman failed for " << package_name << ":" << profile_name;
+        return false;
+    }
+    return true;
+}
+
 }  // namespace installd
 }  // namespace android
diff --git a/cmds/installd/dexopt.h b/cmds/installd/dexopt.h
index 4923a43..62f9467 100644
--- a/cmds/installd/dexopt.h
+++ b/cmds/installd/dexopt.h
@@ -33,24 +33,27 @@
 static constexpr int DEX2OAT_FOR_FILTER          = 3;
 static constexpr int DEX2OAT_FOR_RELOCATION      = 4;
 
-// Clear the reference profile for the primary apk of the given package.
-bool clear_primary_reference_profile(const std::string& pkgname);
-// Clear the current profile for the primary apk of the given package and user.
-bool clear_primary_current_profile(const std::string& pkgname, userid_t user);
-// Clear all current profile for the primary apk of the given package.
-bool clear_primary_current_profiles(const std::string& pkgname);
-
-bool move_ab(const char* apk_path, const char* instruction_set, const char* output_path);
+// Clear the reference profile identified by the given profile name.
+bool clear_primary_reference_profile(const std::string& pkgname, const std::string& profile_name);
+// Clear the current profile identified by the given profile name (for single user).
+bool clear_primary_current_profile(const std::string& pkgname, const std::string& profile_name,
+         userid_t user);
+// Clear all current profiles identified by the given profile name (all users).
+bool clear_primary_current_profiles(const std::string& pkgname, const std::string& profile_name);
 
 // Decide if profile guided compilation is needed or not based on existing profiles.
-// The analysis is done for the primary apks (base + splits) of the given package.
+// The analysis is done for a single profile name (which corresponds to a single code path).
 // Returns true if there is enough information in the current profiles that makes it
 // worth to recompile the package.
 // If the return value is true all the current profiles would have been merged into
 // the reference profiles accessible with open_reference_profile().
-bool analyze_primary_profiles(uid_t uid, const std::string& pkgname);
+bool analyze_primary_profiles(uid_t uid,
+                              const std::string& pkgname,
+                              const std::string& profile_name);
 
-// Create a snapshot of the profile information for the given package and code path.
+// Create a snapshot of the profile information for the given package profile.
+// If appId is -1, the method creates the profile snapshot for the boot image.
+//
 // The profile snapshot is the aggregation of all existing profiles (all current user
 // profiles & the reference profile) and is meant to capture the all the profile information
 // without performing a merge into the reference profile which might impact future dex2oat
@@ -59,14 +62,34 @@
 // ownership is assigned to AID_SYSTEM.
 // The snapshot location is reference_profile_location.snapshot. If a snapshot is already
 // there, it will be truncated and overwritten.
-bool create_profile_snapshot(int32_t app_id, const std::string& package,
-        const std::string& code_path);
+//
+// The classpath acts as filter: only profiling data belonging to elements of the classpath
+// will end up in the snapshot.
+bool create_profile_snapshot(int32_t app_id,
+                             const std::string& package,
+                             const std::string& profile_name,
+                             const std::string& classpath);
 
-bool dump_profiles(int32_t uid, const std::string& pkgname, const char* code_paths);
+bool dump_profiles(int32_t uid,
+                   const std::string& pkgname,
+                   const std::string& profile_name,
+                   const std::string& code_path);
 
 bool copy_system_profile(const std::string& system_profile,
                          uid_t packageUid,
-                         const std::string& data_profile_location);
+                         const std::string& pkgname,
+                         const std::string& profile_name);
+
+// Prepare the app profile for the given code path:
+//  - create the current profile using profile_name
+//  - merge the profile from the dex metadata file (if present) into
+//    the reference profile.
+bool prepare_app_profile(const std::string& package_name,
+                         userid_t user_id,
+                         appid_t app_id,
+                         const std::string& profile_name,
+                         const std::string& code_path,
+                         const std::unique_ptr<std::string>& dex_metadata);
 
 bool delete_odex(const char* apk_path, const char* instruction_set, const char* output_path);
 
@@ -75,10 +98,15 @@
         const std::unique_ptr<std::string>& volumeUuid, int storage_flag,
         /*out*/bool* out_secondary_dex_exists);
 
+bool hash_secondary_dex_file(const std::string& dex_path,
+        const std::string& pkgname, int uid, const std::unique_ptr<std::string>& volume_uuid,
+        int storage_flag, std::vector<uint8_t>* out_secondary_dex_hash);
+
 int dexopt(const char *apk_path, uid_t uid, const char *pkgName, const char *instruction_set,
         int dexopt_needed, const char* oat_dir, int dexopt_flags, const char* compiler_filter,
         const char* volume_uuid, const char* class_loader_context, const char* se_info,
-        bool downgrade, int target_sdk_version);
+        bool downgrade, int target_sdk_version, const char* profile_name,
+        const char* dexMetadataPath, const char* compilation_reason);
 
 bool calculate_oat_file_path_default(char path[PKG_PATH_MAX], const char *oat_dir,
         const char *apk_path, const char *instruction_set);
@@ -89,6 +117,8 @@
 bool create_cache_path_default(char path[PKG_PATH_MAX], const char *src,
         const char *instruction_set);
 
+bool move_ab(const char* apk_path, const char* instruction_set, const char* output_path);
+
 }  // namespace installd
 }  // namespace android
 
diff --git a/cmds/installd/installd_constants.h b/cmds/installd/installd_constants.h
index 9b6714d..06c83e4 100644
--- a/cmds/installd/installd_constants.h
+++ b/cmds/installd/installd_constants.h
@@ -52,7 +52,7 @@
 // Tells the compiler that it is invoked from the background service.  This
 // controls whether extra debugging flags can be used (taking more compile time.)
 constexpr int DEXOPT_IDLE_BACKGROUND_JOB = 1 << 9;
-constexpr int DEXOPT_DISABLE_HIDDEN_API_CHECKS = 1 << 10;
+constexpr int DEXOPT_ENABLE_HIDDEN_API_CHECKS = 1 << 10;
 
 /* all known values for dexopt flags */
 constexpr int DEXOPT_MASK =
@@ -64,7 +64,7 @@
     | DEXOPT_FORCE
     | DEXOPT_STORAGE_CE
     | DEXOPT_STORAGE_DE
-    | DEXOPT_DISABLE_HIDDEN_API_CHECKS;
+    | DEXOPT_ENABLE_HIDDEN_API_CHECKS;
 
 // NOTE: keep in sync with StorageManager
 constexpr int FLAG_STORAGE_DE = 1 << 0;
diff --git a/cmds/installd/otapreopt.cpp b/cmds/installd/otapreopt.cpp
index 1c78ff0..d8d5796 100644
--- a/cmds/installd/otapreopt.cpp
+++ b/cmds/installd/otapreopt.cpp
@@ -43,6 +43,7 @@
 #include "globals.h"
 #include "installd_constants.h"
 #include "installd_deps.h"  // Need to fill in requirements of commands.
+#include "otapreopt_parameters.h"
 #include "otapreopt_utils.h"
 #include "system_properties.h"
 #include "utils.h"
@@ -78,8 +79,8 @@
 static_assert(DEXOPT_FORCE          == 1 << 6, "DEXOPT_FORCE unexpected.");
 static_assert(DEXOPT_STORAGE_CE     == 1 << 7, "DEXOPT_STORAGE_CE unexpected.");
 static_assert(DEXOPT_STORAGE_DE     == 1 << 8, "DEXOPT_STORAGE_DE unexpected.");
-static_assert(DEXOPT_DISABLE_HIDDEN_API_CHECKS == 1 << 10,
-        "DEXOPT_DISABLE_HIDDEN_API_CHECKS unexpected");
+static_assert(DEXOPT_ENABLE_HIDDEN_API_CHECKS == 1 << 10,
+        "DEXOPT_ENABLE_HIDDEN_API_CHECKS unexpected");
 
 static_assert(DEXOPT_MASK           == 0x5fe, "DEXOPT_MASK unexpected.");
 
@@ -158,31 +159,15 @@
     }
 
     std::string GetOTADataDirectory() const {
-        return StringPrintf("%s/%s", GetOtaDirectoryPrefix().c_str(), target_slot_.c_str());
+        return StringPrintf("%s/%s", GetOtaDirectoryPrefix().c_str(), GetTargetSlot().c_str());
     }
 
     const std::string& GetTargetSlot() const {
-        return target_slot_;
+        return parameters_.target_slot;
     }
 
 private:
 
-    struct Parameters {
-        const char *apk_path;
-        uid_t uid;
-        const char *pkgName;
-        const char *instruction_set;
-        int dexopt_needed;
-        const char* oat_dir;
-        int dexopt_flags;
-        const char* compiler_filter;
-        const char* volume_uuid;
-        const char* shared_libraries;
-        const char* se_info;
-        bool downgrade;
-        int target_sdk_version;
-    };
-
     bool ReadSystemProperties() {
         static constexpr const char* kPropertyFiles[] = {
                 "/default.prop", "/system/build.prop"
@@ -306,439 +291,7 @@
     }
 
     bool ReadArguments(int argc, char** argv) {
-        // Expected command line:
-        //   target-slot [version] dexopt {DEXOPT_PARAMETERS}
-
-        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;
-        if (!ValidateTargetSlotSuffix(target_slot_)) {
-            LOG(ERROR) << "Target slot suffix not legal: " << target_slot_;
-            return false;
-        }
-
-        // Check for version or "dexopt" next.
-        if (argv[2] == nullptr) {
-            LOG(ERROR) << "Missing parameters";
-            return false;
-        }
-
-        if (std::string("dexopt").compare(argv[2]) == 0) {
-            // This is version 1 (N) or pre-versioning version 2.
-            constexpr int kV2ArgCount =   1   // "otapreopt"
-                                        + 1   // slot
-                                        + 1   // "dexopt"
-                                        + 1   // apk_path
-                                        + 1   // uid
-                                        + 1   // pkg
-                                        + 1   // isa
-                                        + 1   // dexopt_needed
-                                        + 1   // oat_dir
-                                        + 1   // dexopt_flags
-                                        + 1   // filter
-                                        + 1   // volume
-                                        + 1   // libs
-                                        + 1;  // seinfo
-            if (argc == kV2ArgCount) {
-                return ReadArgumentsV2(argc, argv, false);
-            } else {
-                return ReadArgumentsV1(argc, argv);
-            }
-        }
-
-        uint32_t version;
-        if (!ParseUInt(argv[2], &version)) {
-            LOG(ERROR) << "Could not parse version: " << argv[2];
-            return false;
-        }
-
-        switch (version) {
-            case 2:
-                return ReadArgumentsV2(argc, argv, true);
-            case 3:
-                return ReadArgumentsV3(argc, argv);
-            case 4:
-                return ReadArgumentsV4(argc, argv);
-
-            default:
-                LOG(ERROR) << "Unsupported version " << version;
-                return false;
-        }
-    }
-
-    bool ReadArgumentsV2(int argc ATTRIBUTE_UNUSED, char** argv, bool versioned) {
-        size_t dexopt_index = versioned ? 3 : 2;
-
-        // Check for "dexopt".
-        if (argv[dexopt_index] == nullptr) {
-            LOG(ERROR) << "Missing parameters";
-            return false;
-        }
-        if (std::string("dexopt").compare(argv[dexopt_index]) != 0) {
-            LOG(ERROR) << "Expected \"dexopt\"";
-            return false;
-        }
-
-        size_t param_index = 0;
-        for (;; ++param_index) {
-            const char* param = argv[dexopt_index + 1 + param_index];
-            if (param == nullptr) {
-                break;
-            }
-
-            switch (param_index) {
-                case 0:
-                    package_parameters_.apk_path = param;
-                    break;
-
-                case 1:
-                    package_parameters_.uid = atoi(param);
-                    break;
-
-                case 2:
-                    package_parameters_.pkgName = param;
-                    break;
-
-                case 3:
-                    package_parameters_.instruction_set = param;
-                    break;
-
-                case 4:
-                    package_parameters_.dexopt_needed = atoi(param);
-                    break;
-
-                case 5:
-                    package_parameters_.oat_dir = param;
-                    break;
-
-                case 6:
-                    package_parameters_.dexopt_flags = atoi(param);
-                    break;
-
-                case 7:
-                    package_parameters_.compiler_filter = param;
-                    break;
-
-                case 8:
-                    package_parameters_.volume_uuid = ParseNull(param);
-                    break;
-
-                case 9:
-                    package_parameters_.shared_libraries = ParseNull(param);
-                    break;
-
-                case 10:
-                    package_parameters_.se_info = ParseNull(param);
-                    break;
-
-                default:
-                    LOG(ERROR) << "Too many arguments, got " << param;
-                    return false;
-            }
-        }
-
-        // Set downgrade to false. It is only relevant when downgrading compiler
-        // filter, which is not the case during ota.
-        package_parameters_.downgrade = false;
-
-        // Set target_sdk_version to 0, ie the platform SDK version. This is
-        // conservative and may force some classes to verify at runtime.
-        package_parameters_.target_sdk_version = 0;
-
-        if (param_index != 11) {
-            LOG(ERROR) << "Not enough parameters";
-            return false;
-        }
-
-        return true;
-    }
-
-    bool ReadArgumentsV3(int argc ATTRIBUTE_UNUSED, char** argv) {
-        size_t dexopt_index = 3;
-
-        // Check for "dexopt".
-        if (argv[dexopt_index] == nullptr) {
-            LOG(ERROR) << "Missing parameters";
-            return false;
-        }
-        if (std::string("dexopt").compare(argv[dexopt_index]) != 0) {
-            LOG(ERROR) << "Expected \"dexopt\"";
-            return false;
-        }
-
-        size_t param_index = 0;
-        for (;; ++param_index) {
-            const char* param = argv[dexopt_index + 1 + param_index];
-            if (param == nullptr) {
-                break;
-            }
-
-            switch (param_index) {
-                case 0:
-                    package_parameters_.apk_path = param;
-                    break;
-
-                case 1:
-                    package_parameters_.uid = atoi(param);
-                    break;
-
-                case 2:
-                    package_parameters_.pkgName = param;
-                    break;
-
-                case 3:
-                    package_parameters_.instruction_set = param;
-                    break;
-
-                case 4:
-                    package_parameters_.dexopt_needed = atoi(param);
-                    break;
-
-                case 5:
-                    package_parameters_.oat_dir = param;
-                    break;
-
-                case 6:
-                    package_parameters_.dexopt_flags = atoi(param);
-                    break;
-
-                case 7:
-                    package_parameters_.compiler_filter = param;
-                    break;
-
-                case 8:
-                    package_parameters_.volume_uuid = ParseNull(param);
-                    break;
-
-                case 9:
-                    package_parameters_.shared_libraries = ParseNull(param);
-                    break;
-
-                case 10:
-                    package_parameters_.se_info = ParseNull(param);
-                    break;
-
-                case 11:
-                    package_parameters_.downgrade = ParseBool(param);
-                    break;
-
-                default:
-                    LOG(ERROR) << "Too many arguments, got " << param;
-                    return false;
-            }
-        }
-
-        // Set target_sdk_version to 0, ie the platform SDK version. This is
-        // conservative and may force some classes to verify at runtime.
-        package_parameters_.target_sdk_version = 0;
-
-        if (param_index != 12) {
-            LOG(ERROR) << "Not enough parameters";
-            return false;
-        }
-
-        return true;
-    }
-
-    bool ReadArgumentsV4(int argc ATTRIBUTE_UNUSED, char** argv) {
-        size_t dexopt_index = 3;
-
-        // Check for "dexopt".
-        if (argv[dexopt_index] == nullptr) {
-            LOG(ERROR) << "Missing parameters";
-            return false;
-        }
-        if (std::string("dexopt").compare(argv[dexopt_index]) != 0) {
-            LOG(ERROR) << "Expected \"dexopt\"";
-            return false;
-        }
-
-        size_t param_index = 0;
-        for (;; ++param_index) {
-            const char* param = argv[dexopt_index + 1 + param_index];
-            if (param == nullptr) {
-                break;
-            }
-
-            switch (param_index) {
-                case 0:
-                    package_parameters_.apk_path = param;
-                    break;
-
-                case 1:
-                    package_parameters_.uid = atoi(param);
-                    break;
-
-                case 2:
-                    package_parameters_.pkgName = param;
-                    break;
-
-                case 3:
-                    package_parameters_.instruction_set = param;
-                    break;
-
-                case 4:
-                    package_parameters_.dexopt_needed = atoi(param);
-                    break;
-
-                case 5:
-                    package_parameters_.oat_dir = param;
-                    break;
-
-                case 6:
-                    package_parameters_.dexopt_flags = atoi(param);
-                    break;
-
-                case 7:
-                    package_parameters_.compiler_filter = param;
-                    break;
-
-                case 8:
-                    package_parameters_.volume_uuid = ParseNull(param);
-                    break;
-
-                case 9:
-                    package_parameters_.shared_libraries = ParseNull(param);
-                    break;
-
-                case 10:
-                    package_parameters_.se_info = ParseNull(param);
-                    break;
-
-                case 11:
-                    package_parameters_.downgrade = ParseBool(param);
-                    break;
-
-                case 12:
-                    package_parameters_.target_sdk_version = atoi(param);
-                    break;
-
-                default:
-                    LOG(ERROR) << "Too many arguments, got " << param;
-                    return false;
-            }
-        }
-
-        if (param_index != 13) {
-            LOG(ERROR) << "Not enough parameters";
-            return false;
-        }
-
-        return true;
-    }
-
-    static int ReplaceMask(int input, int old_mask, int new_mask) {
-        return (input & old_mask) != 0 ? new_mask : 0;
-    }
-
-    bool ReadArgumentsV1(int argc ATTRIBUTE_UNUSED, char** argv) {
-        // Check for "dexopt".
-        if (argv[2] == nullptr) {
-            LOG(ERROR) << "Missing parameters";
-            return false;
-        }
-        if (std::string("dexopt").compare(argv[2]) != 0) {
-            LOG(ERROR) << "Expected \"dexopt\"";
-            return false;
-        }
-
-        size_t param_index = 0;
-        for (;; ++param_index) {
-            const char* param = argv[3 + param_index];
-            if (param == nullptr) {
-                break;
-            }
-
-            switch (param_index) {
-                case 0:
-                    package_parameters_.apk_path = param;
-                    break;
-
-                case 1:
-                    package_parameters_.uid = atoi(param);
-                    break;
-
-                case 2:
-                    package_parameters_.pkgName = param;
-                    break;
-
-                case 3:
-                    package_parameters_.instruction_set = param;
-                    break;
-
-                case 4: {
-                    // Version 1 had:
-                    //   DEXOPT_DEX2OAT_NEEDED       = 1
-                    //   DEXOPT_PATCHOAT_NEEDED      = 2
-                    //   DEXOPT_SELF_PATCHOAT_NEEDED = 3
-                    // We will simply use DEX2OAT_FROM_SCRATCH.
-                    package_parameters_.dexopt_needed = DEX2OAT_FROM_SCRATCH;
-                    break;
-                }
-
-                case 5:
-                    package_parameters_.oat_dir = param;
-                    break;
-
-                case 6: {
-                    // Version 1 had:
-                    constexpr int OLD_DEXOPT_PUBLIC         = 1 << 1;
-                    // Note: DEXOPT_SAFEMODE has been removed.
-                    // constexpr int OLD_DEXOPT_SAFEMODE       = 1 << 2;
-                    constexpr int OLD_DEXOPT_DEBUGGABLE     = 1 << 3;
-                    constexpr int OLD_DEXOPT_BOOTCOMPLETE   = 1 << 4;
-                    constexpr int OLD_DEXOPT_PROFILE_GUIDED = 1 << 5;
-                    constexpr int OLD_DEXOPT_OTA            = 1 << 6;
-                    int input = atoi(param);
-                    package_parameters_.dexopt_flags =
-                            ReplaceMask(input, OLD_DEXOPT_PUBLIC, DEXOPT_PUBLIC) |
-                            ReplaceMask(input, OLD_DEXOPT_DEBUGGABLE, DEXOPT_DEBUGGABLE) |
-                            ReplaceMask(input, OLD_DEXOPT_BOOTCOMPLETE, DEXOPT_BOOTCOMPLETE) |
-                            ReplaceMask(input, OLD_DEXOPT_PROFILE_GUIDED, DEXOPT_PROFILE_GUIDED) |
-                            ReplaceMask(input, OLD_DEXOPT_OTA, 0);
-                    break;
-                }
-
-                case 7:
-                    package_parameters_.compiler_filter = param;
-                    break;
-
-                case 8:
-                    package_parameters_.volume_uuid = ParseNull(param);
-                    break;
-
-                case 9:
-                    package_parameters_.shared_libraries = ParseNull(param);
-                    break;
-
-                default:
-                    LOG(ERROR) << "Too many arguments, got " << param;
-                    return false;
-            }
-        }
-
-        if (param_index != 10) {
-            LOG(ERROR) << "Not enough parameters";
-            return false;
-        }
-
-        // Set se_info to null. It is only relevant for secondary dex files, which we won't
-        // receive from a v1 A side.
-        package_parameters_.se_info = nullptr;
-
-        // Set downgrade to false. It is only relevant when downgrading compiler
-        // filter, which is not the case during ota.
-        package_parameters_.downgrade = false;
-
-        // Set target_sdk_version to 0, ie the platform SDK version. This is
-        // conservative and may force some classes to verify at runtime.
-        package_parameters_.target_sdk_version = 0;
-
-        return true;
+        return parameters_.ReadArguments(argc, const_cast<const char**>(argv));
     }
 
     void PrepareEnvironment() {
@@ -754,11 +307,11 @@
     // Ensure that we have the right boot image. The first time any app is
     // compiled, we'll try to generate it.
     bool PrepareBootImage(bool force) const {
-        if (package_parameters_.instruction_set == nullptr) {
+        if (parameters_.instruction_set == nullptr) {
             LOG(ERROR) << "Instruction set missing.";
             return false;
         }
-        const char* isa = package_parameters_.instruction_set;
+        const char* isa = parameters_.instruction_set;
 
         // Check whether the file exists where expected.
         std::string dalvik_cache = GetOTADataDirectory() + "/" + DALVIK_CACHE;
@@ -799,7 +352,7 @@
 
         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);
+          return PatchoatBootImage(isa_path, isa);
         } else {
           // No preopted boot image. Try to compile.
           return Dex2oatBootImage(boot_classpath_, art_path, oat_path, isa);
@@ -868,14 +421,14 @@
         CHECK_EQ(0, closedir(c_dir)) << "Unable to close directory.";
     }
 
-    bool PatchoatBootImage(const std::string& art_path, const char* isa) const {
+    bool PatchoatBootImage(const std::string& output_dir, 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/patchoat");
 
         cmd.push_back("--input-image-location=/system/framework/boot.art");
-        cmd.push_back(StringPrintf("--output-image-file=%s", art_path.c_str()));
+        cmd.push_back(StringPrintf("--output-image-directory=%s", output_dir.c_str()));
 
         cmd.push_back(StringPrintf("--instruction-set=%s", isa));
 
@@ -982,9 +535,9 @@
         //       jar content must be exactly the same).
 
         //       (This is ugly as it's the only thing where we need to understand the contents
-        //        of package_parameters_, but it beats postponing the decision or using the call-
+        //        of parameters_, but it beats postponing the decision or using the call-
         //        backs to do weird things.)
-        const char* apk_path = package_parameters_.apk_path;
+        const char* apk_path = parameters_.apk_path;
         CHECK(apk_path != nullptr);
         if (StartsWith(apk_path, android_root_)) {
             const char* last_slash = strrchr(apk_path, '/');
@@ -1011,21 +564,25 @@
         return false;
     }
 
-    // Run dexopt with the parameters of package_parameters_.
+    // Run dexopt with the parameters of parameters_.
+    // TODO(calin): embed the profile name in the parameters.
     int Dexopt() {
-        return dexopt(package_parameters_.apk_path,
-                      package_parameters_.uid,
-                      package_parameters_.pkgName,
-                      package_parameters_.instruction_set,
-                      package_parameters_.dexopt_needed,
-                      package_parameters_.oat_dir,
-                      package_parameters_.dexopt_flags,
-                      package_parameters_.compiler_filter,
-                      package_parameters_.volume_uuid,
-                      package_parameters_.shared_libraries,
-                      package_parameters_.se_info,
-                      package_parameters_.downgrade,
-                      package_parameters_.target_sdk_version);
+        return dexopt(parameters_.apk_path,
+                      parameters_.uid,
+                      parameters_.pkgName,
+                      parameters_.instruction_set,
+                      parameters_.dexopt_needed,
+                      parameters_.oat_dir,
+                      parameters_.dexopt_flags,
+                      parameters_.compiler_filter,
+                      parameters_.volume_uuid,
+                      parameters_.shared_libraries,
+                      parameters_.se_info,
+                      parameters_.downgrade,
+                      parameters_.target_sdk_version,
+                      parameters_.profile_name,
+                      parameters_.dex_metadata_path,
+                      parameters_.compilation_reason);
     }
 
     int RunPreopt() {
@@ -1056,12 +613,12 @@
 
         // If this was a profile-guided run, we may have profile version issues. Try to downgrade,
         // if possible.
-        if ((package_parameters_.dexopt_flags & DEXOPT_PROFILE_GUIDED) == 0) {
+        if ((parameters_.dexopt_flags & DEXOPT_PROFILE_GUIDED) == 0) {
             return dexopt_result;
         }
 
         LOG(WARNING) << "Downgrading compiler filter in an attempt to progress compilation";
-        package_parameters_.dexopt_flags &= ~DEXOPT_PROFILE_GUIDED;
+        parameters_.dexopt_flags &= ~DEXOPT_PROFILE_GUIDED;
         return Dexopt();
     }
 
@@ -1186,13 +743,12 @@
     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_;
 
-    Parameters package_parameters_;
+    OTAPreoptParameters parameters_;
 
     // Store environment values we need to set.
     std::vector<std::string> environ_;
diff --git a/cmds/installd/otapreopt_parameters.cpp b/cmds/installd/otapreopt_parameters.cpp
new file mode 100644
index 0000000..802d5d7
--- /dev/null
+++ b/cmds/installd/otapreopt_parameters.cpp
@@ -0,0 +1,355 @@
+/*
+ ** Copyright 2016, The Android Open Source Project
+ **
+ ** Licensed under the Apache License, Version 2.0 (the "License");
+ ** you may not use this file except in compliance with the License.
+ ** You may obtain a copy of the License at
+ **
+ **     http://www.apache.org/licenses/LICENSE-2.0
+ **
+ ** Unless required by applicable law or agreed to in writing, software
+ ** distributed under the License is distributed on an "AS IS" BASIS,
+ ** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ ** See the License for the specific language governing permissions and
+ ** limitations under the License.
+ */
+
+#include "otapreopt_parameters.h"
+
+#include <android-base/logging.h>
+
+#include "dexopt.h"
+#include "installd_constants.h"
+#include "otapreopt_utils.h"
+
+#ifndef LOG_TAG
+#define LOG_TAG "otapreopt"
+#endif
+
+namespace android {
+namespace installd {
+
+static bool ParseBool(const char* in) {
+    if (strcmp(in, "true") == 0) {
+        return true;
+    }
+    return false;
+}
+
+static const char* ParseNull(const char* arg) {
+    return (strcmp(arg, "!") == 0) ? nullptr : arg;
+}
+
+static bool ParseUInt(const char* in, uint32_t* out) {
+    char* end;
+    long long int result = strtoll(in, &end, 0);
+    if (in == end || *end != '\0') {
+        return false;
+    }
+    if (result < std::numeric_limits<uint32_t>::min() ||
+            std::numeric_limits<uint32_t>::max() < result) {
+        return false;
+    }
+    *out = static_cast<uint32_t>(result);
+    return true;
+}
+
+bool OTAPreoptParameters::ReadArguments(int argc, const char** argv) {
+    // Expected command line:
+    //   target-slot [version] dexopt {DEXOPT_PARAMETERS}
+
+    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;
+    if (!ValidateTargetSlotSuffix(target_slot)) {
+        LOG(ERROR) << "Target slot suffix not legal: " << target_slot;
+        return false;
+    }
+
+    // Check for version or "dexopt" next.
+    if (argv[2] == nullptr) {
+        LOG(ERROR) << "Missing parameters";
+        return false;
+    }
+
+    if (std::string("dexopt").compare(argv[2]) == 0) {
+        // This is version 1 (N) or pre-versioning version 2.
+        constexpr int kV2ArgCount =   1   // "otapreopt"
+                                    + 1   // slot
+                                    + 1   // "dexopt"
+                                    + 1   // apk_path
+                                    + 1   // uid
+                                    + 1   // pkg
+                                    + 1   // isa
+                                    + 1   // dexopt_needed
+                                    + 1   // oat_dir
+                                    + 1   // dexopt_flags
+                                    + 1   // filter
+                                    + 1   // volume
+                                    + 1   // libs
+                                    + 1;  // seinfo
+        if (argc == kV2ArgCount) {
+            return ReadArgumentsPostV1(2, argv, false);
+        } else {
+            return ReadArgumentsV1(argv);
+        }
+    }
+
+    uint32_t version;
+    if (!ParseUInt(argv[2], &version)) {
+        LOG(ERROR) << "Could not parse version: " << argv[2];
+        return false;
+    }
+
+    return ReadArgumentsPostV1(version, argv, true);
+}
+
+static int ReplaceMask(int input, int old_mask, int new_mask) {
+    return (input & old_mask) != 0 ? new_mask : 0;
+}
+
+void OTAPreoptParameters::SetDefaultsForPostV1Arguments() {
+    // Set se_info to null. It is only relevant for secondary dex files, which we won't
+    // receive from a v1 A side.
+    se_info = nullptr;
+
+    // Set downgrade to false. It is only relevant when downgrading compiler
+    // filter, which is not the case during ota.
+    downgrade = false;
+
+    // Set target_sdk_version to 0, ie the platform SDK version. This is
+    // conservative and may force some classes to verify at runtime.
+    target_sdk_version = 0;
+
+    // Set the profile name to the primary apk profile.
+    profile_name = "primary.prof";
+
+    // By default we don't have a dex metadata file.
+    dex_metadata_path = nullptr;
+
+    // The compilation reason is ab-ota (match the system property pm.dexopt.ab-ota)
+    compilation_reason = "ab-ota";
+}
+
+bool OTAPreoptParameters::ReadArgumentsV1(const char** argv) {
+    // Check for "dexopt".
+    if (argv[2] == nullptr) {
+        LOG(ERROR) << "Missing parameters";
+        return false;
+    }
+    if (std::string("dexopt").compare(argv[2]) != 0) {
+        LOG(ERROR) << "Expected \"dexopt\" but found: " << argv[2];
+        return false;
+    }
+
+    size_t param_index = 0;
+    for (;; ++param_index) {
+        const char* param = argv[3 + param_index];
+        if (param == nullptr) {
+            break;
+        }
+
+        switch (param_index) {
+            case 0:
+                apk_path = param;
+                break;
+
+            case 1:
+                uid = atoi(param);
+                break;
+
+            case 2:
+                pkgName = param;
+                break;
+
+            case 3:
+                instruction_set = param;
+                break;
+
+            case 4: {
+                // Version 1 had:
+                //   DEXOPT_DEX2OAT_NEEDED       = 1
+                //   DEXOPT_PATCHOAT_NEEDED      = 2
+                //   DEXOPT_SELF_PATCHOAT_NEEDED = 3
+                // We will simply use DEX2OAT_FROM_SCRATCH.
+                dexopt_needed = DEX2OAT_FROM_SCRATCH;
+                break;
+            }
+
+            case 5:
+                oat_dir = param;
+                break;
+
+            case 6: {
+                // Version 1 had:
+                constexpr int OLD_DEXOPT_PUBLIC         = 1 << 1;
+                // Note: DEXOPT_SAFEMODE has been removed.
+                // constexpr int OLD_DEXOPT_SAFEMODE       = 1 << 2;
+                constexpr int OLD_DEXOPT_DEBUGGABLE     = 1 << 3;
+                constexpr int OLD_DEXOPT_BOOTCOMPLETE   = 1 << 4;
+                constexpr int OLD_DEXOPT_PROFILE_GUIDED = 1 << 5;
+                constexpr int OLD_DEXOPT_OTA            = 1 << 6;
+                int input = atoi(param);
+                dexopt_flags =
+                        ReplaceMask(input, OLD_DEXOPT_PUBLIC, DEXOPT_PUBLIC) |
+                        ReplaceMask(input, OLD_DEXOPT_DEBUGGABLE, DEXOPT_DEBUGGABLE) |
+                        ReplaceMask(input, OLD_DEXOPT_BOOTCOMPLETE, DEXOPT_BOOTCOMPLETE) |
+                        ReplaceMask(input, OLD_DEXOPT_PROFILE_GUIDED, DEXOPT_PROFILE_GUIDED) |
+                        ReplaceMask(input, OLD_DEXOPT_OTA, 0);
+                break;
+            }
+
+            case 7:
+                compiler_filter = param;
+                break;
+
+            case 8:
+                volume_uuid = ParseNull(param);
+                break;
+
+            case 9:
+                shared_libraries = ParseNull(param);
+                break;
+
+            default:
+                LOG(ERROR) << "Too many arguments, got " << param;
+                return false;
+        }
+    }
+
+    if (param_index != 10) {
+        LOG(ERROR) << "Not enough parameters";
+        return false;
+    }
+
+    SetDefaultsForPostV1Arguments();
+
+    return true;
+}
+
+bool OTAPreoptParameters::ReadArgumentsPostV1(uint32_t version, const char** argv, bool versioned) {
+    size_t num_args_expected = 0;
+    switch (version) {
+        case 2: num_args_expected = 11; break;
+        case 3: num_args_expected = 12; break;
+        case 4: num_args_expected = 13; break;
+        case 5: num_args_expected = 14; break;
+        case 6: num_args_expected = 15; break;
+        case 7: num_args_expected = 16; break;
+        default:
+            LOG(ERROR) << "Don't know how to read arguments for version " << version;
+            return false;
+    }
+    size_t dexopt_index = versioned ? 3 : 2;
+
+    // Check for "dexopt".
+    if (argv[dexopt_index] == nullptr) {
+        LOG(ERROR) << "Missing parameters";
+        return false;
+    }
+    if (std::string("dexopt").compare(argv[dexopt_index]) != 0) {
+        LOG(ERROR) << "Expected \"dexopt\" but found: " << argv[dexopt_index];
+        return false;
+    }
+
+    // Validate the number of arguments.
+    size_t num_args_actual = 0;
+    while (argv[dexopt_index + 1 + num_args_actual] != nullptr) {
+        num_args_actual++;
+    }
+
+    if (num_args_actual != num_args_expected) {
+        LOG(ERROR) << "Invalid number of arguments. expected="
+                << num_args_expected << " actual=" << num_args_actual;
+        return false;
+    }
+
+    // The number of arguments is OK.
+    // Configure the default values for the parameters that were added after V1.
+    // The default values will be overwritten in case they are passed as arguments.
+    SetDefaultsForPostV1Arguments();
+
+    for (size_t param_index = 0; param_index < num_args_actual; ++param_index) {
+        const char* param = argv[dexopt_index + 1 + param_index];
+        switch (param_index) {
+            case 0:
+                apk_path = param;
+                break;
+
+            case 1:
+                uid = atoi(param);
+                break;
+
+            case 2:
+                pkgName = param;
+                break;
+
+            case 3:
+                instruction_set = param;
+                break;
+
+            case 4:
+                dexopt_needed = atoi(param);
+                break;
+
+            case 5:
+                oat_dir = param;
+                break;
+
+            case 6:
+                dexopt_flags = atoi(param);
+                break;
+
+            case 7:
+                compiler_filter = param;
+                break;
+
+            case 8:
+                volume_uuid = ParseNull(param);
+                break;
+
+            case 9:
+                shared_libraries = ParseNull(param);
+                break;
+
+            case 10:
+                se_info = ParseNull(param);
+                break;
+
+            case 11:
+                downgrade = ParseBool(param);
+                break;
+
+            case 12:
+                target_sdk_version = atoi(param);
+                break;
+
+            case 13:
+                profile_name = ParseNull(param);
+                break;
+
+            case 14:
+                dex_metadata_path = ParseNull(param);
+                break;
+
+            case 15:
+                compilation_reason = ParseNull(param);
+                break;
+
+            default:
+                LOG(FATAL) << "Should not get here. Did you call ReadArguments "
+                        << "with the right expectation? index=" << param_index
+                        << " num_args=" << num_args_actual;
+                return false;
+        }
+    }
+
+    return true;
+}
+
+}  // namespace installd
+}  // namespace android
diff --git a/cmds/installd/otapreopt_parameters.h b/cmds/installd/otapreopt_parameters.h
new file mode 100644
index 0000000..a2f6e44
--- /dev/null
+++ b/cmds/installd/otapreopt_parameters.h
@@ -0,0 +1,61 @@
+/*
+ ** Copyright 2018, The Android Open Source Project
+ **
+ ** Licensed under the Apache License, Version 2.0 (the "License");
+ ** you may not use this file except in compliance with the License.
+ ** You may obtain a copy of the License at
+ **
+ **     http://www.apache.org/licenses/LICENSE-2.0
+ **
+ ** Unless required by applicable law or agreed to in writing, software
+ ** distributed under the License is distributed on an "AS IS" BASIS,
+ ** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ ** See the License for the specific language governing permissions and
+ ** limitations under the License.
+ */
+
+#ifndef OTAPREOPT_PARAMETERS_H_
+#define OTAPREOPT_PARAMETERS_H_
+
+#include <string>
+#include <sys/types.h>
+
+namespace android {
+namespace installd {
+
+class OTAPreoptParameters {
+  public:
+    bool ReadArguments(int argc, const char** argv);
+
+  private:
+    bool ReadArgumentsV1(const char** argv);
+    bool ReadArgumentsPostV1(uint32_t version, const char** argv, bool versioned);
+
+    void SetDefaultsForPostV1Arguments();
+    const char* apk_path;
+    uid_t uid;
+    const char* pkgName;
+    const char* instruction_set;
+    int dexopt_needed;
+    const char* oat_dir;
+    int dexopt_flags;
+    const char* compiler_filter;
+    const char* volume_uuid;
+    const char* shared_libraries;
+    const char* se_info;
+    bool downgrade;
+    int target_sdk_version;
+    const char* profile_name;
+    const char* dex_metadata_path;
+    const char* compilation_reason;
+
+    std::string target_slot;
+
+    friend class OTAPreoptService;
+    friend class OTAPreoptTest;
+};
+
+}  // namespace installd
+}  // namespace android
+
+#endif  //  OTAPREOPT_PARAMETERS_H_
diff --git a/cmds/installd/tests/Android.bp b/cmds/installd/tests/Android.bp
index 1a22992..7438d3d 100644
--- a/cmds/installd/tests/Android.bp
+++ b/cmds/installd/tests/Android.bp
@@ -24,6 +24,7 @@
     shared_libs: [
         "libbase",
         "libbinder",
+        "libcrypto",
         "libcutils",
         "libselinux",
         "libutils",
@@ -44,6 +45,7 @@
     shared_libs: [
         "libbase",
         "libbinder",
+        "libcrypto",
         "libcutils",
         "libselinux",
         "libutils",
@@ -64,6 +66,7 @@
     shared_libs: [
         "libbase",
         "libbinder",
+        "libcrypto",
         "libcutils",
         "libselinux",
         "libutils",
@@ -75,3 +78,20 @@
         "liblogwrap",
     ],
 }
+
+cc_test {
+    name: "installd_otapreopt_test",
+    clang: true,
+    srcs: ["installd_otapreopt_test.cpp"],
+    cflags: ["-Wall", "-Werror"],
+    shared_libs: [
+        "libbase",
+        "libcutils",
+        "libutils",
+    ],
+    static_libs: [
+        "liblog",
+        "libotapreoptparameters"
+    ],
+}
+
diff --git a/cmds/installd/tests/installd_dexopt_test.cpp b/cmds/installd/tests/installd_dexopt_test.cpp
index ff29506..d640558 100644
--- a/cmds/installd/tests/installd_dexopt_test.cpp
+++ b/cmds/installd/tests/installd_dexopt_test.cpp
@@ -14,6 +14,7 @@
  * limitations under the License.
  */
 
+#include <cstdlib>
 #include <fcntl.h>
 #include <stdlib.h>
 #include <string.h>
@@ -153,10 +154,12 @@
     InstalldNativeService* service_;
     std::unique_ptr<std::string> volume_uuid_;
     std::string package_name_;
+    std::string apk_path_;
     std::string app_apk_dir_;
     std::string app_private_dir_ce_;
     std::string app_private_dir_de_;
     std::string se_info_;
+    std::string app_oat_dir_;
 
     int64_t ce_data_inode_;
 
@@ -197,13 +200,13 @@
 
     void create_mock_app() {
         // Create the oat dir.
-        std::string app_oat_dir = app_apk_dir_ + "/oat";
+        app_oat_dir_ = app_apk_dir_ + "/oat";
         mkdir(app_apk_dir_, kSystemUid, kSystemGid, 0755);
-        service_->createOatDir(app_oat_dir, kRuntimeIsa);
+        service_->createOatDir(app_oat_dir_, kRuntimeIsa);
 
         // Copy the primary apk.
-        std::string apk_path = app_apk_dir_ + "/base.jar";
-        ASSERT_TRUE(WriteBase64ToFile(kDexFile, apk_path, kSystemUid, kSystemGid, 0644));
+        apk_path_ = app_apk_dir_ + "/base.jar";
+        ASSERT_TRUE(WriteBase64ToFile(kDexFile, apk_path_, kSystemUid, kSystemGid, 0644));
 
         // Create the app user data.
         ASSERT_TRUE(service_->createAppData(
@@ -259,6 +262,9 @@
         std::unique_ptr<std::string> se_info_ptr(new std::string(se_info_));
         bool downgrade = false;
         int32_t target_sdk_version = 0;  // default
+        std::unique_ptr<std::string> profile_name_ptr = nullptr;
+        std::unique_ptr<std::string> dm_path_ptr = nullptr;
+        std::unique_ptr<std::string> compilation_reason_ptr = nullptr;
 
         binder::Status result = service_->dexopt(path,
                                                  uid,
@@ -272,8 +278,11 @@
                                                  class_loader_context_ptr,
                                                  se_info_ptr,
                                                  downgrade,
-                                                 target_sdk_version);
-        ASSERT_EQ(should_binder_call_succeed, result.isOk());
+                                                 target_sdk_version,
+                                                 profile_name_ptr,
+                                                 dm_path_ptr,
+                                                 compilation_reason_ptr);
+        ASSERT_EQ(should_binder_call_succeed, result.isOk()) << result.toString8().c_str();
         int expected_access = should_dex_be_compiled ? 0 : -1;
         std::string odex = GetSecondaryDexArtifact(path, "odex");
         std::string vdex = GetSecondaryDexArtifact(path, "vdex");
@@ -301,7 +310,7 @@
             storage_flag,
             &out_secondary_dex_exists);
 
-        ASSERT_EQ(should_binder_call_succeed, result.isOk());
+        ASSERT_EQ(should_binder_call_succeed, result.isOk()) << result.toString8().c_str();
         ASSERT_EQ(should_dex_exist, out_secondary_dex_exists);
 
         int expected_access = should_dex_be_deleted ? -1 : 0;
@@ -320,6 +329,115 @@
         ASSERT_EQ(gid, st.st_gid);
         ASSERT_EQ(mode, st.st_mode);
     }
+
+    void CompilePrimaryDexOk(std::string compiler_filter,
+                             int32_t dex_flags,
+                             const char* oat_dir,
+                             int32_t uid,
+                             int32_t dexopt_needed,
+                             const char* dm_path = nullptr,
+                             bool downgrade = false) {
+        return CompilePrimaryDex(
+                compiler_filter, dex_flags, oat_dir, uid, dexopt_needed, dm_path, downgrade, true);
+    }
+
+    void CompilePrimaryDexFail(std::string compiler_filter,
+                               int32_t dex_flags,
+                               const char* oat_dir,
+                               int32_t uid,
+                               int32_t dexopt_needed,
+                               const char* dm_path = nullptr,
+                               bool downgrade = false) {
+        return CompilePrimaryDex(
+                compiler_filter, dex_flags, oat_dir, uid, dexopt_needed, dm_path, downgrade, false);
+    }
+
+    void CompilePrimaryDex(std::string compiler_filter,
+                           int32_t dex_flags,
+                           const char* oat_dir,
+                           int32_t uid,
+                           int32_t dexopt_needed,
+                           const char* dm_path,
+                           bool downgrade,
+                           bool should_binder_call_succeed) {
+        std::unique_ptr<std::string> package_name_ptr(new std::string(package_name_));
+        std::unique_ptr<std::string> out_path(
+                oat_dir == nullptr ? nullptr : new std::string(oat_dir));
+        std::unique_ptr<std::string> class_loader_context_ptr(new std::string("&"));
+        std::unique_ptr<std::string> se_info_ptr(new std::string(se_info_));
+        int32_t target_sdk_version = 0;  // default
+        std::unique_ptr<std::string> profile_name_ptr(new std::string("primary.prof"));
+        std::unique_ptr<std::string> dm_path_ptr = nullptr;
+        if (dm_path != nullptr) {
+            dm_path_ptr.reset(new std::string(dm_path));
+        }
+        std::unique_ptr<std::string> compilation_reason_ptr(new std::string("test-reason"));
+
+        bool prof_result;
+        binder::Status prof_binder_result = service_->prepareAppProfile(
+                package_name_, kTestUserId, kTestAppId, *profile_name_ptr, apk_path_,
+                /*dex_metadata*/ nullptr, &prof_result);
+
+        ASSERT_TRUE(prof_binder_result.isOk()) << prof_binder_result.toString8().c_str();
+        ASSERT_TRUE(prof_result);
+
+        binder::Status result = service_->dexopt(apk_path_,
+                                                 uid,
+                                                 package_name_ptr,
+                                                 kRuntimeIsa,
+                                                 dexopt_needed,
+                                                 out_path,
+                                                 dex_flags,
+                                                 compiler_filter,
+                                                 volume_uuid_,
+                                                 class_loader_context_ptr,
+                                                 se_info_ptr,
+                                                 downgrade,
+                                                 target_sdk_version,
+                                                 profile_name_ptr,
+                                                 dm_path_ptr,
+                                                 compilation_reason_ptr);
+        ASSERT_EQ(should_binder_call_succeed, result.isOk()) << result.toString8().c_str();
+
+        if (!should_binder_call_succeed) {
+            return;
+        }
+        // Check the access to the compiler output.
+        //  - speed-profile artifacts are not world-wide readable.
+        //  - files are owned by the system uid.
+        std::string odex = GetPrimaryDexArtifact(oat_dir, apk_path_, "odex");
+        std::string vdex = GetPrimaryDexArtifact(oat_dir, apk_path_, "vdex");
+        std::string art = GetPrimaryDexArtifact(oat_dir, apk_path_, "art");
+
+        bool is_public = (dex_flags & DEXOPT_PUBLIC) != 0;
+        mode_t mode = S_IFREG | (is_public ? 0644 : 0640);
+        CheckFileAccess(odex, kSystemUid, uid, mode);
+        CheckFileAccess(vdex, kSystemUid, uid, mode);
+
+        if (compiler_filter == "speed-profile") {
+            CheckFileAccess(art, kSystemUid, uid, mode);
+        }
+    }
+
+    std::string GetPrimaryDexArtifact(const char* oat_dir,
+                                      const std::string& dex_path,
+                                      const std::string& type) {
+        if (oat_dir == nullptr) {
+            std::string path = dex_path;
+            for (auto it = path.begin() + 1; it < path.end(); ++it) {
+                if (*it == '/') {
+                    *it = '@';
+                }
+            }
+            return android_data_dir + DALVIK_CACHE + '/' + kRuntimeIsa + "/" + path
+                    + "@classes.dex";
+        } else {
+            std::string::size_type name_end = dex_path.rfind('.');
+            std::string::size_type name_start = dex_path.rfind('/');
+            return std::string(oat_dir) + "/" + kRuntimeIsa + "/" +
+                    dex_path.substr(name_start + 1, name_end - name_start) + type;
+        }
+    }
 };
 
 
@@ -366,6 +484,87 @@
         /*binder_ok*/ false,  /*compile_ok*/ false, kSystemUid);
 }
 
+TEST_F(DexoptTest, DexoptPrimaryPublic) {
+    LOG(INFO) << "DexoptPrimaryPublic";
+    CompilePrimaryDexOk("verify",
+                        DEXOPT_BOOTCOMPLETE | DEXOPT_PUBLIC,
+                        app_oat_dir_.c_str(),
+                        kTestAppGid,
+                        DEX2OAT_FROM_SCRATCH);
+}
+
+TEST_F(DexoptTest, DexoptPrimaryFailedInvalidFilter) {
+    LOG(INFO) << "DexoptPrimaryFailedInvalidFilter";
+    CompilePrimaryDexFail("awesome-filter",
+                          DEXOPT_IDLE_BACKGROUND_JOB | DEXOPT_PUBLIC,
+                          app_oat_dir_.c_str(),
+                          kTestAppGid,
+                          DEX2OAT_FROM_SCRATCH);
+}
+
+TEST_F(DexoptTest, DexoptPrimaryProfileNonPublic) {
+    LOG(INFO) << "DexoptPrimaryProfileNonPublic";
+    CompilePrimaryDexOk("speed-profile",
+                        DEXOPT_BOOTCOMPLETE | DEXOPT_PROFILE_GUIDED,
+                        app_oat_dir_.c_str(),
+                        kTestAppGid,
+                        DEX2OAT_FROM_SCRATCH);
+}
+
+TEST_F(DexoptTest, DexoptPrimaryProfilePublic) {
+    LOG(INFO) << "DexoptPrimaryProfilePublic";
+    CompilePrimaryDexOk("speed-profile",
+                        DEXOPT_BOOTCOMPLETE | DEXOPT_PROFILE_GUIDED | DEXOPT_PUBLIC,
+                        app_oat_dir_.c_str(),
+                        kTestAppGid,
+                        DEX2OAT_FROM_SCRATCH);
+}
+
+TEST_F(DexoptTest, DexoptPrimaryBackgroundOk) {
+    LOG(INFO) << "DexoptPrimaryBackgroundOk";
+    CompilePrimaryDexOk("speed-profile",
+                        DEXOPT_IDLE_BACKGROUND_JOB | DEXOPT_PROFILE_GUIDED,
+                        app_oat_dir_.c_str(),
+                        kTestAppGid,
+                        DEX2OAT_FROM_SCRATCH);
+}
+
+class PrimaryDexReCompilationTest : public DexoptTest {
+  public:
+    virtual void SetUp() {
+        DexoptTest::SetUp();
+        CompilePrimaryDexOk("verify",
+                            DEXOPT_BOOTCOMPLETE | DEXOPT_PUBLIC,
+                            app_oat_dir_.c_str(),
+                            kTestAppGid,
+                            DEX2OAT_FROM_SCRATCH);
+        std::string odex = GetSecondaryDexArtifact(apk_path_, "odex");
+        std::string vdex = GetSecondaryDexArtifact(apk_path_, "vdex");
+
+        first_compilation_odex_fd_.reset(open(odex.c_str(), O_RDONLY));
+        first_compilation_vdex_fd_.reset(open(vdex.c_str(), O_RDONLY));
+    }
+
+    virtual void TearDown() {
+        first_compilation_odex_fd_.reset(-1);
+        first_compilation_vdex_fd_.reset(-1);
+        DexoptTest::TearDown();
+    }
+
+  protected:
+    unique_fd first_compilation_odex_fd_;
+    unique_fd first_compilation_vdex_fd_;
+};
+
+TEST_F(PrimaryDexReCompilationTest, DexoptPrimaryUpdateInPlaceVdex) {
+    LOG(INFO) << "DexoptPrimaryUpdateInPlaceVdex";
+
+    CompilePrimaryDexOk("verify",
+                        DEXOPT_IDLE_BACKGROUND_JOB | DEXOPT_PUBLIC,
+                        app_oat_dir_.c_str(),
+                        kTestAppGid,
+                        DEX2OAT_FOR_BOOT_IMAGE);
+}
 
 class ReconcileTest : public DexoptTest {
     virtual void SetUp() {
@@ -431,17 +630,22 @@
     std::string ref_profile_;
     std::string snap_profile_;
 
+    static constexpr const char* kPrimaryProfile = "primary.prof";
+
     virtual void SetUp() {
         DexoptTest::SetUp();
         cur_profile_ = create_current_profile_path(
-                kTestUserId, package_name_, /*is_secondary_dex*/ false);
-        ref_profile_ = create_reference_profile_path(package_name_, /*is_secondary_dex*/ false);
-        snap_profile_ = create_snapshot_profile_path(package_name_, "base.jar");
+                kTestUserId, package_name_, kPrimaryProfile, /*is_secondary_dex*/ false);
+        ref_profile_ = create_reference_profile_path(package_name_, kPrimaryProfile,
+                /*is_secondary_dex*/ false);
+        snap_profile_ = create_snapshot_profile_path(package_name_, kPrimaryProfile);
     }
 
-    void SetupProfile(const std::string& path, uid_t uid, gid_t gid, mode_t mode, int32_t seed) {
-        run_cmd("profman --generate-test-profile-seed=" + std::to_string(seed) +
-                " --generate-test-profile-num-dex=2 --generate-test-profile=" + path);
+    void SetupProfile(const std::string& path, uid_t uid, gid_t gid, mode_t mode,
+            int32_t num_dex) {
+        run_cmd("profman --generate-test-profile-seed=" + std::to_string(num_dex) +
+                " --generate-test-profile-num-dex=" + std::to_string(num_dex) +
+                " --generate-test-profile=" + path);
         ::chmod(path.c_str(), mode);
         ::chown(path.c_str(), uid, gid);
     }
@@ -449,7 +653,7 @@
     void SetupProfiles(bool setup_ref) {
         SetupProfile(cur_profile_, kTestAppUid, kTestAppGid, 0600, 1);
         if (setup_ref) {
-            SetupProfile(ref_profile_, kTestAppUid, kTestAppGid, 0060, 2);
+            SetupProfile(ref_profile_, kTestAppUid, kTestAppGid, 0600, 2);
         }
     }
 
@@ -457,8 +661,8 @@
             bool expected_result) {
         bool result;
         binder::Status binder_result = service_->createProfileSnapshot(
-                appid, package_name, "base.jar", &result);
-        ASSERT_TRUE(binder_result.isOk());
+                appid, package_name, kPrimaryProfile, apk_path_, &result);
+        ASSERT_TRUE(binder_result.isOk()) << binder_result.toString8().c_str();
         ASSERT_EQ(expected_result, result);
 
         if (!expected_result) {
@@ -475,7 +679,8 @@
         run_cmd("touch " + expected_profile_content);
         run_cmd("profman --profile-file=" + cur_profile_ +
                 " --profile-file=" + ref_profile_ +
-                " --reference-profile-file=" + expected_profile_content);
+                " --reference-profile-file=" + expected_profile_content +
+                " --apk=" + apk_path_);
 
         ASSERT_TRUE(AreFilesEqual(expected_profile_content, snap_profile_));
 
@@ -493,7 +698,62 @@
         ASSERT_TRUE(WIFEXITED(wait_child(pid)));
     }
 
-  private:
+    void mergePackageProfiles(const std::string& package_name,
+                              const std::string& code_path,
+                              bool expected_result) {
+        bool result;
+        binder::Status binder_result = service_->mergeProfiles(
+                kTestAppUid, package_name, code_path, &result);
+        ASSERT_TRUE(binder_result.isOk()) << binder_result.toString8().c_str();
+        ASSERT_EQ(expected_result, result);
+
+        if (!expected_result) {
+            // Do not check the files if we expect to fail.
+            return;
+        }
+
+        // Check that the snapshot was created witht he expected acess flags.
+        CheckFileAccess(ref_profile_, kTestAppUid, kTestAppUid, 0600 | S_IFREG);
+
+        // The snapshot should be equivalent to the merge of profiles.
+        std::string ref_profile_content = ref_profile_ + ".expected";
+        run_cmd("rm -f " + ref_profile_content);
+        run_cmd("touch " + ref_profile_content);
+        run_cmd("profman --profile-file=" + cur_profile_ +
+                " --profile-file=" + ref_profile_ +
+                " --reference-profile-file=" + ref_profile_content);
+
+        ASSERT_TRUE(AreFilesEqual(ref_profile_content, ref_profile_));
+    }
+
+    // TODO(calin): add dex metadata tests once the ART change is merged.
+    void preparePackageProfile(const std::string& package_name, const std::string& profile_name,
+            bool expected_result) {
+        bool result;
+        binder::Status binder_result = service_->prepareAppProfile(
+                package_name, kTestUserId, kTestAppId, profile_name, apk_path_,
+                /*dex_metadata*/ nullptr, &result);
+        ASSERT_TRUE(binder_result.isOk()) << binder_result.toString8().c_str();
+        ASSERT_EQ(expected_result, result);
+
+        if (!expected_result) {
+            // Do not check the files if we expect to fail.
+            return;
+        }
+
+        std::string code_path_cur_prof = create_current_profile_path(
+                kTestUserId, package_name, profile_name, /*is_secondary_dex*/ false);
+        std::string code_path_ref_profile = create_reference_profile_path(package_name,
+                profile_name, /*is_secondary_dex*/ false);
+
+        // Check that we created the current profile.
+        CheckFileAccess(code_path_cur_prof, kTestAppUid, kTestAppUid, 0600 | S_IFREG);
+
+        // Without dex metadata we don't generate a reference profile.
+        ASSERT_EQ(-1, access(code_path_ref_profile.c_str(), R_OK));
+    }
+
+  protected:
     void TransitionToSystemServer() {
         ASSERT_TRUE(DropCapabilities(kSystemUid, kSystemGid));
         int32_t res = selinux_android_setcontext(
@@ -559,24 +819,46 @@
     SetupProfiles(/*setup_ref*/ true);
     createProfileSnapshot(kTestAppId, package_name_, /*expected_result*/ true);
 
-    binder::Status binder_result = service_->destroyProfileSnapshot(package_name_, "base.jar");
-    ASSERT_TRUE(binder_result.isOk());
+    binder::Status binder_result = service_->destroyProfileSnapshot(package_name_, kPrimaryProfile);
+    ASSERT_TRUE(binder_result.isOk()) << binder_result.toString8().c_str();
     struct stat st;
     ASSERT_EQ(-1, stat(snap_profile_.c_str(), &st));
     ASSERT_EQ(ENOENT, errno);
 }
 
+TEST_F(ProfileTest, ProfileMergeOk) {
+    LOG(INFO) << "ProfileMergeOk";
+
+    SetupProfiles(/*setup_ref*/ true);
+    mergePackageProfiles(package_name_, "primary.prof", /*expected_result*/ true);
+}
+
+// The reference profile is created on the fly. We need to be able to
+// merge without one.
+TEST_F(ProfileTest, ProfileMergeOkNoReference) {
+    LOG(INFO) << "ProfileMergeOkNoReference";
+
+    SetupProfiles(/*setup_ref*/ false);
+    mergePackageProfiles(package_name_, "primary.prof", /*expected_result*/ true);
+}
+
+TEST_F(ProfileTest, ProfileMergeFailWrongPackage) {
+    LOG(INFO) << "ProfileMergeFailWrongPackage";
+
+    SetupProfiles(/*setup_ref*/ true);
+    mergePackageProfiles("not.there", "primary.prof", /*expected_result*/ false);
+}
+
 TEST_F(ProfileTest, ProfileDirOk) {
     LOG(INFO) << "ProfileDirOk";
 
     std::string cur_profile_dir = create_primary_current_profile_package_dir_path(
             kTestUserId, package_name_);
     std::string cur_profile_file = create_current_profile_path(kTestUserId, package_name_,
-            /*is_secondary_dex*/false);
+            kPrimaryProfile, /*is_secondary_dex*/false);
     std::string ref_profile_dir = create_primary_reference_profile_package_dir_path(package_name_);
 
     CheckFileAccess(cur_profile_dir, kTestAppUid, kTestAppUid, 0700 | S_IFDIR);
-    CheckFileAccess(cur_profile_file, kTestAppUid, kTestAppUid, 0600 | S_IFREG);
     CheckFileAccess(ref_profile_dir, kSystemUid, kTestAppGid, 0770 | S_IFDIR);
 }
 
@@ -588,7 +870,7 @@
     std::string cur_profile_dir = create_primary_current_profile_package_dir_path(
             kTestUserId, package_name_);
     std::string cur_profile_file = create_current_profile_path(kTestUserId, package_name_,
-            /*is_secondary_dex*/false);
+            kPrimaryProfile, /*is_secondary_dex*/false);
     std::string ref_profile_dir = create_primary_reference_profile_package_dir_path(package_name_);
 
     // Simulate a pre-P setup by changing the owner to kTestAppGid and permissions to 0700.
@@ -608,9 +890,132 @@
 
     // Check the file access.
     CheckFileAccess(cur_profile_dir, kTestAppUid, kTestAppUid, 0700 | S_IFDIR);
-    CheckFileAccess(cur_profile_file, kTestAppUid, kTestAppUid, 0600 | S_IFREG);
     CheckFileAccess(ref_profile_dir, kSystemUid, kTestAppGid, 0770 | S_IFDIR);
 }
 
+TEST_F(ProfileTest, ProfilePrepareOk) {
+    LOG(INFO) << "ProfilePrepareOk";
+    preparePackageProfile(package_name_, "split.prof", /*expected_result*/ true);
+}
+
+TEST_F(ProfileTest, ProfilePrepareFailInvalidPackage) {
+    LOG(INFO) << "ProfilePrepareFailInvalidPackage";
+    preparePackageProfile("not.there.package", "split.prof", /*expected_result*/ false);
+}
+
+TEST_F(ProfileTest, ProfilePrepareFailProfileChangedUid) {
+    LOG(INFO) << "ProfilePrepareFailProfileChangedUid";
+    SetupProfiles(/*setup_ref*/ false);
+    // Change the uid on the profile to trigger a failure.
+    ::chown(cur_profile_.c_str(), kTestAppUid + 1, kTestAppGid + 1);
+    preparePackageProfile(package_name_, "primary.prof", /*expected_result*/ false);
+}
+
+
+class BootProfileTest : public ProfileTest {
+  public:
+    virtual void setup() {
+        ProfileTest::SetUp();
+        intial_android_profiles_dir = android_profiles_dir;
+    }
+
+    virtual void TearDown() {
+        android_profiles_dir = intial_android_profiles_dir;
+        ProfileTest::TearDown();
+    }
+
+    void UpdateAndroidProfilesDir(const std::string& profile_dir) {
+        android_profiles_dir = profile_dir;
+        // We need to create the reference profile directory in the new profile dir.
+        run_cmd("mkdir -p " + profile_dir + "/ref");
+    }
+
+    void createBootImageProfileSnapshot(const std::string& classpath, bool expected_result) {
+        bool result;
+        binder::Status binder_result = service_->createProfileSnapshot(
+                -1, "android", "android.prof", classpath, &result);
+        ASSERT_TRUE(binder_result.isOk());
+        ASSERT_EQ(expected_result, result);
+
+        if (!expected_result) {
+            // Do not check the files if we expect to fail.
+            return;
+        }
+
+        // Check that the snapshot was created with he expected access flags.
+        const std::string boot_profile = create_snapshot_profile_path("android", "android.prof");
+        CheckFileAccess(boot_profile, kSystemUid, kSystemGid, 0600 | S_IFREG);
+
+        pid_t pid = fork();
+        if (pid == 0) {
+            /* child */
+            TransitionToSystemServer();
+
+            // System server should be able to open the snapshot.
+            unique_fd fd(open(boot_profile.c_str(), O_RDONLY));
+            ASSERT_TRUE(fd > -1) << "Failed to open profile as kSystemUid: " << strerror(errno);
+            _exit(0);
+        }
+        /* parent */
+        ASSERT_TRUE(WIFEXITED(wait_child(pid)));
+    }
+  protected:
+    std::string intial_android_profiles_dir;
+};
+
+TEST_F(BootProfileTest, BootProfileSnapshotOk) {
+    LOG(INFO) << "BootProfileSnapshotOk";
+    char* boot_classpath = getenv("BOOTCLASSPATH");
+    ASSERT_TRUE(boot_classpath != nullptr);
+    createBootImageProfileSnapshot(boot_classpath, /*expected_result*/ true);
+}
+
+TEST_F(BootProfileTest, BootProfileSnapshotFailEmptyClasspath) {
+    LOG(INFO) << "BootProfileSnapshotFailEmptyClasspath";
+
+    createBootImageProfileSnapshot(/*boot_classpath*/ "", /*expected_result*/ false);
+}
+
+TEST_F(BootProfileTest, BootProfileSnapshotOkNoProfiles) {
+    LOG(INFO) << "BootProfileSnapshotOkNoProfiles";
+    char* boot_classpath = getenv("BOOTCLASSPATH");
+    ASSERT_TRUE(boot_classpath != nullptr);
+
+    // The app_apk_dir has no profiles. So we shouldn't be able to merge anything.
+    // Still, this is not a failure case.
+    UpdateAndroidProfilesDir(app_apk_dir_);
+    createBootImageProfileSnapshot(boot_classpath, /*expected_result*/ true);
+}
+
+// Verify that profile collection.
+TEST_F(BootProfileTest, CollectProfiles) {
+    LOG(INFO) << "CollectProfiles";
+
+    // Create some profile directories mimicking the real profile structure.
+    run_cmd("mkdir -p " + app_private_dir_de_ + "/profiles/ref");
+    run_cmd("mkdir -p " + app_private_dir_de_ + "/profiles/cur/0/");
+    run_cmd("mkdir -p " + app_private_dir_de_ + "/profiles/cur/1/");
+    // Create an empty profile.
+    run_cmd("touch " + app_private_dir_de_ + "/profiles/cur/1/primary.prof");
+    // Create a random file.
+    run_cmd("touch " + app_private_dir_de_ + "/profiles/cur/0/non.profile.file");
+
+    // Create some non-empty profiles.
+    std::string current_prof = app_private_dir_de_ + "/profiles/cur/0/primary.prof";
+    run_cmd("echo 1 > " + current_prof);
+    std::string ref_prof = app_private_dir_de_ + "/profiles/ref/primary.prof";
+    run_cmd("echo 1 > " + ref_prof);
+
+    UpdateAndroidProfilesDir(app_private_dir_de_ + "/profiles");
+
+    std::vector<std::string> profiles;
+    collect_profiles(&profiles);
+
+    // Only two profiles should be in the output.
+    ASSERT_EQ(2u, profiles.size());
+    ASSERT_TRUE(std::find(profiles.begin(), profiles.end(), current_prof) != profiles.end());
+    ASSERT_TRUE(std::find(profiles.begin(), profiles.end(), ref_prof) != profiles.end());
+}
+
 }  // namespace installd
 }  // namespace android
diff --git a/cmds/installd/tests/installd_otapreopt_test.cpp b/cmds/installd/tests/installd_otapreopt_test.cpp
new file mode 100644
index 0000000..82bf932
--- /dev/null
+++ b/cmds/installd/tests/installd_otapreopt_test.cpp
@@ -0,0 +1,223 @@
+/**
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <stdlib.h>
+#include <string.h>
+
+#include <android-base/logging.h>
+#include <gtest/gtest.h>
+
+#include "otapreopt_parameters.h"
+
+namespace android {
+namespace installd {
+
+static bool ParseBool(const char* in) {
+    if (strcmp(in, "true") == 0) {
+        return true;
+    }
+    return false;
+}
+
+static const char* ParseNull(const char* arg) {
+    return (strcmp(arg, "!") == 0) ? nullptr : arg;
+}
+
+class OTAPreoptTest : public testing::Test {
+protected:
+    virtual void SetUp() {
+        setenv("ANDROID_LOG_TAGS", "*:f", 1);
+        android::base::InitLogging(nullptr, android::base::StderrLogger);
+    }
+
+    void verifyPackageParameters(const OTAPreoptParameters& params,
+                                 uint32_t version,
+                                 bool versioned,
+                                 const char** args) {
+        //  otapreopt target-slot [version] dexopt {DEXOPT_PARAMETERS}
+        int i = 0;
+        if (version > 2 || (version == 2 && versioned)) {
+            i += 4;
+        } else {
+            i += 3;
+        }
+        ASSERT_STREQ(params.target_slot.c_str(), args[1]);
+        ASSERT_STREQ(params.apk_path, args[i++]);
+        ASSERT_EQ(params.uid, static_cast<uid_t>(atoi(args[i++])));
+        ASSERT_STREQ(params.pkgName, args[i++]);
+        ASSERT_STREQ(params.instruction_set, args[i++]);
+        ASSERT_EQ(params.dexopt_needed, atoi(args[i++]));
+        ASSERT_STREQ(params.oat_dir, args[i++]);
+        ASSERT_EQ(params.dexopt_flags, atoi(args[i++]));
+        ASSERT_STREQ(params.compiler_filter, args[i++]);
+        ASSERT_STREQ(params.volume_uuid, ParseNull(args[i++]));
+        ASSERT_STREQ(params.shared_libraries, ParseNull(args[i++]));
+        if (version > 1) {
+            ASSERT_STREQ(params.se_info, ParseNull(args[i++]));
+        } else {
+            ASSERT_EQ(params.se_info, nullptr);
+        }
+        if (version > 2) {
+            ASSERT_EQ(params.downgrade, ParseBool(args[i++]));
+        } else {
+            ASSERT_FALSE(params.downgrade);
+        }
+        if (version > 3) {
+            ASSERT_EQ(params.target_sdk_version, atoi(args[i++]));
+        } else {
+             ASSERT_EQ(params.target_sdk_version, 0);
+        }
+        if (version > 4) {
+            ASSERT_STREQ(params.profile_name, ParseNull(args[i++]));
+        } else {
+            ASSERT_STREQ(params.profile_name, "primary.prof");
+        }
+        if (version > 5) {
+            ASSERT_STREQ(params.dex_metadata_path, ParseNull(args[i++]));
+        } else {
+            ASSERT_EQ(params.dex_metadata_path, nullptr);
+        }
+        if (version > 6) {
+            ASSERT_STREQ(params.compilation_reason, ParseNull(args[i++]));
+        } else {
+            ASSERT_STREQ(params.compilation_reason, "ab-ota");
+        }
+    }
+
+    const char* getVersionCStr(uint32_t version) {
+        switch (version) {
+            case 1: return "1";
+            case 2: return "2";
+            case 3: return "3";
+            case 4: return "4";
+            case 5: return "5";
+            case 6: return "6";
+            case 7: return "7";
+        }
+        return nullptr;
+    }
+
+    std::vector<const char*> getArgs(uint32_t version, bool versioned) {
+        std::vector<const char*> args;
+        args.push_back("otapreopt");  // "otapreopt"
+        args.push_back("a");  // slot
+        if (versioned) {
+            args.push_back(getVersionCStr(version));
+        }
+        args.push_back("dexopt");  // "dexopt"
+        args.push_back("foo.apk");  // apk_path
+        args.push_back("123");  // uid
+        args.push_back("pkgname");  // pkg
+        args.push_back("arm");  // isa
+        args.push_back("1");  // dexopt_needed (DEX2OAT_FROM_SCRATCH)
+        args.push_back("oat_dir");  // oat_dir
+        args.push_back("0");  // dexopt_flags
+        args.push_back("speed");  // filter
+        args.push_back("!");  // volume
+        args.push_back("shared.lib");  // libs
+
+        if (version > 1) {
+            args.push_back("!");  // seinfo
+        }
+        if (version > 2) {
+            args.push_back("true");  // downgrade
+        }
+        if (version > 3) {
+            args.push_back("28");  // sdk_version
+        }
+        if (version > 4) {
+            args.push_back("split_a.prof");  // profile_name
+        }
+        if (version > 5) {
+            args.push_back("dex_metadata.dm");  // dex_metadata_path
+        }
+        if (version > 6) {
+            args.push_back("ab-ota-test");  // compilation_reason
+        }
+        args.push_back(nullptr);  // we have to end with null.
+        return args;
+    }
+
+    void VerifyReadArguments(uint32_t version, bool versioned) {
+        OTAPreoptParameters params;
+        std::vector<const char*> args = getArgs(version, versioned);
+        ASSERT_TRUE(params.ReadArguments(args.size() - 1, args.data()));
+        verifyPackageParameters(params, version, versioned, args.data());
+    }
+};
+
+TEST_F(OTAPreoptTest, ReadArgumentsV1) {
+    VerifyReadArguments(1, false);
+}
+
+TEST_F(OTAPreoptTest, ReadArgumentsV2Unversioned) {
+    VerifyReadArguments(2, false);
+}
+
+TEST_F(OTAPreoptTest, ReadArgumentsV2) {
+    VerifyReadArguments(2, true);
+}
+
+TEST_F(OTAPreoptTest, ReadArgumentsV3) {
+    VerifyReadArguments(3, true);
+}
+
+TEST_F(OTAPreoptTest, ReadArgumentsV4) {
+    VerifyReadArguments(4, true);
+}
+
+TEST_F(OTAPreoptTest, ReadArgumentsV5) {
+    VerifyReadArguments(5, true);
+}
+
+TEST_F(OTAPreoptTest, ReadArgumentsV6) {
+    VerifyReadArguments(6, true);
+}
+
+TEST_F(OTAPreoptTest, ReadArgumentsV7) {
+    VerifyReadArguments(7, true);
+}
+
+TEST_F(OTAPreoptTest, ReadArgumentsFailToManyArgs) {
+    OTAPreoptParameters params;
+    std::vector<const char*> args = getArgs(5, true);
+    args[2] = "3";  // pretend it's version 3. It should fail since there are too many args.
+    ASSERT_FALSE(params.ReadArguments(args.size() - 1, args.data()));
+}
+
+TEST_F(OTAPreoptTest, ReadArgumentsFailInsufficientArgs) {
+    OTAPreoptParameters params;
+    std::vector<const char*> args = getArgs(4, true);
+    args[2] = "5";  // pretend it's version 5. It should fail since there are insufficient args.
+    ASSERT_FALSE(params.ReadArguments(args.size() - 1, args.data()));
+}
+
+TEST_F(OTAPreoptTest, ReadArgumentsFailInvalidDexopt) {
+    OTAPreoptParameters params;
+    std::vector<const char*> args = getArgs(4, true);
+    args[3] = "dexopt-invalid";
+    ASSERT_FALSE(params.ReadArguments(args.size() - 1, args.data()));
+}
+
+TEST_F(OTAPreoptTest, ReadArgumentsFailInvalidSlot) {
+    OTAPreoptParameters params;
+    std::vector<const char*> args = getArgs(3, true);
+    args[1] = "invalid-slot???";
+    ASSERT_FALSE(params.ReadArguments(args.size() - 1, args.data()));
+}
+
+}  // namespace installd
+}  // namespace android
diff --git a/cmds/installd/tests/installd_service_test.cpp b/cmds/installd/tests/installd_service_test.cpp
index ca812bd..a5af5d7 100644
--- a/cmds/installd/tests/installd_service_test.cpp
+++ b/cmds/installd/tests/installd_service_test.cpp
@@ -14,6 +14,7 @@
  * limitations under the License.
  */
 
+#include <sstream>
 #include <stdlib.h>
 #include <string.h>
 #include <sys/statvfs.h>
@@ -56,16 +57,19 @@
     return create_cache_path_default(path, src, instruction_set);
 }
 
+static std::string get_full_path(const char* path) {
+    return StringPrintf("/data/local/tmp/user/0/%s", path);
+}
+
 static void mkdir(const char* path, uid_t owner, gid_t group, mode_t mode) {
-    const char* fullPath = StringPrintf("/data/local/tmp/user/0/%s", path).c_str();
-    ::mkdir(fullPath, mode);
-    ::chown(fullPath, owner, group);
-    ::chmod(fullPath, mode);
+    const std::string fullPath = get_full_path(path);
+    ::mkdir(fullPath.c_str(), mode);
+    ::chown(fullPath.c_str(), owner, group);
+    ::chmod(fullPath.c_str(), mode);
 }
 
 static void touch(const char* path, uid_t owner, gid_t group, mode_t mode) {
-    int fd = ::open(StringPrintf("/data/local/tmp/user/0/%s", path).c_str(),
-            O_RDWR | O_CREAT, mode);
+    int fd = ::open(get_full_path(path).c_str(), O_RDWR | O_CREAT, mode);
     ::fchown(fd, owner, group);
     ::fchmod(fd, mode);
     ::close(fd);
@@ -73,13 +77,13 @@
 
 static int stat_gid(const char* path) {
     struct stat buf;
-    ::stat(StringPrintf("/data/local/tmp/user/0/%s", path).c_str(), &buf);
+    ::stat(get_full_path(path).c_str(), &buf);
     return buf.st_gid;
 }
 
 static int stat_mode(const char* path) {
     struct stat buf;
-    ::stat(StringPrintf("/data/local/tmp/user/0/%s", path).c_str(), &buf);
+    ::stat(get_full_path(path).c_str(), &buf);
     return buf.st_mode & (S_IRWXU | S_IRWXG | S_IRWXO | S_ISGID);
 }
 
@@ -149,6 +153,69 @@
     EXPECT_EQ(10000, stat_gid("com.example/bar/file"));
 }
 
+TEST_F(ServiceTest, HashSecondaryDex) {
+    LOG(INFO) << "HashSecondaryDex";
+
+    mkdir("com.example", 10000, 10000, 0700);
+    mkdir("com.example/foo", 10000, 10000, 0700);
+    touch("com.example/foo/file", 10000, 20000, 0700);
+
+    std::vector<uint8_t> result;
+    std::string dexPath = get_full_path("com.example/foo/file");
+    EXPECT_TRUE(service->hashSecondaryDexFile(
+        dexPath, "com.example", 10000, testUuid, FLAG_STORAGE_CE, &result).isOk());
+
+    EXPECT_EQ(result.size(), 32U);
+
+    std::ostringstream output;
+    output << std::hex << std::setfill('0');
+    for (auto b : result) {
+        output << std::setw(2) << +b;
+    }
+
+    // This is the SHA256 of an empty string (sha256sum /dev/null)
+    EXPECT_EQ(output.str(), "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855");
+}
+
+TEST_F(ServiceTest, HashSecondaryDex_NoSuch) {
+    LOG(INFO) << "HashSecondaryDex_NoSuch";
+
+    std::vector<uint8_t> result;
+    std::string dexPath = get_full_path("com.example/foo/file");
+    EXPECT_TRUE(service->hashSecondaryDexFile(
+        dexPath, "com.example", 10000, testUuid, FLAG_STORAGE_CE, &result).isOk());
+
+    EXPECT_EQ(result.size(), 0U);
+}
+
+TEST_F(ServiceTest, HashSecondaryDex_Unreadable) {
+    LOG(INFO) << "HashSecondaryDex_Unreadable";
+
+    mkdir("com.example", 10000, 10000, 0700);
+    mkdir("com.example/foo", 10000, 10000, 0700);
+    touch("com.example/foo/file", 10000, 20000, 0300);
+
+    std::vector<uint8_t> result;
+    std::string dexPath = get_full_path("com.example/foo/file");
+    EXPECT_TRUE(service->hashSecondaryDexFile(
+        dexPath, "com.example", 10000, testUuid, FLAG_STORAGE_CE, &result).isOk());
+
+    EXPECT_EQ(result.size(), 0U);
+}
+
+TEST_F(ServiceTest, HashSecondaryDex_WrongApp) {
+    LOG(INFO) << "HashSecondaryDex_WrongApp";
+
+    mkdir("com.example", 10000, 10000, 0700);
+    mkdir("com.example/foo", 10000, 10000, 0700);
+    touch("com.example/foo/file", 10000, 20000, 0700);
+
+    std::vector<uint8_t> result;
+    std::string dexPath = get_full_path("com.example/foo/file");
+    EXPECT_FALSE(service->hashSecondaryDexFile(
+        dexPath, "com.wrong", 10000, testUuid, FLAG_STORAGE_CE, &result).isOk());
+}
+
 TEST_F(ServiceTest, CalculateOat) {
     char buf[PKG_PATH_MAX];
 
diff --git a/cmds/installd/tests/installd_utils_test.cpp b/cmds/installd/tests/installd_utils_test.cpp
index 49da85d..bbff6fb 100644
--- a/cmds/installd/tests/installd_utils_test.cpp
+++ b/cmds/installd/tests/installd_utils_test.cpp
@@ -337,34 +337,50 @@
 }
 
 TEST_F(UtilsTest, CreatePrimaryCurrentProfile) {
-    std::string expected =
+    std::string expected_base =
         create_primary_current_profile_package_dir_path(0, "com.example") + "/primary.prof";
-    EXPECT_EQ(expected,
-            create_current_profile_path(/*user*/0, "com.example", /*is_secondary*/false));
+    EXPECT_EQ(expected_base,
+            create_current_profile_path(/*user*/0, "com.example", "primary.prof",
+                    /*is_secondary*/false));
+
+    std::string expected_split =
+        create_primary_current_profile_package_dir_path(0, "com.example") + "/split.prof";
+    EXPECT_EQ(expected_split,
+            create_current_profile_path(/*user*/0, "com.example", "split.prof",
+                    /*is_secondary*/false));
 }
 
 TEST_F(UtilsTest, CreatePrimaryReferenceProfile) {
-    std::string expected =
+    std::string expected_base =
         create_primary_reference_profile_package_dir_path("com.example") + "/primary.prof";
-    EXPECT_EQ(expected,
-            create_reference_profile_path("com.example", /*is_secondary*/false));
+    EXPECT_EQ(expected_base,
+            create_reference_profile_path("com.example", "primary.prof", /*is_secondary*/false));
+
+    std::string expected_split =
+        create_primary_reference_profile_package_dir_path("com.example") + "/split.prof";
+    EXPECT_EQ(expected_split,
+            create_reference_profile_path("com.example", "split.prof", /*is_secondary*/false));
 }
 
 TEST_F(UtilsTest, CreateProfileSnapshot) {
-    std::string expected =
+    std::string expected_base =
         create_primary_reference_profile_package_dir_path("com.example") + "/primary.prof.snapshot";
-    EXPECT_EQ(expected, create_snapshot_profile_path("com.example", "base.apk"));
+    EXPECT_EQ(expected_base, create_snapshot_profile_path("com.example", "primary.prof"));
+
+    std::string expected_split =
+        create_primary_reference_profile_package_dir_path("com.example") + "/split.prof.snapshot";
+    EXPECT_EQ(expected_split, create_snapshot_profile_path("com.example", "split.prof"));
 }
 
 TEST_F(UtilsTest, CreateSecondaryCurrentProfile) {
     EXPECT_EQ("/data/user/0/com.example/oat/secondary.dex.cur.prof",
-            create_current_profile_path(/*user*/0,
+            create_current_profile_path(/*user*/0, "com.example",
                     "/data/user/0/com.example/secondary.dex", /*is_secondary*/true));
 }
 
 TEST_F(UtilsTest, CreateSecondaryReferenceProfile) {
     EXPECT_EQ("/data/user/0/com.example/oat/secondary.dex.prof",
-            create_reference_profile_path(
+            create_reference_profile_path("com.example",
                     "/data/user/0/com.example/secondary.dex", /*is_secondary*/true));
 }
 
diff --git a/cmds/installd/utils.cpp b/cmds/installd/utils.cpp
index 61c9c8f..a8c32ed 100644
--- a/cmds/installd/utils.cpp
+++ b/cmds/installd/utils.cpp
@@ -26,7 +26,9 @@
 #include <sys/statvfs.h>
 
 #include <android-base/logging.h>
+#include <android-base/strings.h>
 #include <android-base/stringprintf.h>
+#include <android-base/unique_fd.h>
 #include <cutils/fs.h>
 #include <cutils/properties.h>
 #include <log/log.h>
@@ -40,7 +42,9 @@
 
 #define DEBUG_XATTRS 0
 
+using android::base::EndsWith;
 using android::base::StringPrintf;
+using android::base::unique_fd;
 
 namespace android {
 namespace installd {
@@ -235,7 +239,6 @@
 // Keep profile paths in sync with ActivityThread and LoadedApk.
 const std::string PROFILE_EXT = ".prof";
 const std::string CURRENT_PROFILE_EXT = ".cur";
-const std::string PRIMARY_PROFILE_NAME = "primary" + PROFILE_EXT;
 const std::string SNAPSHOT_PROFILE_EXT = ".snapshot";
 
 // Gets the parent directory and the file name for the given secondary dex path.
@@ -256,8 +259,8 @@
    return true;
 }
 
-std::string create_current_profile_path(userid_t user, const std::string& location,
-        bool is_secondary_dex) {
+std::string create_current_profile_path(userid_t user, const std::string& package_name,
+        const std::string& location, bool is_secondary_dex) {
     if (is_secondary_dex) {
         // Secondary dex current profiles are stored next to the dex files under the oat folder.
         std::string dex_dir;
@@ -269,12 +272,14 @@
                 PROFILE_EXT.c_str());
     } else {
         // Profiles for primary apks are under /data/misc/profiles/cur.
-        std::string profile_dir = create_primary_current_profile_package_dir_path(user, location);
-        return StringPrintf("%s/%s", profile_dir.c_str(), PRIMARY_PROFILE_NAME.c_str());
+        std::string profile_dir = create_primary_current_profile_package_dir_path(
+                user, package_name);
+        return StringPrintf("%s/%s", profile_dir.c_str(), location.c_str());
     }
 }
 
-std::string create_reference_profile_path(const std::string& location, bool is_secondary_dex) {
+std::string create_reference_profile_path(const std::string& package_name,
+        const std::string& location, bool is_secondary_dex) {
     if (is_secondary_dex) {
         // Secondary dex reference profiles are stored next to the dex files under the oat folder.
         std::string dex_dir;
@@ -285,16 +290,15 @@
                 dex_dir.c_str(), dex_name.c_str(), PROFILE_EXT.c_str());
     } else {
         // Reference profiles for primary apks are stored in /data/misc/profile/ref.
-        std::string profile_dir = create_primary_reference_profile_package_dir_path(location);
-        return StringPrintf("%s/%s", profile_dir.c_str(), PRIMARY_PROFILE_NAME.c_str());
+        std::string profile_dir = create_primary_reference_profile_package_dir_path(package_name);
+        return StringPrintf("%s/%s", profile_dir.c_str(), location.c_str());
     }
 }
 
 std::string create_snapshot_profile_path(const std::string& package,
-        const std::string& code_path ATTRIBUTE_UNUSED) {
-    // TODD(calin): code_path is ignored for now. It will be used when each split gets its own
-    // profile file.
-    std::string ref_profile = create_reference_profile_path(package, /*is_secondary_dex*/ false);
+        const std::string& profile_name) {
+    std::string ref_profile = create_reference_profile_path(package, profile_name,
+            /*is_secondary_dex*/ false);
     return ref_profile + SNAPSHOT_PROFILE_EXT;
 }
 
@@ -984,5 +988,75 @@
     return 0;
 }
 
+// Collect all non empty profiles from the given directory and puts then into profile_paths.
+// The profiles are identified based on PROFILE_EXT extension.
+// If a subdirectory or profile file cannot be opened the method logs a warning and moves on.
+// It returns true if there were no errors at all, and false otherwise.
+static bool collect_profiles(DIR* d,
+                             const std::string& current_path,
+                             std::vector<std::string>* profiles_paths) {
+    int32_t dir_fd = dirfd(d);
+    if (dir_fd < 0) {
+        return false;
+    }
+
+    bool result = true;
+    struct dirent* dir_entry;
+    while ((dir_entry = readdir(d))) {
+        std::string name = dir_entry->d_name;
+        std::string local_path = current_path + "/" + name;
+
+        if (dir_entry->d_type == DT_REG) {
+            // Check if this is a non empty profile file.
+            if (EndsWith(name, PROFILE_EXT)) {
+                struct stat st;
+                if (stat(local_path.c_str(), &st) != 0) {
+                    PLOG(WARNING) << "Cannot stat local path " << local_path;
+                    result = false;
+                    continue;
+                } else if (st.st_size > 0) {
+                    profiles_paths->push_back(local_path);
+                }
+            }
+        } else if (dir_entry->d_type == DT_DIR) {
+            // always skip "." and ".."
+            if (name == "." || name == "..") {
+                continue;
+            }
+
+            unique_fd subdir_fd(openat(dir_fd, name.c_str(),
+                    O_RDONLY | O_DIRECTORY | O_NOFOLLOW | O_CLOEXEC));
+            if (subdir_fd < 0) {
+                PLOG(WARNING) << "Could not open dir path " << local_path;
+                result = false;
+                continue;
+            }
+
+            DIR* subdir = fdopendir(subdir_fd);
+            if (subdir == NULL) {
+                PLOG(WARNING) << "Could not open dir path " << local_path;
+                result = false;
+                continue;
+            }
+            bool new_result = collect_profiles(subdir, local_path, profiles_paths);
+            result = result && new_result;
+            if (closedir(subdir) != 0) {
+                PLOG(WARNING) << "Could not close dir path " << local_path;
+            }
+        }
+    }
+
+    return result;
+}
+
+bool collect_profiles(std::vector<std::string>* profiles_paths) {
+    DIR* d = opendir(android_profiles_dir.c_str());
+    if (d == NULL) {
+        return false;
+    } else {
+        return collect_profiles(d, android_profiles_dir, profiles_paths);
+    }
+}
+
 }  // namespace installd
 }  // namespace android
diff --git a/cmds/installd/utils.h b/cmds/installd/utils.h
index 5391061..b74073c 100644
--- a/cmds/installd/utils.h
+++ b/cmds/installd/utils.h
@@ -80,10 +80,17 @@
 std::string create_primary_reference_profile_package_dir_path(const std::string& package_name);
 
 std::string create_current_profile_path(
-        userid_t user, const std::string& package_name, bool is_secondary_dex);
+        userid_t user,
+        const std::string& package_name,
+        const std::string& location,
+        bool is_secondary_dex);
 std::string create_reference_profile_path(
-        const std::string& package_name, bool is_secondary_dex);
-std::string create_snapshot_profile_path(const std::string& package, const std::string& code_path);
+        const std::string& package_name,
+        const std::string& location,
+        bool is_secondary_dex);
+std::string create_snapshot_profile_path(
+        const std::string& package,
+        const std::string& profile_name);
 
 std::vector<userid_t> get_known_users(const char* volume_uuid);
 
@@ -129,6 +136,12 @@
 int prepare_app_cache_dir(const std::string& parent, const char* name, mode_t target_mode,
         uid_t uid, gid_t gid);
 
+// Collect all non empty profiles from the global profile directory and
+// put then into profile_paths. The profiles are identified based on PROFILE_EXT extension.
+// If a subdirectory or profile file cannot be opened the method logs a warning and moves on.
+// It returns true if there were no errors at all, and false otherwise.
+bool collect_profiles(std::vector<std::string>* profiles_paths);
+
 }  // namespace installd
 }  // namespace android
 
diff --git a/libs/binder/BufferedTextOutput.cpp b/libs/binder/BufferedTextOutput.cpp
index a2443c0..30e70b0 100644
--- a/libs/binder/BufferedTextOutput.cpp
+++ b/libs/binder/BufferedTextOutput.cpp
@@ -17,11 +17,11 @@
 #include <binder/BufferedTextOutput.h>
 #include <binder/Debug.h>
 
-#include <utils/Atomic.h>
+#include <cutils/atomic.h>
+#include <cutils/threads.h>
 #include <utils/Log.h>
 #include <utils/RefBase.h>
 #include <utils/Vector.h>
-#include <cutils/threads.h>
 
 #include <private/binder/Static.h>
 
diff --git a/libs/binder/ProcessState.cpp b/libs/binder/ProcessState.cpp
index da806aa..8606129 100644
--- a/libs/binder/ProcessState.cpp
+++ b/libs/binder/ProcessState.cpp
@@ -18,12 +18,12 @@
 
 #include <binder/ProcessState.h>
 
-#include <utils/Atomic.h>
 #include <binder/BpBinder.h>
 #include <binder/IPCThreadState.h>
+#include <binder/IServiceManager.h>
+#include <cutils/atomic.h>
 #include <utils/Log.h>
 #include <utils/String8.h>
-#include <binder/IServiceManager.h>
 #include <utils/String8.h>
 #include <utils/threads.h>
 
diff --git a/libs/vr/libpdx/service_tests.cpp b/libs/vr/libpdx/service_tests.cpp
index c7412b7..e623abf 100644
--- a/libs/vr/libpdx/service_tests.cpp
+++ b/libs/vr/libpdx/service_tests.cpp
@@ -180,8 +180,8 @@
   EXPECT_EQ(kTestTid, message.GetThreadId());
   EXPECT_EQ(kTestCid, message.GetChannelId());
   EXPECT_EQ(kTestMid, message.GetMessageId());
-  EXPECT_EQ(kTestEuid, message.GetEffectiveUserId());
-  EXPECT_EQ(kTestEgid, message.GetEffectiveGroupId());
+  EXPECT_EQ((unsigned) kTestEuid, message.GetEffectiveUserId());
+  EXPECT_EQ((unsigned) kTestEgid, message.GetEffectiveGroupId());
   EXPECT_EQ(kTestOp, message.GetOp());
   EXPECT_EQ(service_, message.GetService());
   EXPECT_EQ(test_channel, message.GetChannel());
diff --git a/opengl/libagl/BufferObjectManager.cpp b/opengl/libagl/BufferObjectManager.cpp
index 6bf28ee..3d93c19 100644
--- a/opengl/libagl/BufferObjectManager.cpp
+++ b/opengl/libagl/BufferObjectManager.cpp
@@ -18,7 +18,7 @@
 #include <stddef.h>
 #include <sys/types.h>
 
-#include <utils/Atomic.h>
+#include <cutils/atomic.h>
 #include <utils/RefBase.h>
 #include <utils/KeyedVector.h>
 #include <utils/Errors.h>
diff --git a/opengl/libagl/TextureObjectManager.h b/opengl/libagl/TextureObjectManager.h
index de9e03e..9cf8771 100644
--- a/opengl/libagl/TextureObjectManager.h
+++ b/opengl/libagl/TextureObjectManager.h
@@ -21,7 +21,7 @@
 #include <stddef.h>
 #include <sys/types.h>
 
-#include <utils/Atomic.h>
+#include <cutils/atomic.h>
 #include <utils/threads.h>
 #include <utils/RefBase.h>
 #include <utils/KeyedVector.h>
diff --git a/opengl/tests/gl2_java/Android.mk b/opengl/tests/gl2_java/Android.mk
index 34f4aee..71aa5a0 100644
--- a/opengl/tests/gl2_java/Android.mk
+++ b/opengl/tests/gl2_java/Android.mk
@@ -14,5 +14,6 @@
 LOCAL_SRC_FILES := $(call all-subdir-java-files)
 
 LOCAL_PACKAGE_NAME := GL2Java
+LOCAL_SDK_VERSION := current
 
 include $(BUILD_PACKAGE)
diff --git a/opengl/tests/gl2_jni/Android.mk b/opengl/tests/gl2_jni/Android.mk
index 409bd73..af65b5f 100644
--- a/opengl/tests/gl2_jni/Android.mk
+++ b/opengl/tests/gl2_jni/Android.mk
@@ -14,6 +14,7 @@
 LOCAL_SRC_FILES := $(call all-subdir-java-files)
 
 LOCAL_PACKAGE_NAME := GL2JNI
+LOCAL_SDK_VERSION := current
 
 LOCAL_JNI_SHARED_LIBRARIES := libgl2jni
 
diff --git a/opengl/tests/gl_jni/Android.mk b/opengl/tests/gl_jni/Android.mk
index 11b4c8b..570ae2b 100644
--- a/opengl/tests/gl_jni/Android.mk
+++ b/opengl/tests/gl_jni/Android.mk
@@ -14,6 +14,7 @@
 LOCAL_SRC_FILES := $(call all-subdir-java-files)
 
 LOCAL_PACKAGE_NAME := GLJNI
+LOCAL_SDK_VERSION := current
 
 LOCAL_JNI_SHARED_LIBRARIES := libgljni
 
diff --git a/opengl/tests/gldual/Android.mk b/opengl/tests/gldual/Android.mk
index 1991ed9..dc265b6 100644
--- a/opengl/tests/gldual/Android.mk
+++ b/opengl/tests/gldual/Android.mk
@@ -14,6 +14,7 @@
 LOCAL_SRC_FILES := $(call all-subdir-java-files)
 
 LOCAL_PACKAGE_NAME := GLDual
+LOCAL_SDK_VERSION := current
 
 LOCAL_JNI_SHARED_LIBRARIES := libgldualjni
 
diff --git a/opengl/tests/lighting1709/Android.mk b/opengl/tests/lighting1709/Android.mk
index 9563e61..0047231 100644
--- a/opengl/tests/lighting1709/Android.mk
+++ b/opengl/tests/lighting1709/Android.mk
@@ -6,6 +6,7 @@
 LOCAL_SRC_FILES := $(call all-subdir-java-files)
 
 LOCAL_PACKAGE_NAME := LightingTest
+LOCAL_SDK_VERSION := current
 LOCAL_CERTIFICATE := platform
 
 include $(BUILD_PACKAGE)
diff --git a/opengl/tests/testFramerate/Android.mk b/opengl/tests/testFramerate/Android.mk
index 500abf3..2864fcf 100644
--- a/opengl/tests/testFramerate/Android.mk
+++ b/opengl/tests/testFramerate/Android.mk
@@ -15,5 +15,6 @@
 LOCAL_SRC_FILES := $(call all-subdir-java-files)
 
 LOCAL_PACKAGE_NAME := TestFramerate
+LOCAL_PRIVATE_PLATFORM_APIS := true
 
 include $(BUILD_PACKAGE)
diff --git a/opengl/tests/testPauseResume/Android.mk b/opengl/tests/testPauseResume/Android.mk
index cf8bdc3..dda5424 100644
--- a/opengl/tests/testPauseResume/Android.mk
+++ b/opengl/tests/testPauseResume/Android.mk
@@ -14,5 +14,6 @@
 LOCAL_SRC_FILES := $(call all-subdir-java-files)
 
 LOCAL_PACKAGE_NAME := TestEGL
+LOCAL_SDK_VERSION := current
 
 include $(BUILD_PACKAGE)
diff --git a/services/sensorservice/BatteryService.cpp b/services/sensorservice/BatteryService.cpp
index 330861d..d8e5b29 100644
--- a/services/sensorservice/BatteryService.cpp
+++ b/services/sensorservice/BatteryService.cpp
@@ -18,7 +18,7 @@
 #include <math.h>
 #include <sys/types.h>
 
-#include <utils/Atomic.h>
+#include <cutils/atomic.h>
 #include <utils/Errors.h>
 #include <utils/Singleton.h>
 
diff --git a/services/sensorservice/SensorDevice.cpp b/services/sensorservice/SensorDevice.cpp
index 535d0db..4daae4d 100644
--- a/services/sensorservice/SensorDevice.cpp
+++ b/services/sensorservice/SensorDevice.cpp
@@ -18,7 +18,7 @@
 
 #include <android-base/logging.h>
 #include <sensors/convert.h>
-#include <utils/Atomic.h>
+#include <cutils/atomic.h>
 #include <utils/Errors.h>
 #include <utils/Singleton.h>
 
diff --git a/services/surfaceflinger/SurfaceFlinger.h b/services/surfaceflinger/SurfaceFlinger.h
index 1b77aaf..821e15c8 100644
--- a/services/surfaceflinger/SurfaceFlinger.h
+++ b/services/surfaceflinger/SurfaceFlinger.h
@@ -28,8 +28,8 @@
  */
 
 #include <cutils/compiler.h>
+#include <cutils/atomic.h>
 
-#include <utils/Atomic.h>
 #include <utils/Errors.h>
 #include <utils/KeyedVector.h>
 #include <utils/RefBase.h>