Upgrade to Pro — share decks privately, control downloads, hide ads and more …

How to recognise that the user has just uninstalled your app

How to recognise that the user has just uninstalled your app

Presented during Droidcon.de 2015 barcamp session.

Hacking Android to get a notification when your app is being uninstalled. Mixing JNI and native C code to do something that was never meant to be done ;-)

Aleksander Piotrowski

June 03, 2015

More Decks by Aleksander Piotrowski

Other Decks in Programming


  1. How to recognise that the user has just uninstalled your

    Android app fb.me/pjakubczyk +AleksanderPiotrowski @pelotasplus
  2. Read the broadcast <receiver android:name=".PackageWatcher"> <intent-filter> <action android:name="android.intent.action. PACKAGE_ADDED"/> <action

    android:name="android.intent.action. PACKAGE_REMOVED"/> <action android:name="android.intent.action. PACKAGE_REPLACED" /> <data android:scheme="package"/> </intent-filter> </receiver>
  3. Read the broadcast void onReceive(Context context, Intent intent) { Bundle

    bundle = intent.getExtras(); Iterator<String> it = bundle.keySet().iterator; while (it.hasNext()) { String key = it.next(); Log.e("DDD", key +"="+bundle.get(key)); }
  4. Usually we see (install) E/DDD (29199): Dumping Intent start [android.intent.extra.UID=10089]

    [android.intent.extra.user_handle=0] E/DDD (29199): Dumping Intent end
  5. Usually we see (reinstall) E/DDD (29199): Dumping Intent start [android.intent.extra.REMOVED_FOR_ALL_USERS=false]

    [android.intent.extra.UID=10089] [android.intent.extra.DATA_REMOVED=false] [android.intent.extra.REPLACING=true] [android.intent.extra.user_handle=0] E/DDD (29199): Dumping Intent end
  6. Usually we see (uninstall) E/DDD (29199): Dumping Intent start [android.intent.extra.REMOVED_FOR_ALL_USERS=true]

    [android.intent.extra.UID=10089] [android.intent.extra.DATA_REMOVED=true] [android.intent.extra.user_handle=0] E/DDD (29199): Dumping Intent end
  7. Let’s uninstall our app and there’s nothing …. Why ?

    OS unregisters listener during removal
  8. What Opera does? It does not listen for package removal

    it does some magic ;-) … not in Java code
  9. Getting the APK • genymotion with gapps installed • get

    app from play store • be careful with the right ABI
  10. Apktool • decoded XML files • smali assembly code •

    PNGs, layouts, resources • id-s mapping
  11. Found a clue! There are *.so files We can inspect

    them to see more Tools: strings, objdump, nm, readelf
  12. inotify framework http://linux.die.net/man/7/inotify The inotify API provides a mechanism for

    monitoring file system events. Inotify can be used to monitor individual files, or to monitor directories.
  13. more details $ ps USER PID PPID u0_a91 24318 20265

    246900 27716 ffffffff b6edf5cc S com.opera.max u0_a91 24337 24318 856 336 c00e4944 b6f72158 S /data/app-lib/com.opera.max-2/libuo.so
  14. The scenario 1. Fork the native process 2. Inside the

    child process use inotify to watch a file 3. Watcher is woken up on file deletion. Start another native process 4. The last process run the ‘am’ (ActivityManager) command to run intent.
  15. local.properties # Location of the SDK. This is only used

    by Gradle. # For customization when using a Version Control System, please read the sdk.dir=/Users/alek/android-sdk ndk.dir=/Users/alek/android-ndk-r10e
  16. MainActivity.java declaring public class MainActivity extends AppCompatActivity { public native

    String stringFromJNI(); public native void observer(); static { System.loadLibrary("hello-jni"); // System.loadLibrary("/data/data/com.foo.test/lib/liba.so"); } }
  17. MainActivity.java calling protected void onCreate(Bundle savedInstanceState) { setContentView(R.layout.activity_main); textView =

    (TextView) findViewById(R.id.textView); textView.setText(stringFromJNI()); observer(); }
  18. Sample by Google jstring Java_pl_pelotasplus_actionafteruninstall_MainActivity_stringFro mJNI (JNIEnv* env, jobject thiz)

    { return (*env)->NewStringUTF( env, "Hello from JNI ! Compiled with ABI foo." ); }
  19. Android.mk LOCAL_PATH := $(call my-dir) include $(CLEAR_VARS) LOCAL_MODULE := hello-jni

    LOCAL_SRC_FILES := hello-jni.c LOCAL_LDFLAGS += -llog -lpthread include $(BUILD_SHARED_LIBRARY)
  20. inotify on Linux int main( int argc, char **argv) {

    int length, i = 0; int fd; int wd; char buffer[BUF_LEN]; fd = inotify_init(); printf("fd=%d\n", fd); }
  21. inotify on Linux int main( int argc, char **argv) {

    [...] wd = inotify_add_watch(fd, "/var/tmp", IN_MODIFY | IN_CREATE | IN_DELETE); length = read( fd, buffer, BUF_LEN ); printf("length=%d\n", length); if (length < 0) { perror("read"); }
  22. inotify on Linux while (i < length) { struct inotify_event

    *event = (struct inotify_event*)&buffer[ i]; printf("Event len %d\n", event->len); if (event->len) { if (event->mask & IN_DELETE) { if (event->mask & IN_ISDIR) { printf( "The directory %s was deleted.\n", event->name ); } else { printf( "The file %s was deleted.\n", event->name );
  23. inotify on Android (pseudo code) void observer(void) { inotify_init(); inotify_add_watch(fd,

    DIRECTORY, IN_DELETE); if (event->mask & IN_DELETE) { startIntent(); } }
  24. second attempt, with thread void Java_pl_pelotasplus_actionafteruninstall_MainActivity_observer (JNIEnv* env, jobject thiz)

    { pthread_attr_init(&attr); pthread_create(&thread, &attr, &observer_thread, NULL); } App not blocked but native code stopped when stopping app for uninstalling
  25. third attempt, with fork void Java_pl_pelotasplus_actionafteruninstall_MainActivity_observer(JNIEnv* env, jobject thiz) {

    pid_t pid; pid = fork(); if (pid == 0) { __android_log_print(ANDROID_LOG_INFO, TAG, "Fork child\n"); observer(); } }
  26. start intent, another fork void startIntent(void) { pid_t p =

    fork(); if (p == 0) { __android_log_print(ANDROID_LOG_INFO, TAG, "startIntent %d", getpid()); system("/system/bin/am start --user 0 -a android.intent. action.VIEW -d http://droidcon.de"); } }
  27. Moral > What happens when I call fork() in JNI

    code? Will this totally break the > Activity lifecycle model in Android? Don't do this. Just don't. -- Dianne Hackborn Android framework engineer [email protected] http://markmail.org/message/ruqp2t6gvhnhv654