Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 7ff94367 authored by Android (Google) Code Review's avatar Android (Google) Code Review
Browse files

Merge change 911 into donut

* changes:
  Debug: Add setPropertiesOn(), and @hide it for now
parents 5c6f93f1 1ce5bd34
Loading
Loading
Loading
Loading
+203 −1
Original line number Diff line number Diff line
@@ -16,10 +16,20 @@

package android.os;

import com.android.internal.util.TypedProperties;

import android.util.Config;
import android.util.Log;

import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.FileReader;
import java.io.IOException;
import java.io.OutputStreamWriter;
import java.io.PrintWriter;
import java.io.Reader;
import java.lang.reflect.Field;
import java.lang.reflect.Modifier;

import org.apache.harmony.dalvik.ddmc.Chunk;
import org.apache.harmony.dalvik.ddmc.ChunkHandler;
@@ -721,5 +731,197 @@ href="{@docRoot}guide/developing/tools/traceview.html">Traceview: A Graphical Lo
            count += mCounts[Opcodes.OP_INVOKE_SUPER_QUICK_RANGE];
            return count;
        }
    };
    }


    /**
     * A Map of typed debug properties.
     */
    private static final TypedProperties debugProperties;

    /*
     * Load the debug properties from the standard files into debugProperties.
     */
    static {
        if (Config.DEBUG) {
            final String TAG = "DebugProperties";
            final String[] files = { "/system/debug.prop", "/debug.prop", "/data/debug.prop" };
            final TypedProperties tp = new TypedProperties();

            // Read the properties from each of the files, if present.
            for (String file: files) {
                Reader r;
                try {
                    r = new FileReader(file);
                } catch (FileNotFoundException ex) {
                    // It's ok if a file is missing.
                    continue;
                }

                Exception failure = null;
                try {
                    tp.load(r);
                } catch (IOException ex) {
                    failure = ex;
                } catch (TypedProperties.ParseException ex) {
                    failure = ex;
                }
                if (failure != null) {
                    throw new RuntimeException("Problem loading " + file, failure);
                }
            }

            debugProperties = tp.isEmpty() ? null : tp;
        } else {
            debugProperties = null;
        }
    }


    /**
     * Returns true if the type of the field matches the specified class.
     * Handles the case where the class is, e.g., java.lang.Boolean, but
     * the field is of the primitive "boolean" type.  Also handles all of
     * the java.lang.Number subclasses.
     */
    private static boolean fieldTypeMatches(Field field, Class<?> cl) {
        Class<?> fieldClass = field.getType();
        if (fieldClass == cl) {
            return true;
        }
        Field primitiveTypeField;
        try {
            /* All of the classes we care about (Boolean, Integer, etc.)
             * have a Class field called "TYPE" that points to the corresponding
             * primitive class.
             */
            primitiveTypeField = cl.getField("TYPE");
        } catch (NoSuchFieldException ex) {
            return false;
        }
        try {
            return fieldClass == (Class<?>)primitiveTypeField.get(null);
        } catch (IllegalAccessException ex) {
            return false;
        }
    }


    /**
     * Looks up the property that corresponds to the field, and sets the field's value
     * if the types match.
     */
    private static void modifyFieldIfSet(final Field field, final String propertyName) {
        if (field.getType() == java.lang.String.class) {
            int stringInfo = debugProperties.getStringInfo(propertyName);
            switch (stringInfo) {
            case TypedProperties.STRING_SET:
                // Handle as usual below.
                break;
            case TypedProperties.STRING_NULL:
                try {
                    field.set(null, null);  // null object for static fields; null string
                } catch (IllegalAccessException ex) {
                    throw new IllegalArgumentException(
                        "Cannot set field for " + propertyName, ex);
                }
                return;
            case TypedProperties.STRING_NOT_SET:
                return;
            case TypedProperties.STRING_TYPE_MISMATCH:
                throw new IllegalArgumentException(
                    "Type of " + propertyName + " " +
                    " does not match field type (" + field.getType() + ")");
            default:
                throw new IllegalStateException(
                    "Unexpected getStringInfo(" + propertyName + ") return value " +
                    stringInfo);
            }
        }
        Object value = debugProperties.get(propertyName);
        if (value != null) {
            if (!fieldTypeMatches(field, value.getClass())) {
                throw new IllegalArgumentException(
                    "Type of " + propertyName + " (" + value.getClass() + ") " +
                    " does not match field type (" + field.getType() + ")");
            }
            try {
                field.set(null, value);  // null object for static fields
            } catch (IllegalAccessException ex) {
                throw new IllegalArgumentException(
                    "Cannot set field for " + propertyName, ex);
            }
        }
    }


    /**
     * Reflectively sets static fields of a class based on internal debugging
     * properties.  This method is a no-op if android.util.Config.DEBUG is
     * false.
     * <p>
     * <strong>NOTE TO APPLICATION DEVELOPERS</strong>: Config.DEBUG will
     * always be false in release builds.  This API is typically only useful
     * for platform developers.
     * </p>
     * Class setup: define a class whose only fields are non-final, static
     * primitive types (except for "char") or Strings.  In a static block
     * after the field definitions/initializations, pass the class to
     * this method, Debug.setPropertiesOn().  Example:
     * <pre>
     * package com.example;
     *
     * import android.os.Debug;
     *
     * public class MyDebugVars {
     *    public static String s = "a string";
     *    public static String s2 = "second string";
     *    public static String ns = null;
     *    public static boolean b = false;
     *    public static int i = 5;
     *    public static float f = 0.1f;
     *    public static double d = 0.5d;
     *
     *    // This MUST appear AFTER all fields are defined and initialized!
     *    static {
     *        Debug.setPropertiesOn(MyDebugVars.class);
     *    }
     * }
     * </pre>
     * setPropertiesOn() may override the value of any field in the class based
     * on internal properties that are fixed at boot time.
     * <p>
     * These properties are only set during platform debugging, and are not
     * meant to be used as a general-purpose properties store.
     *
     * {@hide}
     *
     * @param cl The class to (possibly) modify
     * @throws IllegalArgumentException if any fields are final or non-static,
     *         or if the type of the field does not match the type of
     *         the internal debugging property value.
     */
    public static void setPropertiesOn(Class<?> cl) {
        if (Config.DEBUG) {
            if (debugProperties != null) {
                /* Only look for fields declared directly by the class,
                 * so we don't mysteriously change static fields in superclasses.
                 */
                for (Field field : cl.getDeclaredFields()) {
                    final String propertyName = cl.getName() + "." + field.getName();
                    boolean isStatic = Modifier.isStatic(field.getModifiers());
                    boolean isFinal = Modifier.isFinal(field.getModifiers());
                    if (!isStatic || isFinal) {
                        throw new IllegalArgumentException(propertyName +
                            " must be static and non-final");
                    }
                    modifyFieldIfSet(field, propertyName);
                }
            }
        } else {
            Log.w("android.os.Debug",
                  "setPropertiesOn(" + (cl == null ? "null" : cl.getName()) +
                  ") called in non-DEBUG build");
        }
    }
}