Shared user ID & Process name

The Shared user ID developer option allows generating clones with a specific shared user ID, an arbitrary string value, which must contain at least one ‘.’ separator. Apps with the same shared user ID can access each other’s data and, if desired, run in the same process, provided the apps are also signed with the same certificate.

The Process name developer option forces all app components to run in the named process. The process name must start with a lower case character and must also contain at least one ‘.’ separator.

This allows creating your own add-ons in separate apps, extending the clone’s functionality.

By signing the clone using a custom certificate (using the Custom certificate option) and creating your own app using the same shared user ID, process name and certificate, the clone and your app will run in the same process and virtual machine (VM).

Each app is executed using its own class loader, which loads classes from the corresponding APK .dex files, so a simple Class.forName("...") won’t work. To access the cloned app’s classes via reflection you must enumerate all threads, find the thread called ContextClassLoaderThread, which is provided by App Cloner, and use its context class loader to load the app’s classes.

Here’s an example in Kotlin:

val threads = arrayOfNulls<Thread>(Thread.activeCount())
Thread.enumerate(threads)
for (thread in threads) {
    if ("ContextClassLoaderThread" == thread!!.getName()) {
        val contextClassLoader = thread.getContextClassLoader()
        val clazz = contextClassLoader.loadClass("...")
        // TODO: Do something with the class
        break
    }
}

If the ContextClassLoaderThread thread cannot be found it means the clone hasn’t yet been launched into the process.

In order to get the cloned app’s Application instance you can use the Utils class provided by App Cloner:

val utilsClass = contextClassLoader.loadClass("com.applisto.appcloner.classes.Utils")
val getApplicationMethod = utilsClass.getMethod("getApplication")
val application: Application = getApplicationMethod.invoke(null) as Application

Once you have the Application instance you can register ActivityLifecycleCallbacks to get notified when the cloned app appears on the screen:

application.registerActivityLifecycleCallbacks(object : ActivityLifecycleCallbacks {
override fun onActivityCreated(activity: Activity, savedInstanceState: Bundle?) {}
override fun onActivityStarted(activity: Activity) {}
override fun onActivityResumed(activity: Activity) {}
override fun onActivityPaused(activity: Activity?) {}
override fun onActivityDestroyed(activity: Activity?) {}
override fun onActivitySaveInstanceState(activity: Activity?, outState: Bundle?) {}
override fun onActivityStopped(activity: Activity?) {}
})

The Shared user ID option is available with the medium donation, the Process name option is available with the large donation.