Friday, November 27, 2009

Calling private methods in Android

First and foremost, I won't be held responsible for the content of the article if applied to your apps, and if it results in any kinds of misfunctions and such. Use it at your own risk and use it when there's are no other way that is available in android.

In java, there is a set of API called Reflection API, see this for more detailed look and this for examples. What Reflection does is that it will, from its name, reflect the class for you, reflecting public, private, protected, static and even native variables, function/methods, classes and more. By getting their reflection you could execute private functions or get private class. In this article however, will focus on calling private methods on a public class. We would use SmsManager and test it on Android 1.6 throughout this article.

Update:
1- There is a bug on the emulator, i tested this on G1, sendRawPdu is missing on the reflection


try {
  // the following would just be (but using reflections)
  // sm.sendTextMessage("", null, "Test", null, null);

  // initialize variables
  SmsManager sm = SmsManager.getDefault();
  Class c = sm.getClass();

  // debug, print all methods in this class , there are 2 private methods in this class
  Method[] ms = c.getDeclaredMethods();
  for (int i = 0; i < ms.length; i++)     Log.d("ListMethos",ms[i].toString());   // get private method 1   Method m1 = SmsManager.class.getDeclaredMethod("createMessageListFromRawRecords", List.class);   Log.d("success","success getting sendRawPdu");   // get private method 2   byte[] bb = new byte[1];   Method m2 = SmsManager.class.getDeclaredMethod("sendRawPdu",bb.getClass(),bb.getClass(), PendingIntent.class,PendingIntent.class);   Log.d("success","success getting sendRawPdu");   // send message   m2.setAccessible(true);   SmsMessage.SubmitPdu pdus = SmsMessage.getSubmitPdu( null,"", "Test", false);
  m2.invoke(sm, pdus.encodedScAddress, pdus.encodedMessage, null, null );

  Log.d("success","success sedding message");
} catch (SecurityException e) {
  // TODO Auto-generated catch block
  e.printStackTrace();
} catch (NoSuchMethodException e) {
  // TODO Auto-generated catch block
  e.printStackTrace();
} catch (IllegalArgumentException e) {
  // TODO Auto-generated catch block
  e.printStackTrace();
} catch (IllegalAccessException e) {
  // TODO Auto-generated catch block
  e.printStackTrace();
} catch (InvocationTargetException e) {
  // TODO Auto-generated catch block
  e.printStackTrace();
}



Explanation
Get the private method from the SmsManager class having one argument of List
Method m1 = SmsManager.class.getDeclaredMethod("createMessageListFromRawRecords", List.class);
-- From our trace: D/ListMethos( 2900): private java.util.ArrayList android.telephony.SmsManager.createMessageListFromRawRecords(java.util.List)

Get another method from SmsManager that has 4 argument
Method m2 = SmsManager.class.getDeclaredMethod("sendRawPdu",bb.getClass(),bb.getClass(), PendingIntent.class,PendingIntent.class);
-- From our trace: D/ListMethos( 2900): private void android.telephony.SmsManager.sendRawPdu(byte[],byte[],android.app.PendingIntent,android.app.PendingIntent)

Make the private function to be public
m2.setAccessible(true);

Call the method m2(sendRawPdu) on SmsManager object with the following parameters
m2.invoke(sm, pdus.encodedScAddress, pdus.encodedMessage, null, null );

Basically thats it :) Hope this helps and remember limit the use of this, its not recommended by all means :)
Post a Comment