Merge "Log resource api times to statsd"
diff --git a/core/java/android/content/res/ResourceTimer.java b/core/java/android/content/res/ResourceTimer.java
index 13f0b72..d51f64c 100644
--- a/core/java/android/content/res/ResourceTimer.java
+++ b/core/java/android/content/res/ResourceTimer.java
@@ -19,6 +19,7 @@
 import android.annotation.NonNull;
 import android.annotation.Nullable;
 
+import android.app.AppProtoEnums;
 import android.os.Handler;
 import android.os.Looper;
 import android.os.Message;
@@ -30,6 +31,7 @@
 
 import com.android.internal.annotations.GuardedBy;
 import com.android.internal.util.FastPrintWriter;
+import com.android.internal.util.FrameworkStatsLog;
 
 import java.io.FileOutputStream;
 import java.io.PrintWriter;
@@ -111,6 +113,14 @@
     private static Config sConfig;
 
     /**
+     * This array contains the statsd enum associated with each timer entry.  A value of NONE (0)
+     * means that the entry should not be logged to statsd.  (This would be the case for timers
+     * that are created for temporary debugging.)
+     */
+    @GuardedBy("sLock")
+    private static int[] sApiMap;
+
+    /**
      * A singleton Summary object that is refilled from the native side.  The length of the array
      * is the number of timers that can be fetched.  nativeGetTimers() will fill the array to the
      * smaller of the length of the array or the actual number of timers in the runtime.  The
@@ -165,6 +175,19 @@
                 sTimers[i].percentile = new int[sConfig.maxBuckets];
                 sTimers[i].largest = new int[sConfig.maxLargest];
             }
+            // Map the values returned from the runtime to statsd enumerals  The runtime may
+            // return timers that are not meant to be logged via statsd.  Such timers are mapped
+            // to RESOURCE_API_NONE.
+            sApiMap = new int[sConfig.maxTimer];
+            for (int i = 0; i < sApiMap.length; i++) {
+                if (sConfig.timers[i].equals("GetResourceValue")) {
+                    sApiMap[i] = AppProtoEnums.RESOURCE_API_GET_VALUE;
+                } else if (sConfig.timers[i].equals("RetrieveAttributes")) {
+                    sApiMap[i] = AppProtoEnums.RESOURCE_API_RETRIEVE_ATTRIBUTES;
+                } else {
+                    sApiMap[i] = AppProtoEnums.RESOURCE_API_NONE;
+                }
+            }
 
             sCurrentPoint = 0;
             startTimer();
@@ -194,7 +217,9 @@
             delay = sPublicationPoints[sCurrentPoint];
         } else {
             // Repeat with the final publication point.
-            delay = sCurrentPoint * sPublicationPoints[sPublicationPoints.length - 1];
+            final long repeated = sPublicationPoints[sPublicationPoints.length - 1];
+            final int prelude = sPublicationPoints.length - 1;
+            delay = (sCurrentPoint - prelude) * repeated;
         }
         // Convert minutes to milliseconds.
         delay *= 60 * 1000;
@@ -223,10 +248,19 @@
         update(true);
         // Log the number of records read.  This happens a few times a day.
         for (int i = 0; i < sTimers.length; i++) {
-            if (sTimers[i].count > 0) {
+            var timer = sTimers[i];
+            if (timer.count > 0) {
                 Log.i(TAG, TextUtils.formatSimple("%s count=%d pvalues=%s",
-                                sConfig.timers[i], sTimers[i].count,
-                                packedString(sTimers[i].percentile)));
+                                sConfig.timers[i], timer.count, packedString(timer.percentile)));
+                if (sApiMap[i] != AppProtoEnums.RESOURCE_API_NONE) {
+                    FrameworkStatsLog.write(FrameworkStatsLog.RESOURCE_API_INFO,
+                            sApiMap[i],
+                            timer.count, timer.total,
+                            timer.percentile[0], timer.percentile[1],
+                            timer.percentile[2], timer.percentile[3],
+                            timer.largest[0], timer.largest[1], timer.largest[2],
+                            timer.largest[3], timer.largest[4]);
+                }
             }
         }
         sCurrentPoint++;
diff --git a/core/jni/android_util_AssetManager.cpp b/core/jni/android_util_AssetManager.cpp
index b5a78b0..b60ec9f 100644
--- a/core/jni/android_util_AssetManager.cpp
+++ b/core/jni/android_util_AssetManager.cpp
@@ -631,7 +631,7 @@
                                    jshort density, jobject typed_value,
                                    jboolean resolve_references) {
   ScopedLock<AssetManager2> assetmanager(AssetManagerFromLong(ptr));
-  ResourceTimer _tag(ResourceTimer::Counter::GetResourceValue);
+  ResourceTimer _timer(ResourceTimer::Counter::GetResourceValue);
   auto value = assetmanager->GetResource(static_cast<uint32_t>(resid), false /*may_be_bag*/,
                                          static_cast<uint16_t>(density));
   if (!value.has_value()) {
@@ -1234,7 +1234,7 @@
   }
 
   ScopedLock<AssetManager2> assetmanager(AssetManagerFromLong(ptr));
-  ResourceTimer _tag(ResourceTimer::Counter::RetrieveAttributes);
+  ResourceTimer _timer(ResourceTimer::Counter::RetrieveAttributes);
   ResXMLParser* xml_parser = reinterpret_cast<ResXMLParser*>(xml_parser_ptr);
   auto result =
           RetrieveAttributes(assetmanager.get(), xml_parser, reinterpret_cast<uint32_t*>(attrs),