Added getStyleAttributes to access all Theme attributes.

ViewDebug uses getStyleAttributes to get the attributes, and then gets the
attribute name and value so that Hierarchy Viewer can display it.

Bug: 17407087
Change-Id: I3577e32ae99668383701dc908bb46db14a75c3c4
diff --git a/core/java/android/content/res/AssetManager.java b/core/java/android/content/res/AssetManager.java
index 2684e6c..e578822 100644
--- a/core/java/android/content/res/AssetManager.java
+++ b/core/java/android/content/res/AssetManager.java
@@ -773,6 +773,7 @@
     private native final String[] getArrayStringResource(int arrayRes);
     private native final int[] getArrayStringInfo(int arrayRes);
     /*package*/ native final int[] getArrayIntResource(int arrayRes);
+    /*package*/ native final int[] getStyleAttributes(int themeRes);
 
     private native final void init(boolean isSystem);
     private native final void destroy();
diff --git a/core/java/android/content/res/Resources.java b/core/java/android/content/res/Resources.java
index 31813c10..cabe228 100644
--- a/core/java/android/content/res/Resources.java
+++ b/core/java/android/content/res/Resources.java
@@ -1572,6 +1572,16 @@
         }
 
         /**
+         * Gets all of the attribute ids associated with this {@link Theme}. For debugging only.
+         *
+         * @return The int array containing attribute ids associated with this {@link Theme}.
+         * @hide
+         */
+        public int[] getAllAttributes() {
+            return mAssets.getStyleAttributes(getAppliedStyleResId());
+        }
+
+        /**
          * Returns the resources to which this theme belongs.
          *
          * @return Resources to which this theme belongs.
diff --git a/core/java/android/view/ViewDebug.java b/core/java/android/view/ViewDebug.java
index 6c66eb0..a94f973 100644
--- a/core/java/android/view/ViewDebug.java
+++ b/core/java/android/view/ViewDebug.java
@@ -26,6 +26,7 @@
 import android.os.RemoteException;
 import android.util.DisplayMetrics;
 import android.util.Log;
+import android.util.TypedValue;
 
 import java.io.BufferedOutputStream;
 import java.io.BufferedWriter;
@@ -315,6 +316,7 @@
 
     private static final String REMOTE_COMMAND_CAPTURE = "CAPTURE";
     private static final String REMOTE_COMMAND_DUMP = "DUMP";
+    private static final String REMOTE_COMMAND_DUMP_THEME = "DUMP_THEME";
     private static final String REMOTE_COMMAND_INVALIDATE = "INVALIDATE";
     private static final String REMOTE_COMMAND_REQUEST_LAYOUT = "REQUEST_LAYOUT";
     private static final String REMOTE_PROFILE = "PROFILE";
@@ -430,6 +432,8 @@
 
         if (REMOTE_COMMAND_DUMP.equalsIgnoreCase(command)) {
             dump(view, false, true, clientStream);
+        } else if (REMOTE_COMMAND_DUMP_THEME.equalsIgnoreCase(command)) {
+            dumpTheme(view, clientStream);
         } else if (REMOTE_COMMAND_CAPTURE_LAYERS.equalsIgnoreCase(command)) {
             captureLayers(view, new DataOutputStream(clientStream));
         } else {
@@ -820,6 +824,64 @@
         }
     }
 
+    /**
+     * Dumps the theme attributes from the given View.
+     * @hide
+     */
+    public static void dumpTheme(View view, OutputStream clientStream) throws IOException {
+        BufferedWriter out = null;
+        try {
+            out = new BufferedWriter(new OutputStreamWriter(clientStream, "utf-8"), 32 * 1024);
+            String[] attributes = getStyleAttributesDump(view.getContext().getResources(),
+                    view.getContext().getTheme());
+            if (attributes != null) {
+                for (int i = 0; i < attributes.length; i += 2) {
+                    if (attributes[i] != null) {
+                        out.write(attributes[i] + "\n");
+                        out.write(attributes[i + 1] + "\n");
+                    }
+                }
+            }
+            out.write("DONE.");
+            out.newLine();
+        } catch (Exception e) {
+            android.util.Log.w("View", "Problem dumping View Theme:", e);
+        } finally {
+            if (out != null) {
+                out.close();
+            }
+        }
+    }
+
+    /**
+     * Gets the style attributes from the {@link Resources.Theme}. For debugging only.
+     *
+     * @param resources Resources to resolve attributes from.
+     * @param theme Theme to dump.
+     * @return a String array containing pairs of adjacent Theme attribute data: name followed by
+     * its value.
+     *
+     * @hide
+     */
+    private static String[] getStyleAttributesDump(Resources resources, Resources.Theme theme) {
+        TypedValue outValue = new TypedValue();
+        String nullString = "null";
+        int i = 0;
+        int[] attributes = theme.getAllAttributes();
+        String[] data = new String[attributes.length * 2];
+        for (int attributeId : attributes) {
+            try {
+                data[i] = resources.getResourceName(attributeId);
+                data[i + 1] = theme.resolveAttribute(attributeId, outValue, true) ?
+                        outValue.coerceToString().toString() :  nullString;
+                i += 2;
+            } catch (Resources.NotFoundException e) {
+                // ignore resources we can't resolve
+            }
+        }
+        return data;
+    }
+
     private static View findView(ViewGroup group, String className, int hashCode) {
         if (isRequestedView(group, className, hashCode)) {
             return group;
diff --git a/core/jni/android_util_AssetManager.cpp b/core/jni/android_util_AssetManager.cpp
index 5faf03d..396f3ec 100644
--- a/core/jni/android_util_AssetManager.cpp
+++ b/core/jni/android_util_AssetManager.cpp
@@ -1888,6 +1888,37 @@
     return array;
 }
 
+static jintArray android_content_AssetManager_getStyleAttributes(JNIEnv* env, jobject clazz,
+                                                                 jint styleId)
+{
+    AssetManager* am = assetManagerForJavaObject(env, clazz);
+    if (am == NULL) {
+        return NULL;
+    }
+    const ResTable& res(am->getResources());
+
+    const ResTable::bag_entry* startOfBag;
+    const ssize_t N = res.lockBag(styleId, &startOfBag);
+    if (N < 0) {
+        return NULL;
+    }
+
+    jintArray array = env->NewIntArray(N);
+    if (array == NULL) {
+        res.unlockBag(startOfBag);
+        return NULL;
+    }
+
+    Res_value value;
+    const ResTable::bag_entry* bag = startOfBag;
+    for (size_t i=0; ((ssize_t)i)<N; i++, bag++) {
+        int resourceId = bag->map.name.ident;
+        env->SetIntArrayRegion(array, i, 1, &resourceId);
+    }
+    res.unlockBag(startOfBag);
+    return array;
+}
+
 static void android_content_AssetManager_init(JNIEnv* env, jobject clazz, jboolean isSystem)
 {
     if (isSystem) {
@@ -2038,6 +2069,8 @@
         (void*) android_content_AssetManager_getArrayStringInfo },
     { "getArrayIntResource","(I)[I",
         (void*) android_content_AssetManager_getArrayIntResource },
+    { "getStyleAttributes","(I)[I",
+        (void*) android_content_AssetManager_getStyleAttributes },
 
     // Bookkeeping.
     { "init",           "(Z)V",