People have a tendency to install and enable things that they do not fully understand. In this article I will try to explain just what Root and Xposed Bridge is and how dangerous it can be. Don't get me wrong, I love both of these things, but it is important to be careful and not grant just any application these types of rights.
Let's picture a small village. In the middle of this village is a large bank. It is surrounded with a lot of smaller buildings. The bank represents the core Android system while the buildings are all of the Applications. All the people within each building can communicate with one another since they are all within the same building. If a building needs to communicate with another building, it has to send someone across town to the other building. It is then up to the people in that building to decide whether or not they want to let that person inside. While inside, the person can make a request or a delivery. Maybe he wants to borrow some sugar. He then delivers the response (in this case the sugar) back to his building. The same applies for the bank which is surrounded by guards. To make a request for a specific item in the bank, a building will need a permission slip for that item in order to bypass the guard watching it.
One building however has an Xposed Module implemented. In this case it is a specific type of person, I spy if you will. The building can send this person over to the bank and make him act as a guard. The bank and it's other guards will not not sense that anything is wrong. This spy can now move around in any section of the bank without any permission slip. He can steel items, place new items or make changes to the existing once without anyone asking questions. He can also disguise himself as a member of other buildings and walk around those without an invite. This spy is essentially a god amongst men.
In Android it allows applications to provide features not normally available, like changing theme and colors in any part of the system, create security modules that can restrict other applications from gathering specific information and much more. But it also allows application to do things that you might not want it to, like gathering information and uploading it to a server. Since the module can do whatever it wants, there is no way to restrict it.
Root is similar. It is the main built-in Administrator in the Linux kernel (Which Android is built on top of). In this case it acts as the emperor of the village. It is the main authority in the system and no one would dare to tell it no. It can move, do and behave just as it feels like without no one trying to stop it. The most important thing to note here is that Xposed Modules is able to acts as root, even if the device is not rooted. It is also important to note that gaining root via Xposed Bridge will not trigger your normal Root Popup window on rooted devices. So you will not even know that this has happened.
There is no doubt that devices with Xposed Bridge and Root enabled are much more fun. This article is not meant to scare anyone from rooting their devices or install Xposed Bridge on them. It is meant to inform people about the danger of doing so to make them more aware next time they enable an Xposed module or grant root to an application asking for it. Make sure that the application in question can be trusted, which most importantly mean that you should not allow this for Closed Source applications. If the source codes are close, there is no telling what has been implemented into the application.
So the next time you think about enabling an application in Xposed Bridge or grant root to an application, do some research first. Make sure that you can find a link to the source codes, make sure that the developer is contactable, do some searches to make sure that others have not warned about this application.
In any case, do not just blindly enable whatever the application asks for.
Showing posts with label Android. Show all posts
Showing posts with label Android. Show all posts
Wednesday, May 27, 2015
Tuesday, April 7, 2015
Check if current user is owner
One would think that this is a simple task in Android, especially since one would expect Google to have added some kind of tool for it, like something to return the current user id. Well they have, but they also decided to hide it. Android actually has a very pore collection of tools when it comes to working with multi-users. Like many things in the framework, Google did not think that apps had any reason to access to any information regarding the users. Android's sources might be fully open, but the framework is more closed than boot loaders on HTC devices.
The user id is normally not a very important information since it is nothing more than a number from 10 and up, except for the owner which have 00. The only thing that this number can tell you, is which user was created first. But checking for the 00 id to identify the owner can be very helpful. You may be working on an app that should have certain restrictions when not used by the device owner. Maybe your app should not be used by other users at all. In any case identifying the owner is useful enough that Google should have added something like isOwner() to the UserManager service.
Searching the web, it seams like people are using a lot of reflection to access some of the hidden classes in order to identify the device owner. However reflection is not really needed, and it should be avoided if possible, simply because hidden classes are not official and might change over time. If this happens, apps need to be updated or they will be broken on future versions of Android.
Even though Android did not directly provide any access to the current user id, they did indirectly do this in one way, namely the app data folder. In Android 4.1 and below, all app data was placed in /data/data, but in newer versions they are placed in /data/user/[userid] and all apps have access to their own data folder. So to get the current user id from within an app, you only need to get the name of the parent folder to your apps data location. This is very simple.
MainActivity.java:
The above code is even backward compatible with Android version without multi-user support, without any need to check the current API level. And we did not use any reflection or unsupported classes/methods.
The user id is normally not a very important information since it is nothing more than a number from 10 and up, except for the owner which have 00. The only thing that this number can tell you, is which user was created first. But checking for the 00 id to identify the owner can be very helpful. You may be working on an app that should have certain restrictions when not used by the device owner. Maybe your app should not be used by other users at all. In any case identifying the owner is useful enough that Google should have added something like isOwner() to the UserManager service.
Searching the web, it seams like people are using a lot of reflection to access some of the hidden classes in order to identify the device owner. However reflection is not really needed, and it should be avoided if possible, simply because hidden classes are not official and might change over time. If this happens, apps need to be updated or they will be broken on future versions of Android.
Even though Android did not directly provide any access to the current user id, they did indirectly do this in one way, namely the app data folder. In Android 4.1 and below, all app data was placed in /data/data, but in newer versions they are placed in /data/user/[userid] and all apps have access to their own data folder. So to get the current user id from within an app, you only need to get the name of the parent folder to your apps data location. This is very simple.
MainActivity.java:
public class Utils {
public static boolean isOwner(Context context) {
/*
* Get the parent location.
* This can either be /data/user/[userid] or /data/data depending on Android version.
*/
File file = new File(context.getApplicationInfo().dataDir).getParentFile();
/*
* Get the name of the folder in the parent location.
* This can either be [userid] or data
*/
String user = file.getName();
try {
/*
* Returns TRUE if this user has the id 0 (device owner)
*/
return Integer.valueOf(user) == 0;
} catch (NumberFormatException e) {
/*
* The user variable contained "data".
* This is not an multi-user environment, which means that we only have the device owner available.
*/
return true;
}
}
}
The above code is even backward compatible with Android version without multi-user support, without any need to check the current API level. And we did not use any reflection or unsupported classes/methods.
Friday, March 20, 2015
JNI / Android NDK
If you visit the Android developer page and read about Android's NDK, it will seam as if Google is trying to scare people from using it and just stick with Java. In most cases this will properly be the best idea. Java is a great language and does a good job for most tasks. However JNI is not as dangerous as Google is trying to make it. Most of Android is build on JNI and IPC. Information is being parsed in and out of JVM and between processes constantly, so why should applications not take advantage of this as well?
One thing to remember about JNI is that it creates a bit of overload to parse in and out of JVM. But depending on the task, this is not necessarily enough downside to keep away from it. The best way to figure out whether or not to use C/C++ or Java, is to test your task in both and then compare the result.
One good example of a task better suited for JNI could be some extensive file operations. For this example we will collect the content of the stat file for all currently running processes on a device. On my device running Android 5, we are talking about rounded 300 files. We add this to a loop which will run 100 times which in turn will create 3000 file read operations.
The first thing we need, is a basic activity to run our examples in.
MainActivity.java:
We also need a Java method to do the collection work. We will separate the native method and the Java method into two files in this example. It makes it easier to keep an overview.
JavaCollector.java:
And we cannot compare the above Java class to JNI without a native example as well.
NativeCollector.java:
collector.cpp:
If we compile and run this application, we will get the following result:
So get started with:
One thing to remember about JNI is that it creates a bit of overload to parse in and out of JVM. But depending on the task, this is not necessarily enough downside to keep away from it. The best way to figure out whether or not to use C/C++ or Java, is to test your task in both and then compare the result.
One good example of a task better suited for JNI could be some extensive file operations. For this example we will collect the content of the stat file for all currently running processes on a device. On my device running Android 5, we are talking about rounded 300 files. We add this to a loop which will run 100 times which in turn will create 3000 file read operations.
The first thing we need, is a basic activity to run our examples in.
MainActivity.java:
public class MainActivity extends Activity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
StringBuilder builder = new StringBuilder();
for (int i=0; i < 2; i++) {
double start = System.nanoTime();
int files = 0;
for (int x=0; x < 100; x++) {
String[] list = null;
switch (i) {
case 0: list = JavaCollector.collect(); break;
case 1: list = NativeCollector.collect();
}
files += list.length;
}
double end = System.nanoTime();
builder.append(i == 0 ? "JavaCollector" : "NativeCollector\n");
builder.append("Files = " + files + "\n");
builder.append("Time = " + ((end - start) * Math.pow(10, -9)) + " seconds\n");
builder.append("\n");
}
TextView view = (TextView) findViewById(R.id.textview);
view.setText(builder.toString());
}
}
We also need a Java method to do the collection work. We will separate the native method and the Java method into two files in this example. It makes it easier to keep an overview.
JavaCollector.java:
public class JavaCollector {
/*
* Re-defined regexp to match directories in /proc with process id's
*/
protected static final Pattern REFEXP_PID = Pattern.compile("^[0-9]+$");
/*
* The method that collects all the file data
*/
public static String[] collect() {
/*
* Get a listing from the /proc directory
*/
String[] procListing = new File("/proc").list();
/*
* Define our input stream
*/
BufferedReader in = null;
/*
* Create a temp container for the file output
*/
ArrayList<String> lines = new ArrayList<String>();
/*
* Handle each entity in /proc
*/
for (String procEntity : procListing) {
/*
* We only want the pid directories
*/
if (REFEXP_PID.matcher(procEntity).matches()) {
try {
/*
* Open the sub directory stat file
*/
in = new BufferedReader(new FileReader("/proc/" + procEntity + "/stat"));
/*
* Get the content of the stat file
*/
lines.add(in.readLine());
} catch (IOException e) {} finally {
if (in != null) {
try {
/*
* Close the file
*/
in.close();
in = null;
} catch (IOException e) {}
}
}
}
}
/*
* Create and return a string array
*/
return lines.toArray( new String[ lines.size() ] );
}
}
And we cannot compare the above Java class to JNI without a native example as well.
NativeCollector.java:
public class NativeCollector {
/*
* Load our collector library
*/
static {
System.loadLibrary("collector");
}
/*
* This is really a call to Java_com_example_NativeCollector_collect() in collector.cpp
*/
public static native String[] collect();
}
collector.cpp:
JNIEXPORT jobjectArray JNICALL Java_com_example_NativeCollector_collect(JNIEnv *env, jobject thisObj) {
/*
* Open /proc
*/
DIR* procDirectory = opendir("/proc");
if (procDirectory != NULL) {
/*
* Create a temp container with minimum 100 indexes pre-allocated
*/
std::vector<std::string> lines(100);
/*
* Create an input stream
*/
std::ifstream in;
/*
* Create a variable for the proc entities
*/
struct dirent* procEntity;
/*
* Handle each entity in /proc
*/
while ((procEntity = readdir(procDirectory)) != NULL) {
/*
* We only want the pid directories
*/
if (std::regex_match (procEntity->d_name, std::regex("^[0-9]+$") )) {
/*
* Open the sub directory stat file
*/
std::string path = std::string("/proc/") + procEntity->d_name + "/stat";
in.open( path.c_str() );
if (in && in.good()) {
/*
* Get the content of the stat file
*/
std::string line;
std::getline(in, line);
lines.push_back(line);
}
/*
* Close the file
*/
if (in) {
in.close();
}
}
}
/*
* Create Java return data
*/
if (lines.size() > 0) {
/*
* Create a Java array
*/
jobjectArray ret = env->NewObjectArray(lines.size(), env->FindClass("java/lang/String"), NULL);
for (int i=0; i < lines.size(); i++) {
/*
* Place the line in a Java String
*/
jstring stringObject = env->NewStringUTF( lines[i].c_str() );
/*
* Add the Java String to the Java Array
*/
env->SetObjectArrayElement(ret, i, stringObject);
/*
* Release the Java String reference.
*
* Note: This is important. We can only have a limited ammount of Java objects
* at a time and they are not auto released until we return to the JVM. And since we
* are looping an unknown, but large, number of files, we could end up with a memory overflow.
*/
env->DeleteLocalRef(stringObject);
}
/*
* Return the array to JVM
*/
return ret;
}
}
/*
* Return an empty array
*/
return env->NewObjectArray(0, env->FindClass("java/lang/String"), NULL);;
}
If we compile and run this application, we will get the following result:
- JavaCollector$collect: 21.2 seconds
- NativeCollector$collect: 2.7 seconds
This is not just slightly faster than Java, this is 87% faster.
This is only a simple example. If it should be used in a real application, then depending on the amount of files and amount of data, it might be a good idea to make some sort of iteration mechanism to avoid memory overflow. But as the result shows, it is sometimes a good idea to check whether or not a JNI solution might be better. Java is not always the correct solution.
So get started with:
- Learning C/C++
- Learning JNI
- Setup your NDK
- Bookmark the C++ manual and the JNI manual
Saturday, January 17, 2015
Communication across the UI
One of the greater things in newer Android versions is the Fragments. It allows you to split the UI into smaller pieces that can be put together in different ways, depending on different circumstances like screen size and such. However some times you might need to adapt things in one Fragment based on some conditions in another. Talking to an Activity from within a Fragment is easy enough, but Fragments to Fragments is another thing. Especially since it is unadvised to have one specific Fragment talk to another specific one.
A better solution is to have a small message delivery system that works similar to Android's broadcast system. That way you can parse information between Fragments, but without targeting specific ones. For this we need an extended Activity that has these capabilities.
One issue with Java (or not, depending on who you ask) is that Java does not support multi-inheritance. I for one is quite fine with this, but I would like to see something like traits be introduced into the language. In this the problem lies with the many different Activity and Fragment classes that Android has available. We don't want to copy paste everything into each Activity and Fragment sub-class that we want to use.
With the lack of traits or similar, we will be using the famous logic trick instead where we place all the logic into one class, and then only create redirection in each of the Activity and Fragment classes. Not only will this limit the size of each Activity and Fragment class, but it will also make sure that we only have to edit one class if we want to change something.
ActivityLogic.java:
The above Activity Logic class is only the first step towards getting our message delivery system working. Since this is meant to be used to communicate between Fragments, we still need a Fragment Logic class as well.
FragmentLogic.java:
Now that we have all of the logic behind this delivery system, we need to extend some classes that will be using these classes. Let's start with the Activity class.
AbstractActivity.java:
And we will of course also need a Fragment class.
AbstractFragment.java:
And that's it. We now have one normal Activity and Fragment class that we can use. Note that these are abstract, this is because we use these to extend our actual ones.
Let's take a small example of how to use these.
SomeFragment.java:
If we need other types as well, like DialogFragment or ActionBarActivity, we can re-use our logic classes to easily create them.
A better solution is to have a small message delivery system that works similar to Android's broadcast system. That way you can parse information between Fragments, but without targeting specific ones. For this we need an extended Activity that has these capabilities.
One issue with Java (or not, depending on who you ask) is that Java does not support multi-inheritance. I for one is quite fine with this, but I would like to see something like traits be introduced into the language. In this the problem lies with the many different Activity and Fragment classes that Android has available. We don't want to copy paste everything into each Activity and Fragment sub-class that we want to use.
With the lack of traits or similar, we will be using the famous logic trick instead where we place all the logic into one class, and then only create redirection in each of the Activity and Fragment classes. Not only will this limit the size of each Activity and Fragment class, but it will also make sure that we only have to edit one class if we want to change something.
ActivityLogic.java:
final public class ActivityLogic {
public static interface IActivityLogic {
public void onReceiveMessage(String message, Object data, Boolean sticky);
public void onFragmentAttachment(IActivityLogicFragment fragment);
public void onFragmentDetachment(IActivityLogicFragment fragment);
public void sendMessage(String message, Object data);
public void sendMessage(String message, Object data, Boolean sticky);
}
public static interface IActivityLogicFragment {
public void onReceiveMessage(String message, Object data, Boolean sticky);
}
private final static class ActivityLogic_MessageHandler extends AbstractHandler<ActivityLogic> {
public ActivityLogic_MessageHandler(ActivityLogic reference) {
super(reference);
}
@Override
public void handleMessage(Message msg) {
ActivityLogic logic = getReference();
if (logic != null) {
IActivityLogic activity = logic.ActivityLogic_mActivity.get();
if (activity != null) {
Set<IActivityLogicFragment> fragments = new HashSet<IActivityLogicFragment>(logic.ActivityLogic_mFragments);
Object[] input = (Object[]) msg.obj;
String message = (String) input[0];
Object data = input[1];
activity.onReceiveMessage(message, data, false);
for (IActivityLogicFragment fragment : fragments) {
fragment.onReceiveMessage(message, data, false);
}
}
}
}
}
private Set<IActivityLogicFragment> ActivityLogic_mFragments = Collections.newSetFromMap(new WeakHashMap<IActivityLogicFragment, Boolean>());
private Map<String, Object> ActivityLogic_mStickyMessages = new HashMap<String, Object>();
private ActivityLogic_MessageHandler ActivityLogic_mMessageHandler;
private WeakReference<IActivityLogic> ActivityLogic_mActivity;
public ActivityLogic(IActivityLogic activity) {
ActivityLogic_mActivity = new WeakReference<IActivityLogic>(activity);
ActivityLogic_mMessageHandler = new ActivityLogic_MessageHandler(this);
}
public void onFragmentAttachment(IActivityLogicFragment fragment) {
synchronized (ActivityLogic_mFragments) {
ActivityLogic_mFragments.add(fragment);
for (String message : ActivityLogic_mStickyMessages.keySet()) {
fragment.onReceiveMessage(message, ActivityLogic_mStickyMessages.get(message), true);
}
}
}
public void onFragmentDetachment(IActivityLogicFragment fragment) {
synchronized (ActivityLogic_mFragments) {
ActivityLogic_mFragments.remove(fragment);
}
}
public void sendMessage(String message, Object data) {
sendMessage(message, data, false);
}
public void sendMessage(String message, Object data, Boolean sticky) {
synchronized(ActivityLogic_mFragments) {
if (sticky) {
ActivityLogic_mStickyMessages.put(message, data);
}
ActivityLogic_mMessageHandler.obtainMessage(0, new Object[]{message, data}).sendToTarget();
}
}
}
The above Activity Logic class is only the first step towards getting our message delivery system working. Since this is meant to be used to communicate between Fragments, we still need a Fragment Logic class as well.
FragmentLogic.java:
final public class FragmentLogic {
public static interface IFragmentLogic extends IActivityLogicFragment {
public IActivityLogic getParent();
public void sendMessage(String message, Object data);
public void sendMessage(String message, Object data, Boolean sticky);
}
private WeakReference<IActivityLogic> FragmentLogic_mActivity;
private WeakReference<IFragmentLogic> FragmentLogic_mFragment;
public FragmentLogic(IFragmentLogic fragment) {
FragmentLogic_mFragment = new WeakReference<IFragmentLogic>(fragment);
}
public void onAttach(IActivityLogic activity) {
FragmentLogic_mActivity = new WeakReference<IActivityLogic>(activity);
IFragmentLogic fragment = FragmentLogic_mFragment.get();
if (fragment != null) {
activity.onFragmentAttachment(fragment);
}
}
public void onDetach() {
IFragmentLogic fragment = FragmentLogic_mFragment.get();
IActivityLogic activity = FragmentLogic_mActivity.get();
if (fragment != null && activity != null) {
activity.onFragmentDetachment(fragment);
}
FragmentLogic_mActivity.clear();
}
public IActivityLogic getParent() {
return FragmentLogic_mActivity.get();
}
public void sendMessage(String message, Object data) {
sendMessage(message, data, false);
}
public void sendMessage(String message, Object data, Boolean sticky) {
IActivityLogic activity = getParent();
if (activity != null) {
activity.sendMessage(message, data, sticky);
}
}
}
Now that we have all of the logic behind this delivery system, we need to extend some classes that will be using these classes. Let's start with the Activity class.
AbstractActivity.java:
public abstract class AbstractActivity extends Activity implements IActivityLogic {
private ActivityLogic mLogic;
public AbstractActivity() {
mLogic = new ActivityLogic(this);
}
@Override
public void onFragmentAttachment(IActivityLogicFragment fragment) {
mLogic.onFragmentAttachment(fragment);
}
@Override
public void onFragmentDetachment(IActivityLogicFragment fragment) {
mLogic.onFragmentDetachment(fragment);
}
@Override
public void onReceiveMessage(String message, Object data, Boolean sticky) {}
@Override
public final void sendMessage(String message, Object data) {
mLogic.sendMessage(message, data);
}
@Override
public final void sendMessage(String message, Object data, Boolean sticky) {
mLogic.sendMessage(message, data, sticky);
}
}
And we will of course also need a Fragment class.
AbstractFragment.java:
public abstract class AbstractFragment extends Fragment implements IFragmentLogic {
private FragmentLogic mLogic;
public AbstractFragment() {
mLogic = new FragmentLogic(this);
}
@Override
public final IActivityLogic getParent() {
return mLogic.getParent();
}
@Override
public void onAttach(Activity activity) {
super.onAttach(activity);
mLogic.onAttach((IActivityLogic) activity);
}
@Override
public void onDetach() {
super.onDetach();
mLogic.onDetach();
}
public void onReceiveMessage(String message, Object data, Boolean sticky) {}
public final void sendMessage(String message, Object data) {
mLogic.sendMessage(message, data);
}
public final void sendMessage(String message, Object data, Boolean sticky) {
mLogic.sendMessage(message, data, sticky);
}
}
And that's it. We now have one normal Activity and Fragment class that we can use. Note that these are abstract, this is because we use these to extend our actual ones.
Let's take a small example of how to use these.
SomeFragment.java:
public class SomeFragment extends AbstractFragment {
@Override
public void onResume() {
// Send a message to other Fragments or the Activity
if (someVariable == "someCondition") {
sendMessage("tellSomeone", "someCondition");
}
}
@Override
public void onReceiveMessage(String message, Object data, Boolean sticky) {
// Receive message from other Fragments or the Activity
if (message.equals("someOtherCondition")) {
// Do something
}
}
}
If we need other types as well, like DialogFragment or ActionBarActivity, we can re-use our logic classes to easily create them.
Wednesday, February 12, 2014
Android Resources
This is not a guide or an article. It is mainly an observation that I can no longer ignorer. The issue is that most Android developers have a habit of working out custom solutions using the few tools that they know, rather than looking to see if Android has a pee-built tool for that specific task.
In this case, the issue is regarding Android's Resources class. Let's say that we want to create a dynamic string using placeholders. If you try to google this, you will mostly find the same solution on each page that you visit. I have written this solution below.
The problem here is that there is no point in including String.format into this, because Resources.getString is already able to do this for you.
If you took a look at the documentation for this method, you would see that it does not only take [String] as an argument. It actually takes [String, Object...], where each Object parsed will be used to replace the placeholders of the string.
Please have a look at the Resources Documentation. This class contains a lot of useful tools for when working with the resources in Android. Many of which is not seen being used very much, if even at all.
In this case, the issue is regarding Android's Resources class. Let's say that we want to create a dynamic string using placeholders. If you try to google this, you will mostly find the same solution on each page that you visit. I have written this solution below.
<resources>
<string name="dynamic_string">Let\'s insert the number %1$d</string>
</resources>
Integer numberToInsert = 1; String dynamicString = String.format( getResources().getString(R.string.welcome_messages), numberToInsert );
The problem here is that there is no point in including String.format into this, because Resources.getString is already able to do this for you.
Integer numberToInsert = 1; String dynamicString = getResources().getString(R.string.welcome_messages, numberToInsert );
If you took a look at the documentation for this method, you would see that it does not only take [String] as an argument. It actually takes [String, Object...], where each Object parsed will be used to replace the placeholders of the string.
Please have a look at the Resources Documentation. This class contains a lot of useful tools for when working with the resources in Android. Many of which is not seen being used very much, if even at all.
Friday, November 8, 2013
Custom System Service using XposedBridge
For those of you who don't know XposedBridge, you should really have a look at it. By enabling you to hook any Class and Method within Android, system classes as well as normal application classes, this is just as powerful a tool as a root shell session. This however operates within the Java VM instead of hacking your way in from a shell.
After working with XposedBridge for awhile, I started stumbling across a few limitations. The Xposed Framework might be able to hook any method in any class, but it can of cause only add a hook before and after execution of the original method. This means that you cannot change anything within a method, but instead either replace it, change the arguments being parsed to it or change the result from it. In most cases this will be enough, however since Android parses a lot of things between multiple processes in JVM and Native code (which you cannot hook), there are circumstances where this will not be enough. For an example, if you decide to add a hook to PhoneWindowManager's interceptKeyBeforeQueueing or interceptKeyBeforeDispatching methods to change the incoming key code or some policy flags, that hook will not be enough. The two original methods will be executed with these new values, but after they have executed, the Native Event Handler will parse the original values to the application dispatchers, and your hook changes will not affect that part. This means that you would also have to add a hook to the KeyEvent's dispatch method. But, since that one will run in different processes than your first hook(s), you cannot share data between them, not even if you add all hooks to one single class instance or use a static class.
In some cases, you can use Broadcasts to send data from one process to another. But the problem with Broadcasts is that you cannot be sure about when the receiver will be invoked or when it is done processing your data. Also, sending a Broadcast or adding a Receiver requires access to a Context, and when working with Xposed you will not always have one of those. The best option in these cases is adding a custom System Service that you can use to share data between processes. The positive thing about System Services, unlike normal Application Services, is that they are available always, from the beginning of System Boot and until the System is shut down. Also they are much easier to connect to. Normally you would not be able to create such a service, but since we are working with XposedBridge, you can actually add whatever you'd like since you can operate as being part of Android.
The first thing that we will need, is an AIDL interface file. You cannot have a System Service without this. In your project, create a new package named android.os and in that package create a new file named ICustomService.aidl that looks like the below example.
android.os.ICustomService.aidl:
We will also need a class in our regular project package that will be used to hook the service into Android. Create a class named CustomServiceHook.java somewhere in your project package.
CustomServiceHook.java:
Since you should already be familiar with XposedBridge, you should already know how to add this hook to be invoked by Xposed during boot.
The CustomService class that we call in our hook above is our System Service Class. The inject() method is a static method from where we will inject the Service into Android so that it can be used by our module. Create a new package named com.android.server and create a new class in that package named CustomService.java.
com.android.server.CustomService.java:
This is our basic classes. Now it's time to extend CustomService.java to actually make it work. All of Android's original services are created and registered from com.android.server.SystemServer.java. However, all of the creation and registration is done from within a Thread. The problem here is that this Thread keeps the System Context as a normal variable, so we have no way to access it outside the Run() method. And since we cannot hook our way into the method but only add a hook before or after, this method and class is of no use to us.
However, the first thing that this method does, is invoke the main() method of com.android.server.am.ActivityManagerService in order to get the System Context. So if we add a hook after that method, the hook will be invoked in the beginning of the SystemServer thread, and we will also have access to the System Context which will be in the result from the main() method that we added our hook to. Let's change our CustomService.java file and add this hook to it.
com.android.server.CustomService.java:
The service will now be created and registered with Android during boot, but we are not quite done yet. At the time this service is created, no other services are available. So if you want to do some initializing, the constructor is not the place to do it. What we need is yet another hook that will be invoked once the system is ready. The ActivityManagerService class also has a good place for this. It has a systemReady() method that is invoked once all of the services has been created and is up and running. Let's add this to our CustomService class.
com.android.server.CustomService.java:
Now you are done. Your new service will now be loaded into Android's ServiceManager during boot via our first hook to the ActivityServiceManager.main() method and it will be initialized once all services is ready via our second hook to ActivityServiceManager.systemReady().
After the initialization, you can access the binder using ServiceManager.getService( name ) just like with any other System Service provided by Android.
All that is left is to implement whatever options you would like this service to provide and start using it in your module.
Example of usage:
After working with XposedBridge for awhile, I started stumbling across a few limitations. The Xposed Framework might be able to hook any method in any class, but it can of cause only add a hook before and after execution of the original method. This means that you cannot change anything within a method, but instead either replace it, change the arguments being parsed to it or change the result from it. In most cases this will be enough, however since Android parses a lot of things between multiple processes in JVM and Native code (which you cannot hook), there are circumstances where this will not be enough. For an example, if you decide to add a hook to PhoneWindowManager's interceptKeyBeforeQueueing or interceptKeyBeforeDispatching methods to change the incoming key code or some policy flags, that hook will not be enough. The two original methods will be executed with these new values, but after they have executed, the Native Event Handler will parse the original values to the application dispatchers, and your hook changes will not affect that part. This means that you would also have to add a hook to the KeyEvent's dispatch method. But, since that one will run in different processes than your first hook(s), you cannot share data between them, not even if you add all hooks to one single class instance or use a static class.
In some cases, you can use Broadcasts to send data from one process to another. But the problem with Broadcasts is that you cannot be sure about when the receiver will be invoked or when it is done processing your data. Also, sending a Broadcast or adding a Receiver requires access to a Context, and when working with Xposed you will not always have one of those. The best option in these cases is adding a custom System Service that you can use to share data between processes. The positive thing about System Services, unlike normal Application Services, is that they are available always, from the beginning of System Boot and until the System is shut down. Also they are much easier to connect to. Normally you would not be able to create such a service, but since we are working with XposedBridge, you can actually add whatever you'd like since you can operate as being part of Android.
The first thing that we will need, is an AIDL interface file. You cannot have a System Service without this. In your project, create a new package named android.os and in that package create a new file named ICustomService.aidl that looks like the below example.
android.os.ICustomService.aidl:
package android.os;
/** {@hide} */
interface IXAService {
}
We will also need a class in our regular project package that will be used to hook the service into Android. Create a class named CustomServiceHook.java somewhere in your project package.
CustomServiceHook.java:
public final class CustomServiceHook implements IXposedHookZygoteInit {
@Override
public void initZygote(IXposedHookZygoteInit.StartupParam startupParam) throws Throwable {
CustomService.inject();
}
}
Since you should already be familiar with XposedBridge, you should already know how to add this hook to be invoked by Xposed during boot.
The CustomService class that we call in our hook above is our System Service Class. The inject() method is a static method from where we will inject the Service into Android so that it can be used by our module. Create a new package named com.android.server and create a new class in that package named CustomService.java.
com.android.server.CustomService.java:
public class CustomService extends ICustomService.Stub {
public static void inject() {
}
}
This is our basic classes. Now it's time to extend CustomService.java to actually make it work. All of Android's original services are created and registered from com.android.server.SystemServer.java. However, all of the creation and registration is done from within a Thread. The problem here is that this Thread keeps the System Context as a normal variable, so we have no way to access it outside the Run() method. And since we cannot hook our way into the method but only add a hook before or after, this method and class is of no use to us.
However, the first thing that this method does, is invoke the main() method of com.android.server.am.ActivityManagerService in order to get the System Context. So if we add a hook after that method, the hook will be invoked in the beginning of the SystemServer thread, and we will also have access to the System Context which will be in the result from the main() method that we added our hook to. Let's change our CustomService.java file and add this hook to it.
com.android.server.CustomService.java:
public class CustomService extends ICustomService.Stub {
private Context mContext;
private static CustomService oInstance;
public static void inject() {
final Class ActivityManagerServiceClazz = XposedHelpers.findClass("com.android.server.am.ActivityManagerService", null);
XposedBridge.hookAllMethods(
ActivityManagerServiceClazz,
"main",
new XC_MethodHook() {
@Override
protected final void afterHookedMethod(final MethodHookParam param) {
Context context = (Context) param.getResult();
oInstance = new CustomService(context);
XposedHelpers.callMethod(
XposedTools.findClass("android.os.ServiceManager"),
"addService",
new Class[]{String.class, IBinder.class},
"custom.service",
oInstance
);
}
}
);
}
public XAService(Context context) {
mContext = context;
}
}
The service will now be created and registered with Android during boot, but we are not quite done yet. At the time this service is created, no other services are available. So if you want to do some initializing, the constructor is not the place to do it. What we need is yet another hook that will be invoked once the system is ready. The ActivityManagerService class also has a good place for this. It has a systemReady() method that is invoked once all of the services has been created and is up and running. Let's add this to our CustomService class.
com.android.server.CustomService.java:
public class CustomService extends ICustomService.Stub {
private Context mContext;
private static CustomService oInstance;
public static void inject() {
final Class ActivityManagerServiceClazz = XposedHelpers.findClass("com.android.server.am.ActivityManagerService", null);
XposedBridge.hookAllMethods(
ActivityManagerServiceClazz,
"main",
new XC_MethodHook() {
@Override
protected final void afterHookedMethod(final MethodHookParam param) {
Context context = (Context) param.getResult();
oInstance = new CustomService(context);
XposedHelpers.callMethod(
XposedTools.findClass("android.os.ServiceManager"),
"addService",
new Class[]{String.class, IBinder.class},
"custom.service",
oInstance
);
}
}
);
XposedBridge.hookAllMethods(
ActivityManagerServiceClazz,
"systemReady",
new XC_MethodHook() {
@Override
protected final void afterHookedMethod(final MethodHookParam param) {
oInstance.systemReady();
}
}
);
}
public XAService(Context context) {
mContext = context;
}
private void systemReady() {
// Make your initialization here
}
}
Now you are done. Your new service will now be loaded into Android's ServiceManager during boot via our first hook to the ActivityServiceManager.main() method and it will be initialized once all services is ready via our second hook to ActivityServiceManager.systemReady().
After the initialization, you can access the binder using ServiceManager.getService( name ) just like with any other System Service provided by Android.
All that is left is to implement whatever options you would like this service to provide and start using it in your module.
Example of usage:
public class SomeClass {
ICustomService mService;
public void someMethod() {
if (mService == null) {
mService = ICustomService.Stub.asInterface(
ServiceManager.getService("custom.service")
);
}
mService.someServiceMethod();
}
}
Saturday, July 13, 2013
Add missing Android Activity tools
One major issue with Android, is that Google have never implemented a proper way of detecting which Activity is currently in the foreground and which are behind. If you open multiple Activities in an application, and then flip the screen, all the callbacks (onCreate(), onResume() etc) will be invoked in all your Activities. The problem is that you might have some code in some of these Activities that you do not wish to have executed, unless the Activity holding that code, is currently placed in the foreground. Problem is that you can't check this state of an Activity. At least not in a proper way.
There is the possibility to use something like the above. There is just two issues regarding this. For one, google states that getSystemService(Context.ACTIVITY_SERVICE) is only intended for debugging and presenting task management user interfaces, and that it should never be used for core logic in an application. Second, you need to have android.permission.GET_TASKS added to the application in order to use this, which is not ideal for a simple task like this.
So what we can do instead, is extending the Activity class with a custom one which includes this missing feature and then extend our application Activities with our custom one instead.
What this does, it keep track of all active Activities and in which order they where opened. We only need to extend all of our Activities using this one, and then we can use isForeground() to check the state of an Activity Object.
public Boolean isForeground() {
ActivityManager am = (ActivityManager) getSystemService(Context.ACTIVITY_SERVICE);
ComponentName cn = am.getRunningTasks(1).get(0).topActivity;
}
There is the possibility to use something like the above. There is just two issues regarding this. For one, google states that getSystemService(Context.ACTIVITY_SERVICE) is only intended for debugging and presenting task management user interfaces, and that it should never be used for core logic in an application. Second, you need to have android.permission.GET_TASKS added to the application in order to use this, which is not ideal for a simple task like this.
So what we can do instead, is extending the Activity class with a custom one which includes this missing feature and then extend our application Activities with our custom one instead.
public class ExtendedActivity extends Activity {
private final static ArrayList mActivities = new ArrayList();
private final static Object oLock = new Object();
private Boolean mReCreate = false;
@Override
public void onSaveInstanceState(Bundle savedInstanceState) {
savedInstanceState.putBoolean("mReCreate", true);
super.onSaveInstanceState(savedInstanceState);
}
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
if (savedInstanceState != null) {
mReCreate = savedInstanceState.getBoolean("mReCreate");
}
synchronized (oLock) {
mActivities.add( (mReCreate ? mActivities.size() : 0), this.getClass().getName());
}
}
@Override
protected void onDestroy() {
super.onDestroy();
synchronized (oLock) {
mActivities.remove(this.getClass().getName());
}
}
public final Boolean isForeground() {
synchronized (oLock) {
return mActivities.size() == 0 || mActivities.get(0).equals(this.getClass().getName());
}
}
}
What this does, it keep track of all active Activities and in which order they where opened. We only need to extend all of our Activities using this one, and then we can use isForeground() to check the state of an Activity Object.
public class MyActivity extends ExtendedActivity {
@Override
protected void onResume() {
super.onResume();
if ( isForeground() ) {
// Do something
}
}
}
Sunday, June 30, 2013
Android Activity in Dialog Style
I was working on an Application where I had created a custom settings Activity (Not using the Android built-in settings tools), in order to adopt the same layout style as in the rest of the Application. It worked fine, and now I wanted to add some additional tablet styles as well. When using the Application on a tablet, I wanted a more dialog like view, something without a panel and did not take up the whole screen, but instead placed itself on top of the main activity. When searching the web for a solution to this, the only thing that I could find over and over again, was to add the Dialog Theme to the Activity via AndroidManifest.xml. I had two issues with this. For one, I did not want a Dialog like Activity for all devices, but only for tablets. And second, I have my own custom themes that I apply to all Activities. So I went another way which I was sure would work.
Sure enough it worked, but with one little issue. The space around the Activity was not transparent. It seamed that you could not change the window background from within the Activity. The panel however was removed, the size was set at 70% of the screen size and my custom theme had been applied to the content within, but with a black background surrounding it all.
I needed another work-around. I went to AndroidManifest.xml and applied Androids's Translucent theme to the Activity.
The Activity theme would still be replaced by my Custom theme from within the Activity, but since you cannot change the window background from within there, the transparency would still stick. And then on Phones, you just apply a background to the main View, which you then configure as match_parent for both height and width, which will overlay the transparent window background, making it seam like a regular full-screen Activity.
public class ActivityAppSettings extends Activity {
@Override
protected void onCreate(Bundle savedInstanceState) {
if (getApplication() != null && ((ApplicationBase) getApplication()).mTheme > 0) {
setTheme( ((ApplicationBase) getApplication()).mTheme );
}
super.onCreate(savedInstanceState);
if (getResources().getString(R.string.config_screen_type).equals("xlarge")) {
requestWindowFeature(Window.FEATURE_NO_TITLE);
Rect display = new Rect();
getWindow().getDecorView().getWindowVisibleDisplayFrame(display);
getWindow().setBackgroundDrawable(new ColorDrawable(0));
getWindow().setLayout(
(int) ((display.width() > display.height() ? display.height() : display.width()) * 0.7),
(int) (display.height() * 0.7)
);
}
setContentView(R.layout.activity_app_settings);
}
}
Sure enough it worked, but with one little issue. The space around the Activity was not transparent. It seamed that you could not change the window background from within the Activity. The panel however was removed, the size was set at 70% of the screen size and my custom theme had been applied to the content within, but with a black background surrounding it all.
I needed another work-around. I went to AndroidManifest.xml and applied Androids's Translucent theme to the Activity.
<activity android:theme="@android:style/Theme.Translucent"
The Activity theme would still be replaced by my Custom theme from within the Activity, but since you cannot change the window background from within there, the transparency would still stick. And then on Phones, you just apply a background to the main View, which you then configure as match_parent for both height and width, which will overlay the transparent window background, making it seam like a regular full-screen Activity.
Friday, June 28, 2013
Check Android Uptime
So you are programming an application for Android. You have made a custom caching system which should clear the cache after each boot. One way to do this, could be to create an onBoot Receiver and have that clear out your cache. However, why have your application run on each boot if the user might not even use it all that much? And what if the user installed some Privacy Guard Application and disabled your app's receiver?
The best way to handle this, is to have the cache cleared on the first launch of the application. But in order to do this, you will have to know whether or not this actually is the first launch or not. To help you with this, Android has two useful methods. One to provide the total amount of milliseconds that have past since 1970 (or something like that) and one to provide the total amount of milliseconds that have pasted since the device was booted. Extract the boot time from the total time, and you will have the exact timestamp from when the device was started. Now just save this to your shared preferences and compare it with a fresh timestamp on each application launch.
There is however one tiny issue with this way of checking last boot time, and that is that both methods might take a few milliseconds to execute (depending on the speed of the device) and you can only execute one at a time. This means that your calculation could differ each time you run it, making the comparison with the cached timestamp useless.
The solution to this problem is to check the difference between the two timestamps instead of comparing them to see if they are equal. The methods might take a few milliseconds to execute and any type of device will take several seconds, some even minutes, to boot. So we will just check to see if the two timestamps has a difference less than 3 seconds.
If, when you extract the cached timestamp from the fresh one, get a difference on more than +3000 milliseconds, you can be sure that the device has been rebooted since your last application launch.
The best way to handle this, is to have the cache cleared on the first launch of the application. But in order to do this, you will have to know whether or not this actually is the first launch or not. To help you with this, Android has two useful methods. One to provide the total amount of milliseconds that have past since 1970 (or something like that) and one to provide the total amount of milliseconds that have pasted since the device was booted. Extract the boot time from the total time, and you will have the exact timestamp from when the device was started. Now just save this to your shared preferences and compare it with a fresh timestamp on each application launch.
public class myclass extends something {
/*
* We can use this to avoid to much checking.
* As long as this static property exists, we know that device has not been rebooted and there is no reason to do a check.
*/
private static Boolean oCacheCheck = false;
public SharedPreferences getCache() {
SharedPreferences preferences = .getSharedPreferences("cache", 0x00000000);
if (!oCacheCheck) {
Long freshTime = System.currentTimeMillis() - SystemClock.elapsedRealtime();
Long cachedTime = preferences.getLong("timestamp", 0);
if (freshTime == cachedTime) {
Editor edit = preferences.edit();
edit.clear();
edit.putLong("timestamp", freshTime);
edit.commit();
}
oCacheCheck = true;
}
return preferences;
}
}
if (freshTime == cachedTime) {
There is however one tiny issue with this way of checking last boot time, and that is that both methods might take a few milliseconds to execute (depending on the speed of the device) and you can only execute one at a time. This means that your calculation could differ each time you run it, making the comparison with the cached timestamp useless.
The solution to this problem is to check the difference between the two timestamps instead of comparing them to see if they are equal. The methods might take a few milliseconds to execute and any type of device will take several seconds, some even minutes, to boot. So we will just check to see if the two timestamps has a difference less than 3 seconds.
public class myclass extends something {
/*
* We can use this to avoid to much checking.
* As long as this static property exists, we know that device has not been rebooted and there is no reason to do a check.
*/
private static Boolean oCacheCheck = false;
public SharedPreferences getCache() {
SharedPreferences preferences = .getSharedPreferences("cache", 0x00000000);
if (!oCacheCheck) {
Long freshTime = System.currentTimeMillis() - SystemClock.elapsedRealtime();
Long cachedTime = preferences.getLong("timestamp", 0);
/*
* If this is grater than 3 second, then we will most likely have a fresh application launch.
* No device, no mater how slow, takes 3 seconds or more to execute the time methods.
* And no device, no mater how fast, can boot and launch the application in less than 3 seconds.
*/
if ((freshTime - cachedTime) > 3000) {
Editor edit = preferences.edit();
edit.clear();
edit.putLong("timestamp", freshTime);
edit.commit();
}
oCacheCheck = true;
}
return preferences;
}
}
if ((freshTime - cachedTime) > 3000) {
If, when you extract the cached timestamp from the fresh one, get a difference on more than +3000 milliseconds, you can be sure that the device has been rebooted since your last application launch.
Subscribe to:
Posts (Atom)