/*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* See the NOTICE file distributed with this work for additional
* information regarding copyright ownership.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package io.appium.android.bootstrap.utils;
import io.appium.android.bootstrap.Logger;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.util.Arrays;
public class ReflectionUtils {
/**
* Clears the in-process Accessibility cache, removing any stale references.
* Because the AccessibilityInteractionClient singleton stores copies of
* AccessibilityNodeInfo instances, calls to public APIs such as `recycle` do
* not guarantee cached references get updated. See the
* android.view.accessibility AIC and ANI source code for more information.
*/
public static boolean clearAccessibilityCache() {
boolean success = false;
try {
final Class c = Class
.forName("android.view.accessibility.AccessibilityInteractionClient");
final Method getInstance = ReflectionUtils.method(c, "getInstance");
final Object instance = getInstance.invoke(null);
final Method clearCache = ReflectionUtils.method(instance.getClass(),
"clearCache");
clearCache.invoke(instance);
success = true;
} catch (final Exception ex) {
// Expected: ClassNotFoundException, NoSuchMethodException,
// IllegalAccessException,
// InvocationTargetException, NoSuchFieldException
Logger.error("Failed to clear Accessibility Node cache. "
+ ex.getMessage());
}
return success;
}
public static Class getClass(final String name) {
try {
return Class.forName(name);
} catch (final ClassNotFoundException e) {
final String msg = String.format("unable to find class %s", name);
throw new RuntimeException(msg, e);
}
}
public static Object getField(final Class clazz, final String fieldName,
final Object object) {
try {
final Field field = clazz.getDeclaredField(fieldName);
field.setAccessible(true);
return field.get(object);
} catch (final Exception e) {
final String msg = String.format(
"error while getting field %s from object %s", fieldName, object);
Logger.error(msg + " " + e.getMessage());
throw new RuntimeException(msg, e);
}
}
public static Object getField(final String field, final Object object) {
return getField(object.getClass(), field, object);
}
public static Object getField(final String className, final String field,
final Object object) {
return getField(getClass(className), field, object);
}
public static Object invoke(final Method method, final Object object,
final Object... parameters) {
try {
return method.invoke(object, parameters);
} catch (final Exception e) {
final String msg = String.format(
"error while invoking method %s on object %s with parameters %s",
method, object, Arrays.toString(parameters));
Logger.error(msg + " " + e.getMessage());
throw new RuntimeException(msg, e);
}
}
public static Method method(final Class clazz, final String methodName,
final Class... parameterTypes) {
try {
final Method method = clazz.getDeclaredMethod(methodName, parameterTypes);
method.setAccessible(true);
return method;
} catch (final Exception e) {
final String msg = String
.format(
"error while getting method %s from class %s with parameter types %s",
methodName, clazz, Arrays.toString(parameterTypes));
Logger.error(msg + " " + e.getMessage());
throw new RuntimeException(msg, e);
}
}
public static Method method(final String className, final String method,
final Class... parameterTypes) {
return method(getClass(className), method, parameterTypes);
}
}