Installd: Separate profile merging and compilation

Create a new merge_profiles command to check and merge profiles.

(Almost) always pass a profile to a dexopt pass.

Require compilation filter input to dexopt and use it for dex2oat
(except for hard overrides like safe-mode).

Bug: 27689078
Change-Id: I1257857cc15c17e2271d1261ea4cc80752270fcb
diff --git a/cmds/installd/commands.cpp b/cmds/installd/commands.cpp
index 47c78c3..c0e0214 100644
--- a/cmds/installd/commands.cpp
+++ b/cmds/installd/commands.cpp
@@ -714,7 +714,7 @@
 
 static void run_dex2oat(int zip_fd, int oat_fd, int image_fd, const char* input_file_name,
         const char* output_file_name, int swap_fd, const char *instruction_set,
-        bool vm_safe_mode, bool debuggable, bool post_bootcomplete, bool extract_only,
+        const char* compiler_filter, bool vm_safe_mode, bool debuggable, bool post_bootcomplete,
         int profile_fd) {
     static const unsigned int MAX_INSTRUCTION_SET_LEN = 7;
 
@@ -730,10 +730,6 @@
     char dex2oat_Xmx_flag[kPropertyValueMax];
     bool have_dex2oat_Xmx_flag = get_property("dalvik.vm.dex2oat-Xmx", dex2oat_Xmx_flag, NULL) > 0;
 
-    char dex2oat_compiler_filter_flag[kPropertyValueMax];
-    bool have_dex2oat_compiler_filter_flag = get_property("dalvik.vm.dex2oat-filter",
-                                                          dex2oat_compiler_filter_flag, NULL) > 0;
-
     char dex2oat_threads_buf[kPropertyValueMax];
     bool have_dex2oat_threads_flag = get_property(post_bootcomplete
                                                       ? "dalvik.vm.dex2oat-threads"
@@ -825,6 +821,10 @@
     if (have_dex2oat_Xmx_flag) {
         sprintf(dex2oat_Xmx_arg, "-Xmx%s", dex2oat_Xmx_flag);
     }
+
+    // Compute compiler filter.
+
+    bool have_dex2oat_compiler_filter_flag;
     if (skip_compilation) {
         strcpy(dex2oat_compiler_filter_arg, "--compiler-filter=verify-none");
         have_dex2oat_compiler_filter_flag = true;
@@ -832,13 +832,20 @@
     } else if (vm_safe_mode) {
         strcpy(dex2oat_compiler_filter_arg, "--compiler-filter=interpret-only");
         have_dex2oat_compiler_filter_flag = true;
-    } else if (extract_only) {
-        // Temporarily make extract-only mean interpret-only, so extracted files will be verified.
-        // b/26833007
-        strcpy(dex2oat_compiler_filter_arg, "--compiler-filter=interpret-only");
+    } else if (compiler_filter != nullptr &&
+            strlen(compiler_filter) + strlen("--compiler-filter=") <
+                    arraysize(dex2oat_compiler_filter_arg)) {
+        sprintf(dex2oat_compiler_filter_arg, "--compiler-filter=%s", compiler_filter);
         have_dex2oat_compiler_filter_flag = true;
-    } else if (have_dex2oat_compiler_filter_flag) {
-        sprintf(dex2oat_compiler_filter_arg, "--compiler-filter=%s", dex2oat_compiler_filter_flag);
+    } else {
+        char dex2oat_compiler_filter_flag[kPropertyValueMax];
+        have_dex2oat_compiler_filter_flag = get_property("dalvik.vm.dex2oat-filter",
+                                                         dex2oat_compiler_filter_flag, NULL) > 0;
+        if (have_dex2oat_compiler_filter_flag) {
+            sprintf(dex2oat_compiler_filter_arg,
+                    "--compiler-filter=%s",
+                    dex2oat_compiler_filter_flag);
+        }
     }
 
     // Check whether all apps should be compiled debuggable.
@@ -1283,9 +1290,14 @@
     return true;
 }
 
+// TODO: Consider returning error codes.
+bool merge_profiles(uid_t uid, const char *pkgname) {
+    return analyse_profiles(uid, pkgname);
+}
+
 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* volume_uuid ATTRIBUTE_UNUSED, bool use_profiles)
+           int dexopt_needed, const char* oat_dir, int dexopt_flags, const char* compiler_filter,
+           const char* volume_uuid ATTRIBUTE_UNUSED)
 {
     struct utimbuf ut;
     struct stat input_stat;
@@ -1300,29 +1312,18 @@
     bool vm_safe_mode = (dexopt_flags & DEXOPT_SAFEMODE) != 0;
     bool debuggable = (dexopt_flags & DEXOPT_DEBUGGABLE) != 0;
     bool boot_complete = (dexopt_flags & DEXOPT_BOOTCOMPLETE) != 0;
-    bool extract_only = (dexopt_flags & DEXOPT_EXTRACTONLY) != 0;
+    bool profile_guided = (dexopt_flags & DEXOPT_PROFILE_GUIDED) != 0;
+
+    CHECK(pkgname != nullptr);
+    CHECK(pkgname[0] != 0);
+
     fd_t reference_profile_fd = -1;
-
-    if (is_public && use_profiles) {
-        // We should not give public access to apks compiled with profile information.
-        // Log an error and return early if are asked to do so.
-        ALOGE("use_profiles should not be used with is_public.");
-        return -1;
-    }
-
-    if (use_profiles) {
-        if (analyse_profiles(uid, pkgname)) {
-            // Open again reference profile in read only mode as dex2oat does not get write
-            // permissions.
-            reference_profile_fd = open_reference_profile(uid, pkgname, /*read_write*/ false);
-            if (reference_profile_fd == -1) {
-                PLOG(WARNING) << "Couldn't open reference profile in read only mode " << pkgname;
-                exit(72);
-            }
-        } else {
-            // No need to (re)compile. Return early.
-            return 0;
-        }
+    // Public apps should not be compiled with profile information ever. Same goes for the special
+    // package '*' used for the system server.
+    if (!is_public && pkgname[0] != '*') {
+        // Open reference profile in read only mode as dex2oat does not get write permissions.
+        reference_profile_fd = open_reference_profile(uid, pkgname, /*read_write*/ false);
+        // Note: it's OK to not find a profile here.
     }
 
     if ((dexopt_flags & ~DEXOPT_MASK) != 0) {
@@ -1397,7 +1398,9 @@
       char app_image_format[kPropertyValueMax];
       bool have_app_image_format =
               get_property("dalvik.vm.appimageformat", app_image_format, NULL) > 0;
-      if (!extract_only && have_app_image_format) {
+      // Use app images only if it is enabled (by a set image format) and we are compiling
+      // profile-guided (so the app image doesn't conservatively contain all classes).
+      if (profile_guided && have_app_image_format) {
           // Recreate is false since we want to avoid deleting the image in case dex2oat decides to
           // not compile anything.
           image_fd = open_output_file(image_path, /*recreate*/false);
@@ -1440,7 +1443,7 @@
                 input_file_name++;
             }
             run_dex2oat(input_fd, out_fd, image_fd, input_file_name, out_path, swap_fd,
-                        instruction_set, vm_safe_mode, debuggable, boot_complete, extract_only,
+                        instruction_set, compiler_filter, vm_safe_mode, debuggable, boot_complete,
                         reference_profile_fd);
         } else {
             ALOGE("Invalid dexopt needed: %d\n", dexopt_needed);
diff --git a/cmds/installd/commands.h b/cmds/installd/commands.h
index b473e3e..70cb410 100644
--- a/cmds/installd/commands.h
+++ b/cmds/installd/commands.h
@@ -48,9 +48,12 @@
 int delete_user(const char *uuid, userid_t userid);
 int rm_dex(const char *path, const char *instruction_set);
 int free_cache(const char *uuid, int64_t free_size);
+
+bool merge_profiles(uid_t uid, const char *pkgname);
+
 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* volume_uuid, bool use_profiles);
+           int dexopt_needed, const char* oat_dir, int dexopt_flags, const char* compiler_filter,
+           const char* volume_uuid);
 int mark_boot_complete(const char *instruction_set);
 int linklib(const char* uuid, const char* pkgname, const char* asecLibDir, int userId);
 int idmap(const char *target_path, const char *overlay_path, uid_t uid);
diff --git a/cmds/installd/installd.cpp b/cmds/installd/installd.cpp
index 2bf27a2..dc3418a 100644
--- a/cmds/installd/installd.cpp
+++ b/cmds/installd/installd.cpp
@@ -258,10 +258,27 @@
     if ((dexopt_flags & DEXOPT_OTA) != 0) {
       return do_ota_dexopt(arg, reply);
     }
-    /* apk_path, uid, pkgname, instruction_set, dexopt_needed, oat_dir, dexopt_flags, volume_uuid,
-            use_profiles */
-    return dexopt(arg[0], atoi(arg[1]), arg[2], arg[3], atoi(arg[4]),
-                  arg[5], dexopt_flags, parse_null(arg[7]), (atoi(arg[8]) == 0 ? false : true));
+    return dexopt(arg[0],                      // apk_path
+                  atoi(arg[1]),                // uid
+                  arg[2],                      // pkgname
+                  arg[3],                      // instruction_set
+                  atoi(arg[4]),                // dexopt_needed
+                  arg[5],                      // oat_dir
+                  dexopt_flags,
+                  arg[7],                      // compiler_filter
+                  parse_null(arg[8]));         // volume_uuid
+}
+
+static int do_merge_profiles(char **arg, char reply[REPLY_MAX])
+{
+    uid_t uid = static_cast<uid_t>(atoi(arg[0]));
+    const char* pkgname = arg[1];
+    if (merge_profiles(uid, pkgname)) {
+        strncpy(reply, "true", REPLY_MAX);
+    } else {
+        strncpy(reply, "false", REPLY_MAX);
+    }
+    return 0;
 }
 
 static int do_mark_boot_complete(char **arg, char reply[REPLY_MAX] ATTRIBUTE_UNUSED)
@@ -388,6 +405,7 @@
     { "rmprofiles",           1, do_rm_profiles },
     { "linkfile",             3, do_link_file },
     { "move_ab",              3, do_move_ab },
+    { "merge_profiles",       2, do_merge_profiles },
 };
 
 static int readx(int s, void *_buf, int count)
diff --git a/cmds/installd/installd_constants.h b/cmds/installd/installd_constants.h
index 8f6e928..8513695 100644
--- a/cmds/installd/installd_constants.h
+++ b/cmds/installd/installd_constants.h
@@ -75,12 +75,12 @@
  * IMPORTANT: These values are passed from Java code. Keep them in sync with
  * frameworks/base/services/core/java/com/android/server/pm/Installer.java
  ***************************************************************************/
-constexpr int DEXOPT_PUBLIC       = 1 << 1;
-constexpr int DEXOPT_SAFEMODE     = 1 << 2;
-constexpr int DEXOPT_DEBUGGABLE   = 1 << 3;
-constexpr int DEXOPT_BOOTCOMPLETE = 1 << 4;
-constexpr int DEXOPT_EXTRACTONLY  = 1 << 5;
-constexpr int DEXOPT_OTA          = 1 << 6;
+constexpr int DEXOPT_PUBLIC         = 1 << 1;
+constexpr int DEXOPT_SAFEMODE       = 1 << 2;
+constexpr int DEXOPT_DEBUGGABLE     = 1 << 3;
+constexpr int DEXOPT_BOOTCOMPLETE   = 1 << 4;
+constexpr int DEXOPT_PROFILE_GUIDED = 1 << 5;
+constexpr int DEXOPT_OTA            = 1 << 6;
 
 /* all known values for dexopt flags */
 constexpr int DEXOPT_MASK =
@@ -88,7 +88,7 @@
     | DEXOPT_SAFEMODE
     | DEXOPT_DEBUGGABLE
     | DEXOPT_BOOTCOMPLETE
-    | DEXOPT_EXTRACTONLY
+    | DEXOPT_PROFILE_GUIDED
     | DEXOPT_OTA;
 
 #define ARRAY_SIZE(a) (sizeof(a) / sizeof(*(a)))
diff --git a/cmds/installd/otapreopt.cpp b/cmds/installd/otapreopt.cpp
index 89a4225..245694a 100644
--- a/cmds/installd/otapreopt.cpp
+++ b/cmds/installd/otapreopt.cpp
@@ -357,17 +357,15 @@
     }
 
     int RunPreopt() {
-        /* apk_path, uid, pkgname, instruction_set, dexopt_needed, oat_dir, dexopt_flags,
-           volume_uuid, use_profiles */
-        int ret = dexopt(package_parameters_[0],
-                atoi(package_parameters_[1]),
-                package_parameters_[2],
-                package_parameters_[3],
-                atoi(package_parameters_[4]),
-                package_parameters_[5],
-                atoi(package_parameters_[6]),
-                ParseNull(package_parameters_[7]),
-                (atoi(package_parameters_[8]) == 0 ? false : true));
+        int ret = dexopt(package_parameters_[0],          // apk_path
+                atoi(package_parameters_[1]),             // uid
+                package_parameters_[2],                   // pkgname
+                package_parameters_[3],                   // instruction_set
+                atoi(package_parameters_[4]),             // dexopt_needed
+                package_parameters_[5],                   // oat_dir
+                atoi(package_parameters_[6]),             // dexopt_flags
+                package_parameters_[7],                   // compiler_filter
+                ParseNull(package_parameters_[8]));       // volume_uuid
         return ret;
     }