Collector for getting app versions as metrics

Sample output:
```
INSTRUMENTATION_STATUS: com.android.chrome=447211483
INSTRUMENTATION_STATUS: com.google.android.youtube=1521481176
```

Bug: b/195074731
Test: atest CollectorsHelperTest:com.android.helpers.tests.AppVersionHelperTest

Change-Id: I6679eebcc7eda36a3701d5b3e415a9c5948de591
diff --git a/libraries/collectors-helper/app/Android.bp b/libraries/collectors-helper/app/Android.bp
new file mode 100644
index 0000000..d60aff2
--- /dev/null
+++ b/libraries/collectors-helper/app/Android.bp
@@ -0,0 +1,36 @@
+// Copyright (C) 2021 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.
+
+// Used for collecting app metrics.
+package {
+    default_applicable_licenses: ["Android-Apache-2.0"],
+}
+
+java_library {
+    name: "app-collector-helper",
+    defaults: ["tradefed_errorprone_defaults"],
+
+    srcs: [
+        "src/**/*.java",
+    ],
+
+    static_libs: [
+        "androidx.test.core",
+        "androidx.test.runner",
+        "androidx.test.uiautomator",
+        "collector-helper-utilities",
+    ],
+
+    sdk_version: "current",
+}
diff --git a/libraries/collectors-helper/app/src/com/android/helpers/AppVersionHelper.java b/libraries/collectors-helper/app/src/com/android/helpers/AppVersionHelper.java
new file mode 100644
index 0000000..f8d2678
--- /dev/null
+++ b/libraries/collectors-helper/app/src/com/android/helpers/AppVersionHelper.java
@@ -0,0 +1,88 @@
+/*
+ * Copyright (C) 2021 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
+ */
+
+package com.android.helpers;
+
+import static com.android.helpers.MetricUtility.constructKey;
+
+import android.content.Context;
+import android.content.pm.PackageInfo;
+import android.content.pm.PackageManager;
+import android.util.Log;
+
+import androidx.test.core.app.ApplicationProvider;
+
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+/**
+ * This is a collector helper to use PackageManager to get app package versions.
+ */
+public class AppVersionHelper implements ICollectorHelper<Long> {
+
+    private static final String TAG = AppVersionHelper.class.getSimpleName();
+    private static final String METRIC_PREFIX = "app-version-code-long";
+
+    private Context context;
+    private PackageManager packageManager;
+
+    private String[] mPackageNames = {};
+
+    public void setUp(String... packageNames) {
+        context = ApplicationProvider.getApplicationContext().getApplicationContext();
+        packageManager = context.getPackageManager();
+        mPackageNames = packageNames;
+    }
+
+    @Override
+    public boolean startCollecting() {
+        return true;
+    }
+
+    @Override
+    public Map<String, Long> getMetrics() {
+        Map<String, Long> metrics = new HashMap<>();
+        if (mPackageNames == null || mPackageNames.length == 0 || mPackageNames[0].length() == 0) {
+            // If no package specified, collects all.
+            // Additional option flags is set to 0, which is simply default.
+            List<PackageInfo> pkgInfos = packageManager.getInstalledPackages(0);
+            for (PackageInfo pkgInfo : pkgInfos) {
+                metrics.put(constructKey(METRIC_PREFIX, pkgInfo.packageName),
+                        pkgInfo.getLongVersionCode());
+            }
+        } else {
+            for (String pkg : mPackageNames) {
+                try {
+                    // Additional option flags is set to 0, which is simply default.
+                    PackageInfo pkgInfo = packageManager.getPackageInfo(pkg, 0);
+                    metrics.put(constructKey(METRIC_PREFIX, pkg),
+                            pkgInfo.getLongVersionCode());
+                    Log.d(TAG, "Found app version for package name " + pkg);
+                } catch (PackageManager.NameNotFoundException exception) {
+                    Log.e(TAG, "Can't find package name " + pkg);
+                    continue;
+                }
+            }
+        }
+        return metrics;
+    }
+
+    @Override
+    public boolean stopCollecting() {
+        return true;
+    }
+}
diff --git a/libraries/collectors-helper/app/test/Android.bp b/libraries/collectors-helper/app/test/Android.bp
new file mode 100644
index 0000000..3537f65
--- /dev/null
+++ b/libraries/collectors-helper/app/test/Android.bp
@@ -0,0 +1,33 @@
+// Copyright (C) 2021 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.
+
+package {
+    default_applicable_licenses: ["Android-Apache-2.0"],
+}
+
+java_library {
+    name: "app-collector-helper-test",
+    defaults: ["tradefed_errorprone_defaults"],
+
+    srcs: ["src/**/*.java"],
+
+    static_libs: [
+        "androidx.test.runner",
+        "junit",
+        "mockito-target",
+        "app-collector-helper",
+    ],
+
+    sdk_version: "current",
+}
\ No newline at end of file
diff --git a/libraries/collectors-helper/app/test/src/com/android/helpers/tests/AppVersionHelperTest.java b/libraries/collectors-helper/app/test/src/com/android/helpers/tests/AppVersionHelperTest.java
new file mode 100644
index 0000000..6d196e6
--- /dev/null
+++ b/libraries/collectors-helper/app/test/src/com/android/helpers/tests/AppVersionHelperTest.java
@@ -0,0 +1,119 @@
+/*
+ * Copyright (C) 2021 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.
+ */
+
+package com.android.helpers.tests;
+
+import static com.android.helpers.MetricUtility.constructKey;
+
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertTrue;
+
+import androidx.test.runner.AndroidJUnit4;
+
+import com.android.helpers.AppVersionHelper;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+import java.util.Map;
+
+/**
+ * Android Unit tests for {@link AppVersionHelper}.
+ *
+ * To run:
+ * atest CollectorsHelperTest:com.android.helpers.tests.AppVersionHelperTest
+ */
+@RunWith(AndroidJUnit4.class)
+public class AppVersionHelperTest {
+
+    // Package name used for testing
+    private static final String TEST_PACKAGE_NAME = "com.android.systemui";
+    // Second package name used for testing
+    private static final String TEST_PACKAGE_NAME_2 = "com.google.android.apps.nexuslauncher";
+    // Invalid package name used for testing
+    private static final String INVALID_PACKAGE_NAME = "abc";
+    // App version metric prefix in Key.
+    private static final String METRIC_PREFIX = "app-version-code-long";
+
+    private AppVersionHelper mAppVersionHelper;
+    private static final String TAG = AppVersionHelper.class.getSimpleName();
+
+    @Before
+    public void setUp() {
+        mAppVersionHelper = new AppVersionHelper();
+    }
+
+    /** Test all app version metrics are sampled if package name is empty. */
+    @Test
+    public void testEmptyPackageName() {
+        mAppVersionHelper.setUp("");
+        Map<String, Long> appVersionMetrics = mAppVersionHelper.getMetrics();
+        assertFalse("Metrics should not be empty for empty package name.",
+                appVersionMetrics.isEmpty());
+    }
+
+    /** Test all app version metrics are sampled if package names is null */
+    @Test
+    public void testNullPackageName() {
+        mAppVersionHelper.setUp(null);
+        Map<String, Long> appVersionMetrics = mAppVersionHelper.getMetrics();
+        assertFalse("Metrics should not be empty for null package name.",
+                appVersionMetrics.isEmpty());
+    }
+
+    /** Test getting metrics for single package. */
+    @Test
+    public void testGetMetrics_OnePackage() {
+        mAppVersionHelper.setUp(TEST_PACKAGE_NAME);
+        Map<String, Long> appVersionMetrics = mAppVersionHelper.getMetrics();
+        assertFalse("Metrics should not be empty for single package name.",
+                appVersionMetrics.isEmpty());
+        String metricKey = constructKey(METRIC_PREFIX, TEST_PACKAGE_NAME);
+        assertTrue("Missing metric key: " + metricKey, appVersionMetrics.containsKey(metricKey));
+        assertTrue("Bad metric for " + metricKey + ": " + appVersionMetrics.get(metricKey),
+                appVersionMetrics.get(metricKey) > 0);
+    }
+
+    /** Test getting metrics for multiple packages. */
+    @Test
+    public void testGetMetrics_MultiplePackages() {
+        mAppVersionHelper.setUp(TEST_PACKAGE_NAME, TEST_PACKAGE_NAME_2);
+        Map<String, Long> appVersionMetrics = mAppVersionHelper.getMetrics();
+        assertFalse("Metrics should not be empty for multiple package names.",
+                appVersionMetrics.isEmpty());
+        String metricKey1 = constructKey(METRIC_PREFIX, TEST_PACKAGE_NAME);
+        String metricKey2 = constructKey(METRIC_PREFIX, TEST_PACKAGE_NAME_2);
+
+        assertTrue("Missing metric key: " + metricKey1,
+                appVersionMetrics.containsKey(metricKey1));
+        assertTrue("Missing metric key: " + metricKey2,
+                appVersionMetrics.containsKey(metricKey2));
+        assertTrue("Bad metric for " + metricKey1 + ": " + appVersionMetrics.get(metricKey1),
+                appVersionMetrics.get(metricKey1) > 0);
+        assertTrue("Bad metric for " + metricKey2 + ": " + appVersionMetrics.get(metricKey2),
+                appVersionMetrics.get(metricKey2) > 0);
+    }
+
+    /** Test app version metric is 0 for invalid package name. */
+    @Test
+    public void testGetMetrics_InvalidPackage() {
+        mAppVersionHelper.setUp(INVALID_PACKAGE_NAME);
+        Map<String, Long> appVersionMetrics = mAppVersionHelper.getMetrics();
+        assertTrue("Metrics should not be collected for invalid package names.",
+                appVersionMetrics.isEmpty());
+    }
+}
\ No newline at end of file
diff --git a/libraries/collectors-helper/tests/Android.bp b/libraries/collectors-helper/tests/Android.bp
index 4a3413a..502e9fc 100644
--- a/libraries/collectors-helper/tests/Android.bp
+++ b/libraries/collectors-helper/tests/Android.bp
@@ -22,6 +22,7 @@
 
     static_libs: [
         "perfetto-helper-test",
+        "app-collector-helper-test",
         "jank-helper-test",
         "memory-helper-test",
         "system-helper-test",
diff --git a/libraries/device-collectors/src/main/Android.bp b/libraries/device-collectors/src/main/Android.bp
index f9bee71..709f21f 100644
--- a/libraries/device-collectors/src/main/Android.bp
+++ b/libraries/device-collectors/src/main/Android.bp
@@ -26,6 +26,7 @@
         "androidx.annotation_annotation",
         "androidx.test.runner",
         "androidx.test.uiautomator",
+        "app-collector-helper",
         "jank-helper",
         "junit",
         "lyric-metric-helper",
diff --git a/libraries/device-collectors/src/main/java/android/device/collectors/AppVersionListener.java b/libraries/device-collectors/src/main/java/android/device/collectors/AppVersionListener.java
new file mode 100644
index 0000000..809c9bd
--- /dev/null
+++ b/libraries/device-collectors/src/main/java/android/device/collectors/AppVersionListener.java
@@ -0,0 +1,70 @@
+/*
+ * Copyright (C) 2021 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.
+ */
+
+package android.device.collectors;
+
+import android.device.collectors.annotations.OptionClass;
+import android.os.Bundle;
+import android.util.Log;
+import androidx.annotation.VisibleForTesting;
+
+import com.android.helpers.AppVersionHelper;
+
+/**
+ * A {@link AppVersionListener} that captures and records the versions of the app packages
+ * on the device.
+ * <p>Options:
+ * <ul>
+ *   <li>-e pkg-names [pkgNames] : the pkg names we want to get versions for.
+ * </ul>
+ */
+@OptionClass(alias = "app-version-listener")
+public class AppVersionListener extends BaseCollectionListener<Long> {
+
+    private static final String TAG = AppVersionHelper.class.getSimpleName();
+    private AppVersionHelper mAppVersionHelper = new AppVersionHelper();
+    @VisibleForTesting static final String PKG_NAME_SEPARATOR = ",";
+    @VisibleForTesting static final String PKG_NAMES_ARG = "app-version-pkg-names";
+
+    public AppVersionListener() {
+        createHelperInstance(mAppVersionHelper);
+    }
+
+    /**
+     * Constructor to simulate receiving the instrumentation arguments. Should not be used except
+     * for testing.
+     */
+    @VisibleForTesting
+    public AppVersionListener(Bundle args, AppVersionHelper helper) {
+        super(args, helper);
+        mAppVersionHelper = helper;
+        createHelperInstance(mAppVersionHelper);
+    }
+
+    /**
+     * Adds the options for app version collector.
+     */
+    @Override
+    public void setupAdditionalArgs() {
+        Bundle args = getArgsBundle();
+        String pkgNamesString = args.getString(PKG_NAMES_ARG);
+        if (pkgNamesString == null) {
+            Log.w(TAG, "No package name provided. All packages will be collected");
+            mAppVersionHelper.setUp();
+        }
+        mAppVersionHelper.setUp(pkgNamesString.split(PKG_NAME_SEPARATOR));
+    }
+}