Snap for 6511343 from ffbf04411d222e3f38a1678934915ef03af79c2b to studio-3.7-release

Change-Id: I87c27c2e5225f1530d73f9adf253108af9f0f8a5
diff --git a/shared/src/main/java/com/android/tools/analytics/AnalyticsSettings.kt b/shared/src/main/java/com/android/tools/analytics/AnalyticsSettings.kt
index 27c1d9a..d3c630b 100644
--- a/shared/src/main/java/com/android/tools/analytics/AnalyticsSettings.kt
+++ b/shared/src/main/java/com/android/tools/analytics/AnalyticsSettings.kt
@@ -46,12 +46,16 @@
 import java.util.TimeZone
 import java.util.UUID
 import java.util.concurrent.ScheduledExecutorService
+import java.util.logging.Level
+import java.util.logging.Logger
 
 /**
  * Settings related to analytics reporting. These settings are stored in
  * ~/.android/analytics.settings as a json file.
  */
 object AnalyticsSettings {
+  private val LOG = Logger.getLogger(AnalyticsSettings.javaClass.name)
+
   private const val DAYS_IN_LEAP_YEAR = 366
   private const val DAYS_IN_NON_LEAP_YEAR = 365
   private const val DAYS_TO_WAIT_FOR_REQUESTING_SENTIMENT_AGAIN = 7
@@ -61,30 +65,56 @@
     private set
 
   @JvmStatic
+  var exceptionThrown = false
+
+  @JvmStatic
   val userId: String
     get() {
-      synchronized(gate) {
-        ensureInitialized()
-        return instance?.userId ?: ""
+      return runIfAnalyticsSettingsUsable("") {
+        instance?.userId ?: ""
       }
     }
 
   @JvmStatic
   var optedIn: Boolean
     get() {
-      synchronized(gate) {
-        ensureInitialized()
-        return instance?.optedIn ?: false
+      return runIfAnalyticsSettingsUsable(false) {
+        instance?.optedIn ?: false
       }
     }
+
     set(value) {
-      synchronized(gate) {
+      runIfAnalyticsSettingsUsable(Unit) {
         instance?.apply {
           optedIn = value
         }
       }
     }
 
+  @JvmStatic
+  private fun<T> runIfAnalyticsSettingsUsable(default: T, callback: () -> T): T {
+    var throwable : Throwable? = null
+    synchronized(gate) {
+      if (exceptionThrown) {
+        return default
+      }
+      ensureInitialized()
+      try {
+        return callback()
+      } catch (t: Throwable) {
+        exceptionThrown = true
+        throwable = t
+      }
+    }
+    if (throwable != null) {
+      try {
+        LOG.log(Level.SEVERE, throwable) { "AnalyticsSettings call failed" }
+      } catch (ignored: Throwable) {
+      }
+    }
+    return default
+  }
+
   private fun ensureInitialized() {
     if (!initialized && java.lang.Boolean.getBoolean("idea.is.internal")) {
       // Android Studio Developers: If you hit this exception, you're trying to find out the status
@@ -97,22 +127,20 @@
   @JvmStatic
   val debugDisablePublishing: Boolean
     get() {
-      synchronized(gate) {
-        ensureInitialized()
-        return instance?.debugDisablePublishing ?: false
+      return runIfAnalyticsSettingsUsable(false) {
+        instance?.debugDisablePublishing ?: false
       }
     }
 
   @JvmStatic
   var lastSentimentQuestionDate: Date?
     get() {
-      synchronized(gate) {
-        ensureInitialized()
-        return instance?.lastSentimentQuestionDate
+      return runIfAnalyticsSettingsUsable(null) {
+        instance?.lastSentimentQuestionDate
       }
     }
     set(value) {
-      synchronized(gate) {
+      runIfAnalyticsSettingsUsable(Unit) {
         instance?.lastSentimentQuestionDate = value
       }
     }
@@ -120,13 +148,12 @@
   @JvmStatic
   var lastSentimentAnswerDate: Date?
     get() {
-      synchronized(gate) {
-        ensureInitialized()
-        return instance?.lastSentimentAnswerDate
+      return runIfAnalyticsSettingsUsable(null) {
+        instance?.lastSentimentAnswerDate
       }
     }
     set(value) {
-      synchronized(gate) {
+      runIfAnalyticsSettingsUsable(Unit) {
         instance?.lastSentimentAnswerDate = value
       }
     }
@@ -297,6 +324,7 @@
     synchronized(gate) {
       instance = settings
       initialized = instance != null
+      exceptionThrown = false
     }
   }
 
@@ -340,8 +368,9 @@
   @JvmStatic
   @Throws(IOException::class)
   fun saveSettings() {
-    ensureInitialized()
-    instance?.saveSettings()
+    runIfAnalyticsSettingsUsable(Unit) {
+      instance?.saveSettings()
+    }
   }
 
   /** Checks if the AnalyticsSettings object is in a valid state.  */
diff --git a/tracker/src/main/java/com/android/tools/analytics/UsageTracker.kt b/tracker/src/main/java/com/android/tools/analytics/UsageTracker.kt
index 586427e..1c3362a 100755
--- a/tracker/src/main/java/com/android/tools/analytics/UsageTracker.kt
+++ b/tracker/src/main/java/com/android/tools/analytics/UsageTracker.kt
@@ -22,6 +22,8 @@
 import java.util.*
 import java.util.concurrent.ScheduledExecutorService
 import java.util.concurrent.TimeUnit
+import java.util.logging.Level
+import java.util.logging.Logger
 
 /**
  * UsageTracker is an api to report usage of features. This data is used to improve
@@ -33,8 +35,10 @@
  */
 object UsageTracker {
   private val gate = Any()
+  private val LOG = Logger.getLogger(UsageTracker.javaClass.name)
 
   private var initialized = false
+  private var exceptionThrown = false
 
   @VisibleForTesting
   @JvmStatic
@@ -78,10 +82,7 @@
   var ideaIsInternal = false
 
   /**
-   * Gets the ide brand specified for this UsageTracker.
-   */
-  /**
-   * Set the ide brand specified for this UsageTracker.
+   * IDE brand specified for this UsageTracker.
    */
   @JvmStatic
   var ideBrand: AndroidStudioEvent.IdeBrand = AndroidStudioEvent.IdeBrand.UNKNOWN_IDE_BRAND
@@ -105,18 +106,39 @@
    */
   @JvmStatic
   fun setMaxJournalTime(duration: Long, unit: TimeUnit) {
-    synchronized(gate) {
-      ensureInitialized()
+    runIfUsageTrackerUsable {
       maxJournalTime = unit.toNanos(duration)
       writer.scheduleJournalTimeout(maxJournalTime)
     }
   }
 
+  @JvmStatic
+  private fun runIfUsageTrackerUsable(callback: () -> Unit) {
+    var throwable : Throwable? = null
+      synchronized(gate) {
+        if (exceptionThrown) {
+          return
+        }
+        ensureInitialized()
+        try {
+          callback()
+        } catch (t: Throwable) {
+          exceptionThrown = true
+          throwable = t
+        }
+      }
+    if (throwable != null) {
+      try {
+        LOG.log(Level.SEVERE, throwable) { "UsageTracker call failed" }
+      } catch (ignored: Throwable) {
+      }
+    }
+  }
+
   /** Logs usage data provided in the @{link AndroidStudioEvent}.  */
   @JvmStatic
   fun log(studioEvent: AndroidStudioEvent.Builder) {
-    synchronized(gate) {
-      ensureInitialized()
+    runIfUsageTrackerUsable {
       writer.logNow(studioEvent)
     }
   }
@@ -124,8 +146,7 @@
   /** Logs usage data provided in the @{link AndroidStudioEvent} with provided event time.  */
   @JvmStatic
   fun log(eventTimeMs: Long, studioEvent: AndroidStudioEvent.Builder) {
-    synchronized(gate) {
-      ensureInitialized()
+    runIfUsageTrackerUsable {
       writer.logAt(eventTimeMs, studioEvent)
     }
   }
@@ -214,6 +235,7 @@
     synchronized(gate) {
       isTesting = true
       initialized = true
+      exceptionThrown = false
       val old = writer
       writer = tracker
       return old
@@ -230,5 +252,6 @@
     isTesting = false
     writer = NullUsageTracker
     initialized = false
+    exceptionThrown = false
   }
 }