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

Commit 95fc70bc authored by TreeHugger Robot's avatar TreeHugger Robot Committed by Android (Google) Code Review
Browse files

Merge "Changing RemoteViews to using MethodHandles instead of relection"

parents 44bc284d 271e3227
Loading
Loading
Loading
Loading
+90 −97
Original line number Original line Diff line number Diff line
@@ -73,6 +73,9 @@ import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
import java.lang.annotation.Target;
import java.lang.invoke.MethodHandle;
import java.lang.invoke.MethodHandles;
import java.lang.invoke.MethodType;
import java.lang.reflect.Method;
import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.HashMap;
@@ -188,17 +191,12 @@ public class RemoteViews implements Parcelable, Filter {


    private static final OnClickHandler DEFAULT_ON_CLICK_HANDLER = new OnClickHandler();
    private static final OnClickHandler DEFAULT_ON_CLICK_HANDLER = new OnClickHandler();


    private static final Object[] sMethodsLock = new Object[0];
    private static final ArrayMap<MethodKey, MethodArgs> sMethods = new ArrayMap<>();
    private static final ArrayMap<Class<? extends View>, ArrayMap<MutablePair<String, Class<?>>, Method>> sMethods =
            new ArrayMap<Class<? extends View>, ArrayMap<MutablePair<String, Class<?>>, Method>>();
    private static final ArrayMap<Method, Method> sAsyncMethods = new ArrayMap<>();


    private static final ThreadLocal<Object[]> sInvokeArgsTls = new ThreadLocal<Object[]>() {
    /**
        @Override
     * This key is used to perform lookups in sMethods without causing allocations.
        protected Object[] initialValue() {
     */
            return new Object[1];
    private static final MethodKey sLookupKey = new MethodKey();
        }
    };


    /**
    /**
     * @hide
     * @hide
@@ -255,37 +253,47 @@ public class RemoteViews implements Parcelable, Filter {
    }
    }


    /**
    /**
     * Handle with care!
     * Stores information related to reflection method lookup.
     */
     */
    static class MutablePair<F, S> {
    static class MethodKey {
        F first;
        public Class targetClass;
        S second;
        public Class paramClass;

        public String methodName;
        MutablePair(F first, S second) {
            this.first = first;
            this.second = second;
        }


        @Override
        @Override
        public boolean equals(Object o) {
        public boolean equals(Object o) {
            if (!(o instanceof MutablePair)) {
            if (!(o instanceof MethodKey)) {
                return false;
                return false;
            }
            }
            MutablePair<?, ?> p = (MutablePair<?, ?>) o;
            MethodKey p = (MethodKey) o;
            return Objects.equal(p.first, first) && Objects.equal(p.second, second);
            return Objects.equal(p.targetClass, targetClass)
                    && Objects.equal(p.paramClass, paramClass)
                    && Objects.equal(p.methodName, methodName);
        }
        }


        @Override
        @Override
        public int hashCode() {
        public int hashCode() {
            return (first == null ? 0 : first.hashCode()) ^ (second == null ? 0 : second.hashCode());
            return Objects.hashCode(targetClass) ^ Objects.hashCode(paramClass)
                    ^ Objects.hashCode(methodName);
        }

        public void set(Class targetClass, Class paramClass, String methodName) {
            this.targetClass = targetClass;
            this.paramClass = paramClass;
            this.methodName = methodName;
        }
        }
    }
    }



    /**
    /**
     * This pair is used to perform lookups in sMethods without causing allocations.
     * Stores information related to reflection method lookup result.
     */
     */
    private final MutablePair<String, Class<?>> mPair =
    static class MethodArgs {
            new MutablePair<String, Class<?>>(null, null);
        public MethodHandle syncMethod;
        public MethodHandle asyncMethod;
        public String asyncMethodName;
    }



    /**
    /**
     * This annotation indicates that a subclass of View is allowed to be used
     * This annotation indicates that a subclass of View is allowed to be used
@@ -307,6 +315,12 @@ public class RemoteViews implements Parcelable, Filter {
        public ActionException(String message) {
        public ActionException(String message) {
            super(message);
            super(message);
        }
        }
        /**
         * @hide
         */
        public ActionException(Throwable t) {
            super(t);
        }
    }
    }


    /** @hide */
    /** @hide */
@@ -943,73 +957,66 @@ public class RemoteViews implements Parcelable, Filter {
        return rect;
        return rect;
    }
    }


    private Method getMethod(View view, String methodName, Class<?> paramType) {
    private MethodHandle getMethod(View view, String methodName, Class<?> paramType,
        Method method;
            boolean async) {
        MethodArgs result;
        Class<? extends View> klass = view.getClass();
        Class<? extends View> klass = view.getClass();


        synchronized (sMethodsLock) {
        synchronized (sMethods) {
            ArrayMap<MutablePair<String, Class<?>>, Method> methods = sMethods.get(klass);
            // The key is defined by the view class, param class and method name.
            if (methods == null) {
            sLookupKey.set(klass, paramType, methodName);
                methods = new ArrayMap<MutablePair<String, Class<?>>, Method>();
            result = sMethods.get(sLookupKey);
                sMethods.put(klass, methods);
            }


            mPair.first = methodName;
            if (result == null) {
            mPair.second = paramType;
                Method method;

            method = methods.get(mPair);
            if (method == null) {
                try {
                try {
                    if (paramType == null) {
                    if (paramType == null) {
                        method = klass.getMethod(methodName);
                        method = klass.getMethod(methodName);
                    } else {
                    } else {
                        method = klass.getMethod(methodName, paramType);
                        method = klass.getMethod(methodName, paramType);
                    }
                    }
                } catch (NoSuchMethodException ex) {
                    throw new ActionException("view: " + klass.getName() + " doesn't have method: "
                            + methodName + getParameters(paramType));
                }

                    if (!method.isAnnotationPresent(RemotableViewMethod.class)) {
                    if (!method.isAnnotationPresent(RemotableViewMethod.class)) {
                        throw new ActionException("view: " + klass.getName()
                        throw new ActionException("view: " + klass.getName()
                                + " can't use method with RemoteViews: "
                                + " can't use method with RemoteViews: "
                                + methodName + getParameters(paramType));
                                + methodName + getParameters(paramType));
                    }
                    }


                methods.put(new MutablePair<String, Class<?>>(methodName, paramType), method);
                    result = new MethodArgs();
            }
                    result.syncMethod = MethodHandles.publicLookup().unreflect(method);
                    result.asyncMethodName =
                            method.getAnnotation(RemotableViewMethod.class).asyncImpl();
                } catch (NoSuchMethodException | IllegalAccessException ex) {
                    throw new ActionException("view: " + klass.getName() + " doesn't have method: "
                            + methodName + getParameters(paramType));
                }
                }


        return method;
                MethodKey key = new MethodKey();
                key.set(klass, paramType, methodName);
                sMethods.put(key, result);
            }
            }


    /**
            if (!async) {
     * @return the async implementation of the provided method.
                return result.syncMethod;
     */
    private Method getAsyncMethod(Method method) {
        synchronized (sAsyncMethods) {
            int valueIndex = sAsyncMethods.indexOfKey(method);
            if (valueIndex >= 0) {
                return sAsyncMethods.valueAt(valueIndex);
            }
            }

            // Check this so see if async method is implemented or not.
            RemotableViewMethod annotation = method.getAnnotation(RemotableViewMethod.class);
            if (result.asyncMethodName.isEmpty()) {
            Method asyncMethod = null;
                return null;
            if (!annotation.asyncImpl().isEmpty()) {
                try {
                    asyncMethod = method.getDeclaringClass()
                            .getMethod(annotation.asyncImpl(), method.getParameterTypes());
                    if (!asyncMethod.getReturnType().equals(Runnable.class)) {
                        throw new ActionException("Async implementation for " + method.getName() +
                            " does not return a Runnable");
            }
            }
                } catch (NoSuchMethodException ex) {
            // Async method is lazily loaded. If it is not yet loaded, load now.
                    throw new ActionException("Async implementation declared but not defined for " +
            if (result.asyncMethod == null) {
                            method.getName());
                MethodType asyncType = result.syncMethod.type()
                        .dropParameterTypes(0, 1).changeReturnType(Runnable.class);
                try {
                    result.asyncMethod = MethodHandles.publicLookup().findVirtual(
                            klass, result.asyncMethodName, asyncType);
                } catch (NoSuchMethodException | IllegalAccessException ex) {
                    throw new ActionException("Async implementation declared as "
                            + result.asyncMethodName + " but not defined for " + methodName
                            + ": public Runnable " + result.asyncMethodName + " ("
                            + TextUtils.join(",", asyncType.parameterArray()) + ")");
                }
                }
            }
            }
            sAsyncMethods.put(method, asyncMethod);
            return result.asyncMethod;
            return asyncMethod;
        }
        }
    }
    }


@@ -1018,12 +1025,6 @@ public class RemoteViews implements Parcelable, Filter {
        return "(" + paramType + ")";
        return "(" + paramType + ")";
    }
    }


    private static Object[] wrapArg(Object value) {
        Object[] args = sInvokeArgsTls.get();
        args[0] = value;
        return args;
    }

    /**
    /**
     * Equivalent to calling a combination of {@link Drawable#setAlpha(int)},
     * Equivalent to calling a combination of {@link Drawable#setAlpha(int)},
     * {@link Drawable#setColorFilter(int, android.graphics.PorterDuff.Mode)},
     * {@link Drawable#setColorFilter(int, android.graphics.PorterDuff.Mode)},
@@ -1140,10 +1141,8 @@ public class RemoteViews implements Parcelable, Filter {
            if (view == null) return;
            if (view == null) return;


            try {
            try {
                getMethod(view, this.methodName, null).invoke(view);
                getMethod(view, this.methodName, null, false /* async */).invoke(view);
            } catch (ActionException e) {
            } catch (Throwable ex) {
                throw e;
            } catch (Exception ex) {
                throw new ActionException(ex);
                throw new ActionException(ex);
            }
            }
        }
        }
@@ -1516,12 +1515,9 @@ public class RemoteViews implements Parcelable, Filter {
            if (param == null) {
            if (param == null) {
                throw new ActionException("bad type: " + this.type);
                throw new ActionException("bad type: " + this.type);
            }
            }

            try {
            try {
                getMethod(view, this.methodName, param).invoke(view, wrapArg(this.value));
                getMethod(view, this.methodName, param, false /* async */).invoke(view, this.value);
            } catch (ActionException e) {
            } catch (Throwable ex) {
                throw e;
            } catch (Exception ex) {
                throw new ActionException(ex);
                throw new ActionException(ex);
            }
            }
        }
        }
@@ -1537,11 +1533,10 @@ public class RemoteViews implements Parcelable, Filter {
            }
            }


            try {
            try {
                Method method = getMethod(view, this.methodName, param);
                MethodHandle method = getMethod(view, this.methodName, param, true /* async */);
                Method asyncMethod = getAsyncMethod(method);


                if (asyncMethod != null) {
                if (method != null) {
                    Runnable endAction = (Runnable) asyncMethod.invoke(view, wrapArg(this.value));
                    Runnable endAction = (Runnable) method.invoke(view, this.value);
                    if (endAction == null) {
                    if (endAction == null) {
                        return ACTION_NOOP;
                        return ACTION_NOOP;
                    } else {
                    } else {
@@ -1555,9 +1550,7 @@ public class RemoteViews implements Parcelable, Filter {
                        return new RunnableAction(endAction);
                        return new RunnableAction(endAction);
                    }
                    }
                }
                }
            } catch (ActionException e) {
            } catch (Throwable ex) {
                throw e;
            } catch (Exception ex) {
                throw new ActionException(ex);
                throw new ActionException(ex);
            }
            }


@@ -2672,7 +2665,7 @@ public class RemoteViews implements Parcelable, Filter {
     * given {@link RemoteViews}.
     * given {@link RemoteViews}.
     *
     *
     * @param viewId The id of the parent {@link ViewGroup} to add the child into.
     * @param viewId The id of the parent {@link ViewGroup} to add the child into.
     * @param nestedView {@link RemoveViews} of the child to add.
     * @param nestedView {@link RemoteViews} of the child to add.
     * @param index The position at which to add the child.
     * @param index The position at which to add the child.
     *
     *
     * @hide
     * @hide