how to recognise that the user has just uninstalled your android app

Post on 15-Aug-2015

26 Views

Category:

Mobile

5 Downloads

Preview:

Click to see full reader

TRANSCRIPT

How to recognise that the user has just uninstalled your Android

app

fb.me/pjakubczyk+AleksanderPiotrowski@pelotasplus

Opera Max

The Java way

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>

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)); }

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

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

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

Let’s uninstall our app

and there’s nothing ….

Why ?

OS unregisters listener during removal

What Opera does?

It does not listen for package removal

it does some magic ;-)

… not in Java code

Getting the APK

Getting the APK

● genymotion withgapps installed

● get app from play store● be careful with the right ABI

Getting the APK

1.adb shell2.pm list packages

Getting the APK

3. pm path com.opera.max4. adb pull /data/app/com.opera.max.apk

Hacking APK

Apktool

A tool for reverse engineering Android apk files

Made with <3 in Poland ;-)

Apktool

Easy to use

$ apktool d com.opera.max.apk

Apktool

● decoded XML files● smali assembly code● PNGs, layouts, resources● id-s mapping

with Opera Max APK

live apktool demo

Opera Findings

Found a clue!

There are *.so files

We can inspect them to see more

Tools: strings, objdump, nm, readelf

rudy$ strings opera/lib/armeabi/libuo.so (II)

...inotify_initinotify_add_watchinotify_rm_watch/data/data/%s/%s%s

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.

rudy$ strings opera/lib/armeabi/libuo.so (I)

...Androidstartandroid.intent.action.VIEW--user...

am command

part of Android system/system/bin/am

A way to start apps, intents and whatnot

more details

$ ps

USER PID PPIDu0_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

The scenario

1. Fork the native process2. Inside the child process use inotify to watch

a file3. Watcher is woken up on file deletion. Start

another native process4. The last process run the ‘am’

(ActivityManager) command to run intent.

Setup

JNI

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-sdkndk.dir=/Users/alek/android-ndk-r10e

build.gradle

android.defaultConfig { applicationId "pl.pelotasplus.actionafteruninstall"

ndk { moduleName "hello-jni" ldLibs "log", "android" stl "stlport_static" } }

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");

}}

MainActivity.java calling

protected void onCreate(Bundle savedInstanceState) { setContentView(R.layout.activity_main);

textView = (TextView) findViewById(R.id.textView);

textView.setText(stringFromJNI());

observer(); }

project structure

Native code

JNI

Sample by Google

jstringJava_pl_pelotasplus_actionafteruninstall_MainActivity_stringFromJNI

(JNIEnv* env, jobject thiz){ return (*env)->NewStringUTF(

env,"Hello from JNI ! Compiled with ABI foo."

);}

Android.mk

LOCAL_PATH := $(call my-dir)

include $(CLEAR_VARS)

LOCAL_MODULE := hello-jniLOCAL_SRC_FILES := hello-jni.cLOCAL_LDFLAGS += -llog -lpthreadinclude $(BUILD_SHARED_LIBRARY)

Application.mk

APP_ABI := armeabi-v7a# allAPP_STL := stlport_static

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);}

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"); }

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 );

inotify on Android (pseudo code)

void observer(void) { inotify_init(); inotify_add_watch(fd, DIRECTORY, IN_DELETE); if (event->mask & IN_DELETE) { startIntent(); }}

first attemptvoidJava_pl_pelotasplus_actionafteruninstall_MainActivity_observer(JNIEnv* env, jobject thiz){

observer();}

App blocked as native code blocked app

second attempt, with threadvoidJava_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

third attempt, with forkvoidJava_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(); }}

start intent, another forkvoid 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"); }}

Live demo of our app

https://github.com/pelotasplus/ActionAfterUninstall

Check the dirty source code

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 HackbornAndroid framework engineerhack...@android.com

http://markmail.org/message/ruqp2t6gvhnhv654

top related