Merge "Add mixed-build args (system build info) in acloud create."
diff --git a/create/avd_spec.py b/create/avd_spec.py
index bda90f2..b5b23c1 100644
--- a/create/avd_spec.py
+++ b/create/avd_spec.py
@@ -40,9 +40,6 @@
 
 # Default values for build target.
 _BRANCH_RE = re.compile(r"^Manifest branch: (?P<branch>.+)")
-_BUILD_TARGET = "build_target"
-_BUILD_BRANCH = "build_branch"
-_BUILD_ID = "build_id"
 _COMMAND_REPO_INFO = ["repo", "info"]
 _CF_ZIP_PATTERN = "*img*.zip"
 _DEFAULT_BUILD_BITNESS = "x86"
@@ -113,6 +110,7 @@
         self._image_download_dir = None
         self._num_of_instances = None
         self._remote_image = None
+        self._system_build_info = None
         self._hw_property = None
         # Create config instance for android_build_client to query build api.
         self._cfg = config.GetAcloudConfig(args)
@@ -408,34 +406,40 @@
             args: Namespace object from argparse.parse_args.
         """
         self._remote_image = {}
-        self._remote_image[_BUILD_BRANCH] = args.branch
-        if not self._remote_image[_BUILD_BRANCH]:
-            self._remote_image[_BUILD_BRANCH] = self._GetBuildBranch(
+        self._remote_image[constants.BUILD_BRANCH] = args.branch
+        if not self._remote_image[constants.BUILD_BRANCH]:
+            self._remote_image[constants.BUILD_BRANCH] = self._GetBuildBranch(
                 args.build_id, args.build_target)
 
-        self._remote_image[_BUILD_TARGET] = args.build_target
-        if not self._remote_image[_BUILD_TARGET]:
-            self._remote_image[_BUILD_TARGET] = self._GetBuildTarget(args)
+        self._remote_image[constants.BUILD_TARGET] = args.build_target
+        if not self._remote_image[constants.BUILD_TARGET]:
+            self._remote_image[constants.BUILD_TARGET] = self._GetBuildTarget(args)
         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._GetFlavorFromString(
-                self._remote_image[_BUILD_TARGET]) or constants.FLAVOR_PHONE
+                self._remote_image[constants.BUILD_TARGET]) or constants.FLAVOR_PHONE
             # infer avd_type from build_target.
             for avd_type, avd_type_abbr in constants.AVD_TYPES_MAPPING.items():
                 if re.match(r"(.*_)?%s_" % avd_type_abbr,
-                            self._remote_image[_BUILD_TARGET]):
+                            self._remote_image[constants.BUILD_TARGET]):
                     self._avd_type = avd_type
                     break
 
-        self._remote_image[_BUILD_ID] = args.build_id
-        if not self._remote_image[_BUILD_ID]:
+        self._remote_image[constants.BUILD_ID] = args.build_id
+        if not self._remote_image[constants.BUILD_ID]:
             build_client = android_build_client.AndroidBuildClient(
                 auth.CreateCredentials(self._cfg))
+
             self._remote_image[constants.BUILD_ID] = build_client.GetLKGB(
                 self._remote_image[constants.BUILD_TARGET],
                 self._remote_image[constants.BUILD_BRANCH])
 
+        # Process system image
+        self._system_build_info = {constants.BUILD_ID: args.system_build_id,
+                                   constants.BUILD_BRANCH: args.system_branch,
+                                   constants.BUILD_TARGET: args.system_build_target}
+
     @staticmethod
     def _GetGitRemote():
         """Get the remote repo.
@@ -514,7 +518,7 @@
         Returns:
             build_target: String, name of build target.
         """
-        branch = re.split("-|_", self._remote_image[_BUILD_BRANCH])[0]
+        branch = re.split("-|_", self._remote_image[constants.BUILD_BRANCH])[0]
         return "%s%s_%s_%s-%s" % (
             _BRANCH_TARGET_PREFIX.get(branch, ""),
             constants.AVD_TYPES_MAPPING[args.avd_type],
@@ -635,3 +639,8 @@
     def boot_timeout_secs(self):
         """Return boot_timeout_secs."""
         return self._boot_timeout_secs
+
+    @property
+    def system_build_info(self):
+        """Return system_build_info."""
+        return self._system_build_info
diff --git a/create/avd_spec_test.py b/create/avd_spec_test.py
index eda8536..4621d53 100644
--- a/create/avd_spec_test.py
+++ b/create/avd_spec_test.py
@@ -147,21 +147,21 @@
     # pylint: disable=protected-access
     def testGetBuildTarget(self):
         """Test get build target name."""
-        self.AvdSpec._remote_image[avd_spec._BUILD_BRANCH] = "git_branch"
+        self.AvdSpec._remote_image[constants.BUILD_BRANCH] = "git_branch"
         self.AvdSpec._flavor = constants.FLAVOR_IOT
         self.args.avd_type = constants.TYPE_GCE
         self.assertEqual(
             self.AvdSpec._GetBuildTarget(self.args),
             "gce_x86_iot-userdebug")
 
-        self.AvdSpec._remote_image[avd_spec._BUILD_BRANCH] = "aosp-master"
+        self.AvdSpec._remote_image[constants.BUILD_BRANCH] = "aosp-master"
         self.AvdSpec._flavor = constants.FLAVOR_PHONE
         self.args.avd_type = constants.TYPE_CF
         self.assertEqual(
             self.AvdSpec._GetBuildTarget(self.args),
             "aosp_cf_x86_phone-userdebug")
 
-        self.AvdSpec._remote_image[avd_spec._BUILD_BRANCH] = "git_branch"
+        self.AvdSpec._remote_image[constants.BUILD_BRANCH] = "git_branch"
         self.AvdSpec._flavor = constants.FLAVOR_PHONE
         self.args.avd_type = constants.TYPE_CF
         self.assertEqual(
diff --git a/create/create_args.py b/create/create_args.py
index 4e8e9f0..c7a2f48 100644
--- a/create/create_args.py
+++ b/create/create_args.py
@@ -98,6 +98,28 @@
         type=int,
         required=False,
         help="The maximum time in seconds used to wait for the AVD to boot.")
+    parser.add_argument(
+        "--system-branch",
+        type=str,
+        dest="system_branch",
+        help="'cuttlefish only' Branch to consume the system image (system.img) "
+        "from, will default to what is defined by --branch. "
+        "That feature allows to (automatically) test various combinations "
+        "of vendor.img (CF, e.g.) and system images (GSI, e.g.). ",
+        required=False)
+    parser.add_argument(
+        "--system-build-id",
+        type=str,
+        dest="system_build_id",
+        help="'cuttlefish only' System image build id, e.g. 2145099, P2804227",
+        required=False)
+    parser.add_argument(
+        "--system-build-target",
+        type=str,
+        dest="system_build_target",
+        help="'cuttlefish only' System image build target, specify if different "
+        "from --build_target",
+        required=False)
 
     # TODO(b/118439885): Old arg formats to support transition, delete when
     # transistion is done.
@@ -125,6 +147,24 @@
         dest="build_target",
         required=False,
         help=argparse.SUPPRESS)
+    parser.add_argument(
+        "--system_branch",
+        type=str,
+        dest="system_branch",
+        required=False,
+        help=argparse.SUPPRESS)
+    parser.add_argument(
+        "--system_build_id",
+        type=str,
+        dest="system_build_id",
+        required=False,
+        help=argparse.SUPPRESS)
+    parser.add_argument(
+        "--system_build_target",
+        type=str,
+        dest="system_build_target",
+        required=False,
+        help=argparse.SUPPRESS)
 
 
 def GetCreateArgParser(subparser):
@@ -279,6 +319,9 @@
         errors.CheckPathError: Zipped image path doesn't exist.
         errors.UnsupportedFlavor: Flavor doesn't support.
         errors.UnsupportedMultiAdbPort: multi adb port doesn't support.
+        errors.UnsupportedCreateArgs: When a create arg is specified but
+                                      unsupported for a particular avd type.
+                                      (e.g. --system-build-id for gf)
     """
     # Verify that user specified flavor name is in support list.
     # We don't use argparse's builtin validation because we need to be able to
@@ -287,6 +330,11 @@
         raise errors.UnsupportedFlavor(
             "Flavor[%s] isn't in support list: %s" % (args.flavor,
                                                       constants.ALL_FLAVORS))
+    if args.avd_type != constants.TYPE_CF:
+        if args.system_branch or args.system_build_id or args.system_build_target:
+            raise errors.UnsupportedCreateArgs(
+                "--system-* args are not supported for AVD type: %s"
+                % args.avd_type)
 
     if args.num > 1 and args.adb_port:
         raise errors.UnsupportedMultiAdbPort(
diff --git a/errors.py b/errors.py
index d145ce7..f2c027b 100644
--- a/errors.py
+++ b/errors.py
@@ -151,6 +151,10 @@
     """Unsupported create action for multi AVDs and specify adb port."""
 
 
+class UnsupportedCreateArgs(CreateError):
+    """Unsupported create arg for a specified AVD type."""
+
+
 class GetBuildIDError(CreateError):
     """Can't get build id from Android Build."""
 
diff --git a/public/acloud_main.py b/public/acloud_main.py
index abe15bd..5abafaf 100644
--- a/public/acloud_main.py
+++ b/public/acloud_main.py
@@ -173,28 +173,6 @@
         dest="kernel_build_target",
         default="kernel",
         help="Kernel build target, specify if different from 'kernel'")
-    create_cf_parser.add_argument(
-        "--system_branch",
-        type=str,
-        dest="system_branch",
-        help="Branch to consume the system image (system.img) from, will "
-        "default to what is defined by --branch. "
-        "That feature allows to (automatically) test various combinations "
-        "of vendor.img (CF, e.g.) and system images (GSI, e.g.). ",
-        required=False)
-    create_cf_parser.add_argument(
-        "--system_build_id",
-        type=str,
-        dest="system_build_id",
-        help="System image build id, e.g. 2145099, P2804227",
-        required=False)
-    create_cf_parser.add_argument(
-        "--system_build_target",
-        type=str,
-        dest="system_build_target",
-        help="System image build target, specify if different from "
-        "--build_target",
-        required=False)
 
     create_args.AddCommonCreateArgs(create_cf_parser)
     subparser_list.append(create_cf_parser)
@@ -315,6 +293,9 @@
 
     Raises:
         errors.CommandArgError: If args are invalid.
+        errors.UnsupportedCreateArgs: When a create arg is specified but
+                                      unsupported for a particular avd type.
+                                      (e.g. --system-build-id for gf)
     """
     if parsed_args.which == create_args.CMD_CREATE:
         create_args.VerifyArgs(parsed_args)
@@ -330,6 +311,12 @@
                 "--emulator_branch or --emulator_build_id")
         if not parsed_args.build_target:
             raise errors.CommandArgError("Must specify --build_target")
+        if (parsed_args.system_branch
+                or parsed_args.system_build_id
+                or parsed_args.system_build_target):
+            raise errors.UnsupportedCreateArgs(
+                "--system-* args are not supported for AVD type: %s"
+                % constants.TYPE_GF)
 
     if parsed_args.which in [
             create_args.CMD_CREATE, CMD_CREATE_CUTTLEFISH, CMD_CREATE_GOLDFISH
diff --git a/public/actions/create_cuttlefish_action.py b/public/actions/create_cuttlefish_action.py
index 69056bb..e67c4b0 100644
--- a/public/actions/create_cuttlefish_action.py
+++ b/public/actions/create_cuttlefish_action.py
@@ -213,6 +213,10 @@
         logcat_file = avd_spec.logcat_file
         client_adb_port = avd_spec.client_adb_port
         boot_timeout_secs = avd_spec.boot_timeout_secs
+        system_branch = avd_spec.system_build_info[constants.BUILD_BRANCH]
+        system_build_id = avd_spec.system_build_info[constants.BUILD_ID]
+        system_build_target = avd_spec.system_build_info[constants.BUILD_TARGET]
+
     logger.info(
         "Creating a cuttlefish device in project %s, "
         "build_target: %s, "