Let me try to clarify.
Before Android 4.4 we could use inotify
, which provides a mechanism for monitoring filesystem events, we create a daemon to monitor if a file we created in our app's directory is been deleted or our main app directory is deleted, which should happen when user uninstall the app, the JNI code will look something like this:
// initializes a new inotify instance and returns a file descriptor
fd = inotify_init();
// watch directory delete/create events
inotify_add_watch(fd, DIRECTORY, IN_DELETE | IN_CREATE);
__android_log_print(ANDROID_LOG_INFO, "TAG", "Watching [%s]", DIRECTORY);
// TODO: implement checkIfDeleted
if(checkIfDeleted()) {
// execute intent to open url
system("/system/bin/am start --user 0 -a android.intent.action.VIEW -d https://www...");
This no longer works because uninstall also kill group processes, attaching the relevant code from current source code


Take a look at git log
I need more time to investigate on unrooted device, 360security pack the app with specific architecture (a.k.a. ABI) and probably per API to reduce APK size, unfortunately apkmirror(.com) has only ARM available for download, I prefer to read x86, might edit this answer in the near future.
So far it seems the native code is creating files and playing with the locks to detect when the process is dead after the uninstall and then use JNI interface to invoke the callback.
To simplify, it seems it locks itself, and then join the synchronization module notify_and_waitfor
.
You can see a native code example for Android 5.0 here
NativeHelper source code (decompiled):
All method with keyword native
are implemented inside eternity
binary
package com.qihoo.eternity;
import com.qihoo.eternity.b;
public class NativeHelper {
static {
try {
System.loadLibrary((String)"eternity");
}
catch (Exception exception) {}
}
public native void look(String var1, String var2, String var3, String var4, String var5);
public void onU() {
b.a().g();
}
public native void pass(String var1, String var2);
public void peerDead() {
b.a().f();
}
public native void watch(String var1, String var2, String var3, String var4);
public native void watch2(String var1, String var2, String var3, String var4, String var5);
}
App references for NativeHelper:
com/qihoo/eternity/b.java:203:
new NativeHelper().look(b.this.h.getPackageName(), b.b((b)b.this).f, string2, b.b(b.this.i), string3);
com/qihoo/eternity/b.java:224:
new NativeHelper().watch(new File(file, "a1").getAbsolutePath(), new File(file, "a2").getAbsolutePath(), new File(file, "a3").getAbsolutePath(), new File(file, "a4").getAbsolutePath());
com/qihoo/eternity/b.java:264:
new NativeHelper().watch(new File(file, "a2").getAbsolutePath(), new File(file, "a1").getAbsolutePath(), new File(file, "a4").getAbsolutePath(), new File(file, "a3").getAbsolutePath());
com/qihoo/eternity/b.java:518:
new NativeHelper().pass(this.a, this.b);
com/qihoo/eternity/b.java:563:
new NativeHelper().watch2(new File(file, "b1").getAbsolutePath(), new File(file, "b2").getAbsolutePath(), new File(file, "b3").getAbsolutePath(), new File(file, "b4").getAbsolutePath(), b.this.h.getDir("lib", 0).getAbsolutePath());
com/qihoo/eternity/b.java:588:
new NativeHelper().watch2(new File(file, "b2").getAbsolutePath(), new File(file, "b1").getAbsolutePath(), new File(file, "b4").getAbsolutePath(), new File(file, "b3").getAbsolutePath(), b.this.h.getDir("lib", 0).getAbsolutePath());
One solution will be to include the shared object eternity.so
in your JNI folder and implement NativeHelper.onU
method :)