Merge "Make delete a little bit handier."
diff --git a/Android.mk b/Android.mk
new file mode 100644
index 0000000..98bbce6
--- /dev/null
+++ b/Android.mk
@@ -0,0 +1,17 @@
+#!/usr/bin/env python
+#
+# 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.
+
+$(call dist-for-goals,droidcore,$(HOST_OUT_EXECUTABLES)/acloud)
diff --git a/create/local_image_local_instance.py b/create/local_image_local_instance.py
index b9c9a8d..a47606b 100644
--- a/create/local_image_local_instance.py
+++ b/create/local_image_local_instance.py
@@ -23,6 +23,7 @@
 import logging
 import os
 import subprocess
+import sys
 import time
 
 from acloud import errors
@@ -35,7 +36,6 @@
 logger = logging.getLogger(__name__)
 
 _BOOT_COMPLETE = "VIRTUAL_DEVICE_BOOT_COMPLETED"
-_CMD_LAUNCH_CVD = "launch_cvd"
 # TODO(b/117366819): Currently --serial_number is not working.
 _CMD_LAUNCH_CVD_ARGS = (" --daemon --cpus %s --x_res %s --y_res %s --dpi %s "
                         "--memory_mb %s --blank_data_image_mb %s "
@@ -72,7 +72,7 @@
                                        local_image_path,
                                        avd_spec.flavor)
         try:
-            self.CheckLaunchCVD(cmd)
+            self.CheckLaunchCVD(cmd, os.path.dirname(launch_cvd_path))
         except errors.LaunchCVDFail as launch_error:
             raise launch_error
 
@@ -117,7 +117,7 @@
 
         # Check if launch_cvd is exist.
         launch_cvd_path = os.path.join(
-            os.environ.get(_ENV_ANDROID_HOST_OUT), "bin", _CMD_LAUNCH_CVD)
+            os.environ.get(_ENV_ANDROID_HOST_OUT), "bin", constants.CMD_LAUNCH_CVD)
         if not os.path.exists(launch_cvd_path):
             raise errors.GetCvdLocalHostPackageError(
                 "No launch_cvd found. Please run \"m launch_cvd\" first")
@@ -177,28 +177,26 @@
         logger.debug("launch_cvd cmd:\n %s", combined_launch_cmd)
         return combined_launch_cmd
 
-    def CheckLaunchCVD(self, cmd):
+    @utils.TimeExecute(function_description="Waiting for AVD(s) to boot up")
+    def CheckLaunchCVD(self, cmd, host_pack_dir):
         """Execute launch_cvd command and wait for boot up completed.
 
         Args:
             cmd: String, launch_cvd command.
+            host_pack_dir: String of host package directory.
         """
-        start = time.time()
-
         # Cuttlefish support launch single AVD at one time currently.
         if self._IsLaunchCVDInUse():
             logger.info("Cuttlefish AVD is already running.")
             if utils.GetUserAnswerYes(_CONFIRM_RELAUNCH):
-                stop_cvd_cmd = os.path.join(os.environ.get(_ENV_ANDROID_HOST_OUT),
-                                            "bin", _CMD_STOP_CVD)
-                subprocess.check_output(stop_cvd_cmd)
-            else:
-                print("Only 1 cuttlefish AVD at a time, "
-                      "please stop the current AVD via #acloud delete")
-                return
+                stop_cvd_cmd = os.path.join(host_pack_dir, _CMD_STOP_CVD)
+                with open(os.devnull, "w") as dev_null:
+                    subprocess.check_call(stop_cvd_cmd, stderr=dev_null,
+                                          stdout=dev_null)
 
-        utils.PrintColorString("Waiting for AVD to boot... ",
-                               utils.TextColors.WARNING, end="")
+            else:
+                print("Exiting out")
+                sys.exit()
 
         process = subprocess.Popen(cmd, shell=True, stdout=subprocess.PIPE,
                                    stderr=subprocess.STDOUT)
@@ -208,13 +206,10 @@
             logger.debug(line.strip())
             # cvd is still running and got boot complete.
             if _BOOT_COMPLETE in line:
-                utils.PrintColorString("OK! (%ds)" % (time.time() - start),
-                                       utils.TextColors.OKGREEN)
                 boot_complete = True
                 break
 
         if not boot_complete:
-            utils.PrintColorString("Fail!", utils.TextColors.WARNING)
             raise errors.LaunchCVDFail(
                 "Can't launch cuttlefish AVD. No %s found" % _BOOT_COMPLETE)
 
@@ -226,7 +221,9 @@
             Boolean, True if launch_cvd is running. False otherwise.
         """
         try:
-            subprocess.check_output([_CMD_PGREP, _CMD_LAUNCH_CVD])
+            with open(os.devnull, "w") as dev_null:
+                subprocess.check_call([_CMD_PGREP, constants.CMD_LAUNCH_CVD],
+                                      stderr=dev_null, stdout=dev_null)
             return True
         except subprocess.CalledProcessError:
             # launch_cvd process is not in use.
diff --git a/create/local_image_local_instance_test.py b/create/local_image_local_instance_test.py
index 4544f6c..f29fd90 100644
--- a/create/local_image_local_instance_test.py
+++ b/create/local_image_local_instance_test.py
@@ -45,8 +45,7 @@
         constants.LIST_CF_USER_GROUPS = ["group1", "group2"]
 
         launch_cmd = self.local_image_local_instance.PrepareLaunchCVDCmd(
-            local_image_local_instance._CMD_LAUNCH_CVD,
-            hw_property, "fake_image_dir", "flavor")
+            constants.CMD_LAUNCH_CVD, hw_property, "fake_image_dir", "flavor")
 
         self.assertEqual(launch_cmd, self.LAUNCH_CVD_CMD)
 
diff --git a/create/remote_image_local_instance.py b/create/remote_image_local_instance.py
index 6d85287..3ec8e2a 100644
--- a/create/remote_image_local_instance.py
+++ b/create/remote_image_local_instance.py
@@ -25,7 +25,7 @@
 import tempfile
 
 from acloud import errors
-from acloud.create import base_avd_create
+from acloud.create import local_image_local_instance
 from acloud.internal import constants
 from acloud.internal.lib import android_build_client
 from acloud.internal.lib import auth
@@ -50,26 +50,36 @@
 logger = logging.getLogger(__name__)
 
 
-class RemoteImageLocalInstance(base_avd_create.BaseAVDCreate):
-    """Create class for a remote image local instance AVD."""
+class RemoteImageLocalInstance(local_image_local_instance.LocalImageLocalInstance):
+    """Create class for a remote image local instance AVD.
 
-    # pylint: disable=no-self-use
-    def Create(self, avd_spec):
-        """Create the AVD.
+    RemoteImageLocalInstance just defines logic in downloading the remote image
+    artifacts and leverages the existing logic to launch a local instance in
+    LocalImageLocalInstance.
+    """
+
+    @utils.TimeExecute(function_description="Downloading Android Build image")
+    def GetImageArtifactsPath(self, avd_spec):
+        """Download the image artifacts and return the paths to them.
 
         Args:
             avd_spec: AVDSpec object that tells us what we're going to create.
 
         Raises:
             errors.NoCuttlefishCommonInstalled: cuttlefish-common doesn't install.
+
+        Returns:
+            Tuple of (local image file, launch_cvd package) paths.
         """
-        print("We will create a local instance AVD with a remote image: %s" %
-              avd_spec)
         if not setup_common.PackageInstalled("cuttlefish-common"):
             raise errors.NoCuttlefishCommonInstalled(
                 "Package [cuttlefish-common] is not installed!\n"
                 "Please run 'acloud setup --host' to install.")
-        self._DownloadAndProcessImageFiles(avd_spec)
+
+        image_dir = self._DownloadAndProcessImageFiles(avd_spec)
+        launch_cvd_path = os.path.join(image_dir, "bin", constants.CMD_LAUNCH_CVD)
+
+        return image_dir, launch_cvd_path
 
     def _DownloadAndProcessImageFiles(self, avd_spec):
         """Download the CF image artifacts and process them.
@@ -79,20 +89,24 @@
 
         Args:
             avd_spec: AVDSpec object that tells us what we're going to create.
+
+        Returns:
+            extract_path: String, path to image folder.
         """
         cfg = avd_spec.cfg
         build_id = avd_spec.remote_image[constants.BUILD_ID]
         build_target = avd_spec.remote_image[constants.BUILD_TARGET]
         extract_path = os.path.join(_TEMP_IMAGE_FOLDER, build_id)
         logger.debug("Extract path: %s", extract_path)
+        # TODO(b/117189191): If extract folder exists, check if the files are
+        # already downloaded and skip this step if they are.
         if not os.path.exists(extract_path):
             os.makedirs(extract_path)
+            self._DownloadRemoteImage(cfg, build_target, build_id, extract_path)
+            self._UnpackBootImage(extract_path)
+            self._AclCfImageFiles(extract_path)
 
-        # TODO(b/117189191): Check if the files are already downloaded and
-        # skip this step if they are.
-        self._DownloadRemoteImage(cfg, build_target, build_id, extract_path)
-        self._UnpackBootImage(extract_path)
-        self._AclCfImageFiles(extract_path)
+        return extract_path
 
     @staticmethod
     def _DownloadRemoteImage(cfg, build_target, build_id, extract_path):
diff --git a/create/remote_image_local_instance_test.py b/create/remote_image_local_instance_test.py
index c35e7b2..e549ee5 100644
--- a/create/remote_image_local_instance_test.py
+++ b/create/remote_image_local_instance_test.py
@@ -46,18 +46,18 @@
         self._extract_path = "/tmp/acloud_image_artifacts/cuttlefish/1234"
 
     @mock.patch.object(RemoteImageLocalInstance, "_DownloadAndProcessImageFiles")
-    def testCreate(self, mock_proc):
-        """Test Create."""
+    def testGetImageArtifactsPath(self, mock_proc):
+        """Test get image artifacts path."""
         avd_spec = mock.MagicMock()
         # raise errors.NoCuttlefishCommonInstalled
         self.Patch(setup_common, "PackageInstalled", return_value=False)
         self.assertRaises(errors.NoCuttlefishCommonInstalled,
-                          self.RemoteImageLocalInstance.Create,
+                          self.RemoteImageLocalInstance.GetImageArtifactsPath,
                           avd_spec)
 
         # Valid _DownloadAndProcessImageFiles run.
         self.Patch(setup_common, "PackageInstalled", return_value=True)
-        self.RemoteImageLocalInstance.Create(avd_spec)
+        self.RemoteImageLocalInstance.GetImageArtifactsPath(avd_spec)
         mock_proc.assert_called_once_with(avd_spec)
 
     @mock.patch.object(RemoteImageLocalInstance, "_AclCfImageFiles")
@@ -68,7 +68,7 @@
         avd_spec = mock.MagicMock()
         avd_spec.cfg = mock.MagicMock()
         avd_spec.remote_image = self._fake_remote_image
-        self.Patch(os.path, "exists", return_value=True)
+        self.Patch(os.path, "exists", return_value=False)
         self.RemoteImageLocalInstance._DownloadAndProcessImageFiles(avd_spec)
 
         # To make sure each function execute once.
@@ -79,6 +79,8 @@
             self._extract_path)
         mock_unpack.assert_called_once_with(self._extract_path)
         mock_acl.assert_called_once_with(self._extract_path)
+        # Clean extarcted folder after test completed.
+        os.rmdir(self._extract_path)
 
     @mock.patch.object(utils, "TempDir")
     @mock.patch.object(utils, "Decompress")
diff --git a/internal/constants.py b/internal/constants.py
index 2ca62f2..80d22d0 100755
--- a/internal/constants.py
+++ b/internal/constants.py
@@ -95,6 +95,7 @@
 VNC_PORT = "vnc_port"
 ADB_PORT = "adb_port"
 
+CMD_LAUNCH_CVD = "launch_cvd"
 ENV_ANDROID_BUILD_TOP = "ANDROID_BUILD_TOP"
 
 LOCALHOST_ADB_SERIAL = "127.0.0.1:%d"
diff --git a/public/actions/create_cuttlefish_action.py b/public/actions/create_cuttlefish_action.py
index 5c2eb2c..db48a43 100644
--- a/public/actions/create_cuttlefish_action.py
+++ b/public/actions/create_cuttlefish_action.py
@@ -92,7 +92,8 @@
         # Create host instances for cuttlefish device. Currently one host instance
         # has one cuttlefish device. In the future, these logics should be modified
         # to support multiple cuttlefish devices per host instance.
-        instance = self._compute_client.GenerateInstanceName(self._build_id)
+        instance = self._compute_client.GenerateInstanceName(
+            build_id=self._build_id, build_target=self._build_target)
 
         # Create an instance from Stable Host Image
         if self.RELEASE_BRANCH_SUFFIX in self._branch: