Android and ProGuard

Hello Monkeys!!!

I’m trying to obfuscate a jMonkey project with the Enable Android Deployment checked and the last SDK.

I’ve the following error:
C:\Users\Javi\Documents\galvezgames\PruebaProGuard3\nbproject\mobile-impl.xml:36: The following error occurred while executing this line:
C:\Users\Javi\Documents\galvezgames\PruebaProGuard3\nbproject\mobile-impl.xml:93: src ‘C:\Users\Javi\Documents\galvezgames\PruebaProGuard3\dist\lib\assets.jar’ doesn’t exist.

I’ve looking around the forum and the web but I didn’t find a solution.

When I compile and clean the project without the android option checked I can obfuscate it.
When I compile and clean the project whit the android deployment checked but with the obfuscation disabled the process ends without problem.

Any ideas??

Thanks,

I don’t know about ProGuard, but assets.jar is extracted during the build process in mobile-impl.xml. The files inside assets.jar are copied into the mobile/assets directory and then the assets.jar file is deleted from the mobile project. This was done due to Android’s SoundPool / MediaPlayer not being able to play sounds that are packed inside of jar files.

The fact that the assets.jar file itself is not being copied into the mobile/libs directory may be what is causing your issue.

You can try to modify the necessary targets from mobile-impl.xml and overriding them in the build.xml file to not do this if you want. Just be aware of the audio issue. Using the new OpenAL Soft audio renderer for Android doesn’t have this issue if you need to play audio from within the assets.jar file, but it is only supported on Android version 2.3 and above.

http://hub.jmonkeyengine.org/forum/topic/new-experimental-android-audio-renderer-openal-soft/

The targets in mobile-impl.xml to look at are the following:
-unzip-assets => unzips the files from assets.jar and places them in mobile/assets
-copy-project-libs => deletes the assets.jar file from the mobile project

copy these targets into build.xml and then modify as necessary.

Thanks for your help iwgeric.

I’ve copied the targets:
-unzip-assests
-copy-projects-libs

To the build.xmp and I played a while with them. I’ve no idea of Ant so I’ve learn a lot. In any case I haven’t found a solution. I’m not sure if I understand your explanation.

The problem is in the line:

<unzip src=“dist/lib/${assets.jar.name}” dest=“mobile/assets”/>
inside the -unzip-assests.

The process is looking for a jar in the dist/lib/ directory, but this directory is empty. In some point of the build the files in the directory are deleted. If I see this directory while jMonkey SDK is compiling my project I can see the assets.jar file appear and after a while dissapear. I can’t find when this file is deleted. I think that this process is not done in the targets that you point.

In any case, should android deployment and obfuscation work? I mean, is a bug in the sdk or I’m doing something wrong.

@galvez6000 said: Thanks for your help iwgeric.

I’ve copied the targets:
-unzip-assests
-copy-projects-libs

To the build.xmp and I played a while with them. I’ve no idea of Ant so I’ve learn a lot. In any case I haven’t found a solution. I’m not sure if I understand your explanation.

The problem is in the line:

<unzip src=“dist/lib/${assets.jar.name}” dest=“mobile/assets”/>
inside the -unzip-assests.

The process is looking for a jar in the dist/lib/ directory, but this directory is empty. In some point of the build the files in the directory are deleted. If I see this directory while jMonkey SDK is compiling my project I can see the assets.jar file appear and after a while dissapear. I can’t find when this file is deleted. I think that this process is not done in the targets that you point.

In any case, should android deployment and obfuscation work? I mean, is a bug in the sdk or I’m doing something wrong.

The targets I pointed you to only deal with copying the jars from the main project dist/lib directory to the mobile/lib directory and extracting/deleting the necessary libs from the mobile/libs directory before sending the project to the Android SDK to build the apk.

the dist/lib directory of the main project is dealt with by the main project ant process starting with build-impl.xml.

In the target -copy-project-libs remove the line that deletes the assets.jar file
In the target -copy-android-libs remove the line that calls the -unzip-assets target

Then, when you build your project, the mobile/libs directory should have an assets.jar file.

Thanks for your answer.

Now my build.xml file looks like this:

[java]<?xml version=“1.0” encoding=“UTF-8”?>
<!-- You may freely edit this file. See commented blocks below for -->
<!-- some examples of how to customize the build. -->
<!-- (If you delete it and reopen the project it will be recreated.) -->
<!-- By default, only the Clean and Build commands use this build script. -->
<!-- Commands such as Run, Debug, and Test only use this build script if -->
<!-- the Compile on Save feature is turned off for the project. -->
<!-- You can turn off the Compile on Save (or Deploy on Save) setting -->
<!-- in the project’s Project Properties dialog box.–>
<project name=“BasicGameTemplate” default=“default” basedir=".">
<description>Builds, tests, and runs the project BasicGameTemplate.</description>
<import file=“nbproject/build-impl.xml”/>

&lt;target description="create mobile/libs directory and copy project libraries" name="-copy-project-libs"&gt;
    &lt;copy todir="mobile/libs" flatten="true" verbose="false" preservelastmodified="true"&gt;
        &lt;path&gt;
            &lt;pathelement path="${run.classpath.without.build.classes.dir}"/&gt;
        &lt;/path&gt;
    &lt;/copy&gt;
    &lt;delete file="mobile/libs/jME3-desktop.jar"/&gt;
    &lt;delete file="mobile/libs/jME3-blender.jar"/&gt;
    &lt;delete file="mobile/libs/jME3-lwjgl.jar"/&gt;
    &lt;delete file="mobile/libs/jME3-lwjgl-natives.jar"/&gt;
    &lt;delete file="mobile/libs/jME3-bullet-natives.jar"/&gt;
    &lt;delete file="mobile/libs/jME3-jbullet.jar"/&gt;
    &lt;delete file="mobile/libs/jME3-bullet.jar"/&gt;
    &lt;delete file="mobile/libs/jbullet.jar"/&gt;
    &lt;delete file="mobile/libs/stack-alloc.jar"/&gt;
    &lt;delete file="mobile/libs/vecmath.jar"/&gt;
    &lt;delete file="mobile/libs/lwjgl.jar"/&gt;
    &lt;delete file="mobile/libs/jinput.jar"/&gt;
&lt;/target&gt;
        
&lt;target name="-unzip-assets"&gt;
    &lt;echo&gt;Unzipping Assets to Android Directories (Modified...)&lt;/echo&gt;
    &lt;delete dir="mobile/assets" failonerror="false"/&gt;
    &lt;mkdir dir="mobile/assets"/&gt;
    &lt;delete file="mobile/libs/${assets.jar.name}" failonerror="true"/&gt;
&lt;/target&gt;

&lt;!--

There exist several targets which are by default empty and which can be 
used for execution of your tasks. These targets are usually executed 
before and after some main targets. They are: 

  -pre-init:                 called before initialization of project properties
  -post-init:                called after initialization of project properties
  -pre-compile:              called before javac compilation
  -post-compile:             called after javac compilation
  -pre-compile-single:       called before javac compilation of single file
  -post-compile-single:      called after javac compilation of single file
  -pre-compile-test:         called before javac compilation of JUnit tests
  -post-compile-test:        called after javac compilation of JUnit tests
  -pre-compile-test-single:  called before javac compilation of single JUnit test
  -post-compile-test-single: called after javac compilation of single JUunit test
  -pre-jar:                  called before JAR building
  -post-jar:                 called after JAR building
  -post-clean:               called after cleaning build products

(Targets beginning with '-' are not intended to be called on their own.)

Example of inserting an obfuscator after compilation could look like this:

    &lt;target name="-post-compile"&gt;
        &lt;obfuscate&gt;
            &lt;fileset dir="${build.classes.dir}"/&gt;
        &lt;/obfuscate&gt;
    &lt;/target&gt;

For list of available properties check the imported 
nbproject/build-impl.xml file. 


Another way to customize the build is by overriding existing main targets.
The targets of interest are: 

  -init-macrodef-javac:     defines macro for javac compilation
  -init-macrodef-junit:     defines macro for junit execution
  -init-macrodef-debug:     defines macro for class debugging
  -init-macrodef-java:      defines macro for class execution
  -do-jar-with-manifest:    JAR building (if you are using a manifest)
  -do-jar-without-manifest: JAR building (if you are not using a manifest)
  run:                      execution of project 
  -javadoc-build:           Javadoc generation
  test-report:              JUnit report generation

An example of overriding the target for project execution could look like this:

    &lt;target name="run" depends="BasicGameTemplate-impl.jar"&gt;
        &lt;exec dir="bin" executable="launcher.exe"&gt;
            &lt;arg file="${dist.jar}"/&gt;
        &lt;/exec&gt;
    &lt;/target&gt;

Notice that the overridden target depends on the jar target and not only on 
the compile target as the regular run target does. Again, for a list of available 
properties which you can use, check the target you are overriding in the
nbproject/build-impl.xml file. 

--&gt;

</project>
[/java]

I’ve the following output:

UNEXPECTED TOP-LEVEL EXCEPTION:
com.android.dx.util.DexException: Multiple dex files define Lcom/jme3/app/SimpleApplication;
Result compacted from 3089,3KiB to 1582,8KiB to save 1506,5KiB
at com.android.dx.merge.DexMerger.readSortableTypes(DexMerger.java:592)
Merged dex A (324 defs/1090,8KiB) with dex B (596 defs/1148,4KiB). Result is 920 defs/1582,8KiB. Took 0,3s
at com.android.dx.merge.DexMerger.getSortedTypes(DexMerger.java:550)
at com.android.dx.merge.DexMerger.mergeClassDefs(DexMerger.java:531)
at com.android.dx.merge.DexMerger.mergeDexBuffers(DexMerger.java:168)
at com.android.dx.merge.DexMerger.merge(DexMerger.java:186)
at com.android.dx.command.dexer.Main.mergeLibraryDexBuffers(Main.java:300)
at com.android.dx.command.dexer.Main.run(Main.java:232)
at com.android.dx.command.dexer.Main.main(Main.java:174)
at com.android.dx.command.Main.main(Main.java:91)

C:\Users\Javi\Documents\galvezgames\PruebaProGuard3\nbproject\mobile-impl.xml:21: The following error occurred while executing this line:
C:\android-sdk\tools\ant\build.xml:892: The following error occurred while executing this line:
C:\android-sdk\tools\ant\build.xml:894: The following error occurred while executing this line:
C:\android-sdk\tools\ant\build.xml:906: The following error occurred while executing this line:
C:\android-sdk\tools\ant\build.xml:284: null returned: 2
BUILD FAILED (total time: 21 seconds)

I’ve removed the line that deletes the assets.jar and the line that unzip the assets but another problem has appeard.

Any ideas of what is wrong now??

Sorry for not responding quicker. Been tied up with some other things. I made a test project to try this out (without proguard).

Here is the complete build.xml I have to leave the assets.jar file alone.

[java]

<?xml version="1.0" encoding="UTF-8"?> Builds, tests, and runs the project BasicGameTemplate.
<target name="-copy-android-libs" if="is.android.enabled">
    <echo>Custom Target -copy-android-libs</echo>
    <echo>Copying application libraries to android project.</echo>
    <delete dir="mobile/libs" failonerror="false"/>
    <mkdir dir="mobile/libs"/>
    <antcall target="-copy-project-libs"/>
    <antcall target="-add-android-lib"/>
    <antcall target="-unzip-bullet-libs"/>
    <copy file="${dist.jar}" todir="mobile/libs/" verbose="false" preservelastmodified="true"/>
    <antcall target="-unzip-openal-soft-libs"/>
</target>

<target description="create mobile/libs directory and copy project libraries" name="-copy-project-libs">
    <echo>Custom Target -copy-project-libs</echo>
    <copy todir="mobile/libs" flatten="true" verbose="false" preservelastmodified="true">
        <path>
            <pathelement path="${run.classpath.without.build.classes.dir}"/>
        </path>
    </copy>
    <delete file="mobile/libs/jME3-desktop.jar"/>
    <delete file="mobile/libs/jME3-blender.jar"/>
    <delete file="mobile/libs/jME3-lwjgl.jar"/>
    <delete file="mobile/libs/jME3-lwjgl-natives.jar"/>
    <delete file="mobile/libs/jME3-bullet-natives.jar"/>
    <delete file="mobile/libs/jME3-jbullet.jar"/>
    <delete file="mobile/libs/jME3-bullet.jar"/>
    <delete file="mobile/libs/jbullet.jar"/>
    <delete file="mobile/libs/stack-alloc.jar"/>
    <delete file="mobile/libs/vecmath.jar"/>
    <delete file="mobile/libs/lwjgl.jar"/>
    <delete file="mobile/libs/jinput.jar"/>
</target>

<!--

There exist several targets which are by default empty and which can be 
used for execution of your tasks. These targets are usually executed 
before and after some main targets. They are: 

  -pre-init:                 called before initialization of project properties
  -post-init:                called after initialization of project properties
  -pre-compile:              called before javac compilation
  -post-compile:             called after javac compilation
  -pre-compile-single:       called before javac compilation of single file
  -post-compile-single:      called after javac compilation of single file
  -pre-compile-test:         called before javac compilation of JUnit tests
  -post-compile-test:        called after javac compilation of JUnit tests
  -pre-compile-test-single:  called before javac compilation of single JUnit test
  -post-compile-test-single: called after javac compilation of single JUunit test
  -pre-jar:                  called before JAR building
  -post-jar:                 called after JAR building
  -post-clean:               called after cleaning build products

(Targets beginning with '-' are not intended to be called on their own.)

Example of inserting an obfuscator after compilation could look like this:

    <target name="-post-compile">
        <obfuscate>
            <fileset dir="${build.classes.dir}"/>
        </obfuscate>
    </target>

For list of available properties check the imported 
nbproject/build-impl.xml file. 


Another way to customize the build is by overriding existing main targets.
The targets of interest are: 

  -init-macrodef-javac:     defines macro for javac compilation
  -init-macrodef-junit:     defines macro for junit execution
  -init-macrodef-debug:     defines macro for class debugging
  -init-macrodef-java:      defines macro for class execution
  -do-jar-with-manifest:    JAR building (if you are using a manifest)
  -do-jar-without-manifest: JAR building (if you are not using a manifest)
  run:                      execution of project 
  -javadoc-build:           Javadoc generation
  test-report:              JUnit report generation

An example of overriding the target for project execution could look like this:

    <target name="run" depends="BasicGameTemplate-impl.jar">
        <exec dir="bin" executable="launcher.exe">
            <arg file="${dist.jar}"/>
        </exec>
    </target>

Notice that the overridden target depends on the jar target and not only on 
the compile target as the regular run target does. Again, for a list of available 
properties which you can use, check the target you are overriding in the
nbproject/build-impl.xml file. 

-->
[/java]

Thanks Eric for your help. As you, I’ve been very busy and I haven’t had time to work on this.
I’ve been doing several test and I think I have something wroken related to ProGuard. When I create a new project and compile it, I can execute MyGame.jar file without problems.
However, if I enable Obfuscation, first I have the following error:

C:\Users\Javi\Documents\galvezgames\PruebaProGuard5\nbproject\obfuscate-impl.xml:7: Can’t read [C:\Users\Javi\Documents\galvezgames\PruebaProGuard5${platform.home}\jre\lib\rt.jar] (No such file or directory)

So I replace in the obfuscate-impl.xml file the following line:
<property name=“proguard.library.path” value="${platform.home}/jre/lib/rt.jar"/>
For this one:
<property name=“proguard.library.path” value=“C:/jmonkeyplatform/jdk/jre/lib/rt.jar”/>
Then I can compile the project without problems but I can execute MyGame.jar file. The game doesn’t start.

Any ideas??

I’ve researched this problem a bit and it seems that the there are no clean solutions as for now.

Just for reference @normen or anybody else. Are you able to use proguard on JME as of now? In the event that you can, can you post here your xml and other configuration files for us to see, please?

I don’t see the point of obfuscating for Android, the android build process does that anyway.

1 Like
@normen said: I don't see the point of obfuscating for Android, the android build process does that anyway.

Thanks Normen!!

I’ve been working in something that is already done… :-o :-o :-o

So if I want to release for Win, Mac or Linux, I can check the obfuscation option to protect my code, but if I want to release for android I can left the option unchecked because it’s already implemented as a feature in the android buil process.
I don’t know if other developers have this concept clear, maybe an aclaration can be added in the android deployment documentation.

1 Like
@normen said: I don't see the point of obfuscating for Android, the android build process does that anyway.

Sorry, to bring this old thread up again. But IMHO there is no obfuscation for Android turned on by default.
At least it has to be turned on in mobile/project.properties, but then it will turn out that you have to configure proguard-project.txt, too. This requires a look in the ProGuard manuals. That’s absolutely OK, the IDE can’t know the project details.

I hope I haven’t overlooked something…

If you want obfuscation in desktop not android I found a solution. Obfuscation should be done by Proguard alone so download it. A complex software which contains Swing , JMonkey , Groovy and etc get corrupted by JMonkeyplatform Proguard and some parts may stop functioning. First compile normally without obfuscation. Use your main jar files (the ones you developed not libraries) as input and Lib folder as library in Proguard GUI (don’t forget jre if it wasn’t defined (written as default)). I know that this level of obfuscation is not perfect but better than nothing.

Yes there is obfuscation happening in the default android build process. It depends on what you actually mean by obfuscation though - the method names are not changed so you don’t get the “code protection” (if you can call that so and regard it as a viable option to do so - which I don’t) but you get the optimization.

@Amir: proguard is included in the SDK and you can enable it by just checking the appropriate checkbox in the SDK project properties.

Yes and it works but just Swing works after implementing, Groovy and JMonkey libraries get corrupted. First the lib folder gets empty when I check Obfuscation and Second when I add them manually to lib folder, again Groovy and JMonkey parts don’t work. However if Proguard is implemented out of JMonkryplatform it works well. In my idea if lib folder contents are added to -libraryjars it will be solved. That’s the reason @galvez6000 game doesn’t start because it may start with JMonkey part not Swing or AWT parts.

They get obfuscated…

You mean that libraries get obfuscated. Yes I think so and as I said external Proguard is not a perfect obfuscation.

Sorry to revive this old thread, but I just found out that jme DONT obfuscate the project code by default, and it seems its not easy to make this to work, I am getting several of this thread errors in the project.
Do anyone has an simple solution for this or I will have to install new proguard version, and configure all the ants , builds etc by my self to archive this ?

I was able to make this works with the new version of proguard on my android game ant maze 3d ( you can test it on playstore its a free game )

Some tips :

  1. Create the configuration on the proguard, run it and test the jar on your desktop, if your game crashs with some random errors like class bla not found you are in the right way.
  2. Fix the obfuscated classes using an custom proguard configuration, run your game and test everything.
  3. Change the build ant : nbproject/build-impl.xml , add an target based on the build all on this, add your proguard configuration on it, and when you want to release your game, run this target instead of the build all.

For example, this is my proguard ant task if :

<property name="appname" value="AntMaze3DFull"/>
<target depends="test,init,compile,-pre-jar,-do-jar-with-manifest,-do-jar-without-manifest,-do-jar-with-mainclass,-do-jar-with-libraries,-post-jar,_wag_justobfuscatejar,replaceObfuscated,-mobile-deployment" description="BUILD ALL THE PROJECT OBFUSCATED." name="_wag_compile_obfuscated"/>
<target name="replaceObfuscated" description="Replace the original jar with the obfuscated one">
    <copy file="${basedir}\dist\${appname}_ob.jar" tofile="${basedir}\dist\${appname}.jar"/>
</target>
<target name="_wag_justobfuscatejar">
    <property name="proguard.jar" location="../proguard5.2.1/lib/proguard.jar" />
    <taskdef resource="proguard/ant/task.properties" classpath="${proguard.jar}" />
    <proguard>
-injars '${basedir}\dist\${appname}.jar'
-outjars '${basedir}\dist\${appname}_ob.jar'
-libraryjars 'C:\Program Files\Java\jdk1.7.0_80\jre\lib\rt.jar'
-libraryjars '${basedir}\dist\lib\assets.jar'
-libraryjars '${basedir}\dist\lib\jinput.jar'
-libraryjars '${basedir}\dist\lib\jME3-core.jar'
-libraryjars '${basedir}\dist\lib\jME3-desktop.jar'
-libraryjars '${basedir}\dist\lib\jME3-jogg.jar'
-libraryjars '${basedir}\dist\lib\jME3-lwjgl-natives.jar'
-libraryjars '${basedir}\dist\lib\jME3-lwjgl.jar'
-libraryjars '${basedir}\dist\lib\j-ogg-oggd.jar'
-libraryjars '${basedir}\dist\lib\j-ogg-vorbisd.jar'
-libraryjars '${basedir}\dist\lib\lwjgl.jar'
-printmapping '${basedir}\dist\obresult.txt'
-dontnote
-dontwarn
-keep public class * extends com.jme3.app.Application{public *;}
-keep class mygame.AndroidInterface  { *; *; }
-keep class mygame.Main
-keep class mygame.AppStateGamePhaseRun
-keep class mygame.AppStateMenus
-keep class mygame.AppStateGamePhaseRun { *** onMouseClickUp_*(...); *** update(...); }
-keep class mygame.AppStateMenus { *** onMouseClickUp_*(...); *** update(...); }
-keep class mygame.RemotePropertiesProtocol { *; *; }
    </proguard>
</target> <!-- END OF MY WAG OBFUSCATOR TASK -->
1 Like