2

I'm not sure if I'm having trouble with Reflection itself, or the method I'm trying to obtain.

What I'd like to do is call the function setLine1Number from the class:

com.android.internal.telephony.gsm.GSMPhone

So that I can have my number properly inserted into my phone as it is not required to be in my SIM. Therefore I want to be able to call the function getLine1Number and have it return the same number that I set.

Reflection appears the only way to be able to use this function as it is not in the public API.

I've written this, but keep getting an illegal argument exception. Here is my code:

String className = "com.android.internal.telephony.gsm.GSMPhone";
Class classToInvestigate = Class.forName(className);

Object arglist[] = new Object[3];
arglist[0] = new String("Phone Number");
arglist[1] = new String ("16035552412"); // Not a real phone number
arglist[2] = null;

Class[] paramTypes = new Class[3];
paramTypes[0] = String.class;
paramTypes[1] = String.class;
paramTypes[2] = Message.class;
Method setLine1Number = classToInvestigate.getMethod("setLine1Number", paramTypes);
setLine1Number.setAccessible(true);

Object TestReturn = setLine1Number.invoke(classToInvestigate, arglist); // Problem is here. Not sure how to properly do this. 

Now at this point, I would like if I could call

TelephonyManager telephonyManager = (TelephonyManager) context.getSystemService(Context.TELEPHONY_SERVICE);
String PhoneNumber2 =  telephonyManager.getLine1Number();

and have it return the number that I've input. But as I said illegal argument exception, and I'm not sure exactly how to solve this. Any help appreciated. I am still new to reflection so this might be a simple issue.

Here is the error log:

Error Log


Something else I forgot to mention is that I've used the following code to check that this method really exists in the class file:

        Method[] checkMethod = classToInvestigate.getDeclaredMethods();

        Test = new String[checkMethod.length];
        int i = 0;
        for(Method m : checkMethod)  
        {  
            // Found a method m  
            Test[i] = m.getName();
            i++;
        }  

And the method setLine1Number was returned in the array. So I'm fairly confident it is there and I can use it somehow.

Bill the Lizard
  • 398,270
  • 210
  • 566
  • 880
Dave
  • 3,178
  • 5
  • 28
  • 44

7 Answers7

1

You need an instance of the class you wish to operate on - an object of that type that has already been created.

You pass that to setLine1Number.invoke()

For example, if we were dealing with the Integer class you'd say:

Integer i = new Integer(5);
...
Object TestReturn = someIntegerMethod.invoke(i, arglist);
Brian Roach
  • 76,169
  • 12
  • 136
  • 161
  • Thanks for the reply! Ok, I now understand that I need to pass in an instance of the class I'd like to operate on. Any idea how could I create an instance of classToInvestigate ? – Dave Feb 20 '12 at 23:49
1

You must create an instance of GSMPhone (classToInvestigate). Here is a start:

Class commandsInterface = Class.forName("com.android.internal.telephony.CommandsInterface");
Class phoneNotifier = Class.forName("com.android.internal.telephony.PhoneNotifier");
Constructor constructor = classToInvestigate.getConstructor(Context.class, commandsInterface, phoneNotifier);

// This creation of newInstance will have to be modified depending on NullPointerExceptions.
// Will probably have to pass in context, as well as instantiate CommandsInterface and PhoneNotifier in a similar fashion and then inject them as well.
Object newInstance = constructor.newInstance(context, null, null);

setLine1Number.invoke(newInstance, arglist);
Justin Muller
  • 1,283
  • 13
  • 21
0

1) I suggest using "com.android.internal.telephony.Phone" instead of "....gsm.GSMPhone"

2) try this one in the beginning (and use mPhone when invoking your setLine1Number method): NB: ommit try/catch if you desire, but set your calling method to throw everything up...

private static final String CLASS_PHONEFACTORY = "com.android.internal.telephony.PhoneFactory";
private static final String METHOD_GETDEFAULTPHONE = "getDefaultPhone";

private Class<?> cPhoneFactory;
private Class<?> cPhone;
private Method mGetDefaultPhone;

private Object mPhone;

...

    try {
        cPhoneFactory = Class.forName(CLASS_PHONEFACTORY);
    } catch (ClassNotFoundException e) {
        cPhoneFactory = null; 
    }
    try {
        cPhone = Class.forName(CLASS_PHONE);
    } catch (ClassNotFoundException e) {
        cPhone = null;
    }

    try {
        mGetDefaultPhone = cPhoneFactory == null ? null : cPhoneFactory.getMethod(METHOD_GETDEFAULTPHONE,(Class[])null);
    }
    catch (NoSuchMethodException e) {
        mGetDefaultPhone = null;
    }

    try {
        mPhone = mGetDefaultPhone == null ? null : mGetDefaultPhone.invoke(null,(Object[])null);
    } catch (Exception e) {
        mPhone = null;
    }
dzilbers
  • 352
  • 1
  • 4
  • 10
0

You need to pass in an object of the type you want to call the method on, not the class object.

Lincoded
  • 425
  • 2
  • 13
  • Thanks for the quick reply! This is likely the part I'm confused about. What do you mean by object of the type you want to call the method on? Do you perhaps have an example? – Dave Feb 20 '12 at 23:04
  • I can't find any good examples for this, as all the examples I've found call classes that the user has defined as opposed to this class which is only accessible through reflection. – Dave Feb 20 '12 at 23:10
0

in the invoke call you need to pass an instance of GSMPhone class not classToInvestigate

I'm not familiar with any android API so i'm not sure how you can obtain an instance of it.

EDIT: looking at GSMPhone it calls a public method of SIMRecords maybe you can obtain an instance of that class and call that method instead?

  • Thanks Vincent for your reply. I am attempting to try an get an instance of this, but am not having any luck. If you have any more direction on this, that would be really good. Thanks for any help. – Dave Feb 21 '12 at 00:01
0

In case you like to set not accessible fields or call not accesible methods, you can set them to accessible if you like See:

https://github.com/ko5tik/andject/blob/master/src/main/java/de/pribluda/android/andject/ViewInjector.java

( lines 34-37 )

Konstantin Pribluda
  • 12,329
  • 1
  • 30
  • 35
-1

please try the following code.

String className = "com.android.internal.telephony.gsm.GSMPhone";
Class classToInvestigate = Class.forName(className);
Object gsmObj = classToInvestigate.newInstance();

Object arglist[] = new Object[3];
arglist[0] = new String("Phone Number");
arglist[1] = new String ("16035552412"); // Not a real phone number
arglist[2] = null;

Class[] paramTypes = new Class[3];
paramTypes[0] = String.class;
paramTypes[1] = String.class;
paramTypes[2] = Message.class;

Method setLine1Number = classToInvestigate.getMethod("setLine1Number", paramTypes);
boolean accessible = setLine1Number.isAccessible();
setLine1Number.setAccessible(true);

Object TestReturn = setLine1Number.invoke(gsmObj, arglist);
athspk
  • 6,722
  • 7
  • 37
  • 51
Jason Zong
  • 121
  • 1
  • 6
  • Hey Jason, Thanks for your response. I've tried your code by I keep getting an InstantiationException on line 3. Any ideas? – Dave Sep 27 '12 at 20:13