Separated Acloud Command Handlers

Acloud has three command handlers, and one library class.
The four have been separated.

Bug: 266441203
Test: m hosttar
Change-Id: I7bf7cce45eaed6f4a3341c167484532b1a21269e
diff --git a/host/commands/cvd/acloud/converter.cpp b/host/commands/cvd/acloud/converter.cpp
index 7d270d3..7a3ef03 100644
--- a/host/commands/cvd/acloud/converter.cpp
+++ b/host/commands/cvd/acloud/converter.cpp
@@ -14,6 +14,8 @@
  * limitations under the License.
  */
 
+#include "host/commands/cvd/acloud/converter.h"
+
 #include <sys/stat.h>
 
 #include <cstdio>
@@ -26,7 +28,6 @@
 #include <android-base/strings.h>
 #include <google/protobuf/text_format.h>
 
-#include "common/libs/fs/shared_buf.h"
 #include "common/libs/fs/shared_fd.h"
 #include "common/libs/utils/files.h"
 #include "common/libs/utils/flag_parser.h"
@@ -40,31 +41,13 @@
 #include "host/commands/cvd/instance_lock.h"
 #include "host/commands/cvd/selector/selector_constants.h"
 #include "host/commands/cvd/server_client.h"
-#include "host/commands/cvd/server_command/acloud.h"
 #include "host/commands/cvd/server_command/utils.h"
 #include "host/commands/cvd/types.h"
 #include "host/libs/config/cuttlefish_config.h"
 
 namespace cuttlefish {
-
-static constexpr char kTranslatorHelpMessage[] =
-    R"(Cuttlefish Virtual Device (CVD) CLI.
-
-usage: cvd acloud translator <args>
-
-Args:
-  --opt-out              Opt-out CVD Acloud and choose to run original Python Acloud.
-  --opt-in               Opt-in and run CVD Acloud as default.
-Both -opt-out and --opt-in are mutually exclusive.
-)";
-
 namespace {
 
-struct ConvertedAcloudCreateCommand {
-  InstanceLockFile lock;
-  std::vector<RequestWithStdio> requests;
-};
-
 // Image names to search
 const std::vector<std::string> _KERNEL_IMAGE_NAMES = {"kernel", "bzImage",
                                                       "Image"};
@@ -111,13 +94,19 @@
   return android::base::Split(stdout_str, "\n");
 }
 
-class ConvertAcloudCreateCommand {
+}  // namespace
+
+// body of pure virtual destructor required by C++
+ConvertAcloudCreateCommand::~ConvertAcloudCreateCommand() {}
+
+class ConvertAcloudCreateCommandImpl : public ConvertAcloudCreateCommand {
  public:
-  INJECT(ConvertAcloudCreateCommand(InstanceLockFileManager& lock_file_manager))
+  INJECT(ConvertAcloudCreateCommandImpl(
+      InstanceLockFileManager& lock_file_manager))
       : fetch_cvd_args_file_(""),
         fetch_command_str_(""),
         lock_file_manager_(lock_file_manager) {}
-
+  ~ConvertAcloudCreateCommandImpl() override = default;
   Result<ConvertedAcloudCreateCommand> Convert(
       const RequestWithStdio& request) {
     auto arguments = ParseInvocation(request.Message()).arguments;
@@ -712,181 +701,25 @@
     return ret;
   }
 
- public:
-  std::string fetch_cvd_args_file_;
-  std::string fetch_command_str_;
+  const std::string& FetchCvdArgsFile() const override {
+    return fetch_cvd_args_file_;
+  }
+
+  const std::string& FetchCommandString() const override {
+    return fetch_command_str_;
+  }
 
  private:
+  std::string fetch_cvd_args_file_;
+  std::string fetch_command_str_;
   InstanceLockFileManager& lock_file_manager_;
 };
 
-static bool IsSubOperationSupported(const RequestWithStdio& request) {
-  auto invocation = ParseInvocation(request.Message());
-  if (invocation.arguments.empty()) {
-    return false;
-  }
-  return invocation.arguments[0] == "create";
-}
-
-class TryAcloudCommand : public CvdServerHandler {
- public:
-  INJECT(TryAcloudCommand(ConvertAcloudCreateCommand& converter,
-                          ANNOTATED(AcloudTranslatorOptOut,
-                                    const std::atomic<bool>&) optout))
-      : converter_(converter), optout_(optout) {}
-  ~TryAcloudCommand() = default;
-
-  Result<bool> CanHandle(const RequestWithStdio& request) const override {
-    auto invocation = ParseInvocation(request.Message());
-    return invocation.command == "try-acloud";
-  }
-
-  cvd_common::Args CmdList() const override { return {"try-acloud"}; }
-
-  Result<cvd::Response> Handle(const RequestWithStdio& request) override {
-    CF_EXPECT(CanHandle(request));
-    CF_EXPECT(IsSubOperationSupported(request));
-    CF_EXPECT(converter_.Convert(request));
-    // currently, optout/optin feature only works in local instance
-    // remote instance still uses legacy python acloud
-    CF_EXPECT(!optout_);
-    cvd::Response response;
-    response.mutable_command_response();
-    return response;
-  }
-  Result<void> Interrupt() override { return CF_ERR("Can't be interrupted."); }
-
- private:
-  ConvertAcloudCreateCommand& converter_;
-  const std::atomic<bool>& optout_;
-};
-
-class AcloudTranslatorCommand : public CvdServerHandler {
- public:
-  INJECT(AcloudTranslatorCommand(ANNOTATED(AcloudTranslatorOptOut,
-                                           std::atomic<bool>&) optout))
-      : optout_(optout) {}
-  ~AcloudTranslatorCommand() = default;
-
-  Result<bool> CanHandle(const RequestWithStdio& request) const override {
-    auto invocation = ParseInvocation(request.Message());
-    if (invocation.arguments.size() >= 2) {
-      if (invocation.command == "acloud" &&
-          invocation.arguments[0] == "translator") {
-        return true;
-      }
-    }
-    return false;
-  }
-
-  cvd_common::Args CmdList() const override { return {}; }
-
-  Result<cvd::Response> Handle(const RequestWithStdio& request) override {
-    CF_EXPECT(CanHandle(request));
-    auto invocation = ParseInvocation(request.Message());
-    if (invocation.arguments.empty() || invocation.arguments.size() < 2) {
-      return CF_ERR("Translator command not support");
-    }
-
-    // cvd acloud translator --opt-out
-    // cvd acloud translator --opt-in
-    cvd::Response response;
-    response.mutable_command_response();
-    bool help = false;
-    bool flag_optout = false;
-    bool flag_optin = false;
-    std::vector<Flag> translator_flags = {
-        GflagsCompatFlag("help", help),
-        GflagsCompatFlag("opt-out", flag_optout),
-        GflagsCompatFlag("opt-in", flag_optin),
-    };
-    CF_EXPECT(ParseFlags(translator_flags, invocation.arguments),
-              "Failed to process translator flag.");
-    if (help) {
-      WriteAll(request.Out(), kTranslatorHelpMessage);
-      return response;
-    }
-    CF_EXPECT(flag_optout != flag_optin,
-              "Only one of --opt-out or --opt-in should be given.");
-    optout_ = flag_optout;
-    return response;
-  }
-  Result<void> Interrupt() override { return CF_ERR("Can't be interrupted."); }
-
- private:
-  std::atomic<bool>& optout_;
-};
-
-class AcloudCommand : public CvdServerHandler {
- public:
-  INJECT(AcloudCommand(CommandSequenceExecutor& executor,
-                       ConvertAcloudCreateCommand& converter))
-      : executor_(executor), converter_(converter) {}
-  ~AcloudCommand() = default;
-
-  Result<bool> CanHandle(const RequestWithStdio& request) const override {
-    auto invocation = ParseInvocation(request.Message());
-    if (invocation.arguments.size() >= 2) {
-      if (invocation.command == "acloud" &&
-          invocation.arguments[0] == "translator") {
-        return false;
-      }
-    }
-    return invocation.command == "acloud";
-  }
-
-  cvd_common::Args CmdList() const override { return {"acloud"}; }
-
-  Result<cvd::Response> Handle(const RequestWithStdio& request) override {
-    std::unique_lock interrupt_lock(interrupt_mutex_);
-    if (interrupted_) {
-      return CF_ERR("Interrupted");
-    }
-    CF_EXPECT(CanHandle(request));
-    CF_EXPECT(IsSubOperationSupported(request));
-    auto converted = CF_EXPECT(converter_.Convert(request));
-    interrupt_lock.unlock();
-    CF_EXPECT(executor_.Execute(converted.requests, request.Err()));
-
-    CF_EXPECT(converted.lock.Status(InUseState::kInUse));
-
-    if (converter_.fetch_command_str_ != "") {
-      // has cvd fetch command, update the fetch cvd command file
-      using android::base::WriteStringToFile;
-      CF_EXPECT(WriteStringToFile(converter_.fetch_command_str_,
-                                  converter_.fetch_cvd_args_file_),
-                true);
-    }
-
-    cvd::Response response;
-    response.mutable_command_response();
-    return response;
-  }
-  Result<void> Interrupt() override {
-    std::scoped_lock interrupt_lock(interrupt_mutex_);
-    interrupted_ = true;
-    CF_EXPECT(executor_.Interrupt());
-    return {};
-  }
-
- private:
-  CommandSequenceExecutor& executor_;
-  ConvertAcloudCreateCommand& converter_;
-
-  std::mutex interrupt_mutex_;
-  bool interrupted_ = false;
-};
-
-}  // namespace
-
-fruit::Component<fruit::Required<
-    CommandSequenceExecutor,
-    fruit::Annotated<AcloudTranslatorOptOut, std::atomic<bool>>>>
-AcloudCommandComponent() {
+fruit::Component<fruit::Required<InstanceLockFileManager>,
+                 ConvertAcloudCreateCommand>
+AcloudCreateConvertCommandComponent() {
   return fruit::createComponent()
-      .addMultibinding<CvdServerHandler, AcloudCommand>()
-      .addMultibinding<CvdServerHandler, TryAcloudCommand>()
-      .addMultibinding<CvdServerHandler, AcloudTranslatorCommand>();
+      .bind<ConvertAcloudCreateCommand, ConvertAcloudCreateCommandImpl>();
 }
 
 }  // namespace cuttlefish
diff --git a/host/commands/cvd/acloud/converter.h b/host/commands/cvd/acloud/converter.h
new file mode 100644
index 0000000..01d1470
--- /dev/null
+++ b/host/commands/cvd/acloud/converter.h
@@ -0,0 +1,50 @@
+/*
+ * Copyright (C) 2023 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.
+ */
+
+#pragma once
+
+#include <atomic>
+
+#include <fruit/fruit.h>
+
+#include "host/commands/cvd/instance_lock.h"
+#include "host/commands/cvd/server_client.h"
+
+namespace cuttlefish {
+
+struct ConvertedAcloudCreateCommand {
+  InstanceLockFile lock;
+  std::vector<RequestWithStdio> requests;
+};
+
+class ConvertAcloudCreateCommand {
+ public:
+  virtual Result<ConvertedAcloudCreateCommand> Convert(
+      const RequestWithStdio& request) = 0;
+  virtual const std::string& FetchCvdArgsFile() const = 0;
+  virtual const std::string& FetchCommandString() const = 0;
+  /*
+   * Android prouction build system appears to mandate virtual
+   * destructor.
+   */
+  virtual ~ConvertAcloudCreateCommand() = 0;
+};
+
+fruit::Component<fruit::Required<InstanceLockFileManager>,
+                 ConvertAcloudCreateCommand>
+AcloudCreateConvertCommandComponent();
+
+}  // namespace cuttlefish
diff --git a/host/commands/cvd/server.cc b/host/commands/cvd/server.cc
index 780cfba..d4a8622 100644
--- a/host/commands/cvd/server.cc
+++ b/host/commands/cvd/server.cc
@@ -111,7 +111,7 @@
       .bindInstance<
           fruit::Annotated<AcloudTranslatorOptOut, std::atomic<bool>>>(
           server->optout_)
-      .install(AcloudCommandComponent)
+      .install(CvdAcloudComponent)
       .install(CvdCmdlistComponent)
       .install(CommandSequenceExecutorComponent)
       .install(CvdCrosVmComponent)
diff --git a/host/commands/cvd/server_command/Android.bp b/host/commands/cvd/server_command/Android.bp
index 44fbe50..a006b80 100644
--- a/host/commands/cvd/server_command/Android.bp
+++ b/host/commands/cvd/server_command/Android.bp
@@ -21,6 +21,10 @@
     name: "libcvd_sub_commands",
     srcs: [
         "utils.cpp",
+        "acloud.cpp",
+        "acloud_command.cpp",
+        "acloud_common.cpp",
+        "acloud_translator.cpp",
         "cmd_list.cpp",
         "components.cpp",
         "crosvm.cpp",
@@ -44,7 +48,11 @@
         "start.cpp",
         "start_impl.cpp",
         "subprocess_waiter.cpp",
+        "try_acloud.cpp",
         "version.cpp",
     ],
+    static_libs: [
+        "libcvd_acloud",
+    ],
     defaults: ["cvd_lib_defaults"],
 }
diff --git a/host/commands/cvd/server_command/acloud.cpp b/host/commands/cvd/server_command/acloud.cpp
new file mode 100644
index 0000000..df1d73d
--- /dev/null
+++ b/host/commands/cvd/server_command/acloud.cpp
@@ -0,0 +1,42 @@
+/*
+ * Copyright (C) 2022 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 "host/commands/cvd/server_command/acloud.h"
+
+#include <atomic>
+
+#include <fruit/fruit.h>
+
+#include "host/commands/cvd/acloud/converter.h"
+#include "host/commands/cvd/server_command/acloud.h"
+#include "host/commands/cvd/server_command/acloud_command.h"
+#include "host/commands/cvd/server_command/acloud_translator.h"
+#include "host/commands/cvd/server_command/try_acloud.h"
+
+namespace cuttlefish {
+
+fruit::Component<fruit::Required<
+    CommandSequenceExecutor,
+    fruit::Annotated<AcloudTranslatorOptOut, std::atomic<bool>>>>
+CvdAcloudComponent() {
+  return fruit::createComponent()
+      .install(AcloudCreateConvertCommandComponent)
+      .install(AcloudCommandComponent)
+      .install(TryAcloudCommandComponent)
+      .install(AcloudTranslatorCommandComponent);
+}
+
+}  // namespace cuttlefish
diff --git a/host/commands/cvd/server_command/acloud.h b/host/commands/cvd/server_command/acloud.h
index 0adf6f2..c476165 100644
--- a/host/commands/cvd/server_command/acloud.h
+++ b/host/commands/cvd/server_command/acloud.h
@@ -21,13 +21,13 @@
 #include <fruit/fruit.h>
 
 #include "host/commands/cvd/command_sequence.h"
+#include "host/commands/cvd/server_command/acloud_common.h"
 
 namespace cuttlefish {
 
-struct AcloudTranslatorOptOut {};
-
 fruit::Component<fruit::Required<
     CommandSequenceExecutor,
     fruit::Annotated<AcloudTranslatorOptOut, std::atomic<bool>>>>
-AcloudCommandComponent();
-}  // namespace cuttlefish
+CvdAcloudComponent();
+
+}
diff --git a/host/commands/cvd/server_command/acloud_command.cpp b/host/commands/cvd/server_command/acloud_command.cpp
new file mode 100644
index 0000000..e9ea96d
--- /dev/null
+++ b/host/commands/cvd/server_command/acloud_command.cpp
@@ -0,0 +1,103 @@
+/*
+ * Copyright (C) 2023 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 "host/commands/cvd/server_command/acloud_command.h"
+
+#include <atomic>
+#include <mutex>
+
+#include <android-base/file.h>
+#include <android-base/strings.h>
+#include <fruit/fruit.h>
+
+#include "common/libs/utils/result.h"
+#include "cvd_server.pb.h"
+#include "host/commands/cvd/instance_lock.h"
+#include "host/commands/cvd/server_command/acloud_common.h"
+#include "host/commands/cvd/server_command/server_handler.h"
+#include "host/commands/cvd/server_command/utils.h"
+#include "host/commands/cvd/types.h"
+
+namespace cuttlefish {
+
+class AcloudCommand : public CvdServerHandler {
+ public:
+  INJECT(AcloudCommand(CommandSequenceExecutor& executor,
+                       ConvertAcloudCreateCommand& converter))
+      : executor_(executor), converter_(converter) {}
+  ~AcloudCommand() = default;
+
+  Result<bool> CanHandle(const RequestWithStdio& request) const override {
+    auto invocation = ParseInvocation(request.Message());
+    if (invocation.arguments.size() >= 2) {
+      if (invocation.command == "acloud" &&
+          invocation.arguments[0] == "translator") {
+        return false;
+      }
+    }
+    return invocation.command == "acloud";
+  }
+
+  cvd_common::Args CmdList() const override { return {"acloud"}; }
+
+  Result<cvd::Response> Handle(const RequestWithStdio& request) override {
+    std::unique_lock interrupt_lock(interrupt_mutex_);
+    if (interrupted_) {
+      return CF_ERR("Interrupted");
+    }
+    CF_EXPECT(CanHandle(request));
+    CF_EXPECT(IsSubOperationSupported(request));
+    auto converted = CF_EXPECT(converter_.Convert(request));
+    interrupt_lock.unlock();
+    CF_EXPECT(executor_.Execute(converted.requests, request.Err()));
+
+    CF_EXPECT(converted.lock.Status(InUseState::kInUse));
+
+    if (converter_.FetchCommandString() != "") {
+      // has cvd fetch command, update the fetch cvd command file
+      using android::base::WriteStringToFile;
+      CF_EXPECT(WriteStringToFile(converter_.FetchCommandString(),
+                                  converter_.FetchCvdArgsFile()),
+                true);
+    }
+
+    cvd::Response response;
+    response.mutable_command_response();
+    return response;
+  }
+  Result<void> Interrupt() override {
+    std::scoped_lock interrupt_lock(interrupt_mutex_);
+    interrupted_ = true;
+    CF_EXPECT(executor_.Interrupt());
+    return {};
+  }
+
+ private:
+  CommandSequenceExecutor& executor_;
+  ConvertAcloudCreateCommand& converter_;
+
+  std::mutex interrupt_mutex_;
+  bool interrupted_ = false;
+};
+
+fruit::Component<
+    fruit::Required<CommandSequenceExecutor, ConvertAcloudCreateCommand>>
+AcloudCommandComponent() {
+  return fruit::createComponent()
+      .addMultibinding<CvdServerHandler, AcloudCommand>();
+}
+
+}  // namespace cuttlefish
diff --git a/host/commands/cvd/server_command/acloud_command.h b/host/commands/cvd/server_command/acloud_command.h
new file mode 100644
index 0000000..a0a87e1
--- /dev/null
+++ b/host/commands/cvd/server_command/acloud_command.h
@@ -0,0 +1,32 @@
+/*
+ * Copyright (C) 2023 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.
+ */
+
+#pragma once
+
+#include <atomic>
+
+#include <fruit/fruit.h>
+
+#include "host/commands/cvd/acloud/converter.h"
+#include "host/commands/cvd/command_sequence.h"
+
+namespace cuttlefish {
+
+fruit::Component<
+    fruit::Required<CommandSequenceExecutor, ConvertAcloudCreateCommand>>
+AcloudCommandComponent();
+
+}
diff --git a/host/commands/cvd/server_command/acloud_common.cpp b/host/commands/cvd/server_command/acloud_common.cpp
new file mode 100644
index 0000000..4c343bb
--- /dev/null
+++ b/host/commands/cvd/server_command/acloud_common.cpp
@@ -0,0 +1,31 @@
+/*
+ * Copyright (C) 2023 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 "host/commands/cvd/server_command/acloud_common.h"
+
+#include "host/commands/cvd/server_command/utils.h"
+
+namespace cuttlefish {
+
+bool IsSubOperationSupported(const RequestWithStdio& request) {
+  auto invocation = ParseInvocation(request.Message());
+  if (invocation.arguments.empty()) {
+    return false;
+  }
+  return invocation.arguments[0] == "create";
+}
+
+}  // namespace cuttlefish
diff --git a/host/commands/cvd/server_command/acloud_common.h b/host/commands/cvd/server_command/acloud_common.h
new file mode 100644
index 0000000..820973f
--- /dev/null
+++ b/host/commands/cvd/server_command/acloud_common.h
@@ -0,0 +1,27 @@
+/*
+ * Copyright (C) 2023 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.
+ */
+
+#pragma once
+
+#include "host/commands/cvd/server_client.h"
+
+namespace cuttlefish {
+
+struct AcloudTranslatorOptOut {};
+
+bool IsSubOperationSupported(const RequestWithStdio& request);
+
+}  // namespace cuttlefish
diff --git a/host/commands/cvd/server_command/acloud_translator.cpp b/host/commands/cvd/server_command/acloud_translator.cpp
new file mode 100644
index 0000000..7368540
--- /dev/null
+++ b/host/commands/cvd/server_command/acloud_translator.cpp
@@ -0,0 +1,108 @@
+/*
+ * Copyright (C) 2023 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 "host/commands/cvd/server_command/acloud_translator.h"
+
+#include <mutex>
+
+#include <fruit/fruit.h>
+
+#include "common/libs/fs/shared_buf.h"
+#include "common/libs/utils/flag_parser.h"
+#include "common/libs/utils/result.h"
+#include "cvd_server.pb.h"
+#include "host/commands/cvd/server_client.h"
+#include "host/commands/cvd/server_command/server_handler.h"
+#include "host/commands/cvd/server_command/utils.h"
+#include "host/commands/cvd/types.h"
+
+namespace cuttlefish {
+
+static constexpr char kTranslatorHelpMessage[] =
+    R"(Cuttlefish Virtual Device (CVD) CLI.
+
+usage: cvd acloud translator <args>
+
+Args:
+  --opt-out              Opt-out CVD Acloud and choose to run original Python Acloud.
+  --opt-in               Opt-in and run CVD Acloud as default.
+Both -opt-out and --opt-in are mutually exclusive.
+)";
+
+class AcloudTranslatorCommand : public CvdServerHandler {
+ public:
+  INJECT(AcloudTranslatorCommand(ANNOTATED(AcloudTranslatorOptOut,
+                                           std::atomic<bool>&) optout))
+      : optout_(optout) {}
+  ~AcloudTranslatorCommand() = default;
+
+  Result<bool> CanHandle(const RequestWithStdio& request) const override {
+    auto invocation = ParseInvocation(request.Message());
+    if (invocation.arguments.size() >= 2) {
+      if (invocation.command == "acloud" &&
+          invocation.arguments[0] == "translator") {
+        return true;
+      }
+    }
+    return false;
+  }
+
+  cvd_common::Args CmdList() const override { return {}; }
+
+  Result<cvd::Response> Handle(const RequestWithStdio& request) override {
+    CF_EXPECT(CanHandle(request));
+    auto invocation = ParseInvocation(request.Message());
+    if (invocation.arguments.empty() || invocation.arguments.size() < 2) {
+      return CF_ERR("Translator command not support");
+    }
+
+    // cvd acloud translator --opt-out
+    // cvd acloud translator --opt-in
+    cvd::Response response;
+    response.mutable_command_response();
+    bool help = false;
+    bool flag_optout = false;
+    bool flag_optin = false;
+    std::vector<Flag> translator_flags = {
+        GflagsCompatFlag("help", help),
+        GflagsCompatFlag("opt-out", flag_optout),
+        GflagsCompatFlag("opt-in", flag_optin),
+    };
+    CF_EXPECT(ParseFlags(translator_flags, invocation.arguments),
+              "Failed to process translator flag.");
+    if (help) {
+      WriteAll(request.Out(), kTranslatorHelpMessage);
+      return response;
+    }
+    CF_EXPECT(flag_optout != flag_optin,
+              "Only one of --opt-out or --opt-in should be given.");
+    optout_ = flag_optout;
+    return response;
+  }
+  Result<void> Interrupt() override { return CF_ERR("Can't be interrupted."); }
+
+ private:
+  std::atomic<bool>& optout_;
+};
+
+fruit::Component<fruit::Required<
+    fruit::Annotated<AcloudTranslatorOptOut, std::atomic<bool>>>>
+AcloudTranslatorCommandComponent() {
+  return fruit::createComponent()
+      .addMultibinding<CvdServerHandler, AcloudTranslatorCommand>();
+}
+
+}  // namespace cuttlefish
diff --git a/host/commands/cvd/server_command/acloud_translator.h b/host/commands/cvd/server_command/acloud_translator.h
new file mode 100644
index 0000000..45232a5
--- /dev/null
+++ b/host/commands/cvd/server_command/acloud_translator.h
@@ -0,0 +1,31 @@
+/*
+ * Copyright (C) 2023 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.
+ */
+
+#pragma once
+
+#include <atomic>
+
+#include <fruit/fruit.h>
+
+#include "host/commands/cvd/server_command/acloud_common.h"
+
+namespace cuttlefish {
+
+fruit::Component<fruit::Required<
+    fruit::Annotated<AcloudTranslatorOptOut, std::atomic<bool>>>>
+AcloudTranslatorCommandComponent();
+
+}
diff --git a/host/commands/cvd/server_command/try_acloud.cpp b/host/commands/cvd/server_command/try_acloud.cpp
new file mode 100644
index 0000000..6b72033
--- /dev/null
+++ b/host/commands/cvd/server_command/try_acloud.cpp
@@ -0,0 +1,72 @@
+/*
+ * Copyright (C) 2023 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 "host/commands/cvd/server_command/try_acloud.h"
+
+#include <mutex>
+
+#include <fruit/fruit.h>
+
+#include "common/libs/utils/result.h"
+#include "cvd_server.pb.h"
+#include "host/commands/cvd/server_command/server_handler.h"
+#include "host/commands/cvd/server_command/utils.h"
+#include "host/commands/cvd/types.h"
+
+namespace cuttlefish {
+
+class TryAcloudCommand : public CvdServerHandler {
+ public:
+  INJECT(TryAcloudCommand(ConvertAcloudCreateCommand& converter,
+                          ANNOTATED(AcloudTranslatorOptOut,
+                                    const std::atomic<bool>&) optout))
+      : converter_(converter), optout_(optout) {}
+  ~TryAcloudCommand() = default;
+
+  Result<bool> CanHandle(const RequestWithStdio& request) const override {
+    auto invocation = ParseInvocation(request.Message());
+    return invocation.command == "try-acloud";
+  }
+
+  cvd_common::Args CmdList() const override { return {"try-acloud"}; }
+
+  Result<cvd::Response> Handle(const RequestWithStdio& request) override {
+    CF_EXPECT(CanHandle(request));
+    CF_EXPECT(IsSubOperationSupported(request));
+    CF_EXPECT(converter_.Convert(request));
+    // currently, optout/optin feature only works in local instance
+    // remote instance still uses legacy python acloud
+    CF_EXPECT(!optout_);
+    cvd::Response response;
+    response.mutable_command_response();
+    return response;
+  }
+  Result<void> Interrupt() override { return CF_ERR("Can't be interrupted."); }
+
+ private:
+  ConvertAcloudCreateCommand& converter_;
+  const std::atomic<bool>& optout_;
+};
+
+fruit::Component<fruit::Required<
+    ConvertAcloudCreateCommand,
+    fruit::Annotated<AcloudTranslatorOptOut, std::atomic<bool>>>>
+TryAcloudCommandComponent() {
+  return fruit::createComponent()
+      .addMultibinding<CvdServerHandler, TryAcloudCommand>();
+}
+
+}  // namespace cuttlefish
diff --git a/host/commands/cvd/server_command/try_acloud.h b/host/commands/cvd/server_command/try_acloud.h
new file mode 100644
index 0000000..68212c2
--- /dev/null
+++ b/host/commands/cvd/server_command/try_acloud.h
@@ -0,0 +1,31 @@
+/*
+ * Copyright (C) 2023 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.
+ */
+
+#pragma once
+
+#include <fruit/fruit.h>
+
+#include "host/commands/cvd/acloud/converter.h"
+#include "host/commands/cvd/server_command/acloud_common.h"
+
+namespace cuttlefish {
+
+fruit::Component<fruit::Required<
+    ConvertAcloudCreateCommand,
+    fruit::Annotated<AcloudTranslatorOptOut, std::atomic<bool>>>>
+TryAcloudCommandComponent();
+
+}