25

I'm new to using Proguard so I'm probably making a newbie mistake. I've got an app that after I run the release build (which uses Proguard to obfuscate) it crashes pretty quickly. I believe I've narrowed it down to the fact that it seems like it is obfuscating my reference libraries. In my case my reference libraries are used to define my message classes that I am using to communicate to another device using Google Protobuffers. I am building using, ant release. My proguard config is:

-optimizationpasses 5
-dontusemixedcaseclassnames
-dontskipnonpubliclibraryclasses
-dontpreverify
-verbose
-optimizations !code/simplification/arithmetic,!field/*,!class/merging/*

-keep public class * extends android.app.Activity
-keep public class * extends android.app.Application
-keep public class * extends android.app.Service
-keep public class * extends android.content.BroadcastReceiver
-keep public class * extends android.content.ContentProvider
-keep public class * extends android.app.backup.BackupAgentHelper
-keep public class * extends android.preference.Preference
-keep public class com.android.vending.licensing.ILicensingService

-keepclasseswithmembernames class * 
 {
    native <methods>;
 }

-keepclasseswithmembernames class * 
 {
    public <init>(android.content.Context, android.util.AttributeSet);
 }

-keepclasseswithmembernames class * 
 {
    public <init>(android.content.Context, android.util.AttributeSet, int);
 }

-keepclassmembers enum * 
 {
    public static **[] values();
    public static ** valueOf(java.lang.String);
 }

-keep class * implements android.os.Parcelable 
 {
    public static final android.os.Parcelable$Creator *;
 }

In my ant build.xml file I have the following defined:

<target name="-obfuscate" unless="do.not.compile">
    <if condition="${proguard.enabled}">
        <then>
            <property name="obfuscate.absolute.dir" location="${out.absolute.dir}/proguard" />
            <property name="preobfuscate.jar.file"  value="${obfuscate.absolute.dir}/original.jar" />
            <property name="obfuscated.jar.file"    value="${obfuscate.absolute.dir}/obfuscated.jar" />

             <!-- input for dex will be proguard's output -->
            <property name="out.dex.input.absolute.dir" value="${obfuscated.jar.file}" />

            <!-- Add Proguard Tasks -->
            <property name="proguard.jar" location="${android.tools.dir}/proguard/lib/proguard.jar" />
            <taskdef name="proguard" classname="proguard.ant.ProGuardTask" classpath="${proguard.jar}" />

            <!-- Set the android classpath Path object into a single property. It'll be
                 all the jar files separated by a platform path-separator.
            -->
            <property name="android.libraryjars" refid="android.target.classpath"/>

            <!-- Build a path object with all the jar files that must be obfuscated.
                 This include the project compiled source code and any 3rd party jar
                 files. 
            -->
            <path id="project.jars.ref">
                <pathelement location="${preobfuscate.jar.file}" />
                <path refid="jar.libs.ref" />
            </path>

            <!-- Set the project jar files Path object into a single property. It'll be
                 all the jar files separated by a platform path-separator.
            -->
            <property name="project.jars" refid="project.jars.ref" />

            <mkdir  dir="${obfuscate.absolute.dir}" />
            <delete file="${preobfuscate.jar.file}" />
            <delete file="${obfuscated.jar.file}"   />
            <jar basedir="${out.classes.dir}" destfile="${preobfuscate.jar.file}" />
            <proguard>
                @${proguard.config}
                -injars ${project.jars}
                -outjars ${obfuscated.jar.file}
                -libraryjars ${android.libraryjars}
                -dump ${obfuscate.absolute.dir}/dump.txt
                -printseeds ${obfuscate.absolute.dir}/seeds.txt
                -printusage ${obfuscate.absolute.dir}/usage.txt
                -printmapping ${obfuscate.absolute.dir}/mapping.txt
            </proguard>
        </then>
    </if>
</target>

<!-- Converts this project's .class files into .dex files -->
<target name="-dex" depends="compile, -post-compile, -obfuscate"
        unless="do.not.compile">
    <if condition="${manifest.hasCode}">
        <then>
            <dex-helper />
        </then>
        <else>
            <echo>hasCode = false. Skipping...</echo>
        </else>
    </if>
</target>

<!-- Puts the project's resources into the output package file
     This actually can create multiple resource package in case
     Some custom apk with specific configuration have been
     declared in default.properties.
     -->
<target name="-package-resources">
    <echo>Packaging resources</echo>
    <aapt executable="${aapt}"
            command="package"
            versioncode="${version.code}"
            debug="${build.packaging.debug}"
            manifest="AndroidManifest.xml"
            assets="${asset.absolute.dir}"
            androidjar="${android.jar}"
            apkfolder="${out.absolute.dir}"
            resourcefilename="${resource.package.file.name}"
            resourcefilter="${aapt.resource.filter}">
        <res path="${resource.absolute.dir}" />
        <!-- <nocompress /> forces no compression on any files in assets or res/raw -->
        <!-- <nocompress extension="xml" /> forces no compression on specific file extensions in assets and res/raw -->
    </aapt>
</target>

<!-- Packages the application and sign it with a debug key. -->
<target name="-package-debug-sign" depends="-dex, -package-resources">
    <package-helper
            output.filepath="${out.debug.unaligned.file}" />
</target>

<!-- Packages the application without signing it. -->
<target name="-package-release" depends="-dex, -package-resources">
    <package-helper
            output.filepath="${out.unsigned.file}"/>
</target>
    <target name="-set-release-mode">
        <!-- release mode is only valid if the manifest does not explicitly
             set debuggable to true. default is false.
             We actually store build.packaging.debug, not build.release -->
        <xpath input="AndroidManifest.xml" expression="/manifest/application/@android:debuggable"
               output="build.packaging.debug" default="false"/>

        <!-- Enable proguard -->
        <property name="proguard.enabled" value="true"/>
        <property name="proguard.config" value="proguard.cfg"/>

        <!-- signing mode: release -->
        <property name="build.signing.debug" value="false" />

        <if condition="${build.packaging.debug}">
            <then>
                <echo>*************************************************</echo>
                <echo>****  Android Manifest has debuggable=true   ****</echo>
                <echo>**** Doing DEBUG packaging with RELEASE keys ****</echo>
                <echo>*************************************************</echo>
            </then>
            <else>
                <!-- property only set in release mode.
                     Useful for if/unless attributes in target node
                     when using Ant before 1.8 -->
                <property name="build.mode.release" value="true"/>
            </else>
        </if>
    </target>

    <target name="release"
            depends="-set-release-mode, -release-obfuscation-check, -package-release, -release-prompt-for-password, -release-nosign"
                if="has.keystore"
                description="Builds the application. The generated apk file must be signed before
                            it is published.">
        <!-- Signs the APK -->
        <echo>Signing final apk...</echo>
        <signjar
                jar="${out.unsigned.file}"
                signedjar="${out.unaligned.file}"
                keystore="${key.store}"
                storepass="${key.store.password}"
                alias="${key.alias}"
                keypass="${key.alias.password}"
                verbose="${verbose}" />

        <!-- Zip aligns the APK -->
        <zipalign-helper in.package="${out.unaligned.file}"
                                   out.package="${out.release.file}" />
        <echo>Release Package: ${out.release.file}</echo>
    </target>

I'd appreciate any ideas. I copied in most of my build and proguard config from online templates, I suspect that I am actually intructing Proguard to obfuscate the library jars or I have something lisconfigured. I am seeing the following output in my cmd window at the end of the build:

[proguard] Writing output...
[proguard] Preparing output jar [C:\Workspace\UI\MyApp\build\proguard\obfuscated.jar]
[proguard]   Copying resources from program jar [C:\Workspace\UI\MyApp\build\proguard\original.jar]
[proguard]   Copying resources from program jar [C:\Workspace\UI\MyApp\libs\libmessaging.jar]
[proguard] Warning: can't write resource [META-INF/MANIFEST.MF] (Duplicate zip entry [libmessaging.jar:META-INF/MANIFEST.MF])
[proguard]   Copying resources from program jar [C:\Workspace\UI\MyApp\libs\protobuf-2.3.0.jar]
[proguard] Warning: can't write resource [META-INF/MANIFEST.MF] (Duplicate zip entry [protobuf-2.3.0.jar:META-INF/MANIFEST.MF])
[proguard] Printing classes to [C:\Workspace\Riptide1_1_ptr_74\WPG_HAWKSBILL\UI\MyApp\build\proguard\dump.txt]...

Thanks!

bursk
  • 1,647
  • 7
  • 25
  • 39

2 Answers2

47

You have commented your library jar directives in the Proguard config.
Change:

# -libraryjars /libs/protobuf-2.3.0.jar
# -libraryjars /libs/libmessaging.jar

to:

-libraryjars /libs/protobuf-2.3.0.jar
-libraryjars /libs/libmessaging.jar

(and then don't bother defining your library jars in your build.xml)

EDIT:
I found another way to make Proguard leave library jars alone was to ask it to preserve their package names, eg:

-keep class javax.** { *; }
-keep class org.** { *; }
-keep class twitter4j.** { *; }
Caspar Harmer
  • 8,097
  • 2
  • 42
  • 39
  • I knew I had them commented out - I was getting all kinds of output so I commented them out - but I hadn't noticed that I also had them referenced in the build. I'll give it a try! So I need to remove lines and -libraryjars ${android.libraryjars} from my build file? Thank you! – bursk Jul 01 '11 at 11:04
  • I changed my answer to add something else I did also... See above. – Caspar Harmer Jul 01 '11 at 20:47
  • Thanks for your help. I must still be missing something. I edited my proguard.cfg to show how I have uncommented the libraryjars and added -keeps. I commented out the lines ( -libraryjars ${android.libraryjars}) and ( ) from my build.xml from the target name="-obfuscate", but when I try ant release I get errors like " can't find referenced class java.lang.String" and "duplicate definition of library class" for all the classes I have in my libmessaging.jar. – bursk Jul 04 '11 at 15:21
  • 1
    I updated my question above. Hopefully you have another idea? I tried commenting out the library declaration in the build.xml and that seemed to make things worse. I also tried adding -keep class com.google.protobuf.** {*;} and -keep class com.my.messaging.** {*;} to the proguard.cfg with the same results as I had originally. – bursk Jul 07 '11 at 21:46
  • I'm not really sure what's happening here, but it may be that you are somehow invoking proguard twice - perhaps that's the reason for your "Duplicate Entry" error. Have you tried to build using Eclipse to see if it works that way? – Caspar Harmer Jul 07 '11 at 22:36
  • I wasn't aware that I could use it when building with Eclipse. I've been using Eclipse for debugging and then ant for a release build. – bursk Jul 08 '11 at 11:28
  • Found the Export utility and was able to build and obfuscated apk. I still don't understand what is going on with the library calls - but - the -keep class idea you had for the contents is working. I realized when trying this that for one I had a slight typo. Thanks! – bursk Jul 08 '11 at 12:03
  • Hi i have a question. how do u exclude all jars inside /libs? – Jono Feb 04 '13 at 15:45
  • Sorry dude, this would qualify as a separate question - so you should probably create a new question to get an answer to this (plus, without experimentation, I'm not sure). – Caspar Harmer Feb 05 '13 at 06:37
  • 2
    Your -keep class method saved me! Thanks! – Vinay S Shenoy Apr 25 '13 at 12:58
2

I had a similar problem when proGuard encrypted the jsoup lib. I fixed it by adding the following to my proguard-rules.txt:

-keep class org.jsoup** {
    *;
}

So the build.gradle will end up with the following section:

release {
    minifyEnabled true
    proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.txt'
}
Jared Burrows
  • 54,294
  • 25
  • 151
  • 185
Percy Vega
  • 1,449
  • 1
  • 16
  • 13