Snap for 5612763 from a5a04590baca2daf95ba38b054af6d19331eed30 to qt-aml-release

Change-Id: Iae49802d9952e862c401e84b6cd5f680469127d8
diff --git a/Android.bp b/Android.bp
index 65b7b79..ef42197 100644
--- a/Android.bp
+++ b/Android.bp
@@ -73,6 +73,7 @@
         "public/*_test.py",
         "public/actions/*_test.py",
         "internal/lib/*_test.py",
+        "metrics/*.py",
     ],
     libs: [
         "acloud_create",
@@ -83,6 +84,7 @@
         "acloud_proto",
         "acloud_public",
         "acloud_setup",
+        "asuite_cc_client",
         "py-apitools",
         "py-dateutil",
         "py-google-api-python-client",
@@ -180,6 +182,7 @@
          "metrics/*.py",
     ],
     libs: [
+         "asuite_cc_client",
          "asuite_metrics",
     ],
 }
diff --git a/PREUPLOAD.cfg b/PREUPLOAD.cfg
index 136a761..e5c5e28 100644
--- a/PREUPLOAD.cfg
+++ b/PREUPLOAD.cfg
@@ -2,4 +2,4 @@
 pylint = true
 
 [Hook Scripts]
-acloud_unittests = ${REPO_ROOT}/prebuilts/asuite/atest/${BUILD_OS}/atest acloud_test
+acloud_unittests = ${REPO_ROOT}/prebuilts/asuite/atest/${BUILD_OS}/atest acloud_test --host
diff --git a/create/avd_spec.py b/create/avd_spec.py
index 039b167..84bcbc4 100644
--- a/create/avd_spec.py
+++ b/create/avd_spec.py
@@ -20,6 +20,7 @@
 initialization of AVDSpec (like LKGB build id, image branch, etc).
 """
 
+import glob
 import logging
 import os
 import re
@@ -31,6 +32,7 @@
 from acloud.internal import constants
 from acloud.internal.lib import android_build_client
 from acloud.internal.lib import auth
+from acloud.internal.lib import utils
 from acloud.public import config
 
 # Default values for build target.
@@ -39,10 +41,16 @@
 _BUILD_BRANCH = "build_branch"
 _BUILD_ID = "build_id"
 _COMMAND_REPO_INFO = ["repo", "info"]
+_CF_ZIP_PATTERN = "*img*.zip"
 _DEFAULT_BUILD_BITNESS = "x86"
 _DEFAULT_BUILD_TYPE = "userdebug"
 _ENV_ANDROID_PRODUCT_OUT = "ANDROID_PRODUCT_OUT"
 _ENV_ANDROID_BUILD_TOP = "ANDROID_BUILD_TOP"
+_GCE_LOCAL_IMAGE_CANDIDATES = ["avd-system.tar.gz",
+                               "android_system_disk_syslinux.img"]
+_LOCAL_ZIP_WARNING_MSG = "'adb sync' will take a long time if using images " \
+                         "built with `m dist`. Building with just `m` will " \
+                         "enable a faster 'adb sync' process."
 _RE_ANSI_ESCAPE = re.compile(r"(\x9B|\x1B\[)[0-?]*[ -\/]*[@-~]")
 _RE_FLAVOR = re.compile(r"^.+_(?P<flavor>.+)-img.+")
 _RE_GBSIZE = re.compile(r"^(?P<gb_size>\d+)g$", re.IGNORECASE)
@@ -107,6 +115,9 @@
         # Reporting args.
         self._serial_log_file = None
         self._logcat_file = None
+        # gpu and emulator_build_id is only used for goldfish avd_type.
+        self._gpu = None
+        self._emulator_build_id = None
 
         self._ProcessArgs(args)
 
@@ -248,88 +259,128 @@
         self._kernel_build_id = args.kernel_build_id
         self._serial_log_file = args.serial_log_file
         self._logcat_file = args.logcat_file
+        self._emulator_build_id = args.emulator_build_id
+        self._gpu = args.gpu
 
     @staticmethod
-    def _GetFlavorFromLocalImage(image_path):
-        """Get flavor name from local image file name.
+    def _GetFlavorFromString(flavor_string):
+        """Get flavor name from flavor string.
 
-        If the user didn't specify a flavor, we can infer it from the image
-        name, e.g. cf_x86_tv-img-eng.zip should be created with a flavor of tv.
+        Flavor string can come from the zipped image name or the lunch target.
+        e.g.
+        If flavor_string come from zipped name:aosp_cf_x86_phone-img-5455843.zip
+        , then "phone" is the flavor.
+        If flavor_string come from a lunch'd target:aosp_cf_x86_auto-userdebug,
+        then "auto" is the flavor.
 
         Args:
-            image_path: String of image path.
-
-        Returns:
-            String of flavor name, None if flavor can't be determined.
-        """
-        local_image_name = os.path.basename(image_path)
-        match = _RE_FLAVOR.match(local_image_name)
-        if match:
-            image_flavor = match.group("flavor")
-            if image_flavor in constants.ALL_FLAVORS:
-                logger.debug("Get flavor[%s] from local image name(%s).",
-                             image_flavor, local_image_name)
-                return image_flavor
-            else:
-                logger.debug("Flavor[%s] from image name is not supported.",
-                             image_flavor)
-
-        return None
-
-    @staticmethod
-    def _GetFlavorFromTarget(build_target):
-        """Get flavor name from build target name.
-
-        If the user didn't specify a flavor, we can infer it from the target
-        name, e.g. cf_x86_phone-userdebug should be created with a flavor of
-        phone.
-
-        Args:
-            build_target: String of build target name.
+            flavor_string: String which contains flavor.It can be a
+                           build target or filename.
 
         Returns:
             String of flavor name. None if flavor can't be determined.
         """
         for flavor in constants.ALL_FLAVORS:
-            if re.match(r"(.*_)?%s-" % flavor, build_target):
+            if re.match(r"(.*_)?%s" % flavor, flavor_string):
                 return flavor
 
         logger.debug("Unable to determine flavor from build target: %s",
-                     build_target)
+                     flavor_string)
         return None
 
     def _ProcessLocalImageArgs(self, args):
         """Get local image path.
 
-        -Specified local_image with no arg: Set $ANDROID_PRODUCT_OUT.
-        -Specified local_image with an arg: Set user specified path.
-
         Args:
             args: Namespace object from argparse.parse_args.
+        """
+        if self._avd_type == constants.TYPE_CF:
+            self._ProcessCFLocalImageArgs(args.local_image, args.flavor)
+        elif self._avd_type == constants.TYPE_GCE:
+            self._local_image_artifact = self._GetGceLocalImagePath(
+                args.local_image)
+        else:
+            raise errors.CreateError(
+                "Local image doesn't support the AVD type: %s" % self._avd_type
+            )
+
+    @staticmethod
+    def _GetGceLocalImagePath(local_image_dir):
+        """Get gce local image path.
+
+        Choose image file in local_image_dir over $ANDROID_PRODUCT_OUT.
+        There are various img files so we prioritize returning the one we find
+        first based in the specified order in _GCE_LOCAL_IMAGE_CANDIDATES.
+
+        Args:
+            local_image_dir: A string to specify local image dir.
+
+        Returns:
+            String, image file path if exists.
 
         Raises:
-            errors.CreateError: Can't get $ANDROID_PRODUCT_OUT.
+            errors.BootImgDoesNotExist if image doesn't exist.
         """
-        if args.local_image:
-            self._local_image_dir = args.local_image
-        else:
-            try:
-                self._local_image_dir = os.environ[_ENV_ANDROID_PRODUCT_OUT]
-            except KeyError:
-                raise errors.GetAndroidBuildEnvVarError(
-                    "Could not get environment var: %s\n"
-                    "Try to run '#source build/envsetup.sh && lunch <target>'"
-                    % _ENV_ANDROID_PRODUCT_OUT
-                )
+        # IF the user specified a file, return it
+        if local_image_dir and os.path.isfile(local_image_dir):
+            return local_image_dir
 
-        self._local_image_artifact = create_common.VerifyLocalImageArtifactsExist(
-            self.local_image_dir)
-        # Overwrite flavor by local image name
-        local_image_flavor = self._GetFlavorFromLocalImage(
-            self._local_image_artifact)
-        if local_image_flavor and not args.flavor:
-            self._flavor = local_image_flavor
-            self._cfg.OverrideHwPropertyWithFlavor(local_image_flavor)
+        # If the user didn't specify a dir, assume $ANDROID_PRODUCT_OUT
+        if not local_image_dir:
+            local_image_dir = utils.GetBuildEnvironmentVariable(
+                _ENV_ANDROID_PRODUCT_OUT)
+
+        for img_name in _GCE_LOCAL_IMAGE_CANDIDATES:
+            full_file_path = os.path.join(local_image_dir, img_name)
+            if os.path.exists(full_file_path):
+                return full_file_path
+
+        raise errors.BootImgDoesNotExist("Could not find any GCE images (%s), "
+                                         "you can build them via \"m dist\"" %
+                                         ", ".join(_GCE_LOCAL_IMAGE_CANDIDATES))
+
+    def _ProcessCFLocalImageArgs(self, local_image_arg, flavor_arg):
+        """Get local built image path for cuttlefish-type AVD.
+
+        Two scenarios of using --local-image:
+        - Without a following argument
+          Set flavor string if the required images are in $ANDROID_PRODUCT_OUT,
+        - With a following filename/dirname
+          Set flavor string from the specified image/dir name.
+
+        Args:
+            local_image_arg: String of local image args.
+            flavor_arg: String of flavor arg
+
+        """
+        flavor_from_build_string = None
+        local_image_path = local_image_arg or utils.GetBuildEnvironmentVariable(
+            _ENV_ANDROID_PRODUCT_OUT)
+
+        if os.path.isfile(local_image_path):
+            self._local_image_artifact = local_image_arg
+            flavor_from_build_string = self._GetFlavorFromString(
+                self._local_image_artifact)
+            # Since file is provided and I assume it's a zip, so print the
+            # warning message.
+            utils.PrintColorString(_LOCAL_ZIP_WARNING_MSG,
+                                   utils.TextColors.WARNING)
+        else:
+            self._local_image_dir = local_image_path
+            # Since dir is provided, so checking that any images exist to ensure
+            # user didn't forget to 'make' before launch AVD.
+            image_list = glob.glob(os.path.join(self.local_image_dir, "*.img"))
+            if not image_list:
+                raise errors.GetLocalImageError(
+                    "No image found(Did you choose a lunch target and run `m`?)"
+                    ": %s.\n " % self.local_image_dir)
+
+            flavor_from_build_string = self._GetFlavorFromString(
+                utils.GetBuildEnvironmentVariable(constants.ENV_BUILD_TARGET))
+
+        if flavor_from_build_string and not flavor_arg:
+            self._flavor = flavor_from_build_string
+            self._cfg.OverrideHwPropertyWithFlavor(flavor_from_build_string)
 
     def _ProcessRemoteBuildArgs(self, args):
         """Get the remote build args.
@@ -351,7 +402,7 @@
         else:
             # If flavor isn't specified, try to infer it from build target,
             # if we can't, just default to phone flavor.
-            self._flavor = args.flavor or self._GetFlavorFromTarget(
+            self._flavor = args.flavor or self._GetFlavorFromString(
                 self._remote_image[_BUILD_TARGET]) or constants.FLAVOR_PHONE
             # infer avd_type from build_target.
             for avd_type, avd_type_abbr in constants.AVD_TYPES_MAPPING.items():
@@ -516,3 +567,13 @@
     def logcat_file(self):
         """Return logcat file path."""
         return self._logcat_file
+
+    @property
+    def gpu(self):
+        """Return gpu."""
+        return self._gpu
+
+    @property
+    def emulator_build_id(self):
+        """Return emulator_build_id."""
+        return self._emulator_build_id
diff --git a/create/avd_spec_test.py b/create/avd_spec_test.py
index 5d5681b..48b8038 100644
--- a/create/avd_spec_test.py
+++ b/create/avd_spec_test.py
@@ -13,21 +13,27 @@
 # limitations under the License.
 """Tests for avd_spec."""
 
+import glob
+import os
 import unittest
 import mock
 
+
 from acloud import errors
 from acloud.create import avd_spec
 from acloud.create import create_common
 from acloud.internal import constants
+from acloud.internal.lib import driver_test_lib
+from acloud.internal.lib import utils
 
 
 # pylint: disable=invalid-name,protected-access
-class AvdSpecTest(unittest.TestCase):
+class AvdSpecTest(driver_test_lib.BaseDriverTest):
     """Test avd_spec methods."""
 
     def setUp(self):
         """Initialize new avd_spec.AVDSpec."""
+        super(AvdSpecTest, self).setUp()
         self.args = mock.MagicMock()
         self.args.local_image = ""
         self.args.config_file = ""
@@ -35,36 +41,69 @@
         self.AvdSpec = avd_spec.AVDSpec(self.args)
 
     # pylint: disable=protected-access
-    @mock.patch.object(create_common, "VerifyLocalImageArtifactsExist")
-    def testProcessLocalImageArgs(self, mock_image):
+    def testProcessLocalImageArgs(self):
         """Test process args.local_image."""
-        # Specified local_image with an arg
-        mock_image.return_value = "cf_x86_phone-img-eng.user.zip"
-        self.args.local_image = "test_path"
+        self.Patch(create_common, "ZipCFImageFiles",
+                   return_value="/path/cf_x86_phone-img-eng.user.zip")
+        self.Patch(glob, "glob", return_value=["fake.img"])
+        expected_image_artifact = "/path/cf_x86_phone-img-eng.user.zip"
+        expected_image_dir = "/path-to-image-dir"
+
+        # Specified --local-image to a local zipped image file
+        self.Patch(os.path, "isfile", return_value=True)
+        self.args.local_image = "/path/cf_x86_phone-img-eng.user.zip"
+        self.AvdSpec._avd_type = constants.TYPE_CF
+        self.AvdSpec._instance_type = constants.INSTANCE_TYPE_REMOTE
         self.AvdSpec._ProcessLocalImageArgs(self.args)
-        self.assertEqual(self.AvdSpec._local_image_dir, "test_path")
+        self.assertEqual(self.AvdSpec._local_image_artifact,
+                         expected_image_artifact)
 
-        # Specified local_image with no arg
+        # Specified --local-image to a dir contains images
+        self.Patch(utils, "GetBuildEnvironmentVariable",
+                   return_value="test_environ")
+        self.Patch(os.path, "isfile", return_value=False)
+        self.args.local_image = "/path-to-image-dir"
+        self.AvdSpec._avd_type = constants.TYPE_CF
+        self.AvdSpec._instance_type = constants.INSTANCE_TYPE_REMOTE
+        self.AvdSpec._ProcessLocalImageArgs(self.args)
+        self.assertEqual(self.AvdSpec._local_image_dir, expected_image_dir)
+
+        # Specified local_image without arg
         self.args.local_image = None
-        with mock.patch.dict("os.environ", {"ANDROID_PRODUCT_OUT": "test_environ"}):
-            self.AvdSpec._ProcessLocalImageArgs(self.args)
-            self.assertEqual(self.AvdSpec._local_image_dir, "test_environ")
+        self.Patch(utils, "GetBuildEnvironmentVariable",
+                   return_value="test_environ")
+        self.AvdSpec._ProcessLocalImageArgs(self.args)
+        self.assertEqual(self.AvdSpec._local_image_dir, "test_environ")
+        self.assertEqual(self.AvdSpec.local_image_artifact, expected_image_artifact)
 
-    @mock.patch.object(create_common, "VerifyLocalImageArtifactsExist")
-    def testProcessImageArgs(self, mock_image):
+    def testProcessImageArgs(self):
         """Test process image source."""
+        self.Patch(glob, "glob", return_value=["fake.img"])
         # No specified local_image, image source is from remote
         self.args.local_image = ""
         self.AvdSpec._ProcessImageArgs(self.args)
         self.assertEqual(self.AvdSpec._image_source, constants.IMAGE_SRC_REMOTE)
         self.assertEqual(self.AvdSpec._local_image_dir, None)
 
-        # Specified local_image with an arg, image source is from local
-        mock_image.return_value = "cf_x86_phone-img-eng.user.zip"
-        self.args.local_image = "test_path"
+        # Specified local_image with an arg for cf type
+        self.Patch(os.path, "isfile", return_value=True)
+        self.args.local_image = "/test_path/cf_x86_phone-img-eng.user.zip"
+        self.AvdSpec._avd_type = constants.TYPE_CF
+        self.AvdSpec._instance_type = constants.INSTANCE_TYPE_REMOTE
         self.AvdSpec._ProcessImageArgs(self.args)
         self.assertEqual(self.AvdSpec._image_source, constants.IMAGE_SRC_LOCAL)
-        self.assertEqual(self.AvdSpec._local_image_dir, "test_path")
+        self.assertEqual(self.AvdSpec._local_image_artifact,
+                         "/test_path/cf_x86_phone-img-eng.user.zip")
+
+        # Specified local_image with an arg for gce type
+        self.Patch(os.path, "isfile", return_value=False)
+        self.Patch(os.path, "exists", return_value=True)
+        self.args.local_image = "/test_path_to_dir/"
+        self.AvdSpec._avd_type = constants.TYPE_GCE
+        self.AvdSpec._ProcessImageArgs(self.args)
+        self.assertEqual(self.AvdSpec._image_source, constants.IMAGE_SRC_LOCAL)
+        self.assertEqual(self.AvdSpec._local_image_artifact,
+                         "/test_path_to_dir/avd-system.tar.gz")
 
     @mock.patch.object(avd_spec.AVDSpec, "_GetGitRemote")
     @mock.patch("subprocess.check_output")
@@ -144,23 +183,20 @@
         result_dict = self.AvdSpec._ParseHWPropertyStr(args_str)
         self.assertTrue(expected_dict == result_dict)
 
-    def testGetFlavorFromLocalImage(self):
+    def testGetFlavorFromBuildTargetString(self):
         """Test _GetFlavorFromLocalImage."""
         img_path = "/fack_path/cf_x86_tv-img-eng.user.zip"
-        self.assertEqual(self.AvdSpec._GetFlavorFromLocalImage(img_path), "tv")
+        self.assertEqual(self.AvdSpec._GetFlavorFromString(img_path),
+                         "tv")
+
+        build_target_str = "aosp_cf_x86_auto"
+        self.assertEqual(self.AvdSpec._GetFlavorFromString(
+            build_target_str), "auto")
 
         # Flavor is not supported.
         img_path = "/fack_path/cf_x86_error-img-eng.user.zip"
-        self.assertEqual(self.AvdSpec._GetFlavorFromLocalImage(img_path), None)
-
-    def testGetFlavorFromTarget(self):
-        """Test _GetFlavorFromTarget."""
-        target_name = "cf_x86_auto-userdebug"
-        self.assertEqual(self.AvdSpec._GetFlavorFromTarget(target_name), "auto")
-
-        # Target is not supported.
-        target_name = "cf_x86_error-userdebug"
-        self.assertEqual(self.AvdSpec._GetFlavorFromTarget(target_name), None)
+        self.assertEqual(self.AvdSpec._GetFlavorFromString(img_path),
+                         None)
 
     # pylint: disable=protected-access
     def testProcessRemoteBuildArgs(self):
@@ -212,6 +248,39 @@
         expected_result = " Manifest branch:"
         self.assertEqual(avd_spec.EscapeAnsi(test_string), expected_result)
 
+    def testGetGceLocalImagePath(self):
+        """Test get gce local image path."""
+        self.Patch(os.path, "isfile", return_value=True)
+        # Verify when specify --local-image ~/XXX.tar.gz.
+        fake_image_path = "~/gce_local_image_dir/gce_image.tar.gz"
+        self.Patch(os.path, "exists", return_value=True)
+        self.assertEqual(self.AvdSpec._GetGceLocalImagePath(fake_image_path),
+                         "~/gce_local_image_dir/gce_image.tar.gz")
+
+        # Verify when specify --local-image ~/XXX.img.
+        fake_image_path = "~/gce_local_image_dir/gce_image.img"
+        self.assertEqual(self.AvdSpec._GetGceLocalImagePath(fake_image_path),
+                         "~/gce_local_image_dir/gce_image.img")
+
+        # Verify if exist argument --local-image as a directory.
+        self.Patch(os.path, "isfile", return_value=False)
+        self.Patch(os.path, "exists", return_value=True)
+        fake_image_path = "~/gce_local_image_dir/"
+        # Default to find */avd-system.tar.gz if exist then return the path.
+        self.assertEqual(self.AvdSpec._GetGceLocalImagePath(fake_image_path),
+                         "~/gce_local_image_dir/avd-system.tar.gz")
+
+        # Otherwise choose raw file */android_system_disk_syslinux.img if
+        # exist then return the path.
+        self.Patch(os.path, "exists", side_effect=[False, True])
+        self.assertEqual(self.AvdSpec._GetGceLocalImagePath(fake_image_path),
+                         "~/gce_local_image_dir/android_system_disk_syslinux.img")
+
+        # Both _GCE_LOCAL_IMAGE_CANDIDATE could not be found then raise error.
+        self.Patch(os.path, "exists", side_effect=[False, False])
+        self.assertRaises(errors.BootImgDoesNotExist,
+                          self.AvdSpec._GetGceLocalImagePath, fake_image_path)
+
 
 if __name__ == "__main__":
     unittest.main()
diff --git a/create/base_avd_create.py b/create/base_avd_create.py
index 109f0cf..1e5341c 100644
--- a/create/base_avd_create.py
+++ b/create/base_avd_create.py
@@ -68,7 +68,8 @@
             avd_spec.instance_type)
         if avd_spec.image_source == constants.IMAGE_SRC_LOCAL:
             utils.PrintColorString("Image (local):")
-            utils.PrintColorString("  %s" % avd_spec.local_image_dir)
+            utils.PrintColorString("  %s" % (avd_spec.local_image_dir or
+                                             avd_spec.local_image_artifact))
         elif avd_spec.image_source == constants.IMAGE_SRC_REMOTE:
             utils.PrintColorString("Image:")
             utils.PrintColorString(
diff --git a/create/cheeps_remote_image_remote_instance.py b/create/cheeps_remote_image_remote_instance.py
index ef3b260..1f6602f 100644
--- a/create/cheeps_remote_image_remote_instance.py
+++ b/create/cheeps_remote_image_remote_instance.py
@@ -48,16 +48,16 @@
             "Creating a cheeps device in project %s, build_id: %s",
             avd_spec.cfg.project, build_id)
 
-        device_factory = CheepsDeviceFactory(avd_spec.cfg, build_id)
+        device_factory = CheepsDeviceFactory(avd_spec.cfg, build_id, avd_spec)
 
         report = common_operations.CreateDevices(
             command="create_cheeps",
             cfg=avd_spec.cfg,
             device_factory=device_factory,
             num=avd_spec.num,
+            report_internal_ip=avd_spec.report_internal_ip,
             autoconnect=avd_spec.autoconnect,
-            vnc_port=constants.DEFAULT_CHEEPS_TARGET_VNC_PORT,
-            adb_port=constants.DEFAULT_CHEEPS_TARGET_ADB_PORT)
+            avd_type=constants.TYPE_CHEEPS)
 
         # Launch vnc client if we're auto-connecting.
         if avd_spec.autoconnect:
@@ -76,12 +76,13 @@
     """
     LOG_FILES = []
 
-    def __init__(self, cfg, build_id):
+    def __init__(self, cfg, build_id, avd_spec=None):
         """Initialize.
 
         Args:
             cfg: An AcloudConfig instance.
             build_id: String, Build id, e.g. "2263051", "P2804227"
+            avd_spec: An AVDSpec instance.
         """
         self.credentials = auth.CreateCredentials(cfg)
 
@@ -91,6 +92,7 @@
 
         self._cfg = cfg
         self._build_id = build_id
+        self._avd_spec = avd_spec
 
     def CreateInstance(self):
         """Creates single configured cheeps device.
@@ -103,5 +105,6 @@
             instance=instance,
             image_name=self._cfg.stable_cheeps_host_image_name,
             image_project=self._cfg.stable_cheeps_host_image_project,
-            build_id=self._build_id)
+            build_id=self._build_id,
+            avd_spec=self._avd_spec)
         return instance
diff --git a/create/cheeps_remote_image_remote_instance_test.py b/create/cheeps_remote_image_remote_instance_test.py
index 473838d..df0e164 100644
--- a/create/cheeps_remote_image_remote_instance_test.py
+++ b/create/cheeps_remote_image_remote_instance_test.py
@@ -72,6 +72,7 @@
         avd_spec.cfg = self._CreateCfg()
         avd_spec.remote_image = {constants.BUILD_ID: self.ANDROID_BUILD_ID}
         avd_spec.autoconnect = False
+        avd_spec.report_internal_ip = False
         instance = cheeps_remote_image_remote_instance.CheepsRemoteImageRemoteInstance()
         report = instance.Create(avd_spec, no_prompts=False)
 
@@ -80,10 +81,12 @@
             instance=self.INSTANCE,
             image_name=self.CHEEPS_HOST_IMAGE_NAME,
             image_project=self.CHEEPS_HOST_IMAGE_PROJECT,
-            build_id=self.ANDROID_BUILD_ID)
+            build_id=self.ANDROID_BUILD_ID,
+            avd_spec=avd_spec)
 
         self.assertEquals(report.data, {
             "devices": [{
+                "build_id": self.ANDROID_BUILD_ID,
                 "instance_name": self.INSTANCE,
                 "ip": self.IP.external,
             },],
diff --git a/create/create.py b/create/create.py
index 011eb8b..9dfba02 100644
--- a/create/create.py
+++ b/create/create.py
@@ -32,6 +32,7 @@
 from acloud.create import cheeps_remote_image_remote_instance
 from acloud.create import gce_local_image_remote_instance
 from acloud.create import gce_remote_image_remote_instance
+from acloud.create import goldfish_remote_image_remote_instance
 from acloud.create import local_image_local_instance
 from acloud.create import local_image_remote_instance
 from acloud.create import remote_image_remote_instance
@@ -63,6 +64,9 @@
     # Cheeps types
     (constants.TYPE_CHEEPS, constants.IMAGE_SRC_REMOTE, constants.INSTANCE_TYPE_REMOTE):
         cheeps_remote_image_remote_instance.CheepsRemoteImageRemoteInstance,
+    # GF types
+    (constants.TYPE_GF, constants.IMAGE_SRC_REMOTE, constants.INSTANCE_TYPE_REMOTE):
+        goldfish_remote_image_remote_instance.GoldfishRemoteImageRemoteInstance,
 }
 
 
@@ -179,7 +183,7 @@
             setup.Run(args)
         else:
             print("Please run '#acloud setup' so we can get your host setup")
-            sys.exit()
+            sys.exit(constants.EXIT_BY_USER)
 
 
 def PreRunCheck(args):
diff --git a/create/create_args.py b/create/create_args.py
index 638e2ea..4ed04f8 100644
--- a/create/create_args.py
+++ b/create/create_args.py
@@ -177,7 +177,9 @@
         default="",
         required=False,
         help="Use the locally built image for the AVD. Look for the image "
-        "artifact in $ANDROID_TARGET_OUT unless a path is specified.")
+        "artifact in $ANDROID_PRODUCT_OUT if no args value is provided."
+        "e.g --local-image or --local-image /path/to/dir or --local-image "
+        "/path/to/file")
     create_parser.add_argument(
         "--image-download-dir",
         type=str,
@@ -208,6 +210,24 @@
         choices=constants.SPEC_NAMES,
         help="The name of a pre-configured device spec that we are "
         "going to use.")
+    # Arguments for goldfish type.
+    # TODO(b/118439885): Verify args that are used in wrong avd_type.
+    # e.g. $acloud create --avd-type cuttlefish --emulator-build-id
+    create_parser.add_argument(
+        "--gpu",
+        type=str,
+        dest="gpu",
+        required=False,
+        default=None,
+        help="'goldfish only' GPU accelerator to use if any. "
+        "e.g. nvidia-tesla-k80, omit to use swiftshader")
+    create_parser.add_argument(
+        "--emulator-build-id",
+        type=int,
+        dest="emulator_build_id",
+        required=False,
+        help="'goldfish only' Emulator build used to run the images. "
+        "e.g. 4669466.")
 
     AddCommonCreateArgs(create_parser)
     return create_parser
@@ -220,7 +240,7 @@
         args: Namespace object from argparse.parse_args.
 
     Raises:
-        errors.CreateError: Path doesn't exist.
+        errors.CheckPathError: Zipped image path doesn't exist.
         errors.UnsupportedFlavor: Flavor doesn't support.
     """
     # Verify that user specified flavor name is in support list.
diff --git a/create/create_common.py b/create/create_common.py
index 6598cf9..3480228 100644
--- a/create/create_common.py
+++ b/create/create_common.py
@@ -20,12 +20,18 @@
 import glob
 import logging
 import os
+import tempfile
+import time
+import zipfile
 
 from acloud import errors
+from acloud.internal import constants
 from acloud.internal.lib import utils
 
 logger = logging.getLogger(__name__)
 
+_ACLOUD_IMAGE_ZIP_POSTFIX = "-local-img-%s.zip"
+
 
 def ParseHWPropertyArgs(dict_str, item_separator=",", key_value_separator=":"):
     """Helper function to initialize a dict object from string.
@@ -62,43 +68,40 @@
     return hw_dict
 
 
-def VerifyLocalImageArtifactsExist(local_image_dir):
-    """Verify local image artifacts exists.
+@utils.TimeExecute(function_description="Compressing images")
+def ZipCFImageFiles(basedir):
+    """Zip images from basedir.
 
-    Look for the image in the local_image_dir and dist dir as backup.
-
-    The image name follows the pattern:
-    Remote image: {target product}-img-{build id}.zip,
-                  e.g. aosp_cf_x86_phone-img-5046769.zip
-    Local built image: {target product}-img-{username}.zip,
-                       e.g. aosp_cf_x86_64_phone-img-eng.droid.zip
+    TODO(b/129376163):Use lzop for fast sparse image upload when host image
+    support it.
 
     Args:
-        local_image_dir: A string to specifies local image dir.
+        basedir: String of local images path.
 
     Return:
-        Strings of local image path.
-
-    Raises:
-        errors.GetLocalImageError: Can't find local image.
+        Strings of zipped image path.
     """
-    dirs_to_check = [local_image_dir]
-    dist_dir = utils.GetDistDir()
-    if dist_dir:
-        dirs_to_check.append(dist_dir)
-    for img_dir in dirs_to_check:
-        image_pattern = os.path.join(img_dir, "*img*.zip")
-        images = glob.glob(image_pattern)
-        if images:
-            break
-    if not images:
-        raise errors.GetLocalImageError("No images found in %s\n"
-                                        "(Try building with 'm dist')" %
-                                        [local_image_dir, dist_dir])
-    if len(images) > 1:
-        print("Multiple images found, please choose 1.")
-        image_path = utils.GetAnswerFromList(images)[0]
-    else:
-        image_path = images[0]
-    logger.debug("Local image: %s ", image_path)
-    return image_path
+    tmp_folder = os.path.join(tempfile.gettempdir(),
+                              constants.TEMP_ARTIFACTS_FOLDER)
+    if not os.path.exists(tmp_folder):
+        os.makedirs(tmp_folder)
+    archive_name = "%s-local-%d.zip" % (os.environ.get(constants.ENV_BUILD_TARGET),
+                                        int(time.time()))
+    archive_file = os.path.join(tmp_folder, archive_name)
+    if os.path.exists(archive_file):
+        raise errors.ZipImageError("This file shouldn't exist, please delete: %s"
+                                   % archive_file)
+
+    zip_file = zipfile.ZipFile(archive_file, 'w', zipfile.ZIP_DEFLATED,
+                               allowZip64=True)
+    required_files = ([os.path.join(basedir, "android-info.txt")] +
+                      glob.glob(os.path.join(basedir, "*.img")))
+    logger.debug("archiving images: %s", required_files)
+
+    for f in required_files:
+        # Pass arcname arg to remove the directory structure.
+        zip_file.write(f, arcname=os.path.basename(f))
+
+    zip_file.close()
+    logger.debug("zip images done:%s", archive_file)
+    return archive_file
diff --git a/create/create_common_test.py b/create/create_common_test.py
index 25b193a..2af3177 100644
--- a/create/create_common_test.py
+++ b/create/create_common_test.py
@@ -13,15 +13,35 @@
 # limitations under the License.
 """Tests for create_common."""
 
+import os
+import tempfile
+import time
 import unittest
-import mock
+import zipfile
 
 from acloud import errors
 from acloud.create import create_common
+from acloud.internal import constants
+from acloud.internal.lib import driver_test_lib
+
+
+
+class FakeZipFile(object):
+    """Fake implementation of ZipFile()"""
+
+    # pylint: disable=invalid-name,unused-argument,no-self-use
+    def write(self, filename, arcname=None, compress_type=None):
+        """Fake write method."""
+        return
+
+    # pylint: disable=invalid-name,no-self-use
+    def close(self):
+        """Fake close method."""
+        return
 
 
 # pylint: disable=invalid-name,protected-access
-class CreateCommonTest(unittest.TestCase):
+class CreateCommonTest(driver_test_lib.BaseDriverTest):
     """Test create_common functions."""
 
     # pylint: disable=protected-access
@@ -45,21 +65,25 @@
         result_dict = create_common.ParseHWPropertyArgs(args_str)
         self.assertTrue(expected_dict == result_dict)
 
-    @mock.patch("glob.glob")
-    def testVerifyLocalImageArtifactsExist(self, mock_glob):
-        """Test VerifyArtifactsPath."""
-        #can't find the image
-        mock_glob.return_value = []
-        self.assertRaises(errors.GetLocalImageError,
-                          create_common.VerifyLocalImageArtifactsExist,
-                          "/fake_dirs")
+    def testZipCFImageFiles(self):
+        """Test ZipCFImageFiles."""
+        # Should raise error if zip file already exists
+        fake_image_path = "/fake_image_dir/"
+        self.Patch(os.path, "exists", return_value=True)
+        self.Patch(os, "makedirs")
+        self.assertRaises(errors.ZipImageError,
+                          create_common.ZipCFImageFiles,
+                          fake_image_path)
 
-        mock_glob.return_value = [
-            "/fake_dirs/aosp_cf_x86_phone-img-5046769.zip"
-        ]
-        self.assertEqual(
-            create_common.VerifyLocalImageArtifactsExist("/fake_dirs"),
-            "/fake_dirs/aosp_cf_x86_phone-img-5046769.zip")
+        # Test should get archive name by timestamp if zip file does not exist.
+        self.Patch(zipfile, "ZipFile", return_value=FakeZipFile())
+        self.Patch(os.path, "exists", return_value=False)
+        self.Patch(os.environ, "get", return_value="fake_build_target")
+        self.Patch(time, "time", return_value=12345)
+        self.Patch(tempfile, "gettempdir", return_value="/fake_temp")
+        self.assertEqual(create_common.ZipCFImageFiles(fake_image_path),
+                         "/fake_temp/%s/fake_build_target-local-12345.zip" %
+                         constants.TEMP_ARTIFACTS_FOLDER)
 
 
 if __name__ == "__main__":
diff --git a/create/gce_local_image_remote_instance.py b/create/gce_local_image_remote_instance.py
index 84c0030..05f5a03 100644
--- a/create/gce_local_image_remote_instance.py
+++ b/create/gce_local_image_remote_instance.py
@@ -18,15 +18,11 @@
 """
 
 import logging
-import os
 
-from acloud import errors
 from acloud.create import base_avd_create
 from acloud.internal.lib import utils
 from acloud.public import device_driver
 
-_GCE_LOCAL_IMAGE_CANDIDATE = ["avd-system.tar.gz",
-                              "android_system_disk_syslinux.img"]
 
 logger = logging.getLogger(__name__)
 
@@ -46,13 +42,12 @@
         Returns:
             A Report instance.
         """
-        local_image_path = self._GetGceLocalImagePath(avd_spec.local_image_dir)
-        logger.info("GCE local image: %s", local_image_path)
+        logger.info("GCE local image: %s", avd_spec.local_image_artifact)
 
         report = device_driver.CreateAndroidVirtualDevices(
             avd_spec.cfg,
             num=avd_spec.num,
-            local_disk_image=local_image_path,
+            local_disk_image=avd_spec.local_image_artifact,
             autoconnect=avd_spec.autoconnect,
             report_internal_ip=avd_spec.report_internal_ip,
             avd_spec=avd_spec)
@@ -62,32 +57,3 @@
             utils.LaunchVNCFromReport(report, avd_spec, no_prompts)
 
         return report
-
-
-    @staticmethod
-    def _GetGceLocalImagePath(local_image_dir):
-        """Get gce local image path.
-
-        If local_image_dir is dir, prioritize find avd-system.tar.gz;
-        otherwise, find android_system_disk_syslinux.img.
-
-        Args:
-            local_image_dir: A string to specify local image dir.
-
-        Returns:
-            String, image file path if exists.
-
-        Raises:
-            errors.BootImgDoesNotExist if image not exist.
-        """
-        if os.path.isfile(local_image_dir):
-            return local_image_dir
-
-        for img_name in _GCE_LOCAL_IMAGE_CANDIDATE:
-            full_file_path = os.path.join(local_image_dir, img_name)
-            if os.path.exists(full_file_path):
-                return full_file_path
-
-        raise errors.BootImgDoesNotExist("Could not find any GCE images (%s), "
-                                         "you can build them via \"m dist\"" %
-                                         ", ".join(_GCE_LOCAL_IMAGE_CANDIDATE))
diff --git a/create/gce_local_image_remote_instance_test.py b/create/gce_local_image_remote_instance_test.py
deleted file mode 100644
index 9b5161b..0000000
--- a/create/gce_local_image_remote_instance_test.py
+++ /dev/null
@@ -1,75 +0,0 @@
-# Copyright 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.
-"""Tests for gce_local_image_remote_instance."""
-
-import unittest
-import os
-import mock
-
-from acloud import errors
-from acloud.create.gce_local_image_remote_instance import GceLocalImageRemoteInstance
-from acloud.internal.lib import driver_test_lib
-
-
-# pylint: disable=invalid-name, protected-access
-class GceLocalImageRemoteInstanceTest(driver_test_lib.BaseDriverTest):
-    """Test gce_local_image_remote_instance methods."""
-
-    def setUp(self):
-        """Initialize gce_local_image_remote_instance."""
-        super(GceLocalImageRemoteInstanceTest, self).setUp()
-        self.build_client = mock.MagicMock()
-        self.GceLocalImageRemoteInstance = GceLocalImageRemoteInstance()
-
-    def testGetGceLocalImagePath(self):
-        """Test get gce local image path."""
-        self.Patch(os.path, "isfile", return_value=True)
-        # Verify when specify --local-image ~/XXX.tar.gz.
-        fake_image_path = "~/gce_local_image_dir/gce_image.tar.gz"
-        self.Patch(os.path, "exists", return_value=True)
-        self.assertEqual(
-            self.GceLocalImageRemoteInstance._GetGceLocalImagePath(
-                fake_image_path), "~/gce_local_image_dir/gce_image.tar.gz")
-
-        # Verify when specify --local-image ~/XXX.img.
-        fake_image_path = "~/gce_local_image_dir/gce_image.img"
-        self.assertEqual(
-            self.GceLocalImageRemoteInstance._GetGceLocalImagePath(
-                fake_image_path), "~/gce_local_image_dir/gce_image.img")
-
-        # Verify if exist argument --local-image as a directory.
-        self.Patch(os.path, "isfile", return_value=False)
-        self.Patch(os.path, "exists", return_value=True)
-        fake_image_path = "~/gce_local_image_dir/"
-        # Default to find */avd-system.tar.gz if exist then return the path.
-        self.assertEqual(
-            self.GceLocalImageRemoteInstance._GetGceLocalImagePath(
-                fake_image_path), "~/gce_local_image_dir/avd-system.tar.gz")
-
-        # Otherwise choose raw file */android_system_disk_syslinux.img if
-        # exist then return the path.
-        self.Patch(os.path, "exists", side_effect=[False, True])
-        self.assertEqual(
-            self.GceLocalImageRemoteInstance._GetGceLocalImagePath(
-                fake_image_path), "~/gce_local_image_dir/android_system_disk_syslinux.img")
-
-        # Both _GCE_LOCAL_IMAGE_CANDIDATE could not be found then raise error.
-        self.Patch(os.path, "exists", side_effect=[False, False])
-        self.assertRaises(errors.BootImgDoesNotExist,
-                          self.GceLocalImageRemoteInstance._GetGceLocalImagePath,
-                          fake_image_path)
-
-
-if __name__ == "__main__":
-    unittest.main()
diff --git a/create/goldfish_remote_image_remote_instance.py b/create/goldfish_remote_image_remote_instance.py
new file mode 100644
index 0000000..592ffaf
--- /dev/null
+++ b/create/goldfish_remote_image_remote_instance.py
@@ -0,0 +1,45 @@
+# Copyright 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.
+r"""GoldfishRemoteImageRemoteInstance class.
+
+Create class that is responsible for creating a goldfish remote instance AVD
+with a remote image.
+"""
+
+from acloud.create import base_avd_create
+from acloud.internal.lib import utils
+from acloud.public.actions import create_goldfish_action
+
+
+class GoldfishRemoteImageRemoteInstance(base_avd_create.BaseAVDCreate):
+    """Create class for a remote image remote instance AVD."""
+
+    @utils.TimeExecute(function_description="Total time: ",
+                       print_before_call=False, print_status=False)
+    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.
+        """
+        report = create_goldfish_action.CreateDevices(avd_spec=avd_spec)
+
+        # Launch vnc client if we're auto-connecting.
+        if avd_spec.autoconnect:
+            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 147cd3a..d0e67f3 100644
--- a/create/local_image_local_instance.py
+++ b/create/local_image_local_instance.py
@@ -82,8 +82,8 @@
         result_report.SetStatus(report.Status.SUCCESS)
         result_report.AddData(
             key="devices",
-            value={"adb_port": constants.DEFAULT_ADB_PORT,
-                   constants.VNC_PORT: constants.DEFAULT_VNC_PORT})
+            value={constants.ADB_PORT: constants.CF_ADB_PORT,
+                   constants.VNC_PORT: constants.CF_VNC_PORT})
         # Launch vnc client if we're auto-connecting.
         if avd_spec.autoconnect:
             utils.LaunchVNCFromReport(result_report, avd_spec, no_prompts)
@@ -132,7 +132,7 @@
         launch_cvd_w_args = launch_cvd_path + _CMD_LAUNCH_CVD_ARGS % (
             hw_property["cpu"], hw_property["x_res"], hw_property["y_res"],
             hw_property["dpi"], hw_property["memory"], hw_property["disk"],
-            system_image_dir, constants.DEFAULT_VNC_PORT)
+            system_image_dir, constants.CF_VNC_PORT)
 
         launch_cmd = utils.AddUserGroupsToCmd(launch_cvd_w_args,
                                               constants.LIST_CF_USER_GROUPS)
@@ -165,11 +165,10 @@
                         stderr=dev_null, stdout=dev_null, shell=True)
 
                 # Delete ssvnc viewer
-                delete.CleanupSSVncviewer(constants.CF_TARGET_VNC_PORT)
+                delete.CleanupSSVncviewer(constants.CF_VNC_PORT)
 
             else:
-                print("Exiting out")
-                sys.exit()
+                sys.exit(constants.EXIT_BY_USER)
         self._LaunchCvd(cmd)
 
     @staticmethod
diff --git a/create/local_image_remote_instance.py b/create/local_image_remote_instance.py
index 4d247ac..e507fb4 100644
--- a/create/local_image_remote_instance.py
+++ b/create/local_image_remote_instance.py
@@ -27,6 +27,7 @@
 
 from acloud import errors
 from acloud.create import base_avd_create
+from acloud.create import create_common
 from acloud.internal import constants
 from acloud.internal.lib import auth
 from acloud.internal.lib import cvd_compute_client
@@ -41,7 +42,6 @@
 _CMD_LAUNCH_CVD_ARGS = (" -cpus %s -x_res %s -y_res %s -dpi %s "
                         "-memory_mb %s -blank_data_image_mb %s "
                         "-data_policy always_create ")
-
 #Output to Serial port 1 (console) group in the instance
 _OUTPUT_CONSOLE_GROUPS = "tty"
 SSH_BIN = "ssh"
@@ -155,7 +155,7 @@
                         else ip.external)}
         return instance
 
-    @utils.TimeExecute(function_description="Setup GCE environment")
+    @utils.TimeExecute(function_description="Setting up GCE environment")
     def _SetAVDenv(self, cvd_user):
         """set the user to run AVD in the instance.
 
@@ -183,7 +183,7 @@
             local_image_artifact: A string, path to local image.
             cvd_host_package_artifact: A string, path to cvd host package.
         """
-        # local image
+        # TODO(b/129376163) Use lzop for fast sparse image upload
         remote_cmd = ("\"sudo su -c '/usr/bin/install_zip.sh .' - '%s'\" < %s" %
                       (cvd_user, local_image_artifact))
         logger.debug("remote_cmd:\n %s", remote_cmd)
@@ -222,32 +222,20 @@
         """LocalImageRemoteInstance initialize."""
         self.cvd_host_package_artifact = None
 
-    def VerifyArtifactsExist(self, local_image_dir):
-        """Verify required cuttlefish image artifacts exists.
-
-        Arsg:
-            local_image_dir: A string, path to check the artifacts.
-        """
-        self.cvd_host_package_artifact = self.VerifyHostPackageArtifactsExist(
-            local_image_dir)
-
-    def VerifyHostPackageArtifactsExist(self, local_image_dir):
+    def VerifyHostPackageArtifactsExist(self):
         """Verify the host package exists and return its path.
 
-        Look for the host package in local_image_dir (when we download the
-        image artifacts from Android Build into 1 folder), if we can't find
-        it, look in the dist dir (if the user built the image locally).
-
-        Args:
-            local_image_dir: A string, path to check for the host package.
+        Look for the host package in $ANDROID_HOST_OUT and dist dir.
 
         Return:
             A string, the path to the host package.
         """
-        dirs_to_check = [local_image_dir]
+        dirs_to_check = filter(None,
+                               [os.environ.get(constants.ENV_ANDROID_HOST_OUT)])
         dist_dir = utils.GetDistDir()
         if dist_dir:
             dirs_to_check.append(dist_dir)
+
         cvd_host_package_artifact = self.GetCvdHostPackage(dirs_to_check)
         logger.debug("cvd host package: %s", cvd_host_package_artifact)
         return cvd_host_package_artifact
@@ -270,8 +258,9 @@
             if os.path.exists(cvd_host_package):
                 return cvd_host_package
         raise errors.GetCvdLocalHostPackageError, (
-            "Can't find the cvd host package (Try building with 'm dist'): \n%s"
-            % '\n'.join(paths))
+            "Can't find the cvd host package (Try lunching a cuttlefish target"
+            " like aosp_cf_x86_phone-userdebug and running 'm'): \n%s" %
+            '\n'.join(paths))
 
     @utils.TimeExecute(function_description="Total time: ",
                        print_before_call=False, print_status=False)
@@ -282,17 +271,23 @@
             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)
+        self.cvd_host_package_artifact = self.VerifyHostPackageArtifactsExist()
+
+        if avd_spec.local_image_artifact:
+            local_image_artifact = avd_spec.local_image_artifact
+        else:
+            local_image_artifact = create_common.ZipCFImageFiles(
+                avd_spec.local_image_dir)
+
         device_factory = RemoteInstanceDeviceFactory(
             avd_spec,
-            avd_spec.local_image_artifact,
+            local_image_artifact,
             self.cvd_host_package_artifact)
         report = common_operations.CreateDevices(
             "create_cf", avd_spec.cfg, device_factory, avd_spec.num,
             report_internal_ip=avd_spec.report_internal_ip,
             autoconnect=avd_spec.autoconnect,
-            vnc_port=constants.CF_TARGET_VNC_PORT,
-            adb_port=constants.CF_TARGET_ADB_PORT)
+            avd_type=constants.TYPE_CF)
         # Launch vnc client if we're auto-connecting.
         if avd_spec.autoconnect:
             utils.LaunchVNCFromReport(report, avd_spec, no_prompts)
diff --git a/create/local_image_remote_instance_test.py b/create/local_image_remote_instance_test.py
index d29d492..1509d9f 100644
--- a/create/local_image_remote_instance_test.py
+++ b/create/local_image_remote_instance_test.py
@@ -20,6 +20,8 @@
 """
 import uuid
 
+import glob
+import os
 import subprocess
 import time
 import unittest
@@ -30,16 +32,19 @@
 from acloud.create import avd_spec
 from acloud.create import create_common
 from acloud.create import local_image_remote_instance
+from acloud.internal import constants
 from acloud.internal.lib import auth
 from acloud.internal.lib import cvd_compute_client
 from acloud.internal.lib import driver_test_lib
+from acloud.internal.lib import utils
 
 
-class LocalImageRemoteInstanceTest(unittest.TestCase):
+class LocalImageRemoteInstanceTest(driver_test_lib.BaseDriverTest):
     """Test LocalImageRemoteInstance method."""
 
     def setUp(self):
         """Initialize new LocalImageRemoteInstance."""
+        super(LocalImageRemoteInstanceTest, self).setUp()
         self.local_image_remote_instance = local_image_remote_instance.LocalImageRemoteInstance()
 
     def testVerifyHostPackageArtifactsExist(self):
@@ -49,17 +54,27 @@
             exists.return_value = False
             self.assertRaises(
                 errors.GetCvdLocalHostPackageError,
-                self.local_image_remote_instance.VerifyHostPackageArtifactsExist,
-                "/fake_dirs")
+                self.local_image_remote_instance.VerifyHostPackageArtifactsExist)
 
-    def testVerifyArtifactsExist(self):
-        """test verify artifacts exist."""
+        self.Patch(os.environ, "get", return_value="/fake_dir2")
+        self.Patch(utils, "GetDistDir", return_value="/fake_dir1")
+        # First path is host out dir, 2nd path is dist dir.
+        self.Patch(os.path, "exists",
+                   side_effect=[False, True])
+
+        # Find cvd host in dist dir.
+        self.assertEqual(
+            self.local_image_remote_instance.VerifyHostPackageArtifactsExist(),
+            "/fake_dir1/cvd-host_package.tar.gz")
+
+        # Find cvd host in host out dir.
+        self.Patch(os.environ, "get", return_value="/fake_dir2")
+        self.Patch(utils, "GetDistDir", return_value=None)
         with mock.patch("os.path.exists") as exists:
             exists.return_value = True
-            self.local_image_remote_instance.VerifyArtifactsExist("/fake_dirs")
             self.assertEqual(
-                self.local_image_remote_instance.cvd_host_package_artifact,
-                "/fake_dirs/cvd-host_package.tar.gz")
+                self.local_image_remote_instance.VerifyHostPackageArtifactsExist(),
+                "/fake_dir2/cvd-host_package.tar.gz")
 
 
 class RemoteInstanceDeviceFactoryTest(driver_test_lib.BaseDriverTest):
@@ -88,16 +103,19 @@
         self.assertEqual(factory._ShellCmdWithRetry("fake cmd"), True)
 
     # pylint: disable=protected-access
-    @mock.patch.object(create_common, "VerifyLocalImageArtifactsExist")
-    def testCreateGceInstanceName(self, mock_image):
+    def testCreateGceInstanceName(self):
         """test create gce instance."""
+        self.Patch(utils, "GetBuildEnvironmentVariable",
+                   return_value="test_environ")
+        self.Patch(glob, "glob", return_vale=["fake.img"])
+        self.Patch(create_common, "ZipCFImageFiles",
+                   return_value="/fake/aosp_cf_x86_phone-img-eng.username.zip")
         # Mock uuid
         args = mock.MagicMock()
-        args.local_image = "/tmp/path"
         args.config_file = ""
-        args.avd_type = "cf"
+        args.avd_type = constants.TYPE_CF
         args.flavor = "phone"
-        mock_image.return_value = "/fake/aosp_cf_x86_phone-img-eng.username.zip"
+        args.local_image = None
         fake_avd_spec = avd_spec.AVDSpec(args)
 
         fake_uuid = mock.MagicMock(hex="1234")
diff --git a/create/remote_image_local_instance.py b/create/remote_image_local_instance.py
index 779efdf..08f0255 100644
--- a/create/remote_image_local_instance.py
+++ b/create/remote_image_local_instance.py
@@ -19,6 +19,7 @@
 remote image.
 """
 from __future__ import print_function
+import glob
 import logging
 import os
 import subprocess
@@ -46,9 +47,8 @@
 # Let's add an extra buffer (~2G) to make sure user has enough disk space
 # for the downloaded image artifacts.
 _REQUIRED_SPACE = 10
-_CF_IMAGES = ["cache.img", "cmdline", "kernel", "ramdisk.img", "system.img",
-              "userdata.img", "vendor.img"]
 _BOOT_IMAGE = "boot.img"
+# TODO(b/129009852):UnpackBootImage and setfacl are deprecated.
 UNPACK_BOOTIMG_CMD = "%s -boot_img %s" % (
     os.path.join(_CUTTLEFISH_COMMON_BIN_PATH, "unpack_boot_image.py"),
     "%s -dest %s")
@@ -113,7 +113,7 @@
 
         extract_path = os.path.join(
             avd_spec.image_download_dir,
-            "acloud_image_artifacts",
+            constants.TEMP_ARTIFACTS_FOLDER,
             build_id)
 
         logger.debug("Extract path: %s", extract_path)
@@ -192,18 +192,12 @@
 
         Args:
             extract_path: String, a path include extracted files.
-
-        Raises:
-            errors.CheckPathError: Path doesn't exist.
         """
-        logger.info("Start to acl files: %s", ",".join(_CF_IMAGES))
-        for image in _CF_IMAGES:
-            image_path = os.path.join(extract_path, image)
-            if not os.path.exists(image_path):
-                raise errors.CheckPathError(
-                    "Specified file doesn't exist: %s" % image_path)
+        image_list = glob.glob(os.path.join(extract_path, "*.img"))
+        logger.info("Start to set ACLs on files: %s", ",".join(image_list))
+        for image_path in image_list:
             subprocess.check_call(ACL_CMD % image_path, shell=True)
-        logger.info("ACL files completed!")
+        logger.info("The ACLs have set completed!")
 
     @staticmethod
     def _ConfirmDownloadRemoteImageDir(download_dir):
@@ -228,8 +222,7 @@
                 if answer.lower() == "y":
                     os.makedirs(download_dir)
                 else:
-                    print("Exiting acloud!")
-                    sys.exit()
+                    sys.exit(constants.EXIT_BY_USER)
 
             stat = os.statvfs(download_dir)
             available_space = stat.f_bavail*stat.f_bsize/(1024)**3
@@ -239,7 +232,6 @@
                                              "available_space":available_space,
                                              "required_space":_REQUIRED_SPACE})
                 if download_dir.lower() == "q":
-                    print("Exiting acloud!")
-                    sys.exit()
+                    sys.exit(constants.EXIT_BY_USER)
             else:
                 return download_dir
diff --git a/create/remote_image_local_instance_test.py b/create/remote_image_local_instance_test.py
index 4f5bd15..14bb3a1 100644
--- a/create/remote_image_local_instance_test.py
+++ b/create/remote_image_local_instance_test.py
@@ -127,18 +127,6 @@
                           self.RemoteImageLocalInstance._UnpackBootImage,
                           self._extract_path)
 
-    @mock.patch.object(subprocess, "check_call")
-    def testAclCfImageFiles(self, mock_call):
-        """Test acl related files."""
-        self.Patch(os.path, "exists",
-                   side_effect=[True, True, True, True, False, True, True])
-        # Raise error when acl required file does not exist at 5th run cehck_call.
-        self.assertRaises(errors.CheckPathError,
-                          self.RemoteImageLocalInstance._AclCfImageFiles,
-                          self._extract_path)
-        # it should be run check_call 4 times before raise error.
-        self.assertEqual(mock_call.call_count, 4)
-
     def testConfirmDownloadRemoteImageDir(self):
         """Test confirm download remote image dir"""
         self.Patch(os.path, "exists", return_value=True)
diff --git a/delete/delete.py b/delete/delete.py
index 1753132..2d5ec70 100644
--- a/delete/delete.py
+++ b/delete/delete.py
@@ -173,8 +173,8 @@
     except subprocess.CalledProcessError as e:
         delete_report.AddError(str(e))
         delete_report.SetStatus(report.Status.FAIL)
-
-    CleanupSSVncviewer(constants.DEFAULT_VNC_PORT)
+    # Only CF supports local instances so assume it's a CF VNC port.
+    CleanupSSVncviewer(constants.CF_VNC_PORT)
     return delete_report
 
 
@@ -202,6 +202,10 @@
         print("There is no local instance AVD to delete.")
         return report.Report(command="delete")
 
+    if args.adb_port:
+        return DeleteInstances(
+            cfg, list_instances.GetInstanceFromAdbPort(cfg, args.adb_port))
+
     # Provide instances list to user and let user choose what to delete if user
     # didn't specific instance name in args.
     return DeleteInstances(cfg, list_instances.ChooseInstances(cfg, args.all))
diff --git a/delete/delete_args.py b/delete/delete_args.py
index b9c9dc6..dc9c106 100644
--- a/delete/delete_args.py
+++ b/delete/delete_args.py
@@ -34,29 +34,36 @@
     delete_parser = subparser.add_parser(CMD_DELETE)
     delete_parser.required = False
     delete_parser.set_defaults(which=CMD_DELETE)
-    delete_parser.add_argument(
+    delete_group = delete_parser.add_mutually_exclusive_group()
+    delete_group.add_argument(
         "--instance-names",
         dest="instance_names",
         nargs="+",
         required=False,
         help="The names of the remote instances that need to delete, "
         "separated by spaces, e.g. --instance-names instance-1 instance-2")
-    delete_parser.add_argument(
+    delete_group.add_argument(
         "--all",
         action="store_true",
         dest="all",
         required=False,
         help="If more than 1 AVD instance is found, delete them all.")
-    delete_parser.add_argument(
+    delete_group.add_argument(
         "--local-instance",
         action="store_true",
         dest="local_instance",
         required=False,
         help="Only delete the local instance.")
+    delete_group.add_argument(
+        "--adb-port", "-p",
+        type=int,
+        dest="adb_port",
+        required=False,
+        help="Delete instance with specified adb-port.")
 
     # TODO(b/118439885): Old arg formats to support transition, delete when
     # transistion is done.
-    delete_parser.add_argument(
+    delete_group.add_argument(
         "--instance_names",
         dest="instance_names",
         nargs="+",
diff --git a/errors.py b/errors.py
index 1bc6604..09a2c0d 100644
--- a/errors.py
+++ b/errors.py
@@ -205,3 +205,7 @@
 
 class FunctionTimeoutError(Exception):
     """Timeout error of decorator function."""
+
+
+class ZipImageError(Exception):
+    """Zip image error."""
diff --git a/internal/constants.py b/internal/constants.py
index aaec56a..8ea18b3 100755
--- a/internal/constants.py
+++ b/internal/constants.py
@@ -91,31 +91,29 @@
 USER_ANSWER_YES = {"y", "yes", "Y"}
 
 # Cuttlefish groups
-LIST_CF_USER_GROUPS = ["kvm", "libvirt", "cvdnetwork"]
-#For the cuttlefish remote instances: adb port is 6520 and vnc is 6444.
-CF_TARGET_ADB_PORT = 6520
-CF_TARGET_VNC_PORT = 6444
+LIST_CF_USER_GROUPS = ["kvm", "cvdnetwork"]
 
-# For gce_x86_phones remote instances
-DEFAULT_GCE_VNC_PORT = 6444
-DEFAULT_GCE_ADB_PORT = 5555
-# For goldfish remote instances
-DEFAULT_GOLDFISH_VNC_PORT = 6444
-DEFAULT_GOLDFISH_ADB_PORT = 5555
-# For cuttlefish remote instances
-DEFAULT_VNC_PORT = 6444
-DEFAULT_ADB_PORT = 6520
-VNC_PORT = "vnc_port"
 ADB_PORT = "adb_port"
-
-# For cheeps remote instances.
-DEFAULT_CHEEPS_TARGET_ADB_PORT = 9222
-DEFAULT_CHEEPS_TARGET_VNC_PORT = 5900
+VNC_PORT = "vnc_port"
+# For cuttlefish remote instances
+CF_ADB_PORT = 6520
+CF_VNC_PORT = 6444
+# For cheeps remote instances
+CHEEPS_ADB_PORT = 9222
+CHEEPS_VNC_PORT = 5900
+# For gce_x86_phones remote instances
+GCE_ADB_PORT = 5555
+GCE_VNC_PORT = 6444
+# For goldfish remote instances
+GF_ADB_PORT = 5555
+GF_VNC_PORT = 6444
 
 COMMAND_PS = ["ps", "aux"]
 CMD_LAUNCH_CVD = "launch_cvd"
 CMD_STOP_CVD = "stop_cvd"
 ENV_ANDROID_BUILD_TOP = "ANDROID_BUILD_TOP"
+ENV_ANDROID_HOST_OUT = "ANDROID_HOST_OUT"
+ENV_BUILD_TARGET = "TARGET_PRODUCT"
 
 LOCALHOST = "127.0.0.1"
 LOCALHOST_ADB_SERIAL = LOCALHOST + ":%d"
@@ -139,3 +137,8 @@
 INS_KEY_IS_LOCAL = "remote"
 INS_STATUS_RUNNING = "RUNNING"
 LOCAL_INS_NAME = "local-instance"
+
+TEMP_ARTIFACTS_FOLDER = "acloud_image_artifacts"
+TOOL_NAME = "acloud"
+EXIT_BY_USER = 1
+EXIT_BY_ERROR = -99
diff --git a/internal/lib/android_build_client.py b/internal/lib/android_build_client.py
index 32a1179..b2bf3bd 100644
--- a/internal/lib/android_build_client.py
+++ b/internal/lib/android_build_client.py
@@ -85,7 +85,7 @@
                 while not done:
                     _, done = downloader.next_chunk()
             logger.info("Downloaded artifact: %s", local_dest)
-        except OSError as e:
+        except (OSError, apiclient.errors.HttpError) as e:
             logger.error("Downloading artifact failed: %s", str(e))
             raise errors.DriverError(str(e))
 
@@ -170,6 +170,7 @@
             maxResults=self.ONE_RESULT,
             successful=self.BUILD_SUCCESSFUL)
         build = self.Execute(api)
+        logger.info("GetLKGB build API response: %s", build)
         if build:
             return str(build.get("builds")[0].get("buildId"))
         raise errors.GetBuildIDError(
diff --git a/internal/lib/android_compute_client.py b/internal/lib/android_compute_client.py
index 4d6b239..2c77cfb 100755
--- a/internal/lib/android_compute_client.py
+++ b/internal/lib/android_compute_client.py
@@ -47,7 +47,6 @@
 
 class AndroidComputeClient(gcompute_client.ComputeClient):
     """Client that manages Anadroid Virtual Device."""
-    INSTANCE_NAME_FMT = "ins-{uuid}-{build_id}-{build_target}"
     IMAGE_NAME_FMT = "img-{uuid}-{build_id}-{build_target}"
     DATA_DISK_NAME_FMT = "data-{instance}"
     BOOT_COMPLETED_MSG = "VIRTUAL_DEVICE_BOOT_COMPLETED"
@@ -78,6 +77,8 @@
         self._resolution = acloud_config.resolution
         self._metadata = acloud_config.metadata_variable.copy()
         self._ssh_public_key_path = acloud_config.ssh_public_key_path
+        self._launch_args = acloud_config.launch_args
+        self._instance_name_pattern = acloud_config.instance_name_pattern
 
     @classmethod
     def _FormalizeName(cls, name):
@@ -149,8 +150,7 @@
         name = cls.DATA_DISK_NAME_FMT.format(instance=instance)
         return cls._FormalizeName(name)
 
-    @classmethod
-    def GenerateInstanceName(cls, build_target=None, build_id=None):
+    def GenerateInstanceName(self, build_target=None, build_id=None):
         """Generate an instance name given build_target, build_id.
 
         Target is not used as instance name has a length limit.
@@ -162,13 +162,10 @@
         Returns:
             A string, representing instance name.
         """
-        if not build_target and not build_id:
-            return "instance-" + uuid.uuid4().hex
-        name = cls.INSTANCE_NAME_FMT.format(
-            build_target=build_target,
-            build_id=build_id,
-            uuid=uuid.uuid4().hex[:8]).replace("_", "-")
-        return cls._FormalizeName(name)
+        name = self._instance_name_pattern.format(build_target=build_target,
+                                                  build_id=build_id,
+                                                  uuid=uuid.uuid4().hex[:8])
+        return self._FormalizeName(name)
 
     def CreateDisk(self,
                    disk_name,
@@ -241,8 +238,10 @@
                        gpu=None,
                        extra_disk_name=None,
                        labels=None,
-                       avd_spec=None):
+                       avd_spec=None,
+                       extra_scopes=None):
         """Create a gce instance with a gce image.
+
         Args:
             instance: String, instance name.
             image_name: String, source image used to create this disk.
@@ -262,6 +261,8 @@
             extra_disk_name: String,the name of the extra disk to attach.
             labels: Dict, will be added to the instance's labels.
             avd_spec: AVDSpec object that tells us what we're going to create.
+            extra_scopes: List, extra scopes (strings) to be passed to the
+                          instance.
         """
         self._CheckMachineSize()
         disk_args = self._GetDiskArgs(instance, image_name)
@@ -296,7 +297,7 @@
         super(AndroidComputeClient, self).CreateInstance(
             instance, image_name, self._machine_type, metadata, self._network,
             self._zone, disk_args, image_project, gpu, extra_disk_name,
-            labels=labels)
+            labels=labels, extra_scopes=extra_scopes)
 
     def CheckBootFailure(self, serial_out, instance):
         """Determine if serial output has indicated any boot failure.
diff --git a/internal/lib/android_compute_client_test.py b/internal/lib/android_compute_client_test.py
index 9b4bdca..f73913f 100644
--- a/internal/lib/android_compute_client_test.py
+++ b/internal/lib/android_compute_client_test.py
@@ -48,6 +48,7 @@
     DPI = 160
     X_RES = 720
     Y_RES = 1280
+    EXTRA_SCOPES = None
 
     def _GetFakeConfig(self):
         """Create a fake configuration object.
@@ -67,6 +68,7 @@
         fake_cfg.orientation = self.ORIENTATION
         fake_cfg.resolution = self.DEVICE_RESOLUTION
         fake_cfg.metadata_variable = {self.METADATA[0]: self.METADATA[1]}
+        fake_cfg.extra_scopes = self.EXTRA_SCOPES
         return fake_cfg
 
     def setUp(self):
@@ -137,7 +139,7 @@
                   instance_name, self.IMAGE, self.MACHINE_TYPE,
                   expected_metadata, self.NETWORK, self.ZONE,
                   expected_disk_args, image_project, gpu, extra_disk_name,
-                  labels=labels)
+                  labels=labels, extra_scopes=self.EXTRA_SCOPES)
 
     # pylint: disable=invalid-name
     def testCheckMachineSizeMeetsRequirement(self):
diff --git a/internal/lib/cheeps_compute_client.py b/internal/lib/cheeps_compute_client.py
index 5ef344a..98e249b 100644
--- a/internal/lib/cheeps_compute_client.py
+++ b/internal/lib/cheeps_compute_client.py
@@ -38,6 +38,7 @@
 import logging
 
 from acloud import errors
+from acloud.internal import constants
 from acloud.internal.lib import android_compute_client
 from acloud.internal.lib import gcompute_client
 
@@ -49,7 +50,7 @@
     Cheeps is a VM that run Chrome OS which runs on GCE.
     """
     # This is the timeout for betty to start.
-    BOOT_TIMEOUT_SECS = 10*60
+    BOOT_TIMEOUT_SECS = 15*60
     # This is printed by betty.sh.
     BOOT_COMPLETED_MSG = "VM successfully started"
     # systemd prints this if betty.sh returns nonzero status code.
@@ -62,7 +63,7 @@
 
     # pylint: disable=too-many-locals,arguments-differ
     def CreateInstance(self, instance, image_name, image_project,
-                       build_id=None):
+                       build_id=None, avd_spec=None):
         """ Creates a cheeps instance in GCE.
 
         Args:
@@ -71,11 +72,23 @@
             image_project: project the GCE image is in
             build_id: (optional) the Android build id to use. To specify a
                 different betty image you should use a different image_name
+            avd_spec: An AVDSpec instance.
         """
         metadata = self._metadata.copy()
+        metadata[constants.INS_KEY_AVD_TYPE] = constants.TYPE_CHEEPS
         if build_id:
             metadata['android_build_id'] = build_id
 
+        # Update metadata by avd_spec
+        if avd_spec:
+            metadata["cvd_01_x_res"] = avd_spec.hw_property[constants.HW_X_RES]
+            metadata["cvd_01_y_res"] = avd_spec.hw_property[constants.HW_Y_RES]
+            metadata["cvd_01_dpi"] = avd_spec.hw_property[constants.HW_ALIAS_DPI]
+            metadata[constants.INS_KEY_DISPLAY] = ("%sx%s (%s)" % (
+                avd_spec.hw_property[constants.HW_X_RES],
+                avd_spec.hw_property[constants.HW_Y_RES],
+                avd_spec.hw_property[constants.HW_ALIAS_DPI]))
+
         # Add per-instance ssh key
         if self._ssh_public_key_path:
             rsa = self._LoadSshPublicKey(self._ssh_public_key_path)
@@ -87,6 +100,10 @@
             logger.warning("ssh_public_key_path is not specified in config, "
                            "only project-wide key will be effective.")
 
+        # Add labels for giving the instances ability to be filter for
+        # acloud list/delete cmds.
+        labels = {constants.LABEL_CREATE_BY: getpass.getuser()}
+
         gcompute_client.ComputeClient.CreateInstance(
             self,
             instance=instance,
@@ -97,4 +114,4 @@
             machine_type=self._machine_type,
             network=self._network,
             zone=self._zone,
-        )
+            labels=labels)
diff --git a/internal/lib/cheeps_compute_client_test.py b/internal/lib/cheeps_compute_client_test.py
index fa163fa..f80c0c4 100644
--- a/internal/lib/cheeps_compute_client_test.py
+++ b/internal/lib/cheeps_compute_client_test.py
@@ -18,6 +18,7 @@
 import unittest
 import mock
 
+from acloud.internal import constants
 from acloud.internal.lib import cheeps_compute_client
 from acloud.internal.lib import driver_test_lib
 from acloud.internal.lib import gcompute_client
@@ -36,6 +37,9 @@
     METADATA = {"metadata_key": "metadata_value"}
     BOOT_DISK_SIZE_GB = 10
     ANDROID_BUILD_ID = 123
+    DPI = 320
+    X_RES = 720
+    Y_RES = 1280
 
     def _GetFakeConfig(self):
         """Create a fake configuration object.
@@ -49,6 +53,8 @@
         fake_cfg.machine_type = self.MACHINE_TYPE
         fake_cfg.network = self.NETWORK
         fake_cfg.zone = self.ZONE
+        fake_cfg.resolution = "{x}x{y}x32x{dpi}".format(
+            x=self.X_RES, y=self.Y_RES, dpi=self.DPI)
         fake_cfg.metadata_variable = self.METADATA
         return fake_cfg
 
@@ -69,14 +75,34 @@
             return_value={"diskSizeGb": self.BOOT_DISK_SIZE_GB})
         self.Patch(gcompute_client.ComputeClient, "CreateInstance")
 
-    def testCreateInstance(self):
+    @mock.patch("getpass.getuser", return_value="fake_user")
+    def testCreateInstance(self, _mock_user):
         """Test CreateInstance."""
 
-        expected_metadata = {'android_build_id': self.ANDROID_BUILD_ID}
+        expected_metadata = {
+            'android_build_id': self.ANDROID_BUILD_ID,
+            'avd_type': "cheeps",
+            'cvd_01_dpi': str(self.DPI),
+            'cvd_01_x_res': str(self.X_RES),
+            'cvd_01_y_res': str(self.Y_RES),
+            'display': "%sx%s (%s)"%(
+                str(self.X_RES),
+                str(self.Y_RES),
+                str(self.DPI))}
         expected_metadata.update(self.METADATA)
+        expected_labels = {'created_by': "fake_user"}
+
+        avd_spec = mock.MagicMock()
+        avd_spec.hw_property = {constants.HW_X_RES: str(self.X_RES),
+                                constants.HW_Y_RES: str(self.Y_RES),
+                                constants.HW_ALIAS_DPI: str(self.DPI)}
 
         self.cheeps_compute_client.CreateInstance(
-            self.INSTANCE, self.IMAGE, self.IMAGE_PROJECT, self.ANDROID_BUILD_ID)
+            self.INSTANCE,
+            self.IMAGE,
+            self.IMAGE_PROJECT,
+            self.ANDROID_BUILD_ID,
+            avd_spec)
         # pylint: disable=no-member
         gcompute_client.ComputeClient.CreateInstance.assert_called_with(
             self.cheeps_compute_client,
@@ -87,7 +113,8 @@
             metadata=expected_metadata,
             machine_type=self.MACHINE_TYPE,
             network=self.NETWORK,
-            zone=self.ZONE)
+            zone=self.ZONE,
+            labels=expected_labels)
 
 if __name__ == "__main__":
     unittest.main()
diff --git a/internal/lib/cvd_compute_client.py b/internal/lib/cvd_compute_client.py
index 7697405..920e807 100644
--- a/internal/lib/cvd_compute_client.py
+++ b/internal/lib/cvd_compute_client.py
@@ -61,21 +61,23 @@
     def CreateInstance(self, instance, image_name, image_project,
                        build_target=None, branch=None, build_id=None,
                        kernel_branch=None, kernel_build_id=None,
-                       blank_data_disk_size_gb=None, avd_spec=None):
+                       blank_data_disk_size_gb=None, avd_spec=None,
+                       extra_scopes=None):
         """Create a cuttlefish instance given stable host image and build id.
 
         Args:
             instance: instance name.
             image_name: A string, the name of the GCE image.
-            image_project: A string, name of the project where the image belongs.
+            image_project: A string, name of the project where the image lives.
                            Assume the default project if None.
             build_target: Target name, e.g. "aosp_cf_x86_phone-userdebug"
             branch: Branch name, e.g. "aosp-master"
             build_id: Build id, a string, e.g. "2263051", "P2804227"
-            kernel_branch: Kernel branch name, e.g. "kernel-android-cf-4.4-x86_64"
-            kernel_build_id: Kernel build id, a string, e.g. "2263051", "P2804227"
+            kernel_branch: Kernel branch name, e.g. "kernel-common-android-4.14"
+            kernel_build_id: Kernel build id, a string, e.g. "223051", "P280427"
             blank_data_disk_size_gb: Size of the blank data disk in GB.
             avd_spec: An AVDSpec instance.
+            extra_scopes: A list of extra scopes to be passed to the instance.
         """
         self._CheckMachineSize()
 
@@ -100,7 +102,8 @@
         if kernel_branch and kernel_build_id:
             metadata["cvd_01_fetch_kernel_bid"] = "{branch}/{build_id}".format(
                 branch=kernel_branch, build_id=kernel_build_id)
-        metadata["cvd_01_launch"] = "1"
+        metadata["cvd_01_launch"] = (self._launch_args
+                                     if self._launch_args else "1")
 
         # The cuttlefish-google tools changed the usage of this cvd_01_launch
         # variable. For the local image, we remove the cvd_01_launch from
@@ -168,4 +171,5 @@
             machine_type=self._machine_type,
             network=self._network,
             zone=self._zone,
-            labels=labels)
+            labels=labels,
+            extra_scopes=extra_scopes)
diff --git a/internal/lib/cvd_compute_client_test.py b/internal/lib/cvd_compute_client_test.py
index 93b63b6..aea2579 100644
--- a/internal/lib/cvd_compute_client_test.py
+++ b/internal/lib/cvd_compute_client_test.py
@@ -16,15 +16,16 @@
 
 """Tests for acloud.internal.lib.cvd_compute_client."""
 
+import glob
 import unittest
 import mock
 
 from acloud.create import avd_spec
-from acloud.create import create_common
 from acloud.internal import constants
 from acloud.internal.lib import cvd_compute_client
 from acloud.internal.lib import driver_test_lib
 from acloud.internal.lib import gcompute_client
+from acloud.internal.lib import utils
 
 
 class CvdComputeClientTest(driver_test_lib.BaseDriverTest):
@@ -48,6 +49,8 @@
     METADATA = {"metadata_key": "metadata_value"}
     EXTRA_DATA_DISK_SIZE_GB = 4
     BOOT_DISK_SIZE_GB = 10
+    LAUNCH_ARGS = "--setupwizard_mode=REQUIRED"
+    EXTRA_SCOPES = ["scope1"]
 
     def _GetFakeConfig(self):
         """Create a fake configuration object.
@@ -64,6 +67,8 @@
             x=self.X_RES, y=self.Y_RES, dpi=self.DPI)
         fake_cfg.metadata_variable = self.METADATA
         fake_cfg.extra_data_disk_size_gb = self.EXTRA_DATA_DISK_SIZE_GB
+        fake_cfg.launch_args = self.LAUNCH_ARGS
+        fake_cfg.extra_scopes = self.EXTRA_SCOPES
         return fake_cfg
 
     def setUp(self):
@@ -73,7 +78,8 @@
         self.cvd_compute_client = cvd_compute_client.CvdComputeClient(
             self._GetFakeConfig(), mock.MagicMock())
 
-    @mock.patch.object(create_common, "VerifyLocalImageArtifactsExist")
+    @mock.patch.object(utils, "GetBuildEnvironmentVariable", return_value="fake_env")
+    @mock.patch.object(glob, "glob", return_value=["fake.img"])
     @mock.patch.object(gcompute_client.ComputeClient, "CompareMachineSize",
                        return_value=1)
     @mock.patch.object(gcompute_client.ComputeClient, "GetImage",
@@ -83,7 +89,8 @@
                        return_value=[{"fake_arg": "fake_value"}])
     @mock.patch("getpass.getuser", return_value="fake_user")
     def testCreateInstance(self, _get_user, _get_disk_args, mock_create,
-                           _get_image, _compare_machine_size, mock_img_path):
+                           _get_image, _compare_machine_size, mock_check_img,
+                           _mock_env):
         """Test CreateInstance."""
         expected_metadata = {
             "cvd_01_dpi": str(self.DPI),
@@ -101,14 +108,14 @@
         }
         expected_metadata.update(self.METADATA)
         remote_image_metadata = dict(expected_metadata)
-        remote_image_metadata["cvd_01_launch"] = "1"
+        remote_image_metadata["cvd_01_launch"] = self.LAUNCH_ARGS
         expected_disk_args = [{"fake_arg": "fake_value"}]
 
         self.cvd_compute_client.CreateInstance(
-            self.INSTANCE, self.IMAGE, self.IMAGE_PROJECT, self.TARGET, self.BRANCH,
-            self.BUILD_ID, self.KERNEL_BRANCH, self.KERNEL_BUILD_ID,
-            self.EXTRA_DATA_DISK_SIZE_GB)
-        # gcompute_client.ComputeClient.CreateInstance.assert_called_with(
+            self.INSTANCE, self.IMAGE, self.IMAGE_PROJECT, self.TARGET,
+            self.BRANCH, self.BUILD_ID, self.KERNEL_BRANCH,
+            self.KERNEL_BUILD_ID, self.EXTRA_DATA_DISK_SIZE_GB,
+            extra_scopes=self.EXTRA_SCOPES)
         mock_create.assert_called_with(
             self.cvd_compute_client,
             instance=self.INSTANCE,
@@ -119,15 +126,16 @@
             machine_type=self.MACHINE_TYPE,
             network=self.NETWORK,
             zone=self.ZONE,
-            labels={constants.LABEL_CREATE_BY: "fake_user"})
+            labels={constants.LABEL_CREATE_BY: "fake_user"},
+            extra_scopes=self.EXTRA_SCOPES)
 
         #test use local image in the remote instance.
         local_image_metadata = dict(expected_metadata)
         args = mock.MagicMock()
-        mock_img_path.return_value = "cf_x86_phone-img-eng.user.zip"
-        args.local_image = "/tmp/path"
+        mock_check_img.return_value = True
+        args.local_image = None
         args.config_file = ""
-        args.avd_type = "cf"
+        args.avd_type = constants.TYPE_CF
         args.flavor = "phone"
         fake_avd_spec = avd_spec.AVDSpec(args)
         fake_avd_spec.hw_property[constants.HW_X_RES] = str(self.X_RES)
@@ -135,7 +143,7 @@
         fake_avd_spec.hw_property[constants.HW_ALIAS_DPI] = str(self.DPI)
         fake_avd_spec.hw_property[constants.HW_ALIAS_DISK] = str(
             self.EXTRA_DATA_DISK_SIZE_GB * 1024)
-        local_image_metadata["avd_type"] = "cf"
+        local_image_metadata["avd_type"] = constants.TYPE_CF
         local_image_metadata["flavor"] = "phone"
         local_image_metadata[constants.INS_KEY_DISPLAY] = ("%sx%s (%s)" % (
             fake_avd_spec.hw_property[constants.HW_X_RES],
@@ -144,7 +152,8 @@
         self.cvd_compute_client.CreateInstance(
             self.INSTANCE, self.IMAGE, self.IMAGE_PROJECT, self.TARGET, self.BRANCH,
             self.BUILD_ID, self.KERNEL_BRANCH, self.KERNEL_BUILD_ID,
-            self.EXTRA_DATA_DISK_SIZE_GB, fake_avd_spec)
+            self.EXTRA_DATA_DISK_SIZE_GB, fake_avd_spec,
+            extra_scopes=self.EXTRA_SCOPES)
 
         expected_labels = {constants.LABEL_CREATE_BY: "fake_user"}
         mock_create.assert_called_with(
@@ -157,7 +166,8 @@
             machine_type=self.MACHINE_TYPE,
             network=self.NETWORK,
             zone=self.ZONE,
-            labels=expected_labels)
+            labels=expected_labels,
+            extra_scopes=self.EXTRA_SCOPES)
 
 
 if __name__ == "__main__":
diff --git a/internal/lib/gcompute_client.py b/internal/lib/gcompute_client.py
index 1f44de0..759fb65 100755
--- a/internal/lib/gcompute_client.py
+++ b/internal/lib/gcompute_client.py
@@ -39,6 +39,11 @@
 logger = logging.getLogger(__name__)
 
 _MAX_RETRIES_ON_FINGERPRINT_CONFLICT = 10
+_METADATA_KEY = "key"
+_METADATA_KEY_VALUE = "value"
+_SSH_KEYS_NAME = "sshKeys"
+_ITEMS = "items"
+_METADATA = "metadata"
 
 BASE_DISK_ARGS = {
     "type": "PERSISTENT",
@@ -1078,7 +1083,8 @@
                        image_project=None,
                        gpu=None,
                        extra_disk_name=None,
-                       labels=None):
+                       labels=None,
+                       extra_scopes=None):
         """Create a gce instance with a gce image.
 
         Args:
@@ -1099,11 +1105,18 @@
                  https://cloud.google.com/compute/docs/gpus/add-gpus
             extra_disk_name: String,the name of the extra disk to attach.
             labels: Dict, will be added to the instance's labels.
+            extra_scopes: A list of extra scopes to be provided to the instance.
         """
         disk_args = (disk_args
                      or self._GetDiskArgs(instance, image_name, image_project))
         if extra_disk_name:
             disk_args.extend(self._GetExtraDiskArgs(extra_disk_name, zone))
+
+        scopes = []
+        scopes.extend(self.DEFAULT_INSTANCE_SCOPE)
+        if extra_scopes:
+            scopes.extend(extra_scopes)
+
         body = {
             "machineType": self.GetMachineType(machine_type, zone)["selfLink"],
             "name": instance,
@@ -1111,10 +1124,11 @@
             "disks": disk_args,
             "serviceAccounts": [{
                 "email": "default",
-                "scopes": self.DEFAULT_INSTANCE_SCOPE
+                "scopes": scopes,
             }],
         }
 
+
         if labels is not None:
             body["labels"] = labels
         if gpu:
@@ -1127,10 +1141,10 @@
             body["scheduling"] = {"onHostMaintenance": "terminate"}
         if metadata:
             metadata_list = [{
-                "key": key,
-                "value": val
+                _METADATA_KEY: key,
+                _METADATA_KEY_VALUE: val
             } for key, val in metadata.iteritems()]
-            body["metadata"] = {"items": metadata_list}
+            body[_METADATA] = {_ITEMS: metadata_list}
         logger.info("Creating instance: project %s, zone %s, body:%s",
                     self._project, zone, body)
         api = self.service.instances().insert(
@@ -1364,63 +1378,53 @@
         external_ip = instance["networkInterfaces"][0]["accessConfigs"][0]["natIP"]
         return IP(internal=internal_ip, external=external_ip)
 
-    def SetCommonInstanceMetadata(self, body):
-        """Set project-wide metadata.
+    @utils.TimeExecute(function_description="Updating instance metadata: ")
+    def SetInstanceMetadata(self, zone, instance, body):
+        """Set instance metadata.
 
         Args:
-            body: Metadata body.
+            zone: String, name of zone.
+            instance: String, representing instance name.
+            body: Dict, Metadata body.
                   metdata is in the following format.
                   {
                     "kind": "compute#metadata",
                     "fingerprint": "a-23icsyx4E=",
                     "items": [
                       {
-                        "key": "google-compute-default-region",
-                        "value": "us-central1"
+                        "key": "sshKeys",
+                        "value": "key"
                       }, ...
                     ]
                   }
         """
-        api = self.service.projects().setCommonInstanceMetadata(
-            project=self._project, body=body)
+        api = self.service.instances().setMetadata(
+            project=self._project, zone=zone, instance=instance, body=body)
         operation = self.Execute(api)
-        self.WaitOnOperation(operation, operation_scope=OperationScope.GLOBAL)
+        self.WaitOnOperation(
+            operation, operation_scope=OperationScope.ZONE, scope_name=zone)
 
-    def AddSshRsa(self, user, ssh_rsa_path):
-        """Add the public rsa key to the project's metadata.
+    def AddSshRsaInstanceMetadata(self, zone, user, ssh_rsa_path, instance):
+        """Add the public rsa key to the instance's metadata.
 
-        Compute engine instances that are created after will
-        by default contain the key.
+        Confirm that the instance has this public key in the instance's
+        metadata, if not we will add this public key.
 
         Args:
-            user: the name of the user which the key belongs to.
-            ssh_rsa_path: The absolute path to public rsa key.
+            zone: String, name of zone.
+            user: String, name of the user which the key belongs to.
+            ssh_rsa_path: String, The absolute path to public rsa key.
+            instance: String, representing instance name.
         """
-        if not os.path.exists(ssh_rsa_path):
-            raise errors.DriverError(
-                "RSA file %s does not exist." % ssh_rsa_path)
-
-        logger.info("Adding ssh rsa key from %s to project %s for user: %s",
-                    ssh_rsa_path, self._project, user)
-        project = self.GetProject()
-        with open(ssh_rsa_path) as f:
-            rsa = f.read()
-            rsa = rsa.strip() if rsa else rsa
-            utils.VerifyRsaPubKey(rsa)
-        metadata = project["commonInstanceMetadata"]
-        for item in metadata.setdefault("items", []):
-            if item["key"] == "sshKeys":
-                sshkey_item = item
-                break
-        else:
-            sshkey_item = {"key": "sshKeys", "value": ""}
-            metadata["items"].append(sshkey_item)
-
+        ssh_rsa_path = os.path.expanduser(ssh_rsa_path)
+        rsa = GetRsaKey(ssh_rsa_path)
         entry = "%s:%s" % (user, rsa)
         logger.debug("New RSA entry: %s", entry)
-        sshkey_item["value"] = "\n".join([sshkey_item["value"].strip(),
-                                          entry]).strip()
-        self.SetCommonInstanceMetadata(metadata)
+
+        gce_instance = self.GetInstance(instance, zone)
+        metadata = gce_instance.get(_METADATA)
+        if RsaNotInMetadata(metadata, entry):
+            self.UpdateRsaInMetadata(zone, instance, metadata, entry)
 
     def CheckAccess(self):
         """Check if the user has read access to the cloud project.
@@ -1443,3 +1447,88 @@
                 return False
             raise
         return True
+
+    def UpdateRsaInMetadata(self, zone, instance, metadata, entry):
+        """Update ssh public key to sshKeys's value in this metadata.
+
+        Args:
+            zone: String, name of zone.
+            instance: String, representing instance name.
+            metadata: Dict, maps a metadata name to its value.
+            entry: String, ssh public key.
+        """
+        ssh_key_item = GetSshKeyFromMetadata(metadata)
+        if ssh_key_item:
+            # The ssh key exists in the metadata so update the reference to it
+            # in the metadata. There may not be an actual ssh key value so
+            # that's why we filter for None to avoid an empty line in front.
+            ssh_key_item[_METADATA_KEY_VALUE] = "\n".join(
+                filter(None, [ssh_key_item[_METADATA_KEY_VALUE], entry]))
+        else:
+            # Since there is no ssh key item in the metadata, we need to add it in.
+            ssh_key_item = {_METADATA_KEY: _SSH_KEYS_NAME,
+                            _METADATA_KEY_VALUE: entry}
+            metadata[_ITEMS].append(ssh_key_item)
+        utils.PrintColorString(
+            "Ssh public key doesn't exist in the instance(%s), adding it."
+            % instance, utils.TextColors.WARNING)
+        self.SetInstanceMetadata(zone, instance, metadata)
+
+
+def RsaNotInMetadata(metadata, entry):
+    """Check ssh public key exist in sshKeys's value.
+
+    Args:
+        metadata: Dict, maps a metadata name to its value.
+        entry: String, ssh public key.
+
+    Returns:
+        Boolean. True if ssh public key doesn't exist in metadata.
+    """
+    for item in metadata.setdefault(_ITEMS, []):
+        if item[_METADATA_KEY] == _SSH_KEYS_NAME:
+            if entry in item[_METADATA_KEY_VALUE]:
+                return False
+    return True
+
+
+def GetSshKeyFromMetadata(metadata):
+    """Get ssh key item from metadata.
+
+    Args:
+        metadata: Dict, maps a metadata name to its value.
+
+    Returns:
+        Dict of ssk_key_item in metadata, None if can't find the ssh key item
+        in metadata.
+    """
+    for item in metadata.setdefault(_ITEMS, []):
+        if item.get(_METADATA_KEY, '') == _SSH_KEYS_NAME:
+            return item
+    return None
+
+
+def GetRsaKey(ssh_rsa_path):
+    """Get rsa key from rsa path.
+
+    Args:
+        ssh_rsa_path: String, The absolute path to public rsa key.
+
+    Returns:
+        String, rsa key.
+
+    Raises:
+        errors.DriverError: RSA file does not exist.
+    """
+    ssh_rsa_path = os.path.expanduser(ssh_rsa_path)
+    if not os.path.exists(ssh_rsa_path):
+        raise errors.DriverError(
+            "RSA file %s does not exist." % ssh_rsa_path)
+
+    with open(ssh_rsa_path) as f:
+        rsa = f.read()
+        # The space must be removed here for string processing,
+        # if it is not string, it doesn't have a strip function.
+        rsa = rsa.strip() if rsa else rsa
+        utils.VerifyRsaPubKey(rsa)
+    return rsa
diff --git a/internal/lib/gcompute_client_test.py b/internal/lib/gcompute_client_test.py
index a34bfa2..5176306 100644
--- a/internal/lib/gcompute_client_test.py
+++ b/internal/lib/gcompute_client_test.py
@@ -57,6 +57,15 @@
     OPERATION_NAME = "fake-op"
     IMAGE_FINGERPRINT = "L_NWHuz7wTY="
     GPU = "fancy-graphics"
+    SSHKEY = (
+        "ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABAQDBkTOTRze9v2VOqkkf7RG"
+        "jSkg6Z2kb9Q9UHsDGatvend3fmjIw1Tugg0O7nnjlPkskmlgyd4a/j99WOeLL"
+        "CPk6xPyoVjrPUVBU/pAk09ORTC4Zqk6YjlW7LOfzvqmXhmIZfYu6Q4Yt50pZzhl"
+        "lllfu26nYjY7Tg12D019nJi/kqPX5+NKgt0LGXTu8T1r2Gav/q4V7QRWQrB8Eiu"
+        "pxXR7I2YhynqovkEt/OXG4qWgvLEXGsWtSQs0CtCzqEVxz0Y9ECr7er4VdjSQxV"
+        "AaeLAsQsK9ROae8hMBFZ3//8zLVapBwpuffCu+fUoql9qeV9xagZcc9zj8XOUOW"
+        "ApiihqNL1111 test@test1.org")
+    EXTRA_SCOPES = ["scope1"]
 
     def setUp(self):
         """Set up test."""
@@ -64,6 +73,7 @@
         self.Patch(gcompute_client.ComputeClient, "InitResourceHandle")
         fake_cfg = mock.MagicMock()
         fake_cfg.project = PROJECT
+        fake_cfg.extra_scopes = self.EXTRA_SCOPES
         self.compute_client = gcompute_client.ComputeClient(
             fake_cfg, mock.MagicMock())
         self.compute_client._service = mock.MagicMock()
@@ -522,6 +532,9 @@
         extra_disk_name = "gce-x86-userdebug-2345-abcd-data"
         expected_disk_args = [self._disk_args]
         expected_disk_args.extend([{"fake_extra_arg": "fake_extra_value"}])
+        expected_scope = []
+        expected_scope.extend(self.compute_client.DEFAULT_INSTANCE_SCOPE)
+        expected_scope.extend(self.EXTRA_SCOPES)
 
         expected_body = {
             "machineType": self.MACHINE_TYPE_URL,
@@ -539,7 +552,7 @@
             "disks": expected_disk_args,
             "serviceAccounts": [
                 {"email": "default",
-                 "scopes": self.compute_client.DEFAULT_INSTANCE_SCOPE}
+                 "scopes": expected_scope}
             ],
             "metadata": {
                 "items": [{"key": self.METADATA[0],
@@ -554,7 +567,8 @@
             metadata={self.METADATA[0]: self.METADATA[1]},
             network=self.NETWORK,
             zone=self.ZONE,
-            extra_disk_name=extra_disk_name)
+            extra_disk_name=extra_disk_name,
+            extra_scopes=self.EXTRA_SCOPES)
 
         resource_mock.insert.assert_called_with(
             project=PROJECT, zone=self.ZONE, body=expected_body)
@@ -624,7 +638,8 @@
             metadata={self.METADATA[0]: self.METADATA[1]},
             network=self.NETWORK,
             zone=self.ZONE,
-            gpu=self.GPU)
+            gpu=self.GPU,
+            extra_scopes=None)
 
         resource_mock.insert.assert_called_with(
             project=PROJECT, zone=self.ZONE, body=expected_body)
@@ -1026,25 +1041,192 @@
         self.assertEqual(ip_name_map, {"172.22.22.22": "instance_1",
                                        "172.22.22.23": None})
 
-    def testAddSshRsa(self):
-        """Test AddSshRsa.."""
+    def testRsaNotInMetadata(self):
+        """Test rsa not in metadata."""
         fake_user = "fake_user"
-        sshkey = (
-            "ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABAQDBkTOTRze9v2VOqkkf7RG"
-            "jSkg6Z2kb9Q9UHsDGatvend3fmjIw1Tugg0O7nnjlPkskmlgyd4a/j99WOeLL"
-            "CPk6xPyoVjrPUVBU/pAk09ORTC4Zqk6YjlW7LOfzvqmXhmIZfYu6Q4Yt50pZzhl"
-            "lllfu26nYjY7Tg12D019nJi/kqPX5+NKgt0LGXTu8T1r2Gav/q4V7QRWQrB8Eiu"
-            "pxXR7I2YhynqovkEt/OXG4qWgvLEXGsWtSQs0CtCzqEVxz0Y9ECr7er4VdjSQxV"
-            "AaeLAsQsK9ROae8hMBFZ3//8zLVapBwpuffCu+fUoql9qeV9xagZcc9zj8XOUOW"
-            "ApiihqNL1111 test@test1.org")
-        project = {
-            "commonInstanceMetadata": {
+        fake_ssh_key = "fake_ssh"
+        metadata = {
+            "kind": "compute#metadata",
+            "fingerprint": "a-23icsyx4E=",
+            "items": [
+                {
+                    "key": "sshKeys",
+                    "value": "%s:%s" % (fake_user, self.SSHKEY)
+                }
+            ]
+        }
+        # Test rsa doesn't exist in metadata.
+        new_entry = "%s:%s" % (fake_user, fake_ssh_key)
+        self.assertEqual(True, gcompute_client.RsaNotInMetadata(metadata, new_entry))
+
+        # Test rsa exists in metadata.
+        exist_entry = "%s:%s" %(fake_user, self.SSHKEY)
+        self.assertEqual(False, gcompute_client.RsaNotInMetadata(metadata, exist_entry))
+
+    def testGetSshKeyFromMetadata(self):
+        """Test get ssh key from metadata."""
+        fake_user = "fake_user"
+        metadata_key_exist_value_is_empty = {
+            "kind": "compute#metadata",
+            "fingerprint": "a-23icsyx4E=",
+            "items": [
+                {
+                    "key": "sshKeys",
+                    "value": ""
+                }
+            ]
+        }
+        metadata_key_exist = {
+            "kind": "compute#metadata",
+            "fingerprint": "a-23icsyx4E=",
+            "items": [
+                {
+                    "key": "sshKeys",
+                    "value": "%s:%s" % (fake_user, self.SSHKEY)
+                }
+            ]
+        }
+        metadata_key_not_exist = {
+            "kind": "compute#metadata",
+            "fingerprint": "a-23icsyx4E=",
+            "items": [
+                {
+                }
+            ]
+        }
+        expected_key_exist_value_is_empty = {
+            "key": "sshKeys",
+            "value": ""
+        }
+        expected_key_exist = {
+            "key": "sshKeys",
+            "value": "%s:%s" % (fake_user, self.SSHKEY)
+        }
+        self.assertEqual(expected_key_exist_value_is_empty,
+                         gcompute_client.GetSshKeyFromMetadata(metadata_key_exist_value_is_empty))
+        self.assertEqual(expected_key_exist,
+                         gcompute_client.GetSshKeyFromMetadata(metadata_key_exist))
+        self.assertEqual(None,
+                         gcompute_client.GetSshKeyFromMetadata(metadata_key_not_exist))
+
+
+    def testGetRsaKeyPathExistsFalse(self):
+        """Test the rsa key path not exists."""
+        fake_ssh_rsa_path = "/path/to/test_rsa.pub"
+        self.Patch(os.path, "exists", return_value=False)
+        self.assertRaisesRegexp(errors.DriverError,
+                                "RSA file %s does not exist." % fake_ssh_rsa_path,
+                                gcompute_client.GetRsaKey,
+                                ssh_rsa_path=fake_ssh_rsa_path)
+
+    def testGetRsaKey(self):
+        """Test get the rsa key."""
+        fake_ssh_rsa_path = "/path/to/test_rsa.pub"
+        self.Patch(os.path, "exists", return_value=True)
+        m = mock.mock_open(read_data=self.SSHKEY)
+        with mock.patch("__builtin__.open", m):
+            result = gcompute_client.GetRsaKey(fake_ssh_rsa_path)
+            self.assertEqual(self.SSHKEY, result)
+
+    def testUpdateRsaInMetadata(self):
+        """Test update rsa in metadata."""
+        fake_ssh_key = "fake_ssh"
+        fake_metadata_sshkeys_not_exist = {
+            "kind": "compute#metadata",
+            "fingerprint": "a-23icsyx4E=",
+            "items": [
+                {
+                    "key": "not_sshKeys",
+                    "value": ""
+                }
+            ]
+        }
+        new_entry = "new_user:%s" % fake_ssh_key
+        expected = {
+            "kind": "compute#metadata",
+            "fingerprint": "a-23icsyx4E=",
+            "items": [
+                {
+                    "key": "not_sshKeys",
+                    "value": ""
+                },
+                {
+                    "key": "sshKeys",
+                    "value": new_entry
+                }
+            ]
+        }
+        self.Patch(os.path, "exists", return_value=True)
+        self.Patch(gcompute_client.ComputeClient, "WaitOnOperation")
+        resource_mock = mock.MagicMock()
+        self.compute_client.SetInstanceMetadata = mock.MagicMock(
+            return_value=resource_mock)
+        # Test the key item not exists in the metadata.
+        self.compute_client.UpdateRsaInMetadata(
+            "fake_zone",
+            "fake_instance",
+            fake_metadata_sshkeys_not_exist,
+            new_entry)
+        self.compute_client.SetInstanceMetadata.assert_called_with(
+            "fake_zone",
+            "fake_instance",
+            expected)
+
+        # Test the key item exists in the metadata.
+        fake_metadata_ssh_keys_exists = {
+            "kind": "compute#metadata",
+            "fingerprint": "a-23icsyx4E=",
+            "items": [
+                {
+                    "key": "sshKeys",
+                    "value": "old_user:%s" % self.SSHKEY
+                }
+            ]
+        }
+        expected_ssh_keys_exists = {
+            "kind": "compute#metadata",
+            "fingerprint": "a-23icsyx4E=",
+            "items": [
+                {
+                    "key": "sshKeys",
+                    "value": "old_user:%s\n%s" % (self.SSHKEY, new_entry)
+                }
+            ]
+        }
+
+        self.compute_client.UpdateRsaInMetadata(
+            "fake_zone",
+            "fake_instance",
+            fake_metadata_ssh_keys_exists,
+            new_entry)
+        self.compute_client.SetInstanceMetadata.assert_called_with(
+            "fake_zone",
+            "fake_instance",
+            expected_ssh_keys_exists)
+
+    def testAddSshRsaToInstance(self):
+        """Test add ssh rsa key to instance."""
+        fake_user = "fake_user"
+        instance_metadata_key_not_exist = {
+            "metadata": {
                 "kind": "compute#metadata",
                 "fingerprint": "a-23icsyx4E=",
                 "items": [
                     {
                         "key": "sshKeys",
-                        "value": "user:key"
+                        "value": ""
+                    }
+                ]
+            }
+        }
+        instance_metadata_key_exist = {
+            "metadata": {
+                "kind": "compute#metadata",
+                "fingerprint": "a-23icsyx4E=",
+                "items": [
+                    {
+                        "key": "sshKeys",
+                        "value": "%s:%s" % (fake_user, self.SSHKEY)
                     }
                 ]
             }
@@ -1055,51 +1237,47 @@
             "items": [
                 {
                     "key": "sshKeys",
-                    "value": "user:key\n%s:%s" % (fake_user, sshkey)
+                    "value": "%s:%s" % (fake_user, self.SSHKEY)
                 }
             ]
         }
 
         self.Patch(os.path, "exists", return_value=True)
-        m = mock.mock_open(read_data=sshkey)
+        m = mock.mock_open(read_data=self.SSHKEY)
         self.Patch(gcompute_client.ComputeClient, "WaitOnOperation")
-        self.Patch(
-            gcompute_client.ComputeClient, "GetProject", return_value=project)
         resource_mock = mock.MagicMock()
-        self.compute_client._service.projects = mock.MagicMock(
+        self.compute_client._service.instances = mock.MagicMock(
             return_value=resource_mock)
-        resource_mock.setCommonInstanceMetadata = mock.MagicMock()
+        resource_mock.setMetadata = mock.MagicMock()
 
-        with mock.patch("__builtin__.open", m):
-            self.compute_client.AddSshRsa(fake_user, "/path/to/test_rsa.pub")
-            resource_mock.setCommonInstanceMetadata.assert_called_with(
-                project=PROJECT, body=expected)
-
-    def testAddSshRsaInvalidKey(self):
-        """Test AddSshRsa.."""
-        fake_user = "fake_user"
-        sshkey = "ssh-rsa v2VOqkkf7RGL1111 test@test1.org"
-        project = {
-            "commonInstanceMetadata": {
-                "kind": "compute#metadata",
-                "fingerprint": "a-23icsyx4E=",
-                "items": [
-                    {
-                        "key": "sshKeys",
-                        "value": "user:key"
-                    }
-                ]
-            }
-        }
-        self.Patch(os.path, "exists", return_value=True)
-        m = mock.mock_open(read_data=sshkey)
-        self.Patch(gcompute_client.ComputeClient, "WaitOnOperation")
+        # Test the key not exists in the metadata.
         self.Patch(
-            gcompute_client.ComputeClient, "GetProject", return_value=project)
+            gcompute_client.ComputeClient, "GetInstance",
+            return_value=instance_metadata_key_not_exist)
         with mock.patch("__builtin__.open", m):
-            self.assertRaisesRegexp(errors.DriverError, "rsa key is invalid:*",
-                                    self.compute_client.AddSshRsa, fake_user,
-                                    "/path/to/test_rsa.pub")
+            self.compute_client.AddSshRsaInstanceMetadata(
+                "fake_zone",
+                fake_user,
+                "/path/to/test_rsa.pub",
+                "fake_instance")
+            resource_mock.setMetadata.assert_called_with(
+                project=PROJECT,
+                zone="fake_zone",
+                instance="fake_instance",
+                body=expected)
+
+        # Test the key already exists in the metadata.
+        resource_mock.setMetadata.call_count = 0
+        self.Patch(
+            gcompute_client.ComputeClient, "GetInstance",
+            return_value=instance_metadata_key_exist)
+        with mock.patch("__builtin__.open", m):
+            self.compute_client.AddSshRsaInstanceMetadata(
+                "fake_zone",
+                fake_user,
+                "/path/to/test_rsa.pub",
+                "fake_instance")
+            resource_mock.setMetadata.assert_not_called()
 
     @mock.patch.object(gcompute_client.ComputeClient, "WaitOnOperation")
     def testDeleteDisks(self, mock_wait):
diff --git a/internal/lib/goldfish_compute_client.py b/internal/lib/goldfish_compute_client.py
index 4377d6d..877eb3d 100644
--- a/internal/lib/goldfish_compute_client.py
+++ b/internal/lib/goldfish_compute_client.py
@@ -43,6 +43,7 @@
 import logging
 
 from acloud import errors
+from acloud.internal import constants
 from acloud.internal.lib import android_compute_client
 from acloud.internal.lib import gcompute_client
 
@@ -132,7 +133,9 @@
                        emulator_branch=None,
                        emulator_build_id=None,
                        blank_data_disk_size_gb=None,
-                       gpu=None):
+                       gpu=None,
+                       avd_spec=None,
+                       extra_scopes=None):
         """Create a goldfish instance given a stable host image and a build id.
 
         Args:
@@ -148,6 +151,8 @@
             blank_data_disk_size_gb: Integer, size of the blank data disk in GB.
             gpu: String, GPU that should be attached to the instance, or None of no
                  acceleration is needed. e.g. "nvidia-tesla-k80"
+            avd_spec: An AVDSpec instance.
+            extra_scopes: A list of extra scopes to be passed to the instance.
         """
         self._CheckMachineSize()
 
@@ -161,10 +166,10 @@
         # Goldfish instances are metadata compatible with cuttlefish devices.
         # See details goto/goldfish-deployment
         metadata = self._metadata.copy()
-        resolution = self._resolution.split("x")
+        metadata["user"] = getpass.getuser()
+        metadata[constants.INS_KEY_AVD_TYPE] = constants.TYPE_GF
 
         # Note that we use the same metadata naming conventions as cuttlefish
-        metadata["cvd_01_dpi"] = resolution[3]
         metadata["cvd_01_fetch_android_build_target"] = build_target
         metadata["cvd_01_fetch_android_bid"] = "{branch}/{build_id}".format(
             branch=branch, build_id=build_id)
@@ -173,8 +178,28 @@
                 "cvd_01_fetch_emulator_bid"] = "{branch}/{build_id}".format(
                     branch=emulator_branch, build_id=emulator_build_id)
         metadata["cvd_01_launch"] = "1"
-        metadata["cvd_01_x_res"] = resolution[0]
-        metadata["cvd_01_y_res"] = resolution[1]
+
+        # Update metadata by avd_spec
+        # for legacy create_gf cmd, we will keep using resolution.
+        # And always use avd_spec for acloud create cmd.
+        if avd_spec:
+            metadata[constants.INS_KEY_AVD_FLAVOR] = avd_spec.flavor
+            metadata["cvd_01_x_res"] = avd_spec.hw_property[constants.HW_X_RES]
+            metadata["cvd_01_y_res"] = avd_spec.hw_property[constants.HW_Y_RES]
+            metadata["cvd_01_dpi"] = avd_spec.hw_property[constants.HW_ALIAS_DPI]
+            metadata[constants.INS_KEY_DISPLAY] = ("%sx%s (%s)" % (
+                avd_spec.hw_property[constants.HW_X_RES],
+                avd_spec.hw_property[constants.HW_Y_RES],
+                avd_spec.hw_property[constants.HW_ALIAS_DPI]))
+        else:
+            resolution = self._resolution.split("x")
+            metadata["cvd_01_x_res"] = resolution[0]
+            metadata["cvd_01_y_res"] = resolution[1]
+            metadata["cvd_01_dpi"] = resolution[3]
+
+        # Add labels for giving the instances ability to be filter for
+        # acloud list/delete cmds.
+        labels = {constants.LABEL_CREATE_BY: getpass.getuser()}
 
         # Add per-instance ssh key
         if self._ssh_public_key_path:
@@ -197,4 +222,6 @@
             machine_type=self._machine_type,
             network=self._network,
             zone=self._zone,
-            gpu=gpu)
+            gpu=gpu,
+            labels=labels,
+            extra_scopes=extra_scopes)
diff --git a/internal/lib/goldfish_compute_client_test.py b/internal/lib/goldfish_compute_client_test.py
index 461fa48..fcdf085 100644
--- a/internal/lib/goldfish_compute_client_test.py
+++ b/internal/lib/goldfish_compute_client_test.py
@@ -44,6 +44,7 @@
     EXTRA_DATA_DISK_SIZE_GB = 4
     BOOT_DISK_SIZE_GB = 10
     GPU = "nvidia-tesla-k80"
+    EXTRA_SCOPES = "scope1"
 
     def _GetFakeConfig(self):
         """Create a fake configuration object.
@@ -60,6 +61,7 @@
             x=self.X_RES, y=self.Y_RES, dpi=self.DPI)
         fake_cfg.metadata_variable = self.METADATA
         fake_cfg.extra_data_disk_size_gb = self.EXTRA_DATA_DISK_SIZE_GB
+        fake_cfg.extra_scopes = self.EXTRA_SCOPES
         return fake_cfg
 
     def setUp(self):
@@ -86,34 +88,34 @@
                 "fake_arg": "fake_value"
             }])
 
-    def testCreateInstance(self):
+    @mock.patch("getpass.getuser", return_value="fake_user")
+    def testCreateInstance(self, _mock_user):
         """Test CreateInstance."""
 
         expected_metadata = {
-            "cvd_01_dpi":
-            str(self.DPI),
-            "cvd_01_fetch_android_build_target":
-            self.TARGET,
+            "user": "fake_user",
+            "avd_type": "goldfish",
+            "cvd_01_fetch_android_build_target": self.TARGET,
             "cvd_01_fetch_android_bid":
             "{branch}/{build_id}".format(
                 branch=self.BRANCH, build_id=self.BUILD_ID),
             "cvd_01_fetch_emulator_bid":
             "{branch}/{build_id}".format(
                 branch=self.EMULATOR_BRANCH, build_id=self.EMULATOR_BUILD_ID),
-            "cvd_01_launch":
-            "1",
-            "cvd_01_x_res":
-            str(self.X_RES),
-            "cvd_01_y_res":
-            str(self.Y_RES),
+            "cvd_01_launch": "1",
+            "cvd_01_dpi": str(self.DPI),
+            "cvd_01_x_res": str(self.X_RES),
+            "cvd_01_y_res": str(self.Y_RES),
         }
         expected_metadata.update(self.METADATA)
         expected_disk_args = [{"fake_arg": "fake_value"}]
+        expected_labels = {'created_by': "fake_user"}
 
         self.goldfish_compute_client.CreateInstance(
             self.INSTANCE, self.IMAGE, self.IMAGE_PROJECT, self.TARGET,
             self.BRANCH, self.BUILD_ID, self.EMULATOR_BRANCH,
-            self.EMULATOR_BUILD_ID, self.EXTRA_DATA_DISK_SIZE_GB, self.GPU)
+            self.EMULATOR_BUILD_ID, self.EXTRA_DATA_DISK_SIZE_GB, self.GPU,
+            extra_scopes=self.EXTRA_SCOPES)
 
         # pylint: disable=no-member
         gcompute_client.ComputeClient.CreateInstance.assert_called_with(
@@ -126,7 +128,9 @@
             machine_type=self.MACHINE_TYPE,
             network=self.NETWORK,
             zone=self.ZONE,
-            gpu=self.GPU)
+            gpu=self.GPU,
+            labels=expected_labels,
+            extra_scopes=self.EXTRA_SCOPES)
 
 
 if __name__ == "__main__":
diff --git a/internal/lib/utils.py b/internal/lib/utils.py
index 0f19078..e1a2239 100755
--- a/internal/lib/utils.py
+++ b/internal/lib/utils.py
@@ -61,6 +61,17 @@
 # Store the ports that vnc/adb are forwarded to, both are integers.
 ForwardedPorts = collections.namedtuple("ForwardedPorts", [constants.VNC_PORT,
                                                            constants.ADB_PORT])
+AVD_PORT_DICT = {
+    constants.TYPE_GCE: ForwardedPorts(constants.GCE_VNC_PORT,
+                                       constants.GCE_ADB_PORT),
+    constants.TYPE_CF: ForwardedPorts(constants.CF_VNC_PORT,
+                                      constants.CF_ADB_PORT),
+    constants.TYPE_GF: ForwardedPorts(constants.GF_VNC_PORT,
+                                      constants.GF_ADB_PORT),
+    constants.TYPE_CHEEPS: ForwardedPorts(constants.CHEEPS_VNC_PORT,
+                                          constants.CHEEPS_ADB_PORT)
+}
+
 _VNC_BIN = "ssvnc"
 _CMD_KILL = ["pkill", "-9", "-f"]
 _CMD_PGREP = "pgrep"
@@ -68,7 +79,7 @@
 _CMD_START_VNC = "%(bin)s vnc://127.0.0.1:%(port)d"
 _CMD_INSTALL_SSVNC = "sudo apt-get --assume-yes install ssvnc"
 _ENV_DISPLAY = "DISPLAY"
-_SSVNC_ENV_VARS = {"SSVNC_NO_ENC_WARN": "1", "SSVNC_SCALE": "auto"}
+_SSVNC_ENV_VARS = {"SSVNC_NO_ENC_WARN": "1", "SSVNC_SCALE": "auto", "VNCVIEWER_X11CURSOR": "1"}
 _DEFAULT_DISPLAY_SCALE = 1.0
 _DIST_DIR = "DIST_DIR"
 
@@ -82,6 +93,7 @@
 _SUPPORTED_SYSTEMS_AND_DISTS = {"Linux": ["Ubuntu", "Debian"]}
 _DEFAULT_TIMEOUT_ERR = "Function did not complete within %d secs."
 
+
 class TempDir(object):
     """A context manager that ceates a temporary directory.
 
@@ -436,6 +448,7 @@
         raise errors.DriverError(
             "rsa key is invalid: %s, error: %s" % (rsa, str(e)))
 
+
 def Decompress(sourcefile, dest=None):
     """Decompress .zip or .tar.gz.
 
@@ -459,6 +472,7 @@
             "Sorry, we could only support compression file type "
             "for zip or tar.gz.")
 
+
 # pylint: disable=old-style-class,no-init
 class TextColors:
     """A class that defines common color ANSI code."""
@@ -845,6 +859,7 @@
 
     Args:
         answer_list: list of the answers to choose from.
+        enable_choose_all: True to choose all items from answer list.
 
     Return:
         List holding the answer(s).
@@ -870,8 +885,7 @@
             continue
         # Filter out choices
         if choice == 0:
-            print("Exiting acloud.")
-            sys.exit()
+            sys.exit(constants.EXIT_BY_USER)
         if enable_choose_all and choice == max_choice:
             return answer_list
         if choice < 0 or choice > max_choice:
@@ -898,8 +912,8 @@
             PrintColorString("No VNC port specified, skipping VNC startup.",
                              TextColors.FAIL)
 
-def LaunchVncClient(port=constants.DEFAULT_VNC_PORT, avd_width=None,
-                    avd_height=None, no_prompts=False):
+
+def LaunchVncClient(port, avd_width=None, avd_height=None, no_prompts=False):
     """Launch ssvnc.
 
     Args:
@@ -1169,3 +1183,25 @@
         return _FunctionWrapper
 
     return _Wrapper
+
+
+def GetBuildEnvironmentVariable(variable_name):
+    """Get build environment variable.
+
+    Args:
+        variable_name: String of variable name.
+
+    Returns:
+        String, the value of the variable.
+
+    Raises:
+        errors.GetAndroidBuildEnvVarError: No environment variable found.
+    """
+    try:
+        return os.environ[variable_name]
+    except KeyError:
+        raise errors.GetAndroidBuildEnvVarError(
+            "Could not get environment var: %s\n"
+            "Try to run 'source build/envsetup.sh && lunch <target>'"
+            % variable_name
+        )
diff --git a/internal/proto/internal_config.proto b/internal/proto/internal_config.proto
index a9d539b..cbb1db7 100755
--- a/internal/proto/internal_config.proto
+++ b/internal/proto/internal_config.proto
@@ -38,6 +38,10 @@
   optional string stable_cheeps_host_image_name = 9;
   // [CHEEPS only] The project where stable host image is
   optional string stable_cheeps_host_image_project = 10;
+  // The pattern of the instance name, e.g. ins-{uuid}-{build_id}-{build_target}
+  // the parts in {} will be automatically replaced with the actual value if
+  // you specify them in the pattern, uuid will be automatically generated.
+  optional string instance_name_pattern = 11;
 }
 
 // Internal configuration
diff --git a/internal/proto/user_config.proto b/internal/proto/user_config.proto
index d862099..131b42e 100755
--- a/internal/proto/user_config.proto
+++ b/internal/proto/user_config.proto
@@ -85,4 +85,17 @@
   optional string stable_cheeps_host_image_name = 22;
   // [CHEEPS only] The project that the stable host image is released to
   optional string stable_cheeps_host_image_project = 23;
+
+  // [CVD only] It will get passed into the launch_cvd command if not empty.
+  // In version 0.7.2 and later.
+  optional string launch_args = 24;
+
+  // The pattern of the instance name, e.g. ins-{uuid}-{build_id}-{build_target}
+  // the parts in {} will be automatically replaced with the actual value if
+  // you specify them in the pattern, uuid will be automatically generated.
+  optional string instance_name_pattern = 25;
+
+  // List of scopes that will be given to the instance
+  // https://cloud.google.com/compute/docs/access/create-enable-service-accounts-for-instances#changeserviceaccountandscopes
+  repeated string extra_scopes = 26;
 }
diff --git a/list/instance.py b/list/instance.py
index 4cc64a7..b347785 100644
--- a/list/instance.py
+++ b/list/instance.py
@@ -26,7 +26,6 @@
 - and more!
 """
 
-import collections
 import datetime
 import logging
 import re
@@ -61,9 +60,6 @@
 _FULL_NAME_STRING = ("device serial: %(device_serial)s (%(instance_name)s) "
                      "elapsed time: %(elapsed_time)s")
 
-ForwardedPorts = collections.namedtuple("ForwardedPorts",
-                                        [constants.VNC_PORT, constants.ADB_PORT])
-
 
 def _GetElapsedTime(start_time):
     """Calculate the elapsed time from start_time till now.
@@ -225,14 +221,14 @@
                 local_instance._elapsed_time = _GetElapsedTime(date_str)
                 local_instance._fullname = (_FULL_NAME_STRING %
                                             {"device_serial": "127.0.0.1:%d" %
-                                                              constants.DEFAULT_ADB_PORT,
+                                                              constants.CF_ADB_PORT,
                                              "instance_name": local_instance._name,
                                              "elapsed_time": local_instance._elapsed_time})
                 local_instance._avd_type = constants.TYPE_CF
                 local_instance._ip = "127.0.0.1"
                 local_instance._status = constants.INS_STATUS_RUNNING
-                local_instance._adb_port = constants.DEFAULT_ADB_PORT
-                local_instance._vnc_port = constants.DEFAULT_VNC_PORT
+                local_instance._adb_port = constants.CF_ADB_PORT
+                local_instance._vnc_port = constants.CF_VNC_PORT
                 local_instance._display = ("%sx%s (%s)" % (x_res, y_res, dpi))
                 local_instance._is_local = True
                 local_instance._ssh_tunnel_is_connected = True
@@ -336,12 +332,8 @@
             used in the ssh forwarded call. Both fields are integers.
         """
         process_output = subprocess.check_output(constants.COMMAND_PS)
-        default_vnc_port = (constants.DEFAULT_GCE_VNC_PORT
-                            if avd_type == constants.TYPE_GCE
-                            else constants.DEFAULT_VNC_PORT)
-        default_adb_port = (constants.DEFAULT_GCE_ADB_PORT
-                            if avd_type == constants.TYPE_GCE
-                            else constants.DEFAULT_ADB_PORT)
+        default_vnc_port = utils.AVD_PORT_DICT[avd_type].vnc_port
+        default_adb_port = utils.AVD_PORT_DICT[avd_type].adb_port
         re_pattern = re.compile(_RE_SSH_TUNNEL_PATTERN %
                                 (_RE_GROUP_VNC, default_vnc_port,
                                  _RE_GROUP_ADB, default_adb_port, ip))
@@ -359,4 +351,4 @@
                       "IP:%s, forwarding (adb:%d, vnc:%d)"), ip, adb_port,
                      vnc_port)
 
-        return ForwardedPorts(vnc_port=vnc_port, adb_port=adb_port)
+        return utils.ForwardedPorts(vnc_port=vnc_port, adb_port=adb_port)
diff --git a/list/instance_test.py b/list/instance_test.py
index e0f4ffd..e29994b 100644
--- a/list/instance_test.py
+++ b/list/instance_test.py
@@ -57,7 +57,7 @@
         self.assertEqual("1080x1920 (480)", local_instance.display)
         self.assertEqual("Sat Nov 10 21:55:10 2018", local_instance.createtime)
         expected_full_name = "device serial: 127.0.0.1:%s (%s) elapsed time: %s" % (
-            constants.DEFAULT_ADB_PORT, constants.LOCAL_INS_NAME, "fake_time")
+            constants.CF_ADB_PORT, constants.LOCAL_INS_NAME, "fake_time")
         self.assertEqual(expected_full_name, local_instance.fullname)
 
         # test return None if no launch_cvd process found
@@ -95,7 +95,7 @@
         self.Patch(instance, "_GetElapsedTime", return_value="fake_time")
         forwarded_ports = instance.RemoteInstance(
             mock.MagicMock()).GetAdbVncPortFromSSHTunnel(
-                "fake_ip", "fake_avd_type")
+                "fake_ip", constants.TYPE_CF)
         self.assertEqual(54321, forwarded_ports.adb_port)
         self.assertEqual(12345, forwarded_ports.vnc_port)
 
diff --git a/list/list.py b/list/list.py
index 9354a99..6c18881 100644
--- a/list/list.py
+++ b/list/list.py
@@ -142,10 +142,10 @@
     """
     instances_list = GetInstances(cfg)
     if (len(instances_list) > 1) and not select_all_instances:
-        print("Multiple instance detected, choose 1 to proceed:")
-        instances_to_delete = utils.GetAnswerFromList(instances_list,
-                                                      enable_choose_all=True)
-        return instances_to_delete
+        print("Multiple instances detected, choose any one to proceed:")
+        instances = utils.GetAnswerFromList(instances_list,
+                                            enable_choose_all=True)
+        return instances
 
     return instances_list
 
@@ -184,6 +184,34 @@
     return instance_list
 
 
+def GetInstanceFromAdbPort(cfg, adb_port):
+    """Get instance from adb port.
+
+    Args:
+        cfg: AcloudConfig object.
+        adb_port: int, adb port of instance.
+
+    Returns:
+        List of list.Instance() object.
+
+    Raises:
+        errors.NoInstancesFound: No instances found.
+    """
+    all_instance_info = []
+    for instance_object in GetInstances(cfg):
+        if instance_object.forwarding_adb_port == adb_port:
+            return [instance_object]
+        all_instance_info.append(instance_object.fullname)
+
+    # Show devices information to user when user provides wrong adb port.
+    if all_instance_info:
+        hint_message = ("No instance with adb port %d, available instances:\n%s"
+                        % (adb_port, "\n".join(all_instance_info)))
+    else:
+        hint_message = "No instances to delete."
+    raise errors.NoInstancesFound(hint_message)
+
+
 def Run(args):
     """Run list.
 
diff --git a/list/list_test.py b/list/list_test.py
index 5cbe02a..d42fa37 100644
--- a/list/list_test.py
+++ b/list/list_test.py
@@ -61,6 +61,22 @@
             cfg=cfg,
             instance_names=instance_names)
 
+    # pylint: disable=attribute-defined-outside-init
+    def testGetInstanceFromAdbPort(self):
+        """test GetInstanceFromAdbPort."""
+        cfg = mock.MagicMock()
+        alive_instance1 = InstanceObject("alive_instance1")
+        alive_instance1.forwarding_adb_port = 1111
+        alive_instance1.fullname = "device serial: 127.0.0.1:1111 alive_instance1"
+        expected_instance = [alive_instance1]
+        self.Patch(list_instance, "GetInstances", return_value=[alive_instance1])
+        # Test to find instance by adb port number.
+        self.assertEqual(expected_instance,
+                         list_instance.GetInstanceFromAdbPort(cfg, 1111))
+        # Test for instance can't be found by adb port number.
+        with self.assertRaises(errors.NoInstancesFound):
+            list_instance.GetInstanceFromAdbPort(cfg, 2222)
+
 
 if __name__ == "__main__":
     unittest.main()
diff --git a/metrics/metrics.py b/metrics/metrics.py
index 5ec7027..1d8b1a3 100644
--- a/metrics/metrics.py
+++ b/metrics/metrics.py
@@ -26,9 +26,24 @@
 
 logger = logging.getLogger(__name__)
 
+# pylint: disable=broad-except
+def LogUsage(argv):
+    """Log acloud start event.
 
-def LogUsage():
-    """Log acloud run."""
+    Log acloud start event and the usage, following are the data we log:
+    - tool_name: All asuite tools are storing event in the same database. This
+      property is provided to distinguish different tools.
+    - command_line: Log all command arguments.
+    - test_references: Should be a list, we record the acloud sub-command.
+      e.g. create/delete/reconnect/..etc. We could use this property as filter
+      criteria to speed up query time.
+    - cwd: User's current working directory.
+    - os: The platform that users are working at.
+
+    Args:
+        argv: A list of system arguments.
+    """
+    # TODO(b131867764): We could remove this metics tool after we apply clearcut.
     try:
         from asuite import asuite_metrics
         asuite_metrics.log_event(_METRICS_URL, dummy_key_fallback=False,
@@ -36,8 +51,19 @@
     except ImportError:
         logger.debug("No metrics recorder available, not sending metrics.")
 
+    #Log start event via clearcut tool.
+    try:
+        from asuite import atest_utils
+        from asuite.metrics import metrics_utils
+        atest_utils.print_data_collection_notice()
+        metrics_utils.send_start_event(tool_name=constants.TOOL_NAME,
+                                       command_line=' '.join(argv),
+                                       test_references=[argv[0]])
+    except Exception as e:
+        logger.debug("Failed to send start event:%s", str(e))
 
 
+#TODO(b131867764): We could remove this metics tool after we apply clearcut.
 def _GetLdap():
     """Return string email username for valid domains only, None otherwise."""
     try:
@@ -52,3 +78,23 @@
     except Exception as e:
         logger.debug("error retrieving email: %s", e)
     return None
+
+# pylint: disable=broad-except
+def LogExitEvent(exit_code, stacktrace="", logs=""):
+    """Log acloud exit event.
+
+    A start event should followed by an exit event to calculate the consuming
+    time. This function will be run at the end of acloud main process or
+    at the init of the error object.
+
+    Args:
+        exit_code: Integer, the exit code of acloud main process.
+        stacktrace: A string of stacktrace.
+        logs: A string of logs.
+    """
+    try:
+        from asuite.metrics import metrics_utils
+        metrics_utils.send_exit_event(exit_code, stacktrace=stacktrace,
+                                      logs=logs)
+    except Exception as e:
+        logger.debug("Failed to send exit event:%s", str(e))
diff --git a/public/acloud_main.py b/public/acloud_main.py
index bb4da6c..c960276 100644
--- a/public/acloud_main.py
+++ b/public/acloud_main.py
@@ -48,10 +48,10 @@
 
 from __future__ import print_function
 import argparse
-import getpass
 import logging
 import platform
 import sys
+import traceback
 
 # TODO: Remove this once we switch over to embedded launcher.
 # Exit out if python version is < 2.7.13 due to b/120883119.
@@ -73,13 +73,14 @@
         print("  POSIXLY_CORRECT=1 port -N install python27")
     sys.exit(1)
 
-# Needed to silence oauth2client.
-# This is a workaround to get rid of below warning message:
+# By Default silence root logger's stream handler since 3p lib may initial
+# root logger no matter what level we're using. The acloud logger behavior will
+# be defined in _SetupLogging(). This also could workaround to get rid of below
+# oauth2client warning:
 # 'No handlers could be found for logger "oauth2client.contrib.multistore_file'
-# TODO(b/112803893): Remove this code once bug is fixed.
-OAUTH2_LOGGER = logging.getLogger('oauth2client.contrib.multistore_file')
-OAUTH2_LOGGER.setLevel(logging.CRITICAL)
-OAUTH2_LOGGER.addHandler(logging.FileHandler("/dev/null"))
+DEFAULT_STREAM_HANDLER = logging.StreamHandler()
+DEFAULT_STREAM_HANDLER.setLevel(logging.CRITICAL)
+logging.getLogger().addHandler(DEFAULT_STREAM_HANDLER)
 
 # pylint: disable=wrong-import-position
 from acloud import errors
@@ -87,6 +88,7 @@
 from acloud.create import create_args
 from acloud.delete import delete
 from acloud.delete import delete_args
+from acloud.internal import constants
 from acloud.reconnect import reconnect
 from acloud.reconnect import reconnect_args
 from acloud.list import list as list_instances
@@ -107,7 +109,6 @@
 CMD_CREATE_CUTTLEFISH = "create_cf"
 CMD_CREATE_GOLDFISH = "create_gf"
 CMD_CLEANUP = "cleanup"
-CMD_SSHKEY = "project_sshkey"
 
 
 # pylint: disable=too-many-statements
@@ -145,13 +146,26 @@
         help="Android branch, e.g. git_master")
     create_cf_parser.add_argument(
         "--kernel_build_id",
+        "--kernel-build-id",
         type=str,
         dest="kernel_build_id",
         required=False,
         help="Android kernel build id, e.g. 4586590. This is to test a new"
-        " kernel build with a particular Android build (--build_id). If not"
-        " specified, the kernel that's bundled with the Android build would"
-        " be used.")
+        " kernel build with a particular Android build (--build_id). If neither"
+        " kernel_branch nor kernel_build_id are specified, the kernel that's"
+        " bundled with the Android build would be used.")
+    create_cf_parser.add_argument(
+        "--kernel_branch",
+        "--kernel-branch",
+        type=str,
+        dest="kernel_branch",
+        required=False,
+        help="Android kernel build branch name, e.g."
+        " kernel-common-android-4.14. This is to test a new kernel build with a"
+        " particular Android build (--build-id). If specified without"
+        " specifying kernel_build_id, the last green build in the branch will"
+        " be used. If neither kernel_branch nor kernel_build_id are specified,"
+        " the kernel that's bundled with the Android build would be used.")
 
     create_args.AddCommonCreateArgs(create_cf_parser)
     subparser_list.append(create_cf_parser)
@@ -209,26 +223,6 @@
         "images that are older than |expiration_mins|.")
     subparser_list.append(cleanup_parser)
 
-    # Command "project_sshkey"
-    sshkey_parser = subparsers.add_parser(CMD_SSHKEY)
-    sshkey_parser.required = False
-    sshkey_parser.set_defaults(which=CMD_SSHKEY)
-    sshkey_parser.add_argument(
-        "--user",
-        type=str,
-        dest="user",
-        default=getpass.getuser(),
-        help="The user name which the sshkey belongs to, default to: %s." %
-        getpass.getuser())
-    sshkey_parser.add_argument(
-        "--ssh_rsa_path",
-        type=str,
-        dest="ssh_rsa_path",
-        required=True,
-        help="Absolute path to the file that contains the public rsa key "
-        "that will be added as project-wide ssh key.")
-    subparser_list.append(sshkey_parser)
-
     # Command "create"
     subparser_list.append(create_args.GetCreateArgParser(subparsers))
 
@@ -363,7 +357,6 @@
     # Check access.
     # device_driver.CheckAccess(cfg)
 
-    metrics.LogUsage()
     report = None
     if args.which == create_args.CMD_CREATE:
         create.Run(args)
@@ -373,6 +366,7 @@
             build_target=args.build_target,
             build_id=args.build_id,
             kernel_build_id=args.kernel_build_id,
+            kernel_branch=args.kernel_branch,
             num=args.num,
             serial_log_file=args.serial_log_file,
             logcat_file=args.logcat_file,
@@ -399,8 +393,6 @@
         list_instances.Run(args)
     elif args.which == reconnect_args.CMD_RECONNECT:
         reconnect.Run(args)
-    elif args.which == CMD_SSHKEY:
-        report = device_driver.AddSshRsa(cfg, args.user, args.ssh_rsa_path)
     elif args.which == setup_args.CMD_SETUP:
         setup.Run(args)
     else:
@@ -417,4 +409,19 @@
 
 
 if __name__ == "__main__":
-    main(sys.argv[1:])
+    EXIT_CODE = None
+    EXCEPTION_STACKTRACE = None
+    EXCEPTION_LOG = None
+    metrics.LogUsage(sys.argv[1:])
+    try:
+        EXIT_CODE = main(sys.argv[1:])
+    except Exception as e:
+        EXIT_CODE = constants.EXIT_BY_ERROR
+        EXCEPTION_STACKTRACE = traceback.format_exc()
+        EXCEPTION_LOG = str(e)
+        raise
+    finally:
+        # Log Exit event here to calculate the consuming time.
+        metrics.LogExitEvent(EXIT_CODE,
+                             stacktrace=EXCEPTION_STACKTRACE,
+                             logs=EXCEPTION_LOG)
diff --git a/public/actions/base_device_factory.py b/public/actions/base_device_factory.py
index 4cfdf65..bd70aad 100644
--- a/public/actions/base_device_factory.py
+++ b/public/actions/base_device_factory.py
@@ -24,6 +24,8 @@
 class BaseDeviceFactory(object):
   """A class that provides basic interface to create a device factory."""
 
+  LATEST = "latest"
+
   def __init__(self, compute_client):
 
     self._compute_client = compute_client
diff --git a/public/actions/common_operations.py b/public/actions/common_operations.py
index ff86217..79e5ef9 100644
--- a/public/actions/common_operations.py
+++ b/public/actions/common_operations.py
@@ -34,6 +34,7 @@
 
 logger = logging.getLogger(__name__)
 
+
 def CreateSshKeyPairIfNecessary(cfg):
     """Create ssh key pair if necessary.
 
@@ -237,9 +238,8 @@
 # TODO: Delete unused-argument when b/119614469 is resolved.
 # pylint: disable=unused-argument
 # pylint: disable=too-many-locals
-def CreateDevices(command, cfg, device_factory, num,
+def CreateDevices(command, cfg, device_factory, num, avd_type,
                   report_internal_ip=False, autoconnect=False,
-                  vnc_port=None, adb_port=None,
                   serial_log_file=None, logcat_file=None):
     """Create a set of devices using the given factory.
 
@@ -252,10 +252,9 @@
         cfg: An AcloudConfig instance.
         device_factory: A factory capable of producing a single device.
         num: The number of devices to create.
+        avd_type: String, the AVD type(cuttlefish, goldfish...).
         report_internal_ip: Boolean to report the internal ip instead of
                             external ip.
-        vnc_port: (int) The VNC port to use for a remote instance.
-        adb_port: (int) The ADB port to use for a remote instance.
         serial_log_file: String, the file path to tar the serial logs.
         logcat_file: String, the file path to tar the logcats.
         autoconnect: Boolean, whether to auto connect to device.
@@ -294,15 +293,18 @@
                 "ip": ip,
                 "instance_name": device.instance_name
             }
+            for attr in ("branch", "build_target", "build_id", "kernel_branch",
+                         "kernel_build_target", "kernel_build_id",
+                         "emulator_branch", "emulator_build_target",
+                         "emulator_build_id"):
+                if getattr(device_factory, "_%s" % attr, None):
+                    device_dict[attr] = getattr(device_factory, "_%s" % attr)
             if autoconnect:
-                if (not vnc_port) or (not adb_port):
-                    logger.error("vnc_port and adb_port must be specified to"
-                                 " use autoconnect")
-                forwarded_ports = utils.AutoConnect(ip,
-                                                    cfg.ssh_private_key_path,
-                                                    vnc_port,
-                                                    adb_port,
-                                                    getpass.getuser())
+                forwarded_ports = utils.AutoConnect(
+                    ip, cfg.ssh_private_key_path,
+                    utils.AVD_PORT_DICT[avd_type].vnc_port,
+                    utils.AVD_PORT_DICT[avd_type].adb_port,
+                    getpass.getuser())
                 device_dict[constants.VNC_PORT] = forwarded_ports.vnc_port
                 device_dict[constants.ADB_PORT] = forwarded_ports.adb_port
             if device.instance_name in failures:
diff --git a/public/actions/common_operations_test.py b/public/actions/common_operations_test.py
index 205a321..030a958 100644
--- a/public/actions/common_operations_test.py
+++ b/public/actions/common_operations_test.py
@@ -36,12 +36,26 @@
     IP = gcompute_client.IP(external="127.0.0.1", internal="10.0.0.1")
     INSTANCE = "fake-instance"
     CMD = "test-cmd"
+    AVD_TYPE = "fake-type"
+    BRANCH = "fake-branch"
+    BUILD_TARGET = "fake-target"
+    BUILD_ID = "fake-build-id"
 
+    # pylint: disable=protected-access
     def setUp(self):
         """Set up the test."""
         super(CommonOperationsTest, self).setUp()
         self.build_client = mock.MagicMock()
         self.device_factory = mock.MagicMock()
+        self.device_factory._branch = self.BRANCH
+        self.device_factory._build_target = self.BUILD_TARGET
+        self.device_factory._build_id = self.BUILD_ID
+        self.device_factory._kernel_branch = None
+        self.device_factory._kernel_build_id = None
+        self.device_factory._kernel_build_target = None
+        self.device_factory._emulator_branch = None
+        self.device_factory._emulator_build_id = None
+        self.device_factory._emulator_build_target = None
         self.Patch(
             android_build_client,
             "AndroidBuildClient",
@@ -84,14 +98,18 @@
         """Test Create Devices."""
         cfg = self._CreateCfg()
         _report = common_operations.CreateDevices(self.CMD, cfg,
-                                                  self.device_factory, 1)
+                                                  self.device_factory, 1,
+                                                  self.AVD_TYPE)
         self.assertEqual(_report.command, self.CMD)
         self.assertEqual(_report.status, report.Status.SUCCESS)
         self.assertEqual(
             _report.data,
             {"devices": [{
                 "ip": self.IP.external,
-                "instance_name": self.INSTANCE
+                "instance_name": self.INSTANCE,
+                "branch": self.BRANCH,
+                "build_id": self.BUILD_ID,
+                "build_target": self.BUILD_TARGET,
             }]})
 
     def testCreateDevicesInternalIP(self):
@@ -99,6 +117,7 @@
         cfg = self._CreateCfg()
         _report = common_operations.CreateDevices(self.CMD, cfg,
                                                   self.device_factory, 1,
+                                                  self.AVD_TYPE,
                                                   report_internal_ip=True)
         self.assertEqual(_report.command, self.CMD)
         self.assertEqual(_report.status, report.Status.SUCCESS)
@@ -106,7 +125,10 @@
             _report.data,
             {"devices": [{
                 "ip": self.IP.internal,
-                "instance_name": self.INSTANCE
+                "instance_name": self.INSTANCE,
+                "branch": self.BRANCH,
+                "build_id": self.BUILD_ID,
+                "build_target": self.BUILD_TARGET,
             }]})
 
 if __name__ == "__main__":
diff --git a/public/actions/create_cuttlefish_action.py b/public/actions/create_cuttlefish_action.py
index 288d67f..bcaef94 100644
--- a/public/actions/create_cuttlefish_action.py
+++ b/public/actions/create_cuttlefish_action.py
@@ -52,7 +52,7 @@
                  "/home/vsoc-01/cuttlefish_runtime/cuttlefish_config.json"]
 
     def __init__(self, cfg, build_target, build_id, kernel_build_id=None,
-                 avd_spec=None):
+                 avd_spec=None, kernel_branch=None):
 
         self.credentials = auth.CreateCredentials(cfg)
 
@@ -67,6 +67,9 @@
         self._kernel_build_id = kernel_build_id
         self._blank_data_disk_size_gb = cfg.extra_data_disk_size_gb
         self._avd_spec = avd_spec
+        self._kernel_branch = kernel_branch
+        self._kernel_build_target = cfg.kernel_build_target
+        self._extra_scopes = cfg.extra_scopes
 
         # Configure clients for interaction with GCE/Build servers
         self._build_client = android_build_client.AndroidBuildClient(
@@ -75,9 +78,18 @@
         # Discover branches
         self._branch = self._build_client.GetBranch(build_target, build_id)
         self._kernel_branch = None
-        if kernel_build_id:
+        if kernel_branch:
+            # If no kernel_build_id is given or kernel_build_id is latest,
+            # use the last green build id in the given kernel_branch
+            if not kernel_build_id or kernel_build_id == self.LATEST:
+                self._kernel_build_id = (
+                    self._build_client.GetLKGB(self._kernel_build_target,
+                                               kernel_branch))
+        elif kernel_build_id:
             self._kernel_branch = self._build_client.GetBranch(
-                cfg.kernel_build_target, kernel_build_id)
+                self._kernel_build_target, kernel_build_id)
+        else:
+            self._kernel_build_target = None
 
     def CreateInstance(self):
         """Creates singe configured cuttlefish device.
@@ -110,7 +122,8 @@
             kernel_branch=self._kernel_branch,
             kernel_build_id=self._kernel_build_id,
             blank_data_disk_size_gb=self._blank_data_disk_size_gb,
-            avd_spec=self._avd_spec)
+            avd_spec=self._avd_spec,
+            extra_scopes=self._extra_scopes)
 
         return instance
 
@@ -120,6 +133,7 @@
                   build_target=None,
                   build_id=None,
                   kernel_build_id=None,
+                  kernel_branch=None,
                   num=1,
                   serial_log_file=None,
                   logcat_file=None,
@@ -133,6 +147,7 @@
         build_target: String, Target name.
         build_id: String, Build id, e.g. "2263051", "P2804227"
         kernel_build_id: String, Kernel build id.
+        kernel_branch: String, Kernel branch name.
         num: Integer, Number of devices to create.
         serial_log_file: String, A path to a tar file where serial output should
                          be saved to.
@@ -161,15 +176,10 @@
         build_id, num, serial_log_file, logcat_file, autoconnect,
         report_internal_ip)
     device_factory = CuttlefishDeviceFactory(cfg, build_target, build_id,
-                                             kernel_build_id, avd_spec)
-    return common_operations.CreateDevices(
-        command="create_cf",
-        cfg=cfg,
-        device_factory=device_factory,
-        num=num,
-        report_internal_ip=report_internal_ip,
-        autoconnect=autoconnect,
-        vnc_port=constants.CF_TARGET_VNC_PORT,
-        adb_port=constants.CF_TARGET_ADB_PORT,
-        serial_log_file=serial_log_file,
-        logcat_file=logcat_file)
+                                             avd_spec=avd_spec,
+                                             kernel_build_id=kernel_build_id,
+                                             kernel_branch=kernel_branch)
+    return common_operations.CreateDevices("create_cf", cfg, device_factory,
+                                           num, constants.TYPE_CF,
+                                           report_internal_ip, autoconnect,
+                                           serial_log_file, logcat_file)
diff --git a/public/actions/create_cuttlefish_action_test.py b/public/actions/create_cuttlefish_action_test.py
index 5ec2bd3..1e61412 100644
--- a/public/actions/create_cuttlefish_action_test.py
+++ b/public/actions/create_cuttlefish_action_test.py
@@ -40,11 +40,14 @@
     IMAGE = "fake-image"
     BUILD_TARGET = "fake-build-target"
     BUILD_ID = "12345"
+    KERNEL_BRANCH = "fake-kernel-branch"
     KERNEL_BUILD_ID = "54321"
+    KERNEL_BUILD_TARGET = "kernel"
     BRANCH = "fake-branch"
     STABLE_HOST_IMAGE_NAME = "fake-stable-host-image-name"
     STABLE_HOST_IMAGE_PROJECT = "fake-stable-host-image-project"
     EXTRA_DATA_DISK_GB = 4
+    EXTRA_SCOPES = ["scope1", "scope2"]
 
     def setUp(self):
         """Set up the test."""
@@ -78,6 +81,8 @@
         cfg.stable_host_image_name = self.STABLE_HOST_IMAGE_NAME
         cfg.stable_host_image_project = self.STABLE_HOST_IMAGE_PROJECT
         cfg.extra_data_disk_size_gb = self.EXTRA_DATA_DISK_GB
+        cfg.kernel_build_target = self.KERNEL_BUILD_TARGET
+        cfg.extra_scopes = self.EXTRA_SCOPES
         return cfg
 
     def testCreateDevices(self):
@@ -94,7 +99,8 @@
         self.compute_client.GenerateInstanceName.return_value = self.INSTANCE
 
         # Mock build client method
-        self.build_client.GetBranch.return_value = self.BRANCH
+        self.build_client.GetBranch.side_effect = [self.BRANCH,
+                                                   self.KERNEL_BRANCH]
 
         # Setup avd_spec as None to use cfg to create devices
         none_avd_spec = None
@@ -111,14 +117,21 @@
             build_target=self.BUILD_TARGET,
             branch=self.BRANCH,
             build_id=self.BUILD_ID,
-            kernel_branch=self.BRANCH,
+            kernel_branch=self.KERNEL_BRANCH,
             kernel_build_id=self.KERNEL_BUILD_ID,
             blank_data_disk_size_gb=self.EXTRA_DATA_DISK_GB,
-            avd_spec=none_avd_spec)
+            avd_spec=none_avd_spec,
+            extra_scopes=self.EXTRA_SCOPES)
 
         self.assertEquals(report.data, {
             "devices": [
                 {
+                    "branch": self.BRANCH,
+                    "build_id": self.BUILD_ID,
+                    "build_target": self.BUILD_TARGET,
+                    "kernel_branch": self.KERNEL_BRANCH,
+                    "kernel_build_id": self.KERNEL_BUILD_ID,
+                    "kernel_build_target": self.KERNEL_BUILD_TARGET,
                     "instance_name": self.INSTANCE,
                     "ip": self.IP.external,
                 },
diff --git a/public/actions/create_goldfish_action.py b/public/actions/create_goldfish_action.py
index 42e3f0f..c67c255 100644
--- a/public/actions/create_goldfish_action.py
+++ b/public/actions/create_goldfish_action.py
@@ -22,8 +22,8 @@
 import os
 
 from acloud import errors
-from acloud.public.actions import common_operations
 from acloud.public.actions import base_device_factory
+from acloud.public.actions import common_operations
 from acloud.internal import constants
 from acloud.internal.lib import android_build_client
 from acloud.internal.lib import auth
@@ -64,7 +64,8 @@
                  build_id,
                  emulator_build_target,
                  emulator_build_id,
-                 gpu=None):
+                 gpu=None,
+                 avd_spec=None):
 
         """Initialize.
 
@@ -75,6 +76,7 @@
             emulator_build_target: String, the emulator build target, e.g. aosp_x86-eng.
             emulator_build_id: String, emulator build id.
             gpu: String, GPU to attach to the device or None. e.g. "nvidia-tesla-k80"
+            avd_spec: An AVDSpec instance.
         """
 
         self.credentials = auth.CreateCredentials(cfg)
@@ -90,7 +92,9 @@
         self._emulator_build_id = emulator_build_id
         self._emulator_build_target = emulator_build_target
         self._gpu = gpu
+        self._avd_spec = avd_spec
         self._blank_data_disk_size_gb = cfg.extra_data_disk_size_gb
+        self._extra_scopes = cfg.extra_scopes
 
         # Configure clients
         self._build_client = android_build_client.AndroidBuildClient(
@@ -109,7 +113,8 @@
         Returns:
             String, the name of the created instance.
         """
-        instance = self._compute_client.GenerateInstanceName(self._build_id)
+        instance = self._compute_client.GenerateInstanceName(
+            build_id=self._build_id, build_target=self._build_target)
 
         self._compute_client.CreateInstance(
             instance=instance,
@@ -121,7 +126,9 @@
             emulator_branch=self._emulator_branch,
             emulator_build_id=self._emulator_build_id,
             gpu=self._gpu,
-            blank_data_disk_size_gb=self._blank_data_disk_size_gb)
+            blank_data_disk_size_gb=self._blank_data_disk_size_gb,
+            avd_spec=self._avd_spec,
+            extra_scopes=self._extra_scopes)
 
         return instance
 
@@ -180,7 +187,8 @@
         return ParseBuildInfo(temp_filename, pattern)
 
 
-def CreateDevices(cfg,
+def CreateDevices(avd_spec=None,
+                  cfg=None,
                   build_target=None,
                   build_id=None,
                   emulator_build_id=None,
@@ -194,6 +202,7 @@
     """Create one or multiple Goldfish devices.
 
     Args:
+        avd_spec: An AVDSpec instance.
         cfg: An AcloudConfig instance.
         build_target: String, the build target, e.g. aosp_x86-eng.
         build_id: String, Build id, e.g. "2263051", "P2804227"
@@ -211,7 +220,23 @@
     Returns:
         A Report instance.
     """
+    if avd_spec:
+        cfg = avd_spec.cfg
+        build_target = avd_spec.remote_image[constants.BUILD_TARGET]
+        build_id = avd_spec.remote_image[constants.BUILD_ID]
+        branch = avd_spec.remote_image[constants.BUILD_BRANCH]
+        num = avd_spec.num
+        emulator_build_id = avd_spec.emulator_build_id
+        gpu = avd_spec.gpu
+        serial_log_file = avd_spec.serial_log_file
+        logcat_file = avd_spec.logcat_file
+        autoconnect = avd_spec.autoconnect
+        report_internal_ip = avd_spec.report_internal_ip
+
     if emulator_build_id is None:
+        logger.info("emulator_build_id not provided. "
+                    "Try to get %s from build %s/%s.", _EMULATOR_INFO_FILENAME,
+                    build_id, build_target)
         emulator_build_id = _FetchBuildIdFromFile(cfg,
                                                   build_target,
                                                   build_id,
@@ -242,15 +267,9 @@
 
     device_factory = GoldfishDeviceFactory(cfg, build_target, build_id,
                                            cfg.emulator_build_target,
-                                           emulator_build_id, gpu)
+                                           emulator_build_id, gpu, avd_spec)
 
-    return common_operations.CreateDevices(
-        command="create_gf",
-        cfg=cfg,
-        device_factory=device_factory,
-        num=num,
-        report_internal_ip=report_internal_ip,
-        vnc_port=constants.DEFAULT_GOLDFISH_VNC_PORT,
-        adb_port=constants.DEFAULT_GOLDFISH_ADB_PORT,
-        serial_log_file=serial_log_file,
-        logcat_file=logcat_file)
+    return common_operations.CreateDevices("create_gf", cfg, device_factory,
+                                           num, constants.TYPE_GF,
+                                           report_internal_ip, autoconnect,
+                                           serial_log_file, logcat_file)
diff --git a/public/actions/create_goldfish_action_test.py b/public/actions/create_goldfish_action_test.py
index 3d841b1..12320f2 100644
--- a/public/actions/create_goldfish_action_test.py
+++ b/public/actions/create_goldfish_action_test.py
@@ -15,9 +15,10 @@
 # limitations under the License.
 """Tests for acloud.public.actions.create_goldfish_actions."""
 import uuid
-
 import unittest
 import mock
+
+from acloud.internal import constants
 from acloud.internal.lib import android_build_client
 from acloud.internal.lib import android_compute_client
 from acloud.internal.lib import auth
@@ -43,6 +44,7 @@
     GOLDFISH_HOST_IMAGE_NAME = "fake-stable-host-image-name"
     GOLDFISH_HOST_IMAGE_PROJECT = "fake-stable-host-image-project"
     EXTRA_DATA_DISK_GB = 4
+    EXTRA_SCOPES = None
 
     def setUp(self):
         """Sets up the test."""
@@ -62,6 +64,16 @@
             "AndroidComputeClient",
             return_value=self.compute_client)
         self.Patch(auth, "CreateCredentials", return_value=mock.MagicMock())
+        #Initialize new avd_spec
+        self.avd_spec = mock.MagicMock()
+        self.avd_spec.cfg = self._CreateCfg()
+        self.avd_spec.remote_image = {constants.BUILD_ID: self.BUILD_ID,
+                                      constants.BUILD_BRANCH: self.BRANCH,
+                                      constants.BUILD_TARGET: self.BUILD_TARGET}
+        self.avd_spec.emulator_build_id = self.EMULATOR_BUILD_ID
+        self.avd_spec.gpu = self.GPU
+        self.avd_spec.serial_log_file = None
+        self.avd_spec.autoconnect = False
 
     def _CreateCfg(self):
         """A helper method that creates a mock configuration object."""
@@ -75,6 +87,7 @@
         cfg.stable_goldfish_host_image_project = self.GOLDFISH_HOST_IMAGE_PROJECT
         cfg.emulator_build_target = self.EMULATOR_TARGET
         cfg.extra_data_disk_size_gb = self.EXTRA_DATA_DISK_GB
+        cfg.extra_scopes = self.EXTRA_SCOPES
         return cfg
 
     def testCreateDevices(self):
@@ -95,10 +108,12 @@
             self.BRANCH, self.EMULATOR_BRANCH
         ]
 
-        # Call CreateDevices
+        none_avd_spec = None
+
+        # Call CreateDevices with avd_spec is None
         report = create_goldfish_action.CreateDevices(
-            cfg, self.BUILD_TARGET, self.BUILD_ID, self.EMULATOR_BUILD_ID,
-            self.GPU)
+            none_avd_spec, cfg, self.BUILD_TARGET, self.BUILD_ID,
+            self.EMULATOR_BUILD_ID, self.GPU)
 
         # Verify
         self.compute_client.CreateInstance.assert_called_with(
@@ -111,19 +126,47 @@
             build_id=self.BUILD_ID,
             emulator_branch=self.EMULATOR_BRANCH,
             emulator_build_id=self.EMULATOR_BUILD_ID,
-            gpu=self.GPU)
+            gpu=self.GPU,
+            avd_spec=none_avd_spec,
+            extra_scopes=self.EXTRA_SCOPES)
 
         self.assertEquals(report.data, {
             "devices": [
                 {
                     "instance_name": self.INSTANCE,
                     "ip": self.IP.external,
+                    "branch": self.BRANCH,
+                    "build_id": self.BUILD_ID,
+                    "build_target": self.BUILD_TARGET,
+                    "emulator_branch": self.EMULATOR_BRANCH,
+                    "emulator_build_id": self.EMULATOR_BUILD_ID,
+                    "emulator_build_target": self.EMULATOR_TARGET,
                 },
             ],
         })
         self.assertEquals(report.command, "create_gf")
         self.assertEquals(report.status, "SUCCESS")
 
+        # Call CreateDevices with avd_spec
+        self.build_client.GetBranch.side_effect = [
+            self.BRANCH, self.EMULATOR_BRANCH
+        ]
+        report = create_goldfish_action.CreateDevices(avd_spec=self.avd_spec)
+        # Verify
+        self.compute_client.CreateInstance.assert_called_with(
+            instance=self.INSTANCE,
+            blank_data_disk_size_gb=self.EXTRA_DATA_DISK_GB,
+            image_name=self.GOLDFISH_HOST_IMAGE_NAME,
+            image_project=self.GOLDFISH_HOST_IMAGE_PROJECT,
+            build_target=self.BUILD_TARGET,
+            branch=self.BRANCH,
+            build_id=self.BUILD_ID,
+            emulator_branch=self.EMULATOR_BRANCH,
+            emulator_build_id=self.EMULATOR_BUILD_ID,
+            gpu=self.GPU,
+            avd_spec=self.avd_spec,
+            extra_scopes=self.EXTRA_SCOPES)
+
     def testCreateDevicesWithoutBuildId(self):
         """Test CreateDevices when emulator sys image build id is not provided."""
         cfg = self._CreateCfg()
@@ -148,8 +191,10 @@
             "_FetchBuildIdFromFile",
             return_value=self.BUILD_ID)
 
-        # Call CreateDevices
+        none_avd_spec = None
+        # Call CreateDevices with no avd_spec
         report = create_goldfish_action.CreateDevices(
+            none_avd_spec,
             cfg,
             self.BUILD_TARGET,
             None,
@@ -168,17 +213,45 @@
             build_id=self.BUILD_ID,
             emulator_branch=self.EMULATOR_BRANCH,
             emulator_build_id=self.EMULATOR_BUILD_ID,
-            gpu=self.GPU)
+            gpu=self.GPU,
+            avd_spec=none_avd_spec,
+            extra_scopes=self.EXTRA_SCOPES)
 
         self.assertEquals(report.data, {
             "devices": [{
                 "instance_name": self.INSTANCE,
                 "ip": self.IP.external,
+                "branch": self.BRANCH,
+                "build_id": self.BUILD_ID,
+                "build_target": self.BUILD_TARGET,
+                "emulator_branch": self.EMULATOR_BRANCH,
+                "emulator_build_id": self.EMULATOR_BUILD_ID,
+                "emulator_build_target": self.EMULATOR_TARGET,
             },],
         })
         self.assertEquals(report.command, "create_gf")
         self.assertEquals(report.status, "SUCCESS")
 
+        # Call CreateDevices with avd_spec
+        self.build_client.GetBranch.side_effect = [
+            self.BRANCH, self.EMULATOR_BRANCH
+        ]
+        report = create_goldfish_action.CreateDevices(avd_spec=self.avd_spec)
+        # Verify
+        self.compute_client.CreateInstance.assert_called_with(
+            instance=self.INSTANCE,
+            blank_data_disk_size_gb=self.EXTRA_DATA_DISK_GB,
+            image_name=self.GOLDFISH_HOST_IMAGE_NAME,
+            image_project=self.GOLDFISH_HOST_IMAGE_PROJECT,
+            build_target=self.BUILD_TARGET,
+            branch=self.BRANCH,
+            build_id=self.BUILD_ID,
+            emulator_branch=self.EMULATOR_BRANCH,
+            emulator_build_id=self.EMULATOR_BUILD_ID,
+            gpu=self.GPU,
+            avd_spec=self.avd_spec,
+            extra_scopes=self.EXTRA_SCOPES)
+
     #pylint: disable=invalid-name
     def testCreateDevicesWithoutEmulatorBuildId(self):
         """Test CreateDevices when emulator build id is not provided."""
@@ -204,9 +277,11 @@
             "_FetchBuildIdFromFile",
             return_value=self.EMULATOR_BUILD_ID)
 
+        none_avd_spec = None
         # Call CreateDevices
         report = create_goldfish_action.CreateDevices(
-            cfg, self.BUILD_TARGET, self.BUILD_ID, None, self.GPU)
+            none_avd_spec, cfg, self.BUILD_TARGET, self.BUILD_ID, None,
+            self.GPU)
 
         # Verify
         self.compute_client.CreateInstance.assert_called_with(
@@ -219,17 +294,45 @@
             build_id=self.BUILD_ID,
             emulator_branch=self.EMULATOR_BRANCH,
             emulator_build_id=self.EMULATOR_BUILD_ID,
-            gpu=self.GPU)
+            gpu=self.GPU,
+            avd_spec=none_avd_spec,
+            extra_scopes=self.EXTRA_SCOPES)
 
         self.assertEquals(report.data, {
             "devices": [{
                 "instance_name": self.INSTANCE,
                 "ip": self.IP.external,
+                "branch": self.BRANCH,
+                "build_id": self.BUILD_ID,
+                "build_target": self.BUILD_TARGET,
+                "emulator_branch": self.EMULATOR_BRANCH,
+                "emulator_build_id": self.EMULATOR_BUILD_ID,
+                "emulator_build_target": self.EMULATOR_TARGET,
             },],
         })
         self.assertEquals(report.command, "create_gf")
         self.assertEquals(report.status, "SUCCESS")
 
+        # Call CreateDevices with avd_spec
+        self.build_client.GetBranch.side_effect = [
+            self.BRANCH, self.EMULATOR_BRANCH
+        ]
+        report = create_goldfish_action.CreateDevices(avd_spec=self.avd_spec)
+        # Verify
+        self.compute_client.CreateInstance.assert_called_with(
+            instance=self.INSTANCE,
+            blank_data_disk_size_gb=self.EXTRA_DATA_DISK_GB,
+            image_name=self.GOLDFISH_HOST_IMAGE_NAME,
+            image_project=self.GOLDFISH_HOST_IMAGE_PROJECT,
+            build_target=self.BUILD_TARGET,
+            branch=self.BRANCH,
+            build_id=self.BUILD_ID,
+            emulator_branch=self.EMULATOR_BRANCH,
+            emulator_build_id=self.EMULATOR_BUILD_ID,
+            gpu=self.GPU,
+            avd_spec=self.avd_spec,
+            extra_scopes=self.EXTRA_SCOPES)
+
 
 if __name__ == "__main__":
     unittest.main()
diff --git a/public/config.py b/public/config.py
index 176a44d..888f185 100755
--- a/public/config.py
+++ b/public/config.py
@@ -94,6 +94,7 @@
         "disk_image_name", "disk_image_mime_type"
     ]
 
+    # pylint: disable=too-many-statements
     def __init__(self, usr_cfg, internal_cfg):
         """Initialize.
 
@@ -168,6 +169,9 @@
             if "cfg_sta_ephemeral_data_size_mb" in self.metadata_variable:
                 del self.metadata_variable["cfg_sta_ephemeral_data_size_mb"]
 
+        # Additional scopes to be passed to the created instance
+        self.extra_scopes = usr_cfg.extra_scopes
+
         # Fields that can be overriden by args
         self.orientation = usr_cfg.orientation
         self.resolution = usr_cfg.resolution
@@ -198,6 +202,12 @@
         self.common_hw_property_map = internal_cfg.common_hw_property_map
         self.hw_property = usr_cfg.hw_property
 
+        self.launch_args = usr_cfg.launch_args
+        self.instance_name_pattern = (
+            usr_cfg.instance_name_pattern or
+            internal_cfg.default_usr_cfg.instance_name_pattern)
+
+
         # Verify validity of configurations.
         self.Verify()
 
diff --git a/public/config_test.py b/public/config_test.py
index 15e612f..272f6fc 100644
--- a/public/config_test.py
+++ b/public/config_test.py
@@ -49,6 +49,8 @@
     value: "metadata_value_1"
 }
 hw_property: "cpu:3,resolution:1080x1920,dpi:480,memory:4g,disk:10g"
+extra_scopes: "scope1"
+extra_scopes: "scope2"
 """
 
     INTERNAL_CONFIG = """
@@ -70,6 +72,7 @@
     stable_host_image_project: "fake_stable_host_image_project"
     stable_goldfish_host_image_name: "fake_stable_goldfish_host_image_name"
     stable_goldfish_host_image_project: "fake_stable_goldfish_host_image_project"
+    instance_name_pattern: "fake_instance_name_pattern"
     stable_cheeps_host_image_name: "fake_stable_cheeps_host_image_name"
     stable_cheeps_host_image_project: "fake_stable_cheeps_host_image_project"
     metadata_variable {
@@ -135,6 +138,7 @@
         self.assertEqual(cfg.hw_property,
                          "cpu:3,resolution:1080x1920,dpi:480,memory:4g,"
                          "disk:10g")
+        self.assertEqual(cfg.extra_scopes, ["scope1", "scope2"])
 
     # pylint: disable=protected-access
     @mock.patch("os.makedirs")
@@ -231,6 +235,8 @@
         self.assertEqual(cfg.default_usr_cfg.stable_goldfish_host_image_project,
                          "fake_stable_goldfish_host_image_project")
         self.assertEqual(cfg.emulator_build_target, "sdk_tools_linux")
+        self.assertEqual(cfg.default_usr_cfg.instance_name_pattern,
+                         "fake_instance_name_pattern")
 
         # Cheeps related
         self.assertEqual(cfg.default_usr_cfg.stable_cheeps_host_image_name,
diff --git a/public/data/default.config b/public/data/default.config
index bf3e1a7..44ed3ad 100644
--- a/public/data/default.config
+++ b/public/data/default.config
@@ -8,10 +8,16 @@
 creds_cache_file: ".acloud_oauth2.dat"
 user_agent: "acloud"
 
+# [GOLDFISH only] The emulator build target: "sdk_tools_linux".
+# We use it to get build id if build id is not provided and It's very unlikely
+# that this will ever change.
+emulator_build_target: "sdk_tools_linux"
+
 default_usr_cfg {
   machine_type: "n1-standard-4"
   network: "default"
   extra_data_disk_size_gb: 0
+  instance_name_pattern: "ins-{uuid}-{build_id}-{build_target}"
 
   metadata_variable {
     key: "camera_front"
@@ -69,7 +75,7 @@
 
 common_hw_property_map {
   key: "tv"
-  value: "cpu:2,resolution:1080x1920,dpi:320,memory:4g,disk:4g"
+  value: "cpu:2,resolution:1280x720,dpi:213,memory:2g,disk:4g"
 }
 
 # Device resolution
diff --git a/public/device_driver.py b/public/device_driver.py
index 3fabc0c..a678c02 100755
--- a/public/device_driver.py
+++ b/public/device_driver.py
@@ -167,7 +167,8 @@
                       cleanup=True,
                       extra_data_disk_size_gb=None,
                       precreated_data_image=None,
-                      avd_spec=None):
+                      avd_spec=None,
+                      extra_scopes=None):
         """Creates |num| devices for given build_target and build_id.
 
         - If gce_image is provided, will use it to create an instance.
@@ -192,6 +193,7 @@
             extra_data_disk_size_gb: Integer, size of extra disk, or None.
             precreated_data_image: A string, the image to use for the extra disk.
             avd_spec: AVDSpec object for pass hw_property.
+            extra_scopes: A list of extra scopes given to the new instance.
 
         Raises:
             errors.DriverError: If no source is specified for image creation.
@@ -227,7 +229,8 @@
                     instance=instance,
                     image_name=image_name,
                     extra_disk_name=extra_disk_name,
-                    avd_spec=avd_spec)
+                    avd_spec=avd_spec,
+                    extra_scopes=extra_scopes)
                 ip = self._compute_client.GetInstanceIP(instance)
                 self.devices.append(avd.AndroidVirtualDevice(
                     ip=ip, instance_name=instance))
@@ -387,7 +390,8 @@
             extra_data_disk_size_gb=cfg.extra_data_disk_size_gb,
             precreated_data_image=cfg.precreated_data_image_map.get(
                 cfg.extra_data_disk_size_gb),
-            avd_spec=avd_spec)
+            avd_spec=avd_spec,
+            extra_scopes=cfg.extra_scopes)
         failures = device_pool.WaitForBoot()
         # Write result to report.
         for device in device_pool.devices:
@@ -401,8 +405,8 @@
                 forwarded_ports = utils.AutoConnect(
                     ip,
                     cfg.ssh_private_key_path,
-                    constants.DEFAULT_GCE_VNC_PORT,
-                    constants.DEFAULT_GCE_ADB_PORT,
+                    constants.GCE_VNC_PORT,
+                    constants.GCE_ADB_PORT,
                     _SSH_USER)
                 device_dict[constants.VNC_PORT] = forwarded_ports.vnc_port
                 device_dict[constants.ADB_PORT] = forwarded_ports.adb_port
@@ -575,30 +579,6 @@
     return r
 
 
-def AddSshRsa(cfg, user, ssh_rsa_path):
-    """Add public ssh rsa key to the project.
-
-    Args:
-        cfg: An AcloudConfig instance.
-        user: the name of the user which the key belongs to.
-        ssh_rsa_path: The absolute path to public rsa key.
-
-    Returns:
-        A Report instance.
-    """
-    r = report.Report(command="sshkey")
-    try:
-        credentials = auth.CreateCredentials(cfg)
-        compute_client = android_compute_client.AndroidComputeClient(
-            cfg, credentials)
-        compute_client.AddSshRsa(user, ssh_rsa_path)
-        r.SetStatus(report.Status.SUCCESS)
-    except errors.DriverError as e:
-        r.AddError(str(e))
-        r.SetStatus(report.Status.FAIL)
-    return r
-
-
 def CheckAccess(cfg):
     """Check if user has access.
 
diff --git a/public/device_driver_test.py b/public/device_driver_test.py
index 9d4f8cb..60b0fb1 100644
--- a/public/device_driver_test.py
+++ b/public/device_driver_test.py
@@ -48,6 +48,7 @@
         4: "extradisk-image-4gb",
         10: "extradisk-image-10gb"
     }
+    cfg.extra_scopes = None
     cfg.ssh_private_key_path = ""
     cfg.ssh_public_key_path = ""
 
@@ -109,7 +110,8 @@
             instance=fake_instance,
             image_name=fake_image,
             extra_disk_name=disk_name,
-            avd_spec=None)
+            avd_spec=None,
+            extra_scopes=None)
         self.compute_client.DeleteImage.assert_called_with(fake_image)
         self.storage_client.Delete(cfg.storage_bucket_name, fake_gs_object)
 
diff --git a/public/report.py b/public/report.py
index 2ca11da..061cfe6 100755
--- a/public/report.py
+++ b/public/report.py
@@ -159,12 +159,12 @@
             status=self.status,
             errors=self.errors,
             data=self.data)
-        logger.info("Report: %s", json.dumps(result, indent=2))
+        logger.info("Report: %s", json.dumps(result, indent=2, sort_keys=True))
         if not report_file:
             return
         try:
             with open(report_file, "w") as f:
-                json.dump(result, f, indent=2)
+                json.dump(result, f, indent=2, sort_keys=True)
             logger.info("Report file generated at %s",
                         os.path.abspath(report_file))
         except OSError as e:
diff --git a/reconnect/reconnect.py b/reconnect/reconnect.py
index 413b146..b4dc027 100644
--- a/reconnect/reconnect.py
+++ b/reconnect/reconnect.py
@@ -21,12 +21,12 @@
 
 from __future__ import print_function
 
-from collections import namedtuple
 import getpass
 import re
 
 from acloud.delete import delete
-from acloud.internal import constants
+from acloud.internal.lib import auth
+from acloud.internal.lib import android_compute_client
 from acloud.internal.lib import utils
 from acloud.internal.lib.adb_tools import AdbTools
 from acloud.list import list as list_instance
@@ -34,15 +34,6 @@
 
 _RE_DISPLAY = re.compile(r"([\d]+)x([\d]+)\s.*")
 _VNC_STARTED_PATTERN = "ssvnc vnc://127.0.0.1:%(vnc_port)d"
-# TODO(b/122929848): merge all definition of ForwardedPorts into one spot.
-ForwardedPorts = namedtuple("ForwardedPorts",
-                            [constants.VNC_PORT, constants.ADB_PORT])
-_AVD_PORT_CLASS_DICT = {
-    constants.TYPE_GCE: ForwardedPorts(constants.DEFAULT_GCE_VNC_PORT,
-                                       constants.DEFAULT_GCE_ADB_PORT),
-    constants.TYPE_CF: ForwardedPorts(constants.CF_TARGET_VNC_PORT,
-                                      constants.CF_TARGET_ADB_PORT)
-}
 
 
 def StartVnc(vnc_port, display):
@@ -68,6 +59,26 @@
             utils.LaunchVncClient(vnc_port)
 
 
+def AddPublicSshRsaToInstance(cfg, user, instance_name):
+    """Add the public rsa key to the instance's metadata.
+
+    When the public key doesn't exist in the metadata, it will add it.
+
+    Args:
+        cfg: An AcloudConfig instance.
+        user: String, the ssh username to access instance.
+        instance_name: String, instance name.
+    """
+    credentials = auth.CreateCredentials(cfg)
+    compute_client = android_compute_client.AndroidComputeClient(
+        cfg, credentials)
+    compute_client.AddSshRsaInstanceMetadata(
+        cfg.zone,
+        user,
+        cfg.ssh_public_key_path,
+        instance_name)
+
+
 def ReconnectInstance(ssh_private_key_path, instance):
     """Reconnect adb/vnc/ssh to the specified instance.
 
@@ -88,8 +99,8 @@
         forwarded_ports = utils.AutoConnect(
             instance.ip,
             ssh_private_key_path,
-            _AVD_PORT_CLASS_DICT.get(instance.avd_type).vnc_port,
-            _AVD_PORT_CLASS_DICT.get(instance.avd_type).adb_port,
+            utils.AVD_PORT_DICT[instance.avd_type].vnc_port,
+            utils.AVD_PORT_DICT[instance.avd_type].adb_port,
             getpass.getuser())
         vnc_port = forwarded_ports.vnc_port
 
@@ -112,4 +123,5 @@
     if not instances_to_reconnect:
         instances_to_reconnect = list_instance.ChooseInstances(cfg, args.all)
     for instance in instances_to_reconnect:
+        AddPublicSshRsaToInstance(cfg, getpass.getuser(), instance.name)
         ReconnectInstance(cfg.ssh_private_key_path, instance)
diff --git a/reconnect/reconnect_test.py b/reconnect/reconnect_test.py
index b00c131..82e4b6c 100644
--- a/reconnect/reconnect_test.py
+++ b/reconnect/reconnect_test.py
@@ -74,8 +74,8 @@
         reconnect.ReconnectInstance(ssh_private_key_path, instance_object)
         utils.AutoConnect.assert_called_with(instance_object.ip,
                                              ssh_private_key_path,
-                                             constants.CF_TARGET_VNC_PORT,
-                                             constants.CF_TARGET_ADB_PORT,
+                                             constants.CF_VNC_PORT,
+                                             constants.CF_ADB_PORT,
                                              "fake_user")
         utils.LaunchVncClient.assert_called_with(11111)
 
@@ -84,8 +84,8 @@
         reconnect.ReconnectInstance(ssh_private_key_path, instance_object)
         utils.AutoConnect.assert_called_with(instance_object.ip,
                                              ssh_private_key_path,
-                                             constants.CF_TARGET_VNC_PORT,
-                                             constants.CF_TARGET_ADB_PORT,
+                                             constants.CF_VNC_PORT,
+                                             constants.CF_ADB_PORT,
                                              "fake_user")
         utils.LaunchVncClient.assert_called_with(11111, "999", "777")
 
@@ -117,8 +117,8 @@
         reconnect.ReconnectInstance(ssh_private_key_path, instance_object)
         utils.AutoConnect.assert_called_with(instance_object.ip,
                                              ssh_private_key_path,
-                                             constants.DEFAULT_GCE_VNC_PORT,
-                                             constants.DEFAULT_GCE_ADB_PORT,
+                                             constants.GCE_VNC_PORT,
+                                             constants.GCE_ADB_PORT,
                                              "fake_user")
 
         #test reconnect remote instance when avd_type as cuttlefish.
@@ -126,8 +126,8 @@
         reconnect.ReconnectInstance(ssh_private_key_path, instance_object)
         utils.AutoConnect.assert_called_with(instance_object.ip,
                                              ssh_private_key_path,
-                                             constants.CF_TARGET_VNC_PORT,
-                                             constants.CF_TARGET_ADB_PORT,
+                                             constants.CF_VNC_PORT,
+                                             constants.CF_ADB_PORT,
                                              "fake_user")
 
     def testStartVnc(self):
diff --git a/setup/gcp_setup_runner.py b/setup/gcp_setup_runner.py
index fa0f542..0a1b281 100644
--- a/setup/gcp_setup_runner.py
+++ b/setup/gcp_setup_runner.py
@@ -192,17 +192,21 @@
         Args:
             config_path: String, acloud config path.
         """
+        # pylint: disable=invalid-name
         config_mgr = config.AcloudConfigManager(config_path)
         cfg = config_mgr.Load()
         self.config_path = config_mgr.user_config_path
-        self.client_id = cfg.client_id
-        self.client_secret = cfg.client_secret
         self.project = cfg.project
         self.zone = cfg.zone
         self.storage_bucket_name = cfg.storage_bucket_name
         self.ssh_private_key_path = cfg.ssh_private_key_path
         self.ssh_public_key_path = cfg.ssh_public_key_path
         self.stable_host_image_name = cfg.stable_host_image_name
+        self.client_id = cfg.client_id
+        self.client_secret = cfg.client_secret
+        self.service_account_name = cfg.service_account_name
+        self.service_account_private_key_path = cfg.service_account_private_key_path
+        self.service_account_json_private_key_path = cfg.service_account_json_private_key_path
 
     def ShouldRun(self):
         """Check if we actually need to run GCP setup.
@@ -212,9 +216,18 @@
         Returns:
             True if reqired config fields are empty, False otherwise.
         """
-        return (not self.client_id
-                or not self.client_secret
-                or not self.project)
+        # We need to ensure the config has the proper auth-related fields set,
+        # so config requires just 1 of the following:
+        # 1. client id/secret
+        # 2. service account name/private key path
+        # 3. service account json private key path
+        if ((not self.client_id or not self.client_secret)
+                and (not self.service_account_name or not self.service_account_private_key_path)
+                and not self.service_account_json_private_key_path):
+            return True
+
+        # If a project isn't set, then we need to run setup.
+        return not self.project
 
     def _Run(self):
         """Run GCP setup task."""
diff --git a/setup/setup.py b/setup/setup.py
index 8b5eadb..ad498f4 100644
--- a/setup/setup.py
+++ b/setup/setup.py
@@ -95,7 +95,7 @@
     if constants.ENV_ANDROID_BUILD_TOP not in os.environ:
         print("Can't find $%s." % constants.ENV_ANDROID_BUILD_TOP)
         print("Please run '#source build/envsetup.sh && lunch <target>' first.")
-        sys.exit(1)
+        sys.exit(constants.EXIT_BY_USER)
 
     pre_setup_sh = os.path.join(os.environ.get(constants.ENV_ANDROID_BUILD_TOP),
                                 "tools",