Compile .java Code on an Android device from within code [API]? - android

Here is what I want to achieve:
I am building an Android-Application which needs to be plugin-aware. What I need to do is downloading .java files from a Web-Server and then compile them at runtime within my application and then load it into the classpath. I want to use Java because of the ease of use because I can use the plugin objects just like my stock ones.
I've seen javax.tools's way of compiling but that's not available on DalvikVM. What are the best alternatives to that (that work in a similar way)?
EDIT:
I am using .bsh-script now. This works like a charm on a JVM and should work on an Android device (which I will test next):
package de.hotware.beanshell.test;
import bsh.EvalError;
import bsh.Interpreter;
public class BeanShellTest {
public static interface InterfaceTest {
public void sayHello();
}
public static void main(String[] args) {
try {
Interpreter interpreter = new Interpreter();
InterfaceTest res = (InterfaceTest) interpreter.eval("import de.hotware.beanshell.test.BeanShellTest.InterfaceTest;" +
"new InterfaceTest() {" +
"public void sayHello() { System.out.println(\"hello\");}" +
"}");
res.sayHello();
} catch(EvalError e) {
e.printStackTrace();
}
}
public void test() {
}
}

You need to do two things:
Everything you would do if you were running inside a JVM. Download the source, compile it into .class files with javac or equivalent.
Convert the class files to DEX format. This would mean running the dx program on the device, which is doable, but it can be a bit memory-hungry, especially for larger programs.
Once you've done that, you can use DexClassLoader to load the code from the DEX file.
All things considered, I think you're better off with BeanShell.

Related

How to redirect the load of .so lib in Android using Xposed?

I have an app that contains a lib (for example, "test.so") and I want to redirect the load of that .so to another "test.so" which is modified by me, I tried everthing using Xposed like:
public class xposed implements IXposedHookLoadPackage {
public void handleLoadPackage(final LoadPackageParam lpparam) throws Throwable {
if (lpparam.packageName.equals("package")) {
findAndHookMethod("java.lang.System", lpparam.classLoader, "loadLibrary", String.class, new XC_MethodHook() {
#Override
protected void beforeHookedMethod(MethodHookParam param){
XposedBridge.log("(before) Loaded library: " + param.args[0]);
if (param.args[0].toString().equals("test")){
System.load("/data/data/package/modified_test.so");
}
}
});
}
}
}
The app crashes, I searched on Google and I found this: System.loadlibrary hook crash but when I want to hook into Runtime like rovo said it doesn't do nothing. Someone know any solution?
(Please don't tell me about changing the lib into .apk because if I want that I didn't asking this xD).
Your code can not work because you are hooking the wrong method.
System.loadlibrary expects as parameter the library name without file path, without prepended lib and without file extension. Therefore if you replace the parameter with "/data/data/package/modified_test.so" as shown in your example the library loading will not work.
I assume you may have more luck if you hook the method that is responsible for mapping the library name to the actual library file: System.mapLibraryName(String).
You can see how it is used in Runtime.loadLibrary(String, ClassLoader)
To do so use an afterHookedMethod, check the result value and overwrite the return value with the path to your modified library.

Optimum way to load native shared library in Android

A work collegue has implemented a class to load native C++ shared libraries into our Android app, he named this class 'LibLoader'. His proposed solution was to instantiate a LibLoader object every time we needed to use one of the native functions declared in the native library. I believe this is not optimum from a performance point of view so I was thinking about the best way to optimize this.
So far two solutions have come into my mind:
Make the LibLoader class a singleton
Turn the native methods into static ones so I won't even have to make an object
Considering native shared libraries are loaded through static/instace initializacion in the class, my questions are:
Which of these two approaches would be the best from a performance point of view? I need my code to be fast, I'm calling these native functions several times to compute FFTs on real time audio samples
Is there another optimum way to do this?
What happens to static/instance initialization if the native methods are converted to static ones? Will it be called every time a static method is accessed?
My code is:
public class LibLoader {
static final String TAG = "LibLoader";
static boolean armv7 ;
static
{
String arch = System.getProperty("os.arch");
//determine which library to load according to CPU type
if(arch.contentEquals("armv7l"))
{
//fftw neon compiled library functions work with armv71 and armv6
try {
System.loadLibrary("fftwfNeon_fftTwiddle"); //this won't load from any other platform
armv7 = true;
}catch (UnsatisfiedLinkError e)
{
Log.e(TAG, "Unable to load fftwfNeon_fftTwiddle library "+ e.getMessage());
}
}
else
{
try {
System.loadLibrary("fftTwiddle");
armv7 = false;
}catch (UnsatisfiedLinkError e)
{
Log.e(TAG, "Unable to load fftTwiddle library "+ e.getMessage());
}
}
}
public native void GetComplexFFtDoubleIN(double[] realIN, double[] imagIN, int fftSize, double[] TW, boolean ifftFlag);
public native void FFTWfNeonSymb(int fftSize, float[] realPart, float[] imagPart, boolean isFFT);
public native void FFTWfNeonSync(int fftSize, float[] realPart, float[] imagPart, boolean isFFT);
}
Having your methods static vs creating an instance variable for the class, does not affect CPU performance much. But these 2 implementations differs a lot in memory usage.
If it is only FFT calculations then, I would suggest to keep it static. This can keep you code free from memory leaks.
Update: Creating a singleton is between the 2 options I have explained. Here are the 3 ways you can do it in the order of memory simplicity.
All methods are static and you directly access the methods from any class you want. (Only has the class in memory)
Having a singleton variable. Creating a new variable will always return the same static variable. (Keeps the class and the static variable in memory)
Creating a new variable and deleting the memory after using the required methods in the class. (Allocates memory when needed and clears it when not in use.)

How to get version info from a shared library into the Android manifest?

We have a shared library that contains version info and is referenced by all our projects in our Visual Studio Solution.
For the most part, we can reference the version string from every project and the dll reflect the info accordingly.
My issue here is, with our Android application (xamarin based). It has a manifest file which contains the versionName and versionCode.
How can we make those values in our android manifest file read from our shared project?
My understanding is that, it is not possible. Because
The manifest file presents essential information about your app to the Android system, information the system must have before it can run any of the app's code.
From Google's documentation
So this is a file that is required before the App builds.
C# Code in Shared Project (SAP/PCL) is ready to be used only after successful Compilation. So logically setting the Version Code and Version Name in Android Manifest File from Shared logic is not possible.
Another standard approach would be to set it from String Resource (XML) file in Android. You may have to copy and paste the value from Shared Project to strings.xml file and refer it in manifest, like
#string/versionCode
Note: I do not know anything about xamarin.
In java you can get the versioninfo from the manifest like this
public static String getAppVersionName(final Context context) {
try {
final String versionName = context.getPackageManager()
.getPackageInfo(context.getPackageName(), 0).versionName;
return versionName;
} catch (final NameNotFoundException e) {
}
return null;
}
I assume that xamarin has some mechanism to call PackageManager to get Packageinfo, too
You could do this by using a Dependency Service. Here's a great article on them: https://developer.xamarin.com/guides/xamarin-forms/dependency-service/
The idea would be your Dependency Service would expose the Android specific information to the shared code library.
For instance you might have an interface in your common code declared such as:
public interface IPlatformVersionInfo
{
string GetOSVersion ();
}
Now, in your Android library you would implement it:
public class PlatformVersionInfo : IPlatformVersionInfo
{
public string GetOSVersion () {
return Android.OS.Build.VERSION.SdkInt.ToString ();
}
}
Finally, in your common code you would use your dependency service of choice to invoke an instance of it:
var osVersion = DependencyService.Get<IPlatformVersionInfo>().GetOSVersion ();
Of course this is somewhat pseudo-code and depending what dependency service you choose the code may look a bit different.

Android maps exception java.lang.NoClassDefFoundError: android.security.MessageDigest

I have an app which uses Google Maps (v1) and from the crash reports, I am seeing this exception from time to time:
java.lang.NoClassDefFoundError: android.security.MessageDigest
at com.google.android.maps.KeyHelper.getSignatureFingerprint(KeyHelper.java:60)
at com.google.android.maps.MapActivity.createMap(MapActivity.java:513)
at com.google.android.maps.MapActivity.onCreate(MapActivity.java:409)
I have defined
<uses-library
android:name="com.google.android.maps"
android:required="true" />
inside the application tag and I am extending MapActivity as well. The application works fine on most devices but there are some uncommon ones that report this exception, usually on Android 4.0.4 like Woxter Tablet PC 90BL, TAB9008GBBK and other generic names.
From what I read in Stackoverflow, it is a problem in the ROM and it can be solved by the user doing some advanced tricks but what I want is to prevent this crash, as I don't think it can be solved, I just want to inform the user (and thell him to buy a better device :) and disable maps functionality instead of crashing. But I can't find a way to handle this error or test it with the devices I have.
Also my main activity is based on MapActivity so I don't know how can I handle this exception before opening it.
Disclaimer: I've not come across this error on any of my apps / devices but I solved a similar problem. May be that same technique can help you.
Given that the class is either unavailable or an exception occurrs while loading the class, why not try to force load it when your application starts ? Class.forName("android.security.MessageDigest") should load the class and you can catch the Error thrown from that call. I know its dirty, but it should work. You can declare a custom Application class on the manifest to make this check.
Class loading test
try
{
Class.forName("android.security.MessageDigest");
}
catch (Throwable e1)
{
e1.printStackTrace();
//Bad device
}
You can also perform a litmus test and check the functionality of the class should the class loading succeed by digesting a simple String.
Functional test
try
{
MessageDigest digester = MessageDigest.getInstance("MD5");
digester.update("test".getBytes("UTF-8"));
byte[] digest = digester.digest();
}
catch (Throwable e1)
{
e1.printStackTrace();
// Class available but not functional
}
If the class loading / litmus test fails, update a shared preference flag and let the user know that his device sucks :)
Try to change the import android.security.MessageDigest to java.security.MessageDigest
by the look at this link:
What is 'android.security.MessageDigest''?
It looks that the android.security.MessageDigest was remove from Honeycomb so change it to the java one. and check this link as well:
http://productforums.google.com/forum/#!category-topic/maps/google-maps-for-mobile/KinrGn9DcIE
As been suggested there by #XGouchet:
Try downloading the latest version of the Google Maps API and rebuild your application with targetSDK set to the highest available (as of today it should be 17 / Jelly Bean).
The class android.security.MessageDigest is an abstract class (see MessageDigest API) what means that it can't be instantiated right away. So what happens is, that any time a device/app can't find an implementation of this class you will get the exception above, namely
java.lang.NoClassDefFoundError: android.security.MessageDigest
It's a good question why this happens. May be some phone vendors didn't ship their phone with the required library that actually implements this abstract class. I faced a similar issue with the TUN.ko module in the past.
Approach 1
What should help is, if you provide your own (empty) implementation of this class that "implements" the abstract classes and methods like this:
public class MessageDigestSpi extends Object {
byte[] engineDigest() { return new byte[0]; }
void engineReset() { }
void engineUpdate(byte[] input, int offset, int len) { }
}
public class MessageDigest extends MessageDigestSpi {
}
... and put those classes into the folder <src>/java/security/. So this way you provide your own implementation that is always found and might contain some code in order to inform the user or provide an alternative implementation.
So the remaining questions are: what does the app do, if the implementation is provided by the system, too and how to control that the system implementation is the first choice?
The answer: which implementation is chosen depends on the import order. Looking at Eclipse you can define the order in the project properties, Java build path, tab order and export. Be sure that you have any system libraries on top that might include the system implementation (most likely the Android libraries). This way the system searches in those libraries first. If nothing is found your classes get loaded and executed.
Approach 2
As an alternative to the implementation in an own abstract class you could of course simply instantiate the MessageDigest class, catch the NoClassDefFoundError exception and store the result for later evaluation:
import android.security.MessageDigest;
public class MessageDigestTester {
private static Boolean messageDigestAvailable = null;
public static Boolean isLibraryAvailable() {
if (messageDigestAvailable == null) {
try {
MessageDigest digest = MessageDigest.getInstance("MD5");
messageDigestAvailable = true;
} catch (NoClassDefFoundError e) {
messageDigestAvailable = false;
}
}
return messageDigestAvailable;
}
}
Then use if (MessageDigestTester.isLibraryAvailable()) { } else { } in your code in order to encapsulate the usage of this library and to provide an alternative.
Approach two is easier to implement whereas approach one is the more sophisticated solution.
Hope this was helpful ... Cheers!

Is it possible to dynamically load a library at runtime from an Android application?

Is there any way to make an Android application to download and use a Java library at runtime?
Here is an example:
Imagine that the application needs to make some calculations depending on the input values. The application asks for these input values and then checks if the required Classes or Methods are available.
If not, it connects to a server, downloads the needed library, and loads it at runtime to calls the required methods using reflection techniques. The implementation could change depending on various criteria such as the user who is downloading the library.
Sorry, I'm late and the question has already an accepted answer, but yes, you can download and execute external libraries. Here is the way I did:
I was wondering whether this was feasible so I wrote the following class:
package org.shlublu.android.sandbox;
import android.util.Log;
public class MyClass {
public MyClass() {
Log.d(MyClass.class.getName(), "MyClass: constructor called.");
}
public void doSomething() {
Log.d(MyClass.class.getName(), "MyClass: doSomething() called.");
}
}
And I packaged it in a DEX file that I saved on my device's SD card as /sdcard/shlublu.jar.
Then I wrote the "stupid program" below, after having removed MyClass from my Eclipse project and cleaned it:
public class Main extends Activity {
#SuppressWarnings("unchecked")
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
try {
final String libPath = Environment.getExternalStorageDirectory() + "/shlublu.jar";
final File tmpDir = getDir("dex", 0);
final DexClassLoader classloader = new DexClassLoader(libPath, tmpDir.getAbsolutePath(), null, this.getClass().getClassLoader());
final Class<Object> classToLoad = (Class<Object>) classloader.loadClass("org.shlublu.android.sandbox.MyClass");
final Object myInstance = classToLoad.newInstance();
final Method doSomething = classToLoad.getMethod("doSomething");
doSomething.invoke(myInstance);
} catch (Exception e) {
e.printStackTrace();
}
}
}
It basically loads the class MyClass that way:
create a DexClassLoader
use it to extract the class MyClass from "/sdcard/shlublu.jar"
and store this class to the application's "dex" private directory (internal storage of the phone).
Then, it creates an instance of MyClass and invokes doSomething() on the created instance.
And it works... I see the traces defined in MyClass in my LogCat:
I've tried on both an emulator 2.1 and on my physical HTC cellphone (which is running Android 2.2 and which is NOT rooted).
This means you can create external DEX files for the application to download and execute them. Here it was made the hard way (ugly Object casts, Method.invoke() ugly calls...), but it must be possible to play with Interfaces to make something cleaner.
Wow. I'm the first surprised. I was expecting a SecurityException.
Some facts to help investigating more:
My DEX shlublu.jar was signed, but not my app
My app was executed from Eclipse / USB connection. So this is an unsigned APK compiled in DEBUG mode
Shlublu's anwser is really nice. Some small things though that would help a beginner:
for library file "MyClass" make a separate Android Application project which has the MyClass file as only file in the src folder (other stuff, like project.properties, manifest, res, etc. should also be there)
in library project manifest make sure you have:
<application android:icon="#drawable/icon"
android:label="#string/app_name">
<activity android:name=".NotExecutable"
android:label="#string/app_name">
</activity>
</application>
(".NotExecutable" is not a reserved word. It is just that I had to put something here)
For making the .dex file, just run the library project as android application (for the compiling) and locate .apk file from the bin folder of the project.
Copy the .apk file to your phone and rename it as shlublu.jar file (an APK is actually a specialization of a jar, though)
Other steps are the same as described by Shlublu.
Big thanks to Shlublu for cooperation.
Technically should work but what about Google rules?
From: play.google.com/intl/en-GB/about/developer-content-policy-pr‌​int
An app distributed via Google Play may not modify, replace or update
itself using any method other than Google Play’s update mechanism.
Likewise, an app may not download executable code (e.g. dex, JAR, .so
files) from a source other than Google Play. This restriction does not
apply to code that runs in a virtual machine and has limited access to
Android APIs (such as JavaScript in a WebView or browser).
I am not sure if you can achieve this by dynamically loading java code. May be you can try embedding a script engine your code like rhino which can execute java scripts which can be dynamically downloaded and updated.
sure, it is possible. apk which is not installed can be invoked by host android application.generally,resolve resource and activity's lifecircle,then,can load jar or apk dynamically.
detail,please refer to my open source research on github: https://github.com/singwhatiwanna/dynamic-load-apk/blob/master/README-en.md
also,DexClassLoader and reflection is needed, now look at some key code:
/**
* Load a apk. Before start a plugin Activity, we should do this first.<br/>
* NOTE : will only be called by host apk.
* #param dexPath
*/
public DLPluginPackage loadApk(String dexPath) {
// when loadApk is called by host apk, we assume that plugin is invoked by host.
mFrom = DLConstants.FROM_EXTERNAL;
PackageInfo packageInfo = mContext.getPackageManager().
getPackageArchiveInfo(dexPath, PackageManager.GET_ACTIVITIES);
if (packageInfo == null)
return null;
final String packageName = packageInfo.packageName;
DLPluginPackage pluginPackage = mPackagesHolder.get(packageName);
if (pluginPackage == null) {
DexClassLoader dexClassLoader = createDexClassLoader(dexPath);
AssetManager assetManager = createAssetManager(dexPath);
Resources resources = createResources(assetManager);
pluginPackage = new DLPluginPackage(packageName, dexPath, dexClassLoader, assetManager,
resources, packageInfo);
mPackagesHolder.put(packageName, pluginPackage);
}
return pluginPackage;
}
your demands is only partly of function in the open source project mentioned at the begining.
If you're keeping your .DEX files in external memory on the phone, such as the SD card (not recommended! Any app with the same permissions can easily overwrite your class and perform a code injection attack) make sure you've given the app permission to read external memory. The exception that gets thrown if this is the case is 'ClassNotFound' which is quite misleading, put something like the following in your manifest (consult Google for most up to date version).
<manifest ...>
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"
android:maxSdkVersion="18" />
...
</manifest>
I think #Shlublu answer is correct but i just want to highlight some key points.
We can load any classes from external jar and apk file.
In Any way, we can load Activity from external jar but we can not start it because of the context concept.
To load the UI from external jar we can use fragment. Create the instance of the fragment and embedded it in the Activity. But make sure fragment creates the UI dynamically
as given below.
public class MyFragment extends Fragment {
#Nullable
#Override
public View onCreateView(LayoutInflater inflater, #Nullable ViewGroup
container, #Nullable Bundle savedInstanceState)
{
super.onCreateView(inflater, container, savedInstanceState);
LinearLayout layout = new LinearLayout(getActivity());
layout.setLayoutParams(new
LinearLayout.LayoutParams(LinearLayout.LayoutParams.MATCH_PARENT,
LinearLayout.LayoutParams.MATCH_PARENT));
Button button = new Button(getActivity());
button.setText("Invoke host method");
layout.addView(button, LinearLayout.LayoutParams.MATCH_PARENT,
LinearLayout.LayoutParams.WRAP_CONTENT);
return layout;
}
}

Categories

Resources