Merge commit '4cd68a39efe4b6f17a1a4132dd23a8b05863382b' into HEAD

Bug: 131249906
Test: None
Change-Id: I42ccc29dd05218e291a6cda3296cead52f9a7ff3
diff --git a/Android.bp b/Android.bp
index 41c2390..65b7b79 100644
--- a/Android.bp
+++ b/Android.bp
@@ -29,6 +29,8 @@
 
 python_binary_host {
     name: "acloud",
+    // Make acloud's built name to acloud-dev
+    stem: "acloud-dev",
     defaults: ["acloud_default"],
     main: "public/acloud_main.py",
     srcs: [
diff --git a/CleanSpec.mk b/CleanSpec.mk
new file mode 100644
index 0000000..135fa2d
--- /dev/null
+++ b/CleanSpec.mk
@@ -0,0 +1,47 @@
+# Copyright (C) 2019 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.
+#
+
+# If you don't need to do a full clean build but would like to touch
+# a file or delete some intermediate files, add a clean step to the end
+# of the list.  These steps will only be run once, if they haven't been
+# run before.
+#
+# E.g.:
+#     $(call add-clean-step, touch -c external/sqlite/sqlite3.h)
+#     $(call add-clean-step, rm -rf $(PRODUCT_OUT)/obj/STATIC_LIBRARIES/libz_intermediates)
+#
+# Always use "touch -c" and "rm -f" or "rm -rf" to gracefully deal with
+# files that are missing or have been moved.
+#
+# Use $(PRODUCT_OUT) to get to the "out/target/product/blah/" directory.
+# Use $(OUT_DIR) to refer to the "out" directory.
+#
+# If you need to re-do something that's already mentioned, just copy
+# the command and add it to the bottom of the list.  E.g., if a change
+# that you made last week required touching a file and a change you
+# made today requires touching the same file, just copy the old
+# touch step and add it to the end of the list.
+#
+# ************************************************
+# NEWER CLEAN STEPS MUST BE AT THE END OF THE LIST
+# ************************************************
+
+# Discard the original naming "acloud", which is going to be replaced with "acloud-dev".
+$(call add-clean-step, rm -f $(HOST_OUT_EXECUTABLES)/acloud)
+$(call add-clean-step, rm -f $(SOONG_HOST_OUT_EXECUTABLES)/acloud)
+
+# ************************************************
+# NEWER CLEAN STEPS MUST BE AT THE END OF THE LIST
+# ************************************************
diff --git a/create/base_avd_create.py b/create/base_avd_create.py
index a837c03..109f0cf 100644
--- a/create/base_avd_create.py
+++ b/create/base_avd_create.py
@@ -25,22 +25,24 @@
 class BaseAVDCreate(object):
     """Base class for all AVD intance creation classes."""
 
-    def _CreateAVD(self, avd_spec):
+    def _CreateAVD(self, avd_spec, no_prompts):
         """Do the actual creation work, should be overridden by child classes.
 
         Args:
             avd_spec: AVDSpec object that tells us what we're going to create.
+            no_prompts: Boolean, True to skip all prompts.
         """
         raise NotImplementedError
 
-    def Create(self, avd_spec):
+    def Create(self, avd_spec, no_prompts):
         """Create the AVD.
 
         Args:
             avd_spec: AVDSpec object that tells us what we're going to create.
+            no_prompts: Boolean, True to skip all prompts.
         """
         self.PrintAvdDetails(avd_spec)
-        results = self._CreateAVD(avd_spec)
+        results = self._CreateAVD(avd_spec, no_prompts)
         utils.PrintDeviceSummary(results)
         return results
 
diff --git a/create/cheeps_remote_image_remote_instance.py b/create/cheeps_remote_image_remote_instance.py
index 86a11c8..ef3b260 100644
--- a/create/cheeps_remote_image_remote_instance.py
+++ b/create/cheeps_remote_image_remote_instance.py
@@ -33,11 +33,12 @@
 
     @utils.TimeExecute(function_description="Total time: ",
                        print_before_call=False, print_status=False)
-    def _CreateAVD(self, avd_spec):
+    def _CreateAVD(self, avd_spec, no_prompts):
         """Create the AVD.
 
         Args:
             avd_spec: AVDSpec object that tells us what we're going to create.
+            no_prompts: Boolean, True to skip all prompts.
 
         Returns:
             A Report instance.
@@ -60,7 +61,7 @@
 
         # Launch vnc client if we're auto-connecting.
         if avd_spec.autoconnect:
-            utils.LaunchVNCFromReport(report, avd_spec)
+            utils.LaunchVNCFromReport(report, avd_spec, no_prompts)
 
         return report
 
diff --git a/create/cheeps_remote_image_remote_instance_test.py b/create/cheeps_remote_image_remote_instance_test.py
index fac4d81..473838d 100644
--- a/create/cheeps_remote_image_remote_instance_test.py
+++ b/create/cheeps_remote_image_remote_instance_test.py
@@ -73,7 +73,7 @@
         avd_spec.remote_image = {constants.BUILD_ID: self.ANDROID_BUILD_ID}
         avd_spec.autoconnect = False
         instance = cheeps_remote_image_remote_instance.CheepsRemoteImageRemoteInstance()
-        report = instance.Create(avd_spec)
+        report = instance.Create(avd_spec, no_prompts=False)
 
         # Verify
         self.compute_client.CreateInstance.assert_called_with(
diff --git a/create/create.py b/create/create.py
index 623124d..011eb8b 100644
--- a/create/create.py
+++ b/create/create.py
@@ -204,6 +204,6 @@
                                            spec.instance_type,
                                            spec.image_source)
     avd_creator = avd_creator_class()
-    report = avd_creator.Create(spec)
+    report = avd_creator.Create(spec, args.no_prompt)
     if report and args.report_file:
         report.Dump(args.report_file)
diff --git a/create/create_args.py b/create/create_args.py
index 8273a08..638e2ea 100644
--- a/create/create_args.py
+++ b/create/create_args.py
@@ -184,6 +184,13 @@
         dest="image_download_dir",
         required=False,
         help="Define remote image download directory, e.g. /usr/local/dl.")
+    create_parser.add_argument(
+        "--yes", "-y",
+        action="store_true",
+        dest="no_prompt",
+        required=False,
+        help=("Automatic yes to prompts. Assume 'yes' as answer to all prompts "
+              "and run non-interactively."))
     # User should not specify --spec and --hw_property at the same time.
     hw_spec_group = create_parser.add_mutually_exclusive_group()
     hw_spec_group.add_argument(
diff --git a/create/gce_local_image_remote_instance.py b/create/gce_local_image_remote_instance.py
index 8c3395e..84c0030 100644
--- a/create/gce_local_image_remote_instance.py
+++ b/create/gce_local_image_remote_instance.py
@@ -36,11 +36,12 @@
 
     @utils.TimeExecute(function_description="Total time: ",
                        print_before_call=False, print_status=False)
-    def _CreateAVD(self, avd_spec):
+    def _CreateAVD(self, avd_spec, no_prompts):
         """Create the AVD.
 
         Args:
             avd_spec: AVDSpec object that tells us what we're going to create.
+            no_prompts: Boolean, True to skip all prompts.
 
         Returns:
             A Report instance.
@@ -58,7 +59,7 @@
 
         # Launch vnc client if we're auto-connecting.
         if avd_spec.autoconnect:
-            utils.LaunchVNCFromReport(report, avd_spec)
+            utils.LaunchVNCFromReport(report, avd_spec, no_prompts)
 
         return report
 
diff --git a/create/gce_remote_image_remote_instance.py b/create/gce_remote_image_remote_instance.py
index f61b882..70836a8 100644
--- a/create/gce_remote_image_remote_instance.py
+++ b/create/gce_remote_image_remote_instance.py
@@ -32,11 +32,12 @@
 
     @utils.TimeExecute(function_description="Total time: ",
                        print_before_call=False, print_status=False)
-    def _CreateAVD(self, avd_spec):
+    def _CreateAVD(self, avd_spec, no_prompts):
         """Create the AVD.
 
         Args:
             avd_spec: AVDSpec object that tells us what we're going to create.
+            no_prompts: Boolean, True to skip all prompts.
 
         Returns:
             A Report instance.
@@ -52,6 +53,6 @@
 
         # Launch vnc client if we're auto-connecting.
         if avd_spec.autoconnect:
-            utils.LaunchVNCFromReport(report, avd_spec)
+            utils.LaunchVNCFromReport(report, avd_spec, no_prompts)
 
         return report
diff --git a/create/local_image_local_instance.py b/create/local_image_local_instance.py
index 75ca0de..147cd3a 100644
--- a/create/local_image_local_instance.py
+++ b/create/local_image_local_instance.py
@@ -27,6 +27,7 @@
 
 from acloud import errors
 from acloud.create import base_avd_create
+from acloud.delete import delete
 from acloud.internal import constants
 from acloud.internal.lib import utils
 from acloud.public import report
@@ -51,11 +52,12 @@
 
     @utils.TimeExecute(function_description="Total time: ",
                        print_before_call=False, print_status=False)
-    def _CreateAVD(self, avd_spec):
+    def _CreateAVD(self, avd_spec, no_prompts):
         """Create the AVD.
 
         Args:
             avd_spec: AVDSpec object that tells us what we're going to create.
+            no_prompts: Boolean, True to skip all prompts.
         """
         # Running instances on local is not supported on all OS.
         if not utils.IsSupportedPlatform(print_warning=True):
@@ -72,7 +74,7 @@
                                        avd_spec.hw_property,
                                        local_image_path)
         try:
-            self.CheckLaunchCVD(cmd, host_bins_path)
+            self.CheckLaunchCVD(cmd, host_bins_path, no_prompts)
         except errors.LaunchCVDFail as launch_error:
             raise launch_error
 
@@ -84,7 +86,7 @@
                    constants.VNC_PORT: constants.DEFAULT_VNC_PORT})
         # Launch vnc client if we're auto-connecting.
         if avd_spec.autoconnect:
-            utils.LaunchVNCFromReport(result_report, avd_spec)
+            utils.LaunchVNCFromReport(result_report, avd_spec, no_prompts)
         return result_report
 
     @staticmethod
@@ -137,12 +139,13 @@
         logger.debug("launch_cvd cmd:\n %s", launch_cmd)
         return launch_cmd
 
-    def CheckLaunchCVD(self, cmd, host_bins_path):
+    def CheckLaunchCVD(self, cmd, host_bins_path, no_prompts=False):
         """Execute launch_cvd command and wait for boot up completed.
 
         Args:
             cmd: String, launch_cvd command.
             host_bins_path: String of host package directory.
+            no_prompts: Boolean, True to skip all prompts.
         """
         # launch_cvd assumes host bins are in $ANDROID_HOST_OUT, let's overwrite
         # it to wherever we're running launch_cvd since they could be in a
@@ -151,7 +154,7 @@
         # Cuttlefish support launch single AVD at one time currently.
         if utils.IsCommandRunning(constants.CMD_LAUNCH_CVD):
             logger.info("Cuttlefish AVD is already running.")
-            if utils.GetUserAnswerYes(_CONFIRM_RELAUNCH):
+            if no_prompts or utils.GetUserAnswerYes(_CONFIRM_RELAUNCH):
                 stop_cvd_cmd = os.path.join(host_bins_path,
                                             "bin",
                                             constants.CMD_STOP_CVD)
@@ -160,6 +163,10 @@
                         utils.AddUserGroupsToCmd(
                             stop_cvd_cmd, constants.LIST_CF_USER_GROUPS),
                         stderr=dev_null, stdout=dev_null, shell=True)
+
+                # Delete ssvnc viewer
+                delete.CleanupSSVncviewer(constants.CF_TARGET_VNC_PORT)
+
             else:
                 print("Exiting out")
                 sys.exit()
diff --git a/create/local_image_remote_instance.py b/create/local_image_remote_instance.py
index 87b9ce6..4d247ac 100644
--- a/create/local_image_remote_instance.py
+++ b/create/local_image_remote_instance.py
@@ -275,11 +275,12 @@
 
     @utils.TimeExecute(function_description="Total time: ",
                        print_before_call=False, print_status=False)
-    def _CreateAVD(self, avd_spec):
+    def _CreateAVD(self, avd_spec, no_prompts):
         """Create the AVD.
 
         Args:
             avd_spec: AVDSpec object that tells us what we're going to create.
+            no_prompts: Boolean, True to skip all prompts.
         """
         self.VerifyArtifactsExist(avd_spec.local_image_dir)
         device_factory = RemoteInstanceDeviceFactory(
@@ -294,5 +295,5 @@
             adb_port=constants.CF_TARGET_ADB_PORT)
         # Launch vnc client if we're auto-connecting.
         if avd_spec.autoconnect:
-            utils.LaunchVNCFromReport(report, avd_spec)
+            utils.LaunchVNCFromReport(report, avd_spec, no_prompts)
         return report
diff --git a/create/remote_image_remote_instance.py b/create/remote_image_remote_instance.py
index 68a8dde..18ec9f6 100644
--- a/create/remote_image_remote_instance.py
+++ b/create/remote_image_remote_instance.py
@@ -29,11 +29,12 @@
 
     @utils.TimeExecute(function_description="Total time: ",
                        print_before_call=False, print_status=False)
-    def _CreateAVD(self, avd_spec):
+    def _CreateAVD(self, avd_spec, no_prompts):
         """Create the AVD.
 
         Args:
             avd_spec: AVDSpec object that tells us what we're going to create.
+            no_prompts: Boolean, True to skip all prompts.
 
         Returns:
             A Report instance.
@@ -41,6 +42,6 @@
         report = create_cuttlefish_action.CreateDevices(avd_spec=avd_spec)
         # Launch vnc client if we're auto-connecting.
         if avd_spec.autoconnect:
-            utils.LaunchVNCFromReport(report, avd_spec)
+            utils.LaunchVNCFromReport(report, avd_spec, no_prompts)
 
         return report
diff --git a/internal/lib/cheeps_compute_client.py b/internal/lib/cheeps_compute_client.py
index ffa62a3..5ef344a 100644
--- a/internal/lib/cheeps_compute_client.py
+++ b/internal/lib/cheeps_compute_client.py
@@ -37,6 +37,7 @@
 import getpass
 import logging
 
+from acloud import errors
 from acloud.internal.lib import android_compute_client
 from acloud.internal.lib import gcompute_client
 
@@ -49,7 +50,15 @@
     """
     # This is the timeout for betty to start.
     BOOT_TIMEOUT_SECS = 10*60
+    # This is printed by betty.sh.
     BOOT_COMPLETED_MSG = "VM successfully started"
+    # systemd prints this if betty.sh returns nonzero status code.
+    BOOT_FAILED_MSG = "betty.service: Failed with result 'exit-code'"
+
+    def CheckBootFailure(self, serial_out, instance):
+        """Overrides superclass. Determines if there's a boot failure."""
+        if self.BOOT_FAILED_MSG in serial_out:
+            raise errors.DeviceBootError("Betty failed to start")
 
     # pylint: disable=too-many-locals,arguments-differ
     def CreateInstance(self, instance, image_name, image_project,
diff --git a/internal/lib/utils.py b/internal/lib/utils.py
index 11cfa4f..0f19078 100755
--- a/internal/lib/utils.py
+++ b/internal/lib/utils.py
@@ -880,30 +880,33 @@
             return [answer_list[choice-start_index]]
 
 
-def LaunchVNCFromReport(report, avd_spec):
+def LaunchVNCFromReport(report, avd_spec, no_prompts=False):
     """Launch vnc client according to the instances report.
 
     Args:
         report: Report object, that stores and generates report.
         avd_spec: AVDSpec object that tells us what we're going to create.
+        no_prompts: Boolean, True to skip all prompts.
     """
     for device in report.data.get("devices", []):
         if device.get(constants.VNC_PORT):
             LaunchVncClient(device.get(constants.VNC_PORT),
                             avd_width=avd_spec.hw_property["x_res"],
-                            avd_height=avd_spec.hw_property["y_res"])
+                            avd_height=avd_spec.hw_property["y_res"],
+                            no_prompts=no_prompts)
         else:
             PrintColorString("No VNC port specified, skipping VNC startup.",
                              TextColors.FAIL)
 
 def LaunchVncClient(port=constants.DEFAULT_VNC_PORT, avd_width=None,
-                    avd_height=None):
+                    avd_height=None, no_prompts=False):
     """Launch ssvnc.
 
     Args:
         port: Integer, port number.
         avd_width: String, the width of avd.
         avd_height: String, the height of avd.
+        no_prompts: Boolean, True to skip all prompts.
     """
     try:
         os.environ[_ENV_DISPLAY]
@@ -913,7 +916,7 @@
         return
 
     if not find_executable(_VNC_BIN):
-        if GetUserAnswerYes(_CONFIRM_CONTINUE):
+        if no_prompts or GetUserAnswerYes(_CONFIRM_CONTINUE):
             try:
                 PrintColorString("Installing ssvnc vnc client... ", end="")
                 sys.stdout.flush()
diff --git a/list/instance.py b/list/instance.py
index d726839..4cc64a7 100644
--- a/list/instance.py
+++ b/list/instance.py
@@ -50,7 +50,7 @@
                           r"(.+%s)")
 _RE_TIMEZONE = re.compile(r"^(?P<time>[0-9\-\.:T]*)(?P<timezone>[+-]\d+:\d+)$")
 
-_COMMAND_PS_LAUNCH_CVD = ["ps", "-eo", "lstart,cmd"]
+_COMMAND_PS_LAUNCH_CVD = ["ps", "-wweo", "lstart,cmd"]
 _RE_LAUNCH_CVD = re.compile(r"(?P<date_str>^[^/]+)(.*launch_cvd --daemon )+"
                             r"((.*\s*-cpus\s)(?P<cpu>\d+))?"
                             r"((.*\s*-x_res\s)(?P<x_res>\d+))?"