Merge "Extract RunUtil interrupt methods."
diff --git a/atest/atest_metrics.py b/atest/atest_metrics.py
index 2c15b5c..818fa9c 100755
--- a/atest/atest_metrics.py
+++ b/atest/atest_metrics.py
@@ -30,7 +30,11 @@
 def log_start_event():
     """Log that atest started."""
     try:
-        data = {'grouping_key': str(get_grouping_key()),
+        try:
+            key = str(get_grouping_key())
+        except Exception:
+            key = constants.DUMMY_UUID
+        data = {'grouping_key': key,
                 'run_id': str(uuid.uuid4())}
         data = json.dumps(data)
         request = urllib2.Request(constants.METRICS_URL, data=data, headers=JSON_HEADERS)
@@ -43,15 +47,36 @@
 
 def get_grouping_key():
     """Get grouping key. Returns UUID."""
-    meta_file = os.path.join(os.environ[constants.ANDROID_BUILD_TOP],
-                             'tools/tradefederation/core/atest', constants.META_FILE)
-    if os.path.isfile(meta_file):
-        with open(meta_file) as f:
+    if os.path.isfile(constants.META_FILE):
+        with open(constants.META_FILE) as f:
             try:
                 return uuid.UUID(f.read(), version=4)
             except ValueError:
                 logging.debug('malformed group_key in file, rewriting')
-    key = uuid.uuid4()
-    with open(meta_file, 'w+') as f:
+    # TODO: Delete get_old_key() on 11/17/2018
+    key = get_old_key() or uuid.uuid4()
+    dir_path = os.path.dirname(constants.META_FILE)
+    if os.path.isfile(dir_path):
+        os.remove(dir_path)
+    try:
+        os.makedirs(dir_path)
+    except OSError as e:
+        if not os.path.isdir(dir_path):
+            raise e
+    with open(constants.META_FILE, 'w+') as f:
         f.write(str(key))
     return key
+
+def get_old_key():
+    """Get key from old meta data file if exists, else return None."""
+    old_file = os.path.join(os.environ[constants.ANDROID_BUILD_TOP],
+                            'tools/tradefederation/core/atest', '.metadata')
+    key = None
+    if os.path.isfile(old_file):
+        with open(old_file) as f:
+            try:
+                key = uuid.UUID(f.read(), version=4)
+            except ValueError:
+                logging.debug('error reading old key')
+        os.remove(old_file)
+    return key
diff --git a/atest/constants_default.py b/atest/constants_default.py
index 4d5fe5c..ee1eb85 100644
--- a/atest/constants_default.py
+++ b/atest/constants_default.py
@@ -16,6 +16,8 @@
 Various globals used by atest.
 """
 
+import os
+
 MODE = 'DEFAULT'
 
 # Result server constants for atest_utils.
@@ -110,10 +112,12 @@
 BOTH_TEST = 'both'
 
 # Metrics
-META_FILE = '.metadata'
+META_FILE = os.path.join(os.path.expanduser('~'),
+                         '.config', 'asuite', '.metadata')
 METRICS_URL = 'http://asuite-218222.appspot.com/atest/metrics'
 METRICS_TIMEOUT = 2 #seconds
 METRICS_RESPONSE = 'done'
+DUMMY_UUID = '00000000-0000-4000-8000-000000000000'
 
 # VTS plans
 VTS_STAGING_PLAN = 'vts-staging-default'
diff --git a/src/com/android/tradefed/device/DeviceProperties.java b/src/com/android/tradefed/device/DeviceProperties.java
index 20df386..e05e522 100644
--- a/src/com/android/tradefed/device/DeviceProperties.java
+++ b/src/com/android/tradefed/device/DeviceProperties.java
@@ -24,8 +24,10 @@
     public static final String BOARD = "ro.product.board";
     /** proprty name to indicate device variant (e.g. flo vs dev) */
     public static final String VARIANT = "ro.product.vendor.device";
+    /** Legacy O-MR1 property name to indicate device variant (e.g. flo vs dev) */
+    public static final String VARIANT_LEGACY_O_MR1 = "ro.vendor.product.device";
     /** Legacy property name to indicate device variant (e.g. flo vs dev) */
-    public static final String VARIANT_LEGACY = "ro.product.device";
+    public static final String VARIANT_LEGACY_LESS_EQUAL_O = "ro.product.device";
     /** proprty name to indicate SDK version */
     public static final String SDK_VERSION = "ro.build.version.sdk";
 }
diff --git a/src/com/android/tradefed/device/DeviceSelectionOptions.java b/src/com/android/tradefed/device/DeviceSelectionOptions.java
index 0eb45e9..b784e31 100644
--- a/src/com/android/tradefed/device/DeviceSelectionOptions.java
+++ b/src/com/android/tradefed/device/DeviceSelectionOptions.java
@@ -612,7 +612,10 @@
     public String getDeviceProductVariant(IDevice device) {
         String prop = getProperty(device, DeviceProperties.VARIANT);
         if (prop == null) {
-            prop = getProperty(device, DeviceProperties.VARIANT_LEGACY);
+            prop = getProperty(device, DeviceProperties.VARIANT_LEGACY_O_MR1);
+        }
+        if (prop == null) {
+            prop = getProperty(device, DeviceProperties.VARIANT_LEGACY_LESS_EQUAL_O);
         }
         if (prop != null) {
             prop = prop.toLowerCase();
diff --git a/src/com/android/tradefed/device/NativeDevice.java b/src/com/android/tradefed/device/NativeDevice.java
index 83af872..ef332b2 100644
--- a/src/com/android/tradefed/device/NativeDevice.java
+++ b/src/com/android/tradefed/device/NativeDevice.java
@@ -557,7 +557,14 @@
         if (prop == null) {
             prop =
                     internalGetProperty(
-                            DeviceProperties.VARIANT_LEGACY, "variant", "Product variant");
+                            DeviceProperties.VARIANT_LEGACY_O_MR1, "variant", "Product variant");
+        }
+        if (prop == null) {
+            prop =
+                    internalGetProperty(
+                            DeviceProperties.VARIANT_LEGACY_LESS_EQUAL_O,
+                            "variant",
+                            "Product variant");
         }
         if (prop != null) {
             prop = prop.toLowerCase();
diff --git a/src/com/android/tradefed/testtype/suite/GranularRetriableTestWrapper.java b/src/com/android/tradefed/testtype/suite/GranularRetriableTestWrapper.java
index e52eebe..bf99e52 100644
--- a/src/com/android/tradefed/testtype/suite/GranularRetriableTestWrapper.java
+++ b/src/com/android/tradefed/testtype/suite/GranularRetriableTestWrapper.java
@@ -36,6 +36,7 @@
 import com.google.common.collect.Sets;
 
 import java.util.ArrayList;
+import java.util.HashMap;
 import java.util.HashSet;
 import java.util.LinkedHashMap;
 import java.util.LinkedHashSet;
@@ -85,6 +86,8 @@
     private long mSuccessRetried = 0L;
     /** The number of test cases that remained failed after all retry attempts */
     private long mFailedRetried = 0L;
+    /** Store the test that successfully re-run and at which attempt they passed */
+    private Map<String, Integer> mAttemptSuccess = new HashMap<>();
 
     private RetryStrategy mRetryStrategy = RetryStrategy.RETRY_TEST_CASE_FAILURE;
 
@@ -297,6 +300,9 @@
                     if (previousFailedTests != null) {
                         Set<TestDescription> diff = Sets.difference(previousFailedTests, lastRun);
                         mSuccessRetried += diff.size();
+                        final int currentAttempt = attemptNumber;
+                        diff.forEach(
+                                (desc) -> mAttemptSuccess.put(desc.toString(), currentAttempt));
                         previousFailedTests = lastRun;
                     }
                 }
@@ -456,6 +462,11 @@
         return mMainGranularRunListener;
     }
 
+    /** Returns the attempts that turned into success. */
+    public Map<String, Integer> getAttemptSuccessStats() {
+        return mAttemptSuccess;
+    }
+
     /** Forwarder that also handles passing the current attempt we are at. */
     private class RetryLogSaverResultForwarder extends LogSaverResultForwarder {
 
diff --git a/src/com/android/tradefed/testtype/suite/ModuleDefinition.java b/src/com/android/tradefed/testtype/suite/ModuleDefinition.java
index 06f872c..8c58a28 100644
--- a/src/com/android/tradefed/testtype/suite/ModuleDefinition.java
+++ b/src/com/android/tradefed/testtype/suite/ModuleDefinition.java
@@ -100,6 +100,8 @@
     public static final String RETRY_SUCCESS_COUNT = "MODULE_RETRY_SUCCESS";
     public static final String RETRY_FAIL_COUNT = "MODULE_RETRY_FAILED";
 
+    private static final String FLAKE_DATE_PREFIX = "FLAKE_DATA:";
+
     private final IInvocationContext mModuleInvocationContext;
     private final IConfiguration mModuleConfiguration;
     private ILogSaver mLogSaver;
@@ -450,6 +452,8 @@
                     mRetryTime += retriableTest.getRetryTime();
                     mSuccessRetried += retriableTest.getRetrySuccess();
                     mFailedRetried += retriableTest.getRetryFailed();
+
+                    addAttemptStatsToBuild(mBuild, retriableTest.getAttemptSuccessStats());
                 }
                 // After the run, if the test failed (even after retry the final result passed) has
                 // failed, capture a bugreport.
@@ -841,4 +845,11 @@
         }
         return RunStrategy.RUN;
     }
+
+    private void addAttemptStatsToBuild(IBuildInfo build, Map<String, Integer> attemptStats) {
+        for (Entry<String, Integer> entry : attemptStats.entrySet()) {
+            build.addBuildAttribute(
+                    FLAKE_DATE_PREFIX + entry.getKey(), Integer.toString(entry.getValue()));
+        }
+    }
 }
diff --git a/tests/src/com/android/tradefed/device/DeviceSelectionOptionsTest.java b/tests/src/com/android/tradefed/device/DeviceSelectionOptionsTest.java
index 3b0011a..d34906f 100644
--- a/tests/src/com/android/tradefed/device/DeviceSelectionOptionsTest.java
+++ b/tests/src/com/android/tradefed/device/DeviceSelectionOptionsTest.java
@@ -153,7 +153,9 @@
     @Test
     public void testGetProductVariant_legacy() throws Exception {
         EasyMock.expect(mMockDevice.getProperty(DeviceProperties.VARIANT)).andReturn(null);
-        EasyMock.expect(mMockDevice.getProperty(DeviceProperties.VARIANT_LEGACY))
+        EasyMock.expect(mMockDevice.getProperty(DeviceProperties.VARIANT_LEGACY_O_MR1))
+                .andReturn(null);
+        EasyMock.expect(mMockDevice.getProperty(DeviceProperties.VARIANT_LEGACY_LESS_EQUAL_O))
                 .andReturn("legacy");
         EasyMock.replay(mMockDevice);
 
@@ -161,9 +163,23 @@
     }
 
     @Test
+    public void testGetProductVariant_legacyOmr1() throws Exception {
+        EasyMock.expect(mMockDevice.getProperty(DeviceProperties.VARIANT)).andReturn(null);
+        EasyMock.expect(mMockDevice.getProperty(DeviceProperties.VARIANT_LEGACY_O_MR1))
+                .andReturn("legacy_omr1");
+        EasyMock.expect(mMockDevice.getProperty(DeviceProperties.VARIANT_LEGACY_LESS_EQUAL_O))
+                .andReturn("legacy");
+        EasyMock.replay(mMockDevice);
+
+        assertEquals("legacy_omr1", mDeviceSelection.getDeviceProductVariant(mMockDevice));
+    }
+
+    @Test
     public void testGetProductVariant_vendor() throws Exception {
         EasyMock.expect(mMockDevice.getProperty(DeviceProperties.VARIANT)).andReturn("variant");
-        EasyMock.expect(mMockDevice.getProperty(DeviceProperties.VARIANT_LEGACY))
+        EasyMock.expect(mMockDevice.getProperty(DeviceProperties.VARIANT_LEGACY_O_MR1))
+                .andReturn("legacy_mr1");
+        EasyMock.expect(mMockDevice.getProperty(DeviceProperties.VARIANT_LEGACY_LESS_EQUAL_O))
                 .andReturn("legacy");
         EasyMock.replay(mMockDevice);
 
@@ -187,7 +203,10 @@
 
         EasyMock.expect(mMockDevice.getProperty(DeviceProperties.BOARD)).andReturn(DEVICE_TYPE);
         EasyMock.expect(mMockDevice.getProperty(DeviceProperties.VARIANT)).andReturn(null);
-        EasyMock.expect(mMockDevice.getProperty(DeviceProperties.VARIANT_LEGACY)).andReturn(null);
+        EasyMock.expect(mMockDevice.getProperty(DeviceProperties.VARIANT_LEGACY_O_MR1))
+                .andReturn(null);
+        EasyMock.expect(mMockDevice.getProperty(DeviceProperties.VARIANT_LEGACY_LESS_EQUAL_O))
+                .andReturn(null);
         EasyMock.replay(mMockDevice);
         assertTrue(mDeviceSelection.matches(mMockDevice));
     }
@@ -202,7 +221,10 @@
 
         EasyMock.expect(mMockDevice.getProperty(DeviceProperties.BOARD)).andReturn(DEVICE_TYPE);
         EasyMock.expect(mMockDevice.getProperty(DeviceProperties.VARIANT)).andReturn(null);
-        EasyMock.expect(mMockDevice.getProperty(DeviceProperties.VARIANT_LEGACY)).andReturn(null);
+        EasyMock.expect(mMockDevice.getProperty(DeviceProperties.VARIANT_LEGACY_O_MR1))
+                .andReturn(null);
+        EasyMock.expect(mMockDevice.getProperty(DeviceProperties.VARIANT_LEGACY_LESS_EQUAL_O))
+                .andReturn(null);
         EasyMock.replay(mMockDevice);
         assertTrue(mDeviceSelection.matches(mMockDevice));
     }
diff --git a/tests/src/com/android/tradefed/device/NativeDeviceTest.java b/tests/src/com/android/tradefed/device/NativeDeviceTest.java
index 081021a..67c816a 100644
--- a/tests/src/com/android/tradefed/device/NativeDeviceTest.java
+++ b/tests/src/com/android/tradefed/device/NativeDeviceTest.java
@@ -2301,6 +2301,24 @@
     }
 
     @Test
+    public void testGetProductVariant_legacyOmr1() throws Exception {
+        TestableAndroidNativeDevice testDevice =
+                new TestableAndroidNativeDevice() {
+                    @Override
+                    protected String internalGetProperty(
+                            String propName, String fastbootVar, String description)
+                            throws DeviceNotAvailableException, UnsupportedOperationException {
+                        if (DeviceProperties.VARIANT_LEGACY_O_MR1.equals(propName)) {
+                            return "legacy_omr1";
+                        }
+                        return null;
+                    }
+                };
+
+        assertEquals("legacy_omr1", testDevice.getProductVariant());
+    }
+
+    @Test
     public void testGetProductVariant_legacy() throws Exception {
         TestableAndroidNativeDevice testDevice =
                 new TestableAndroidNativeDevice() {
@@ -2308,7 +2326,7 @@
                     protected String internalGetProperty(
                             String propName, String fastbootVar, String description)
                             throws DeviceNotAvailableException, UnsupportedOperationException {
-                        if (DeviceProperties.VARIANT_LEGACY.equals(propName)) {
+                        if (DeviceProperties.VARIANT_LEGACY_LESS_EQUAL_O.equals(propName)) {
                             return "legacy";
                         }
                         return null;