Tuesday, March 29, 2011

Access Internal classes in Android

So long before, i was blogging too much, then i stopped and told you guys i'll be doing more Android and Frontend, then i quit OpenRice and now back to doing Android as hobby.



Senario

Say you want to access some internal class in Android which are not public and you really want to use that feature, what do you do?



Notes

* This is on how to access non public classes thus are restricted classes which are not recommended, use this knowledge at your own risk and i'm NOT to be held responsible to any harm it might cause.

* This is quite advance and i hope to explain it as much as possible while keeping it simple.

* The following codes might not work as you/I expected it to work thus this is for reference only



Introduction

As i was trying to update my tutorial on Localization i was looking at how the locale setting was done and i found it at the LocalePicker.java under the onListItemClick function, if you try this code in your app you would realized that ActivityManagerNative and IActivityManager are internal classes. So how to use this piece of code?

IActivityManager am = ActivityManagerNative.getDefault();

Configuration config = am.getConfiguration();

Loc loc = mLocales[position];

config.locale = loc.locale;

final String language = loc.locale.getLanguage();

final String region = loc.locale.getCountry();

am.updateConfiguration(config);






What Do I Need

In Manafiest.xml

<uses-permission android:name="android.permission.WRITE_SETTINGS" />

<uses-permission android:name="android.permission.CHANGE_CONFIGURATION" />



Codes

DexFile df = new DexFile(new File("/system/app/Settings.apk"));

String name = "com.android.settings.LocalePicker";

ClassLoader cl = getClassLoader();

Class LocalePicker = df.loadClass(name, cl);

Class ActivityManagerNative = Class.forName("android.app.ActivityManagerNative");

Class IActivityManager = Class.forName("android.app.IActivityManager");



Method getDefault = ActivityManagerNative.getMethod("getDefault", null);

Object am = IActivityManager.cast(getDefault.invoke(ActivityManagerNative, null));



Method getConfiguration = am.getClass().getMethod("getConfiguration", null);



Configuration config = (Configuration) getConfiguration.invoke(am, null);

Locale locale = new Locale(languageToLoad);

Locale.setDefault(locale);

config.locale = locale;



Class[] args = new Class[1];

args[0] = Configuration.class;

Method updateConfiguration = am.getClass().getMethod("updateConfiguration", args);

updateConfiguration.invoke(am, config);




Explanation

DexFile df = new DexFile(new File("/system/app/Settings.apk"));

You need to load the package from where the class is from

NOTE: The dex don't directly open or readed in this line but they're memory-mapped read-only by the VM.



String name = "com.android.settings.LocalePicker";

ClassLoader cl = getClassLoader();

Class LocalePicker = df.loadClass(name, cl);


Load the class where your target classes were imported.



Class ActivityManagerNative = Class.forName("android.app.ActivityManagerNative");

Class IActivityManager = Class.forName("android.app.IActivityManager");


Load the classes you want to use



Method getDefault = ActivityManagerNative.getMethod("getDefault", null);

To use a method you have to get the method first. http://developer.android.com/reference/java/lang/reflect/Method.html



Object am = IActivityManager.cast(getDefault.invoke(ActivityManagerNative, null));

Execute the getDefault method using the invoke class, and since it has a return of IActivityManager, cast the returned object.



Method getConfiguration = am.getClass().getMethod("getConfiguration", null);

Since am is an instance of IActivityManager we could use getClass() to reflect on its method.



Configuration config = (Configuration) getConfiguration.invoke(am, null);

Execute the getConfiguration method, you can see here we use the regular casting (Configuration), you could also use Configuration.Class.cast



ocale locale = new Locale(languageString);

Locale.setDefault(locale);

config.locale = locale;


Just set a new Locale configuration



Class[] args = new Class[1];

args[0] = Configuration.class;

Method updateConfiguration = am.getClass().getMethod("updateConfiguration", args);

updateConfiguration.invoke(am, config);


To reflect on a method with arguements you have to do it this way, the second argument is a list of Class type of the required arguments. Optionally you could refer to another approach found in Calling private methods in Android



Conclusion

Reflecting a class could be lead to unknown results, like this class would set System Wide language change and not application wise and thus executing would lead to your whole system being reconfigured on the language (your home screen would HANG after you change and click Home), this is a powerful tool thus "We great knowledge comes great responsibility" :)



2 comments:

  1. I appreciate this piece of useful information. CourseDrill academy one of the best leading Training Institute, provides the best Online services with expert Team. For more information visit our site:
    Oracle Fusion HCM Training
    Workday Training
    Okta Training
    Palo Alto Training
    Adobe Analytics Training

    ReplyDelete
  2. 5-Card Baccarat - Where It's Going to Be | Vegas-Themed
    4) Baccarat is a choegocasino casino game that was invented 바카라 사이트 in the late 1800s 1xbet by Robert DeSalvio, who developed a betting parlay strategy.

    ReplyDelete