Added more systrace markers and made LottieTrace multi-thread compatible (#2275)

diff --git a/lottie/src/main/java/com/airbnb/lottie/L.java b/lottie/src/main/java/com/airbnb/lottie/L.java
index 8bfe62c..a27d7da 100644
--- a/lottie/src/main/java/com/airbnb/lottie/L.java
+++ b/lottie/src/main/java/com/airbnb/lottie/L.java
@@ -12,6 +12,7 @@
 import com.airbnb.lottie.network.LottieNetworkFetcher;
 import com.airbnb.lottie.network.NetworkCache;
 import com.airbnb.lottie.network.NetworkFetcher;
+import com.airbnb.lottie.utils.LottieTrace;
 
 import java.io.File;
 
@@ -21,20 +22,16 @@
   public static boolean DBG = false;
   public static final String TAG = "LOTTIE";
 
-  private static final int MAX_DEPTH = 20;
   private static boolean traceEnabled = false;
   private static boolean networkCacheEnabled = true;
   private static boolean disablePathInterpolatorCache = true;
-  private static String[] sections;
-  private static long[] startTimeNs;
-  private static int traceDepth = 0;
-  private static int depthPastMaxDepth = 0;
 
   private static LottieNetworkFetcher fetcher;
   private static LottieNetworkCacheProvider cacheProvider;
 
   private static volatile NetworkFetcher networkFetcher;
   private static volatile NetworkCache networkCache;
+  private static ThreadLocal<LottieTrace> lottieTrace;
 
   private L() {
   }
@@ -44,9 +41,8 @@
       return;
     }
     traceEnabled = enabled;
-    if (traceEnabled) {
-      sections = new String[MAX_DEPTH];
-      startTimeNs = new long[MAX_DEPTH];
+    if (traceEnabled && lottieTrace == null) {
+      lottieTrace = new ThreadLocal<>();
     }
   }
 
@@ -58,34 +54,23 @@
     if (!traceEnabled) {
       return;
     }
-    if (traceDepth == MAX_DEPTH) {
-      depthPastMaxDepth++;
-      return;
-    }
-    sections[traceDepth] = section;
-    startTimeNs[traceDepth] = System.nanoTime();
-    TraceCompat.beginSection(section);
-    traceDepth++;
+    getTrace().beginSection(section);
   }
 
   public static float endSection(String section) {
-    if (depthPastMaxDepth > 0) {
-      depthPastMaxDepth--;
-      return 0;
-    }
     if (!traceEnabled) {
       return 0;
     }
-    traceDepth--;
-    if (traceDepth == -1) {
-      throw new IllegalStateException("Can't end trace section. There are none.");
+    return getTrace().endSection(section);
+  }
+
+  private static LottieTrace getTrace() {
+    LottieTrace trace = lottieTrace.get();
+    if (trace == null) {
+      trace = new LottieTrace();
+      lottieTrace.set(trace);
     }
-    if (!section.equals(sections[traceDepth])) {
-      throw new IllegalStateException("Unbalanced trace call " + section +
-          ". Expected " + sections[traceDepth] + ".");
-    }
-    TraceCompat.endSection();
-    return (System.nanoTime() - startTimeNs[traceDepth]) / 1000000f;
+    return trace;
   }
 
   public static void setFetcher(LottieNetworkFetcher customFetcher) {
@@ -121,11 +106,8 @@
       synchronized (NetworkCache.class) {
         local = networkCache;
         if (local == null) {
-          networkCache = local = new NetworkCache(cacheProvider != null ? cacheProvider : new LottieNetworkCacheProvider() {
-            @Override @NonNull public File getCacheDir() {
-              return new File(appContext.getCacheDir(), "lottie_network_cache");
-            }
-          });
+          networkCache = local = new NetworkCache(cacheProvider != null ? cacheProvider :
+              () -> new File(appContext.getCacheDir(), "lottie_network_cache"));
         }
       }
     }
diff --git a/lottie/src/main/java/com/airbnb/lottie/animation/keyframe/BaseKeyframeAnimation.java b/lottie/src/main/java/com/airbnb/lottie/animation/keyframe/BaseKeyframeAnimation.java
index 8f13143..f664415 100644
--- a/lottie/src/main/java/com/airbnb/lottie/animation/keyframe/BaseKeyframeAnimation.java
+++ b/lottie/src/main/java/com/airbnb/lottie/animation/keyframe/BaseKeyframeAnimation.java
@@ -46,7 +46,9 @@
   }
 
   public void setProgress(@FloatRange(from = 0f, to = 1f) float progress) {
+    L.beginSection("BaseKeyframeAnimation#setProgress");
     if (keyframesWrapper.isEmpty()) {
+      L.endSection("BaseKeyframeAnimation#setProgress");
       return;
     }
     if (progress < getStartDelayProgress()) {
@@ -56,18 +58,22 @@
     }
 
     if (progress == this.progress) {
+      L.endSection("BaseKeyframeAnimation#setProgress");
       return;
     }
     this.progress = progress;
     if (keyframesWrapper.isValueChanged(progress)) {
       notifyListeners();
     }
+    L.endSection("BaseKeyframeAnimation#setProgress");
   }
 
   public void notifyListeners() {
+    L.beginSection("BaseKeyframeAnimation#notifyListeners");
     for (int i = 0; i < listeners.size(); i++) {
       listeners.get(i).onValueChanged();
     }
+    L.endSection("BaseKeyframeAnimation#notifyListeners");
   }
 
   protected Keyframe<K> getCurrentKeyframe() {
diff --git a/lottie/src/main/java/com/airbnb/lottie/model/layer/BaseLayer.java b/lottie/src/main/java/com/airbnb/lottie/model/layer/BaseLayer.java
index 187cfd1..5775c18 100644
--- a/lottie/src/main/java/com/airbnb/lottie/model/layer/BaseLayer.java
+++ b/lottie/src/main/java/com/airbnb/lottie/model/layer/BaseLayer.java
@@ -571,22 +571,34 @@
   }
 
   void setProgress(@FloatRange(from = 0f, to = 1f) float progress) {
+    L.beginSection("BaseLayer#setProgress");
     // Time stretch should not be applied to the layer transform.
+    L.beginSection("BaseLayer#setProgress.transform");
     transform.setProgress(progress);
+    L.endSection("BaseLayer#setProgress.transform");
     if (mask != null) {
+      L.beginSection("BaseLayer#setProgress.mask");
       for (int i = 0; i < mask.getMaskAnimations().size(); i++) {
         mask.getMaskAnimations().get(i).setProgress(progress);
       }
+      L.endSection("BaseLayer#setProgress.mask");
     }
     if (inOutAnimation != null) {
+      L.beginSection("BaseLayer#setProgress.inout");
       inOutAnimation.setProgress(progress);
+      L.endSection("BaseLayer#setProgress.inout");
     }
     if (matteLayer != null) {
+      L.beginSection("BaseLayer#setProgress.matte");
       matteLayer.setProgress(progress);
+      L.endSection("BaseLayer#setProgress.matte");
     }
+    L.beginSection("BaseLayer#setProgress.animations." + animations.size());
     for (int i = 0; i < animations.size(); i++) {
       animations.get(i).setProgress(progress);
     }
+    L.endSection("BaseLayer#setProgress.animations." + animations.size());
+    L.endSection("BaseLayer#setProgress");
   }
 
   private void buildParentLayerListIfNeeded() {
diff --git a/lottie/src/main/java/com/airbnb/lottie/model/layer/CompositionLayer.java b/lottie/src/main/java/com/airbnb/lottie/model/layer/CompositionLayer.java
index 6ddd5f7..113c80d 100644
--- a/lottie/src/main/java/com/airbnb/lottie/model/layer/CompositionLayer.java
+++ b/lottie/src/main/java/com/airbnb/lottie/model/layer/CompositionLayer.java
@@ -4,6 +4,7 @@
 import android.graphics.Matrix;
 import android.graphics.Paint;
 import android.graphics.RectF;
+import android.util.Log;
 
 import androidx.annotation.FloatRange;
 import androidx.annotation.Nullable;
@@ -142,6 +143,7 @@
   }
 
   @Override public void setProgress(@FloatRange(from = 0f, to = 1f) float progress) {
+    L.beginSection("CompositionLayer#setProgress");
     super.setProgress(progress);
     if (timeRemapping != null) {
       // The duration has 0.01 frame offset to show end of animation properly.
@@ -162,6 +164,7 @@
     for (int i = layers.size() - 1; i >= 0; i--) {
       layers.get(i).setProgress(progress);
     }
+    L.endSection("CompositionLayer#setProgress");
   }
 
   public boolean hasMasks() {
diff --git a/lottie/src/main/java/com/airbnb/lottie/utils/LottieTrace.java b/lottie/src/main/java/com/airbnb/lottie/utils/LottieTrace.java
new file mode 100644
index 0000000..fcac145
--- /dev/null
+++ b/lottie/src/main/java/com/airbnb/lottie/utils/LottieTrace.java
@@ -0,0 +1,42 @@
+package com.airbnb.lottie.utils;
+
+import androidx.core.os.TraceCompat;
+
+public class LottieTrace {
+  private static final int MAX_DEPTH = 20;
+
+  private final String[] sections = new String[MAX_DEPTH];
+  private final long[] startTimeNs = new long[MAX_DEPTH];
+  private int traceDepth = 0;
+  private int depthPastMaxDepth = 0;
+
+  public void beginSection(String section) {
+    if (traceDepth == MAX_DEPTH) {
+      depthPastMaxDepth++;
+      return;
+    }
+    sections[traceDepth] = section;
+    startTimeNs[traceDepth] = System.nanoTime();
+    //noinspection deprecation
+    TraceCompat.beginSection(section);
+    traceDepth++;
+  }
+
+  public float endSection(String section) {
+    if (depthPastMaxDepth > 0) {
+      depthPastMaxDepth--;
+      return 0;
+    }
+    traceDepth--;
+    if (traceDepth == -1) {
+      throw new IllegalStateException("Can't end trace section. There are none.");
+    }
+    if (!section.equals(sections[traceDepth])) {
+      throw new IllegalStateException("Unbalanced trace call " + section +
+          ". Expected " + sections[traceDepth] + ".");
+    }
+    //noinspection deprecation
+    TraceCompat.endSection();
+    return (System.nanoTime() - startTimeNs[traceDepth]) / 1000000f;
+  }
+}