I'm trying to figure out Reflection with this Android class:
Class<?> c = Class.forName("com.android.internal.widget.LockPatternUtils");
Method method = c.getDeclaredMethod("getKeyguardStoredPasswordQuality");
method.setAccessible(true);
Object object = method.invoke(c); // Error with this line
result = object.toString());
The method getKeyguardStoredPasswordQuality is declared as (no parameters):
public int getKeyguardStoredPasswordQuality() {
// codes here
}
The error I got is:
Exception: java.lang.IllegalArgumentException: expected receiver of type com.android.internal.widget.LockPatternUtils, but got java.lang.Class<com.android.internal.widget.LockPatternUtils>
How do I declare com.android.internal.widget.LockPatternUtils as a receiver?
You are passing the class to #invoke() instead of an instance of LockPatternUtils.
You can create an instance using #newInstance().
Never mind, I've figured it out. I've adapted the codes below based on this tutorial.
In case anyone is interested in the solution, here it is:
Class<?> c = Class.forName("com.android.internal.widget.LockPatternUtils");
Constructor<?>[] constructors = c.getDeclaredConstructors();
Constructor<?> constructor = null;
for (int i = 0; i < constructors.length; i++) {
constructor = constructors[i];
if (constructor.getGenericParameterTypes().length == 0)
break;
}
constructor.setAccessible(true);
Object clazz = constructor.newInstance(context, true);
Method method = clazz.getClass().getDeclaredMethod("getKeyguardStoredPasswordQuality");
Object object = method.invoke(clazz);
result = object.toString();
The above solution requires that the public constructor of LockPatternUtils.java class to be defined as:
public LockPatternUtils(Context context) {...}
If the constructor changes in the future (after 2013), the solution will need to be amended.
Note: The above is an exercise for me to understand the usage of Reflection. However, using Reflection in Android production apps should be used sparingly and when absolutely needed.
Related
I was looking as the question : Instantiate a class from its string name which describes how to instantiate a class when having its name. Is there a way to do it in Java? I will have the package name and class name and I need to be able to create an object having that particular name.
Two ways:
Method 1 - only for classes having a no-arg constructor
If your class has a no-arg constructor, you can get a Class object using Class.forName() and use the newInstance() method to create an instance (though beware that this method is often considered evil because it can defeat Java's checked exceptions).
For example:
Class<?> clazz = Class.forName("java.util.Date");
Object date = clazz.newInstance();
Method 2
An alternative safer approach which also works if the class doesn't have any no-arg constructors is to query your class object to get its Constructor object and call a newInstance() method on this object:
Class<?> clazz = Class.forName("com.foo.MyClass");
Constructor<?> constructor = clazz.getConstructor(String.class, Integer.class);
Object instance = constructor.newInstance("stringparam", 42);
Both methods are known as reflection. You will typically have to catch the various exceptions which can occur, including things like:
the JVM can't find or can't load your class
the class you're trying to instantiate doesn't have the right sort of constructors
the constructor itself threw an exception
the constructor you're trying to invoke isn't public
a security manager has been installed and is preventing reflection from occurring
MyClass myInstance = (MyClass) Class.forName("MyClass").newInstance();
Using newInstance() directly is deprecated as of Java 8. You need to use Class.getDeclaredConstructor(...).newInstance(...) with the corresponding exceptions.
To make it easier to get the fully qualified name of a class in order to create an instance using Class.forName(...), one could use the Class.getName() method. Something like:
class ObjectMaker {
// Constructor, fields, initialization, etc...
public Object makeObject(Class<?> clazz) {
Object o = null;
try {
o = Class.forName(clazz.getName()).newInstance();
} catch (ClassNotFoundException e) {
// There may be other exceptions to throw here,
// but I'm writing this from memory.
e.printStackTrace();
}
return o;
}
}
Then you can cast the object you get back to whatever class you pass to makeObject(...):
Data d = (Data) objectMaker.makeObject(Data.class);
use Class.forName("String name of class").newInstance();
Class.forName("A").newInstance();
This will cause class named A initialized.
Use java reflection
Creating New Objects
There is no equivalent to method invocation for constructors, because invoking a constructor is equivalent to creating a new object (to be the most precise, creating a new object involves both memory allocation and object construction). So the nearest equivalent to the previous example is to say:
import java.lang.reflect.*;
public class constructor2 {
public constructor2()
{
}
public constructor2(int a, int b)
{
System.out.println(
"a = " + a + " b = " + b);
}
public static void main(String args[])
{
try {
Class cls = Class.forName("constructor2");
Class partypes[] = new Class[2];
partypes[0] = Integer.TYPE;
partypes[1] = Integer.TYPE;
Constructor ct
= cls.getConstructor(partypes);
Object arglist[] = new Object[2];
arglist[0] = new Integer(37);
arglist[1] = new Integer(47);
Object retobj = ct.newInstance(arglist);
}
catch (Throwable e) {
System.err.println(e);
}
}
}
which finds a constructor that handles the specified parameter types and invokes it, to create a new instance of the object. The value of this approach is that it's purely dynamic, with constructor lookup and invocation at execution time, rather than at compilation time.
Class.forName("ClassName") will solve your purpose.
Class class1 = Class.forName(ClassName);
Object object1 = class1.newInstance();
String str = (String)Class.forName("java.lang.String").newInstance();
something like this should work...
String name = "Test2";//Name of the class
Class myClass = Class.forName(name);
Object o = myClass.newInstance();
I am trying to get method reportLocation() through reflection from
"android.location.ILocationManager$Stub".
CODE:
Class<?> mStub = Class.forName("android.location.ILocationManager$Stub");
Method getInterface = mStub.getMethod("asInterface", IBinder.class);
Class mServiceManager = Class.forName("android.os.ServiceManager");
Method getService = mServiceManager.getMethod("getService", String.class);
Object iBindermInterface = getService.invoke(null, "location");
Object mInterface = getInterface.invoke(null, (IBinder) iBindermInterface);
Method method = mInterface.getClass().getMethod("reportLocation", Location.class, Boolean.class);
ERROR:
java.lang.NoSuchMethodException: android.location.ILocationManager$Stub$Proxy.reportLocation [class android.location.Location, class java.lang.Boolean]
I do not see this method when I trying to print all methods from "android.location.ILocationManager$Stub":
Class<?> mStub = Class.forName("android.location.ILocationManager$Stub");
Method[] getInterfaceAll = mStub.getMethods();
for (Method getInt : getInterfaceAll)
Log.d(LOG_TAG, "getInt = "+getInt);
In Android P (SDK 28) I have no problem with it.
In Android Q (SDK 29) interface android.location.ILocationManager does not have reportLocation() method, instead, to my assumptions, interface com.android.internal.location.ILocationProviderManager has onReportLocation(). But this interface contains the abstract method onReportLocation() and I cannot get its implementation.
What interface contain this method or it has been changed (renamed) to
another method?
This is the life of using internals API with reflection.
The problem it's simple, the reportLocation(in Location location, boolean passive) method was present until Android 9 in the ILocationManager.aidl interface, check here
But starting from Android 10 the method has been removed as you can see in the internal section here
You should probably change your code to use some public APIs, or you
will have hard time scanning the current APIs and find a replacement
to use with reflection. (and you will still risking they blacklisting
the API in the future)
PS: Just for complete information, probably the functionality has been moved here (I've followed some commits, but this could be completely unusable/different and I really discourage you to try to use it)
I found out how to put location on report.
Use the public boolean injectLocation(#NonNull Location newLocation) method, it can be obtained from ILocationManager.
Class<?> mStub = Class.forName("android.location.ILocationManager$Stub");
Method getInterface = mStub.getMethod("asInterface", IBinder.class);
Method getService = Class.forName("android.os.ServiceManager")
.getMethod("getService", String.class);
Object mInterface = getInterface
.invoke(null, (IBinder) getService.invoke(null, "location"));
Method method = mInterface.getClass()
.getMethod("injectLocation", Location.class);
method.invoke(mInterface, location);
I need to call a private method via reflection. In advance I don't kow if that private method is part of the specified class or a super class. I have the name of the private method, their parameters and their parameter types.
My first approach was the following:
Object classToTest = ....;
ArrayList<Method> methods = new ArrayList<Method>();
Class<?> classIndex = classToTest.getClass();
//iterate over (super) classes to collect all methods
do{
methods.addAll(Arrays.asList(classIndex.getDeclaredMethods()));
classIndex = classIndex.getSuperclass();
}
while(classIndex.getClass()!=null);
for(Method method : methods){
//match the right method
}
//call it
method.setAccessible(true);
method.invoke(classToTest,parameterValues);
Problem with that approach: Getting the right method out of the list is not trivial as the sourcecode for .getDeclaredMethod(...) shows. Unfortunatly many private internal methods are used which thus cant't be reused...
The 2nd approach was to use the getDeclaredMethod() which does the matching for me:
Method method=null;
Class<?> classIndex = classToTest.getClass();
//iterate over (super) classes since private method may be inherited
do{
//exception catching as part of the normal code is ugly
try{
method = classIndex.getDeclaredMethod(nameOfMethod, parameterTypes);
//method found thus exit
break;
}
catch(NoSuchMethodException nsme){
//method not found thus check super class
classIndex = classIndex.getSuperclass();
}
}
while(classIndex!=null);
if(method==null) throw new NoSuchMethodException( classToTest.getClass().getName() + "." + nameOfMethod + Arrays.toString(parameterValues));
//no method matching necessary
method.setAccessible(true);
method.invoke(classToTest,parameterValues);
Disadvantage of that approach: the exception catching as part of the code flow is ugly - however I currently don't see an alternative without reimplementing the matching code of Class.java.
So does anyone see an alternative approach to get the right private method?
I am using reflection proxies to perform additional checking on a public API. Essentially I want to wrap every object that comes back from it so that any object the caller gets their hands on is a proxy to the real object.
Java still has the whole erasure problem, so I am passing the type of the wrapped object around with it. I should know what type everything is because the entry into the API is a single, non-generic interface.
public class ProxyInvocationHandler implements InvocationHandler {
private final Object delegate;
private final Type delegateType;
public ProxyInvocationHandler(Object delegate, Type delegateType) {
this.delegate = delegate;
this.delegateType = delegateType;
}
#Override
public Object invoke(Object proxy, Method method, Object[] args) {
// Omitted: additional checks performed here.
Object result = method.invoke(delegate, args);
Type returnType = method.getGenericReturnType();
// e.g. if delegateType is List<Cat> and the method is the get method,
// returnType would be E but resultType should be Cat.
Type resultType = ???
// Utility method I will omit, it just creates another proxy instance
// using its own invocation handler.
return ProxyUtils.wrap(result, resultType);
}
}
I have looked around the Type / ParametrizedType API and can't seem to find a way to get resultType, even though delegateType and returnType should be enough information to compute this.
What is the "proper" way to do this?
You can use Java ClassMate for that purpose. You'll have to use com.fasterxml.classmate.GenericType for type tokens:
GenericType<?> delegateType = new GenericType<List<Cat>>() {};
Note the empty {} that's called the "Super-type Token" pattern.
TypeResolver typeResolver = new TypeResolver();
MemberResolver memberResolver = new MemberResolver(
ResolvedType type = typeResolver.resolve(delegateType);
ResolvedTypeWithMembers members = memberResolver.resolve(type, null, null);
ResolvedMethod[] methods = members.getMemberMethods();
Cache the results in a Map:
Map<Method, ResolvedMethod> resolved = new HashMap<>();
for (ResolvedMethod method: methods) {
resolved.put(method.getRawMember(), method);
}
Now, when you have a method declared by the delegateType, i.e. List, you can get its resolved return type:
Method method = List.class.getMethod("get", int.class);
ResolvedType resultType = resolved.get(method).getReturnType();
System.out.println("resultType = " + resultType); // prints resultType = Cat
Here's the Guava way, for people who come by in the future:
...
Type returnType = method.getGenericReturnType();
TypeToken<?> resultType = TypeToken.of(delegateType).resolveType(returnType);
I changed the type of delegateType to TypeToken<?> to make things a little easier. And of course, I added quite a bit of caching (using a LoadingCache) to get the performance down to a sensible speed. Their resolution code was slower than my initial hacked-up code which did the same thing. The difference is that I now have the confidence that it's being done correctly.
i have a problem with a newInstance call using Reflection in Android.
My Interface:
public interface IGiver {
public int getNr();
}
My Class to call Reflection:
public class NrGiver implements IGiver {
int i = 10;
#Override
public int getNr() {
return i;
}
}
The way i try to call getNr:
String packageName = "de.package";
String className = "de.package.NrGiver";
String apkName = getPackageManager().getApplicationInfo(packageName, 0).sourceDir;
PathClassLoader myClassLoader =
new dalvik.system.PathClassLoader(
apkName,
ClassLoader.getSystemClassLoader());
Class c = Class.forName(className);
IGiver giver = (IGiver) c.newInstance();
The last line wont work it cause an Error and my App stops.
I know it fault of newInstance, but i want to work on the IGiver object.
Pls Help me.
My Solution:
Hey guys finally i got the chicken.
I found an other way. this time i also used newInstance, but this time its worked.
My solution:
Class c = Class.forName(className);
Method methode = c.getDeclaredMethod("getNr");
Object i = methode.invoke(c.newInstance(), new Object[]{});
And this what i wanted to do.
I have an NrGiver.class somewhere on my Phone. And it implements the Interface IGiver. So it can be loaded into my App dynamically. I need the Integer from the class NrGiver. So i can make my code generic. I tried to Cast the Object to my Interface, but it failed.
So i found an other way to invoke the method of a class.
Thx for help
String className = "de.package.NrGiver";//line 1
Class c = Class.forName(className);//line 2
IGiver giver = (IGiver) c.newInstance();//line3
in line 2 forName is trying to find a class but the String is representing a Interface, so the classLoader does not find the class and it throws an exception. adding on to it, in line 3 you are trying to get an Instance of Interface which does't exist in the java world.. i mean to say Interfaces cannot be instanciated and they do not have constructors..
not sure why is classloader used. if both IGiver and NrGiver are loaded:
Class k = NrGiver.class;
IGiver g = (IGiver)k.newInstance();