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

How to recognise that the user has just uninsta...

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
Tweet

More Decks by Aleksander Piotrowski

Other Decks in Programming

Transcript

  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