Merge "Avoid strict mode violation during saveAttributeData" into mnc-dev
diff --git a/core/java/android/content/res/TypedArray.java b/core/java/android/content/res/TypedArray.java
index 1fca920..f0c3f2d 100644
--- a/core/java/android/content/res/TypedArray.java
+++ b/core/java/android/content/res/TypedArray.java
@@ -181,7 +181,26 @@
      *         not be coerced to a string.
      * @throws RuntimeException if the TypedArray has already been recycled.
      */
+    @Nullable
     public String getString(int index) {
+        return getString(index, true);
+    }
+
+    /**
+     * Returns a string representation of the value at the given index,
+     * optionally throwing a resource mismatch strict mode violation if the
+     * value must be coerced to a string.
+     *
+     * @param index the index of the attribute to retrieve
+     * @param strict {@code true} to throw a strict mode violation for string
+     *               coercion, {@code false} otherwise
+     * @return a string representation of the value at the given index, or
+     *         {@code null} if the resource could not be coerced to a string
+     * @see StrictMode#noteResourceMismatch(Object)
+     * @hide Used internally for view attribute inspection.
+     */
+    @Nullable
+    public String getString(int index, boolean strict) {
         if (mRecycled) {
             throw new RuntimeException("Cannot make calls to a recycled instance!");
         }
@@ -197,7 +216,9 @@
 
         final TypedValue v = mValue;
         if (getValueAt(index, v)) {
-            StrictMode.noteResourceMismatch(v);
+            if (strict) {
+                StrictMode.noteResourceMismatch(v);
+            }
             final CharSequence cs = v.coerceToString();
             return cs != null ? cs.toString() : null;
         }
diff --git a/core/java/android/view/View.java b/core/java/android/view/View.java
index 3962b70..81f9f99 100644
--- a/core/java/android/view/View.java
+++ b/core/java/android/view/View.java
@@ -4457,47 +4457,60 @@
 
     private static SparseArray<String> getAttributeMap() {
         if (mAttributeMap == null) {
-            mAttributeMap = new SparseArray<String>();
+            mAttributeMap = new SparseArray<>();
         }
         return mAttributeMap;
     }
 
-    private void saveAttributeData(AttributeSet attrs, TypedArray a) {
-        int length = ((attrs == null ? 0 : attrs.getAttributeCount()) + a.getIndexCount()) * 2;
-        mAttributes = new String[length];
+    private void saveAttributeData(@Nullable AttributeSet attrs, @NonNull TypedArray t) {
+        final int attrsCount = attrs == null ? 0 : attrs.getAttributeCount();
+        final int indexCount = t.getIndexCount();
+        final String[] attributes = new String[(attrsCount + indexCount) * 2];
 
         int i = 0;
-        if (attrs != null) {
-            for (i = 0; i < attrs.getAttributeCount(); i += 2) {
-                mAttributes[i] = attrs.getAttributeName(i);
-                mAttributes[i + 1] = attrs.getAttributeValue(i);
-            }
 
+        // Store raw XML attributes.
+        for (int j = 0; j < attrsCount; ++j) {
+            attributes[i] = attrs.getAttributeName(j);
+            attributes[i + 1] = attrs.getAttributeValue(j);
+            i += 2;
         }
 
-        SparseArray<String> attributeMap = getAttributeMap();
-        for (int j = 0; j < a.length(); ++j) {
-            if (a.hasValue(j)) {
+        // Store resolved styleable attributes.
+        final Resources res = t.getResources();
+        final SparseArray<String> attributeMap = getAttributeMap();
+        for (int j = 0; j < indexCount; ++j) {
+            final int index = t.getIndex(j);
+            if (!t.hasValueOrEmpty(index)) {
+                // Value is undefined. Skip it.
+                continue;
+            }
+
+            final int resourceId = t.getResourceId(index, 0);
+            if (resourceId == 0) {
+                // Value is not a reference. Skip it.
+                continue;
+            }
+
+            String resourceName = attributeMap.get(resourceId);
+            if (resourceName == null) {
                 try {
-                    int resourceId = a.getResourceId(j, 0);
-                    if (resourceId == 0) {
-                        continue;
-                    }
-
-                    String resourceName = attributeMap.get(resourceId);
-                    if (resourceName == null) {
-                        resourceName = a.getResources().getResourceName(resourceId);
-                        attributeMap.put(resourceId, resourceName);
-                    }
-
-                    mAttributes[i] = resourceName;
-                    mAttributes[i + 1] = a.getText(j).toString();
-                    i += 2;
+                    resourceName = res.getResourceName(resourceId);
                 } catch (Resources.NotFoundException e) {
-                    // if we can't get the resource name, we just ignore it
+                    resourceName = "0x" + Integer.toHexString(resourceId);
                 }
+                attributeMap.put(resourceId, resourceName);
             }
+
+            attributes[i] = resourceName;
+            attributes[i + 1] = t.getString(index, false);
+            i += 2;
         }
+
+        // Trim to fit contents.
+        final String[] trimmed = new String[i];
+        System.arraycopy(attributes, 0, trimmed, 0, i);
+        mAttributes = trimmed;
     }
 
     public String toString() {