Usage Tracker: add product version to logs

Right now we only track the version of Android Studio
doing the upload of the metrics. This can cause
really weird behavior if the user is running
two different versions of Android Studio Side-by-Side.
As the tracking and publishing are separated we can end up
 with:
-metrics reported for newer version
-metrics reported for older version

So instead we now track for each metric the version it was logged
as well as the version it was published with.

We can then process this on the server side.
Bug: http://b.android.com/232515
Test: Testcase added

Change-Id: I9a59af65b518166ea0c05e8b961af40325eeb8dc
(cherry picked from commit ea6f7f10ccb321012d4da61cbed41dc85e3d6e3a)
diff --git a/tracker/src/main/java/com/android/tools/analytics/UsageTracker.java b/tracker/src/main/java/com/android/tools/analytics/UsageTracker.java
index 83e0724..4e15225 100755
--- a/tracker/src/main/java/com/android/tools/analytics/UsageTracker.java
+++ b/tracker/src/main/java/com/android/tools/analytics/UsageTracker.java
@@ -22,6 +22,7 @@
 import com.android.utils.ILogger;
 import com.google.wireless.android.play.playlog.proto.ClientAnalytics;
 import com.google.wireless.android.sdk.stats.AndroidStudioEvent;
+import com.google.wireless.android.sdk.stats.ProductDetails;
 import java.io.IOException;
 import java.nio.file.Paths;
 import java.util.UUID;
@@ -48,6 +49,7 @@
 
     private int mMaxJournalSize;
     private long mMaxJournalTime;
+    private String mVersion;
 
     @VisibleForTesting protected long mStartTimeMs = sDateProvider.now().getTime();
 
@@ -90,6 +92,26 @@
         this.mMaxJournalTime = unit.toNanos(duration);
     }
 
+    /**
+     * Gets the version specified for this UsageTracker. This version when specified is used
+     * to populate the product_details.version field of AndroidStudioEvent at time of logging
+     * As the version of the product generating the event can be different of the version uploading
+     * the event.
+     */
+    @NonNull public String getVersion() {
+        return mVersion;
+    }
+
+    /**
+     * Set the version specified for this UsageTracker. This version when specified is used
+     * to populate the product_details.version field of AndroidStudioEvent at time of logging
+     * As the version of the product generating the event can be different of the version uploading
+     * the event.
+     */
+    public void setVersion(@NonNull String version) {
+        mVersion = version;
+    }
+
     /** Gets the analytics settings used by this tracker. */
     public AnalyticsSettings getAnalyticsSettings() {
         return mAnalyticsSettings;
@@ -103,6 +125,11 @@
     /** Logs usage data provided in the @{link AndroidStudioEvent}. */
     public void log(@NonNull AndroidStudioEvent.Builder studioEvent) {
         studioEvent.setStudioSessionId(sSessionId);
+
+        if (mVersion != null && !studioEvent.hasProductDetails()) {
+            studioEvent.setProductDetails(ProductDetails.newBuilder().setVersion(mVersion));
+        }
+
         long now = sDateProvider.now().getTime();
         try {
             logDetails(
diff --git a/tracker/src/test/java/com/android/tools/analytics/JournalingUsageTrackerTest.java b/tracker/src/test/java/com/android/tools/analytics/JournalingUsageTrackerTest.java
index f92623a..aa0259d 100644
--- a/tracker/src/test/java/com/android/tools/analytics/JournalingUsageTrackerTest.java
+++ b/tracker/src/test/java/com/android/tools/analytics/JournalingUsageTrackerTest.java
@@ -108,6 +108,63 @@
         }
     }
 
+
+    @Test
+    public void trackerVersionTest() throws Exception {
+        // Configure the paths to use a temp directory for reading from and writing to.
+        EnvironmentFakes.setCustomAndroidSdkHomeEnvironment(
+                testConfigDir.getRoot().toPath().toString());
+
+        try {
+            // Setup up an instance of the JournalingUsageTracker using a temp spool directory and
+            //  virtual time scheduler.
+            VirtualTimeScheduler virtualTimeScheduler = new VirtualTimeScheduler();
+            JournalingUsageTracker journalingUsageTracker =
+                    new JournalingUsageTracker(
+                            new AnalyticsSettings(),
+                            virtualTimeScheduler,
+                            testSpoolDir.getRoot().toPath());
+
+            // Set version on the usage tracker.
+            journalingUsageTracker.setVersion("1.2.3.4");
+
+            // Create a log entry and log it.
+            AndroidStudioEvent.Builder logEntry = createAndroidStudioEvent(42);
+            journalingUsageTracker.log(logEntry);
+            // Ensure this triggers an action on the scheduler and run the action
+            assertEquals(1, virtualTimeScheduler.getActionsQueued());
+            virtualTimeScheduler.advanceBy(0);
+            assertEquals(0, virtualTimeScheduler.getActionsQueued());
+
+            // The action should have written to the still locked spool file.
+            SpoolDetails beforeClose = getSpoolDetails(testSpoolDir.getRoot().toPath());
+            assertEquals(1, beforeClose.getLockedFiles().size());
+            assertEquals(0, beforeClose.getCompletedLogs().size());
+
+            // Close the usage tracker
+            journalingUsageTracker.close();
+
+            // Ensure that closing the usage tracker released the spool file, and doesn't open a new
+            // one.
+            SpoolDetails afterClose = getSpoolDetails(testSpoolDir.getRoot().toPath());
+            assertEquals(0, afterClose.getLockedFiles().size());
+            assertEquals(1, afterClose.getCompletedLogs().size());
+
+            // Check that there is exactly one spool file with one event logged and that that
+            // event has the version specified on the usage tracker set .
+            for (Map.Entry<Path, List<ClientAnalytics.LogEvent>> entry :
+                    afterClose.getCompletedLogs().entrySet()) {
+                assertEquals(1, entry.getValue().size());
+                ClientAnalytics.LogEvent logEvent = entry.getValue().get(0);
+                AndroidStudioEvent actualEvent = studioEventFromLogEvent(logEvent);
+                assertTrue(actualEvent.hasProductDetails());
+                assertEquals("1.2.3.4", actualEvent.getProductDetails().getVersion());
+            }
+        } finally {
+            EnvironmentFakes.setSystemEnvironment();
+        }
+    }
+
     @Test
     public void trackerTimeoutTest() throws Exception {
         // Configure the paths to use a temp directory for reading from and writing to.