Merge tag 'android-11.0.0_r48' into int/11/fp3

Android 11.0.0 Release 48 (RD2A.211001.002)

* tag 'android-11.0.0_r48':
  Deprecate acloud create_cf.

Change-Id: Ieb90c0504913e9f695f61dcc38899b6efeb0b11c
diff --git a/device_build_interfaces/com/android/tradefed/device/INativeDevice.java b/device_build_interfaces/com/android/tradefed/device/INativeDevice.java
index b3fdbbb..34cd2a6 100644
--- a/device_build_interfaces/com/android/tradefed/device/INativeDevice.java
+++ b/device_build_interfaces/com/android/tradefed/device/INativeDevice.java
@@ -29,6 +29,7 @@
 import com.android.tradefed.targetprep.TargetSetupError;
 import com.android.tradefed.util.Bugreport;
 import com.android.tradefed.util.CommandResult;
+import com.android.tradefed.util.MultiMap;
 import com.android.tradefed.util.ProcessInfo;
 import com.android.tradefed.util.TimeUtil;
 
@@ -1407,10 +1408,11 @@
      * the invocation flow.
      *
      * @param info The {@link IBuildInfo} of the device.
+     * @param attributes The attributes stored in the invocation context
      * @throws TargetSetupError
      * @throws DeviceNotAvailableException
      */
-    public default void preInvocationSetup(IBuildInfo info)
+    public default void preInvocationSetup(IBuildInfo info, MultiMap<String, String> attributes)
             throws TargetSetupError, DeviceNotAvailableException {
         // Empty default implementation.
     }
diff --git a/device_build_interfaces/com/android/tradefed/device/TestDeviceOptions.java b/device_build_interfaces/com/android/tradefed/device/TestDeviceOptions.java
index f0ba7b3..3e5821f 100644
--- a/device_build_interfaces/com/android/tradefed/device/TestDeviceOptions.java
+++ b/device_build_interfaces/com/android/tradefed/device/TestDeviceOptions.java
@@ -260,6 +260,15 @@
     )
     private String mCrosPassword = null;
 
+    @Option(
+            name = "invocation-attribute-to-metadata",
+            description =
+                    "Pass the named attribute found in invocation context to GCE driver (acloud)"
+                            + " as metadata to be associated with the GCE VM. e.g. if invocation"
+                            + " context has form_factor=phone, it'll be added to GCE VM as metadata"
+                            + " form_factor=phone.")
+    private List<String> mInvocationAttributeToMetadata = new ArrayList<>();
+
     // END ====================== Options Related to Virtual Devices ======================
 
     // Option related to Remote Device only
@@ -687,4 +696,8 @@
         }
         return Collections.emptyList();
     }
+
+    public List<String> getInvocationAttributeToMetadata() {
+        return mInvocationAttributeToMetadata;
+    }
 }
diff --git a/src/com/android/tradefed/device/LocalAndroidVirtualDevice.java b/src/com/android/tradefed/device/LocalAndroidVirtualDevice.java
index ae20920..327a05c 100644
--- a/src/com/android/tradefed/device/LocalAndroidVirtualDevice.java
+++ b/src/com/android/tradefed/device/LocalAndroidVirtualDevice.java
@@ -32,6 +32,7 @@
 import com.android.tradefed.util.CommandStatus;
 import com.android.tradefed.util.FileUtil;
 import com.android.tradefed.util.IRunUtil;
+import com.android.tradefed.util.MultiMap;
 import com.android.tradefed.util.RunUtil;
 import com.android.tradefed.util.TarUtil;
 import com.android.tradefed.util.ZipUtil;
@@ -77,10 +78,11 @@
 
     /** Execute common setup procedure and launch the virtual device. */
     @Override
-    public void preInvocationSetup(IBuildInfo info)
+    public synchronized void preInvocationSetup(
+            IBuildInfo info, MultiMap<String, String> attributes)
             throws TargetSetupError, DeviceNotAvailableException {
         // The setup method in super class does not require the device to be online.
-        super.preInvocationSetup(info);
+        super.preInvocationSetup(info, attributes);
 
         createTempDirs(info);
 
diff --git a/src/com/android/tradefed/device/NativeDevice.java b/src/com/android/tradefed/device/NativeDevice.java
index 44fea46..db7d76a 100644
--- a/src/com/android/tradefed/device/NativeDevice.java
+++ b/src/com/android/tradefed/device/NativeDevice.java
@@ -58,6 +58,7 @@
 import com.android.tradefed.util.FileUtil;
 import com.android.tradefed.util.IRunUtil;
 import com.android.tradefed.util.KeyguardControllerState;
+import com.android.tradefed.util.MultiMap;
 import com.android.tradefed.util.ProcessInfo;
 import com.android.tradefed.util.QuotationAwareTokenizer;
 import com.android.tradefed.util.RunUtil;
@@ -4535,7 +4536,7 @@
 
     /** {@inheritDoc} */
     @Override
-    public void preInvocationSetup(IBuildInfo info)
+    public void preInvocationSetup(IBuildInfo info, MultiMap<String, String> attributes)
             throws TargetSetupError, DeviceNotAvailableException {
         // Default implementation
         mContentProvider = null;
diff --git a/src/com/android/tradefed/device/cloud/GceManager.java b/src/com/android/tradefed/device/cloud/GceManager.java
index 58fd31d..c16fee6 100644
--- a/src/com/android/tradefed/device/cloud/GceManager.java
+++ b/src/com/android/tradefed/device/cloud/GceManager.java
@@ -33,6 +33,7 @@
 import com.android.tradefed.util.FileUtil;
 import com.android.tradefed.util.GoogleApiClientUtil;
 import com.android.tradefed.util.IRunUtil;
+import com.android.tradefed.util.MultiMap;
 import com.android.tradefed.util.RunUtil;
 
 import com.google.api.client.auth.oauth2.Credential;
@@ -141,16 +142,21 @@
     }
 
     public GceAvdInfo startGce() throws TargetSetupError {
-        return startGce(null);
+        return startGce(null, null);
     }
 
     /**
      * Attempt to start a gce instance
      *
+     * @param ipDevice the initial IP of the GCE instance to run AVD in, <code>null</code> if not
+     *     applicable
+     * @param attributes attributes associated with current invocation, used for passing applicable
+     *     information down to the GCE instance to be added as VM metadata
      * @return a {@link GceAvdInfo} describing the GCE instance. Could be a BOOT_FAIL instance.
      * @throws TargetSetupError
      */
-    public GceAvdInfo startGce(String ipDevice) throws TargetSetupError {
+    public GceAvdInfo startGce(String ipDevice, MultiMap<String, String> attributes)
+            throws TargetSetupError {
         mGceAvdInfo = null;
         // For debugging purposes bypass.
         if (mGceHost != null && mGceInstanceName != null) {
@@ -165,7 +171,7 @@
         File reportFile = null;
         try {
             reportFile = FileUtil.createTempFile("gce_avd_driver", ".json");
-            List<String> gceArgs = buildGceCmd(reportFile, mBuildInfo, ipDevice);
+            List<String> gceArgs = buildGceCmd(reportFile, mBuildInfo, ipDevice, attributes);
 
             long driverTimeoutMs = getTestDeviceOptions().getGceCmdTimeout();
             if (!getTestDeviceOptions().allowGceCmdTimeoutOverride()) {
@@ -250,7 +256,8 @@
     }
 
     /** Build and return the command to launch GCE. Exposed for testing. */
-    protected List<String> buildGceCmd(File reportFile, IBuildInfo b, String ipDevice) {
+    protected List<String> buildGceCmd(
+            File reportFile, IBuildInfo b, String ipDevice, MultiMap<String, String> attributes) {
         File avdDriverFile = getTestDeviceOptions().getAvdDriverBinary();
         if (!avdDriverFile.exists()) {
             throw new RuntimeException(
@@ -298,6 +305,18 @@
             gceArgs.add("--build_id");
             gceArgs.add(b.getBuildId());
         }
+
+        // process any info in the invocation context that should be passed onto GCE driver
+        // as meta data to be associated with the VM instance
+        if (attributes != null) {
+            for (String key : getTestDeviceOptions().getInvocationAttributeToMetadata()) {
+                for (String value : attributes.get(key)) {
+                    gceArgs.add("--gce-metadata");
+                    gceArgs.add(String.format("%s:%s", key, value));
+                }
+            }
+        }
+
         // Add additional args passed by gce-driver-param.
         gceArgs.addAll(gceDriverParams);
         // Get extra params by instance type
diff --git a/src/com/android/tradefed/device/cloud/ManagedRemoteDevice.java b/src/com/android/tradefed/device/cloud/ManagedRemoteDevice.java
index d8b04d6..a0408a6 100644
--- a/src/com/android/tradefed/device/cloud/ManagedRemoteDevice.java
+++ b/src/com/android/tradefed/device/cloud/ManagedRemoteDevice.java
@@ -39,6 +39,7 @@
 import com.android.tradefed.result.error.DeviceErrorIdentifier;
 import com.android.tradefed.targetprep.TargetSetupError;
 import com.android.tradefed.util.FileUtil;
+import com.android.tradefed.util.MultiMap;
 import com.android.tradefed.util.StreamUtil;
 
 import com.google.common.annotations.VisibleForTesting;
@@ -72,9 +73,9 @@
     }
 
     @Override
-    public void preInvocationSetup(IBuildInfo info)
+    public void preInvocationSetup(IBuildInfo info, MultiMap<String, String> attributes)
             throws TargetSetupError, DeviceNotAvailableException {
-        super.preInvocationSetup(info);
+        super.preInvocationSetup(info, attributes);
         mGceAvd = null;
         // First get the options
         TestDeviceOptions options = getOptions();
diff --git a/src/com/android/tradefed/device/cloud/NestedRemoteDevice.java b/src/com/android/tradefed/device/cloud/NestedRemoteDevice.java
index b362b34..5dd2371 100644
--- a/src/com/android/tradefed/device/cloud/NestedRemoteDevice.java
+++ b/src/com/android/tradefed/device/cloud/NestedRemoteDevice.java
@@ -32,6 +32,7 @@
 import com.android.tradefed.targetprep.TargetSetupError;
 import com.android.tradefed.util.CommandResult;
 import com.android.tradefed.util.CommandStatus;
+import com.android.tradefed.util.MultiMap;
 
 import java.io.File;
 import java.text.SimpleDateFormat;
@@ -70,6 +71,9 @@
     /** When calling launch_cvd, the launcher.log is populated. */
     private static final String LAUNCHER_LOG_PATH = "/home/%s/cuttlefish_runtime/launcher.log";
 
+    /** a cached invocation context attributes so that it can be used during device reset */
+    private MultiMap<String, String> mAttributes = null;
+
     /**
      * Creates a {@link NestedRemoteDevice}.
      *
@@ -86,6 +90,13 @@
         }
     }
 
+    @Override
+    public void preInvocationSetup(IBuildInfo info, MultiMap<String, String> attributes)
+            throws TargetSetupError, DeviceNotAvailableException {
+        super.preInvocationSetup(info, attributes);
+        mAttributes = attributes;
+    }
+
     /** Teardown and restore the virtual device so testing can proceed. */
     public final boolean resetVirtualDevice(
             ITestLogger logger, IBuildInfo info, boolean resetDueToFailure)
@@ -161,7 +172,7 @@
         // Reset recovery since it's a new device
         setRecoveryMode(RecoveryMode.AVAILABLE);
         try {
-            preInvocationSetup(info);
+            preInvocationSetup(info, mAttributes);
         } catch (TargetSetupError e) {
             CLog.e("Failed to re-init the device %s", getSerialNumber());
             CLog.e(e);
diff --git a/src/com/android/tradefed/device/cloud/RemoteAndroidVirtualDevice.java b/src/com/android/tradefed/device/cloud/RemoteAndroidVirtualDevice.java
index 4925bb6..3264513 100644
--- a/src/com/android/tradefed/device/cloud/RemoteAndroidVirtualDevice.java
+++ b/src/com/android/tradefed/device/cloud/RemoteAndroidVirtualDevice.java
@@ -38,6 +38,7 @@
 import com.android.tradefed.result.error.DeviceErrorIdentifier;
 import com.android.tradefed.targetprep.TargetSetupError;
 import com.android.tradefed.util.FileUtil;
+import com.android.tradefed.util.MultiMap;
 import com.android.tradefed.util.StreamUtil;
 
 import com.google.common.annotations.VisibleForTesting;
@@ -85,9 +86,9 @@
 
     /** {@inheritDoc} */
     @Override
-    public void preInvocationSetup(IBuildInfo info)
+    public void preInvocationSetup(IBuildInfo info, MultiMap<String, String> attributes)
             throws TargetSetupError, DeviceNotAvailableException {
-        super.preInvocationSetup(info);
+        super.preInvocationSetup(info, attributes);
         try {
             mGceAvd = null;
             mGceSshMonitor = null;
@@ -98,7 +99,7 @@
 
             // Launch GCE helper script.
             long startTime = getCurrentTime();
-            launchGce(info);
+            launchGce(info, attributes);
             long remainingTime = getOptions().getGceCmdTimeout() - (getCurrentTime() - startTime);
             if (remainingTime < 0) {
                 throw new DeviceNotAvailableException(
@@ -247,11 +248,12 @@
     }
 
     /** Launch the actual gce device based on the build info. */
-    protected void launchGce(IBuildInfo buildInfo) throws TargetSetupError {
+    protected void launchGce(IBuildInfo buildInfo, MultiMap<String, String> attributes)
+            throws TargetSetupError {
         TargetSetupError exception = null;
         for (int attempt = 0; attempt < getOptions().getGceMaxAttempt(); attempt++) {
             try {
-                mGceAvd = getGceHandler().startGce(getInitialIp());
+                mGceAvd = getGceHandler().startGce(getInitialIp(), attributes);
                 if (mGceAvd != null) break;
             } catch (TargetSetupError tse) {
                 CLog.w(
diff --git a/src/com/android/tradefed/invoker/InvocationExecution.java b/src/com/android/tradefed/invoker/InvocationExecution.java
index 8e522a9..938725c 100644
--- a/src/com/android/tradefed/invoker/InvocationExecution.java
+++ b/src/com/android/tradefed/invoker/InvocationExecution.java
@@ -345,7 +345,7 @@
             if (device instanceof ITestLoggerReceiver) {
                 ((ITestLoggerReceiver) context.getDevice(deviceName)).setTestLogger(logger);
             }
-            device.preInvocationSetup(context.getBuildInfo(deviceName));
+            device.preInvocationSetup(context.getBuildInfo(deviceName), context.getAttributes());
         }
     }
 
diff --git a/tests/src/com/android/tradefed/device/LocalAndroidVirtualDeviceTest.java b/tests/src/com/android/tradefed/device/LocalAndroidVirtualDeviceTest.java
index 1098bee..60b7fcd 100644
--- a/tests/src/com/android/tradefed/device/LocalAndroidVirtualDeviceTest.java
+++ b/tests/src/com/android/tradefed/device/LocalAndroidVirtualDeviceTest.java
@@ -350,7 +350,7 @@
         mLocalAvd.setTestLogger(testLogger);
         mLocalAvd.currentRunUtil = acloudCreateRunUtil;
         mLocalAvd.expectToConnect = true;
-        mLocalAvd.preInvocationSetup(mMockBuildInfo);
+        mLocalAvd.preInvocationSetup(mMockBuildInfo, null);
 
         Assert.assertEquals(ONLINE_SERIAL_NUMBER, mLocalAvd.getIDevice().getSerialNumber());
 
@@ -406,7 +406,7 @@
         mLocalAvd.setTestLogger(testLogger);
         mLocalAvd.currentRunUtil = acloudCreateRunUtil;
         try {
-            mLocalAvd.preInvocationSetup(mMockBuildInfo);
+            mLocalAvd.preInvocationSetup(mMockBuildInfo, null);
             Assert.fail("TargetSetupError is not thrown");
         } catch (TargetSetupError e) {
             expectedException = e;
@@ -448,7 +448,7 @@
         mLocalAvd.setTestLogger(testLogger);
         mLocalAvd.currentRunUtil = acloudCreateRunUtil;
         try {
-            mLocalAvd.preInvocationSetup(mMockBuildInfo);
+            mLocalAvd.preInvocationSetup(mMockBuildInfo, null);
             Assert.fail("TargetSetupError is not thrown");
         } catch (TargetSetupError e) {
             expectedException = e;
diff --git a/tests/src/com/android/tradefed/device/cloud/GceManagerTest.java b/tests/src/com/android/tradefed/device/cloud/GceManagerTest.java
index cae19ce..882b252 100644
--- a/tests/src/com/android/tradefed/device/cloud/GceManagerTest.java
+++ b/tests/src/com/android/tradefed/device/cloud/GceManagerTest.java
@@ -34,6 +34,7 @@
 import com.android.tradefed.util.CommandStatus;
 import com.android.tradefed.util.FileUtil;
 import com.android.tradefed.util.IRunUtil;
+import com.android.tradefed.util.MultiMap;
 
 import com.google.common.net.HostAndPort;
 
@@ -73,6 +74,7 @@
         mOptions = new TestDeviceOptions();
         OptionSetter setter = new OptionSetter(mOptions);
         setter.setOptionValue("wait-gce-teardown", "true");
+        setter.setOptionValue("invocation-attribute-to-metadata", "foo");
         mAvdBinary = FileUtil.createTempFile("acloud", ".par");
         mAvdBinary.setExecutable(true);
         mOptions.setAvdDriverBinary(mAvdBinary);
@@ -158,17 +160,20 @@
     /** Test {@link GceManager#buildGceCmd(File, IBuildInfo, String)}. */
     @Test
     public void testBuildGceCommand() throws IOException {
-        IBuildInfo mMockBuildInfo = EasyMock.createMock(IBuildInfo.class);
-        EasyMock.expect(mMockBuildInfo.getBuildAttributes())
+        IBuildInfo mockBuildInfo = EasyMock.createMock(IBuildInfo.class);
+        EasyMock.expect(mockBuildInfo.getBuildAttributes())
                 .andReturn(Collections.<String, String>emptyMap());
-        EasyMock.expect(mMockBuildInfo.getBuildFlavor()).andReturn("FLAVOR");
-        EasyMock.expect(mMockBuildInfo.getBuildBranch()).andReturn("BRANCH");
-        EasyMock.expect(mMockBuildInfo.getBuildId()).andReturn("BUILDID");
-        EasyMock.replay(mMockBuildInfo);
+        EasyMock.expect(mockBuildInfo.getBuildFlavor()).andReturn("FLAVOR");
+        EasyMock.expect(mockBuildInfo.getBuildBranch()).andReturn("BRANCH");
+        EasyMock.expect(mockBuildInfo.getBuildId()).andReturn("BUILDID");
+        EasyMock.replay(mockBuildInfo);
+        MultiMap<String, String> stubAttributes = new MultiMap<>();
+        stubAttributes.put("foo", "bar");
         File reportFile = null;
         try {
             reportFile = FileUtil.createTempFile("test-gce-cmd", "report");
-            List<String> result = mGceManager.buildGceCmd(reportFile, mMockBuildInfo, null);
+            List<String> result =
+                    mGceManager.buildGceCmd(reportFile, mockBuildInfo, null, stubAttributes);
             List<String> expected =
                     ArrayUtil.list(
                             mOptions.getAvdDriverBinary().getAbsolutePath(),
@@ -179,6 +184,8 @@
                             "BRANCH",
                             "--build_id",
                             "BUILDID",
+                            "--gce-metadata",
+                            "foo:bar",
                             "--config_file",
                             mGceManager.getAvdConfigFile().getAbsolutePath(),
                             "--report_file",
@@ -188,7 +195,7 @@
         } finally {
             FileUtil.deleteFile(reportFile);
         }
-        EasyMock.verify(mMockBuildInfo);
+        EasyMock.verify(mockBuildInfo);
     }
 
     /** Test {@link GceManager#buildGceCmd(File, IBuildInfo, String)} with json key file set. */
@@ -206,7 +213,7 @@
         setter.setOptionValue("gce-driver-service-account-json-key-path", "/path/to/key.json");
         try {
             reportFile = FileUtil.createTempFile("test-gce-cmd", "report");
-            List<String> result = mGceManager.buildGceCmd(reportFile, mMockBuildInfo, null);
+            List<String> result = mGceManager.buildGceCmd(reportFile, mMockBuildInfo, null, null);
             List<String> expected =
                     ArrayUtil.list(
                             mOptions.getAvdDriverBinary().getAbsolutePath(),
@@ -255,7 +262,7 @@
                         }
                     };
             reportFile = FileUtil.createTempFile("test-gce-cmd", "report");
-            List<String> result = mGceManager.buildGceCmd(reportFile, mMockBuildInfo, null);
+            List<String> result = mGceManager.buildGceCmd(reportFile, mMockBuildInfo, null, null);
             List<String> expected =
                     ArrayUtil.list(
                             mOptions.getAvdDriverBinary().getAbsolutePath(),
@@ -296,7 +303,7 @@
         setter.setOptionValue("gce-driver-param", "--no-autoconnect");
         try {
             reportFile = FileUtil.createTempFile("test-gce-cmd", "report");
-            List<String> result = mGceManager.buildGceCmd(reportFile, mMockBuildInfo, null);
+            List<String> result = mGceManager.buildGceCmd(reportFile, mMockBuildInfo, null, null);
             List<String> expected =
                     ArrayUtil.list(
                             mOptions.getAvdDriverBinary().getAbsolutePath(),
@@ -338,7 +345,10 @@
 
                     @Override
                     protected List<String> buildGceCmd(
-                            File reportFile, IBuildInfo b, String ipDevice) {
+                            File reportFile,
+                            IBuildInfo b,
+                            String ipDevice,
+                            MultiMap<String, String> attributes) {
                         List<String> tmp = new ArrayList<String>();
                         tmp.add("");
                         return tmp;
@@ -383,7 +393,7 @@
             setter.setOptionValue("gce-driver-param", "--kernel_build_id");
             setter.setOptionValue("gce-driver-param", "KERNELBUILDID");
             reportFile = FileUtil.createTempFile("test-gce-cmd", "report");
-            List<String> result = mGceManager.buildGceCmd(reportFile, mMockBuildInfo, null);
+            List<String> result = mGceManager.buildGceCmd(reportFile, mMockBuildInfo, null, null);
             List<String> expected =
                     ArrayUtil.list(
                             mOptions.getAvdDriverBinary().getAbsolutePath(),
@@ -425,7 +435,10 @@
 
                     @Override
                     protected List<String> buildGceCmd(
-                            File reportFile, IBuildInfo b, String ipDevice) {
+                            File reportFile,
+                            IBuildInfo b,
+                            String ipDevice,
+                            MultiMap<String, String> attributes) {
                         String valid =
                                 " {\n"
                                         + "\"data\": {\n"
@@ -482,7 +495,10 @@
 
                     @Override
                     protected List<String> buildGceCmd(
-                            File reportFile, IBuildInfo b, String ipDevice) {
+                            File reportFile,
+                            IBuildInfo b,
+                            String ipDevice,
+                            MultiMap<String, String> attributes) {
                         // We delete the potential report file to create an issue.
                         FileUtil.deleteFile(reportFile);
                         List<String> tmp = new ArrayList<String>();
@@ -524,7 +540,10 @@
 
                     @Override
                     protected List<String> buildGceCmd(
-                            File reportFile, IBuildInfo b, String ipDevice) {
+                            File reportFile,
+                            IBuildInfo b,
+                            String ipDevice,
+                            MultiMap<String, String> attributes) {
                         String validFail =
                                 " {\n"
                                         + "\"data\": {\n"
@@ -858,7 +877,10 @@
 
                     @Override
                     protected List<String> buildGceCmd(
-                            File reportFile, IBuildInfo b, String ipDevice) {
+                            File reportFile,
+                            IBuildInfo b,
+                            String ipDevice,
+                            MultiMap<String, String> attributes) {
                         // We delete the potential report file to create an issue.
                         FileUtil.deleteFile(reportFile);
                         List<String> tmp = new ArrayList<String>();
diff --git a/tests/src/com/android/tradefed/device/cloud/RemoteAndroidVirtualDeviceTest.java b/tests/src/com/android/tradefed/device/cloud/RemoteAndroidVirtualDeviceTest.java
index e8d0219..f3b88d8 100644
--- a/tests/src/com/android/tradefed/device/cloud/RemoteAndroidVirtualDeviceTest.java
+++ b/tests/src/com/android/tradefed/device/cloud/RemoteAndroidVirtualDeviceTest.java
@@ -45,6 +45,7 @@
 import com.android.tradefed.util.CommandStatus;
 import com.android.tradefed.util.FileUtil;
 import com.android.tradefed.util.IRunUtil;
+import com.android.tradefed.util.MultiMap;
 
 import com.google.common.net.HostAndPort;
 
@@ -177,7 +178,10 @@
                                 getDeviceDescriptor(), new TestDeviceOptions(), mMockBuildInfo) {
                             @Override
                             protected List<String> buildGceCmd(
-                                    File reportFile, IBuildInfo b, String ipDevice) {
+                                    File reportFile,
+                                    IBuildInfo b,
+                                    String ipDevice,
+                                    MultiMap<String, String> attributes) {
                                 FileUtil.deleteFile(reportFile);
                                 List<String> tmp = new ArrayList<String>();
                                 tmp.add("");
@@ -188,7 +192,7 @@
                 };
         EasyMock.replay(mMockRunUtil);
         try {
-            mTestDevice.launchGce(mMockBuildInfo);
+            mTestDevice.launchGce(mMockBuildInfo, null);
             fail("A TargetSetupError should have been thrown");
         } catch (TargetSetupError expected) {
             assertEquals(expectedException, expected.getMessage());
@@ -254,7 +258,9 @@
         mTestDevice =
                 new TestableRemoteAndroidVirtualDevice() {
                     @Override
-                    protected void launchGce(IBuildInfo buildInfo) throws TargetSetupError {
+                    protected void launchGce(
+                            IBuildInfo buildInfo, MultiMap<String, String> attributes)
+                            throws TargetSetupError {
                         // ignore
                     }
 
@@ -281,8 +287,8 @@
         EasyMock.expect(mMockStateMonitor.waitForDeviceAvailable(EasyMock.anyLong()))
                 .andReturn(mMockIDevice);
         EasyMock.expect(mMockIDevice.getState()).andReturn(DeviceState.ONLINE);
-        replayMocks(mMockBuildInfo);
-        mTestDevice.preInvocationSetup(mMockBuildInfo);
+        replayMocks(mMockBuildInfo, null);
+        mTestDevice.preInvocationSetup(mMockBuildInfo, null);
         verifyMocks(mMockBuildInfo);
 
         Mockito.verify(mGceHandler).logStableHostImageInfos(mMockBuildInfo);
@@ -298,7 +304,9 @@
         mTestDevice =
                 new TestableRemoteAndroidVirtualDevice() {
                     @Override
-                    protected void launchGce(IBuildInfo buildInfo) throws TargetSetupError {
+                    protected void launchGce(
+                            IBuildInfo buildInfo, MultiMap<String, String> attributes)
+                            throws TargetSetupError {
                         // ignore
                     }
 
@@ -312,7 +320,7 @@
         EasyMock.expect(mMockIDevice.getState()).andReturn(DeviceState.OFFLINE).times(2);
         replayMocks(mMockBuildInfo);
         try {
-            mTestDevice.preInvocationSetup(mMockBuildInfo);
+            mTestDevice.preInvocationSetup(mMockBuildInfo, null);
             fail("Should have thrown an exception.");
         } catch (DeviceNotAvailableException expected) {
             // expected
@@ -380,10 +388,10 @@
                                 "acloud error",
                                 GceStatus.BOOT_FAIL))
                 .when(mGceHandler)
-                .startGce(null);
+                .startGce(null, null);
         EasyMock.replay(mMockRunUtil, mMockIDevice);
         try {
-            mTestDevice.launchGce(new BuildInfo());
+            mTestDevice.launchGce(new BuildInfo(), null);
             fail("Should have thrown an exception");
         } catch (TargetSetupError expected) {
             // expected
@@ -421,7 +429,7 @@
                 };
         doReturn(new GceAvdInfo("ins-name", null, "acloud error", GceStatus.BOOT_FAIL))
                 .when(mGceHandler)
-                .startGce(null);
+                .startGce(null, null);
         // Each invocation bellow will dump a logcat before the shutdown.
         mMockIDevice.executeShellCommand(
                 EasyMock.eq("logcat -v threadtime -d"), EasyMock.anyObject(),
@@ -437,7 +445,7 @@
         mTestDevice.setTestLogger(mTestLogger);
         Exception expectedException = null;
         try {
-            mTestDevice.launchGce(new BuildInfo());
+            mTestDevice.launchGce(new BuildInfo(), null);
             fail("Should have thrown an exception");
         } catch (TargetSetupError expected) {
             // expected
@@ -541,7 +549,7 @@
                                     null,
                                     GceStatus.SUCCESS))
                     .when(mGceHandler)
-                    .startGce(null);
+                    .startGce(null, null);
 
             // Each invocation bellow will dump a logcat before the shutdown.
             mMockIDevice.executeShellCommand(
@@ -556,7 +564,7 @@
 
             replayMocks(mMockBuildInfo);
             // Run device a first time
-            mTestDevice.preInvocationSetup(mMockBuildInfo);
+            mTestDevice.preInvocationSetup(mMockBuildInfo, null);
             mTestDevice.getGceSshMonitor().joinMonitor();
             // We expect to find our Runtime exception for the ssh key
             assertNotNull(mTestDevice.getGceSshMonitor().getLastException());
@@ -565,7 +573,7 @@
             assertNull(mTestDevice.getGceSshMonitor());
 
             // run a second time on same device should yield exact same exception.
-            mTestDevice.preInvocationSetup(mMockBuildInfo);
+            mTestDevice.preInvocationSetup(mMockBuildInfo, null);
             mTestDevice.getGceSshMonitor().joinMonitor();
             // Should have the same result, the run time exception from ssh key
             assertNotNull(mTestDevice.getGceSshMonitor().getLastException());
@@ -642,7 +650,7 @@
                                     null,
                                     GceStatus.SUCCESS))
                     .when(mGceHandler)
-                    .startGce(null);
+                    .startGce(null, null);
             mMockIDevice.executeShellCommand(
                     EasyMock.eq("logcat -v threadtime -d"), EasyMock.anyObject(),
                     EasyMock.anyLong(), EasyMock.eq(TimeUnit.MILLISECONDS));
@@ -653,7 +661,7 @@
 
             replayMocks(mMockBuildInfo);
             // Run device a first time
-            mTestDevice.preInvocationSetup(mMockBuildInfo);
+            mTestDevice.preInvocationSetup(mMockBuildInfo, null);
             mTestDevice.getGceSshMonitor().joinMonitor();
             // We expect to find our Runtime exception for the ssh key
             assertNotNull(mTestDevice.getGceSshMonitor().getLastException());
@@ -728,7 +736,7 @@
                                     null,
                                     GceStatus.SUCCESS))
                     .when(mGceHandler)
-                    .startGce(null);
+                    .startGce(null, null);
 
             CommandResult bugreportzResult = new CommandResult(CommandStatus.SUCCESS);
             bugreportzResult.setStdout("OK: bugreportz-file");
@@ -759,7 +767,7 @@
             replayMocks(mMockBuildInfo);
             // Run device a first time
             try {
-                mTestDevice.preInvocationSetup(mMockBuildInfo);
+                mTestDevice.preInvocationSetup(mMockBuildInfo, null);
                 fail("Should have thrown an exception.");
             } catch (DeviceNotAvailableException expected) {
                 assertEquals("AVD device booted but was in OFFLINE state", expected.getMessage());
diff --git a/tests/src/com/android/tradefed/invoker/SandboxedInvocationExecutionTest.java b/tests/src/com/android/tradefed/invoker/SandboxedInvocationExecutionTest.java
index dfb8b95..2bae86d 100644
--- a/tests/src/com/android/tradefed/invoker/SandboxedInvocationExecutionTest.java
+++ b/tests/src/com/android/tradefed/invoker/SandboxedInvocationExecutionTest.java
@@ -324,7 +324,7 @@
         mContext.addInvocationAttribute("test", "test");
         // Device early preInvocationSetup was called and even if no tests run we still call tear
         // down
-        Mockito.verify(mMockDevice).preInvocationSetup(any());
+        Mockito.verify(mMockDevice).preInvocationSetup(any(), any());
         Mockito.verify(mMockDevice).postInvocationTearDown(null);
     }
 
@@ -381,7 +381,7 @@
         doReturn(info).when(mMockProvider).getBuild();
 
         DeviceNotAvailableException exception = new DeviceNotAvailableException("reason", "serial");
-        doThrow(exception).when(mMockDevice).preInvocationSetup(eq(info));
+        doThrow(exception).when(mMockDevice).preInvocationSetup(eq(info), any());
 
         mInvocation.invoke(mContext, mConfig, mMockRescheduler, mMockListener);
         // No tests to run but we still call start/end
@@ -394,7 +394,7 @@
         mContext.addInvocationAttribute("test", "test");
         // Device early preInvocationSetup was called and even if no tests run we still call tear
         // down
-        Mockito.verify(mMockDevice).preInvocationSetup(any());
+        Mockito.verify(mMockDevice).preInvocationSetup(any(), any());
         Mockito.verify(mMockDevice).postInvocationTearDown(exception);
     }
 
diff --git a/tests/src/com/android/tradefed/invoker/TestInvocationTest.java b/tests/src/com/android/tradefed/invoker/TestInvocationTest.java
index f8c4eb2..4c487f3 100644
--- a/tests/src/com/android/tradefed/invoker/TestInvocationTest.java
+++ b/tests/src/com/android/tradefed/invoker/TestInvocationTest.java
@@ -94,6 +94,7 @@
 import com.android.tradefed.testtype.IShardableTest;
 import com.android.tradefed.testtype.StubTest;
 import com.android.tradefed.util.FileUtil;
+import com.android.tradefed.util.MultiMap;
 import com.android.tradefed.util.SystemUtil.EnvVariable;
 import com.android.tradefed.util.keystore.IKeyStoreClient;
 import com.android.tradefed.util.keystore.StubKeyStoreFactory;
@@ -174,6 +175,7 @@
         }
     }
 
+    @SuppressWarnings("unchecked")
     @Before
     public void setUp() throws Exception {
         mStubConfiguration =
@@ -237,7 +239,8 @@
         EasyMock.expect(mMockDevice.getDeviceState()).andStubReturn(TestDeviceState.NOT_AVAILABLE);
         mMockDevice.setRecoveryMode(RecoveryMode.AVAILABLE);
         mMockDevice.setRecovery(mMockRecovery);
-        mMockDevice.preInvocationSetup((IBuildInfo) EasyMock.anyObject());
+        mMockDevice.preInvocationSetup(
+                (IBuildInfo) EasyMock.anyObject(), (MultiMap<String, String>) EasyMock.anyObject());
         EasyMock.expectLastCall().anyTimes();
         mFakeDescriptor =
                 new DeviceDescriptor(