Implement another OdrDexopt to use new CompOS API

The old OdrDexoptCompilationOS (now renamed) is still active (currently
controlled by a constant in the code) until everything is in place.

The new OdrDexoptCompilationOS put DexoptBcpExtArgs or
DexoptSystemServerArgs into ExtendableParcelable with TaskType.
The ExtendableParcelable and TaskType makes it easy for ART to add other
task types to run in CompOS.

Bug: 193668901
Test: atest ComposHostTestCases (with other changes)

Change-Id: I7fc76942e38c50c12f5819e5114e48911f77d08b
diff --git a/artd/binder/private/com/android/art/DexoptBcpExtArgs.aidl b/artd/binder/private/com/android/art/DexoptBcpExtArgs.aidl
index 43fd922..0ab2732 100644
--- a/artd/binder/private/com/android/art/DexoptBcpExtArgs.aidl
+++ b/artd/binder/private/com/android/art/DexoptBcpExtArgs.aidl
@@ -62,4 +62,5 @@
     // SECURITY: Computational resource should not affect the compilation results.
     int[] cpuSet;
     int threads;
+    int timeoutSecs = 0;
 }
diff --git a/artd/binder/private/com/android/art/DexoptSystemServerArgs.aidl b/artd/binder/private/com/android/art/DexoptSystemServerArgs.aidl
index b351c67..21c5052 100644
--- a/artd/binder/private/com/android/art/DexoptSystemServerArgs.aidl
+++ b/artd/binder/private/com/android/art/DexoptSystemServerArgs.aidl
@@ -75,4 +75,5 @@
     // SECURITY: Computational resource should not affect the compilation results.
     int[] cpuSet;
     int threads;
+    int timeoutSecs = 0;
 }
diff --git a/artd/binder/private/com/android/art/ExtendableParcelable.aidl b/artd/binder/private/com/android/art/ExtendableParcelable.aidl
new file mode 100644
index 0000000..46a2c2e
--- /dev/null
+++ b/artd/binder/private/com/android/art/ExtendableParcelable.aidl
@@ -0,0 +1,24 @@
+/*
+ * Copyright (C) 2021 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.
+ */
+
+package com.android.art;
+
+import com.android.art.TaskType;
+
+parcelable ExtendableParcelable {
+    TaskType taskType = TaskType.UNSUPPORTED;
+    ParcelableHolder ext;
+}
diff --git a/artd/binder/private/com/android/art/TaskType.aidl b/artd/binder/private/com/android/art/TaskType.aidl
new file mode 100644
index 0000000..cdb79e7
--- /dev/null
+++ b/artd/binder/private/com/android/art/TaskType.aidl
@@ -0,0 +1,24 @@
+/*
+ * Copyright (C) 2021 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.
+ */
+
+package com.android.art;
+
+@Backing(type="int")
+enum TaskType {
+    UNSUPPORTED = 0,
+    DEXOPT_BCP_EXTENSION = 1,
+    DEXOPT_SYSTEM_SERVER = 2,
+}
diff --git a/dex2oat/dex2oat.cc b/dex2oat/dex2oat.cc
index 6f01329..7f71d38 100644
--- a/dex2oat/dex2oat.cc
+++ b/dex2oat/dex2oat.cc
@@ -115,6 +115,8 @@
     kNoFailure = 0,          // No failure, execution completed successfully.
     kOther = 1,              // Some other not closer specified error occurred.
     kCreateRuntime = 2,      // Dex2oat failed creating a runtime.
+
+    // 128-255 are reserved and won't returned by dex2oat.
   };
 }  // namespace dex2oat
 
diff --git a/odrefresh/Android.bp b/odrefresh/Android.bp
index a8ec76c..d9cd08c 100644
--- a/odrefresh/Android.bp
+++ b/odrefresh/Android.bp
@@ -45,6 +45,9 @@
         "artd-private-aidl-ndk",
         "libartpalette",
         "libbase",
+        "libbinder_ndk",
+        "libcompos_client",
+        "libdexopt",
         "liblog",
     ],
     static_libs: ["libtinyxml2"],
@@ -106,7 +109,6 @@
     shared_libs: [
         "libart",
         "libartbase",
-        "libdexopt",
     ],
     apex_available: [
         "com.android.art",
@@ -212,7 +214,6 @@
     ],
     shared_libs: [
         "libdexfile",
-        "libdexopt",
     ],
     test_config_template: "art_odrefresh_tests.xml",
 }
diff --git a/odrefresh/odr_dexopt.cc b/odrefresh/odr_dexopt.cc
index 99e0814..59abf56 100644
--- a/odrefresh/odr_dexopt.cc
+++ b/odrefresh/odr_dexopt.cc
@@ -22,22 +22,30 @@
 #include <android-base/result.h>
 #include "android-base/strings.h"
 #include "exec_utils.h"
+#include "libcompos_client.h"
+#include "libdexopt.h"
 #include "log/log.h"
 #include "odr_config.h"
-#include "libdexopt.h"
 
 #include "aidl/com/android/art/DexoptBcpExtArgs.h"
 #include "aidl/com/android/art/DexoptSystemServerArgs.h"
+#include "aidl/com/android/art/ExtendableParcelable.h"
+#include "aidl/com/android/art/TaskType.h"
 
 namespace art {
 namespace odrefresh {
 
 using aidl::com::android::art::DexoptBcpExtArgs;
 using aidl::com::android::art::DexoptSystemServerArgs;
+using aidl::com::android::art::ExtendableParcelable;
+using aidl::com::android::art::TaskType;
 using android::base::Result;
 
 namespace {
 
+// TODO(193668901): Once migrated to the new API, remove the old implementation.
+constexpr bool USE_NEW_COMPILIATION_OS_API = false;
+
 int ExecAndReturnCode(ExecUtils* exec_utils,
                       std::vector<std::string>& cmdline,
                       time_t timeout_secs,
@@ -48,6 +56,24 @@
   return exec_utils->ExecAndReturnCode(cmdline, timeout_secs, timed_out, error_msg);
 }
 
+bool insertFd(/*inout*/ std::vector<int>& vec, int n) {
+  if (n < 0) {
+    return false;
+  }
+  vec.emplace_back(n);
+  return true;
+}
+
+void insertIfNonNegative(/*inout*/ std::vector<int>& vec, int n) {
+  if (n >= 0) {
+    vec.emplace_back(n);
+  }
+}
+
+void insertOnlyNonNegative(/*inout*/ std::vector<int>& vec, const std::vector<int>& ns) {
+  std::copy_if(ns.begin(), ns.end(), std::back_inserter(vec), [](int n) { return n >= 0; });
+}
+
 class OdrDexoptLocal final : public OdrDexopt {
  public:
   static OdrDexoptLocal* Create(const std::string& dex2oat_path,
@@ -56,7 +82,6 @@
   }
 
   int DexoptBcpExtension(const DexoptBcpExtArgs& args,
-                         time_t timeout_secs,
                          /*out*/ bool* timed_out,
                          /*out*/ std::string* error_msg) override {
     std::vector<std::string> cmdline = { dex2oat_path_ };
@@ -66,11 +91,10 @@
                  << android::base::Join(cmdline, ' ');
       return -1;
     }
-    return ExecAndReturnCode(exec_utils_.get(), cmdline, timeout_secs, timed_out, error_msg);
+    return ExecAndReturnCode(exec_utils_.get(), cmdline, args.timeoutSecs, timed_out, error_msg);
   }
 
   int DexoptSystemServer(const DexoptSystemServerArgs& args,
-                         time_t timeout_secs,
                          /*out*/ bool* timed_out,
                          /*out*/ std::string* error_msg) override {
     std::vector<std::string> cmdline = { dex2oat_path_ };
@@ -80,7 +104,7 @@
                  << android::base::Join(cmdline, ' ');
       return -1;
     }
-    return ExecAndReturnCode(exec_utils_.get(), cmdline, timeout_secs, timed_out, error_msg);
+    return ExecAndReturnCode(exec_utils_.get(), cmdline, args.timeoutSecs, timed_out, error_msg);
   }
 
  private:
@@ -91,14 +115,13 @@
   std::unique_ptr<ExecUtils> exec_utils_;
 };
 
-class OdrDexoptCompilationOS final : public OdrDexopt {
+class OdrDexoptCompilationOSCmdline final : public OdrDexopt {
  public:
-  static OdrDexoptCompilationOS* Create(int cid, std::unique_ptr<ExecUtils> exec_utils) {
-    return new OdrDexoptCompilationOS(cid, std::move(exec_utils));
+  static OdrDexoptCompilationOSCmdline* Create(int cid, std::unique_ptr<ExecUtils> exec_utils) {
+    return new OdrDexoptCompilationOSCmdline(cid, std::move(exec_utils));
   }
 
   int DexoptBcpExtension(const DexoptBcpExtArgs& args,
-                         time_t timeout_secs,
                          /*out*/ bool* timed_out,
                          /*out*/ std::string* error_msg) override {
     std::vector<int> input_fds, output_fds;
@@ -116,11 +139,10 @@
       return -1;
     }
 
-    return ExecAndReturnCode(exec_utils_.get(), cmdline, timeout_secs, timed_out, error_msg);
+    return ExecAndReturnCode(exec_utils_.get(), cmdline, args.timeoutSecs, timed_out, error_msg);
   }
 
   int DexoptSystemServer(const DexoptSystemServerArgs& args,
-                         time_t timeout_secs,
                          /*out*/ bool* timed_out,
                          /*out*/ std::string* error_msg) override {
     std::vector<int> input_fds, output_fds;
@@ -138,13 +160,11 @@
       return -1;
     }
 
-    LOG(DEBUG) << "DexoptSystemServer cmdline: " << android::base::Join(cmdline, ' ')
-               << " [timeout " << timeout_secs << "s]";
-    return ExecAndReturnCode(exec_utils_.get(), cmdline, timeout_secs, timed_out, error_msg);
+    return ExecAndReturnCode(exec_utils_.get(), cmdline, args.timeoutSecs, timed_out, error_msg);
   }
 
  private:
-  OdrDexoptCompilationOS(int cid, std::unique_ptr<ExecUtils> exec_utils)
+  OdrDexoptCompilationOSCmdline(int cid, std::unique_ptr<ExecUtils> exec_utils)
       : cid_(cid), exec_utils_(std::move(exec_utils)) {}
 
   void AppendPvmExecArgs(/*inout*/ std::vector<std::string>& cmdline,
@@ -188,15 +208,109 @@
     insertIfNonNegative(output_fds, args.oatFd);
   }
 
-  void insertIfNonNegative(/*inout*/ std::vector<int>& vec, int n) {
-    if (n < 0) {
-      return;
-    }
-    vec.emplace_back(n);
+  int cid_;
+  std::unique_ptr<ExecUtils> exec_utils_;
+};
+
+class OdrDexoptCompilationOS final : public OdrDexopt {
+ public:
+  static OdrDexoptCompilationOS* Create(int cid, std::unique_ptr<ExecUtils> exec_utils) {
+    return new OdrDexoptCompilationOS(cid, std::move(exec_utils));
   }
 
-  void insertOnlyNonNegative(/*inout*/ std::vector<int>& vec, const std::vector<int>& ns) {
-    std::copy_if(ns.begin(), ns.end(), std::back_inserter(vec), [](int n) { return n >= 0; });
+  int DexoptBcpExtension(const DexoptBcpExtArgs& args,
+                         /*out*/ bool* timed_out,
+                         /*out*/ std::string* error_msg) override {
+    std::vector<int> input_fds, output_fds;
+    if (!insertFd(input_fds, args.profileFd) ||
+        !insertFd(input_fds, args.dirtyImageObjectsFd) ||
+        !insertFd(output_fds, args.imageFd) ||
+        !insertFd(output_fds, args.vdexFd) ||
+        !insertFd(output_fds, args.oatFd)) {
+      *error_msg = "Some required FDs for dexopting BCP extension are missing";
+      return -1;
+    }
+    insertOnlyNonNegative(input_fds, args.dexFds);
+    insertOnlyNonNegative(input_fds, args.bootClasspathFds);
+
+    return DexoptCommon(args, TaskType::DEXOPT_BCP_EXTENSION, input_fds, output_fds, timed_out,
+                        error_msg);
+  }
+
+  int DexoptSystemServer(const DexoptSystemServerArgs& args,
+                         /*out*/ bool* timed_out,
+                         /*out*/ std::string* error_msg) override {
+    std::vector<int> input_fds, output_fds;
+    if (!insertFd(input_fds, args.dexFd) ||
+        !insertFd(output_fds, args.imageFd) ||
+        !insertFd(output_fds, args.vdexFd) ||
+        !insertFd(output_fds, args.oatFd)) {
+      *error_msg = "Some required FDs for dexopting system server jar are missing";
+      return -1;
+    }
+    insertIfNonNegative(input_fds, args.profileFd);
+    insertOnlyNonNegative(input_fds, args.bootClasspathFds);
+    insertOnlyNonNegative(input_fds, args.bootClasspathImageFds);
+    insertOnlyNonNegative(input_fds, args.bootClasspathVdexFds);
+    insertOnlyNonNegative(input_fds, args.bootClasspathOatFds);
+    insertOnlyNonNegative(input_fds, args.classloaderFds);
+
+    return DexoptCommon(args, TaskType::DEXOPT_SYSTEM_SERVER, input_fds, output_fds, timed_out,
+                        error_msg);
+  }
+
+ private:
+  OdrDexoptCompilationOS(int cid, std::unique_ptr<ExecUtils> exec_utils)
+      : cid_(cid), exec_utils_(std::move(exec_utils)) {}
+
+  template<typename Args>
+  int DexoptCommon(const Args& args,
+                   TaskType task_type,
+                   const std::vector<int>& input_fds,
+                   const std::vector<int>& output_fds,
+                   /*out*/ bool* timed_out,
+                   /*out*/ std::string* error_msg) {
+    *timed_out = false;
+    *error_msg = "Unknown error";
+
+    // Serialize to byte array to send as an opaque object to CompOS (which will later call
+    // ADexopt_CreateAndValidateDexoptContext to reconstruct). The reason to use opaque object is
+    // to allow the ART APEX to continue to update independently by providing the flexibility for
+    // ART to evolve, e.g. add or remove compilation parameters. Thereforce, we choose not to
+    // stabilize the arguments to avoid the client/CompOS explicitly depending on them.
+    ExtendableParcelable ep;
+    ep.taskType = task_type;
+    ep.ext.setParcelable(args);
+    auto parcel = std::unique_ptr<AParcel, decltype(&AParcel_delete)>(AParcel_create(),
+                                                                      AParcel_delete);
+    if (ep.writeToParcel(parcel.get()) != STATUS_OK) {
+      *error_msg = "Failed to write args to the parcel";
+      return -1;
+    }
+    std::vector<uint8_t> buffer(AParcel_getDataSize(parcel.get()));
+    if (AParcel_marshal(parcel.get(), buffer.data(), 0, buffer.size()) != STATUS_OK) {
+      *error_msg = "Failed to marshal the parcel";
+      return -1;
+    }
+
+    // Send the request over RPC binder and wait for the response.
+    int exit_code = AComposClient_Request(cid_,
+                                          buffer.data(),
+                                          buffer.size(),
+                                          input_fds.data(),
+                                          input_fds.size(),
+                                          output_fds.data(),
+                                          output_fds.size());
+
+    if (exit_code != 0) {
+      if (exit_code == kDex2oatExitCode_Timeout) {
+        *timed_out = true;
+        *error_msg = "dex2oat has timed out (see service log in the VM)";
+      } else {
+        *error_msg = "dex2oat failed (see service log in the VM)";
+      }
+    }
+    return exit_code;
   }
 
   int cid_;
@@ -210,7 +324,12 @@
                                              std::unique_ptr<ExecUtils> exec_utils) {
   if (config.UseCompilationOs()) {
     int cid = config.GetCompilationOsAddress();
-    return std::unique_ptr<OdrDexopt>(OdrDexoptCompilationOS::Create(cid, std::move(exec_utils)));
+    if (USE_NEW_COMPILIATION_OS_API) {
+      return std::unique_ptr<OdrDexopt>(OdrDexoptCompilationOS::Create(cid, std::move(exec_utils)));
+    } else {
+      return std::unique_ptr<OdrDexopt>(
+          OdrDexoptCompilationOSCmdline::Create(cid, std::move(exec_utils)));
+    }
   } else {
     return std::unique_ptr<OdrDexopt>(OdrDexoptLocal::Create(config.GetDex2Oat(),
                                                              std::move(exec_utils)));
diff --git a/odrefresh/odr_dexopt.h b/odrefresh/odr_dexopt.h
index 2db3b4a..753b9e6 100644
--- a/odrefresh/odr_dexopt.h
+++ b/odrefresh/odr_dexopt.h
@@ -37,17 +37,19 @@
 
 class OdrDexopt {
  public:
+  // Exit code to indicate dex2oat has timed out. This number is in a reserved ranged of dex2oat
+  // that it won't return, which allows us to piggyback to indicate extra execution result.
+  static const int kDex2oatExitCode_Timeout = 128;
+
   static std::unique_ptr<OdrDexopt> Create(const OdrConfig& confg,
                                            std::unique_ptr<ExecUtils> exec_utils);
 
   virtual ~OdrDexopt() {}
 
   virtual int DexoptBcpExtension(const DexoptBcpExtArgs& args,
-                                 time_t timeout_secs,
                                  /*out*/ bool* timed_out,
                                  /*out*/ std::string* error_msg) = 0;
   virtual int DexoptSystemServer(const DexoptSystemServerArgs& args,
-                                 time_t timeout_secs,
                                  /*out*/ bool* timed_out,
                                  /*out*/ std::string* error_msg) = 0;
 };
diff --git a/odrefresh/odrefresh.cc b/odrefresh/odrefresh.cc
index 4e4c800..cd9d2ae 100644
--- a/odrefresh/odrefresh.cc
+++ b/odrefresh/odrefresh.cc
@@ -1360,6 +1360,7 @@
   }
 
   const time_t timeout = GetSubprocessTimeout();
+  dexopt_args.timeoutSecs = timeout;
   LOG(INFO) << "Compiling boot extensions (" << isa << "): " << dexopt_args.toString()
             << " [timeout " << timeout << "s]";
   if (config_.GetDryRun()) {
@@ -1368,8 +1369,7 @@
   }
 
   bool timed_out = false;
-  int dex2oat_exit_code = odr_dexopt_->DexoptBcpExtension(
-      dexopt_args, timeout, &timed_out, error_msg);
+  int dex2oat_exit_code = odr_dexopt_->DexoptBcpExtension(dexopt_args, &timed_out, error_msg);
 
   if (dex2oat_exit_code != 0) {
     if (timed_out) {
@@ -1499,6 +1499,7 @@
     }
 
     const time_t timeout = GetSubprocessTimeout();
+    dexopt_args.timeoutSecs = timeout;
     LOG(INFO) << "Compiling " << jar << ": " << dexopt_args.toString() << " [timeout " << timeout
               << "s]";
     if (config_.GetDryRun()) {
@@ -1507,8 +1508,7 @@
     }
 
     bool timed_out = false;
-    int dex2oat_exit_code = odr_dexopt_->DexoptSystemServer(
-        dexopt_args, timeout, &timed_out, error_msg);
+    int dex2oat_exit_code = odr_dexopt_->DexoptSystemServer(dexopt_args, &timed_out, error_msg);
 
     if (dex2oat_exit_code != 0) {
       if (timed_out) {