1

My requirement is to start an Activity which is present in an aar file inside your asset folder or may be in SDCard. But I am getting below exception:

01-05 21:06:18.717 3150-3150/com.example.nayakc2.dynamicloading W/System.err: java.lang.ClassNotFoundException: Didn't find class "com.example.nayakc2.aarsample.LibActivity" on path: DexPathList[[],nativeLibraryDirectories=[/system/lib, /vendor/lib]]
01-05 21:06:18.717 3150-3150/com.example.nayakc2.dynamicloading W/System.err:     at dalvik.system.BaseDexClassLoader.findClass(BaseDexClassLoader.java:56)
01-05 21:06:18.717 3150-3150/com.example.nayakc2.dynamicloading W/System.err:     at java.lang.ClassLoader.loadClass(ClassLoader.java:380)
01-05 21:06:18.717 3150-3150/com.example.nayakc2.dynamicloading W/System.err:     at java.lang.ClassLoader.loadClass(ClassLoader.java:312)
01-05 21:06:18.717 3150-3150/com.example.nayakc2.dynamicloading W/System.err:     at com.example.nayakc2.dynamicloading.MainActivity.startActivityFromAar(MainActivity.java:70)
01-05 21:06:18.719 3150-3150/com.example.nayakc2.dynamicloading W/System.err:     at com.example.nayakc2.dynamicloading.MainActivity.onOptionsItemSelected(MainActivity.java:56)
01-05 21:06:18.720 3150-3150/com.example.nayakc2.dynamicloading W/System.err:     at android.app.Activity.onMenuItemSelected(Activity.java:3204)
01-05 21:06:18.720 3150-3150/com.example.nayakc2.dynamicloading W/System.err:     at android.support.v4.app.FragmentActivity.onMenuItemSelected(FragmentActivity.java:408)
01-05 21:06:18.720 3150-3150/com.example.nayakc2.dynamicloading W/System.err:     at android.support.v7.app.AppCompatActivity.onMenuItemSelected(AppCompatActivity.java:198)
01-05 21:06:18.721 3150-3150/com.example.nayakc2.dynamicloading W/System.err:     at android.support.v7.view.WindowCallbackWrapper.onMenuItemSelected(WindowCallbackWrapper.java:113)
01-05 21:06:18.721 3150-3150/com.example.nayakc2.dynamicloading W/System.err:     at android.support.v7.view.WindowCallbackWrapper.onMenuItemSelected(WindowCallbackWrapper.java:113)
01-05 21:06:18.721 3150-3150/com.example.nayakc2.dynamicloading W/System.err:     at android.support.v7.app.ToolbarActionBar$2.onMenuItemClick(ToolbarActionBar.java:69)
01-05 21:06:18.721 3150-3150/com.example.nayakc2.dynamicloading W/System.err:     at android.support.v7.widget.Toolbar$1.onMenuItemClick(Toolbar.java:206)
01-05 21:06:18.721 3150-3150/com.example.nayakc2.dynamicloading W/System.err:     at android.support.v7.widget.ActionMenuView$MenuBuilderCallback.onMenuItemSelected(ActionMenuView.java:776)
01-05 21:06:18.721 3150-3150/com.example.nayakc2.dynamicloading W/System.err:     at android.support.v7.view.menu.MenuBuilder.dispatchMenuItemSelected(MenuBuilder.java:822)
01-05 21:06:18.722 3150-3150/com.example.nayakc2.dynamicloading W/System.err:     at android.support.v7.view.menu.MenuItemImpl.invoke(MenuItemImpl.java:156)
01-05 21:06:18.722 3150-3150/com.example.nayakc2.dynamicloading W/System.err:     at android.support.v7.view.menu.MenuBuilder.performItemAction(MenuBuilder.java:969)
01-05 21:06:18.722 3150-3150/com.example.nayakc2.dynamicloading W/System.err:     at android.support.v7.view.menu.MenuPopup.onItemClick(MenuPopup.java:127)
01-05 21:06:18.722 3150-3150/com.example.nayakc2.dynamicloading W/System.err:     at android.widget.AdapterView.performItemClick(AdapterView.java:310)
01-05 21:06:18.722 3150-3150/com.example.nayakc2.dynamicloading W/System.err:     at android.widget.AbsListView.performItemClick(AbsListView.java:1155)
01-05 21:06:18.722 3150-3150/com.example.nayakc2.dynamicloading W/System.err:     at android.widget.AbsListView$PerformClick.run(AbsListView.java:3120)
01-05 21:06:18.722 3150-3150/com.example.nayakc2.dynamicloading W/System.err:     at android.widget.AbsListView.onTouchUp(AbsListView.java:4047)
01-05 21:06:18.722 3150-3150/com.example.nayakc2.dynamicloading W/System.err:     at android.widget.AbsListView.onTouchEvent(AbsListView.java:3806)
01-05 21:06:18.722 3150-3150/com.example.nayakc2.dynamicloading W/System.err:     at android.support.v7.widget.ListViewCompat.onTouchEvent(ListViewCompat.java:124)
01-05 21:06:18.722 3150-3150/com.example.nayakc2.dynamicloading W/System.err:     at android.view.View.dispatchTouchEvent(View.java:9943)
01-05 21:06:18.722 3150-3150/com.example.nayakc2.dynamicloading W/System.err:     at android.view.ViewGroup.dispatchTransformedTouchEvent(ViewGroup.java:2663)
01-05 21:06:18.722 3150-3150/com.example.nayakc2.dynamicloading W/System.err:     at android.view.ViewGroup.dispatchTouchEvent(ViewGroup.java:2344)
01-05 21:06:18.723 3150-3150/com.example.nayakc2.dynamicloading W/System.err:     at android.view.ViewGroup.dispatchTransformedTouchEvent(ViewGroup.java:2669)
01-05 21:06:18.723 3150-3150/com.example.nayakc2.dynamicloading W/System.err:     at android.view.ViewGroup.dispatchTouchEvent(ViewGroup.java:2358)
01-05 21:06:18.723 3150-3150/com.example.nayakc2.dynamicloading W/System.err:     at android.view.ViewGroup.dispatchTransformedTouchEvent(ViewGroup.java:2669)
01-05 21:06:18.723 3150-3150/com.example.nayakc2.dynamicloading W/System.err:     at android.view.ViewGroup.dispatchTouchEvent(ViewGroup.java:2358)
01-05 21:06:18.723 3150-3150/com.example.nayakc2.dynamicloading W/System.err:     at android.widget.PopupWindow$PopupDecorView.dispatchTouchEvent(PopupWindow.java:2266)
 01-05 21:06:18.724 3150-3150/com.example.nayakc2.dynamicloading W/System.err:     at android.view.View.dispatchPointerEvent(View.java:10163)
01-05 21:06:18.724 3150-3150/com.example.nayakc2.dynamicloading W/System.err:     at android.view.ViewRootImpl$ViewPostImeInputStage.processPointerEvent(ViewRootImpl.java:4434)
01-05 21:06:18.726 3150-3150/com.example.nayakc2.dynamicloading W/System.err:     at android.view.ViewRootImpl$ViewPostImeInputStage.onProcess(ViewRootImpl.java:4302)
01-05 21:06:18.726 3150-3150/com.example.nayakc2.dynamicloading W/System.err:     at android.view.ViewRootImpl$InputStage.deliver(ViewRootImpl.java:3849)
01-05 21:06:18.726 3150-3150/com.example.nayakc2.dynamicloading W/System.err:     at android.view.ViewRootImpl$InputStage.onDeliverToNext(ViewRootImpl.java:3902)
01-05 21:06:18.726 3150-3150/com.example.nayakc2.dynamicloading W/System.err:     at android.view.ViewRootImpl$InputStage.forward(ViewRootImpl.java:3868)
01-05 21:06:18.727 3150-3150/com.example.nayakc2.dynamicloading W/System.err:     at android.view.ViewRootImpl$AsyncInputStage.forward(ViewRootImpl.java:3995)
01-05 21:06:18.727 3150-3150/com.example.nayakc2.dynamicloading W/System.err:     at android.view.ViewRootImpl$InputStage.apply(ViewRootImpl.java:3876)
01-05 21:06:18.727 3150-3150/com.example.nayakc2.dynamicloading W/System.err:     at android.view.ViewRootImpl$AsyncInputStage.apply(ViewRootImpl.java:4052)
01-05 21:06:18.727 3150-3150/com.example.nayakc2.dynamicloading W/System.err:     at android.view.ViewRootImpl$InputStage.deliver(ViewRootImpl.java:3849)
01-05 21:06:18.727 3150-3150/com.example.nayakc2.dynamicloading W/System.err:     at android.view.ViewRootImpl$InputStage.onDeliverToNext(ViewRootImpl.java:3902)
01-05 21:06:18.727 3150-3150/com.example.nayakc2.dynamicloading W/System.err:     at android.view.ViewRootImpl$InputStage.forward(ViewRootImpl.java:3868)
01-05 21:06:18.727 3150-3150/com.example.nayakc2.dynamicloading W/System.err:     at android.view.ViewRootImpl$InputStage.apply(ViewRootImpl.java:3876)
01-05 21:06:18.727 3150-3150/com.example.nayakc2.dynamicloading W/System.err:     at android.view.ViewRootImpl$InputStage.deliver(ViewRootImpl.java:3849)
01-05 21:06:18.728 3150-3150/com.example.nayakc2.dynamicloading W/System.err:     at android.view.ViewRootImpl.deliverInputEvent(ViewRootImpl.java:6210)
01-05 21:06:18.728 3150-3150/com.example.nayakc2.dynamicloading W/System.err:     at android.view.ViewRootImpl.doProcessInputEvents(ViewRootImpl.java:6184)
01-05 21:06:18.728 3150-3150/com.example.nayakc2.dynamicloading W/System.err:     at android.view.ViewRootImpl.enqueueInputEvent(ViewRootImpl.java:6145)
01-05 21:06:18.728 3150-3150/com.example.nayakc2.dynamicloading W/System.err:     at android.view.ViewRootImpl$WindowInputEventReceiver.onInputEvent(ViewRootImpl.java:6313)
01-05 21:06:18.728 3150-3150/com.example.nayakc2.dynamicloading W/System.err:     at android.view.InputEventReceiver.dispatchInputEvent(InputEventReceiver.java:185)
01-05 21:06:18.728 3150-3150/com.example.nayakc2.dynamicloading W/System.err:     at android.os.MessageQueue.nativePollOnce(Native Method)
01-05 21:06:18.728 3150-3150/com.example.nayakc2.dynamicloading W/System.err:     at android.os.MessageQueue.next(MessageQueue.java:323)
01-05 21:06:18.728 3150-3150/com.example.nayakc2.dynamicloading W/System.err:     at android.os.Looper.loop(Looper.java:136)
01-05 21:06:18.728 3150-3150/com.example.nayakc2.dynamicloading W/System.err:     at android.app.ActivityThread.main(ActivityThread.java:6077)
01-05 21:06:18.728 3150-3150/com.example.nayakc2.dynamicloading W/System.err:     at java.lang.reflect.Method.invoke(Native Method)
01-05 21:06:18.728 3150-3150/com.example.nayakc2.dynamicloading W/System.err:     at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:865)
01-05 21:06:18.728 3150-3150/com.example.nayakc2.dynamicloading W/System.err:     at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:755)

Here is my code to achieve this:

private void startActivityFromAar() {
File file = new File("file:///android_asset/sample.aar");
DexClassLoader dexLoader = new DexClassLoader(file.getAbsolutePath(),
        getCacheDir().getAbsolutePath(), null, getClassLoader());
setAPKClassLoader(dexLoader);

try {
    Class<?> activityClass = dexLoader.loadClass("com.example.nayakc2.aarsample.LibActivity");
    Intent intent = new Intent(this, activityClass);
    startActivity(intent);
} catch (ClassNotFoundException e) {
    // TODO Auto-generated catch block
    e.printStackTrace();
}
}

private void setAPKClassLoader(ClassLoader classLoader)
{
try {
    Field mMainThread = getField(Activity.class, "mMainThread");
    Object mainThread = mMainThread.get(this);
    Class threadClass = mainThread.getClass();
    Field mPackages = getField(threadClass, "mPackages");

    Map<String,?> map = (Map<String,?>) mPackages.get(mainThread);
    WeakReference<?> ref = (WeakReference<?>) map.get(getPackageName());
    Object apk = ref.get();
    Class apkClass = apk.getClass();
    Field mClassLoader = getField(apkClass, "mClassLoader");

    mClassLoader.set(apk, classLoader);
} catch (IllegalArgumentException | IllegalAccessException e) {
    // TODO Auto-generated catch block
    e.printStackTrace();
}
}

private Field getField(Class<?> cls, String name)
{
for (Field field: cls.getDeclaredFields())
{
    if (!field.isAccessible()) {
        field.setAccessible(true);
    }
    if (field.getName().equals(name)) {
        return field;
    }
}
return null;
}

Where do I do mistake? Any help is greatly helpful.

Chandra Sekhar
  • 18,914
  • 16
  • 84
  • 125

1 Answers1

-1

My requirement is to start an Activity which is present in an aar file inside your asset folder or may be in SDCard.

That is not going to be practical in general.

Where do I do mistake?

First, an asset is a file on your development machine. It is not a file on the device. file:///android_asset/ is usable in a WebView, but not in general.

Second, you are assuming that your reflection will work across all the devices that you are going to support. At best, this approach works when you are in control over the hardware.

Third, the activity will not be in the manifest, unless you somehow arranged for that yourself when you built your main app.

Fourth, the activity will not have access to the resources in the AAR, such as the resources that it was compiled against. It will attempt to load those resources from your main app, and most likely those resources have different ID values.

CommonsWare
  • 986,068
  • 189
  • 2,389
  • 2,491
  • First: Does that mean, if I will move it to SDCard and give proper URL, it will start working? – Chandra Sekhar Jan 05 '17 at 16:32
  • Second: Why do we need control our hardware to make reflection work? Assuming Java is platform independent. – Chandra Sekhar Jan 05 '17 at 16:33
  • Third: The Activity is in the manifest of AAR file. But at the end its a class, So why can't classloader load it? And moreover, if Activity is undeclared in Manifest, it should throw ActivityNotFoundException, not ClassNotFoundException. – Chandra Sekhar Jan 05 '17 at 16:34
  • @ChandraSekhar: "if I will move it to SDCard and give proper URL, it will start working?" -- probably not, due to the other reasons I cited. Plus you now introduce security risks. "Why do we need control our hardware to make reflection work?" -- first, the OS changes over time, and you cannot assume that the internal workings of Android remain unchanged. Second, device manufacturers routinely change lots of things inside of Android. "The Activity is in the manifest of AAR file" -- so? That's just a text file, which will be ignored by everything. It certainly will not be used by Android. – CommonsWare Jan 05 '17 at 16:37
  • One more thing to ad is I know its not in general usage. I want to make a pluggable application, where user can just download a module which ze wants to use. FYI: We are going to have more than 10k modules. Out of that user might be interested in only 5 or 10 modules. – Chandra Sekhar Jan 05 '17 at 16:37
  • Please don't assume that SDCard meant security risk. It may be internal space of our specific app. – Chandra Sekhar Jan 05 '17 at 16:39
  • @ChandraSekhar: "It may be internal space of our specific app" -- that does not prevent a spearphishing attack to replace that with malware (e.g., via `ACTION_OPEN_DOCUMENT`). Also, that does not prevent the user from removing the SD card, plugging into something else, and that injecting some malware. – CommonsWare Jan 05 '17 at 16:41
  • In that case your database can also be affected same way. Because both will be in same location. – Chandra Sekhar Jan 05 '17 at 16:44
  • @ChandraSekhar: By default, a database is in [internal storage](https://commonsware.com/blog/2014/04/07/storage-situation-internal-storage.html). While there are attacks that can get at internal storage, they are more difficult to execute. [External storage](https://commonsware.com/blog/2014/04/08/storage-situation-external-storage.html) and [removable storage](https://commonsware.com/blog/2014/04/09/storage-situation-removable-storage.html) are each less secure, with different attacks. Plus, a database is not executable code, and executable code raises the risks considerably. – CommonsWare Jan 05 '17 at 16:48
  • Even though my modules are executable codes, they don't have specific to harm my business, as everything which communicates with my server is present in themain application. Not in the aar which is going to be downloaded dynamically and stored in internal storage and then loaded the UI. – Chandra Sekhar Jan 05 '17 at 16:50
  • @ChandraSekhar: "they don't have specific to harm my business" -- you are supposed to be caring about the users. Any code that you dynamically load in your process can do everything that your app can. So, for example, the attacker can grab the data from your app -- and anything else that can be reached -- and send it to *their* server. Or, they could add the user's device to a Mirai-style botnet. Or they could use your app as a springboard for trying other malware attacks against the device (e.g., using towelroot). – CommonsWare Jan 05 '17 at 16:53