I believe I've got the main idea of how they did it. Here is the pieces of the puzzle.
Any Android application can start a process by calling Runtime.exec()
function.
Runtime.getRuntime().exec("chmod 755 '/data/data/my.app/files'/native_code");
After this line of code gets executed there is another process spawned. This process runs under the same linux user as the application itself.
When a user opens Settings -> Apps -> My App and presses "Force stop" button, main application process gets killed, but the process hosting native program (see above) still runs. I personally believe this is a security issue and I am going to report it back to AOSP.
Such native program can run infinitely and do nothing - just sleeping. But before going to sleep, it registers a termination signal handler which will be called when process is about to be terminated by the system.
int main(void) {
signal(SIGTERM, termination_handler);
while(1) {
sleep(10);
}
}
void termination_handler(int sig) {
// handle termination signal here
}
Now you should already know what the last piece is, right? My native termination_handler should be able to launch a browser. I didn't try this in code, but I assume this is possible, because I can do it using adb shell
as following
adb shell am start -a android.intent.action.VIEW -d http://www.google.com
Now back to the question about how Dolphin Browser does it. Install the app and launch it at least once. Once started, it registers a native uninstall watcher using the principles described above. To see it, connect to the device and open adb shell
. Then call ps
to see list of processes. You will see two processes similar to following
u0_a109 315 ... mobi.mgeek.TunnyBrowser
u0_a109 371 ... /data/data/mobi.mgeek.TunnyBrowser/files/watch_server
As you can see it starts a watch_server native program, which is a part of its apk-file. Now open App info
page of Dolphin Browser and press "Force Stop"
. Switch back to terminal and call ps
again. You will see there is no mobi.mgeek.TunnyBrowser process anymore, but watch_server still runs.
By the way this approach will only work, if watcher server runs all the time. To
make sure it is always up, both apps require "run at startup"
permission, where they start their watchers.
Now, when you uninstall the app, Android stops all processes belonging to this application. Watcher receives termination signal and opens browser with predefined URL and then shuts down.
I might look a bit different in some details, but the main concept behind this hack must be as described.
ACTION_QUERY_PACKAGE_RESTART
doesn't always get broadcast. See #20700140 – Narghile